[
  {
    "path": ".clang-format",
    "content": "﻿---\r\n# this file work is a derivative of onecoreuap/net/.clang-format\r\n# https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md\r\n#\r\nLanguage:        Cpp\r\n# BasedOnStyle:  LLVM\r\nAccessModifierOffset: -4\r\nAlignAfterOpenBracket: AlwaysBreak\r\nAlignConsecutiveAssignments: false\r\nAlignEscapedNewlines: DontAlign\r\nAlignOperands:   true\r\nAlignTrailingComments: true\r\nAllowAllParametersOfDeclarationOnNextLine: true\r\nAllowShortBlocksOnASingleLine: false\r\nAllowShortCaseLabelsOnASingleLine: false\r\nAllowShortFunctionsOnASingleLine: None\r\nAllowShortIfStatementsOnASingleLine: false\r\nAllowShortLoopsOnASingleLine: false\r\nAlwaysBreakAfterDefinitionReturnType: None\r\nAlwaysBreakAfterReturnType: None\r\nAlwaysBreakBeforeMultilineStrings: true\r\nAlwaysBreakTemplateDeclarations: true\r\nBinPackArguments: false\r\nBinPackParameters: false\r\nBreakBeforeBinaryOperators: None\r\nBreakBeforeBraces: Custom\r\nBraceWrapping:\r\n  AfterCaseLabel: true\r\n  AfterClass: true\r\n  AfterControlStatement: true\r\n  AfterEnum: true\r\n  AfterFunction: true\r\n  AfterNamespace: false\r\n  AfterStruct: true\r\n  AfterUnion: true\r\n  AfterExternBlock: false\r\n  BeforeCatch: true\r\n  BeforeElse: true\r\n  SplitEmptyFunction: true\r\n  SplitEmptyRecord: true\r\n  SplitEmptyNamespace: true\r\nBreakBeforeTernaryOperators: true\r\nBreakConstructorInitializers: AfterColon\r\nColumnLimit: 130\r\nCommentPragmas:  '^ IWYU pragma:'\r\nCompactNamespaces: true\r\nConstructorInitializerAllOnOneLineOrOnePerLine: true\r\nConstructorInitializerIndentWidth: 4\r\nContinuationIndentWidth: 4\r\nCpp11BracedListStyle: true\r\nDerivePointerAlignment: false\r\nDisableFormat:   false\r\nExperimentalAutoDetectBinPacking: false\r\nFixNamespaceComments: true\r\nForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]\r\nIncludeBlocks: Regroup\r\nIncludeCategories:\r\n  - Regex: '^\"(stdafx.h|pch.h|precomp.h)\"$'\r\n    Priority: -1\r\nIndentCaseLabels: false\r\nInsertBraces: true\r\nIndentWidth:     4\r\nIndentWrappedFunctionNames: false\r\nKeepEmptyLinesAtTheStartOfBlocks: true\r\nMacroBlockBegin: '^BEGIN_COM_MAP$|^BEGIN_CONNECTION_POINT_MAP$|^BEGIN_HELPER_NODEMAP$|^BEGIN_MODULE$|^BEGIN_MSG_MAP$|^BEGIN_OBJECT_MAP$|^BEGIN_TEST_CLASS$|^BEGIN_TEST_METHOD$|^BEGIN_TEST_METHOD_PROPERTIES$'\r\nMacroBlockEnd: '^END_COM_MAP$|^END_CONNECTION_POINT_MAP$|^END_HELPER_NODEMAP$|^END_MODULE$|^END_MSG_MAP$|^END_OBJECT_MAP$|^END_TEST_CLASS$|^END_TEST_METHOD$|^END_TEST_METHOD_PROPERTIES$'\r\nMaxEmptyLinesToKeep: 1\r\nNamespaceIndentation: Inner\r\nObjCBlockIndentWidth: 2\r\nObjCSpaceAfterProperty: false\r\nObjCSpaceBeforeProtocolList: true\r\nPenaltyBreakBeforeFirstCallParameter: 19\r\nPenaltyBreakComment: 300\r\nPenaltyBreakFirstLessLess: 120\r\nPenaltyBreakString: 1000\r\nPenaltyExcessCharacter: 1\r\nPenaltyReturnTypeOnItsOwnLine: 1000\r\nPointerAlignment: Left\r\nSortIncludes: false\r\nSpaceAfterCStyleCast: false\r\nSpaceBeforeAssignmentOperators: true\r\nSpaceBeforeParens: ControlStatements\r\nSpaceInEmptyParentheses: false\r\nSpacesBeforeTrailingComments: 1\r\nSpacesInAngles:  false\r\nSpacesInContainerLiterals: true\r\nSpacesInCStyleCastParentheses: false\r\nSpacesInParentheses: false\r\nSpacesInSquareBrackets: false\r\nStandard:        Cpp11\r\nStatementMacros: [\r\n  _Acquires_exclusive_lock_,\r\n  _Acquires_lock_,\r\n  _Acquires_nonreentrant_lock_,\r\n  _Acquires_shared_lock_,\r\n  _Analysis_assume_smart_lock_acquired_,\r\n  _Analysis_assume_smart_lock_released_,\r\n  _Create_lock_level_,\r\n  _Detaches_lock_,\r\n  _Function_class_,\r\n  _Global_cancel_spin_lock_,\r\n  _Global_critical_region_,\r\n  _Global_interlock_,\r\n  _Global_priority_region_,\r\n  _Has_lock_kind_,\r\n  _Has_lock_level_,\r\n  _IRQL_always_function_max_,\r\n  _IRQL_always_function_min_,\r\n  _IRQL_raises_,\r\n  _IRQL_requires_,\r\n  _IRQL_requires_max_,\r\n  _IRQL_requires_min_,\r\n  _IRQL_requires_same_,\r\n  _IRQL_restores_,\r\n  _IRQL_restores_global_,\r\n  _IRQL_saves_,\r\n  _IRQL_saves_global_,\r\n  _Lock_level_order_,\r\n  _Moves_lock_,\r\n  _Must_inspect_result_,\r\n  _No_competing_thread_,\r\n  _Post_same_lock_,\r\n  _Post_writable_byte_size_,\r\n  _Pre_satisfies_,\r\n  _Releases_exclusive_lock_,\r\n  _Releases_lock_,\r\n  _Releases_nonreentrant_lock_,\r\n  _Releases_shared_lock_,\r\n  _Replaces_lock_,\r\n  _Requires_exclusive_lock_held_,\r\n  _Requires_lock_held_,\r\n  _Requires_lock_not_held_,\r\n  _Requires_no_locks_held_,\r\n  _Requires_shared_lock_held_,\r\n  _Ret_maybenull_,\r\n  _Ret_range_,\r\n  _Success_,\r\n  _Swaps_locks_,\r\n  _Use_decl_annotations_,\r\n  _When_,\r\n  RpcEndExcept,\r\n]\r\nTabWidth:        4\r\nTypenameMacros: [\r\n  IFACEMETHOD,\r\n  STDMETHOD,\r\n]\r\nUseTab:          Never\r\n...\r\n\r\n"
  },
  {
    "path": ".gdnsuppress",
    "content": "{\n  \"hydrated\": false,\n  \"properties\": {\n    \"helpUri\": \"https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/suppressions\"\n  },\n  \"version\": \"1.0.0\",\n  \"suppressionSets\": {\n    \"default\": {\n      \"name\": \"default\",\n      \"createdDate\": \"2025-04-30 22:17:21Z\",\n      \"lastUpdatedDate\": \"2025-04-30 22:17:21Z\"\n    }\n  },\n  \"results\": {\n    \"3d7d59d41a06e01093616033ddd4ecec1e8dcb65363768638eea6acb6d012b4e\": {\n      \"signature\": \"3d7d59d41a06e01093616033ddd4ecec1e8dcb65363768638eea6acb6d012b4e\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"aa145b1818ed203013a6267ca382110ec5ea9c1bf50a812964d4c7b5671224ec\": {\n      \"signature\": \"aa145b1818ed203013a6267ca382110ec5ea9c1bf50a812964d4c7b5671224ec\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"c5f15f6ef4ac3f1e82c31ae0f651ab0f1c8448e34824bf231dd2104cef70d9e5\": {\n      \"signature\": \"c5f15f6ef4ac3f1e82c31ae0f651ab0f1c8448e34824bf231dd2104cef70d9e5\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"e8eb99fd7876f9c79c0ff194de9e21cc77d63573a9b73ba1666778a247dcebff\": {\n      \"signature\": \"e8eb99fd7876f9c79c0ff194de9e21cc77d63573a9b73ba1666778a247dcebff\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"43df748a685bc9feca8cf04c518f57a82012d61665dca65d8a1fb652a55d9391\": {\n      \"signature\": \"43df748a685bc9feca8cf04c518f57a82012d61665dca65d8a1fb652a55d9391\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"fc58019c679cf3c469af5161b1f237f7552ee81c3d3ae29aff2d18fcceafe3e7\": {\n      \"signature\": \"fc58019c679cf3c469af5161b1f237f7552ee81c3d3ae29aff2d18fcceafe3e7\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"bedcd464fd4426dbd786cb09b7407100aa548dfff85d3c89e7fd79c8c2623391\": {\n      \"signature\": \"bedcd464fd4426dbd786cb09b7407100aa548dfff85d3c89e7fd79c8c2623391\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"06d5d116da395c14acda7a67c6530e675cc6d59593a94f37fbc2b91ce05380ca\": {\n      \"signature\": \"06d5d116da395c14acda7a67c6530e675cc6d59593a94f37fbc2b91ce05380ca\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"a93af66a96249e7041eee45e325c2548da917d4c6984e7cf67fa0c052a01a980\": {\n      \"signature\": \"a93af66a96249e7041eee45e325c2548da917d4c6984e7cf67fa0c052a01a980\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"2950fa4920cef845442e43dccfbdd49ca5935a1f0aa115c3b25bf5397d39bac7\": {\n      \"signature\": \"2950fa4920cef845442e43dccfbdd49ca5935a1f0aa115c3b25bf5397d39bac7\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"d747605a4cb9b5cbe7715f638b3909572302db204327120c50f712064c405380\": {\n      \"signature\": \"d747605a4cb9b5cbe7715f638b3909572302db204327120c50f712064c405380\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"be9c9eacd39f7740ac8794956f9133718e7bac4556a7a29ebdbcae5a1c762faf\": {\n      \"signature\": \"be9c9eacd39f7740ac8794956f9133718e7bac4556a7a29ebdbcae5a1c762faf\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"1af653b6acce89da21e37933f80d0686bf8a01aab386620533b42e06b55a3f26\": {\n      \"signature\": \"1af653b6acce89da21e37933f80d0686bf8a01aab386620533b42e06b55a3f26\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"5ba96332382fbe9cfb5c6baf51b5bd6e2644f1aa86c3fad66562dd648458f431\": {\n      \"signature\": \"5ba96332382fbe9cfb5c6baf51b5bd6e2644f1aa86c3fad66562dd648458f431\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"29432097a113f870ee7f86e585601fa1a6013f50b65893a359391ef47f996c73\": {\n      \"signature\": \"29432097a113f870ee7f86e585601fa1a6013f50b65893a359391ef47f996c73\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"352c2da0bfcde6f9f60c43359e7263b07247d26bff0343a369c8e3667c37fbe1\": {\n      \"signature\": \"352c2da0bfcde6f9f60c43359e7263b07247d26bff0343a369c8e3667c37fbe1\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"64bf4920e1b5b21e7312594ca9d95e3b48ac05a9cef507b21d91ba588a0ab08e\": {\n      \"signature\": \"64bf4920e1b5b21e7312594ca9d95e3b48ac05a9cef507b21d91ba588a0ab08e\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"720d1478e66c5f46670729c232d5afb787cdcc007f53e67d31edd4457e60967c\": {\n      \"signature\": \"720d1478e66c5f46670729c232d5afb787cdcc007f53e67d31edd4457e60967c\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"cf0a86680efb56a7eb351df85c51c6ad2e1ae64f108b8729ab7a9dd148d8312b\": {\n      \"signature\": \"cf0a86680efb56a7eb351df85c51c6ad2e1ae64f108b8729ab7a9dd148d8312b\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"3cedf840f8bdf45d29de68eedf06e3548fbdf86ebe3929c38b1be7babd790142\": {\n      \"signature\": \"3cedf840f8bdf45d29de68eedf06e3548fbdf86ebe3929c38b1be7babd790142\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"2e746764a2b4ee4971e2afded4c2dd99c5bf7647a5c8d9ae027d901caf7c5982\": {\n      \"signature\": \"2e746764a2b4ee4971e2afded4c2dd99c5bf7647a5c8d9ae027d901caf7c5982\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"04e31f322d76e5b2d92d36987ae5453b9d7b4eb1aba2e59f9956e088756eb3d3\": {\n      \"signature\": \"04e31f322d76e5b2d92d36987ae5453b9d7b4eb1aba2e59f9956e088756eb3d3\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"df6145ffc4e1301e28b405ec934470ab2346ac60ef1b5a806a470f6a86907141\": {\n      \"signature\": \"df6145ffc4e1301e28b405ec934470ab2346ac60ef1b5a806a470f6a86907141\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"53daee355a750916f6516911381cd31bfbb8de644cb0158a7bfb6456ffab163c\": {\n      \"signature\": \"53daee355a750916f6516911381cd31bfbb8de644cb0158a7bfb6456ffab163c\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"cf5679eb50b6062e91bab7a9a53aa2c356a41289192081f733e42c6def3abc96\": {\n      \"signature\": \"cf5679eb50b6062e91bab7a9a53aa2c356a41289192081f733e42c6def3abc96\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"9dac4ccae735c85dd3974f876f336accefa2f90f3cea74801bf4b148926bd132\": {\n      \"signature\": \"9dac4ccae735c85dd3974f876f336accefa2f90f3cea74801bf4b148926bd132\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"d05aa96a810d8140a0c2269e8d5e64b8b6db02652da6842547e1e1c4139096b5\": {\n      \"signature\": \"d05aa96a810d8140a0c2269e8d5e64b8b6db02652da6842547e1e1c4139096b5\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"cd0ad7bf2ffe4b3d068dc6c9731aa1d8f001250f30ebd3667a8982f03e72c879\": {\n      \"signature\": \"cd0ad7bf2ffe4b3d068dc6c9731aa1d8f001250f30ebd3667a8982f03e72c879\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"8415dbbf5b26dadc3e7d7f09b496a7f53452c60b67019bbb92c48dd1cd0f703a\": {\n      \"signature\": \"8415dbbf5b26dadc3e7d7f09b496a7f53452c60b67019bbb92c48dd1cd0f703a\",\n      \"alternativeSignatures\": [\n        \"88c2fa648d6200a3832a1a4b6d568c43a9dd89e05e4032c4286450c4d3a9171d\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"20edc36496c8274a97fe9179b56fcc1b5aab4b7e0d6769c23e14181844f64f35\": {\n      \"signature\": \"20edc36496c8274a97fe9179b56fcc1b5aab4b7e0d6769c23e14181844f64f35\",\n      \"alternativeSignatures\": [\n        \"14b018ac237bbbfe6304e73f454e190a2c60d335f249db0a9af6500eeae4808f\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    },\n    \"2652e38833372ca84eeebf1e9cd6145d4556e3851f7e698f95b5f01574cef372\": {\n      \"signature\": \"2652e38833372ca84eeebf1e9cd6145d4556e3851f7e698f95b5f01574cef372\",\n      \"alternativeSignatures\": [\n        \"d705445d04087b944ab3e7777bce5c61a4efc64fc7be2953cf7ed8573321a87b\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2025-04-30 22:17:21Z\"\n    }\n  }\n}"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "# File containing policy for file ownership\n\n# Reviewers for all files in the repository\n* @microsoft/wsl-maintainers"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Bug_Report.yaml",
    "content": "---\r\nname: \"WSL - Bug Report\"\r\ndescription: Report a bug on Windows Subsystem for Linux\r\nbody:\r\n- type: markdown\r\n  attributes:\r\n    value: |\r\n      Please [search for existing issues](https://github.com/microsoft/WSL/issues) before creating a new one.\r\n      If you'd like to create a new bug report, please [read the guidance here](https://github.com/Microsoft/WSL/blob/master/CONTRIBUTING.md) before getting started. This will save you time.\r\n\r\n- type: input\r\n  attributes:\r\n    label: Windows Version\r\n    description: |\r\n      Please run `cmd.exe /c ver` to get the build of Windows you are on.\r\n    placeholder: \"Microsoft Windows [Version 10.0.19042.867]\"\r\n  validations:\r\n    required: true\r\n\r\n- type: input\r\n  attributes:\r\n    label: WSL Version\r\n    description: |\r\n      If you are running Windows Subsystem for Linux from the Microsoft Store, please run `wsl.exe --version`.\r\n      Else `0.0.0.0`\r\n    placeholder: \"0.0.0.0\"\r\n  validations:\r\n    required: true\r\n\r\n- type: checkboxes\r\n  attributes:\r\n    label: Are you using WSL 1 or WSL 2?\r\n    description: |\r\n      Tell us whether the issue is on WSL 2 and/or WSL 1. You can tell by running `wsl -l -v`. \r\n    options:\r\n      - label: \"WSL 2\"\r\n      - label: \"WSL 1\"\r\n\r\n- type: input\r\n  attributes:\r\n    label: Kernel Version\r\n    description: |\r\n      Please tell us what version of the Linux kernel you are using, or if you are using a custom kernel. \r\n      You can run `wsl.exe --status` if that command is available to you, or by running `cat /proc/version` in your distro.\r\n    placeholder: \"5.4.72\"\r\n  validations:\r\n    required: false\r\n\r\n- type: input\r\n  attributes:\r\n    label: Distro Version\r\n    description: |\r\n      Please tell us what distro you are using (if applicable). \r\n      You can get additional information about the version where possible, e.g. on Debian / Ubuntu, run `lsb_release -r`\r\n    placeholder: \"Ubuntu 16.04\"\r\n  validations:\r\n    required: false\r\n\r\n- type: textarea\r\n  attributes:\r\n    label: Other Software\r\n    description: If you're reporting a bug involving WSL's interaction with other applications, please tell us. What applications? What versions?\r\n    placeholder: |\r\n      Docker Desktop (Windows), version 3.2.2\r\n      traceroute, Version: 1:2.0.21-1\r\n      Visual Studio Code 1.54.3 with Remote-WSL Extension 0.54.6\r\n      MyCustomApplication\r\n  validations:\r\n    required: false\r\n\r\n- type: textarea\r\n  attributes:\r\n    label: Repro Steps\r\n    description: Please list out the steps to reproduce your bug.  \r\n    placeholder: Your steps go here. Include relevant environmental variables or any other configuration.\r\n  validations:\r\n    required: true\r\n\r\n- type: textarea\r\n  attributes:\r\n    label: Expected Behavior\r\n    description: What were you expecting to see? Include any relevant examples or documentation links.\r\n    placeholder: If you want to include screenshots, paste them into the text area or follow up with a separate comment. \r\n  validations:\r\n    required: true\r\n\r\n- type: textarea\r\n  attributes:\r\n    label: Actual Behavior\r\n    description: What happened instead?\r\n    placeholder: Include the terminal output, straces of the failing command, etc. as necessary.\r\n  validations:\r\n    required: true\r\n\r\n- type: textarea\r\n  attributes:\r\n    label: Diagnostic Logs\r\n    description: | \r\n      Please provide additional diagnostics if needed.\r\n      See the [guidance](https://github.com/Microsoft/WSL/blob/master/CONTRIBUTING.md) for info on how to gather Feedback Hub logs, networking logs, and more.\r\n    placeholder: Your links to logs or other information go here.\r\n  validations:\r\n    required: false\r\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request / Contribution idea\nabout: Suggest a feature or improvement for the Windows Subsystem for Linux\ntitle: ''\nlabels: 'feature'\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/actions/triage/action.yml",
    "content": "name: Run automated triage\r\n\r\n\r\ninputs:\r\n    issue:\r\n      required: false\r\n      type: string\r\n    comment:\r\n      required: false\r\n      type: string\r\n    token:\r\n      required: false\r\n      type: string\r\n    previous_body:\r\n      required: false\r\n      type: string\r\n\r\nruns:\r\n    using: \"composite\"\r\n    steps:\r\n      - name: 'Run WTI'\r\n        shell: pwsh\r\n        env: \r\n            previous_body: \"${{ inputs.previous_body }}\"\r\n        run: |\r\n            $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop\r\n            \r\n            $maybe_comment = @()\r\n            if (![string]::IsNullOrEmpty(\"${{ inputs.comment }}\"))\r\n            {\r\n                $maybe_comment = @(\"--comment\", \"${{ inputs.comment }}\")\r\n            }\r\n            \r\n            $maybe_previous_body = @()\r\n            if (![string]::IsNullOrEmpty(\"$env:previous_body\"))\r\n            {\r\n                $env:previous_body | Out-File -Encoding utf8 \"triage\\previous_body.txt\"\r\n                $maybe_previous_body = @(\"--previous-issue-body\", \"previous_body.txt\")\r\n            }\r\n            \r\n            curl.exe -L https://github.com/OneBlue/wti/releases/download/v0.1.12/wti.exe -o triage/wti.exe\r\n            \r\n            cd triage && .\\wti.exe --issue ${{ inputs.issue }} --config config.yml --github-token \"${{ inputs.token }}\" --ignore-tags @maybe_comment @maybe_previous_body"
  },
  {
    "path": ".github/copilot-instructions.md",
    "content": "# Windows Subsystem for Linux (WSL)\n\n**ALWAYS reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.**\n\nWSL is the Windows Subsystem for Linux - a compatibility layer for running Linux binary executables natively on Windows. This repository contains the core Windows components that enable WSL functionality.\n\n## Working Effectively\n\n### Critical Platform Requirements\n- **Full builds ONLY work on Windows** with Visual Studio and Windows SDK 26100\n- **DO NOT attempt to build the main WSL components on Linux** - they require Windows-specific APIs, MSBuild, and Visual Studio toolchain\n- Many validation and development tasks CAN be performed on Linux (documentation, formatting, Python validation scripts)\n\n### Windows Build Requirements (Required for Full Development)\n- CMake >= 3.25 (`winget install Kitware.CMake`)\n- Visual Studio with these components:\n  - Windows SDK 26100 \n  - MSBuild\n  - Universal Windows platform support for v143 build tools (X64 and ARM64)\n  - MSVC v143 - VS 2022 C++ ARM64 build tools (Latest + Spectre) (X64 and ARM64)\n  - C++ core features\n  - C++ ATL for latest v143 tools (X64 and ARM64)\n  - C++ Clang compiler for Windows\n  - .NET desktop development\n  - .NET WinUI app development tools\n- Enable Developer Mode in Windows Settings OR run with Administrator privileges (required for symbolic link support)\n\n### Building WSL (Windows Only)\n1. Clone the repository\n2. Generate Visual Studio solution: `cmake .`\n3. Build: `cmake --build . -- -m` OR open `wsl.sln` in Visual Studio\n4. **NEVER CANCEL: Build takes 20-45 minutes on typical hardware. Set timeout to 60+ minutes.**\n\nBuild parameters:\n- `cmake . -A arm64` - Build for ARM64\n- `cmake . -DCMAKE_BUILD_TYPE=Release` - Release build  \n- `cmake . -DBUILD_BUNDLE=TRUE` - Build bundle msix package (requires ARM64 built first)\n\n### Deploying WSL (Windows Only)  \n- Install MSI: `bin\\<platform>\\<target>\\wsl.msi`\n- OR use script: `powershell tools\\deploy\\deploy-to-host.ps1`\n- For Hyper-V VM: `powershell tools\\deploy\\deploy-to-vm.ps1 -VmName <vm> -Username <user> -Password <pass>`\n\n## Cross-Platform Development Tasks\n\n### Documentation (Works on Linux/Windows)\n- Install tools: `pip install mkdocs-mermaid2-plugin mkdocs --break-system-packages`\n- Build docs: `mkdocs build -f doc/mkdocs.yml`\n- **Build time: ~0.5 seconds. Set timeout to 5+ minutes for safety.**\n- Output location: `doc/site/`\n- **Note**: May show warnings about mermaid CDN access on restricted networks\n\n### Code Formatting and Validation (Works on Linux/Windows)\n- Format check: `clang-format --dry-run --style=file <files>`\n- Apply formatting: `clang-format -i --style=file <files>`\n- Format all source: `powershell formatsource.ps1` (available at repo root after running `cmake .`)\n- Validate copyright headers: `python3 tools/devops/validate-copyright-headers.py`\n  - **Note**: Will report missing headers in generated/dependency files (_deps/), which is expected\n- Validate localization: `python3 tools/devops/validate-localization.py` \n  - **Note**: Only works after Windows build (requires localization/strings/en-us/Resources.resw)\n\n### Distribution Validation (Limited on Linux)\n- Validate distribution info: `python3 distributions/validate.py distributions/DistributionInfo.json`\n- **Note**: May fail on Linux due to network restrictions accessing distribution URLs\n\n## Testing\n\n### Unit Tests (Windows Only - TAEF Framework)\n\n**CRITICAL: ALWAYS build the ENTIRE project before running tests:**\n```powershell\n# Build everything first - this is required!\ncmake --build . -- -m\n\n# Then run tests\nbin\\<platform>\\<target>\\test.bat\n```\n\n**Why full build is required:**\n- Tests depend on multiple components (libwsl.dll, wsltests.dll, wslservice.exe, etc.)\n- Partial builds (e.g., only `configfile` or `wsltests`) will cause test failures\n- Changed components must be built together to ensure compatibility\n- **DO NOT skip the full build step even if only one file changed**\n\nTest execution:\n- Run all tests: `bin\\<platform>\\<target>\\test.bat`\n- **NEVER CANCEL: Full test suite takes 30-60 minutes. Set timeout to 90+ minutes.**\n- Run subset: `bin\\<platform>\\<target>\\test.bat /name:*UnitTest*`\n- Run specific test: `bin\\<platform>\\<target>\\test.bat /name:<class>::<test>`\n- WSL1 tests: Add `-Version 1` flag\n- Fast mode (after first run): Add `-f` flag (requires `wsl --set-default test_distro`)\n- **Requires Administrator privileges** - test.bat will fail without admin rights\n\nTest debugging:\n- Wait for debugger: `/waitfordebugger`\n- Break on failure: `/breakonfailure`\n- Run in-process: `/inproc`\n\n### Linux Unit Tests (Linux Only)\n- Location: `test/linux/unit_tests/`\n- Build script: `test/linux/unit_tests/build_tests.sh`\n- **Note**: Requires specific Linux build environment setup not covered in main build process\n\n## Validation Scenarios\n\n### Always Test These After Changes:\n1. **Documentation Build**: Run `mkdocs build -f doc/mkdocs.yml` and verify no errors\n2. **Code Formatting**: Run `clang-format --dry-run --style=file` on changed files\n3. **Windows Build** (if on Windows): Full cmake build cycle\n4. **Distribution Validation**: Run Python validation scripts on any distribution changes\n\n### Manual Validation Requirements\n- **Windows builds**: Install MSI and test basic WSL functionality (`wsl --version`, `wsl -l`)\n- **Documentation changes**: Review generated HTML in `doc/site/`\n- **Distribution changes**: Test with actual WSL distribution installation\n\n## Repository Navigation\n\n### Key Directories\n- `src/windows/` - Main Windows WSL service components\n- `src/linux/` - Linux-side WSL components  \n- `src/shared/` - Shared code between Windows and Linux\n- `test/windows/` - Windows-based tests (TAEF framework)\n- `test/linux/unit_tests/` - Linux unit test suite\n- `doc/` - Documentation source (MkDocs)\n- `tools/` - Build and deployment scripts\n- `distributions/` - Distribution validation and metadata\n\n### Key Files\n- `CMakeLists.txt` - Main build configuration\n- `doc/docs/dev-loop.md` - Developer build instructions\n- `test/README.md` - Testing framework documentation\n- `CONTRIBUTING.md` - Contribution guidelines\n- `.clang-format` - Code formatting rules\n- `UserConfig.cmake.sample` - Optional build customizations\n\n### Frequently Used Commands (Platform-Specific)\n\n#### Windows Development:\n```bash\n# Initial setup\ncmake .\ncmake --build . -- -m # 20-45 minutes, NEVER CANCEL\n\n# Deploy and test  \npowershell tools\\deploy\\deploy-to-host.ps1\nwsl --version\n\n# Run tests\nbin\\x64\\debug\\test.bat # 30-60 minutes, NEVER CANCEL\n```\n\n#### Cross-Platform Validation:\n```bash  \n# Documentation (0.5 seconds)\nmkdocs build -f doc/mkdocs.yml\n\n# Code formatting  \nfind src -name \"*.cpp\" -o -name \"*.h\" | xargs clang-format --dry-run --style=file\n\n# Copyright header validation (reports expected issues in _deps/)\npython3 tools/devops/validate-copyright-headers.py\n\n# Distribution validation (may fail on networks without external access)\npython3 distributions/validate.py distributions/DistributionInfo.json\n```\n\n## Debugging and Logging\n\n### ETL Tracing (Windows Only)\n```powershell\n# Collect traces\nwpr -start diagnostics\\wsl.wprp -filemode\n# [reproduce issue]  \nwpr -stop logs.ETL\n\n# Available profiles:\n# - WSL (default) - General WSL tracing\n# - WSL-Storage - Enhanced storage tracing\n# - WSL-Networking - Comprehensive networking tracing\n# - WSL-HvSocket - HvSocket-specific tracing\n# Example: wpr -start diagnostics\\wsl.wprp!WSL -filemode\n```\n\n### Log Analysis Tools\n- Use WPA (Windows Performance Analyzer) for ETL traces\n- Key providers: `Microsoft.Windows.Lxss.Manager`, `Microsoft.Windows.Subsystem.Lxss`\n\n### Debug Console (Linux)\nAdd to `%USERPROFILE%\\.wslconfig`:\n```ini\n[wsl2]\ndebugConsole=true\n```\n\n### Common Debugging Commands\n- Debug shell: `wsl --debug-shell`  \n- Collect WSL logs: `powershell diagnostics\\collect-wsl-logs.ps1`\n- Network logs: `powershell diagnostics\\collect-wsl-logs.ps1 -LogProfile networking`\n\n## Critical Timing and Timeout Guidelines\n\n**NEVER CANCEL these operations - always wait for completion:**\n\n- **Full Windows build**: 20-45 minutes (set timeout: 60+ minutes)\n- **Full test suite**: 30-60 minutes (set timeout: 90+ minutes)\n- **Unit test subset**: 5-15 minutes (set timeout: 30+ minutes)\n- **Documentation build**: ~0.5 seconds (set timeout: 5+ minutes)\n- **Distribution validation**: 2-5 minutes (set timeout: 15+ minutes)\n\n## CI/CD Integration\n\n### GitHub Actions\n- **distributions.yml**: Validates distribution metadata (Linux)\n- **documentation.yml**: Builds and deploys docs (Linux) \n- **modern-distributions.yml**: Tests modern distribution support\n\n### Pre-commit Validation\nAlways run before committing:\n1. `clang-format --dry-run --style=file` on changed C++ files\n2. `python3 tools/devops/validate-copyright-headers.py` (ignore _deps/ warnings)\n3. `mkdocs build -f doc/mkdocs.yml` if documentation changed\n4. Full Windows build if core components changed\n\n**Note**: The `.gitignore` file properly excludes build artifacts (*.sln, *.dll, *.pdb, obj/, bin/, etc.) - do not commit these files.\n\n## Development Environment Setup\n\n### Windows (Full Development)\n1. Install Visual Studio with required components (listed above)\n2. Install CMake 3.25+\n3. Enable Developer Mode\n4. Clone repository\n5. Run `cmake .` to generate solution\n\n### Linux (Documentation/Validation Only)  \n1. Install Python 3.8+\n2. Install clang-format\n3. Install docs tools: `pip install mkdocs-mermaid2-plugin mkdocs`\n4. Clone repository\n5. Run validation commands as needed\n\nRemember: **This is a Windows-focused project**. While some tasks can be performed on Linux, full WSL development requires Windows with Visual Studio."
  },
  {
    "path": ".github/policies/resourceManagement.yml",
    "content": "id: \r\nname: GitOps.PullRequestIssueManagement\r\ndescription: GitOps.PullRequestIssueManagement primitive\r\nowner: \r\nresource: repository\r\ndisabled: false\r\nwhere: \r\nconfiguration:\r\n  resourceManagementConfiguration:\r\n    scheduledSearches:\r\n    - description: \r\n      frequencies:\r\n      - hourly:\r\n          hour: 3\r\n      filters:\r\n      - isIssue\r\n      - isOpen\r\n      - hasLabel:\r\n          label: needs-author-feedback\r\n      - noActivitySince:\r\n          days: 7\r\n      actions:\r\n      - closeIssue\r\n      - addReply:\r\n          reply: >-\r\n            This issue has been automatically closed since it has not had any author activity for the past **7 days**. If you're still experiencing this issue please re-file it as a new issue. \r\n\r\n\r\n            Thank you!\r\n    - description: Close year old issues\r\n      frequencies:\r\n      - weekday:\r\n          day: Thursday\r\n          time: 12:00\r\n      filters:\r\n      - isIssue\r\n      - isOpen\r\n      - isNotLabeledWith:\r\n          label: feature\r\n      - noActivitySince:\r\n          days: 365\r\n      actions:\r\n      - closeIssue\r\n      - addReply:\r\n          reply: >-\r\n            This issue has been automatically closed since it has not had any activity for the past **year**. If you're still experiencing this issue please re-file this as a new issue or feature request. \r\n\r\n\r\n            Thank you!\r\n    eventResponderTasks:\r\n    - if:\r\n      - payloadType: Issue_Comment\r\n      - isAction:\r\n          action: Created\r\n      - isActivitySender:\r\n          issueAuthor: True\r\n      - hasLabel:\r\n          label: needs-author-feedback\r\n      then:\r\n      - removeLabel:\r\n          label: needs-author-feedback\r\n      description: \r\n    - if:\r\n      - payloadType: Issue_Comment\r\n      - commentContains:\r\n          pattern: '\\/dup(licate|e)?(\\s+of)?\\s+\\#[\\d]+'\r\n          isRegex: True\r\n      - or:\r\n        - activitySenderHasPermission:\r\n            permission: Admin\r\n        - activitySenderHasPermission:\r\n            permission: Write\r\n      then:\r\n      - addReply:\r\n          reply: >-\r\n            Hi! We've identified this issue as a duplicate of another one that already exists in this repository. This specific instance is being closed in favor of tracking the concern over on the referenced thread.\r\n\r\n\r\n            Thanks for your report!\r\n      - closeIssue\r\n      - addLabel:\r\n          label: duplicate\r\n      description: \r\n    - if:\r\n      - payloadType: Issue_Comment\r\n      - commentContains:\r\n          pattern: '\\/need-repro'\r\n          isRegex: True\r\n      - or:\r\n        - activitySenderHasPermission:\r\n            permission: Admin\r\n        - activitySenderHasPermission:\r\n            permission: Write\r\n      then:\r\n      - addReply:\r\n          reply: >-\r\n            We’ve labelled your issue as ‘need-repro’ since we need more steps to help identify your problem. \r\n\r\n\r\n            Could you please provide us with reproducible steps for the issue you’re experiencing, including things such as the specific command line steps necessary to reproduce the behavior and their output. \r\n\r\n\r\n            Thank you!\r\n      - addLabel:\r\n          label: need-repro\r\n      description: \r\n    - if:\r\n      - payloadType: Issue_Comment\r\n      then:\r\n      - cleanEmailReply\r\n      description: \r\n    - if:\r\n      - payloadType: Issue_Comment\r\n      - commentContains:\r\n          pattern: '\\/template'\r\n          isRegex: True\r\n      - or:\r\n        - activitySenderHasPermission:\r\n            permission: Admin\r\n        - activitySenderHasPermission:\r\n            permission: Write\r\n      then:\r\n      - closeIssue\r\n      - addReply:\r\n          reply: >-\r\n            Hi! We've closed your issue since you haven't used the template, or it is incomplete. Could you please provide all the requested info in the bug template and resubmit? Having that information will help us be able to accurately diagnose and resolve your issue.\r\n\r\n\r\n            Thank you!\r\n      - lockIssue\r\n      description: \r\n    - if:\r\n      - payloadType: Issue_Comment\r\n      - commentContains:\r\n          pattern: '\\/fixed'\r\n          isRegex: True\r\n      - or:\r\n        - activitySenderHasPermission:\r\n            permission: Admin\r\n        - activitySenderHasPermission:\r\n            permission: Write\r\n      then:\r\n      - closeIssue\r\n      - addLabel:\r\n          label: fixedininsiderbuilds\r\n      - addReply:\r\n          reply: >-\r\n            This bug or feature request originally submitted has been addressed in whole or in part. Related or ongoing bug or feature gaps should be opened as a new issue submission if one does not already exist. \r\n\r\n\r\n            Thank you!\r\n      - removeLabel:\r\n          label: fixinbound\r\n      description: \r\n    - if:\r\n      - payloadType: Issue_Comment\r\n      - commentContains:\r\n          pattern: '\\/logs?'\r\n          isRegex: True\r\n      - or:\r\n        - activitySenderHasPermission:\r\n            permission: Admin\r\n        - activitySenderHasPermission:\r\n            permission: Write\r\n      then:\r\n      - addReply:\r\n          reply: >-\r\n            Hello! Could you please provide more logs to help us better diagnose your issue? \r\n\r\n\r\n            To collect WSL logs, download and execute [collect-wsl-logs.ps1](https://github.com/Microsoft/WSL/blob/master/diagnostics/collect-wsl-logs.ps1) in an **administrative powershell prompt**:\r\n\r\n\r\n            ```\r\n\r\n            Invoke-WebRequest -UseBasicParsing \"https://raw.githubusercontent.com/microsoft/WSL/master/diagnostics/collect-wsl-logs.ps1\" -OutFile collect-wsl-logs.ps1\r\n\r\n            Set-ExecutionPolicy Bypass -Scope Process -Force\r\n\r\n            .\\collect-wsl-logs.ps1\r\n\r\n            ```\r\n\r\n            The script will output the path of the log file once done. \r\n\r\n\r\n            Once completed please upload the output files to this GitHub issue.\r\n\r\n\r\n            See [Collect WSL logs (recommended method)](https://github.com/microsoft/WSL/blob/master/CONTRIBUTING.md#8-collect-wsl-logs-recommended-method).\r\n\r\n\r\n            If you choose to email these logs instead of attaching them to the bug, please send them to wsl-gh-logs@microsoft.com with the GitHub issue number in the subject, and include a link to your GitHub issue comment in the message body.\r\n\r\n\r\n            Thank you!\r\n      - addLabel:\r\n          label: needs-author-feedback\r\n      description: \r\n    - if:\r\n      - payloadType: Issue_Comment\r\n      - commentContains:\r\n          pattern: '\\/dump?'\r\n          isRegex: True\r\n      - or:\r\n        - activitySenderHasPermission:\r\n            permission: Admin\r\n        - activitySenderHasPermission:\r\n            permission: Write\r\n      then:\r\n      - addReply:\r\n          reply: >-\r\n            Hello! Could you please provide logs and process dumps to help us better diagnose your issue? \r\n\r\n\r\n            To collect WSL logs and dumps, download and execute [collect-wsl-logs.ps1](https://github.com/Microsoft/WSL/blob/master/diagnostics/collect-wsl-logs.ps1) in an **administrative powershell prompt**:\r\n\r\n\r\n            ```\r\n\r\n            Invoke-WebRequest -UseBasicParsing \"https://raw.githubusercontent.com/microsoft/WSL/master/diagnostics/collect-wsl-logs.ps1\" -OutFile collect-wsl-logs.ps1\r\n\r\n            Set-ExecutionPolicy Bypass -Scope Process -Force\r\n\r\n            .\\collect-wsl-logs.ps1 -Dump\r\n\r\n            ```\r\n\r\n            The script will output the path of the log file once done. \r\n\r\n\r\n            Once completed please upload the output files to this GitHub issue.\r\n\r\n\r\n            See [Collect WSL logs (recommended method)](https://github.com/microsoft/WSL/blob/master/CONTRIBUTING.md#8-collect-wsl-logs-recommended-method).\r\n\r\n\r\n            Thank you!\r\n      - addLabel:\r\n          label: needs-author-feedback\r\n      description: \r\n    - if:\r\n      - payloadType: Issue_Comment\r\n      - commentContains:\r\n          pattern: '\\/bsod?'\r\n          isRegex: True\r\n      - or:\r\n        - activitySenderHasPermission:\r\n            permission: Admin\r\n        - activitySenderHasPermission:\r\n            permission: Write\r\n      then:\r\n      - addReply:\r\n          reply: >-\r\n            Hello! Could you please provide a kernel dump to help us better diagnose your issue? \r\n            To collect a kernel dump, follow [10) Reporting a Windows crash (BSOD)](https://github.com/microsoft/WSL/blob/master/CONTRIBUTING.md#10-reporting-a-windows-crash-bsod)\r\n\r\n            Thank you!\r\n      - addLabel:\r\n          label: needs-author-feedback\r\n      description:\r\nonFailure: \r\nonSuccess: \r\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->\n## Summary of the Pull Request\n\n<!-- Please review the items on the PR checklist before submitting-->\n## PR Checklist\n\n- [ ] **Closes:** Link to issue #xxx\n- [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected\n- [ ] **Tests:** Added/updated if needed and all pass\n- [ ] **Localization:** All end user facing strings can be localized\n- [ ] **Dev docs:** Added/updated if needed\n- [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/wsl/) and link it here: #xxx\n\n<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->\n## Detailed Description of the Pull Request / Additional comments\n\n<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->\n## Validation Steps Performed\n"
  },
  {
    "path": ".github/workflows/distributions.yml",
    "content": "name: Validate distributions\n\non: \n  pull_request: \n    paths: ['distributions/**']\n\njobs:\n  check:\n    name: Validate distributions\n    runs-on: ubuntu-latest\n    steps:\n        - name: Checkout repo\n          uses: actions/checkout@v4\n          \n        - name: Run validation\n          run: python distributions/validate.py distributions/DistributionInfo.json\n          shell: bash\n"
  },
  {
    "path": ".github/workflows/documentation.yml",
    "content": "name: Build documentation\non:\n  workflow_dispatch:\n  push:\n    branches: [main, master]\n\njobs:\n  DeployDocs:\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    runs-on: ubuntu-latest\n    permissions:\n        contents: read\n        pages: write\n        id-token: write\n    steps:\n      - name: Checkout actions\n        uses: actions/checkout@v4\n\n      - name: Install packages\n        run: pip install mkdocs-mermaid2-plugin mkdocs --break-system-packages\n        shell: bash\n\n      - name: Build documentation\n        run:  mkdocs build -f doc/mkdocs.yml\n        shell: bash\n\n      - uses: actions/upload-pages-artifact@v4\n        with:\n          path: doc/site\n\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4"
  },
  {
    "path": ".github/workflows/issue_edited.yml",
    "content": "name: Process edited issue\r\n\r\non:\r\n  workflow_dispatch:\r\n  issues:\r\n    types: [edited]\r\n\r\njobs:\r\n  wti:\r\n    name: Run wti\r\n    runs-on: windows-2022\r\n    permissions:\r\n      issues: write\r\n    steps: \r\n    - name: Checkout repo\r\n      uses: actions/checkout@v4\r\n\r\n    - uses: ./.github/actions/triage\r\n      with: \r\n        issue: \"${{ github.event.issue.number }}\"\r\n        previous_body: \"${{ github.event.changes.body.from }}\"\r\n        token: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".github/workflows/modern-distributions.yml",
    "content": "name: Validate tar based distributions\n\non:\n  pull_request:\n    paths: ['distributions/**']\n\njobs:\n  check:\n    name: Validate tar based distributions changes\n    runs-on: ubuntu-latest\n    steps:\n        - name: Checkout repo\n          uses: actions/checkout@v4\n          with:\n            fetch-depth: 0\n\n        - name: Install pip packages\n          run: pip install -r distributions/requirements.txt\n          shell: bash\n\n        - name: Run validation\n          run: | \n            python distributions/validate-modern.py \\\n            --repo-path . \\\n            --compare-with-branch 'origin/${{ github.base_ref }}' \\\n            --manifest distributions/DistributionInfo.json \\\n\n          shell: bash\n"
  },
  {
    "path": ".github/workflows/new_issue.yml",
    "content": "name: Process new issue\n\non:\n  workflow_dispatch :\n  issues:\n    types: [opened]\n\njobs:\n  wti:\n    name: Run wti\n    runs-on: windows-2022\n    permissions:\n      issues: write\n    steps: \n    - name: Checkout repo\n      uses: actions/checkout@v4\n    \n    - uses: ./.github/actions/triage\n      with: \n        issue: \"${{ github.event.issue.number }}\"\n        token: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".github/workflows/new_issue_comment.yml",
    "content": "name: Process new comment on issue\r\n\r\non: \r\n  workflow_dispatch :\r\n  issue_comment:\r\n    types: [created]\r\n\r\njobs:\r\n  wti:\r\n    name: Run wti\r\n    runs-on: windows-2022\r\n    permissions:\r\n      issues: write\r\n    if: ${{ !github.event.issue.pull_request && github.event.issue.user.id == github.event.comment.user.id }}\r\n    steps: \r\n        - name: Checkout repo\r\n          uses: actions/checkout@v4\r\n        \r\n        - uses: ./.github/actions/triage\r\n          with: \r\n              issue: '${{ github.event.issue.number }}'\r\n              comment: '${{ github.event.comment.id }}'\r\n              token: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".github/workflows/winget.yml",
    "content": "name: Publish to WinGet\n\non:\n  release:\n    types: [released]\n\njobs:\n  publish:\n    if: github.event.release.prerelease == false\n    runs-on: windows-latest # Action can only run on Windows\n    steps:\n      - name: Publish WSL\n        run: |\n\n          Set-StrictMode -Version Latest\n          $ErrorActionPreference = \"Stop\"\n\n          $assets = '${{ toJSON(github.event.release.assets) }}' | ConvertFrom-Json\n          $wingetRelevantAssetx64 = $assets | Where-Object { $_.name -like '*x64.msi' } | Select-Object -First 1\n          $wingetRelevantAssetARM64 = $assets | Where-Object { $_.name -like '*arm64.msi' } | Select-Object -First 1\n          \n          $version = \"${{ github.event.release.tag_name }}\"\n\n          $wingetx64URL = $wingetRelevantAssetx64.browser_download_url\n          $wingetARM64URL = $wingetRelevantAssetARM64.browser_download_url\n\n          $wingetPackageId = \"Microsoft.WSL\"\n\n          & curl.exe -JLO https://aka.ms/wingetcreate/latest\n          & .\\wingetcreate.exe update $wingetPackageId -s -v $version -u \"$wingetx64URL|x64\" \"$wingetARM64URL|arm64\" -t \"${{ secrets.WINGET_TOKEN }}\"\n"
  },
  {
    "path": ".gitignore",
    "content": ".vscode/*\n.vs/\n!vendor/.preserve\nout\ntmp\n/.vs/\n/.vscode/\n*.sln\n*.slnx\n*.user\n*.csproj\n*.vcxproj\n*.filters\n*.pdb\n*.lib\n*.dll\nobj/\nDebug/\nRelease/\nProperties/\n/_deps/\n/package/Strings/en-US/\n/packages/\nCMakeFiles/\nCMakeCache.txt\n*.msix\ncmake_install.cmake\nbuild_tools/\n*_i.c\n*_p.c\n*_p.c\nwslsupport/wslsupport.h\ndlldata.c\n.gdbinit\nllvm/\n*.a\n*.so\n*.o\nlinux/init/init\nlinux/init\ninitrd/init\nbin/\n*.nupkg\ngenerated/\n*.nuspec\ntest/linux/unit_tests/wsl_unit_tests\n*.dir/\nUserConfig.cmake\n*.wix\n*.wixobj\n*.wixpdb\ntest.bat\nAppxManifest.xml\npackage_layout/\npriconf.xml\nresources.map.txt\nresources.pri\nmsi-install-*.txt\nkernellogs.txt\nFormatSource.ps1\nmsixinstaller/x64\npackage/x64\n/appx-logs.txt\ntools/clang-format.exe\n/linux-crashes\ndoc/site/\ndirectory.build.targets\ntest-storage/\n*.vhdx"
  },
  {
    "path": ".pipelines/build-stage.yml",
    "content": "parameters:\n  - name: isRelease\n    type: boolean\n    default: true\n\n  - name: packageVersion\n    type: string\n    default: \"\"\n\n  - name: isNightly\n    type: boolean\n    default: false\n\n  - name: nugetPackages\n    type: object\n    default:\n      - Microsoft.WSL.PluginApi.nuspec\n\n  - name: traceLoggingConfig\n    type: string\n    default: ''\n\n  - name: targets\n    type: object\n    default:\n      - target: \"wsl;libwsl;wslg;wslservice;wslhost;wslrelay;wslinstaller;wslinstall;initramfs;wslserviceproxystub;wslsettings;wslinstallerproxystub;testplugin\"\n        pattern: \"wsl.exe,libwsl.dll,wslg.exe,wslservice.exe,wslhost.exe,wslrelay.exe,wslinstaller.exe,wslinstall.dll,wslserviceproxystub.dll,wslsettings/wslsettings.dll,wslsettings/wslsettings.exe,wslinstallerproxystub.dll,wsldevicehost.dll,WSLDVCPlugin.dll,testplugin.dll,wsldeps.dll\"\n      - target: \"msixgluepackage\"\n        pattern: \"gluepackage.msix\"\n      - target: \"msipackage\"\n        pattern: \"wsl.msi\"\n\n  - name: platforms\n    type: object\n    default:\n      - x64\n      - arm64\n\n  - name: pool\n    type: string\n    default: ''\n\n  - name: vsoOrg\n    type: string\n\n  - name: vsoProject\n    type: string\n\n  - name: esrp\n    type: object\n    default:\n      ConnectedServiceName: \"AzureConnection-AME\"\n      signConfigType: \"inlineSignParams\"\n      SessionTimeout: 60\n      MaxConcurrency: 50\n      MaxRetryAttempts: 5\n      ServiceEndpointUrl: $(EsrpServiceEndpointUrl)\n      AuthAKVName: $(EsrpAuthAKVName)\n      AuthSignCertName: $(EsrpAuthSignCertName)\n      AppRegistrationClientId: $(EsrpAppRegistrationClientId)\n      AppRegistrationTenantId: $(EsrpAppRegistrationTenantId)\n      EsrpClientId: $(EsrpClientId)\n\nstages:\n  - stage: build\n    jobs:\n      - job:\n\n        displayName: \"Formatting & localization checks\"\n        timeoutInMinutes: 30\n\n        variables:\n          ob_outputDirectory: '$(Build.SourcesDirectory)\\out'\n\n        ${{ if eq(parameters.pool, '') }}:\n          pool: {'type': 'windows'}\n\n        ${{ else }}:\n          pool: ${{ parameters.pool }}\n\n        steps:\n          - script: python tools/devops/validate-localization.py localization/strings en-US\n            displayName: Validate localization resources\n\n          - script: python tools\\devops\\validate-copyright-headers.py src\n            displayName: Validate copyright headers (src/)\n\n          - script: python tools\\devops\\validate-copyright-headers.py test\n            displayName: Validate copyright headers (test/)\n\n          - task: CMake@1\n            displayName: \"CMake .\"\n            inputs:\n              workingDirectory: \".\"\n              cmakeArgs: .\n\n          - task: PowerShell@2\n            inputs:\n              targetType: \"filePath\"\n              filePath: FormatSource.ps1\n              arguments: \"-ModifiedOnly $false -Verify $true\"\n            displayName: \"Clang-format check\"\n\n      - job: build\n        displayName: \"Build WSL package\"\n        timeoutInMinutes: 120\n\n        ${{ if eq(parameters.pool, '') }}:\n          pool: {'type': 'windows'}\n\n        ${{ else }}:\n          pool: ${{ parameters.pool }}\n\n        variables:\n          NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS: 60\n          NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS: 60\n          ob_outputDirectory: '$(Build.SourcesDirectory)\\out'\n          ob_artifactBaseName: 'drop_wsl'\n          ob_artifactSuffix: '_build'\n          ob_sdl_codeSignValidation_excludes: -|**testbin\\**\n          Codeql.PublishDatabaseLog: true\n          Codeql.SourceRoot: src\n          packageStagingDir: '$(Build.SourcesDirectory)\\packageStagingDir'\n          ${{ if eq(parameters.isRelease, 'true') }}:\n            packageInputDirArg: '-DPACKAGE_INPUT_DIR=$(packageStagingDir)'\n          ${{ else }}:\n            packageInputDirArg: ''\n\n        steps:\n\n          - task: CodeQL3000Init@0\n            inputs:\n              Enabled: ${{ and(parameters.isNightly, eq(variables['Build.SourceBranch'], 'refs/heads/main'))}}\n\n          - task: UseDotNet@2\n            displayName: Install .NET Core SDK (required by EsrpCodeSigning)\n            condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))\n            inputs:\n              packageType: \"sdk\"\n\n          - task: PowerShell@2\n            displayName: Set trace logging configuration\n            condition: ne('${{ parameters.traceLoggingConfig }}', '')\n            inputs:\n                targetType: 'inline'\n                script: 'Set-Content -Path src/windows/inc/traceloggingconfig.h -Value $env:config.replace(\"\\n\", \"`n\")'\n            env:\n                config: '${{ parameters.traceLoggingConfig }}'\n\n          - task: PowerShell@2\n            displayName: \"Compute package version\"\n            name: version\n            inputs:\n              targetType: inline\n              ${{ if eq(parameters.packageVersion, '') }}:\n                script: |\n                  $gitversion_version = (Select-Xml -Path packages.config  -XPath '/packages/package[@id=''GitVersion.CommandLine'']/@version').Node.Value\n                  $env:path = \"packages/GitVersion.CommandLine.$gitversion_version/tools;$env:path\"\n                  . .\\tools\\devops\\version_functions.ps1\n                  $version = Get-VersionInfo -Nightly $${{ parameters.isNightly }}\n                  Write-Host \"##vso[task.setvariable variable=WSL_PACKAGE_VERSION;isOutput=true]$($version.MsixVersion)\"\n                  Write-Host \"##vso[task.setvariable variable=WSL_NUGET_PACKAGE_VERSION;isOutput=true]$($version.NugetVersion)\"\n\n              ${{ else }}:\n                script: |\n                  Write-Host \"##vso[task.setvariable variable=WSL_PACKAGE_VERSION;isOutput=true]$([string]('${{ parameters.packageVersion }}' + '.0'))\"\n                  Write-Host \"##vso[task.setvariable variable=WSL_NUGET_PACKAGE_VERSION;isOutput=true]$([string]('${{ parameters.packageVersion }}'))\"\n\n          - ${{ each platform in parameters.platforms }}:\n              - task: CMake@1\n                displayName: \"CMake ${{ platform }}\"\n                inputs:\n                  workingDirectory: \".\"\n                  cmakeArgs: . --fresh -A ${{ platform }} -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_VERSION=10.0.26100.0 -DPACKAGE_VERSION=$(version.WSL_PACKAGE_VERSION) -DWSL_NUGET_PACKAGE_VERSION=$(version.WSL_NUGET_PACKAGE_VERSION) -DSKIP_PACKAGE_SIGNING=${{ parameters.isRelease }} -DOFFICIAL_BUILD=${{ parameters.isRelease }} -DPIPELINE_BUILD_ID=$(Build.BuildId) -DVSO_ORG=${{ parameters.vsoOrg }} -DVSO_PROJECT=${{ parameters.vsoProject }} -DWSL_BUILD_WSL_SETTINGS=true $(packageInputDirArg)\\${{ platform }}\n\n              # This additional Restore NuGet package task is added as a workaround for WSL Settings to have its packages restored properly.\n              # Without this, building wsl settings may encounter the following error:\n              #\n              # The plugin credential provider could not acquire credentials. Authentication may require manual action.\n              # Consider re-running the command with --interactive for `dotnet`, /p:NuGetInteractive=\"true\" for MSBuild or removing the -NonInteractive switch for `NuGet`\n              # Response status code does not indicate success: 401 (Unauthorized)\n              - script: _deps\\nuget.exe restore -NonInteractive\n\n              - ${{ each target in parameters.targets }}:\n                  - script: cmake --build . --config Release --target ${{ target.target }} -- -m\n                    condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))\n                    displayName: \"Build ${{ target.target }} (${{ platform }})\"\n\n                  - ${{ if eq(parameters.isRelease, 'true') }}:\n                      - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5\n                        displayName: \"Sign ${{ target.target }} (${{ platform }})\"\n                        condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))\n                        inputs:\n                          ConnectedServiceName: ${{ parameters.esrp.ConnectedServiceName}}\n                          signConfigType: ${{ parameters.esrp.signConfigType }}\n                          SessionTimeout: ${{ parameters.esrp.SessionTimeout }}\n                          MaxConcurrency: ${{ parameters.esrp.MaxConcurrency }}\n                          MaxRetryAttempts: ${{ parameters.esrp.MaxRetryAttempts }}\n                          ServiceEndpointUrl: ${{ parameters.esrp.ServiceEndpointUrl }}\n                          AuthAKVName: ${{ parameters.esrp.AuthAKVName }}\n                          AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}\n                          AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}\n                          AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}\n                          FolderPath: \"bin\\\\${{ platform }}\\\\Release\"\n                          Pattern: \"${{ target.pattern }}\"\n                          UseMSIAuthentication: true\n                          EsrpClientId: ${{ parameters.esrp.EsrpClientId }}\n                          inlineOperation: |\n                            [\n                                  {\n                                      \"KeyCode\": \"CP-230012\",\n                                      \"OperationCode\": \"SigntoolSign\",\n                                      \"Parameters\" : {\n                                            \"OpusName\" : \"Microsoft\",\n                                            \"OpusInfo\" : \"http://www.microsoft.com\",\n                                            \"FileDigest\" : \"/fd \\\"SHA256\\\"\",\n                                            \"PageHash\" : \"/NPH\",\n                                            \"TimeStamp\" : \"/tr \\\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\\" /td sha256\"\n                                        },\n                                        \"ToolName\" : \"sign\",\n                                        \"ToolVersion\" : \"1.0\"\n                                    },\n                                    {\n                                        \"KeyCode\" : \"CP-230012\",\n                                        \"OperationCode\" : \"SigntoolVerify\",\n                                        \"Parameters\" : {},\n                                        \"ToolName\" : \"sign\",\n                                        \"ToolVersion\" : \"1.0\"\n                                    }\n                              ]\n\n                      - task: PowerShell@2\n                        displayName: \"Copy signed ${{ target.target }} to staging (${{ platform }})\"\n                        condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))\n                        inputs:\n                          targetType: inline\n                          script: |\n                              $arch = '${{ platform }}'\n                              $pattern = '${{ target.pattern }}'\n                              $inputDir = \"bin\\$arch\\Release\"\n                              $outputDir = \"$(packageStagingDir)\\$arch\"\n                              New-Item -ItemType Directory -Path \"$outputDir\\wslsettings\" -Force\n                              foreach ($file in $pattern.Split(',')) {\n                                  $sourcePath = Join-Path $inputDir $file\n                                  if (Test-Path $sourcePath) {\n                                      $destPath = Join-Path $outputDir $file\n                                      Write-Host \"Copying signed file: $sourcePath -> $destPath\"\n                                      Copy-Item -Path $sourcePath -Destination $destPath -Force\n                                  } else {\n                                      Write-Warning \"File not found: $sourcePath\"\n                                  }\n                              }\n\n              - script: cmake --build . --config Release -- -m\n                displayName: \"Build installer msix and tests (${{ platform }})\"\n\n              - task: PowerShell@2\n                displayName: \"Move ${{ platform }} installer msi to output directory\"\n                inputs:\n                  targetType: inline\n                  script: |\n                      New-Item -ItemType Directory -Path \"$(ob_outputDirectory)\\bundle\" -Force\n                      $arch = '${{ platform }}'\n                      Copy-Item -Path \"bin\\$arch\\release\\wsl.msi\" -Destination \"$(ob_outputDirectory)\\bundle\\wsl.$(version.WSL_PACKAGE_VERSION).$arch.msi\"\n\n          - ${{ if eq(parameters.isRelease, 'true') }}:\n              - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5\n                displayName: \"Sign the bundle\"\n                condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))\n                inputs:\n                  ConnectedServiceName: ${{ parameters.esrp.ConnectedServiceName}}\n                  signConfigType: ${{ parameters.esrp.signConfigType }}\n                  SessionTimeout: ${{ parameters.esrp.SessionTimeout }}\n                  MaxConcurrency: ${{ parameters.esrp.MaxConcurrency }}\n                  MaxRetryAttempts: ${{ parameters.esrp.MaxRetryAttempts }}\n                  ServiceEndpointUrl: ${{ parameters.esrp.ServiceEndpointUrl }}\n                  AuthAKVName: ${{ parameters.esrp.AuthAKVName }}\n                  AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}\n                  AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}\n                  AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}\n                  FolderPath: \"bundle\"\n                  Pattern: \"*.msixbundle\"\n                  UseMSIAuthentication: true\n                  EsrpClientId: ${{ parameters.esrp.EsrpClientId }}\n                  inlineOperation: |\n                    [\n                          {\n                              \"KeyCode\": \"CP-230012\",\n                              \"OperationCode\": \"SigntoolSign\",\n                              \"Parameters\" : {\n                                    \"OpusName\" : \"Microsoft\",\n                                    \"OpusInfo\" : \"http://www.microsoft.com\",\n                                    \"FileDigest\" : \"/fd \\\"SHA256\\\"\",\n                                    \"PageHash\" : \"/NPH\",\n                                    \"TimeStamp\" : \"/tr \\\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\\" /td sha256\"\n                                },\n                                \"ToolName\" : \"sign\",\n                                \"ToolVersion\" : \"1.0\"\n                            },\n                            {\n                                \"KeyCode\" : \"CP-230012\",\n                                \"OperationCode\" : \"SigntoolVerify\",\n                                \"Parameters\" : {},\n                                \"ToolName\" : \"sign\",\n                                \"ToolVersion\" : \"1.0\"\n                            }\n                      ]\n\n          - script: md.exe $(ob_outputDirectory)\\bin\\nuget\n            displayName: \"Create the nuget directory\"\n\n          - ${{ each package in parameters.nugetPackages }}:\n              - script: nuget.exe pack ${{ package }} -OutputDirectory $(ob_outputDirectory)\\bin\\nuget -NonInteractive\n                displayName: Build ${{ package }}\n\n          - ${{ if eq(parameters.isRelease, 'true') }}:\n              - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5\n                displayName: \"Sign nuget packages\"\n                condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))\n                inputs:\n                  ConnectedServiceName: ${{ parameters.esrp.ConnectedServiceName}}\n                  signConfigType: ${{ parameters.esrp.signConfigType }}\n                  SessionTimeout: ${{ parameters.esrp.SessionTimeout }}\n                  MaxConcurrency: ${{ parameters.esrp.MaxConcurrency }}\n                  MaxRetryAttempts: ${{ parameters.esrp.MaxRetryAttempts }}\n                  ServiceEndpointUrl: ${{ parameters.esrp.ServiceEndpointUrl }}\n                  AuthAKVName: ${{ parameters.esrp.AuthAKVName }}\n                  AuthSignCertName: ${{ parameters.esrp.AuthSignCertName }}\n                  AppRegistrationClientId: ${{ parameters.esrp.AppRegistrationClientId }}\n                  AppRegistrationTenantId: ${{ parameters.esrp.AppRegistrationTenantId }}\n                  FolderPath: '$(ob_outputDirectory)\\bin\\nuget'\n                  Pattern: \"*.nupkg\"\n                  UseMSIAuthentication: true\n                  EsrpClientId: ${{ parameters.esrp.EsrpClientId }}\n                  inlineOperation: |\n                    [\n                        {\n                            \"KeyCode\": \"CP-401405\",\n                            \"OperationCode\": \"NuGetSign\",\n                            \"Parameters\" : {},\n                              \"ToolName\" : \"sign\",\n                              \"ToolVersion\" : \"1.0\"\n                          },\n                          {\n                              \"KeyCode\" : \"CP-401405\",\n                              \"OperationCode\" : \"NuGetVerify\",\n                              \"Parameters\" : {},\n                              \"ToolName\" : \"sign\",\n                              \"ToolVersion\" : \"1.0\"\n                          }\n                    ]\n\n          - powershell: |\n              foreach ($arch in @(\"x64\", \"ARM64\"))\n              {\n                  $binFolder = \".\\bin\\$arch\\Release\"\n                  $pdbFolder = Join-Path $(ob_outputDirectory) \"pdb\\$arch\\Release\"\n                  mkdir $pdbFolder\n                  foreach ($filter in @(\"*.pdb\", \"*.debug\"))\n                  {\n                      foreach ($e in (Get-ChildItem -Recurse -Path $binFolder -Filter $filter)) {Move-Item -Path $e.fullname -Destination (Join-Path $pdbFolder $e.name)}\n                  }\n              }\n\n            displayName: Collect symbols\n\n          - powershell: |\n              mkdir appxsym\n              foreach ($arch in @(\"x64\", \"ARM64\"))\n              {\n                  Get-ChildItem -Path $(ob_outputDirectory)\\pdb\\$arch\\release\\*.pdb -Exclude wsltests.pdb | Compress-Archive -DestinationPath appxsym/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_$arch.zip\n                  Copy-Item -Path appxsym/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_$arch.zip -Destination appxsym/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_$arch.appxsym\n              }\n              mkdir $(ob_outputDirectory)/appxupload\n              Get-ChildItem -Path appxsym/*.appxsym,bundle/release/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_x64_ARM64.msixbundle | Compress-Archive -DestinationPath $(ob_outputDirectory)/appxupload/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_x64_ARM64.zip\n              Move-Item -Path $(ob_outputDirectory)/appxupload/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_x64_ARM64.zip -Destination $(ob_outputDirectory)/appxupload/Microsoft.WSL_$(version.WSL_PACKAGE_VERSION)_x64_ARM64.appxupload\n              rm appxsym/*.appxsym\n\n            displayName: Create appxupload\n            condition: and(succeeded(), eq('${{ parameters.isRelease }}', true))\n\n          - powershell: |\n              $taefVersion = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''Microsoft.Taef'']/@version').Node.Value\n              New-Item -ItemType Directory -Path \"$(ob_outputDirectory)\\bundle\" -Force\n              foreach ($arch in @(\"x64\", \"ARM64\"))\n              {\n                mkdir $(ob_outputDirectory)\\testbin\\$arch\\release\n\n                Move-Item -Path \"bin\\$arch\\release\\wsltests.dll\" -Destination \"$(ob_outputDirectory)\\testbin\\$arch\\release\\wsltests.dll\"\n                Move-Item -Path \"bin\\$arch\\release\\testplugin.dll\" -Destination \"$(ob_outputDirectory)\\testbin\\$arch\\release\\testplugin.dll\"\n                Move-Item -Path \"packages\\Microsoft.Taef.$taefVersion\\build\\Binaries\\$arch\" -Destination \"$(ob_outputDirectory)\\testbin\\$arch\\release\\taef\"\n              }\n\n              Move-Item -Path \"bin\\x64\\cloudtest\" -Destination \"$(ob_outputDirectory)\\testbin\\x64\\cloudtest\"\n              Move-Item -Path \"tools\\test\\test-setup.ps1\" -Destination \"$(ob_outputDirectory)\\testbin\\test-setup.ps1\"\n              Move-Item -Path \"tools\\test\\CloudTest-Setup.bat\" -Destination \"$(ob_outputDirectory)\\testbin\\CloudTest-Setup.bat\"\n              Move-Item -Path \"diagnostics\\wsl.wprp\" -Destination \"$(ob_outputDirectory)\\testbin\\wsl.wprp\"\n              Move-Item -Path \"test\\linux\\unit_tests\" -Destination \"$(ob_outputDirectory)\\testbin\\unit_tests\"\n\n              Move-Item -Path bundle\\release\\* -Destination $(ob_outputDirectory)\\bundle\n              $TestDistroVersion = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''Microsoft.WSL.TestDistro'']/@version').Node.Value\n              Copy-Item \"packages\\Microsoft.WSL.TestDistro.$TestDistroVersion\\test_distro.tar.xz\" \"$(ob_outputDirectory)\\testbin\\x64\"\n\n            displayName: Move artifacts to drop directory\n\n          - task: PublishSymbols@2\n            displayName: Publish symbols\n            inputs:\n              SymbolServerType: \"TeamServices\"\n              TreatNotIndexedAsWarning: true\n              SymbolsProduct: WSL\n              SymbolsVersion: $(version.WSL_PACKAGE_VERSION)\n              SearchPattern: |\n                $(ob_outputDirectory)/pdb/**/*.pdb\n                $(ob_outputDirectory)/bin/**/*.exe\n                $(ob_outputDirectory)/bin/**/*.dll\n\n          - ${{ if ne(parameters.pool, '') }}:\n            - task: PublishPipelineArtifact@1\n              inputs:\n                targetPath: $(ob_outputDirectory)\n                artifactName: $(ob_artifactBaseName)$(ob_artifactSuffix)\n\n          - task: CodeQL3000Finalize@0\n            condition: ${{ and(parameters.isNightly, eq(variables['Build.SourceBranch'], 'refs/heads/main'))}}\n\n"
  },
  {
    "path": ".pipelines/flight-stage.yml",
    "content": "parameters:\n- name: publishPackage\n  type: boolean\n  default: false\n\n- name: packageVersion\n  type: string\n  default: ''\n\n- name: bypassTests\n  type: boolean\n  default: false\n\nstages:\n  - stage: flight\n    ${{ if eq(parameters.bypassTests, true) }}:\n        dependsOn: [build]\n    ${{ else }}:\n        dependsOn: [test]\n\n    jobs:\n    - job: flight\n      displayName: 'Package and Flight WSL package'\n\n      dependsOn: [] # The stage handles this dependency\n      pool:\n        type: windows\n      variables:\n        AppId: 9P9TQF7MRM4R\n        FlightId: $(StoreBrokerFlightId)\n        StoreBrokerPath: $(Build.SourcesDirectory)\\storebroker # location of StoreBroker information in the repo\n        StoreBrokerPayloadPath: $(Build.ArtifactStagingDirectory)\\StoreBrokerPayload # location of package created for flight\n        WSL_PACKAGE_VERSION: $[ dependencies.build.outputs['version.WSL_PACKAGE_VERSION'] ]\n        ob_outputDirectory: '$(Build.SourcesDirectory)\\out'\n        ob_artifactBaseName: 'drop_wsl'\n        ob_artifactSuffix: '_flight'\n        ob_sdl_checkcflags_enabled : false # Disable the CFLAGS check since we're not actually building anything in this stage\n\n      steps:\n\n      # Source: https://learn.microsoft.com/azure/devops/pipelines/build/run-retention?view=azure-devops&tabs=powershell\n      - task: PowerShell@2\n        condition: and(succeeded(), not(canceled()))\n        displayName: Retain this build\n        inputs:\n          failOnStderr: true\n          targetType: 'inline'\n          script: |\n            $contentType = \"application/json\";\n            $headers = @{ Authorization = 'Bearer $(System.AccessToken)' };\n            $rawRequest = @{ daysValid = 365 * 2; definitionId = $(System.DefinitionId); ownerId = 'User:$(Build.RequestedForId)'; protectPipeline = $false; runId = $(Build.BuildId) };\n            $request = ConvertTo-Json @($rawRequest);\n            $uri = \"$(System.CollectionUri)$(System.TeamProject)/_apis/build/retention/leases?api-version=6.0-preview.1\";\n            Invoke-RestMethod -uri $uri -method POST -Headers $headers -ContentType $contentType -Body $request;\n\n      # Download the build drop\n      - task: DownloadPipelineArtifact@2\n        displayName: Download Bundle artifact\n        inputs:\n          artifact: \"drop_wsl_build\"\n          path: drop\n\n      # copy the appxupload folder to the storebroker folder\n      - powershell: |\n                    mkdir $(StoreBrokerPath)\\appxpackage\\\n                    Copy-Item -Path drop\\appxupload\\* -Destination $(StoreBrokerPath)\\appxpackage\\ -Recurse -Force\n        displayName: Copy AppxUpload artifact\n\n      # creates a submission package that is published to the store; containers store page information and the WSL appxupload\n      - task: MS-RDX-MRO.windows-store-publish.package-task.store-package@3\n        displayName: 'Creating StoreBroker Payload'\n        inputs:\n          serviceEndpoint: 'AzureConnection-StoreBroker-WIF'\n          sbConfigPath: $(StoreBrokerPath)\\sbconfig.json\n          sourceFolder: $(StoreBrokerPath)\\appxpackage\\\n          contents: Microsoft.WSL_${{ parameters.packageVersion }}.0_x64_ARM64.appxupload\n          pdpPath: $(StoreBrokerPath)\\PDPs\\\n          pdpInclude: PDP.xml\n          pdpMediaPath: $(StoreBrokerPath)\\Media\\\n          outSBPackagePath: $(StoreBrokerPayloadPath)\n          outSBName: WindowsSubsystemForLinux\n\n      # copy the storebroker submission package to the drop folder so it can be used if needed\n      - powershell: |\n                    New-Item -ItemType Directory -Force -Path $(ob_outputDirectory)\n                    Copy-Item -Path $(StoreBrokerPayloadPath)\\* -Destination $(ob_outputDirectory) -Recurse -Force\n                    Copy-Item -Path SBLog.txt -Destination $(ob_outputDirectory) -Force\n        displayName: Copy StoreBrokerPayload to drop\n\n      # submit the package flight to the WSL SelfHost Flight Group\n      - task: MS-RDX-MRO.windows-store-publish.flight-task.store-flight@3\n        displayName: 'Flight StoreBroker Package to Partner Center - WSL SelfHost Flight Group'\n        condition: and(succeeded(), eq('${{ parameters.publishPackage }}', true))\n        inputs:\n          serviceEndpoint: 'AzureConnection-StoreBroker-WIF'\n          appId: $(AppId)\n          flightId: $(FlightId)\n          inputMethod: JsonAndZip\n          jsonPath: $(StoreBrokerPayloadPath)\\WindowsSubsystemForLinux.json\n          zipPath: $(StoreBrokerPayloadPath)\\WindowsSubsystemForLinux.zip\n          force: true\n          skipPolling: true # skips polling Partner Centre/store API for the state of the package; skipping will mean that errors in the process will only show up in Partner Center\n          targetPublishMode: Immediate # on completion of this task, the package will be published to the WSL SelfHost flight once certified (no manual clicking of any buttons in Partner Center)\n          preserveSubmissionId: false\n          deletePackages: true\n          numberOfPackagesToKeep: 0\n\n      - task: PipAuthenticate@1\n        displayName: 'Pip Authenticate'\n        inputs:\n          artifactFeeds: 'wsl'\n\n      # Create a draft github release\n      - powershell: |\n                pip install --user -r tools/devops/requirements.txt\n                python tools/devops/create-release.py '${{ parameters.packageVersion }}' drop\\bundle\\Microsoft.WSL_${{ parameters.packageVersion }}.0_x64_ARM64.msixbundle drop\\bundle\\wsl.${{ parameters.packageVersion }}.0.arm64.msi drop\\bundle\\wsl.${{ parameters.packageVersion }}.0.x64.msi --no-fetch --github-token \"$env:token\" ${{ iif(parameters.publishPackage, '--publish --auto-release-notes', '--use-current-ref') }}\n\n        displayName: Create GitHub release\n        env:\n            token: $(GITHUB_RELEASE_TOKEN)"
  },
  {
    "path": ".pipelines/nuget-stage.yml",
    "content": "parameters:\n- name: isNightly\n  type: boolean\n  default: false\n  \n- name: nugetPackages\n  type: object\n  default:\n      - Microsoft.WSL.PluginApi\n\nstages:\n- stage: nuget\n  dependsOn: [build, test]\n  jobs:\n  - job: nuget\n    displayName: 'Publish nuget packages'\n    condition: and(succeeded(), or(eq(variables['Build.Reason'], 'Schedule'), eq('${{ parameters.isNightly }}', false)))\n    dependsOn: [] # The stage handles this dependency\n\n    pool:\n      type: windows\n\n    variables:\n      WSL_NUGET_PACKAGE_VERSION: $[ dependencies.build.outputs['version.WSL_NUGET_PACKAGE_VERSION'] ]\n      NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS: 60\n      NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS: 60\n      ob_outputDirectory: '$(Build.SourcesDirectory)\\out'\n      ob_artifactBaseName: 'drop_wsl'\n      ob_artifactSuffix: '_nuget'\n\n    steps:\n\n    - task: DownloadPipelineArtifact@2\n      displayName: Download nuget artifacts\n      inputs:          \n        artifact: \"drop_wsl_build\"\n        path: drop\n\n      # Note: this task might fail if there's been no commits between two nightly pipelines, which is fine.\n    - ${{ each package in parameters.nugetPackages }}:\n        - task: NuGetCommand@2\n          displayName: Push nuget/${{ package }}.$(WSL_NUGET_PACKAGE_VERSION).nupkg\n          inputs:\n            command: 'push'\n            packagesToPush: drop/nuget/${{ package }}.$(WSL_NUGET_PACKAGE_VERSION).nupkg\n            nuGetFeedType: 'internal'\n            publishVstsFeed: wsl\n            allowPackageConflicts: ${{ parameters.isNightly }}"
  },
  {
    "path": ".pipelines/test-job.yml",
    "content": "parameters:\n  - name: branch\n    type: string\n\n  - name: version\n    type: string\n\n  - name: image\n    type: string\n\n  - name: run\n    type: boolean\n\n  - name: pool\n    type: string\n    default: ''\n\njobs:\n  - job: test_${{ parameters.branch }}_${{ parameters.version }}\n    displayName: \"${{ parameters.version }} tests - ${{ parameters.branch }}\"\n    dependsOn: []\n    condition: and(succeeded(), eq('${{ parameters.run }}', true))\n    variables:\n        ob_outputDirectory: '$(Build.SourcesDirectory)\\out'\n        ob_artifactBaseName: 'drop_wsl'\n        ob_artifactSuffix: '_test'\n    timeoutInMinutes: 360\n    cancelTimeoutInMinutes: 420\n    ${{ if eq(parameters.pool, '') }}:\n      pool: {'type': 'cloudtestagentless'}\n\n    ${{ else }}:\n      pool: ${{ parameters.pool }}\n    steps:\n      - task: CloudTestServerBuildTask@2\n        inputs:\n          DisplayName: \"${{ parameters.version }} tests - ${{ parameters.branch }}\"\n          connectedServiceName: \"CloudTest-PROD\"\n          cloudTestTenant: \"wsl\"\n          testMapLocation: 'testbin\\x64\\cloudtest\\wsl-test-image-${{ parameters.image }}-${{ parameters.version}}\\TestMap.xml'\n          pipelineArtifactName: \"drop_wsl_build\"\n          pipelineArtifactBuildUrl: '$(System.TaskDefinitionsUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)'\n          buildDropArtifactName: \"\"\n          timeoutInMinutes: 360\n          cancelTimeoutInMinutes: 420\n          TestTimeout: \"0.05:00:00\"\n          parserProperties: \"worker:VsTestVersion=V150;session:HoldTrigger=Failure;VstsTestResultAttachmentUploadBehavior=Always\"\n          notificationSubscribers: $(Build.RequestedForEmail)\n          scheduleBuildRequesterAlias: \"lowdev\"\n"
  },
  {
    "path": ".pipelines/test-stage.yml",
    "content": "parameters:\n  - name: rs_prerelease_only\n    type: boolean\n    default: false\n\n  - name: pool\n    type: string\n    default: ''\n\n  - name: versions\n    type: object\n    default:\n      - wsl1\n      - wsl2\n\n  - name: test_images\n    type: object\n    default:\n      - name: rs_prerelease\n        image: rs_prerelease-2025-01-30\n      - name: ni_release\n        image: win11-23h2-ent-2024-11-18\n      - name: fe_release\n        image: 2022-datacenter-g2-2024-09-10\n      - name: vb_release\n        image: win10-22h2-ent-g2-2024-09-10\n\n      # TODO: ge_release\n\nstages:\n- stage: test\n  dependsOn: [build]\n  jobs:\n      - ${{ each version in parameters.versions }}:\n        - ${{ each image in parameters.test_images }}:\n          - template: test-job.yml@self\n            parameters:\n              branch: \"${{ image.name }}\"\n              version: ${{ version }}\n              image: \"${{ image.image }}\"\n              run: ${{ or(not(parameters.rs_prerelease_only), eq(image.name, 'rs_prerelease')) }}\n              pool: \"${{ parameters.pool }}\""
  },
  {
    "path": ".pipelines/wsl-build-nightly-localization.yml",
    "content": "trigger:\n  branches:\n    include:\n    - master\n  paths:\n    include:\n    - 'localization/strings/en-US/Resources.resw'\n\n# Schedule nightly build\n# Cron syntax: \"mm HH DD MM DW\" = minutes, hours, days, months, day of week (UTC time)\nschedules:\n# \"0 8\" = 8AM UTC = 12AM PST\n- cron: \"0 8 * * *\"\n  displayName: Nightly Touchdown Build Schedule\n  branches:\n    include:\n    - \"master\"\n  always: true\n\npool:\n  vmImage: 'windows-latest'\n\nsteps:\n- checkout : self\n  persistCredentials: true\n\n- task: TouchdownBuildTask@5\n  displayName: Touchdown Build Localization\n  inputs:\n    environment: 'PRODEXT'\n    teamId: '38646'\n    authType: 'FederatedIdentity'\n    FederatedIdentityServiceConnection: 'Azure-Connection'\n    isPreview: false\n    resourceFilePath: |\n      localization\\strings\\en-US\\Resources.resw;O:localization\\strings\\\n      storebroker\\PDPs\\en-us\\PDP.xml;O:storebroker\\PDPs\\\n    localizationTarget: true\n    pseudoSetting: 'Excluded'\n    cultureMappingType: 'None'\n\n- task: PipAuthenticate@1\n  inputs:\n    artifactFeeds: 'wsl'\n\n- powershell: |\n            pip install --user -r tools/devops/requirements.txt\n            python tools/devops/create-change.py . \"$env:token\" \"WSL localization\"  \"Localization change from build: $env:buildId\" \"user/localization/$env:buildId\"\n\n  displayName: Create pull request\n  env:\n      token: $(GithubPRToken)\n      buildId: $(Build.BuildId)"
  },
  {
    "path": ".pipelines/wsl-build-nightly-onebranch.yml",
    "content": "trigger: none\n\nschedules:\n# \"0 8\" = 8AM UTC = 12AM PST\n- cron: \"0 8 * * *\"\n  displayName: Nightly build\n  branches:\n    include: [master]\n  always: true\n\nvariables:\n  WindowsContainerImage: \"onebranch.azurecr.io/windows/ltsc2022/vse2022:latest\"\n  WindowsHostVersion: '1ESWindows2022'\n\nresources:\n  repositories:\n    - repository: templates\n      type: git\n      name: OneBranch.Pipelines/GovernedTemplates\n      ref: refs/heads/main\n\nextends:\n  template: v2/Microsoft.NonOfficial.yml@templates\n  parameters:\n    platform:\n      name: \"windows_undocked\"\n    featureFlags:\n      EnableCDPxPAT: false\n      WindowsHostVersion: 1ESWindows2022\n    globalSdl:\n      credscan:\n        enabled: true\n      perStage:\n        credscan:\n          enabled: true\n      tsa:\n        enabled: false\n    git:\n      fetchDepth: -1\n      fetchTags: true\n\n    stages:\n    - template: build-stage.yml@self\n      parameters:\n        isRelease: false\n        isNightly: true\n        vsoOrg: microsoft\n        vsoProject: Microsoft.WSL\n\n    - template: test-stage.yml@self\n      parameters:\n        rs_prerelease_only: false"
  },
  {
    "path": ".pipelines/wsl-build-notice.yml",
    "content": "trigger:\n  branches:\n    include:\n    - master\n\npool:\n    vmImage: 'windows-latest'\n\nsteps:\n- checkout : self\n  persistCredentials: true\n\n- task: ComponentGovernanceComponentDetection@0\n  displayName: Component Detection\n\n- task: notice@0\n  displayName: Generate NOTICE file\n  inputs:\n    outputfile: $(System.DefaultWorkingDirectory)/NOTICE.txt\n    outputformat: text\n\n- task: PipAuthenticate@1\n  inputs:\n    artifactFeeds: 'wsl'\n\n- powershell: |\n            pip install --user -r tools/devops/requirements.txt\n            python tools/devops/create-change.py . \"$env:token\" \"WSL notice\"  \"Notice change from build: $env:buildId\" \"user/notice/$env:buildId\"\n\n  displayName: Create pull request\n  env:\n      token: $(GithubPRToken)\n      buildId: $(Build.BuildId)"
  },
  {
    "path": ".pipelines/wsl-build-pr-onebranch.yml",
    "content": "trigger:\n  branches:\n    include:\n    - master\n    - release/*\n\nvariables:\n  WindowsContainerImage: \"onebranch.azurecr.io/windows/ltsc2022/vse2022:latest\"\n  WindowsHostVersion: '1ESWindows2022'\n\nresources:\n  repositories:\n    - repository: templates\n      type: git\n      name: OneBranch.Pipelines/GovernedTemplates\n      ref: refs/heads/main\n\nextends:\n  template: v2/Microsoft.NonOfficial.yml@templates\n  parameters:\n    platform:\n      name: \"windows_undocked\"\n    featureFlags:\n      EnableCDPxPAT: false\n      WindowsHostVersion: 1ESWindows2022\n    globalSdl:\n      suppression:\n        suppressionFile: $(Build.SourcesDirectory)\\.gdnsuppress\n        suppressionSet: default\n      apiscan:\n        enabled: false\n      credscan:\n        enabled: true\n      perStage:\n        credscan:\n          enabled: true\n      policheck:\n        enabled: true\n        break: true\n        severity: Note\n    git:\n      fetchDepth: -1\n      fetchTags: true\n\n    stages:\n    - template: build-stage.yml@self\n      parameters:\n        isRelease: false\n\n    - template: test-stage.yml@self\n      parameters:\n        rs_prerelease_only: true\n"
  },
  {
    "path": ".pipelines/wsl-build-pr.yml",
    "content": "trigger:\n  branches:\n    include:\n    - master\n    - release/*\n\nstages:\n- template: build-stage.yml@self\n  parameters:\n    isRelease: false\n    pool: 'wsl-build'\n    vsoOrg: shine-oss\n    vsoProject: wsl\n\n- template: test-stage.yml@self\n  parameters:\n    rs_prerelease_only: true\n    pool: server\n"
  },
  {
    "path": ".pipelines/wsl-build-release-onebranch.yml",
    "content": "parameters:\n- name: bypassTests\n  displayName: 'Publish release even if tests fail'\n  type: boolean\n  default: false\n\n- name: testVersion\n  displayName: 'Test the release pipeline'\n  type: string\n  default: ''\n\ntrigger:\n  tags:\n    include: ['*.*.*']\n\nvariables:\n  WindowsContainerImage: \"onebranch.azurecr.io/windows/ltsc2022/vse2022:latest\"\n  WindowsHostVersion: '1ESWindows2022'\n\nresources:\n  repositories:\n    - repository: templates\n      type: git\n      name: OneBranch.Pipelines/GovernedTemplates\n      ref: refs/heads/main\n\nextends:\n  template: v2/Microsoft.Official.yml@templates\n  parameters:\n    platform:\n      name: \"windows_undocked\"\n    featureFlags:\n      EnableCDPxPAT: false\n      WindowsHostVersion: 1ESWindows2022\n    globalSdl:\n      credscan:\n        enabled: true\n      perStage:\n        credscan:\n          enabled: true\n      tsa:\n        enabled: false\n      evidence:\n        enabled: false\n    git:\n      fetchDepth: -1\n      fetchTags: true\n\n    stages:\n    - template: build-stage.yml@self\n      parameters:\n        isRelease: true\n        packageVersion: ${{ iif(eq(parameters.testVersion, ''), variables['Build.SourceBranchName'], parameters.testVersion) }}\n        traceLoggingConfig: $(ReleaseTraceLoggingConfig)\n        vsoOrg: microsoft\n        vsoProject: Microsoft.WSL\n\n    - template: test-stage.yml@self\n      parameters:\n        rs_prerelease_only: false\n\n    - template: flight-stage.yml@self\n      parameters:\n        publishPackage: ${{ iif(eq(parameters.testVersion, ''), true, false) }}\n        packageVersion: ${{ iif(eq(parameters.testVersion, ''), variables['Build.SourceBranchName'], parameters.testVersion) }}\n        bypassTests: ${{ parameters.bypassTests }}\n\n    - ${{ if eq(parameters.testVersion, '') }}:\n        - template: nuget-stage.yml@self\n          parameters:\n            isNightly: false"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.25)\nset(CMAKE_SYSTEM_VERSION 10.0.26100.0)\n\nproject(wsl)\n\n# Rationalize TARGET_PLATFORM\n# When neither CMAKE_GENERATOR_PLATFORM nor TARGET_PLATFORM is set, default to the host architecture.\nif(\"${CMAKE_GENERATOR_PLATFORM}\" STREQUAL \"\" AND \"${TARGET_PLATFORM}\" STREQUAL \"\")\n    if(\"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL \"ARM64\")\n        set(TARGET_PLATFORM \"arm64\")\n    else()\n        set(TARGET_PLATFORM \"x64\")\n    endif()\n    message(STATUS \"No platform specified, defaulting to '${TARGET_PLATFORM}' based on host architecture.\")\nendif()\n\nif(\"${CMAKE_GENERATOR_PLATFORM}\" STREQUAL \"arm64\" OR \"${TARGET_PLATFORM}\" STREQUAL \"arm64\")\n    set(TARGET_PLATFORM \"arm64\")\n    set(TEST_DISTRO_PLATFORM \"arm64\")\nelseif(\"${CMAKE_GENERATOR_PLATFORM}\" MATCHES \"x64|amd64\" OR \"${TARGET_PLATFORM}\" MATCHES \"x64|amd64\")\n    set(TARGET_PLATFORM \"x64\")\n    set(TEST_DISTRO_PLATFORM \"amd64\")\nelse()\n    message(FATAL_ERROR \"Unsupported platform: ${CMAKE_GENERATOR_PLATFORM}\")\nendif()\n\n\nif (NOT ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} STREQUAL ${CMAKE_SYSTEM_VERSION})\n    message(FATAL_ERROR \"Incorrect Windows SDK version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}, requires ${CMAKE_SYSTEM_VERSION}\")\nendif()\n\ninclude(FetchContent)\n\n# Import GSL and nlohmannjson\n\nset(FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps/${TARGET_PLATFORM})\n\nFetchContent_Declare(GSL\n                     URL https://github.com/microsoft/GSL/archive/refs/tags/v4.0.0.tar.gz\n                     URL_HASH SHA256=f0e32cb10654fea91ad56bde89170d78cfbf4363ee0b01d8f097de2ba49f6ce9)\n\nFetchContent_MakeAvailable(GSL)\nFetchContent_GetProperties(GSL SOURCE_DIR GSL_SOURCE_DIR)\n\n\nFetchContent_Declare(nlohmannjson\n                     URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz\n                     URL_HASH SHA256=42f6e95cad6ec532fd372391373363b62a14af6d771056dbfc86160e6dfff7aa )\n\nFetchContent_MakeAvailable(nlohmannjson)\nFetchContent_GetProperties(nlohmannjson SOURCE_DIR NLOHMAN_JSON_SOURCE_DIR)\n\n# Import modules\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_LIST_DIR}/cmake\")\nfind_package(IDL REQUIRED)\nfind_package(LINUXBUILD REQUIRED)\nfind_package(NUGET REQUIRED)\nfind_package(VERSION REQUIRED)\nfind_package(MC REQUIRED)\nfind_package(Appx REQUIRED)\n\n# Download nuget packages\nrestore_nuget_packages()\n\n# Load nuget packages versions and paths\nparse_nuget_packages_versions()\n\nfind_nuget_package(Microsoft.Direct3D.Linux DIRECT3D /build/native)\nfind_nuget_package(Microsoft.Identity.MSAL.WSL.Proxy MSAL /build/native/bin)\nfind_nuget_package(Microsoft.RemoteDesktop.Client.MSRDC.SessionHost MSRDC /build/native/bin)\nfind_nuget_package(Microsoft.Taef TAEF /)\nfind_nuget_package(Microsoft.Windows.ImplementationLibrary WIL /)\nfind_nuget_package(Microsoft.WSL.DeviceHost WSL_DEVICE_HOST /build/native)\nfind_nuget_package(Microsoft.WSL.Kernel KERNEL /build/native)\nfind_nuget_package(Microsoft.WSL.bsdtar BSDTARD /build/native/bin)\nfind_nuget_package(Microsoft.WSL.LinuxSdk LINUXSDK /)\nfind_nuget_package(Microsoft.WSL.TestDistro TEST_DISTRO /)\nfind_nuget_package(Microsoft.WSLg WSLG /build/native/bin)\nfind_nuget_package(vswhere VSWHERE /tools)\nfind_nuget_package(Wix WIX /tools/net6.0/any)\n\n# Architecture-specific nuget packages from the OS repo.\nif (${TARGET_PLATFORM} STREQUAL \"x64\")\n    find_nuget_package(Microsoft.DXCore.Linux.amd64fre DXCORE /build/native)\n    find_nuget_package(Microsoft.WSL.Dependencies.amd64fre WSLDEPS /build/native)\nendif()\n\nif (${TARGET_PLATFORM} STREQUAL \"arm64\")\n    find_nuget_package(Microsoft.DXCore.Linux.arm64fre DXCORE /build/native)\n    find_nuget_package(Microsoft.WSL.Dependencies.arm64fre WSLDEPS /build/native)\nendif()\n\n# Wsl Settings packages\nfind_nuget_package(CommunityToolkit.Mvvm CTK_MVVM /)\nfind_nuget_package(CommunityToolkit.WinUI.Animations CTK_ANIMATIONS /)\nfind_nuget_package(CommunityToolkit.WinUI.Controls.SettingsControls CTK_STTNGS_CTRLS /)\nfind_nuget_package(Microsoft.Extensions.Hosting EXTS_HOSTING /)\nfind_nuget_package(Microsoft.NETCore.App.Runtime.win-${TARGET_PLATFORM} DOTNET_RUNTIME /)\nfind_nuget_package(Microsoft.WindowsAppSDK WIN_APP_SDK /)\nfind_nuget_package(Microsoft.Windows.SDK.NET.Ref WINDOWS_SDK_DOTNET /)\nfind_nuget_package(Microsoft.Xaml.Behaviors.WinUI.Managed XAML_BEHAVIORS /)\nfind_nuget_package(WinUIEx WINUIEX /)\n\nset(WSLG_TS_PLUGIN_DLL \"WSLDVCPlugin.dll\")\n\n# Default to debug build if unspecified\nif(NOT CMAKE_BUILD_TYPE)\n  set(CMAKE_BUILD_TYPE \"Debug\")\nendif()\n\nset(SUPPORTED_LANGS cs-CZ;da-DK;de-DE;en-GB;en-US;es-ES;fi-FI;fr-FR;hu-HU;it-IT;ja-JP;ko-KR;nb-NO;nl-NL;pl-PL;pt-BR;pt-PT;ru-RU;sv-SE;tr-TR;zh-CN;zh-TW)\n\nif (EXISTS \"${CMAKE_CURRENT_LIST_DIR}/UserConfig.cmake\")\n    find_package(USER REQUIRED PATHS ${CMAKE_CURRENT_LIST_DIR})\nendif()\n\n# Optional target configuration\n\nif (NOT DEFINED WSL_BUILD_WSL_SETTINGS)\n    set(WSL_BUILD_WSL_SETTINGS false)\nendif ()\n\n# Only generate the build configuration that CMake is configured for\nset(CMAKE_CONFIGURATION_TYPES \"${CMAKE_BUILD_TYPE}\" CACHE STRING \"\" FORCE)\n\nfind_commit_hash(COMMIT_HASH)\n\nif (NOT PACKAGE_VERSION)\n    find_version(PACKAGE_VERSION WSL_NUGET_PACKAGE_VERSION) # Fetch the package version from git if not specified\nendif ()\n\nif (NOT PACKAGE_VERSION MATCHES \"^([0-9]+).([0-9]+).([0-9]+).([0-9]+)$\")\n    message(FATAL_ERROR \"PACKAGE_VERSION is invalid: '${PACKAGE_VERSION}'. Needs to match '([0-9]+).([0-9]+).([0-9]+).([0-9]+)'\")\nendif()\n\nset(PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})\nset(PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})\nset(PACKAGE_VERSION_REVISION ${CMAKE_MATCH_3})\n\n# The store requires the revision number to be 0, so enforce this on official builds\nif (OFFICIAL_BUILD AND NOT PACKAGE_VERSION MATCHES \"^([0-9]+).([0-9]+).([0-9]+).0$\")\n    message(FATAL_ERROR \"PACKAGE_VERSION is invalid: '${PACKAGE_VERSION}'. Needs to match '([0-9]+).([0-9]+).([0-9]+).0' for official builds\")\nendif()\n\n# Configure output directories\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/${TARGET_PLATFORM})\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug)\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Release)\n\nset_property(GLOBAL PROPERTY USE_FOLDERS ON)\n\n# Packaging variables\nset(BIN ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE})\nfile(MAKE_DIRECTORY ${BIN})\n\nset (GENERATED_DIR ${CMAKE_BINARY_DIR}/generated)\nfile(MAKE_DIRECTORY ${GENERATED_DIR})\n\nset(PACKAGE_CERTIFICATE ${GENERATED_DIR}/dev-cert.pfx)\nfile(CREATE_LINK ${WSL_DEVICE_HOST_SOURCE_DIR}/bin/${TARGET_PLATFORM}/wsldevicehost.dll ${BIN}/wsldevicehost.dll)\nfile(CREATE_LINK ${WSLG_SOURCE_DIR}/${TARGET_PLATFORM}/${WSLG_TS_PLUGIN_DLL} ${BIN}/${WSLG_TS_PLUGIN_DLL})\nfile(CREATE_LINK ${WSLDEPS_SOURCE_DIR}/bin/wsldeps.dll ${BIN}/wsldeps.dll)\n\nif (${SKIP_PACKAGE_SIGNING})\n    set(PACKAGE_SIGN_COMMAND echo Skipped package signing for:)\nelse()\n    if (NOT EXISTS ${PACKAGE_CERTIFICATE})\n        execute_process(\n            COMMAND powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive ${CMAKE_CURRENT_LIST_DIR}/tools/create-dev-cert.ps1 -OutputPath ${PACKAGE_CERTIFICATE}\n            COMMAND_ERROR_IS_FATAL ANY)\n    endif()\n\n    set(PACKAGE_SIGN_COMMAND SignTool.exe sign /a /v /fd SHA256 /f ${PACKAGE_CERTIFICATE})\nendif()\n\n\n# Generate local test script\nconfigure_file(${CMAKE_CURRENT_LIST_DIR}/tools/test/test.bat.in  ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/test.bat)\n\n# Common build flags\nset(CMAKE_CXX_STANDARD 20)\n\nif (${CMAKE_BUILD_TYPE} STREQUAL \"Debug\")\n    set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDebug)\nelse()\n    set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)\nendif()\n\nif (${TARGET_PLATFORM} STREQUAL \"x64\")\n    add_compile_definitions(_AMD64_)\nendif()\n\nif (${TARGET_PLATFORM} STREQUAL \"arm64\")\n    add_compile_definitions(_ARM64_)\nendif()\n\nadd_definitions(/sdl) # Default-initialize class members\nadd_definitions(/FS) # Enable parallel PDB access\nadd_compile_definitions(UNICODE\n                        WIL_SUPPRESS_PRIVATE_API_USE\n                        CPPWINRT_SUPPRESS_STATIC_INITIALIZERS\n                        NOMINMAX\n                        _CRT_SECURE_NO_WARNINGS\n                        KERNEL_VERSION=\"${KERNEL_VERSION}\"\n                        WSLDEPS_VERSION=\"${WSLDEPS_VERSION}\"\n                        WSLG_VERSION=\"${WSLG_VERSION}\"\n                        WSLG_TS_PLUGIN_DLL=L\"${WSLG_TS_PLUGIN_DLL}\"\n                        WSL_DEVICE_HOST_VERSION=\"${WSL_DEVICE_HOST_VERSION}\"\n                        COMMIT_HASH=\"${COMMIT_HASH}\"\n                        WSL_PACKAGE_VERSION=\"${PACKAGE_VERSION}\"\n                        MSRDC_VERSION=\"${MSRDC_VERSION}\"\n                        DIRECT3D_VERSION=\"${DIRECT3D_VERSION}\"\n                        DXCORE_VERSION=\"${DXCORE_VERSION}\"\n                        WSL_PACKAGE_VERSION_MAJOR=${PACKAGE_VERSION_MAJOR}\n                        WSL_PACKAGE_VERSION_MINOR=${PACKAGE_VERSION_MINOR}\n                        WSL_PACKAGE_VERSION_REVISION=${PACKAGE_VERSION_REVISION}\n                        WSL_BUILD_WSL_SETTINGS=${WSL_BUILD_WSL_SETTINGS})\n\nif (${OFFICIAL_BUILD})\n    add_compile_definitions(WSL_OFFICIAL_BUILD)\nendif()\n\nif (${WSL_BUILD_THIN_PACKAGE})\n    add_compile_definitions(WSL_DEV_THIN_MSI_PACKAGE=\"${BIN}/wsl.msi\")\nendif ()\n\nstring(REPLACE \"/Zi\" \"\" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})  # make sure /Zi is removed from the debug flags\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} /bigobj /W3 /WX /ZH:SHA_256\")\nset(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} /Z7 -DDEBUG -DDBG\")\nset(CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} /Zi /guard:cf /Qspectre\")\n\n# Linker flags\nset(CMAKE_SHARED_LINKER_FLAGS_RELEASE \"${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /debug:full /debugtype:cv,fixup /guard:cf /DYNAMICBASE\")\nset(CMAKE_EXE_LINKER_FLAGS_RELEASE \"${CMAKE_EXE_LINKER_FLAGS_RELEASE} /debug:full /debugtype:cv,fixup /guard:cf /DYNAMICBASE\")\nif (${TARGET_PLATFORM} STREQUAL \"x64\")\n    set(CMAKE_SHARED_LINKER_FLAGS_RELEASE \"${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /CETCOMPAT\")\n    set(CMAKE_EXE_LINKER_FLAGS_RELEASE \"${CMAKE_EXE_LINKER_FLAGS_RELEASE} /CETCOMPAT\")\nendif()\n\n# Common link libraries\nlink_directories(${WSLDEPS_SOURCE_DIR}/lib/)\nset(COMMON_LINK_LIBRARIES\n    ws2_32.lib\n    Userenv.lib\n    RuntimeObject.lib\n    Pathcch.lib\n    ntdll.lib\n    RpcRT4.lib\n    Mswsock.lib\n    Shlwapi.lib\n    synchronization.lib\n    Bcrypt.lib\n    icu.lib)\n\nset(MSI_LINK_LIBRARIES\n    Wintrust.lib\n    msi.lib)\n\nset(HCS_LINK_LIBRARIES\n    computecore.lib\n    computenetwork.lib\n    Iphlpapi.lib)\n\nset(SERVICE_LINK_LIBRARIES\n    MI.lib\n    wsldeps.lib)\n\n# Linux\nif(${TARGET_PLATFORM} STREQUAL \"\" OR ${TARGET_PLATFORM} STREQUAL \"x64\")\n    set(LLVM_ARCH x86_64)\nelseif(${TARGET_PLATFORM} STREQUAL \"arm64\")\n    set(LLVM_ARCH aarch64)\nelse()\n    message(FATAL_ERROR \"Unsupported platform: '${TARGET_PLATFORM}'\")\nendif()\n\n# Determine the Visual Studio installation directory which contains LLVM tools.\n# Supported versions: VS2022 and VS2026.\n# Prefer VS2022 to keep local clang-format output aligned with pipeline expectations.\nfunction(find_vs_install_dir VERSION_RANGE OUTPUT_VAR)\n    execute_process(\n        COMMAND \"${VSWHERE_SOURCE_DIR}/vswhere.exe\" -version \"${VERSION_RANGE}\" -products * -requires Microsoft.VisualStudio.Component.VC.Llvm.Clang -property installationPath -prerelease -latest\n        OUTPUT_VARIABLE _vs_install_dir\n        OUTPUT_STRIP_TRAILING_WHITESPACE\n        COMMAND_ERROR_IS_FATAL ANY\n    )\n\n    set(${OUTPUT_VAR} \"${_vs_install_dir}\" PARENT_SCOPE)\nendfunction()\n\nfind_vs_install_dir(\"[17.0,18.0)\" VS_INSTALL_DIR)\n\nif (NOT VS_INSTALL_DIR)\n    find_vs_install_dir(\"[18.0,19.0)\" VS_INSTALL_DIR)\n    if (VS_INSTALL_DIR)\n        message(WARNING \"Visual Studio 2022 was not found; using Visual Studio 2026 instead. clang-format output may differ from pipeline expectations.\")\n    endif()\nendif()\n\nif (NOT VS_INSTALL_DIR)\n    message(FATAL_ERROR \"Could not determine Visual Studio installation directory.\")\nendif()\n\nif(\"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL \"AMD64\")\n    set(LLVM_INSTALL_DIR \"${VS_INSTALL_DIR}/VC/Tools/Llvm/x64/bin\")\nelse()\n    set(LLVM_INSTALL_DIR \"${VS_INSTALL_DIR}/VC/Tools/Llvm/${CMAKE_HOST_SYSTEM_PROCESSOR}/bin\")\nendif()\n\nif (NOT EXISTS ${LLVM_INSTALL_DIR})\n    message(FATAL_ERROR \"C++ Clang Compiler for Windows is not installed at ${LLVM_INSTALL_DIR}. Please install it from the Visual Studio Installer.\")\nendif()\n\n# Generate the clang-format script which contains a path to clang-format.exe\nconfigure_file(${CMAKE_CURRENT_LIST_DIR}/tools/FormatSource.ps1.in ${CMAKE_BINARY_DIR}/FormatSource.ps1)\n\ncmake_path(COMPARE \"${wsl_SOURCE_DIR}\" EQUAL \"${wsl_BINARY_DIR}\" BUILD_IN_SOURCE)\nif (NOT ${BUILD_IN_SOURCE}) # Testing on 3.26 project_type_DIR paths appear canonicalized\n    file(CREATE_LINK ${LLVM_INSTALL_DIR}/clang-format.exe ${wsl_SOURCE_DIR}/tools/clang-format.exe COPY_ON_ERROR)\nendif()\n\nset(LINUXSDK_PATH ${LINUXSDK_SOURCE_DIR}/${LLVM_ARCH})\nset(LLVM_TARGET \"${LLVM_ARCH}-unknown-linux-musl\")\nset(LINUX_CC ${LLVM_INSTALL_DIR}/clang.exe)\nset(LINUX_CXX ${LLVM_INSTALL_DIR}/clang++.exe)\nset(LINUX_AR ${LLVM_INSTALL_DIR}/llvm-ar.exe)\nset(LINUX_COMMON_FLAGS --gcc-toolchain=${LINUXSDK_PATH}\n                       -fpic\n                       -B${LINUXSDK_PATH}\n                       -isysroot ${LINUXSDK_PATH}\n                       -isystem ${LINUXSDK_PATH}/include/c++/v1\n                       -isystem ${LINUXSDK_PATH}/include\n                       -isystem ${GSL_SOURCE_DIR}/include\n                       -isystem \"${WSLDEPS_SOURCE_DIR}/include/lxcore\"\n                       -isystem \"${WSLDEPS_SOURCE_DIR}/include/schemas\"\n                       -I \"${CMAKE_CURRENT_LIST_DIR}/src/linux/inc\"\n                       -I \"${CMAKE_CURRENT_LIST_DIR}/src/linux/mountutil\"\n                       -I \"${CMAKE_CURRENT_LIST_DIR}/src/linux/plan9\"\n                       -I \"${CMAKE_CURRENT_LIST_DIR}/src/shared/configfile\"\n                       -I \"${CMAKE_CURRENT_LIST_DIR}/src/shared/inc\"\n                       -I \"${NLOHMAN_JSON_SOURCE_DIR}/include\"\n                       -I \"${CMAKE_BINARY_DIR}/generated\"\n                       --no-standard-libraries\n                       -Werror\n                       -Wall\n                       -Wpointer-arith\n                       -D_POSIX_C_SOURCE=200809L\n                       -Dswprintf_s=swprintf\n                       -fms-extensions\n                       -target ${LLVM_TARGET}\n                       -D_GNU_SOURCE=1\n                       -D_LARGEFILE64_SOURCE\n                       -DWSL_PACKAGE_VERSION=\"${PACKAGE_VERSION}\"\n                       -DWSL_PACKAGE_VERSION_MAJOR=${PACKAGE_VERSION_MAJOR}\n                       -DWSL_PACKAGE_VERSION_MINOR=${PACKAGE_VERSION_MINOR}\n                       -DWSL_PACKAGE_VERSION_REVISION=${PACKAGE_VERSION_REVISION}\n                       )\n\nif (${TARGET_PLATFORM} STREQUAL \"x64\")\n    set(LINUX_COMMON_FLAGS ${LINUX_COMMON_FLAGS} -D_AMD64_)\nendif()\n\nif (${TARGET_PLATFORM} STREQUAL \"arm64\")\n    set(LINUX_COMMON_FLAGS ${LINUX_COMMON_FLAGS} -D_ARM64_)\nendif()\n\nset(LINUX_CXXFLAGS ${LINUX_COMMON_FLAGS} -std=c++20)\nset(LINUX_CFLAGS ${LINUX_COMMON_FLAGS} -std=c99)\n\nstring(TOLOWER ${CMAKE_BUILD_TYPE} build_type)\nif (build_type STREQUAL debug)\n    set(LINUX_BUILD_TYPE_FLAGS -g3 -fno-inline-functions -DDEBUG -DDBG)\nelse()\n    set(LINUX_BUILD_TYPE_FLAGS -g -O2 -DNDEBUG)\nendif()\n\nset(LINUX_LDFLAGS -target ${LLVM_TARGET}\n                  --gcc-toolchain=${LINUXSDK_PATH}\n                  -B${LINUXSDK_PATH}\n                  -isysroot ${LINUXSDK_PATH}\n                  -nostartfiles\n                  --no-standard-libraries\n                  -fuse-ld=lld.exe\n                  -L${LINUXSDK_PATH}/lib\n                  -L${LINUXSDK_PATH}/lib/linux\n                  -L${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}\n                  -lclang_rt.builtins-${LLVM_ARCH}\n                  -l:libc.a\n                  -static)\n\nset(COMMON_LINUX_LINK_LIBRARIES configfile)\nfile(GLOB COMMON_LINUX_HEADERS CONFIGURE_DEPENDS \"${CMAKE_CURRENT_LIST_DIR}/src/shared/inc/*.h\")\n\nif(DEFINED ENV{WSL_DEV_BINARY_PATH})\n    set(WSL_DEV_BINARY_PATH $ENV{WSL_DEV_BINARY_PATH})\nendif()\n\nif (DEFINED WSL_DEV_BINARY_PATH) # Development shortcut to make the package smaller\n    add_compile_definitions(WSL_SYSTEM_DISTRO_PATH=\"${WSL_DEV_BINARY_PATH}/system.vhd\"\n                            WSL_KERNEL_PATH=\"${WSL_DEV_BINARY_PATH}/kernel\"\n                            WSL_KERNEL_MODULES_PATH=\"${WSL_DEV_BINARY_PATH}/modules.vhd\"\n                            WSL_DEV_INSTALL_PATH=\"${WSL_DEV_BINARY_PATH}\"\n                            WSL_GPU_LIB_PATH=\"${WSL_DEV_BINARY_PATH}/lib\")\nendif()\n\n# Common include paths\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR}/wil/include)\ninclude_directories(${WSLDEPS_SOURCE_DIR}/include)\ninclude_directories(${WSLDEPS_SOURCE_DIR}/include/Windows)\ninclude_directories(${WSLDEPS_SOURCE_DIR}/include/schemas)\ninclude_directories(${WSLDEPS_SOURCE_DIR}/include/lxcore)\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/shared/inc)\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/windows/inc)\ninclude_directories(${CMAKE_CURRENT_BINARY_DIR}/src/windows/service/inc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE})\ninclude_directories(${CMAKE_CURRENT_BINARY_DIR}/src/windows/wslinstaller/inc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE})\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/linux/init/inc)\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/windows/common)\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/shared/configfile)\ninclude_directories(${CMAKE_CURRENT_SOURCE_DIR}/localization)\ninclude_directories(${CMAKE_BINARY_DIR}/generated)\n\ninclude_directories(${WIL_SOURCE_DIR}/include)\ninclude_directories(${GSL_SOURCE_DIR}/include)\ninclude_directories(${TAEF_SOURCE_DIR}/build/include)\n\ninclude_directories(${NLOHMAN_JSON_SOURCE_DIR}/include)\nlink_directories(${TAEF_SOURCE_DIR}/build/Library/${TARGET_PLATFORM})\nset(TAEF_LINK_LIBRARIES\n    TE.Common.lib\n    Wex.Common.lib\n    Wex.Logger.lib)\n\n# Subprojects\nadd_subdirectory(nuget)\nadd_subdirectory(msixgluepackage)\nadd_subdirectory(msipackage)\nadd_subdirectory(msixinstaller)\nadd_subdirectory(src/windows/common)\nadd_subdirectory(src/windows/service)\nadd_subdirectory(src/windows/wslinstaller/inc)\nadd_subdirectory(src/windows/wslinstaller/stub)\nadd_subdirectory(src/windows/wslinstaller/exe)\nadd_subdirectory(src/shared/configfile)\nadd_subdirectory(src/windows/wsl)\nadd_subdirectory(src/windows/wslg)\nadd_subdirectory(src/windows/wslhost)\nadd_subdirectory(src/windows/wslrelay)\nadd_subdirectory(src/windows/wslinstall)\n\nif (WSL_BUILD_WSL_SETTINGS)\n    add_subdirectory(src/windows/libwsl)\n    add_subdirectory(src/windows/wslsettings)\nendif()\n\nadd_subdirectory(src/linux/netlinkutil)\nadd_subdirectory(src/linux/mountutil)\nadd_subdirectory(src/linux/plan9)\nadd_subdirectory(src/linux/init)\nadd_subdirectory(localization)\n\nadd_subdirectory(test/windows)\n\nif (DEFINED PIPELINE_BUILD_ID)\n    add_subdirectory(cloudtest)\nendif()\n\n\nif(DEFINED ENV{WSL_POST_BUILD_COMMAND})\n    set(WSL_POST_BUILD_COMMAND $ENV{WSL_POST_BUILD_COMMAND})\nendif ()\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n\nResources:\n\n- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)\n- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)\n- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# WSL contributing guide\n\nThere are a few main ways to contribute to WSL, with guides to each one:\n\n1. [Add a feature or bugfix to WSL](#add-a-feature-or-bugfix-to-wsl)\n2. [File a WSL issue](#file-a-wsl-issue)\n\n## Add a feature or bugfix to WSL\n\nWe welcome any contributions to the WSL source code to add features or fix bugs! Before you start actually working on the feature, please **[file it as an issue, or a feature request in this repository](https://github.com/microsoft/WSL/issues)** so that we can track it and provide any feedback if necessary.\n\nOnce you have done so, please see [the developer docs](./doc/docs/dev-loop.md) for instructions on how to build WSL locally on your machine for development. \n\nWhen your fix is ready, please [submit it as a pull request in this repository](https://github.com/microsoft/WSL/pulls) and the WSL team will triage and respond to it. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.\n\n## File a WSL issue\n\nYou can file issues for WSL at the WSL repository, or linked repositories. Before filing an issue please search for any existing issues and upvote or comment on those if possible. \n\n1. If your issue is related to WSL documentation, please file it at [microsoftdocs/wsl](https://github.com/microsoftdocs/WSL/issues)\n2. If your issue is related to a Linux GUI app, please file it at [microsoft/wslg](https://github.com/microsoft/wslg/issues)\n3. Otherwise, if you have a technical issue related to WSL in general, such as start up issues, etc., please file it at [microsoft/wsl](https://github.com/microsoft/WSL/issues)\n\nPlease provide as much information as possible when reporting a bug or filing an issue on the Windows Subsystem for Linux, and be sure to include logs as necessary!\n\nPlease see the [notes for collecting WSL logs](#notes-for-collecting-wsl-logs) section below for more info on filing issues.\n\n## Thank you\n\nThank you in advance for your contribution! We appreciate your help in making WSL a better tool for everyone.\n\n## Notes for collecting WSL logs\n\n### Important: Reporting BSODs and Security issues\n**Do not open GitHub issues for Windows crashes (BSODs) or security issues.** Instead, send Windows crashes or other security-related issues to secure@microsoft.com.\nSee the `10) Reporting a Windows crash (BSOD)` section below for detailed instructions.\n\n### Reporting issues in Windows Console or WSL text rendering/user experience\nNote that WSL distro's launch in the Windows Console (unless you have taken steps to launch a 3rd party console/terminal). Therefore, *please file UI/UX related issues in the [Windows Console issue tracker](https://github.com/microsoft/console)*.\n\n### Collect WSL logs for networking issues\n\nInstall iptables and tcpdump in your WSL distribution using the following commands.\nNote: This will not work if WSL has Internet connectivity issues.\n\n```\n# sudo apt-get update\n# sudo apt-get -y install iptables tcpdump\n```\n\nInstall [WPR](https://learn.microsoft.com/windows-hardware/test/wpt/windows-performance-recorder)\n\nTo collect WSL networking logs, do the following steps in an administrative powershell prompt:\n\n```\nInvoke-WebRequest -UseBasicParsing \"https://raw.githubusercontent.com/microsoft/WSL/master/diagnostics/collect-wsl-logs.ps1\" -OutFile collect-wsl-logs.ps1\nSet-ExecutionPolicy Bypass -Scope Process -Force\n.\\collect-wsl-logs.ps1 -LogProfile networking\n```\nThe script will output when log collection starts. Reproduce the problem, then press any key to stop the log collection.\nThe script will output the path of the log file once done.\n\nFor additional network creation logs (restarts WSL), use:\n```\n.\\collect-wsl-logs.ps1 -LogProfile networking -RestartWslReproMode\n```\n\n<!-- Preserving anchors -->\n<div id=\"8-detailed-logs\"></div>\n<div id=\"9-networking-logs\"></div>\n<div id=\"8-collect-wsl-logs-recommended-method\"></div>\n\n\n### Collect WSL logs (recommended method)\n\nIf you choose to email these logs instead of attaching them to the bug, please send them to wsl-gh-logs@microsoft.com with the GitHub issue number in the subject, and include a link to your GitHub issue comment in the message body.\n\nTo collect WSL logs, download and execute [collect-wsl-logs.ps1](https://github.com/Microsoft/WSL/blob/master/diagnostics/collect-wsl-logs.ps1) in an administrative powershell prompt:\n\n```\nInvoke-WebRequest -UseBasicParsing \"https://raw.githubusercontent.com/microsoft/WSL/master/diagnostics/collect-wsl-logs.ps1\" -OutFile collect-wsl-logs.ps1\nSet-ExecutionPolicy Bypass -Scope Process -Force\n.\\collect-wsl-logs.ps1\n```\nThe script will output the path of the log file once done.\n\nFor specific scenarios, you can use different log profiles:\n- `.\\collect-wsl-logs.ps1 -LogProfile storage` - Enhanced storage tracing\n- `.\\collect-wsl-logs.ps1 -LogProfile networking` - Comprehensive networking tracing (includes packet capture, tcpdump, etc.)\n- `.\\collect-wsl-logs.ps1 -LogProfile networking -RestartWslReproMode` - Networking tracing with WSL restart for network creation logs\n- `.\\collect-wsl-logs.ps1 -LogProfile hvsocket` - HvSocket-specific tracing\n\n### 10) Reporting a Windows crash (BSOD)\n\nTo collect a kernel crash dump, first run the following command in an elevated command prompt:\n\n```\nreg.exe add HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\CrashControl /v AlwaysKeepMemoryDump  /t REG_DWORD /d 1 /f\n```\n\nThen reproduce the issue, and let the machine crash and reboot.\n\nAfter reboot, the kernel dump will be in `%SystemRoot%\\MEMORY.DMP` (unless this path has been overridden in the advanced system settings).\n\nPlease send this dump to: secure@microsoft.com .\nMake sure that the email body contains:\n\n- The GitHub issue number, if any\n- That this dump is intended for the WSL team\n\n### 11) Reporting a WSL process crash\n\nThe easiest way to report a WSL process crash is by [collecting a user-mode crash dump](https://learn.microsoft.com/windows/win32/wer/collecting-user-mode-dumps).\n\nTo collect dumps of all running WSL processes, please open a PowerShell prompt with admin privileges, navigate to a folder where you'd like to put your log files and run these commands: \n\n```\nInvoke-WebRequest -UseBasicParsing \"https://raw.githubusercontent.com/microsoft/WSL/master/diagnostics/collect-wsl-logs.ps1\" -OutFile collect-wsl-logs.ps1\nSet-ExecutionPolicy Bypass -Scope Process -Force\n.\\collect-wsl-logs.ps1 -Dump\n```\n\nThe script will output the path to the log file when it is done.\n\n#### Enable automatic crash dump collection\n\nIf your crash is sporadic or hard to reproduce, please enable automatic crash dumps to catch logs for this behavior: \n\n```\nmd C:\\crashes\nreg.exe add \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps\" /f\nreg.exe add \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps\" /v DumpFolder /t REG_EXPAND_SZ /d C:\\crashes /f\nreg.exe add \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps\" /v DumpType /t REG_DWORD /d 2 /f\n```\n\nCrash dumps will then automatically be written to C:\\crashes.\n\nOnce you're done, crash dump collection can be disabled by running the following command in an elevated command prompt:\n\n```\nreg.exe delete \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps\" /f\n```\n\n### 12) Collect wslservice time travel debugging traces\n\nTo collect time travel debugging traces:\n\n1) [Install WinDbg preview](https://apps.microsoft.com/store/detail/windbg-preview/9PGJGD53TN86?hl=en-us&gl=us&rtc=1)\n\n2) Open WinDbg preview as administrator by running `windbgx` in an elevated command prompt\n\n3) Navigate to `file` -> `Attach to process`\n\n4) Check `Record with Time Travel Debugging` (at the bottom right)\n\n4) Check `Show processes from all users` (at the bottom)\n\n5) Select `wslservice.exe`. Note, if wslservice.exe is not running, you make it start it with: `wsl.exe -l`\n\n6) Click `Configure and Record` (write down the folder you chose for the traces)\n\n7) Reproduce the issue\n\n8) Go back to WinDbg and click `Stop and Debug`\n\n9) Once the trace is done collecting, click `Stop Debugging` and close WinDbg\n\n10) Go to the folder where the trace was collected, and locate the .run file. It should look like: `wslservice*.run`\n\n11) Share that file on the issue\n"
  },
  {
    "path": "DATA_AND_PRIVACY.md",
    "content": "# WSL data & privacy\n\n## Overview\n\nWSL collects diagnostic data using Windows telemetry, just like other Windows components. You can disable this by opening Windows Settings, navigating to Privacy and Security -> Diagnostics & Feedback and disabling 'Diagnostic data'. You can also view all diagnostic data that you are sending in that menu using the 'View diagnostic data' option. \n\nFor more information please read the [Microsoft privacy statement](https://www.microsoft.com/privacy/privacystatement).\n\n## What does WSL collect?\n\n1. Usage\n   - Understanding what features and settings are most often used in WSL helps us make decisions on where to focus our time and energy.\n2. Stability\n   - Monitoring bugs and system crashes assists us in prioritizing the most urgent issues.\n3. Performance\n   - Assessing the performance of WSL gives us an understanding of what runtimes / components could be causing slow downs. This supports our commitment in providing you a speedy and effective WSL. \n\nYou can search for WSL telemetry events by looking for calls to `WSL_LOG_TELEMETRY` in the source code of this repository."
  },
  {
    "path": "Directory.Build.Props",
    "content": "<Project>\n   <PropertyGroup>\n      <UseMultiToolTask>true</UseMultiToolTask>\n      <EnforceProcessCountAcrossBuilds>true</EnforceProcessCountAcrossBuilds>\n   </PropertyGroup>\n   <ItemDefinitionGroup>\n      <CustomBuild>\n         <BuildInParallel>true</BuildInParallel>\n      </CustomBuild>\n   </ItemDefinitionGroup>\n</Project>"
  },
  {
    "path": "LICENSE",
    "content": "    MIT License\n\n    Copyright (c) Microsoft Corporation.\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in all\n    copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n    SOFTWARE\n"
  },
  {
    "path": "NOTICE.txt",
    "content": "NOTICES AND INFORMATION\nDo Not Translate or Localize\n\nThis software incorporates material from third parties.\nMicrosoft makes certain open source code available at https://3rdpartysource.microsoft.com,\nor you may send a check or money order for US $5.00, including the product name,\nthe open source component name, platform, and version number, to:\n\nSource Code Compliance Team\nMicrosoft Corporation\nOne Microsoft Way\nRedmond, WA 98052\nUSA\n\nNotwithstanding any other terms, you may reverse engineer this software to the extent\nrequired to debug changes to any libraries licensed under the GNU Lesser General Public License.\n\n---------------------------------------------------------\n\nllvm/llvm-project d32170dbd5b0d54436537b6b75beaf44324e0c28 - Apache-2.0 WITH LLVM-exception\n\n\n(c) (tm)\n(c) Value A\n(c) auto AS\n(c) Value Sd\n(c) auto AST\n(c) CXType PT\n(c) Type I1Ty\n(c) Value RHS\n(c) X1 ... X1\n(c) Type I32Ty\n(c) Value Elt0\nCopyright 2014\n(c) Type Int8Ty\n(c) Type V8x8Ty\n(c) Value FMSub\n(c) unsigned AS\n(c) Constant One\n(c) Type FloatTy\n(c) Type Int16Ty\n(c) Type Int32Ty\n(c) Type Int64Ty\n(c) Value AllocA\n(c) Value Alloca\nAlloc1 a1 (c) R1\nAlloc1 a2 (c) R1\n(c) FileID MainID\n(c) Lower C Upper\n(c) Stmt NumExprs\n(c) Type DoubleTy\n(c) Type I32PtrTy\n(c) Value AllocaA\nCoproc, Opc1, CRm\n(c) ConstantInt C2\n(c) EUR CHECK-NEXT\n(c) Scale 1 Offset\n(c) State UNQUOTED\n(c) Type IndexType\nCopyright (c) 2012\nCopyright (c) 2013\nCopyright (c) 2019\nExprs new (c) Stmt\n(c) ExprResult NewE\n(c) Metadata Ops MD\n(c) SelectInst SelI\n(c) SmallVector MDs\n(c) TransferBatch B\n(c) Type Int32PtrTy\n(c) Type Int64PtrTy\n(c) BasicBlock Entry\n(c) CXXRecordDecl CD\n(c) FunctionType FTy\n(c) Offset C- Offset\nCopyright 2010 INRIA\nCopyright 2011 INRIA\nCopyright 2012 INRIA\nCopyright 2014 INRIA\nCopyright 2015 INRIA\nCopyright 2016 INRIA\n(c) FunctionType FnTy\n(c) QualType ResultTy\n(c) SourceRange Range\nAsmToks new (c) Token\nSubExprs new (c) Stmt\n(c) 2008 Red Hat, Inc.\n(c) 2011 Anthony Green\n(c) BasicBlock EntryBB\n(c) FunctionType FFnTy\n(c) FunctionType IFnTy\n(c) SourceLocation Loc\nCondition (c) Branches\nContext (c) LineToUnit\nCopyright (c) 2013 IBM\n(c) ErrMsg Stream Error\n(c) SmallVector NewVars\n(c) SmallVector ToErase\nBbb kind copyright' Bbb\n(c) Constant PosDivisorC\nCommonPtr new (c) Common\nCopyright (c) 2012 CHECK\n(c) CharUnits AlignedSize\n(c) Constant NullV2I32Ptr\n(c) Constant PosDividendC\nCopyright 2010-2011 INRIA\nCopyright 2014-2015 INRIA\nCOPYRIGHT,v 1.3 2003/06/02\nCoproc, Opc1, Rt, Rt2, CRm\nCopyright 2003 Google Inc.\nCopyright 2008 Google Inc.\nCopyright 2009 Google Inc.\nCopyright 2015 Google Inc.\nCopyright 2018 Google Inc.\nCopyright The LLVM project\ncoprime. APInt A HugePrime\ncopyrighted by the author.\n(c) IdentifierInfo NumExprs\n(c) VectorType Int8PtrVecTy\n(c) Willem van Schaik, 1999\nCopyright (c) 1997, Phillip\nCopyright 2005, Google Inc.\nCopyright 2006, Google Inc.\nCopyright 2007, Google Inc.\nCopyright 2008, Google Inc.\nCopyright 2012 Google, Inc.\nCopyright 2013, Google Inc.\nCopyright 2015, Google Inc.\nInitCache (c) TransferBatch\ncopyright u'2015, LLVM Team\nCopyright 2006, Dean Edwards\n(c) CHECK-NEXT 194 CHECK-NEXT\n(c) CXXBaseSpecifier NumBases\n(c) Designator NumDesignators\n(c) StringLiteral NumClobbers\nConstraints new (c) StringRef\nCopyright (c) 2002 Bo Thorsen\nCopyright (c) 2010 Apple Inc.\nCopyright (c) by P.J. Plauger\nCopyright 2010 Zencoder, Inc.\nCopyright 2017 Roman Lebedev.\nParamInfo new (c) ParmVarDecl\nCopyright (c) 2000 Software AG\nCopyright (c) 2002 Roger Sayle\nCopyright (c) 2008 David Daney\nCopyright (c) 2009 Google Inc.\nCopyright (c) 2016 Aaron Watry\nCopyright (c) 2017 Google Inc.\nCopyright (c) 2018 Jim Ingham.\nCopyright 2009 The Go Authors.\nCopyright 2010 The Go Authors.\nCopyright 2011 The Go Authors.\nCopyright 2012 The Go Authors.\nCopyright 2013 The Go Authors.\nCopyright 2014 The Go Authors.\nCopyright 2015 The Go Authors.\n(c) (3) educational corporation\n(c) CHECK-NEXT foo() CHECK-NEXT\n(copr0) ConstantDataVector CDV1\n(copr1) ConstantDataVector CDV2\nCopyright (c) 1994 X Consortium\nCopyright (c) 2008 Matteo Frigo\nCopyright (c) 2009 Matteo Frigo\nCopyright (c) 2010 CodeSourcery\nCopyright (c) 2011 Kyle Moffett\nCopyright (c) 2011 Tilera Corp.\nCopyright (c) 2011 Timothy Wall\nCopyright (c) 2012 Peter Harris\nCopyright (c) 2012 Tilera Corp.\nCopyright 2008-2010 Apple, Inc.\nCopyright 2015 Sven Verdoolaege\nCopyright 2016 Sven Verdoolaege\nCopyright 2017 Sven Verdoolaege\nCopyright 2018 Sven Verdoolaege\nCopyright, License, and Patents\nOther Coprocessor Instructions.\n(c) StringLiteral NumConstraints\nCopyright (c) 1996 Red Hat, Inc.\nCopyright (c) 2002 Ranjit Mathew\nCopyright (c) 2004 Anthony Green\nCopyright (c) 2004 Simon Posnjak\nCopyright (c) 2008 Anthony Green\nCopyright (c) 2008 Red Hat, Inc.\nCopyright (c) 2011 Anthony Green\nCopyright (c) 2012 Anthony Green\nCopyright (c) 2014 Red Hat, Inc.\nCopyright 2011 Sven Verdoolaege.\n(c) foo() pragma omp target teams\nCopyright (c) 1992 Henry Spencer.\nCopyright (c) 2000 John Hornkvist\nCopyright (c) 2001 John Hornkvist\nCopyright (c) 2006 Kirill Simonov\nCopyright (c) 2012 Alan Hourihane\nCopyright 2001-2004 Unicode, Inc.\ncopyright u'2003- d, LLVM Project\ncopyright u'2011- d, LLVM Project\n(c) CyclesSaved + LocalCyclesSaved\nCopyright (c) 1999-2007 Apple Inc.\nCopyright (c) 2009 The Go Authors.\nCopyright (c) 2009-2019 Polly Team\nCopyright (c) 2012 Thorsten Glaser\nCopyright (c) 2013 Tensilica, Inc.\nCopyright 2012 Universiteit Leiden\nCopyright 2016-2017 Tobias Grosser\ncopyright u'2007- d, The LLDB Team\ncopyright u'2013- d, Analyzer Team\nConstraint new (c) AtomicConstraint\nCopyright (c) 1998 Cygnus Solutions\nCopyright (c) 1998 Geoffrey Keating\nCopyright (c) 2001 David E. O'Brien\nCopyright (c) 2013 Mentor Graphics.\nCopyright (c) 2015the LLVM Project.\nCopyright 2009-2010 The Go Authors.\ncopyright u'2007- d, The Clang Team\ncopyright u'2010- d, The Polly Team\ncopyright u'2011-2018, LLVM Project\nCopyright (C ) Microsoft Corporation\nCopyright (c) 2000, 2007 Software AG\nCopyright (c) 2001 Alexander Peslyak\nCopyright (c) 2012, 2013 Xilinx, Inc\nCopyright (c) 2014, 2015 Google Inc.\nCopyright (c) 2019 The MLIR Authors.\nCopyright (c) Microsoft Corporation.\nCopyright 2009, 2010 The Go Authors.\nCopyright 2015-2016 Sven Verdoolaege\nCopyright 2016, 2017 Tobias Grosser.\nCopyright 2016-2017 Sven Verdoolaege\n(c) PointerType InitPtrType InitValue\nCopyright (c) 1996-2003 Red Hat, Inc.\nCopyright (c) 1996-2004 Red Hat, Inc.\nCopyright (c) 1999-2003 Steve Purcell\nCopyright (c) 2012-2016, Yann Collet.\nCopyright 2011,2015 Sven Verdoolaege.\nCopyright (c) 1996, 1998 Red Hat, Inc.\nCopyright (c) 1998, 2008 Red Hat, Inc.\nCopyright (c) 1999, 2008 Red Hat, Inc.\nCopyright (c) 2004 Renesas Technology.\nCopyright (c) 2008 Christian Haggstrom\nCopyright (c) 2008, 2010 Red Hat, Inc.\nCopyright (c) 2011, 2012 Anthony Green\nCopyright (c) 2011, 2013 Anthony Green\nCopyright (c) 2011, 2014 Anthony Green\nCopyright (c) 2012, 2013 Anthony Green\nCopyright (c) 2012, 2014 Anthony Green\nCopyright 2007-2010 by the Sphinx team\n(c) 2006 Free Software Foundation, Inc.\n(c) CHECK-NEXT T break CHECK-NEXT Preds\nCopyright (c) 1998, 2012 Andreas Schwab\nCopyright (c) 2002-2004 Tim J. Robbins.\nCopyright (c) 2005 Free Standards Group\nCopyright 2005-2007 Universiteit Leiden\nCopyright 2006-2007 Universiteit Leiden\nCopyright 2012 Ecole Normale Superieure\nCopyright 2013 Ecole Normale Superieure\nCopyright 2014 Ecole Normale Superieure\nCopyright 2016 Ismael Jimenez Martinez.\nPortions Copyright 2009 The Go Authors.\nin LLVM, Diploma Thesis, (c) April 2011\n(c) StringRef NumClobbers // FIXME Avoid\nCopyright (c) 1996-1998 John D. Polstra.\nCopyright (c) 2002-2008, 2012 Kaz Kojima\nCopyright (c) 1997-2019 Intel Corporation\nCopyright (c) 2005 Axis Communications AB\nCopyright (c) 2010-2015 Benjamin Peterson\nCopyright 1992, 1993, 1994 Henry Spencer.\ncoproc_option_imm Operand let PrintMethod\n(c) CXXCtorInitializer NumIvarInitializers\nCopyright (c) 2000 Hewlett Packard Company\nCopyright (c) 2002 Bo Thorsen <bo@suse.de>\nCopyright (c) 1996-2003, 2010 Red Hat, Inc.\nCopyright (c) 2004 eXtensible Systems, Inc.\nCopyright (c) 2009-2014 by the contributors\nCopyright (c) 2009-2015 by the contributors\nCopyright (c) 2009-2016 by the contributors\nCopyright (c) 2009-2019 by the contributors\nCopyright (c) 2011 Free Software Foundation\nCopyright (c) 2011-2014 by the contributors\nCopyright (c) 2011-2019 by the contributors\nCopyright (c) 2013 Imagination Technologies\nCopyright (c) 2017-2019 by the contributors\n(c) CHECK-NEXT Preds (1) B4 CHECK-NEXT Succs\n(c) CHECK-NEXT Preds (1) B5 CHECK-NEXT Succs\n(c) CHECK-NEXT Preds (1) B7 CHECK-NEXT Succs\nCoprocNumAsmOperand AsmOperandClass let Name\nCoprocRegAsmOperand AsmOperandClass let Name\nCopyright (c) 1993 by Sun Microsystems, Inc.\nCopyright (c) 1996, 1998, 2005 Red Hat, Inc.\nCopyright (c) 1996, 1998, 2007 Red Hat, Inc.\nCopyright (c) 1998, 2008, 2011 Red Hat, Inc.\nCopyright (c) 1999, 2007, 2008 Red Hat, Inc.\nCopyright (c) 2004 by Sun Microsystems, Inc.\nCopyright (c) 2007, 2009, 2010 Red Hat, Inc.\nCopyright (c) 2011, 2012, 2013 Anthony Green\nCopyright 2012,2014 Ecole Normale Superieure\nCopyright 2012-2013 Ecole Normale Superieure\nCopyright 2012-2014 Ecole Normale Superieure\nCopyright 2013-2014 Ecole Normale Superieure\nCopyright (c) 1992, 1993, 1994 Henry Spencer.\nCopyright (c) 2002-2007 Michael J. Fromberger\nCopyright (c) 2009, 2010, 2011, 2012 ARM Ltd.\nCopyright (c) 2012, 2013 Anthony Green Target\nCopyright 2000 Free Software Foundation, Inc.\nCopyright 2004 Free Software Foundation, Inc.\nCopyright (c) 2008 Ryan McCabe <ryan@numb.org>\n(c) 2003-2004 Randolph Chung <tausq@debian.org>\nCoprocOptionAsmOperand AsmOperandClass let Name\nCopyright (c) 2003, 2004, 2006, 2008 Kaz Kojima\nCopyright (c) 2014 Advanced Micro Devices, Inc.\nCopyright (c) 2015 Advanced Micro Devices, Inc.\nCopyright (c) 1994-1999 Lucent Technologies Inc.\nCopyright (c) 2002, 2007 Bo Thorsen <bo@suse.de>\nCopyright (c) 2013 Imagination Technologies Ltd.\n(c) Designator NumDesigs NumDesignators NumDesigs\nCopyright (c) 1992, 1993 UNIX International, Inc.\nCopyright (c) 1996-2003, 2007, 2008 Red Hat, Inc.\nCopyright (c) 2004 Free Software Foundation, Inc.\nCopyright (c) 2006 Free Software Foundation, Inc.\nCopyright (c) 2007 Free Software Foundation, Inc.\nCopyright (c) 2008 Free Software Foundation, Inc.\nCopyright (c) 2009 Free Software Foundation, Inc.\nCopyright (c) 2011 Free Software Foundation, Inc.\nCopyright (c) 2012 Free Software Foundation, Inc.\nCopyright (c) 2012, Noah Spurrier <noah@noah.org>\nCopyright (c) 2013-2014, Pexpect development team\nCopyright (c) 2013-2016, Pexpect development team\nCopyright (c) 2014 Free Software Foundation, Inc.\nCopyright (c) 2015 Paul Norman <penorman@mac.com>\nCopyright (c) 2016 Aaron Watry <awatry@gmail.com>\nCopyright extcopyright~ he year the LLVM Project.\n(c) CHECK-NEXT Preds (3) B3 B4 B2 CHECK-NEXT Succs\n(c) CHECK-NEXT Preds (3) B3 B5 B6 CHECK-NEXT Succs\nCopyright (c) 1996, 2007, 2008, 2011 Red Hat, Inc.\nCopyright (c) 1998, 2001, 2007, 2008 Red Hat, Inc.\nCopyright (c) 1998, 2007, 2008, 2012 Red Hat, Inc.\nCopyright (c) 2000, 2003, 2004, 2008 Red Hat, Inc.\nCopyright (c) 2003-2010 Python Software Foundation\nCopyright (c) 2012 Zack Weinberg <zackw@panix.com>\nCopyright 1992-2018 Free Software Foundation, Inc.\nCopyright 2008-2009 Katholieke Universiteit Leuven\n(c) Stmt NumExprs std::copy Exprs, Exprs + NumExprs\nCopyright (c) 1996, MPEG Software Simulation Group.\nCopyright (c) 2003 Jakub Jelinek <jakub@redhat.com>\nCopyright (c) 2008 Guido U. Draheim <guidod@gmx.de>\nCopyright (c) 2008 Stepan Kasal <skasal@redhat.com>\nCopyright (c) 2011 Plausible Labs Cooperative, Inc.\nCopyright (c) 2012 Qualcomm Innovation Center, Inc.\nCopyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>\nCopyright (c) 2013 Synopsys, Inc. (www.synopsys.com)\nCopyright (c) 2013 Synposys, Inc. (www.synopsys.com)\nCopyright (c) 2014,2015 Advanced Micro Devices, Inc.\nCopyright (c) 2002, 2003, 2004, 2006, 2008 Kaz Kojima\nCopyright (c) 2003, 2004, 2006, 2007, 2012 Kaz Kojima\nCopyright (c) 2013 Miodrag Vallat. <miod@openbsd.org>\nCopyright (c) 2014, 2015 Advanced Micro Devices, Inc.\nCopyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>\nCopyright (c) 1994-2014 Free Software Foundation, Inc.\nCopyright (c) 1994-2017 Free Software Foundation, Inc.\nCopyright (c) 1996, 2003-2004, 2007-2008 Red Hat, Inc.\nCopyright (c) 1996-2014 Free Software Foundation, Inc.\nCopyright (c) 1996-2015 Free Software Foundation, Inc.\nCopyright (c) 1996-2017 Free Software Foundation, Inc.\nCopyright (c) 1997-2014 Free Software Foundation, Inc.\nCopyright (c) 1997-2017 Free Software Foundation, Inc.\nCopyright (c) 1999-2013 Free Software Foundation, Inc.\nCopyright (c) 1999-2014 Free Software Foundation, Inc.\nCopyright (c) 1999-2017 Free Software Foundation, Inc.\nCopyright (c) 2001-2014 Free Software Foundation, Inc.\nCopyright (c) 2001-2017 Free Software Foundation, Inc.\nCopyright (c) 2002-2014 Free Software Foundation, Inc.\nCopyright (c) 2002-2017 Free Software Foundation, Inc.\nCopyright (c) 2003-2014 Free Software Foundation, Inc.\nCopyright (c) 2003-2017 Free Software Foundation, Inc.\nCopyright (c) 2004-2014 Free Software Foundation, Inc.\nCopyright (c) 2004-2015 Free Software Foundation, Inc.\nCopyright (c) 2004-2017 Free Software Foundation, Inc.\nCopyright (c) 2006-2014 Free Software Foundation, Inc.\nCopyright (c) 2006-2017 Free Software Foundation, Inc.\nCopyright (c) 2007, 2008 Free Software Foundation, Inc\nCopyright (c) 2008 Sven Verdoolaege <skimo@kotnet.org>\nCopyright (c) 2009-2014 Free Software Foundation, Inc.\nCopyright (c) 2009-2017 Free Software Foundation, Inc.\nCopyright (c) 2010-2015 Free Software Foundation, Inc.\nCopyright (c) 2010-2017 Free Software Foundation, Inc.\nCopyright (c) 2011-2013 Free Software Foundation, Inc.\nCopyright (c) 2011-2014 Free Software Foundation, Inc.\nCopyright (c) 2012-2014 Free Software Foundation, Inc.\nCopyright (c) 2012-2015 Free Software Foundation, Inc.\nCopyright (c) 2013-2014 Free Software Foundation, Inc.\nCopyright (c) 2013-2015 Free Software Foundation, Inc.\nCopyright (c) 2002, 2009 Free Software Foundation, Inc.\nCopyright (c) 2004, 2005 Free Software Foundation, Inc.\nCopyright (c) 2004, 2010 Free Software Foundation, Inc.\nCopyright (c) 2006, 2008 Free Software Foundation, Inc.\nCopyright (c) 2008, 2010 Free Software Foundation, Inc.\nCopyright (c) 2014 Sebastian Macke <sebastian@macke.de>\nCopyright (c) 2015 Moritz Klammler <moritz@klammler.eu>\nCopyright (c) 1996, 1997, 2003, 2004, 2008 Red Hat, Inc.\nCopyright (c) 1998, 2001, 2007, 2008, 2011, 2014 Red Hat\nCopyright (c) 2009 Bradley Smith <brad@brad-smith.co.uk>\nCopyright (c) 2013 Jesse Towner <jessetowner@lavabit.com>\nCopyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>\n(c) Type AtExitFuncArgs VoidStar FunctionType AtExitFuncTy\ncopyright (c) 1991-2011, Thomas G. Lane, Guido Vollbeding.\n(c) GlobalVariable Handle new GlobalVariable M, DsoHandleTy\n(c) Type ArgTys Int32Ty, Int32Ty, Int32Ty FunctionType FnTy\nCopyright (c) 2004 Scott James Remnant <scott@netsplit.com>\nCopyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>\nCopyright (c) 2009 Steven G. Johnson <stevenj@alum.mit.edu>\nCopyright (c) 2004, 2011-2015 Free Software Foundation, Inc.\nCopyright (c) 2007, 2008, 2010 Free Software Foundation, Inc\nCopyright (c) 2007, 2009, 2010 Free Software Foundation, Inc\nCopyright (c) 2013 Victor Oliveira <victormatheus@gmail.com>\nCopyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier\n(c) DeclRefExpr DR M.makeDeclRefExpr(PV) ImplicitCastExpr ICE\n(c) IdentifierInfo NumExprs std::copy Names, Names + NumExprs\nCopyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>\nCopyright (c) 2001, 2003, 2005 Free Software Foundation, Inc.\nCopyright (c) 2002, 2003, 2009 Free Software Foundation, Inc.\nCopyright (c) 2004, 2005, 2012 Free Software Foundation, Inc.\nCopyright (c) 2006, 2008, 2010 Free Software Foundation, Inc.\n(c) BranchInst BI BranchInst::Create(Exit, Exit, False, Entry)\nCopyright (c) 1996, 1998, 1999, 2001, 2007, 2008 Red Hat, Inc.\nCopyright (c) 1996, 1998, 2001, 2002, 2003, 2005 Red Hat, Inc.\nCopyright (c) 1996, 1998, 2005, 2007, 2009, 2010 Red Hat, Inc.\nCopyright (c) 1996,1998,2001-2003,2005,2008,2010 Red Hat, Inc.\nCopyright (c) 1994 The Regents of the University of California.\nCopyright (c) 1996-2014 Anthony Green, Red Hat, Inc and others.\nCopyright (c) 1992-1996, 1998-2012 Free Software Foundation, Inc.\nCopyright (c) 1996-2001, 2003-2015 Free Software Foundation, Inc.\nCopyright (c) 2001, 2003, 2005, 2008 Free Software Foundation, Inc.\nCopyright (c) 2001, 2003, 2005, 2011 Free Software Foundation, Inc.\nCopyright (c) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.\nCopyright (c) 2003-2019 University of Illinois at Urbana-Champaign.\nCopyright (c) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.\nCopyright (c) 2004, 2005, 2007, 2009 Free Software Foundation, Inc.\nCopyright (c) 2007-2018 University of Illinois at Urbana-Champaign.\nCopyright (c) 2007-2019 University of Illinois at Urbana-Champaign.\nCopyright (c) 2002, 2003, 2004, 2010, Free Software Foundation, Inc.\nCopyright (c) 2006-2009 Steven J. Bethard <steven.bethard@gmail.com>\nCopyright (c) 1992, 1993 The Regents of the University of California.\n(c) StringLiteral NumClobbers std::copy Clobbers, Clobbers + NumClobbers\nCopyright (c) 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).\nCopyright (c) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc.\nCopyright (c) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc.\nCopyright (c) 2001, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.\nCopyright (c) 2001, 2003, 2005, 2008, 2011 Free Software Foundation, Inc.\nCopyright (c) 2002, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.\nCopyright (c) 2003, 2004, 2005, 2006, 2011 Free Software Foundation, Inc.\nCopyright (c) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc.\nCopyright (c) 1992, 1993, 1994 The Regents of the University of California.\nCopyright (c) 2004-2005, 2007-2008, 2011-2015 Free Software Foundation, Inc.\nCopyright (c) 2004-2005, 2007-2009, 2011-2015 Free Software Foundation, Inc.\nCopyright (c) 2004-2005, 2007, 2009, 2011-2015 Free Software Foundation, Inc.\nCopyright (c) 2012 Alexandre K. I. de Mendonca <alexandre.keunecke@gmail.com>\nCopyright (c) 2001, 2002, 2003, 2005, 2008, 2010 Free Software Foundation, Inc.\nCopyright (c) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.\n(c) StringLiteral NumConstraints std::copy Constraints, Constraints + NumConstraints\nCopyright (c) 1996, 1997, 2000, 2001, 2003, 2005, 2008 Free Software Foundation, Inc.\nCopyright (c) 1999, 2000, 2001, 2003, 2004, 2005, 2008 Free Software Foundation, Inc.\nCopyright (c) 2002, 2003, 2005, 2006, 2007, 2008, 2011 Free Software Foundation, Inc.\nCopyright (c) 1995, 1996, 1997, 2003, 2004, 2005, 2007, 2009 Free Software Foundation, Inc.\nCopyright (c) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 Free Software Foundation, Inc.\nCopyright (c) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 Free Software Foundation, Inc.\nCopyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 Free Software Foundation, Inc.\nCopyright (c) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008 Free Software Foundation, Inc.\nCopyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009 Free Software Foundation, Inc.\nCopyright (c) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2011 Free Software Foundation, Inc.\nCopyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010, 2011 Free Software Foundation, Inc.\nCopyright (c) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\nCopyright (c) 2012 Alexandre K. I. de Mendonca <alexandre.keunecke@gmail.com> , Paulo Pizarro <paulo.pizarro@gmail.com>\nCopyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 Free Software Foundation, Inc.\nCopyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.\nCopyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.\nCopyright (c) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\nCopyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\nCopyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.\nCopyright (c) 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2011, 2014 Free Software Foundation, Inc.\nCopyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\nCopyright (c) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.\nCopyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\nCopyright (c) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\nCopyright (c) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\n\nApache-2.0 WITH LLVM-exception\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nCommunityToolkit.Mvvm 8.4.0 - MIT\n\n\nCopyright (c) 2021 Sergio Pedri\nCopyright (c) 2020 Michael Dietrich\nCopyright (c) Microsoft Corporation\n(c) .NET Foundation and Contributors\nCopyright (c) 2009 - 2018 Laurent Bugnion\nCopyright (c) .NET Foundation and Contributors\nCopyright (c) 2017 Pedro Lamas, http://www.pedrolamas.com\n\n# .NET Community Toolkit\n\nCopyright © .NET Foundation and Contributors\n\nAll rights reserved.\n\n## MIT License (MIT)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nCommunityToolkit.WinUI.Animations 8.2.250402 - MIT\n\n\n(c) .NET Foundation and Contributors\nCopyright (c) .NET Foundation and Contributors\n\n# Windows Community Toolkit\n\nCopyright © .NET Foundation and Contributors\n\nAll rights reserved.\n\n## MIT License (MIT)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nCommunityToolkit.WinUI.Controls.SettingsControls 8.2.250402 - MIT\n\n\n(c) .NET Foundation and Contributors\nCopyright (c) .NET Foundation and Contributors\n\n# Windows Community Toolkit\n\nCopyright © .NET Foundation and Contributors\n\nAll rights reserved.\n\n## MIT License (MIT)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nMicrosoft.Extensions.Hosting 10.0.0 - MIT\n\n\nCopyright (c) 2021\nCopyright (c) Six Labors\n(c) Microsoft Corporation\nCopyright (c) 2022 FormatJS\nCopyright (c) Andrew Arnott\nCopyright 2019 LLVM Project\nCopyright (c) 1998 Microsoft\nCopyright 2018 Daniel Lemire\nCopyright (c) .NET Foundation\nCopyright (c) 2011, Google Inc.\nCopyright (c) 2020 Dan Shechter\n(c) 1997-2005 Sean Eron Anderson\nCopyright (c) 2015 Andrew Gallant\nCopyright (c) 2022, Wojciech Mula\nCopyright (c) 2017 Yoshifumi Kawai\nCopyright (c) 2022, Geoff Langdale\nCopyright (c) 2005-2020 Rich Felker\nCopyright (c) 2012-2021 Yann Collet\nCopyright (c) Microsoft Corporation\nCopyright (c) 2007 James Newton-King\nCopyright (c) 1991-2024 Unicode, Inc.\nCopyright (c) 2013-2017, Alfred Klomp\nCopyright (c) 2018 Nemanja Mijailovic\nCopyright 2012 the V8 project authors\nCopyright (c) 1999 Lucent Technologies\nCopyright (c) 2008-2016, Wojciech Mula\nCopyright (c) 2011-2020 Microsoft Corp\nCopyright (c) 2015-2017, Wojciech Mula\nCopyright (c) 2015-2018, Wojciech Mula\nCopyright (c) 2005-2007, Nick Galbreath\nCopyright (c) 2015 The Chromium Authors\nCopyright (c) 2018 Alexander Chermyanin\nCopyright (c) The Internet Society 1997\nCopyright (c) 2004-2006 Intel Corporation\nCopyright (c) 2011-2015 Intel Corporation\nCopyright (c) 2013-2017, Milosz Krajewski\nCopyright (c) 2016-2017, Matthieu Darbois\nCopyright (c) The Internet Society (2003)\nCopyright (c) .NET Foundation Contributors\n(c) 1995-2024 Jean-loup Gailly and Mark Adler\nCopyright (c) 2020 Mara Bos <m-ou.se@m-ou.se>\nCopyright (c) 2012 - present, Victor Zverovich\nCopyright (c) 2006 Jb Evain (jbevain@gmail.com)\nCopyright (c) 2008-2020 Advanced Micro Devices, Inc.\nCopyright (c) 2019 Microsoft Corporation, Daan Leijen\nCopyright (c) 2011 Novell, Inc (http://www.novell.com)\nCopyright (c) 2015 Xamarin, Inc (http://www.xamarin.com)\nCopyright (c) 2009, 2010, 2013-2016 by the Brotli Authors\nCopyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com\nCopyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.\nPortions (c) International Organization for Standardization 1986\nCopyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers\nCopyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip\nCopyright (c) 1980, 1986, 1993 The Regents of the University of California\nCopyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California\nCopyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass\n\nMIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nMicrosoft.Xaml.Behaviors.WinUI.Managed 3.0.0 - MIT\n\n\n(c) Microsoft Corporation\n\nMIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nmicrosoft/gsl 0f6dbc9e2915ef5c16830f3fa3565738de2a9230 - MIT\n\n\nCopyright 2008, Google Inc.\nCopyright (c) 2015 Microsoft Corporation.\n\nCopyright (c) 2015 Microsoft Corporation. All rights reserved. \n \nThis code is licensed under the MIT License (MIT). \n\nPermission is hereby granted, free of charge, to any person obtaining a copy \nof this software and associated documentation files (the \"Software\"), to deal \nin the Software without restriction, including without limitation the rights \nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies \nof the Software, and to permit persons to whom the Software is furnished to do \nso, subject to the following conditions: \n\nThe above copyright notice and this permission notice shall be included in all \ncopies or substantial portions of the Software. \n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR \nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE \nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN \nTHE SOFTWARE. \n\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nvswhere 3.1.7 - MIT\n\n\n(c) 2023 GitHub, Inc.\n(c) Microsoft Corporation\nCopyright (c) by P.J. Plauger\nCopyright (c) Microsoft Corporation\n\nMIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nWinUIEx 2.9.0 - MIT\n\n\nCopyright (c) Microsoft Corporation\nCopyright 2021-2025 - Morten Nielsen\nCopyright 2021-2025 - Morten Nielsen A\nCopyright (c) 2021-2025 - Morten Nielsen\n\nMIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nwix 5.0.2 - MS-RL\n\n\nCopyright James Newton-King 2008\nCopyright UpdateUrl DisableModify\nCopyright (c) Microsoft Corporation\n(c) .NET Foundation and contributors\nCopyright James Newton-King 2008 Json.NET\nCopyright AssemblyDescription AssemblyProduct\nCopyright (c) .NET Foundation and contributors\n\nMicrosoft Reciprocal License (Ms-RL)\n\nThis license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.\n\n   1. Definitions\n\n   The terms \"reproduce,\" \"reproduction,\" \"derivative works,\" and \"distribution\" have the same meaning here as under U.S. copyright law.\n\n   A \"contribution\" is the original software, or any additions or changes to the software.\n\n   A \"contributor\" is any person that distributes its contribution under this license.\n\n   \"Licensed patents\" are a contributor's patent claims that read directly on its contribution.\n\n   2. Grant of Rights\n\n      (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.\n\n      (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.\n\n   3. Conditions and Limitations\n\n      (A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary format), you must provide recipients the source code to that file along with a copy of this license, which license will govern that file. You may license other files that are entirely your own work and do not contain code from the software under any terms you choose.\n\n      (B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.\n\n      (C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.\n\n      (D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.\n\n      (E) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.\n\n      (F) The software is licensed \"as-is.\" You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.\n\n---------------------------------------------------------\n\n"
  },
  {
    "path": "README.md",
    "content": "# Welcome to the Windows Subsystem for Linux (WSL) repository\n\n<p align=\"center\">\n  <img src=\"./Images/Square44x44Logo.targetsize-256.png\" alt=\"WSL logo\"/>\n</p>\n\n[Learn more about WSL](https://aka.ms/wsldocs) | [Downloads & Release notes](https://github.com/microsoft/WSL/releases) | [Contributing to WSL](./CONTRIBUTING.md)\n\n## About\n\nWindows Subsystem for Linux (WSL) is a powerful way for you to run your Linux command-line tools, utilities and applications, all unmodified and directly on Windows without the overhead of a traditional virtual machine or dual boot setup.\n\nYou can install WSL right away by running this command inside of your Windows command line:\n\n```powershell\nwsl --install\n```\n\nYou can learn more about [best practices for setup](https://learn.microsoft.com/windows/wsl/setup/environment), [overviews of WSL](https://learn.microsoft.com/windows/wsl/about) and more at our [WSL documentation page](https://learn.microsoft.com/windows/wsl/).\n\n## Related repositories\n\nWSL also has related open source repositories:\n\n- [microsoft/WSL2-Linux-Kernel](https://github.com/microsoft/WSL2-Linux-Kernel) - The Linux kernel shipped with WSL\n- [microsoft/WSLg](https://github.com/microsoft/wslg) - Support for Linux GUI apps in WSL\n- [microsoftdocs/wsl](https://github.com/microsoftdocs/wsl) - WSL documentation at aka.ms/wsldocs\n\n## Contributing\n\nThis project welcomes contributions of all types, including coding features / bug fixes, documentation fixes, design proposals and more. \n\nWe ask that before you start working on a contribution, please read our [Contributor's Guide](./CONTRIBUTING.md).\n\nFor guidance on developing for WSL, please read the [developer docs](./doc/docs/dev-loop.md) for instructions on how to build WSL from source and details on its architecture.\n\n## Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](./CODE_OF_CONDUCT.md)\n\n## Trademarks\n\nThis project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft’s Trademark & Brand Guidelines](https://www.microsoft.com/legal/intellectualproperty/trademarks). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party’s policies.\n\n## Privacy and telemetry\n\nThe application logs basic diagnostic data (telemetry). For more information on privacy and what we collect, see our [data and privacy documentation](DATA_AND_PRIVACY.md).\n\nThe software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices."
  },
  {
    "path": "SECURITY.md",
    "content": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).\n\nIf you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.\n\n## Reporting Security Issues\n\n**Please do not report security vulnerabilities through public GitHub issues.**\n\nInstead, please [report them to the Microsoft Security Response Center (MSRC)](https://aka.ms/opensource/security/create-report).\n\nIf you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).\n\nYou should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [Microsoft Security Response Center](https://aka.ms/opensource/security/msrc). \n\nPlease include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:\n\n  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)\n  * Full paths of source file(s) related to the manifestation of the issue\n  * The location of the affected source code (tag/branch/commit or direct URL)\n  * Any special configuration required to reproduce the issue\n  * Step-by-step instructions to reproduce the issue\n  * Proof-of-concept or exploit code (if possible)\n  * Impact of the issue, including how an attacker might exploit the issue\n\nThis information will help us triage your report more quickly.\n\nIf you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.\n\n## Preferred Languages\n\nWe prefer all communications to be in English.\n\n## Policy\n\nMicrosoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).\n\n<!-- END MICROSOFT SECURITY.MD BLOCK -->"
  },
  {
    "path": "SUPPORT.md",
    "content": "# Support\r\n\r\n## How to file issues and get help  \r\n\r\nThis project uses [GitHub Issues][gh-issue] to [track bugs][gh-bug] and [feature requests][gh-feature]. Please search the existing issues before filing new issues to avoid duplicates.  For new issues, file your bug or \r\nfeature request as a new Issue.\r\n\r\nFor general information on the Windows Subsystem for Linux, including help, tutorials, how-tos and reference guides, please view our [WSL documentation](https://docs.microsoft.com/windows/wsl/). \r\n\r\n## Microsoft Support Policy\r\n\r\nTechnical Support for the use of WSL may also be available from Microsoft’s Customer Support Services (CSS). If you are a Premier or Unified Support customer, you may reach out to your account manager for further assistance. Otherwise, you may visit Microsoft’s [Support For Business site](https://support.serviceshub.microsoft.com/supportforbusiness/create) to [open a new support case for WSL](https://support.serviceshub.microsoft.com/supportforbusiness/create?sapid=9d5af292-506b-63ca-cb2c-7f4eaa380c56).\r\n\r\n\r\n[gh-issue]: https://github.com/microsoft/WSL/issues/new/choose\r\n[gh-bug]: https://github.com/microsoft/WSL/issues/new?assignees=&labels=&template=bug_report.md&title=\r\n[gh-feature]: https://github.com/microsoft/WSL/issues/new?assignees=&labels=feature&template=feature_request.md&title=\r\n"
  },
  {
    "path": "UserConfig.cmake.sample",
    "content": "# Sample user configuration\n\nmessage(STATUS \"Loading user configuration\")\n\n# Uncomment to enable development packages (smaller, faster to install)\n# # Note: .vhd files fail to mount via symlink / hardlink, so COPY is needed.\n# set(WSL_DEV_BINARY_PATH \"C:/wsldev\")\n\nif(WSL_DEV_BINARY_PATH)\n    file(MAKE_DIRECTORY ${WSL_DEV_BINARY_PATH})\n    file(CREATE_LINK \"${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/kernel\" \"${WSL_DEV_BINARY_PATH}/kernel\" SYMBOLIC)\n    file(COPY_FILE \"${WSLG_SOURCE_DIR}/${TARGET_PLATFORM}/system.vhd\" \"${WSL_DEV_BINARY_PATH}/system.vhd\" ONLY_IF_DIFFERENT)\n    file(COPY_FILE \"${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/modules.vhd\" \"${WSL_DEV_BINARY_PATH}/modules.vhd\" ONLY_IF_DIFFERENT)\n\n    # read-only VHDs need to be world readable to mount successfully.\n    execute_process(\n        COMMAND icacls.exe \"${WSL_DEV_BINARY_PATH}/system.vhd\" \"/grant:r\" \"Everyone:(R)\" /Q\n        COMMAND icacls.exe \"${WSL_DEV_BINARY_PATH}/modules.vhd\" \"/grant:r\" \"Everyone:(R)\" /Q\n        WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}\n        COMMAND_ERROR_IS_FATAL ANY)\n\n    file(CREATE_LINK \"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/msrdc.exe\" \"${WSL_DEV_BINARY_PATH}/msrdc.exe\" SYMBOLIC)\n    file(CREATE_LINK \"${WSLG_SOURCE_DIR}/wslg.rdp\" \"${WSL_DEV_BINARY_PATH}/wslg.rdp\" SYMBOLIC)\n    file(CREATE_LINK \"${WSLG_SOURCE_DIR}/wslg_desktop.rdp\" \"${WSL_DEV_BINARY_PATH}/wslg_desktop.rdp\" SYMBOLIC)\n    file(CREATE_LINK \"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/rdclientax.dll\" \"${WSL_DEV_BINARY_PATH}/rdclientax.dll\" SYMBOLIC)\n    file(CREATE_LINK \"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/rdpnanoTransport.dll\" \"${WSL_DEV_BINARY_PATH}/rdpnanoTransport.dll\" SYMBOLIC)\n    file(CREATE_LINK \"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/RdpWinStlHelper.dll\" \"${WSL_DEV_BINARY_PATH}/RdpWinStlHelper.dll\" SYMBOLIC)\n    file(CREATE_LINK \"${MSAL_SOURCE_DIR}/${TARGET_PLATFORM}/msal.wsl.proxy.exe\" \"${WSL_DEV_BINARY_PATH}/msal.wsl.proxy.exe\" SYMBOLIC)\n    file(CREATE_LINK \"${DIRECT3D_SOURCE_DIR}/lib/${TARGET_PLATFORM}\" \"${WSL_DEV_BINARY_PATH}/lib\" SYMBOLIC)\n\n    foreach(LANG ${SUPPORTED_LANGS})\n        file(CREATE_LINK \"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/${LANG}\" \"${WSL_DEV_BINARY_PATH}/${LANG}\" SYMBOLIC)\n    endforeach()\nendif()\n\n# # Uncomment to skip building, packaging and installing wslsettings\n# set(WSL_BUILD_WSL_SETTINGS false)\n\n# # Uncomment to generate a \"thin\" MSI package which builds and installs faster\n# set(WSL_BUILD_THIN_PACKAGE true)\n\n# # Uncomment to install the package as part of the build\n# set(WSL_POST_BUILD_COMMAND \"powershell;-ExecutionPolicy;Bypass;-NoProfile;-NonInteractive;./tools/deploy/deploy-to-host.ps1\")\n\n# # Uncomment to reduce the verbosity of the appx package build\n# set(WSL_SILENT_APPX_BUILD true)"
  },
  {
    "path": "cgmanifest.json",
    "content": "{\r\n    \"version\": 1,\r\n    \"$schema\": \"https://json.schemastore.org/component-detection-manifest.json\",\r\n    \"registrations\": [\r\n        {\r\n            \"component\": {\r\n                \"type\": \"git\",\r\n                \"git\": {\r\n                    \"repositoryUrl\": \"https://github.com/microsoft/GSL\",\r\n                    \"commitHash\": \"0f6dbc9e2915ef5c16830f3fa3565738de2a9230\"\r\n                }\r\n            }\r\n        },\r\n        {\r\n            \"component\": {\r\n                \"type\": \"git\",\r\n                \"git\": {\r\n                    \"repositoryUrl\": \"https://github.com/llvm/llvm-project\",\r\n                    \"commitHash\": \"d32170dbd5b0d54436537b6b75beaf44324e0c28\"\r\n                }\r\n            }\r\n        },\r\n        {\r\n            \"component\": {\r\n                \"type\": \"other\",\r\n                \"other\": {\r\n                    \"name\": \"libarchive\",\r\n                    \"version\": \"3.7.7\",\r\n                    \"downloadUrl\": \"https://github.com/libarchive/libarchive/releases/download/v3.7.7/libarchive-3.7.7.tar.gz\",\r\n                    \"hash\": \"sha1:918692098b11db61aff23684ab04f375e4a68f69\"\r\n                }\r\n            }\r\n        }\r\n    ]\r\n}"
  },
  {
    "path": "cloudtest/CMakeLists.txt",
    "content": "set(OUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/cloudtest)\nfile(MAKE_DIRECTORY ${OUT})\n\nif (${TARGET_PLATFORM} STREQUAL x64)\n    set(CLOUDTEST_IMAGES\n        \"wsl-test-image-rs_prerelease-2025-01-30\"\n        \"wsl-test-image-win11-23h2-ent-2024-11-18\"\n        \"wsl-test-image-2022-datacenter-g2-2024-09-10\"\n        \"wsl-test-image-win10-22h2-ent-g2-2024-09-10\")\n      \n\n    set(CLOUDTEST_TEST_PACKAGES \"Test_Packages_2025_07_28\")\n    set(DUMPTOOL_DROP \"DumpTool_X64_2025-01-27\")\nelseif (${TARGET_PLATFORM} STREQUAL arm64)\n    set(CLOUDTEST_IMAGES)\nelse()\n    message(FATAL_ERROR \"Unsupported target platform: ${TARGET_PLATFORM}\")\nendif()\n\n# Passed down to test-setup.ps1 to determine if -AllowUnsigned should be passed to Add-AppxPackage\nif (OFFICIAL_BUILD)\n    set(ALLOW_UNSIGNED_PACKAGE \"0\")\nelse()\n    set(ALLOW_UNSIGNED_PACKAGE \"1\")\nendif()\n\nfunction(add_test_group image version)\n    set(DIR ${OUT}/${image}-wsl${version})\n\n    file(MAKE_DIRECTORY ${DIR})\n\n    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/TestMap.xml.in ${DIR}/TestMap.xml)\n    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/TestGroup.xml.in ${DIR}/TestGroup.xml)\nendfunction()\n\nforeach(image ${CLOUDTEST_IMAGES})\n    add_test_group(\"${image}\" \"1\")\n    add_test_group(\"${image}\" \"2\")\nendforeach()\n"
  },
  {
    "path": "cloudtest/TestGroup.xml.in",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TestJobGroup EnableProcessJobObjectBreakaway=\"true\">\n  <ResourceSpec>\n    <Resource SKU=\"Standard_D4_v3\" Image=\"${image}\"/>\n  </ResourceSpec>\n  <Setup TimeoutMins=\"30\">\n    <BuildFiles>\n      <Copy Src=\"[drop]\\bundle\\Microsoft.WSL_${PACKAGE_VERSION}_x64_ARM64.msixbundle\" Dest=\"[WorkingDirectory]\" IsRecursive=\"false\"/>\n      <Copy Src=\"[drop]\\testbin\\${TARGET_PLATFORM}\\release\\*\" Dest=\"[WorkingDirectory]\\\" IsRecursive=\"true\" Writable=\"true\"/>\n      <Copy Src=\"[drop]\\testbin\\${TARGET_PLATFORM}\\test_distro.tar.xz\" Dest=\"[WorkingDirectory]\" IsRecursive=\"false\" Writable=\"true\"/>\n      <Copy Src=\"[drop]\\testbin\\test-setup.ps1\" Dest=\"[WorkingDirectory]\\\" IsRecursive=\"false\" />\n      <Copy Src=\"[drop]\\testbin\\CloudTest-Setup.bat\" Dest=\"[WorkingDirectory]\\\" IsRecursive=\"false\" />\n      <Copy Src=\"[drop]\\testbin\\wsl.wprp\" Dest=\"[WorkingDirectory]\\\" IsRecursive=\"false\" />\n      <Copy Src=\"[drop]\\testbin\\unit_tests\\*\" Dest=\"[WorkingDirectory]\\unit_tests\" IsRecursive=\"true\" Writable=\"true\"/>\n      <Copy Src=\"[test_packages]\\*\" Dest=\"[WorkingDirectory]\" IsRecursive=\"false\" />\n      <Copy Src=\"[dump_tool]\\DumpTool.exe\" Dest=\"[WorkingDirectory]\" IsRecursive=\"false\" />\n    </BuildFiles>\n    <Scripts>\n      <Script Path=\"[WorkingDirectory]\\CloudTest-Setup.bat\" Args=\"[WorkingDirectory] [LoggingDirectory]\" />\n    </Scripts>\n  </Setup>\n\n  <TestJob Name=\"CloudTest.Taef\" TimeoutMins=\"240\">\n    <Execution Type=\"TAEF\" Path=\"[WorkingDirectory]\\wsltests.dll\" Args=\"/p:bugReportDirectory=[LoggingDirectory]\\BugReportOutput /errorOnCrash /testmode:etwlogger /EtwLogger:WPRProfileFile=[WorkingDirectory]\\wsl.wprp /EtwLogger:WPRProfile=WSL /EtwLogger:SavePoint=ExecutionComplete /EtwLogger:RecordingScope=Execution /p:SetupScript=.\\test-setup.ps1 /p:Package=[WorkingDirectory]\\Microsoft.WSL_${PACKAGE_VERSION}_x64_ARM64.msixbundle /p:Version=${version} /p:AllowUnsigned=${ALLOW_UNSIGNED_PACKAGE} /p:UnitTestsPath=[WorkingDirectory]\\unit_tests /p:DistroPath=[WorkingDirectory]\\test_distro.tar.xz /p:DistroName=test_distro /logOutput:High /p:RedirectStdout=[LoggingDirectory]\\stdout.txt /p:RedirectStderr=[LoggingDirectory]\\stderr.txt  /p:KernelLogs=[LoggingDirectory]\\dmesg.txt /p:DumpFolder=[LoggingDirectory] /p:WerReport /p:LogDmesg /p:PipelineBuildId=${PIPELINE_BUILD_ID} /p:DumpTool=DumpTool.exe\" />\n  </TestJob>\n</TestJobGroup>\n"
  },
  {
    "path": "cloudtest/TestMap.xml.in",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TestMap>\n  <TestJobGroup Name=\"TaefTestsGroup\" TestSystem=\"Default\" ConfigPath=\"[BuildRoot]\\testbin\\${TARGET_PLATFORM}\\cloudtest\\${image}-wsl${version}\\TestGroup.xml\" IsActive=\"true\"/>\n  <Providers>\n    <Provider Type=\"PipelineArtifacts\">\n      <Properties>\n        <Add Name=\"CloudTest.ProviderCustomName\" Value=\"[drop]\" />\n        <Add Name=\"PipelineArtifactBuildUrl\" Value=\"https://${VSO_ORG}.visualstudio.com/${VSO_PROJECT}/_build/results?buildId=${PIPELINE_BUILD_ID}\"/>\n        <Add Name=\"PipelineArtifactName\" Value=\"drop_wsl_build\"/>\n      </Properties>\n    </Provider>\n    <Provider Type=\"VSODrop\">\n      <Properties>\n        <Add Name=\"CloudTest.ProviderCustomName\" Value=\"[test_distro]\" />\n        <!-- When updating the drops for this make sure the retention is set to never expire -->\n        <Add Name=\"DropURL\" Value=\"https://${VSO_ORG}.artifacts.visualstudio.com/_apis/drop/drops/WSL/${CLOUDTEST_TEST_DISTRO_DROP}\"/>\n      </Properties>\n    </Provider>\n    <Provider Type=\"VSODrop\">\n      <Properties>\n        <Add Name=\"CloudTest.ProviderCustomName\" Value=\"[test_packages]\" />\n        <!-- When updating the drops for this make sure the retention is set to never expire -->\n        <Add Name=\"DropURL\" Value=\"https://${VSO_ORG}.artifacts.visualstudio.com/_apis/drop/drops/WSL/${CLOUDTEST_TEST_PACKAGES}\"/>\n      </Properties>\n    </Provider>\n    <Provider Type=\"VSODrop\">\n      <Properties>\n        <Add Name=\"CloudTest.ProviderCustomName\" Value=\"[dump_tool]\" />\n        <!-- When updating the drops for this make sure the retention is set to never expire -->\n        <Add Name=\"DropURL\" Value=\"https://${VSO_ORG}.artifacts.visualstudio.com/_apis/drop/drops/WSL/${DUMPTOOL_DROP}\"/>\n      </Properties>\n    </Provider>\n  </Providers>\n</TestMap>\n"
  },
  {
    "path": "cmake/FindIDL.cmake",
    "content": "function(add_idl target idl_files_with_proxy idl_files_no_proxy)\n    set(OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE})\n    file(MAKE_DIRECTORY ${OUTPUT_DIR})\n    set(TARGET_OUTPUTS)\n\n    set(IDL_DEFINITIONS \"\")\n\n    get_directory_property(IDL_DEFS COMPILE_DEFINITIONS )\n    foreach(e ${IDL_DEFS})\n        set(IDL_DEFINITIONS ${IDL_DEFINITIONS} /D${e})\n    endforeach()\n\n    string(TOLOWER ${TARGET_PLATFORM} IDL_ENV)\n\n    foreach(idl_file ${idl_files_with_proxy})\n\n        cmake_path(GET idl_file STEM IDL_NAME)\n\n        set(IDL_HEADER ${OUTPUT_DIR}/${IDL_NAME}.h)\n\n        # Adding a _${TARGET_PLATFORM} to work around object files having\n        # the same paths regardless of TARGET_PLATFORM, which can cause the linker to fail with:\n        # \"fatal error LNK1112: module machine type 'x64' conflicts with target machine type 'ARM64'\"\n        set(IDL_I ${OUTPUT_DIR}/${IDL_NAME}_i_${TARGET_PLATFORM}.c)\n        set(IDL_P ${OUTPUT_DIR}/${IDL_NAME}_p_${TARGET_PLATFORM}.c)\n        set(IDL_DLLDATA ${OUTPUT_DIR}/dlldata_${TARGET_PLATFORM}.c)\n        set(MIDL_OUTPUT ${IDL_HEADER} ${IDL_I} ${IDL_P} ${IDL_DLLDATA})\n\n        add_custom_command(\n            OUTPUT ${MIDL_OUTPUT} ${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\n            COMMAND midl /nologo /target NT100 /env \"${IDL_ENV}\" /Zp8 /char unsigned /ms_ext /c_ext /h ${IDL_HEADER} /iid ${IDL_I} /proxy ${IDL_P} /dlldata ${IDL_DLLDATA} ${idl_file} ${IDL_DEFINITIONS}\n            COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\"\n            WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}\n            DEPENDS ${idl_file}\n            MAIN_DEPENDENCY ${idl_file}\n            VERBATIM\n        )\n\n        set_source_files_properties(${MIDL_OUTPUT} PROPERTIES GENERATED TRUE)\n        list(APPEND TARGET_OUTPUTS ${MIDL_OUTPUT})\n\n    endforeach()\n\n    foreach(idl_file ${idl_files_no_proxy})\n\n        cmake_path(GET idl_file STEM IDL_NAME)\n\n        set(IDL_HEADER ${OUTPUT_DIR}/${IDL_NAME}.h)\n\n        add_custom_command(\n            OUTPUT ${IDL_HEADER} ${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\n            COMMAND midl /nologo /target NT100 /env \"${IDL_ENV}\" /Zp8 /char unsigned /ms_ext /c_ext /h ${IDL_HEADER} ${idl_file} ${IDL_DEFINITIONS}\n            COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\"\n            WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}\n            DEPENDS ${idl_file}\n            MAIN_DEPENDENCY ${idl_file}\n            VERBATIM\n        )\n\n        set_source_files_properties(${IDL_HEADER} PROPERTIES GENERATED TRUE)\n        list(APPEND TARGET_OUTPUTS ${IDL_HEADER})\n\n    endforeach()\n\n    add_custom_target(${target} DEPENDS ${TARGET_OUTPUTS} SOURCES ${idl_files_with_proxy} ${idl_files_no_proxy})\n\nendfunction()"
  },
  {
    "path": "cmake/FindLINUXBUILD.cmake",
    "content": "function(build_linux_objects sources headers)\n    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE})\n\n    foreach(e ${sources})\n        cmake_path(GET e FILENAME object_name)\n        set(object \"${CMAKE_CURRENT_BINARY_DIR}/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/${object_name}.o\")\n        set(objects ${objects} ${object})\n\n        if(\"${e}\" MATCHES \"^.*\\.c$\")\n            set(compiler ${LINUX_CC})\n            set(flags ${LINUX_CFLAGS})\n        else()\n            set(compiler ${LINUX_CXX})\n            set(flags ${LINUX_CXXFLAGS})\n        endif()\n\n        add_custom_command(\n            OUTPUT ${object}\n            COMMAND ${compiler} ${flags} ${LINUX_BUILD_TYPE_FLAGS} ${e} -c -o ${object}\n            WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}\n            MAIN_DEPENDENCY ${e}\n            DEPENDS ${headers} # Every object depends on all headers to trigger a rebuild if a header is changed\n            VERBATIM\n        )\n    endforeach()\n\n    set(objects ${objects} PARENT_SCOPE)\nendfunction()\n\nfunction(add_linux_library_impl target sources headers add_sources)\n    set(ar_output \"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${target}.a\")\n\n    build_linux_objects(\"${sources}\" \"${headers}\")\n\n    add_custom_command(\n        OUTPUT ${ar_output} ${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\n        COMMAND ${LINUX_AR} crus ${ar_output} ${objects}\n        COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\"\n        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n        DEPENDS ${objects}\n        VERBATIM\n    )\n\n    if (${add_sources})\n        add_custom_target(${target} DEPENDS ${ar_output} SOURCES ${sources} ${headers})\n    else()\n        add_custom_target(${target} DEPENDS ${ar_output})\n    endif()\n\n    set_source_files_properties(${ar_output} PROPERTIES GENERATED TRUE)\nendfunction()\n\nfunction(add_linux_library target sources headers)\n    add_linux_library_impl(${target} \"${sources}\" \"${headers}\" TRUE)\nendfunction()\n\nfunction(add_linux_library_no_sources target sources headers)\n    add_linux_library_impl(${target} \"${sources}\" \"${headers}\" FALSE)\nendfunction()\n\n\nfunction(add_linux_executable target sources headers libraries)\n    set(output \"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${target}\")\n    set(output_unstripped \"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${target}.unstripped\")\n\n    build_linux_objects(\"${sources}\" \"${headers}\")\n\n    set(libs -lunwind -lc++abi -lc++)\n    foreach(e ${libraries})\n        set(libs ${libs} -l${e})\n\n        # Note: This makes the assumption that all libraries are static (.a and not .so).\n        # Executables need to depend on both the target and the underlying library file, so that\n        # the libraries target get analyzed for changes, and the executable gets linked again if the .a files changed.\n        list(APPEND lib_targets \"lib${e}\")\n        list(APPEND lib_files \"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/lib${e}.a\")\n    endforeach()\n\n    if (NOT ${CMAKE_BUILD_TYPE} STREQUAL \"Debug\")\n        set(stripped_output \"${output}\")\n        set(output \"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${target}.debug\")\n        add_custom_command(\n            OUTPUT ${stripped_output}\n            COMMAND ${LLVM_INSTALL_DIR}/llvm-strip.exe \"${output}\" -o \"${stripped_output}\"\n            COMMAND ${LLVM_INSTALL_DIR}/llvm-objcopy.exe --add-gnu-debuglink \"${output}\" \"${stripped_output}\"\n            WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n            DEPENDS ${output}\n            VERBATIM\n        )\n     endif()\n\n    add_custom_command(\n         OUTPUT ${output}\n         COMMAND ${LINUX_CXX} -o ${output} ${LINUXSDK_PATH}/lib/crti.o ${LINUXSDK_PATH}/lib/crt1.o ${objects} ${LINUXSDK_PATH}/lib/crtn.o ${LINUX_LDFLAGS} ${libs}\n         COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\"\n         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n         DEPENDS ${objects} ${lib_files}\n         VERBATIM\n     )\n\n    add_custom_target(${target} DEPENDS ${output} ${stripped_output} SOURCES ${sources} ${headers})\n    add_dependencies(${target} ${lib_targets})\n\nendfunction()"
  },
  {
    "path": "cmake/FindMC.cmake",
    "content": "function(add_mc target mc_file)\n\n    cmake_path(GET mc_file STEM MC_NAME)\n\n    set(OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}\\\\${TARGET_PLATFORM}\\\\${CMAKE_BUILD_TYPE})\n    set(RC_FILE ${OUTPUT_DIR}/${MC_NAME}.rc)\n    set(HEADER_FILE ${OUTPUT_DIR}/${MC_NAME}.h)\n\n    add_custom_command(\n        OUTPUT ${RC_FILE} ${HEADER_FILE} ${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\n        COMMAND mc.exe -A -b -c -h ${OUTPUT_DIR} -r ${OUTPUT_DIR} ${mc_file}\n        COMMAND rc.exe -nologo -fo${OUTPUT_DIR}\\\\${MC_NAME}.res ${RC_FILE}\n        COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\"\n        WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}\n        DEPENDS ${mc_file}\n        MAIN_DEPENDENCY ${mc_file}\n        VERBATIM\n    )\n\n    add_custom_target(${target} DEPENDS ${RC_FILE} SOURCES ${mc_file})\n\n    set_source_files_properties(${RC_FILE} PROPERTIES GENERATED TRUE)\nendfunction()"
  },
  {
    "path": "cmake/findAppx.cmake",
    "content": "function(add_appx_target target binaries manifest_in output_package dependencies)\n\n    set(PACKAGE_LAYOUT \"${CMAKE_CURRENT_BINARY_DIR}/${TARGET_PLATFORM}/package_layout\")\n    set(MANIFEST \"${CMAKE_CURRENT_BINARY_DIR}/AppxManifest.xml\")\n    set(PRI_CONF \"${CMAKE_CURRENT_BINARY_DIR}/priconf.xml\")\n    set(OUTPUT_RESOURCES_PRI \"${CMAKE_CURRENT_BINARY_DIR}/resources.pri\")\n\n    # generate the list of languages for the appxmanifest\n    set(SUPPORTED_LANGS_MANIFEST_ENTRIES \"\")\n    foreach(LANG ${SUPPORTED_LANGS})\n        string(TOUPPER \"${LANG}\" LANG)\n        set(SUPPORTED_LANGS_MANIFEST_ENTRIES \"${SUPPORTED_LANGS_MANIFEST_ENTRIES}\\n    <Resource Language=\\\"${LANG}\\\"/>\")\n    endforeach()\n\n    configure_file(${manifest_in} ${MANIFEST})\n\n    file(MAKE_DIRECTORY ${PACKAGE_LAYOUT})\n\n    # images\n    file(MAKE_DIRECTORY ${PACKAGE_LAYOUT}/Images)\n    set(RESOURCES_DEPENDENCY)\n    file(GLOB IMAGES RELATIVE ${PROJECT_SOURCE_DIR}/ \"${PROJECT_SOURCE_DIR}/images/*.png\")\n    foreach(e ${IMAGES})\n        file(CREATE_LINK ${PROJECT_SOURCE_DIR}/${e} ${PACKAGE_LAYOUT}/${e} SYMBOLIC)\n        list(APPEND RESOURCES_DEPENDENCY ${PROJECT_SOURCE_DIR}/${e})\n    endforeach()\n\n    # Localization. Note: these files aren't added to the resource map, so they aren't added to the package,\n    # but they are used by makepri to generate resources.pri\n    file(CREATE_LINK ${PROJECT_SOURCE_DIR}/localization/strings ${PACKAGE_LAYOUT}/Strings SYMBOLIC)\n\n    foreach(binary ${binaries})\n    set(BINARY_SRC \"${BIN}/${binary}\")\n    set(BINARY_DEST \"${PACKAGE_LAYOUT}/${binary}\")\n    add_custom_command(\n        OUTPUT ${BINARY_DEST}\n        COMMAND ${CMAKE_COMMAND} -E create_symlink \"${BINARY_SRC}\" \"${BINARY_DEST}\"\n        DEPENDS ${BINARY_SRC}\n    )\n    list(APPEND BINARIES_DEPENDENCY ${BINARY_DEST})\n    endforeach()\n\n    # Reduce the output of makeappx unless WSL_APPX_DEBUG is set to make the build output nicer to read\n    if (WSL_SILENT_APPX_BUILD)\n        set(COMMAND_SUFFIX \"2>NUL;>;NUL\")\n    endif ()\n\n    # generate priconf.xml\n    string(REPLACE \";\" \"_\" SUPPORTED_LANGS_STR \"${SUPPORTED_LANGS}\")\n    add_custom_command(\n        OUTPUT ${PRI_CONF}\n        COMMAND makepri.exe createconfig /cf ${PRI_CONF} /dq ${SUPPORTED_LANGS_STR} /pv 10.0 /o ${COMMAND_SUFFIX}\n        COMMAND_EXPAND_LISTS\n    )\n\n    # generate resources.pri\n    add_custom_command(\n        OUTPUT ${OUTPUT_RESOURCES_PRI} ${CMAKE_CURRENT_BINARY_DIR}/resources.map.txt\n        COMMAND makepri.exe new /pr ${PACKAGE_LAYOUT} /cf ${PRI_CONF} /of ${OUTPUT_RESOURCES_PRI} /mn ${MANIFEST} /mf AppX /o /IndexOptions +lf ${COMMAND_SUFFIX}\n        COMMAND_EXPAND_LISTS\n        DEPENDS ${PRI_CONF} ${MANIFEST} ${BINARIES_DEPENDENCY} ${RESOURCES_DEPENDENCY} # Make sure the package is rebuilt if any of the resources change\n    )\n\n    # make appx\n    add_custom_command(\n        OUTPUT ${output_package}\n        COMMAND makeappx.exe pack /m ${MANIFEST} /f ${CMAKE_CURRENT_BINARY_DIR}/resources.map.txt /p ${output_package} /o ${COMMAND_SUFFIX}\n        COMMAND ${PACKAGE_SIGN_COMMAND} ${output_package} ${COMMAND_SUFFIX}\n        COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/${target}\"\n        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n        COMMAND_EXPAND_LISTS\n        DEPENDS ${MANIFEST} ${BINARIES_DEPENDENCY} ${OUTPUT_RESOURCES_PRI} ${CMAKE_CURRENT_BINARY_DIR}/resources.map.txt # Make sure the package is rebuilt if any of the binaries or resources change\n    )\n\n    add_custom_target(${target} DEPENDS ${output_package})\n\n    foreach(e ${dependencies})\n        add_dependencies(${target} ${e})\n    endforeach()\n\n    set_target_properties(${target} PROPERTIES EXCLUDE_FROM_ALL FALSE SOURCES ${manifest_in})\n\n    set_source_files_properties(${output_package} PROPERTIES GENERATED TRUE)\nendfunction()"
  },
  {
    "path": "cmake/findNuget.cmake",
    "content": "function(find_nuget_package name var_name path)\n\n    string(JSON \"${var_name}_VERSION\" GET ${NUGET_PACKAGES_JSON} \"${name}\")\n\n    set(${var_name}_SOURCE_DIR \"${CMAKE_BINARY_DIR}/packages/${name}.${${var_name}_VERSION}${path}\" PARENT_SCOPE)\n    set(${var_name}_VERSION \"${${var_name}_VERSION}\" PARENT_SCOPE)\nendfunction()\n\nfunction (restore_nuget_packages)\n\n    # Fetch nuget.exe\n    FILE(DOWNLOAD\n        https://dist.nuget.org/win-x86-commandline/v5.10.0/nuget.exe\n        ${CMAKE_BINARY_DIR}/_deps/nuget.exe\n        EXPECTED_HASH SHA256=852b71cc8c8c2d40d09ea49d321ff56fd2397b9d6ea9f96e532530307bbbafd3)\n\n    set_property(\n        DIRECTORY\n        APPEND\n        PROPERTY CMAKE_CONFIGURE_DEPENDS\n        ${CMAKE_CURRENT_LIST_DIR}/packages.config\n        ${CMAKE_CURRENT_LIST_DIR}/nuget.config)\n\n    # Restore nuget packages\n    execute_process(COMMAND\n        ${CMAKE_BINARY_DIR}/_deps/nuget.exe restore packages.config -SolutionDirectory ${CMAKE_BINARY_DIR}\n        WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}\n        COMMAND_ERROR_IS_FATAL ANY)\nendfunction()\n\nfunction (parse_nuget_packages_versions)\n\n    # Parse the list of available packages\n    set(CMD \"$packages=@{}; (Select-Xml -path ${CMAKE_SOURCE_DIR}/packages.config /packages).Node.ChildNodes | Where-Object { $_.name -ne '#whitespace'} | % {$packages.add($_.id, $_.Attributes['version'].Value) }; $packages | ConvertTo-Json | Write-Host\")\n\n    execute_process(\n        COMMAND powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -Command \"${CMD}\"\n        OUTPUT_VARIABLE output\n        COMMAND_ERROR_IS_FATAL ANY)\n\n    set(NUGET_PACKAGES_JSON ${output} PARENT_SCOPE)\nendfunction()\n"
  },
  {
    "path": "cmake/findVersion.cmake",
    "content": "function(get_version_impl command var_name)\n    execute_process(\n        COMMAND powershell.exe\n                -NoProfile\n                -NonInteractive\n                -ExecutionPolicy Bypass\n                -Command \"$env:Path += ';${GITVERSION_SOURCE_DIR}' ; . .\\\\tools\\\\devops\\\\version_functions.ps1 ; ${command}\"\n        OUTPUT_VARIABLE OUTPUT_VERSION\n        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n        COMMAND_ERROR_IS_FATAL ANY)\n    string(STRIP \"${OUTPUT_VERSION}\" OUTPUT_VERSION)\n    SET(${var_name} ${OUTPUT_VERSION} PARENT_SCOPE)\nendfunction()\n\nfunction(find_commit_hash var_name)\n    get_version_impl(\"Get-Current-Commit-Hash\" \"command_output\")\n    SET(${var_name} ${command_output} PARENT_SCOPE)\nendfunction()\n\nfunction(find_version msix_var_name nuget_var_name)\n    get_version_impl(\"Get-VersionInfo -Nightly $false | ConvertTo-Json\" \"command_output\")\n    string(JSON  \"${msix_var_name}\" GET \"${command_output}\" MsixVersion)\n    string(JSON  \"${nuget_var_name}\" GET \"${command_output}\" NugetVersion)\n\n    return(PROPAGATE ${msix_var_name} ${nuget_var_name})\nendfunction()"
  },
  {
    "path": "diagnostics/collect-wsl-logs.ps1",
    "content": "#Requires -RunAsAdministrator\n\n[CmdletBinding()]\nParam (\n    $LogProfile = $null,\n    [switch]$Dump = $false,\n    [switch]$RestartWslReproMode = $false\n   )\n\nSet-StrictMode -Version Latest\n\nfunction Collect-WindowsNetworkState {\n    param (\n        $Folder,\n        $ReproStep\n    )\n\n    # Collect host networking state relevant for WSL\n    # Using a try/catch for commands below, as some of them do not exist on all OS versions\n\n    try { Get-NetAdapter -includeHidden | select Name,ifIndex,NetLuid,InterfaceGuid,Status,MacAddress,MtuSize,InterfaceType,Hidden,HardwareInterface,ConnectorPresent,MediaType,PhysicalMediaType | Out-File -FilePath \"$Folder/Get-NetAdapter_$ReproStep.log\" -Append } catch {}\n    try { & netsh nlm query all $Folder/nlmquery_\"$ReproStep\".log } catch {}\n    try { Get-NetIPConfiguration -All -Detailed | Out-File -FilePath \"$Folder/Get-NetIPConfiguration_$ReproStep.log\" -Append } catch {}\n    try { Get-NetRoute | Out-File -FilePath \"$Folder/Get-NetRoute_$ReproStep.log\" -Append } catch {}\n    try { Get-NetFirewallHyperVVMCreator | Out-File -FilePath \"$Folder/Get-NetFirewallHyperVVMCreator_$ReproStep.log\" -Append } catch {}\n    try { Get-NetFirewallHyperVVMSetting -PolicyStore ActiveStore | Out-File -FilePath \"$Folder/Get-NetFirewallHyperVVMSetting_ActiveStore_$ReproStep.log\" -Append } catch {}\n    try { Get-NetFirewallHyperVProfile -PolicyStore ActiveStore | Out-File -FilePath \"$Folder/Get-NetFirewallHyperVProfile_ActiveStore_$ReproStep.log\" -Append } catch {}\n    try { Get-NetFirewallHyperVRule -PolicyStore ActiveStore | Out-File -FilePath \"$Folder/Get-NetFirewallHyperVRule_ActiveStore_$ReproStep.log\" -Append } catch {}\n    try { Get-NetFirewallRule -PolicyStore ActiveStore | Out-File -FilePath \"$Folder/Get-NetFirewallRule_ActiveStore_$ReproStep.log\" -Append } catch {}\n    try { Get-NetFirewallProfile -PolicyStore ActiveStore | Out-File -FilePath \"$Folder/Get-NetFirewallProfile_ActiveStore_$ReproStep.log\" -Append } catch {}\n    try { Get-NetFirewallHyperVPort | Out-File -FilePath \"$Folder/Get-NetFirewallHyperVPort_$ReproStep.log\" -Append } catch {}\n    try { & hnsdiag.exe list all 2>&1 > $Folder/hnsdiag_list_all_\"$ReproStep\".log } catch {}\n    try { & hnsdiag.exe list endpoints -df 2>&1 > $Folder/hnsdiag_list_endpoints_\"$ReproStep\".log } catch {}\n    try {\n        foreach ($port in Get-NetFirewallHyperVPort) {\n            & vfpctrl.exe /port $port.PortName /get-port-state 2>&1 > \"$Folder/vfp-port-$($port.PortName)-get-port-state_$ReproStep.log\"\n            & vfpctrl.exe /port $port.PortName /list-rule 2>&1 > \"$Folder/vfp-port-$($port.PortName)-list-rule_$ReproStep.log\"\n        }\n    } catch {}\n    try { & vfpctrl.exe /list-vmswitch-port 2>&1 > $Folder/vfpctrl_list_vmswitch_port_\"$ReproStep\".log } catch {}\n    try { Get-VMSwitch | select Name,Id,SwitchType | Out-File -FilePath \"$Folder/Get-VMSwitch_$ReproStep.log\" -Append } catch {}\n    try { Get-NetUdpEndpoint | Out-File -FilePath \"$Folder/Get-NetUdpEndpoint_$ReproStep.log\" -Append } catch {}\n}\n\n$folder = \"WslLogs-\" + (Get-Date -Format \"yyyy-MM-dd_HH-mm-ss\")\nmkdir -p $folder | Out-Null\n\n# Check if LogProfile is a custom file path or a profile name\nif ($LogProfile -ne $null -And [System.IO.File]::Exists($LogProfile))\n{\n    # User provided a custom .wprp file path - use it directly\n    $wprpFile = $LogProfile\n    $wprpProfile = $null  # Use default profile in the file\n}\nelse\n{\n    # Map log profile names to WPRP profile names\n    $wprpProfile = \"WSL\"\n    if ($LogProfile -eq \"storage\")\n    {\n        $wprpProfile = \"WSL-Storage\"\n    }\n    elseif ($LogProfile -eq \"networking\")\n    {\n        $wprpProfile = \"WSL-Networking\"\n    }\n    elseif ($LogProfile -eq \"hvsocket\")\n    {\n        $wprpProfile = \"WSL-HvSocket\"\n    }\n    elseif ($LogProfile -ne $null)\n    {\n        Write-Error \"Unknown log profile: $LogProfile. Valid options are: storage, networking, hvsocket, or a path to a custom .wprp file\"\n        exit 1\n    }\n\n    # Use the consolidated wsl.wprp file, attempt to use local copy first.\n    $wprpFile = \"$folder/wsl.wprp\"\n    if (Test-Path \"$PSScriptRoot/wsl.wprp\")\n    {\n        Copy-Item \"$PSScriptRoot/wsl.wprp\" $wprpFile\n    }\n    else\n    {\n        Invoke-WebRequest -UseBasicParsing \"https://raw.githubusercontent.com/microsoft/WSL/master/diagnostics/wsl.wprp\" -OutFile $wprpFile\n    }\n}\n\n# Networking-specific setup\nif ($LogProfile -eq \"networking\")\n{\n    # Copy/download networking.sh script\n    $networkingBashScript = \"$folder/networking.sh\"\n    if (Test-Path \"$PSScriptRoot/networking.sh\")\n    {\n        Copy-Item \"$PSScriptRoot/networking.sh\" $networkingBashScript\n    }\n    else\n    {\n        Write-Host -ForegroundColor Yellow \"networking.sh not found in the current directory. Downloading it from GitHub.\"\n        Invoke-WebRequest -UseBasicParsing \"https://raw.githubusercontent.com/microsoft/WSL/master/diagnostics/networking.sh\" -OutFile $networkingBashScript\n    }\n\n    # Detect the super user (uid=0, not necessarily named \"root\" - see #11693)\n    $superUser = & wsl.exe -- id -nu 0\n\n    # Collect Linux & Windows network state before the repro\n    & wsl.exe -u $superUser -e $networkingBashScript 2>&1 > $folder/linux_network_configuration_before.log\n    Collect-WindowsNetworkState -Folder $folder -ReproStep \"before_repro\"\n\n    if ($RestartWslReproMode)\n    {\n        # The WSL HNS network is created once per boot. Resetting it to collect network creation logs.\n        # Note: The below HNS command applies only to WSL in NAT mode\n        Get-HnsNetwork | Where-Object {$_.Name -eq 'WSL' -Or $_.Name -eq 'WSL (Hyper-V firewall)'} | Remove-HnsNetwork\n\n        # Stop WSL\n        net.exe stop WslService\n        if(-not $?) { net.exe stop LxssManager }\n    }\n}\n\nreg.exe export HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss $folder/HKCU.txt 2>&1 | Out-Null\nreg.exe export HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss $folder/HKLM.txt 2>&1 | Out-Null\nreg.exe export HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\P9NP $folder/P9NP.txt 2>&1 | Out-Null\nreg.exe export HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\WinSock2 $folder/Winsock2.txt 2>&1 | Out-Null\nreg.exe export \"HKEY_CLASSES_ROOT\\CLSID\\{e66b0f30-e7b4-4f8c-acfd-d100c46c6278}\" $folder/wslsupport-proxy.txt 2>&1 | Out-Null\nreg.exe export \"HKEY_CLASSES_ROOT\\CLSID\\{a9b7a1b9-0671-405c-95f1-e0612cb4ce7e}\" $folder/wslsupport-impl.txt 2>&1 | Out-Null\nGet-ItemProperty -Path \"Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\" > $folder/windows-version.txt\n\nGet-Service wslservice -ErrorAction Ignore | Format-list * -Force  > $folder/wslservice.txt\n\n$wslconfig = \"$env:USERPROFILE/.wslconfig\"\nif (Test-Path $wslconfig)\n{\n    Copy-Item $wslconfig $folder | Out-Null\n}\n\nCopy-Item \"C:\\Windows\\temp\\wsl-install-log.txt\" $folder -ErrorAction ignore\n\nget-appxpackage MicrosoftCorporationII.WindowsSubsystemforLinux -ErrorAction Ignore > $folder/appxpackage.txt\nget-acl \"C:\\ProgramData\\Microsoft\\Windows\\WindowsApps\" -ErrorAction Ignore | Format-List > $folder/acl.txt\nGet-WindowsOptionalFeature -Online > $folder/optional-components.txt\nbcdedit.exe > $folder/bcdedit.txt\n\n$uninstallLogs = \"$env:TEMP/wsl-uninstall-logs.txt\"\nif (Test-Path $uninstallLogs)\n{\n    Copy-Item $uninstallLogs $folder | Out-Null\n}\n\n$wprOutputLog = \"$folder/wpr.txt\"\n\n# Build wpr command - if wprpProfile is set, use profile syntax, otherwise use file only\nif ($wprpProfile -ne $null)\n{\n    $wprCommand = \"$wprpFile!$wprpProfile\"\n}\nelse\n{\n    $wprCommand = $wprpFile\n}\n\nwpr.exe -start $wprCommand -filemode 2>&1 >> $wprOutputLog\nif ($LastExitCode -Ne 0)\n{\n    Write-Host -ForegroundColor Yellow \"Log collection failed to start (exit code: $LastExitCode), trying to reset it.\"\n    wpr.exe -cancel 2>&1 >> $wprOutputLog\n\n    wpr.exe -start $wprCommand -filemode 2>&1 >> $wprOutputLog\n    if ($LastExitCode -Ne 0)\n    {\n        Write-Host -ForegroundColor Red \"Couldn't start log collection (exitCode: $LastExitCode)\"\n    }\n}\n\n# Start networking-specific captures\n$tcpdumpProcess = $null\nif ($LogProfile -eq \"networking\")\n{\n    pktmon start -c --flags 0x1A --file-name \"$folder/pktmon.etl\" | out-null\n    netsh wfp capture start file=\"$folder/wfpdiag.cab\"\n\n    # Ensure WSL is running before collecting network state\n    & wsl.exe -- true 2>&1 | Out-Null\n\n    # Start tcpdump (may not be installed)\n    try\n    {\n        $tcpdumpProcess = Start-Process wsl.exe -ArgumentList \"-u $superUser tcpdump -n -i any -e -vvv > $folder/tcpdump.log\" -WindowStyle Hidden -PassThru\n    }\n    catch {}\n}\n\ntry\n{\n    Write-Host -NoNewLine \"Log collection is running. Please \"\n    Write-Host -NoNewLine -ForegroundColor Red \"reproduce the problem \"\n    Write-Host -NoNewLine \"and once done press any key to save the logs.\"\n\n    $KeysToIgnore =\n          16,  # Shift (left or right)\n          17,  # Ctrl (left or right)\n          18,  # Alt (left or right)\n          20,  # Caps lock\n          91,  # Windows key (left)\n          92,  # Windows key (right)\n          93,  # Menu key\n          144, # Num lock\n          145, # Scroll lock\n          166, # Back\n          167, # Forward\n          168, # Refresh\n          169, # Stop\n          170, # Search\n          171, # Favorites\n          172, # Start/Home\n          173, # Mute\n          174, # Volume Down\n          175, # Volume Up\n          176, # Next Track\n          177, # Previous Track\n          178, # Stop Media\n          179, # Play\n          180, # Mail\n          181, # Select Media\n          182, # Application 1\n          183  # Application 2\n\n    $Key = $null\n    while ($Key -Eq $null -Or $Key.VirtualKeyCode -Eq $null -Or $KeysToIgnore -Contains $Key.VirtualKeyCode)\n    {\n        $Key = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')\n    }\n\n    Write-Host \"`nSaving logs...\"\n}\nfinally\n{\n    # Stop networking-specific captures\n    if ($LogProfile -eq \"networking\")\n    {\n        try\n        {\n            wsl.exe -u $superUser killall tcpdump\n            if ($tcpdumpProcess -ne $null)\n            {\n                Wait-Process -InputObject $tcpdumpProcess -Timeout 10\n            }\n        }\n        catch {}\n\n        netsh wfp capture stop\n        pktmon stop | out-null\n    }\n\n    wpr.exe -stop $folder/logs.etl 2>&1 >> $wprOutputLog\n}\n\n# Networking-specific post-repro collection\nif ($LogProfile -eq \"networking\")\n{\n    # Collect Linux & Windows network state after the repro\n    & wsl.exe -u $superUser -e $networkingBashScript 2>&1 > $folder/linux_network_configuration_after.log\n    Collect-WindowsNetworkState -Folder $folder -ReproStep \"after_repro\"\n\n    try\n    {\n        # Collect HNS events from past 24 hours\n        $events = Get-WinEvent -ProviderName Microsoft-Windows-Host-Network-Service | Where-Object { $_.TimeCreated -ge ((Get-Date) - (New-TimeSpan -Day 1)) }\n        ($events | ForEach-Object { '{0},{1},{2},{3}' -f $_.TimeCreated, $_.Id, $_.LevelDisplayName, $_.Message }) -join [environment]::NewLine | Out-File -FilePath \"$folder/hns_events.log\" -Append\n    }\n    catch {}\n\n    # Collect the old Tcpip6 registry values - as they can break WSL if DisabledComponents is set to 0xff\n    # see https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-ipv6-in-windows\n    try\n    {\n        Get-Item HKLM:SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters | Out-File -FilePath \"$folder/tcpip6_parameters.log\" -Append\n    }\n    catch {}\n\n    # Collect the setup and NetSetup log files\n    $netSetupPath = \"$env:WINDIR/logs/netsetup\"\n    if (Test-Path $netSetupPath)\n    {\n        Copy-Item $netSetupPath/* $folder\n    }\n\n    $setupApiPath = \"$env:WINDIR/inf/setupapi.dev.log\"\n    if (Test-Path $setupApiPath)\n    {\n        Copy-Item $setupApiPath $folder\n    }\n\n    Remove-Item $networkingBashScript\n}\n\nif ($Dump)\n{\n    $Assembly = [PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting')\n    $DumpMethod = $Assembly.GetNestedType('NativeMethods', 'NonPublic').GetMethod('MiniDumpWriteDump', [Reflection.BindingFlags] 'NonPublic, Static')\n\n    $dumpFolder = Join-Path (Resolve-Path \"$folder\") dumps\n    New-Item -ItemType \"directory\" -Path \"$dumpFolder\"\n\n    $executables = \"wsl\", \"wslservice\", \"wslhost\", \"msrdc\", \"dllhost\"\n    foreach($process in Get-Process | Where-Object { $executables -contains $_.ProcessName})\n    {\n        $dumpFile =  \"$dumpFolder/$($process.ProcessName).$($process.Id).dmp\"\n        Write-Host \"Writing $($dumpFile)\"\n\n        $OutputFile = New-Object IO.FileStream($dumpFile, [IO.FileMode]::Create)\n\n        $Result = $DumpMethod.Invoke($null, @($process.Handle,\n                                              $process.id,\n                                              $OutputFile.SafeFileHandle,\n                                              [UInt32] 2,\n                                              [IntPtr]::Zero,\n                                              [IntPtr]::Zero,\n                                              [IntPtr]::Zero))\n\n        $OutputFile.Close()\n        if (-not $Result)\n        {\n            Write-Host \"Failed to write dump for: $($dumpFile)\"\n        }\n    }\n}\n\n$logArchive = \"$(Resolve-Path $folder).zip\"\nCompress-Archive -Path $folder -DestinationPath $logArchive\nRemove-Item $folder -Recurse\n\nWrite-Host -ForegroundColor Green \"Logs saved in: $logArchive. Please attach that file to the GitHub issue.\"\n"
  },
  {
    "path": "diagnostics/dump-init-stacks.sh",
    "content": "#! /bin/bash\n\nset -ue\n\nfor proc in /proc/[0-9]*; do\n  pid=$(basename \"$proc\")\n\n  echo -e \"\\nProcess: $pid\"\n  echo -en \"cmd: \"\n  cat \"/proc/$pid/cmdline\" || true\n  echo -e \"\\nstat: \"\n  cat \"/proc/$pid/stat\" || true\n\n  for tid in $(ls \"/proc/$pid/task\" || true); do\n    echo -n \"tid: $tid - \"\n    cat \"/proc/$pid/task/$tid/comm\" || true\n    cat \"/proc/$pid/task/$tid/stack\" || true\n  done\n\n  echo \"fds: \"\n  ls -la \"/proc/$pid/fd\" || true\ndone\n\necho \"hvsockets: \"\nss -lap --vsock\n\necho \"meminfo: \"\ncat /proc/meminfo"
  },
  {
    "path": "diagnostics/dump-init.sh",
    "content": "#! /bin/bash\n\nset -ue\n\nif [ $(whoami) != \"root\" ]; then\n  echo \"This script must be run as root in the debug shell (via wsl.exe -u root --debug-shell)\"\n  exit 1\nfi\n\ndmesg\n\n# Try to install gdb\n\nif [ \"${dump:-0}\" == 1 ]; then\n    tdnf install -y gdb || true\nfi\n\ndeclare -a pids_to_dump\n\nfor proc in /proc/[0-9]*; do\n  read -a stats < \"$proc/stat\" # Skip kernel threads to make the output easier to read\n  flags=${stats[8]}\n\n  if (( (\"$flags\" & 0x00200000) == 0x00200000 )); then\n    continue\n  fi\n\n  pid=$(basename \"$proc\")\n\n  if [ \"${dump:-0}\" == 1 ]; then\n    pids_to_dump+=(\"$pid\")\n  fi\n\n  parent=$(ps -o ppid= -p \"$pid\")\n\n  echo -e \"\\nProcess: $pid (parent: $parent) \"\n  echo -en \"cmd: \"\n  cat \"/proc/$pid/cmdline\" || true\n  echo -e \"\\nstat: \"\n  cat \"/proc/$pid/stat\" || true\n\n  for tid in $(ls \"/proc/$pid/task\" || true); do\n    echo -n \"tid: $tid - \"\n    cat \"/proc/$pid/task/$tid/comm\" || true\n    cat \"/proc/$pid/task/$tid/stack\" || true\n  done\n\n  echo \"fds: \"\n  ls -la \"/proc/$pid/fd\" || true\ndone\n\nfor pid in \"${pids_to_dump[@]}\" ; do\n   name=$(ps -p \"$pid\" -o comm=)\n   if [[ \"$name\" =~ ^(bash|login)$ ]]; then\n     echo \"Skipping dump for process: $name\"\n     continue\n   fi\n\n   echo \"Dumping process: $name ($pid) \"\n   if gcore -a -o core \"$pid\" ; then\n     if ! /wsl-capture-crash 0 \"$name\" \"$pid\" 0 < \"core.$pid\" ; then\n         echo \"Failed to dump process $pid\"\n     fi\n\n     rm \"core.$pid\"\n   fi\ndone\n\necho \"hvsockets: \"\nss -lap --vsock\n\necho \"meminfo: \"\ncat /proc/meminfo\n"
  },
  {
    "path": "diagnostics/networking.sh",
    "content": "#! /bin/bash\nset -xu\n\n# Gather distro & kernel info.\nlsb_release -a || cat /etc/issue /etc/os-release\nuname -a\n\necho \"Printing per-distro configuration\"\ncat /etc/wsl.conf\n\necho \"Printing adapter & routing configuration\"\nip a\nip route show table all\nip neighbor\nip link\nip rule show\n\n# We only print whether the HTTP proxy variables are set or not, as their content might contain secrets such as usernames, passwords.\nif [ -z ${http_proxy+x} ]; then echo \"http_proxy is unset\"; else echo \"http_proxy is set\"; fi\nif [ -z ${HTTP_PROXY+x} ]; then echo \"HTTP_PROXY is unset\"; else echo \"HTTP_PROXY is set\"; fi\nif [ -z ${https_proxy+x} ]; then echo \"https_proxy is unset\"; else echo \"https_proxy is set\"; fi\nif [ -z ${HTTPS_PROXY+x} ]; then echo \"HTTPS_PROXY is unset\"; else echo \"HTTPS_PROXY is set\"; fi\nif [ -z ${no_proxy+x} ]; then echo \"no_proxy is unset\"; else echo \"no_proxy is set\"; fi\nif [ -z ${NO_PROXY+x} ]; then echo \"NO_PROXY is unset\"; else echo \"NO_PROXY is set\"; fi\nif [ -z ${WSL_PAC_URL+x} ]; then echo \"WSL_PAC_URL is unset\"; else echo \"WSL_PAC_URL is set\"; fi\n\necho \"Printing DNS configuration\"\ncat /etc/resolv.conf\ncat /etc/nsswitch.conf\n\necho \"Printing iptables and nftables rules\"\n# iptables can be configured using both \"iptables\" and the legacy version \"iptables-legacy\". It's possible they can be used together\n# (although not recommended). Collect both to make sure no rules are missed.\n# We list the contents of the most common tables (filter, nat, mangle, raw, security)\niptables -vL -t filter\niptables -vL -t nat\niptables -vL -t mangle\niptables -vL -t raw\niptables -vL -t security\n\nip6tables -vL -t filter\nip6tables -vL -t nat\nip6tables -vL -t mangle\nip6tables -vL -t raw\nip6tables -vL -t security\n\niptables-legacy -vL -t filter\niptables-legacy -vL -t nat\niptables-legacy -vL -t mangle\niptables-legacy -vL -t raw\niptables-legacy -vL -t security\n\nip6tables-legacy -vL -t filter\nip6tables-legacy -vL -t nat\nip6tables-legacy -vL -t mangle\nip6tables-legacy -vL -t raw\nip6tables-legacy -vL -t security\n\nnft list ruleset\n\n# This will print configurations such as net.ipv4.conf.all.route_localnet or net.ipv4.ip_local_port_range\necho \"Printing networking configurations\"\nsysctl -a | grep net\n"
  },
  {
    "path": "diagnostics/wsl.wprp",
    "content": "<?xml version=\"1.0\"?>\n<!--\n  Available Profiles (use profile name with wpr -start):\n  - WSL: General WSL tracing (default)\n  - WSL-Storage: WSL with enhanced storage tracing\n  - WSL-Networking: WSL with comprehensive networking tracing\n  - WSL-HvSocket: WSL with HvSocket-specific tracing\n  \n  Example: wpr -start wsl.wprp!WSL-Networking -filemode\n-->\n<WindowsPerformanceRecorder Version=\"1\">\n  <Profiles>\n    <!-- Event Collectors -->\n    <EventCollector Id=\"Collector\" Name=\"Collector\">\n      <BufferSize Value=\"512\"/>\n      <Buffers Value=\"4096\"/>\n    </EventCollector>\n   \n    <!-- Event Providers -->\n    <EventProvider Id=\"9p\" Name=\"e13c8d52-b153-571f-78c5-1d4098af2a1e\"/>\n    <EventProvider Id=\"9p_errors\" Name=\"06C601B3-6957-4F8C-A15F-74875B24429D\" />\n    <EventProvider Id=\"aad_WPP\" Name=\"4DE9BC9C-B27A-43C9-8994-0915F1A5E24F\" />\n    <EventProvider Id=\"basecsp_WPP\" Name=\"133A980D-035D-4E2D-B250-94577AD8FCED\" />\n    <EventProvider Id=\"BCRYPT_WPP\" Name=\"A74EFE00-14BE-4EF9-9DA9-1484D5473302\" />\n    <EventProvider Id=\"BFE_WPP\" Name=\"106B464A-8043-46B1-8CB8-E92A0CD7A560\" />\n    <EventProvider Id=\"CAPI1_WPP\" Name=\"A74EFE00-14BE-4EF9-9DA9-1484D5473305\" />\n    <EventProvider Id=\"CHAT_WPP\" Name=\"9B52E09F-0C58-4EAF-877F-70F9B54A7946\" />\n    <EventProvider Id=\"cloudap_WPP\" Name=\"EC3CA551-21E9-47D0-9742-1195429831BB\" />\n    <EventProvider Id=\"CNG_WPP\" Name=\"A74EFE00-14BE-4EF9-9DA9-1484D5473303\" />\n    <EventProvider Id=\"credssp_WPP\" Name=\"6165F3E2-AE38-45D4-9B23-6B4818758BD9\" />\n    <EventProvider Id=\"CRYPTOWINRT_WPP\" Name=\"786396CD-2FF3-53D3-D1CA-43E41D9FB73B\" />\n    <EventProvider Id=\"dclocator_WPP\" Name=\"CA030134-54CD-4130-9177-DAE76A3C5791\" />\n    <EventProvider Id=\"digest_WPP\" Name=\"FB6A424F-B5D6-4329-B9B5-A975B3A93EAD\" />\n    <EventProvider Id=\"DNS_WPP\" Name=\"609151DD-04F5-4DA7-974C-FC6947EAA323\" />\n    <EventProvider Id=\"dpapis_WPP\" Name=\"EA3F84FC-03BB-540E-B6AA-9664F81A31FB\" />\n    <EventProvider Id=\"EventProvider_HvSocketTraceProvider\" Name=\"B2ED3BDB-CD74-5B2C-F660-85079CA074B3\" NonPagedMemory=\"true\">\n      <Keywords>\n        <Keyword Value=\"0x0\"/>\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.EMPS.Enrollment\" Name=\"E74EFD1A-B62D-4B83-AB00-66F4A166A2D3\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.EnterpriseManagement.Enrollment\" Name=\"F9E3B648-9AF1-4DC3-9A8E-BF42C0FBCE9A\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Mobile.Provisioning.AppDownload\" Name=\"0BBE6221-EF09-4A3F-82EE-BE00DBB6A98A\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Mobile.Provisioning.Datastore\" Name=\"42C60CEA-0FE7-4541-A86B-9E11F95BD9BF\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Mobile.Provisioning.PhoneProvisioner\" Name=\"B876B1FC-C7F1-443E-9012-86677F7DE580\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Mobile.Provisioning.PPOEM\" Name=\"7EDBED09-1FF7-4FEE-B8C3-5DB694420830\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.API\" Name=\"82ADD491-01D7-4B85-9EAD-192C3CAACA23\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.CommandCsp\" Name=\"00BB69FC-60BC-4502-9438-25608F375CCB\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.CSP\" Name=\"16E12400-A2D8-44B7-9479-004568EC7819\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.Engine\" Name=\"A6A847B7-4429-49AA-BBA6-2AD8C191AC8C\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.Handlers\" Name=\"0383D92C-2337-4F25-A0B5-A51767F04746\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.Migration\" Name=\"A0AF985E-83F9-4E1A-B658-338DCFE27893\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.Operations\" Name=\"7F99598F-B2C1-4371-9911-63DEE13B9EB1\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.Platform\" Name=\"B1F30020-8BC3-4888-BB1B-4DD681F24209\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.Plugin.Engine\" Name=\"55239D60-0EB6-495B-874E-15DE5D5F9A70\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.Plugin.RemovableMedia\" Name=\"B55883E6-6C45-45C2-AB9D-800BB7B66B13\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.ProvLaunch\" Name=\"08FACCFA-125D-4ED6-B0B7-B4A1A912E693\" />\n    <EventProvider Id=\"EventProvider_Microsoft.Windows.Provisioning.ProvTool\" Name=\"2BF4B6BA-556E-4D05-8534-CAFEDF19FED8\" />\n    <EventProvider Id=\"EventProvider_Telemetry_StorVSP\" Name=\"544d0787-9f6d-432e-8414-e035a8b0541d\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"EventProvider_Trace_Vhdmp\" Name=\"dc284fb3-1eab-4854-828a-b25417280280\" Level=\"3\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"gMSAClient_WPP\" Name=\"2D45EC97-EF01-4D4F-B9ED-EE3F4D3C11F3\" />\n    <EventProvider Id=\"hns\" Name=\"0c885e0d-6eb6-476c-a048-2457eed3a5c1\" />\n    <EventProvider Id=\"HTTP_sys_WPP\" Name=\"20F61733-57F1-4127-9F48-4AB7A9308AE2\" />\n    <EventProvider Id=\"HyperV_EmulatedStorage\" Name=\"86E15E01-EDF1-4AC7-89CF-B19563FD6894\" Level=\"6\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"hyperv_storage\" Name=\"c7ad62c6-5c99-5a1b-bbc4-0821ae5b765e\" />\n    <EventProvider Id=\"HyperV_SyntheticStorage\" Name=\"EDACD782-2564-4497-ADE6-7199377850F2\" Level=\"6\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"idcommon_WPP\" Name=\"B1108F75-3252-4B66-9239-80FD47E06494\" />\n    <EventProvider Id=\"idlisten_WPP\" Name=\"D93FE84A-795E-4608-80EC-CE29A96C8658\" />\n    <EventProvider Id=\"idstore_WPP\" Name=\"82C7D3DF-434D-44FC-A7CC-453A8075144E\" />\n    <EventProvider Id=\"IPNat\" Name=\"AE3F6C6D-BF2A-4291-9D07-59E661274EE3\" />\n    <EventProvider Id=\"kdc_WPP\" Name=\"1BBA8B19-7F31-43C0-9643-6E911F79A06B\" />\n    <EventProvider Id=\"kerb_WPP\" Name=\"6B510852-3583-4E2D-AFFE-A67F9F223438\" />\n    <EventProvider Id=\"KerbClientShared_WPP\" Name=\"FACB33C4-4513-4C38-AD1E-57C1F6828FC0\" />\n    <EventProvider Id=\"KerbComm_WPP\" Name=\"60A7AB7A-BC57-43E9-B78A-A1D516577AE3\" />\n    <EventProvider Id=\"kps_WPP\" Name=\"97A38277-13C0-4394-A0B2-2A70B465D64F\" />\n    <EventProvider Id=\"livessp_WPP\" Name=\"C10B942D-AE1B-4786-BC66-052E5B4BE40E\" />\n    <EventProvider Id=\"LsaAudit_WPP\" Name=\"DAA76F6A-2D11-4399-A646-1D62B7380F15\" />\n    <EventProvider Id=\"LsaDs_WPP\" Name=\"169EC169-5B77-4A3E-9DB6-441799D5CACB\" />\n    <EventProvider Id=\"LsaIso_WPP\" Name=\"366B218A-A5AA-4096-8131-0BDAFCC90E93\" />\n    <EventProvider Id=\"LsaTrace_WPP\" Name=\"D0B639E0-E650-4D1D-8F39-1580ADE72784\" />\n    <EventProvider Id=\"lxcore_kernel\" Name=\"0CD1C309-0878-4515-83DB-749843B3F5C9\"/>\n    <EventProvider Id=\"lxcore_service\" Name=\"B99CDB5A-039C-5046-E672-1A0DE0A40211\"/>\n    <EventProvider Id=\"lxcore_user\" Name=\"D90B9468-67F0-5B3B-42CC-82AC81FFD960\"/>\n    <EventProvider Id=\"Microsoft_OneCore_NetworkingTriage_GetConnected\" Name=\"60523747-6516-48B7-84B1-3264FA2CB359\" />\n    <EventProvider Id=\"Microsoft_OneCore_NetworkingTriage_OnDemand\" Name=\"ABB1FC61-49BA-4CC3-809F-7ABE1F8BA315\" />\n    <EventProvider Id=\"Microsoft_OneCore_NetworkingTriage_RoutePolicyManagement\" Name=\"923C0FFD-7933-4B52-8A49-121ABF2DC357\" />\n    <EventProvider Id=\"Microsoft_OSG_OSS_CredProvFramework_ReportResultStop_WPP\" Name=\"8DB3086D-116F-5BED-CFD5-9AFDA80D28EA\" />\n    <EventProvider Id=\"Microsoft_OSG_Wininet_WPP\" Name=\"1A211EE8-52DB-4AF0-BB66-FB8C9F20B0E2\" />\n    <EventProvider Id=\"Microsoft_Quic_ETW\" Name=\"ff15e657-4f26-570e-88ab-0796b258d11c\" />\n    <EventProvider Id=\"Microsoft_Web_Platform_WPP\" Name=\"FF32ADA1-5A4B-583C-889E-A3C027B201F5\" />\n    <EventProvider Id=\"Microsoft_Windows_AppModel_Exec_WPP\" Name=\"EB65A492-86C0-406A-BACE-9912D595BD69\" />\n    <EventProvider Id=\"Microsoft_Windows_BranchCacheClientEventProvider\" Name=\"e837619c-a2a8-4689-833f-47b48ebd2442\" />\n    <EventProvider Id=\"Microsoft_Windows_BranchCacheEventProvider\" Name=\"dd85457f-4e2d-44a5-a7a7-6253362e34dc\" />\n    <EventProvider Id=\"Microsoft_Windows_BrokerInfrastructure_WPP\" Name=\"63B6C2D2-0440-44DE-A674-AA51A251B123\" />\n    <EventProvider Id=\"Microsoft_Windows_CAPI2_WPP\" Name=\"5BBCA4A8-B209-48DC-A8C7-B23D3E5216FB\" />\n    <EventProvider Id=\"Microsoft_Windows_CertificateServices_Deployment_WPP\" Name=\"B2D1F576-2E85-4489-B504-1861C40544B3\" />\n    <EventProvider Id=\"Microsoft_Windows_CertificateServicesClient_AutoEnrollment_WPP\" Name=\"F0DB7EF8-B6F3-4005-9937-FEB77B9E1B43\" />\n    <EventProvider Id=\"Microsoft_Windows_CertificateServicesClient_CertEnroll_WPP\" Name=\"54164045-7C50-4905-963F-E5BC1EEF0CCA\" />\n    <EventProvider Id=\"Microsoft_Windows_CertificateServicesClient_CredentialRoaming_WPP\" Name=\"89A2278B-C662-4AFF-A06C-46AD3F220BCA\" />\n    <EventProvider Id=\"Microsoft_Windows_CertificateServicesClient_Lifecycle_System_WPP\" Name=\"BC0669E1-A10D-4A78-834E-1CA3C806C93B\" />\n    <EventProvider Id=\"Microsoft_Windows_CertificateServicesClient_Lifecycle_User_WPP\" Name=\"BEA18B89-126F-4155-9EE4-D36038B02680\" />\n    <EventProvider Id=\"Microsoft_Windows_CertificateServicesClient_WPP\" Name=\"73370BD6-85E5-430B-B60A-FEA1285808A7\" />\n    <EventProvider Id=\"Microsoft_Windows_CertificationAuthorityClient_CertCli_WPP\" Name=\"98BF1CD3-583E-4926-95EE-A61BF3F46470\" />\n    <EventProvider Id=\"Microsoft_Windows_CertPolEng_WPP\" Name=\"AF9CC194-E9A8-42BD-B0D1-834E9CFAB799\" />\n    <EventProvider Id=\"Microsoft_Windows_DeviceManagement_Enterprise_Diagnostics_Provider_WPP\" Name=\"3DA494E4-0FE2-415C-B895-FB5265C5C83B\" />\n    <EventProvider Id=\"Microsoft_Windows_DeviceManagement_SCEP_WPP\" Name=\"D5A5B540-C580-4DEE-8BB4-185E34AA00C5\" />\n    <EventProvider Id=\"Microsoft_Windows_DM_Enrollment_Provider_WPP\" Name=\"9FBF7B95-0697-4935-ADA2-887BE9DF12BC\" />\n    <EventProvider Id=\"Microsoft_Windows_DNS_Client\" Name=\"1c95126e-7eea-49a9-a3fe-a378b03ddb4d\" />\n    <EventProvider Id=\"Microsoft_Windows_DNS_Client_Configuration_PowerShell\" Name=\"563a50d8-3536-4c8a-a361-b37af04094ec\" />\n    <EventProvider Id=\"Microsoft_Windows_DNS_Client_WinRNR\" Name=\"b923f87a-b069-42b5-bd32-35623aba1c48\" />\n    <EventProvider Id=\"Microsoft_Windows_HttpService\" Name=\"dd5ef90a-6398-47a4-ad34-4dcecdef795f\" />\n    <EventProvider Id=\"Microsoft_Windows_LiveId_WPP\" Name=\"05F02597-FE85-4E67-8542-69567AB8FD4F\" />\n    <EventProvider Id=\"Microsoft_Windows_MicrosoftAccount_TBProvider_WPP\" Name=\"5836994D-A677-53E7-1389-588AD1420CC5\" />\n    <EventProvider Id=\"Microsoft_Windows_ProcessStateManager_WPP\" Name=\"D49918CF-9489-4BF1-9D7B-014D864CF71F\" />\n    <EventProvider Id=\"Microsoft_Windows_ResourceManager_WPP\" Name=\"4180C4F7-E238-5519-338F-EC214F0B49AA\" />\n    <EventProvider Id=\"Microsoft_Windows_Runtime_Web_Http\" Name=\"41877cb4-11fc-4188-b590-712c143c881d\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_AADAuthHelper_WPP\" Name=\"9EBB3B15-B094-41B1-A3B8-0F141B06BADD\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_AADCloudAPPlugin_WPP\" Name=\"556045FD-58C5-4A97-9881-B121F68B79C5\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_Biometrics_BioCredProv_WPP\" Name=\"9DADD79B-D556-53F2-67C4-129FA62B7512\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_Biometrics_Client_WPP\" Name=\"1B5106B1-7622-4740-AD81-D9C6EE74F124\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_Biometrics_CredentialProvider_Face_WPP\" Name=\"1D480C11-3870-4B19-9144-47A53CD973BD\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_Biometrics_FingerprintCredential_WPP\" Name=\"225B3FED-0356-59D1-1F82-EED163299FA8\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_Biometrics_Service_WPP\" Name=\"39A5AA08-031D-4777-A32D-ED386BF03470\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_Biometrics_Trustlet_WPP\" Name=\"9DF19CFA-E122-5343-284B-F3945CCD65B2\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_Credentials_UserConsentVerifier_WPP\" Name=\"507C53AE-AF42-5938-AEDE-4A9D908640ED\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_DevCredClient_WPP\" Name=\"78983C7D-917F-58DA-E8D4-F393DECF4EC0\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_DevCredProv_WPP\" Name=\"7955D36A-450B-5E2A-A079-95876BCA450A\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_DevCredSvc_WPP\" Name=\"C3FEB5BF-1A8D-53F3-AAA8-44496392BF69\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_DevCredTask_WPP\" Name=\"86D5FE65-0564-4618-B90B-E146049DEBF4\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_DevCredWinRt_WPP\" Name=\"36FF4C84-82A2-4B23-8BA5-A25CBDFF3410\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_DeviceLock_WPP\" Name=\"2056054C-97A6-5AE4-B181-38BC6B58007E\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_Fido_WPP\" Name=\"08B15CE7-C9FF-5E64-0D16-66589573C50F\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_LsaSrv_WPP\" Name=\"4D9DFB91-4337-465A-A8B5-05A27D930D48\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_CredProv_WPP\" Name=\"CAC8D861-7B16-5B6B-5FC0-85014776BDAC\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_CryptNgc_WPP\" Name=\"6D7051A0-9C83-5E52-CF8F-0ECAF5D5F6FD\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_CSP_WPP\" Name=\"89F392FF-EE7C-56A3-3F61-2D5B31A36935\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_KeyCredMgr_WPP\" Name=\"34646397-1635-5D14-4D2C-2FEBDCCCF5E9\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_KeyStaging_WPP\" Name=\"9B223F67-67A1-5B53-9126-4593FE81DF25\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_KspSvc_WPP\" Name=\"B66B577F-AE49-5CCF-D2D7-8EB96BFD440C\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_Local_WPP\" Name=\"3B9DBF69-E9F0-5389-D054-A94BC30E33F7\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_LocalAccountMigPlugin_WPP\" Name=\"CDD94AC7-CD2F-5189-E126-2DEB1B2FACBF\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_NgcCtnr_WPP\" Name=\"0ABA6892-455B-551D-7DA8-3A8F85225E1A\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_NgcCtnrSvc_WPP\" Name=\"9DF6A82D-5174-5EBF-842A-39947C48BF2A\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_NgcIsoCtnr_WPP\" Name=\"CDC6BEB9-6D78-5138-D232-D951916AB98F\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_PopKeySrv_WPP\" Name=\"1D6540CE-A81B-4E74-AD35-EEF8463F97F5\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_Recovery_WPP\" Name=\"9D4CA978-8A14-545E-C047-A45991F0E92F\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_NGC_Trustlet_WPP\" Name=\"C0B2937D-E634-56A2-1451-7D678AA3BC53\" />\n    <EventProvider Id=\"Microsoft_Windows_Security_TokenBroker_WPP\" Name=\"077B8C4A-E425-578D-F1AC-6FDF1220FF68\" />\n    <EventProvider Id=\"Microsoft_Windows_Shell_CloudDomainJoin_Client_WPP\" Name=\"AA02D1A4-72D8-5F50-D425-7402EA09253A\" />\n    <EventProvider Id=\"Microsoft_Windows_Shell_CloudExperienceHost_WPP\" Name=\"D0034F5E-3686-5A74-DC48-5A22DD4F3D5B\" />\n    <EventProvider Id=\"Microsoft_Windows_WBioSrvc_WPP\" Name=\"34BEC984-F11F-4F1F-BB9B-3BA33C8D0132\" />\n    <EventProvider Id=\"Microsoft_Windows_WebIO\" Name=\"50b3e73c-9370-461d-bb9f-26f32d68887d\" />\n    <EventProvider Id=\"Microsoft_Windows_WinHttp\" Name=\"7d44233d-3055-4b9c-ba64-0d47ca40a232\" />\n    <EventProvider Id=\"Microsoft_Windows_WinINet\" Name=\"43d1a55c-76d6-4f7e-995c-64c711e5cafe\" />\n    <EventProvider Id=\"Microsoft_Windows_Winsock_NameResolution\" Name=\"55404E71-4DB9-4DEB-A5F5-8F86E46DDE56\" />\n    <EventProvider Id=\"Microsoft-Windows-DHCP-Client\" Name=\"15A7A4F8-0072-4EAB-ABAD-F98A4D666AED\" />\n    <EventProvider Id=\"Microsoft-Windows-DHCPv6-Client\" Name=\"6A1F2B00-6A90-4C38-95A5-5CAB3B056778\" />\n    <EventProvider Id=\"Microsoft-Windows-Host-Network-Management\" Name=\"93f693dc-9163-4dee-af64-d855218af242\" />\n    <EventProvider Id=\"Microsoft-Windows-Hyper-V-VfpExt\" Name=\"9F2660EA-CFE7-428F-9850-AECA612619B0\"/>\n    <EventProvider Id=\"Microsoft-Windows-Hyper-V-VmSwitch\" Name=\"67DC0D66-3695-47C0-9642-33F76F7BD7AD\"/>\n    <EventProvider Id=\"Microsoft-Windows-HyperV-Vid\" Name=\"5931D877-4860-4ee7-A95C-610A5F0D1407\" NonPagedMemory=\"true\" />\n    <EventProvider Id=\"Microsoft-Windows-Overlay-HNSPlugin\" Name=\"564368D6-577B-4af5-AD84-1C54464848E6\" />\n    <EventProvider Id=\"Microsoft-Windows-SharedAccess_NAT\" Name=\"A6F32731-9A38-4159-A220-3D9B7FC5FE5D\" />\n    <EventProvider Id=\"Microsoft-Windows-TCPIP\" Name=\"2f07e2ee-15db-40f1-90ef-9d7ba282188a\" NonPagedMemory=\"true\" EventKey=\"true\" Strict=\"true\"/>\n    <EventProvider Id=\"Microsoft-Windows-WinNat\" Name=\"66C07ECD-6667-43FC-93F8-05CF07F446EC\" />\n    <EventProvider Id=\"Microsoft.Windows.HostNetworkingService.PrivateCloudPlugin\" Name=\"D0E4BC17-34C7-43fc-9A72-D89A59D6979A\" />\n    <EventProvider Id=\"Microsoft.Windows.Hyper.V.NetSetupHelper\" Name=\"94DEB9D1-0A52-449B-B368-41E4426B4F36\"/>\n    <EventProvider Id=\"Microsoft.Windows.Hyper.V.VmsIf\" Name=\"6C28C7E5-331B-4437-9C69-5352A2F7F296\" />\n    <EventProvider Id=\"Microsoft.Windows.HyperV.Compute\" Name=\"80CE50DE-D264-4581-950D-ABADEEE0D340\" />\n    <EventProvider Id=\"Microsoft.Windows.HyperV.Socket\" Name=\"b2ed3bdb-cd74-5b2c-f660-85079ca074b3\"/>\n    <EventProvider Id=\"Microsoft.Windows.HyperV.Vid\" Name=\"1111450B-DACC-40A3-84AB-F7DBA4A6E63A\" NonPagedMemory=\"true\"/>\n    <EventProvider Id=\"Microsoft.Windows.Networking.FlowSteering\" Name=\"662abf07-6dda-5b25-c2c5-345236dbb2d2\"/>\n    <EventProvider Id=\"mpssvcService\" Name=\"5EEFEBDB-E90C-423a-8ABF-0241E7C5B87D\" />\n    <EventProvider Id=\"MsQuic_WPP\" Name=\"620FD025-BE51-42EF-A5C0-50F13F183AD9\" />\n    <EventProvider Id=\"msxml_WPP\" Name=\"927821F8-D9E6-42E2-84F2-949E18256F3A\" />\n    <EventProvider Id=\"mup\" Name=\"20c46239-d059-4214-a11e-7d6769cbe020\" />\n    <EventProvider Id=\"NCRYPT_WPP\" Name=\"A74EFE00-14BE-4EF9-9DA9-1484D5473301\" />\n    <EventProvider Id=\"NCRYPTSSLP_WPP\" Name=\"A74EFE00-14BE-4EF9-9DA9-1484D5473304\" />\n    <EventProvider Id=\"ndis_wpp\" Name=\"DD7A21E6-A651-46D4-B7C2-66543067B869\"/>\n    <EventProvider Id=\"negoexts_WPP\" Name=\"5AF52B0D-E633-4EAD-828A-4B85B8DAAC2B\" />\n    <EventProvider Id=\"Netio_WPP\" Name=\"EB004A05-9B1A-11D4-9123-0050047759BC\" />\n    <EventProvider Id=\"netmgmt\" Name=\"93f693dc-9163-4dee-af64-d855218af242\" />\n    <EventProvider Id=\"ntlm_WPP\" Name=\"5BBB6C18-AA45-49B1-A15F-085F7ED0AA90\" />\n    <EventProvider Id=\"NtlmShared_WPP\" Name=\"AC69AE5B-5B21-405F-8266-4424944A43E9\" />\n    <EventProvider Id=\"p9rdr\" Name=\"bb1d36f0-e0e0-48cc-9493-fef0e3d0b28c\" NonPagedMemory=\"true\" Strict=\"true\"/>\n    <EventProvider Id=\"pku2u_WPP\" Name=\"2A6FAF47-5449-4805-89A3-A504F3E221A6\" />\n    <EventProvider Id=\"PortTrackerServer_WPP\" Name=\"090bf343-4490-42d3-b273-8af174d314fb\" />\n    <EventProvider Id=\"rfsmon\" Name=\"51734B23-5B7E-4892-BA8E-45BC110B735C\" />\n    <EventProvider Id=\"SharedAccessWPP\" Name=\"9B322459-4AD9-4F81-8EEA-DC77CDD18CA6\" />\n    <EventProvider Id=\"storvsp\" Name=\"10b3d268-9782-49a4-aacc-a93c5482cb6f\" NonPagedMemory=\"true\" Level=\"4\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"storvsp_io\" Name=\"10b3d268-9782-49a4-aacc-a93c5482cb6f\" NonPagedMemory=\"true\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"storvsp_wpp\" Name=\"f96abc17-6a5e-4a49-a3f4-a2a86fa03846\" NonPagedMemory=\"true\" Level=\"4\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"TLS_WPP\" Name=\"37D2C3CD-C5D4-4587-8531-4696C44244C8\" />\n    <EventProvider Id=\"TokenBinding_WPP\" Name=\"AEF66A73-A765-4421-B348-B7E0FC8C9898\" />\n    <EventProvider Id=\"TPM_WPP\" Name=\"3A8D6942-B034-48E2-B314-F69C2B4655A3\" />\n    <EventProvider Id=\"UrlMon_WPP\" Name=\"3FAB3162-24CE-4ED5-B23D-63501544E11D\" />\n    <EventProvider Id=\"vault_WPP\" Name=\"7FDD167C-79E5-4403-8C84-B7C0BB9923A1\" />\n    <EventProvider Id=\"vfpext\" Name=\"9F2660EA-CFE7-428F-9850-AECA612619B0\" />\n    <EventProvider Id=\"vhdmp\" Name=\"E2816346-87F4-4F85-95C3-0C79409AA89D\" NonPagedMemory=\"true\" Level=\"4\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFD\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"vhdmp_wpp\" Name=\"3c70c3b0-2fae-41d3-b68d-8f7fcaf79adb\" NonPagedMemory=\"true\" Level=\"5\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"virtdisk\" Name=\"4D20DF22-E177-4514-A369-F1759FEEDEB3\" Level=\"4\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"virtdisk_wpp\" Name=\"e14dcdd9-d1ec-4dc3-8395-a606df8ef115\" Level=\"4\">\n      <Keywords>\n        <Keyword Value=\"0xFFFFFFFFFFFFFFFF\" />\n      </Keywords>\n    </EventProvider>\n    <EventProvider Id=\"vm_chipset\" Name=\"de9ba731-7f33-4f44-98c9-6cac856b9f83\"/>\n    <EventProvider Id=\"vmcompute\" Name=\"17103E3F-3C6E-4677-BB17-3B267EB5BE57\"/>\n    <EventProvider Id=\"vmcompute_dll\" Name=\"AF7FD3A7-B248-460C-A9F5-FEC39EF8468C\"/>\n    <EventProvider Id=\"vmmm\" Name=\"6066F867-7CA1-4418-85FD-36E3F9C0600C\"/>\n    <EventProvider Id=\"VmSwitch_wpp\" Name=\"1F387CBC-6818-4530-9DB6-5F1058CD7E86\"/>\n    <EventProvider Id=\"vmwp\" Name=\"51DDFA29-D5C8-4803-BE4B-2ECB715570FE\"/>\n    <EventProvider Id=\"webauth_WPP\" Name=\"EF98103D-8D3A-4BEF-9DF2-2156563E64FA\" />\n    <EventProvider Id=\"Webio_WPP\" Name=\"08F93B14-1608-4A72-9CFA-457EECEDBBA7\" />\n    <EventProvider Id=\"webplatform_WPP\" Name=\"2A3C6602-411E-4DC6-B138-EA19D64F5BBA\" />\n    <EventProvider Id=\"WinHttp_WPP\" Name=\"B3A7698A-0C45-44DA-B73D-E181C9B5C8E6\" />\n    <EventProvider Id=\"Wininet_WPP\" Name=\"4E749B6A-667D-4C72-80EF-373EE3246B08\" />\n    <EventProvider Id=\"WinNatSys\" Name=\"aa7387cf-3639-496a-b3bf-dc1e79a6fc5a\" />\n    <EventProvider Id=\"Winsock_WPP\" Name=\"1D44957B-7181-4835-B70A-D0B16112A4DE\" />\n    <EventProvider Id=\"wlidsvc_WPP\" Name=\"3F8B9EF5-BBD2-4C81-B6C9-DA3CDB72D3C5\" />\n    <EventProvider Id=\"wsl_devicehost\" Name=\"9d6c7b9e-2581-4d8a-b8c5-b90b4a17094a\"/>\n    <EventProvider Id=\"wslapi\" Name=\"beb94edf-1a7b-5058-0696-ff9e6b1798d1\"/>\n    <EventProvider Id=\"wslaservice\" Name=\"0383CE62-8F86-4766-AFB2-9D66A7FB1E90\"/>\n    <EventProvider Id=\"wslclient\" Name=\"8cbb7724-7223-5d6f-8137-564dac45104d\"/>\n\n    <!-- PROFILE: General WSL Tracing (Default, also serves as base for other profiles) -->\n    <Profile\n      Id=\"WSL.Verbose.File\"\n      Name=\"WSL\"\n      Description=\"General traces for WSL components\"\n      LoggingMode=\"File\"\n      DetailLevel=\"Verbose\"\n      >\n      <Collectors>\n        <EventCollectorId Value=\"Collector\">\n          <EventProviders>\n            <EventProviderId Value=\"9p\"/>\n            <EventProviderId Value=\"9p_errors\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.EMPS.Enrollment\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.EnterpriseManagement.Enrollment\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Mobile.Provisioning.AppDownload\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Mobile.Provisioning.Datastore\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Mobile.Provisioning.PhoneProvisioner\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Mobile.Provisioning.PPOEM\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.API\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.CommandCsp\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.CSP\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.Engine\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.Handlers\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.Migration\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.Operations\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.Platform\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.Plugin.Engine\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.Plugin.RemovableMedia\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.ProvLaunch\"/>\n            <EventProviderId Value=\"EventProvider_Microsoft.Windows.Provisioning.ProvTool\"/>\n            <EventProviderId Value=\"EventProvider_Telemetry_StorVSP\"/>\n            <EventProviderId Value=\"EventProvider_Trace_Vhdmp\"/>\n            <EventProviderId Value=\"hns\"/>\n            <EventProviderId Value=\"HyperV_EmulatedStorage\"/>\n            <EventProviderId Value=\"hyperv_storage\"/>\n            <EventProviderId Value=\"HyperV_SyntheticStorage\"/>\n            <EventProviderId Value=\"lxcore_kernel\"/>\n            <EventProviderId Value=\"lxcore_service\"/>\n            <EventProviderId Value=\"lxcore_user\"/>\n            <EventProviderId Value=\"Microsoft-Windows-HyperV-Vid\"/>\n            <EventProviderId Value=\"Microsoft.Windows.HyperV.Vid\"/>\n            <EventProviderId Value=\"mup\"/>\n            <EventProviderId Value=\"netmgmt\"/>\n            <EventProviderId Value=\"p9rdr\"/>\n            <EventProviderId Value=\"rfsmon\"/>\n            <EventProviderId Value=\"storvsp\"/>\n            <EventProviderId Value=\"vfpext\"/>\n            <EventProviderId Value=\"vhdmp\"/>\n            <EventProviderId Value=\"vhdmp_wpp\"/>\n            <EventProviderId Value=\"virtdisk\"/>\n            <EventProviderId Value=\"virtdisk_wpp\"/>\n            <EventProviderId Value=\"vm_chipset\"/>\n            <EventProviderId Value=\"vmcompute\"/>\n            <EventProviderId Value=\"vmcompute_dll\"/>\n            <EventProviderId Value=\"vmmm\"/>\n            <EventProviderId Value=\"vmwp\"/>\n            <EventProviderId Value=\"wsl_devicehost\"/>\n            <EventProviderId Value=\"wslapi\"/>\n            <EventProviderId Value=\"wslaservice\"/>\n            <EventProviderId Value=\"wslclient\"/>\n          </EventProviders>\n        </EventCollectorId>\n      </Collectors>\n    </Profile>\n\n    <!-- PROFILE: Storage-focused WSL Tracing -->\n    <Profile\n      Id=\"WSL-Storage.Verbose.File\"\n      Name=\"WSL-Storage\"\n      Description=\"WSL traces with enhanced storage providers\"\n      LoggingMode=\"File\"\n      DetailLevel=\"Verbose\"\n      Base=\"WSL.Verbose.File\"\n      >\n      <Collectors>\n        <EventCollectorId Value=\"Collector\">\n          <EventProviders>\n            <!-- Additional storage-specific providers -->\n            <EventProviderId Value=\"storvsp_io\"/>\n            <EventProviderId Value=\"storvsp_wpp\"/>\n          </EventProviders>\n        </EventCollectorId>\n      </Collectors>\n    </Profile>\n\n    <!-- PROFILE: Networking-focused WSL Tracing -->\n    <Profile\n      Id=\"WSL-Networking.Verbose.File\"\n      Name=\"WSL-Networking\"\n      Description=\"WSL traces with comprehensive networking providers\"\n      LoggingMode=\"File\"\n      DetailLevel=\"Verbose\"\n      Base=\"WSL.Verbose.File\"\n      >\n      <Collectors>\n        <EventCollectorId Value=\"Collector\">\n          <EventProviders>\n            <!-- Additional networking-specific providers -->\n            <EventProviderId Value=\"aad_WPP\"/>\n            <EventProviderId Value=\"basecsp_WPP\"/>\n            <EventProviderId Value=\"BCRYPT_WPP\"/>\n            <EventProviderId Value=\"BFE_WPP\"/>\n            <EventProviderId Value=\"CAPI1_WPP\"/>\n            <EventProviderId Value=\"CHAT_WPP\"/>\n            <EventProviderId Value=\"cloudap_WPP\"/>\n            <EventProviderId Value=\"CNG_WPP\"/>\n            <EventProviderId Value=\"credssp_WPP\"/>\n            <EventProviderId Value=\"CRYPTOWINRT_WPP\"/>\n            <EventProviderId Value=\"dclocator_WPP\"/>\n            <EventProviderId Value=\"digest_WPP\"/>\n            <EventProviderId Value=\"DNS_WPP\"/>\n            <EventProviderId Value=\"dpapis_WPP\"/>\n            <EventProviderId Value=\"gMSAClient_WPP\"/>\n            <EventProviderId Value=\"HTTP_sys_WPP\"/>\n            <EventProviderId Value=\"idcommon_WPP\"/>\n            <EventProviderId Value=\"idlisten_WPP\"/>\n            <EventProviderId Value=\"idstore_WPP\"/>\n            <EventProviderId Value=\"IPNat\"/>\n            <EventProviderId Value=\"kdc_WPP\"/>\n            <EventProviderId Value=\"kerb_WPP\"/>\n            <EventProviderId Value=\"KerbClientShared_WPP\"/>\n            <EventProviderId Value=\"KerbComm_WPP\"/>\n            <EventProviderId Value=\"kps_WPP\"/>\n            <EventProviderId Value=\"livessp_WPP\"/>\n            <EventProviderId Value=\"LsaAudit_WPP\"/>\n            <EventProviderId Value=\"LsaDs_WPP\"/>\n            <EventProviderId Value=\"LsaIso_WPP\"/>\n            <EventProviderId Value=\"LsaTrace_WPP\"/>\n            <EventProviderId Value=\"Microsoft_OneCore_NetworkingTriage_GetConnected\"/>\n            <EventProviderId Value=\"Microsoft_OneCore_NetworkingTriage_OnDemand\"/>\n            <EventProviderId Value=\"Microsoft_OneCore_NetworkingTriage_RoutePolicyManagement\"/>\n            <EventProviderId Value=\"Microsoft_OSG_OSS_CredProvFramework_ReportResultStop_WPP\"/>\n            <EventProviderId Value=\"Microsoft_OSG_Wininet_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Quic_ETW\"/>\n            <EventProviderId Value=\"Microsoft_Web_Platform_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_AppModel_Exec_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_BranchCacheClientEventProvider\"/>\n            <EventProviderId Value=\"Microsoft_Windows_BranchCacheEventProvider\"/>\n            <EventProviderId Value=\"Microsoft_Windows_BrokerInfrastructure_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CAPI2_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CertificateServices_Deployment_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CertificateServicesClient_AutoEnrollment_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CertificateServicesClient_CertEnroll_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CertificateServicesClient_CredentialRoaming_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CertificateServicesClient_Lifecycle_System_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CertificateServicesClient_Lifecycle_User_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CertificateServicesClient_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CertificationAuthorityClient_CertCli_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_CertPolEng_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_DeviceManagement_Enterprise_Diagnostics_Provider_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_DeviceManagement_SCEP_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_DM_Enrollment_Provider_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_DNS_Client\"/>\n            <EventProviderId Value=\"Microsoft_Windows_DNS_Client_Configuration_PowerShell\"/>\n            <EventProviderId Value=\"Microsoft_Windows_DNS_Client_WinRNR\"/>\n            <EventProviderId Value=\"Microsoft_Windows_HttpService\"/>\n            <EventProviderId Value=\"Microsoft_Windows_LiveId_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_MicrosoftAccount_TBProvider_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_ProcessStateManager_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_ResourceManager_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Runtime_Web_Http\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_AADAuthHelper_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_AADCloudAPPlugin_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_Biometrics_BioCredProv_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_Biometrics_Client_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_Biometrics_CredentialProvider_Face_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_Biometrics_FingerprintCredential_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_Biometrics_Service_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_Biometrics_Trustlet_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_Credentials_UserConsentVerifier_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_DevCredClient_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_DevCredProv_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_DevCredSvc_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_DevCredTask_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_DevCredWinRt_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_DeviceLock_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_Fido_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_LsaSrv_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_CredProv_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_CryptNgc_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_CSP_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_KeyCredMgr_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_KeyStaging_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_KspSvc_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_Local_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_LocalAccountMigPlugin_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_NgcCtnr_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_NgcCtnrSvc_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_NgcIsoCtnr_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_PopKeySrv_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_Recovery_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_NGC_Trustlet_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Security_TokenBroker_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Shell_CloudDomainJoin_Client_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Shell_CloudExperienceHost_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_WBioSrvc_WPP\"/>\n            <EventProviderId Value=\"Microsoft_Windows_WebIO\"/>\n            <EventProviderId Value=\"Microsoft_Windows_WinHttp\"/>\n            <EventProviderId Value=\"Microsoft_Windows_WinINet\"/>\n            <EventProviderId Value=\"Microsoft_Windows_Winsock_NameResolution\"/>\n            <EventProviderId Value=\"Microsoft-Windows-DHCP-Client\"/>\n            <EventProviderId Value=\"Microsoft-Windows-DHCPv6-Client\"/>\n            <EventProviderId Value=\"Microsoft-Windows-Host-Network-Management\"/>\n            <EventProviderId Value=\"Microsoft-Windows-Hyper-V-VfpExt\"/>\n            <EventProviderId Value=\"Microsoft-Windows-Hyper-V-VmSwitch\"/>\n            <EventProviderId Value=\"Microsoft-Windows-Overlay-HNSPlugin\"/>\n            <EventProviderId Value=\"Microsoft-Windows-SharedAccess_NAT\"/>\n            <EventProviderId Value=\"Microsoft-Windows-TCPIP\"/>\n            <EventProviderId Value=\"Microsoft-Windows-WinNat\"/>\n            <EventProviderId Value=\"Microsoft.Windows.HostNetworkingService.PrivateCloudPlugin\"/>\n            <EventProviderId Value=\"Microsoft.Windows.Hyper.V.NetSetupHelper\"/>\n            <EventProviderId Value=\"Microsoft.Windows.Hyper.V.VmsIf\"/>\n            <EventProviderId Value=\"Microsoft.Windows.HyperV.Compute\"/>\n            <EventProviderId Value=\"Microsoft.Windows.HyperV.Socket\"/>\n            <EventProviderId Value=\"Microsoft.Windows.Networking.FlowSteering\"/>\n            <EventProviderId Value=\"mpssvcService\"/>\n            <EventProviderId Value=\"MsQuic_WPP\"/>\n            <EventProviderId Value=\"msxml_WPP\"/>\n            <EventProviderId Value=\"NCRYPT_WPP\"/>\n            <EventProviderId Value=\"NCRYPTSSLP_WPP\"/>\n            <EventProviderId Value=\"ndis_wpp\"/>\n            <EventProviderId Value=\"negoexts_WPP\"/>\n            <EventProviderId Value=\"Netio_WPP\"/>\n            <EventProviderId Value=\"ntlm_WPP\"/>\n            <EventProviderId Value=\"NtlmShared_WPP\"/>\n            <EventProviderId Value=\"pku2u_WPP\"/>\n            <EventProviderId Value=\"PortTrackerServer_WPP\"/>\n            <EventProviderId Value=\"SharedAccessWPP\"/>\n            <EventProviderId Value=\"TLS_WPP\"/>\n            <EventProviderId Value=\"TokenBinding_WPP\"/>\n            <EventProviderId Value=\"TPM_WPP\"/>\n            <EventProviderId Value=\"UrlMon_WPP\"/>\n            <EventProviderId Value=\"vault_WPP\"/>\n            <EventProviderId Value=\"VmSwitch_wpp\"/>\n            <EventProviderId Value=\"webauth_WPP\"/>\n            <EventProviderId Value=\"Webio_WPP\"/>\n            <EventProviderId Value=\"webplatform_WPP\"/>\n            <EventProviderId Value=\"WinHttp_WPP\"/>\n            <EventProviderId Value=\"Wininet_WPP\"/>\n            <EventProviderId Value=\"WinNatSys\"/>\n            <EventProviderId Value=\"Winsock_WPP\"/>\n            <EventProviderId Value=\"wlidsvc_WPP\"/>\n          </EventProviders>\n        </EventCollectorId>\n      </Collectors>\n    </Profile>\n\n    <!-- PROFILE: HvSocket-focused WSL Tracing -->\n    <Profile\n      Id=\"WSL-HvSocket.Verbose.File\"\n      Name=\"WSL-HvSocket\"\n      Description=\"WSL traces with HvSocket-specific providers\"\n      LoggingMode=\"File\"\n      DetailLevel=\"Verbose\"\n      Base=\"WSL.Verbose.File\"\n      >\n      <Collectors>\n        <EventCollectorId Value=\"Collector\">\n          <EventProviders>\n            <!-- Additional providers specific to HvSocket profile -->\n            <EventProviderId Value=\"EventProvider_HvSocketTraceProvider\"/>\n          </EventProviders>\n        </EventCollectorId>\n      </Collectors>\n    </Profile>\n\n  </Profiles>\n</WindowsPerformanceRecorder>\n"
  },
  {
    "path": "distributions/DistributionInfo.json",
    "content": "{\n    \"ModernDistributions\": {\n        \"Ubuntu\": [\n            {\n                \"Name\": \"Ubuntu\",\n                \"FriendlyName\": \"Ubuntu\",\n                \"Default\": true,\n                \"Amd64Url\": {\n                    \"Url\": \"https://releases.ubuntu.com/24.04.4/ubuntu-24.04.4-wsl-amd64.wsl\",\n                    \"Sha256\": \"9b2f7730dc68227dd04a9f3e5eab86ad85caf556b8606ad94f1f29ff5c4fd3f5\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://cdimages.ubuntu.com/releases/24.04.4/release/ubuntu-24.04.4-wsl-arm64.wsl\",\n                    \"Sha256\": \"6b244d89f412a68f51e58f396fab65bed3b5896a25c045a99bef9c78a07df507\"\n                }\n            },\n            {\n                \"Name\": \"Ubuntu-24.04\",\n                \"FriendlyName\": \"Ubuntu 24.04 LTS\",\n                \"Default\": false,\n                \"Amd64Url\": {\n                    \"Url\": \"https://releases.ubuntu.com/24.04.4/ubuntu-24.04.4-wsl-amd64.wsl\",\n                    \"Sha256\": \"9b2f7730dc68227dd04a9f3e5eab86ad85caf556b8606ad94f1f29ff5c4fd3f5\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://cdimages.ubuntu.com/releases/24.04.4/release/ubuntu-24.04.4-wsl-arm64.wsl\",\n                    \"Sha256\": \"6b244d89f412a68f51e58f396fab65bed3b5896a25c045a99bef9c78a07df507\"\n                }\n            },\n            {\n                \"Name\": \"Ubuntu-22.04\",\n                \"FriendlyName\": \"Ubuntu 22.04 LTS\",\n                \"Default\": false,\n                \"Amd64Url\": {\n                    \"Url\": \"https://releases.ubuntu.com/jammy/ubuntu-22.04.5-wsl-amd64.wsl\",\n                    \"Sha256\": \"4499c4fe257f2fc83145b429ce211a0a43fd590e70d6261ede616210947d9f8f\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://cdimage.ubuntu.com/ubuntu/releases/jammy/release/ubuntu-22.04.5-wsl-arm64.wsl\",\n                    \"Sha256\": \"3e2d77b92cf9dad1095eaebcad96feba2781007f5f52431cd4fe6fff63b201e5\"\n                }\n            },\n            {\n                \"Name\": \"Ubuntu-20.04\",\n                \"FriendlyName\": \"Ubuntu 20.04 LTS\",\n                \"Default\": false,\n                \"Amd64Url\": {\n                    \"Url\": \"https://releases.ubuntu.com/focal/ubuntu-20.04.6-wsl-amd64.wsl\",\n                    \"Sha256\": \"a9073f3726aab9661076506603706eadf284b80a791dec3adfde8e1989906fa4\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://cdimage.ubuntu.com/ubuntu/releases/focal/release/ubuntu-20.04.6-wsl-arm64.wsl\",\n                    \"Sha256\": \"16174012f9a3f6fb10729e648fd8c7dfa205d039d76902e4c28de746f85bf3c8\"\n                }\n            }\n        ],\n        \"openSUSE\": [\n            {\n                \"Name\": \"openSUSE-Tumbleweed\",\n                \"FriendlyName\": \"openSUSE Tumbleweed\",\n                \"Default\": true,\n                \"Amd64Url\": {\n                    \"Url\": \"https://github.com/openSUSE/WSL-instarball/releases/download/v20260106.0/openSUSE-Tumbleweed-20260103.x86_64-1.224-Build1.224.wsl\",\n                    \"Sha256\": \"0x394be699da2821b331355f3541e237aa3aa00bc4068f33283d68303d8336d484\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://github.com/openSUSE/WSL-instarball/releases/download/v20260106.0/openSUSE-Tumbleweed-20260103.aarch64-2.195-Build2.195.wsl\",\n                    \"Sha256\": \"0xbcbb88e957091c425ecb42f3076b8882b5976fd94885e453afaee40de3b79470\"\n                }\n            },\n            {\n                \"Name\": \"openSUSE-Leap-16.0\",\n                \"FriendlyName\": \"openSUSE Leap 16.0\",\n                \"Default\": false,\n                \"Amd64Url\": {\n                    \"Url\": \"https://github.com/openSUSE/WSL-instarball/releases/download/v20251001.0/openSUSE-Leap-16.0-16.0.x86_64-22.57-Build22.57.wsl\",\n                    \"Sha256\": \"0x0d1faa095153beee0a9b5089b0f9aa3d2aec95e2cdcffdeeff84dd54c48b8393\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://github.com/openSUSE/WSL-instarball/releases/download/v20251001.0/openSUSE-Leap-16.0-16.0.aarch64-22.57-Build22.57.wsl\",\n                    \"Sha256\": \"0x91bcdc7e9f42d7a60a4464ad867d91243aaaecab7b3a057039f77a989daac51e\"\n                }\n            }\n        ],\n        \"SUSE\": [\n            {\n                \"Name\": \"SUSE-Linux-Enterprise-15-SP7\",\n                \"FriendlyName\": \"SUSE Linux Enterprise 15 SP7\",\n                \"Default\": false,\n                \"Amd64Url\": {\n                    \"Url\": \"https://github.com/SUSE/WSL-instarball/releases/download/v20260304.0/SUSE-Linux-Enterprise-15-SP7-15.7.x86_64-30.46-Build30.46.wsl\",\n                    \"Sha256\": \"0x59db8db946bac83729274f4b7dddd91c0fd30badd15f9adc1e5bd56722541b4c\"\n                }\n            },\n            {\n                \"Name\": \"SUSE-Linux-Enterprise-16.0\",\n                \"FriendlyName\": \"SUSE Linux Enterprise 16.0\",\n                \"Default\": true,\n                \"Amd64Url\": {\n                    \"Url\": \"https://github.com/SUSE/WSL-instarball/releases/download/v20260218.0/SUSE-Linux-Enterprise-16.0-16.0.x86_64-1.29-Build1.29.wsl\",\n                    \"Sha256\": \"0xe0bd5e676a30164f43bbe9a7a0e0999ba9cfa396d030abf9c95122a656ed5c49\"\n                }\n            }\n        ],\n        \"kali\": [\n            {\n                \"Name\": \"kali-linux\",\n                \"FriendlyName\": \"Kali Linux Rolling\",\n                \"Default\": true,\n                \"Amd64Url\": {\n                    \"Url\": \"https://kali.download/wsl-images/kali-2025.4/kali-linux-2025.4-wsl-rootfs-amd64.wsl\",\n                    \"Sha256\": \"86aba7bb3d74d313e349f9f50d3f6119ee3b1491072920d063f17ce9b3f706ab\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://kali.download/wsl-images/kali-2025.4/kali-linux-2025.4-wsl-rootfs-arm64.wsl\",\n                    \"Sha256\": \"bd8cdfe340ec596e470b6853ac662382897bc656c0ac3d3096eb399d0780e25e\"\n                }\n            }\n        ],\n        \"Debian\": [\n            {\n                \"Name\": \"Debian\",\n                \"FriendlyName\": \"Debian GNU/Linux\",\n                \"Default\": true,\n                \"Amd64Url\": {\n                    \"Url\": \"https://salsa.debian.org/debian/WSL/-/jobs/7949331/artifacts/raw/Debian_WSL_AMD64_v1.22.0.0.wsl\",\n                    \"Sha256\": \"543123ccc5f838e63dac81634fb0223dc8dcaa78fdb981387d625feb1ed168c7\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://salsa.debian.org/debian/WSL/-/jobs/7949331/artifacts/raw/Debian_WSL_ARM64_v1.22.0.0.wsl\",\n                    \"Sha256\": \"5701f1add55f8cf3b56528109a6220ae5c89f2189d7ae97b9a4b5302b80e967c\"\n                }\n            }\n        ],\n        \"AlmaLinux\": [\n            {\n                \"Name\": \"AlmaLinux-8\",\n                \"FriendlyName\": \"AlmaLinux OS 8\",\n                \"Default\": false,\n                \"Amd64Url\": {\n                    \"Url\": \"https://github.com/AlmaLinux/wsl-images/releases/download/v8.10.20260311.0/AlmaLinux-8.10_x64_20260311.0.wsl\",\n                    \"Sha256\": \"77a662e2947a1482087e3edf22595d62121ad8011385152f9209734adac87941\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://github.com/AlmaLinux/wsl-images/releases/download/v8.10.20260311.0/AlmaLinux-8.10_ARM64_20260311.0.wsl\",\n                    \"Sha256\": \"7fb2e5d25cbba9bfeef1f9e1420fa4e30b671264aa92a015adf8f2901cdfb97f\"\n                }\n            },\n            {\n                \"Name\": \"AlmaLinux-9\",\n                \"FriendlyName\": \"AlmaLinux OS 9\",\n                \"Default\": false,\n                \"Amd64Url\": {\n                    \"Url\": \"https://github.com/AlmaLinux/wsl-images/releases/download/v9.7.20260311.0/AlmaLinux-9.7_x64_20260311.0.wsl\",\n                    \"Sha256\": \"c7d9c8f9bbe8f52a34d920988d6cea913f0251c457aef55f5ed66cce90561f92\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://github.com/AlmaLinux/wsl-images/releases/download/v9.7.20260311.0/AlmaLinux-9.7_ARM64_20260311.0.wsl\",\n                    \"Sha256\": \"e9b5e184aa04e6f72574e2c29cda919d4aaed0c7408dcb81905f6c9391516c7f\"\n                }\n            },\n            {\n                \"Name\": \"AlmaLinux-Kitten-10\",\n                \"FriendlyName\": \"AlmaLinux OS Kitten 10\",\n                \"Default\": false,\n                \"Amd64Url\": {\n                    \"Url\": \"https://github.com/AlmaLinux/wsl-images/releases/download/v10-kitten.20260311.0/AlmaLinux-Kitten-10_x64_20260311.0.wsl\",\n                    \"Sha256\": \"b04ab8ae277ca4bbeb70a15381124609ca7f97b8f7c275930b3fd91195c385ad\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://github.com/AlmaLinux/wsl-images/releases/download/v10-kitten.20260311.0/AlmaLinux-Kitten-10_ARM64_20260311.0.wsl\",\n                    \"Sha256\": \"d502ab27654fa326888940ebce573dbe5abf7e44f709e67b53030da49572a92f\"\n                }\n            },\n            {\n                \"Name\": \"AlmaLinux-10\",\n                \"FriendlyName\": \"AlmaLinux OS 10\",\n                \"Default\": true,\n                \"Amd64Url\": {\n                    \"Url\": \"https://github.com/AlmaLinux/wsl-images/releases/download/v10.1.20260311.0/AlmaLinux-10.1_x64_20260311.0.wsl\",\n                    \"Sha256\": \"6bc470081100ec507d933ee58b467f1903acc07da719ad37532b942439fc16c4\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://github.com/AlmaLinux/wsl-images/releases/download/v10.1.20260311.0/AlmaLinux-10.1_ARM64_20260311.0.wsl\",\n                    \"Sha256\": \"2aedea16c5bdbc83b61d78634cc2f6b839435ad9066df1ea1a0bba7ad2fbdc39\"\n                }\n            }\n        ],\n        \"archlinux\": [\n            {\n                \"Name\": \"archlinux\",\n                \"FriendlyName\": \"Arch Linux\",\n                \"Default\": true,\n                \"Amd64Url\": {\n                    \"Url\": \"https://fastly.mirror.pkgbuild.com/wsl/2026.03.01.160197/archlinux-2026.03.01.160197.wsl\",\n                    \"Sha256\": \"bd0a3d729742e15599ba0351c800225272bffb5061f2ce7cd16fab753055ca77\"\n                }\n            }\n        ],\n        \"Fedora\": [\n            {\n                \"Name\": \"FedoraLinux-43\",\n                \"FriendlyName\": \"Fedora Linux 43\",\n                \"Default\": true,\n                \"Amd64Url\": {\n                    \"Url\": \"https://download.fedoraproject.org/pub/fedora/linux/releases/43/Container/x86_64/images/Fedora-WSL-Base-43-1.6.x86_64.wsl\",\n                    \"Sha256\": \"220780af9cf225e9645313b4c7b0457a26a38a53285eb203b2ab6188d54d5b82\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://download.fedoraproject.org/pub/fedora/linux/releases/43/Container/aarch64/images/Fedora-WSL-Base-43-1.6.aarch64.wsl\",\n                    \"Sha256\": \"7eef7a83260218d8c878b3c7bbdaf11772103145184d0c65df27557f4cd49548\"\n                }\n            },\n            {\n                \"Name\": \"FedoraLinux-42\",\n                \"FriendlyName\": \"Fedora Linux 42\",\n                \"Default\": false,\n                \"Amd64Url\": {\n                    \"Url\": \"https://download.fedoraproject.org/pub/fedora/linux/releases/42/Container/x86_64/images/Fedora-WSL-Base-42-1.1.x86_64.tar.xz\",\n                    \"Sha256\": \"99fb3d05d78ca17c6815bb03cf528da8ef82ebc6260407f2b09461e0da8a1b8d\"\n                },\n                \"Arm64Url\": {\n                    \"Url\": \"https://download.fedoraproject.org/pub/fedora/linux/releases/42/Container/aarch64/images/Fedora-WSL-Base-42-1.1.aarch64.tar.xz\",\n                    \"Sha256\": \"a5a2ceb8ca56b7245b909d021b0fd620427db349f02b8ef3b82b741bcb5611cd\"\n                }\n            }\n        ],\n        \"eLxr\": [\n            {\n                \"Name\": \"eLxr\",\n                \"FriendlyName\": \"eLxr 12.12.0.0 GNU/Linux\",\n                \"Default\": true,\n                \"Amd64Url\": {\n                    \"Url\": \"https://gitlab.com/api/v4/projects/68007430/packages/generic/wsl/12.12.0.0/eLxr_WSL_AMD64_12.12.0.0.wsl\",\n                    \"Sha256\": \"f94e0c44be51550478100ea150b80535f955bf87487d1b964f4636d97478c05d\"\n                }\n            }\n        ]\n    },\n    \"Default\": \"Ubuntu\",\n    \"Distributions\": [\n        {\n            \"Name\": \"Ubuntu\",\n            \"FriendlyName\": \"Ubuntu\",\n            \"StoreAppId\": \"9PDXGNCFSCZV\",\n            \"Amd64\": true,\n            \"Arm64\": true,\n            \"Amd64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/Ubuntu2204-220117.appx\",\n            \"Arm64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/Ubuntu2204-220117_ARM64.appx\",\n            \"PackageFamilyName\": \"CanonicalGroupLimited.Ubuntu_79rhkp1fndgsc\"\n        },\n        {\n            \"Name\": \"Debian\",\n            \"FriendlyName\": \"Debian GNU/Linux\",\n            \"StoreAppId\": \"9MSVKQC78PK6\",\n            \"Amd64\": true,\n            \"Arm64\": true,\n            \"Amd64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/TheDebianProject.DebianGNULinux_1.12.2.0_neutral___76v4gfsz19hv4.AppxBundle\",\n            \"Arm64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/TheDebianProject.DebianGNULinux_1.12.2.0_neutral___76v4gfsz19hv4.AppxBundle\",\n            \"PackageFamilyName\": \"TheDebianProject.DebianGNULinux_76v4gfsz19hv4\"\n        },\n        {\n            \"Name\": \"kali-linux\",\n            \"FriendlyName\": \"Kali Linux Rolling\",\n            \"StoreAppId\": \"9PKR34TNCV07\",\n            \"Amd64\": true,\n            \"Arm64\": true,\n            \"Amd64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/KaliLinux_1.13.1.0.AppxBundle\",\n            \"Arm64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/KaliLinux_1.13.1.0.AppxBundle\",\n            \"PackageFamilyName\": \"KaliLinux.54290C8133FEE_ey8k8hqnwqnmg\"\n        },\n        {\n            \"Name\": \"OracleLinux_7_9\",\n            \"FriendlyName\": \"Oracle Linux 7.9\",\n            \"StoreAppId\": \"9P7L0QWBSLTK\",\n            \"Amd64\": true,\n            \"Arm64\": false,\n            \"Amd64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/OracleLinux_7.9-230428.Appx\",\n            \"Arm64PackageUrl\": null,\n            \"PackageFamilyName\": \"3810OracleAmericaInc.OracleLinux7.9_dm28ctvqnhe9g\"\n        },\n        {\n            \"Name\": \"OracleLinux_8_10\",\n            \"FriendlyName\": \"Oracle Linux 8.10\",\n            \"StoreAppId\": \"9MVFWTCT78ZN\",\n            \"Amd64\": true,\n            \"Arm64\": false,\n            \"Amd64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/OracleLinux_8.10-250708.Appx\",\n            \"Arm64PackageUrl\": null,\n            \"PackageFamilyName\": \"3810OracleAmericaInc.OracleLinux8.10_dm28ctvqnhe9g\"\n        },\n        {\n            \"Name\": \"OracleLinux_9_5\",\n            \"FriendlyName\": \"Oracle Linux 9.5\",\n            \"StoreAppId\": \"9NL3F53JZ3HX\",\n            \"Amd64\": true,\n            \"Arm64\": false,\n            \"Amd64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/OracleLinux_9.5-250708.Appx\",\n            \"Arm64PackageUrl\": null,\n            \"PackageFamilyName\": \"3810OracleAmericaInc.OracleLinux9.5_dm28ctvqnhe9g\"\n        },\n        {\n            \"Name\": \"openSUSE-Leap-15.6\",\n            \"FriendlyName\": \"openSUSE Leap 15.6\",\n            \"StoreAppId\": \"9PDTJHBQRQPF\",\n            \"Amd64\": true,\n            \"Arm64\": true,\n            \"Amd64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/openSUSELeap15p6-250320_x64.Appx\",\n            \"Arm64PackageUrl\": \"https://publicwsldistros.blob.core.windows.net/wsldistrostorage/openSUSELeap15p6-250320_ARM64.Appx\",\n            \"PackageFamilyName\": \"46932SUSE.openSUSELeap15.6_022rs5jcyhyac\"\n        },\n        {\n            \"Name\": \"SUSE-Linux-Enterprise-15-SP6\",\n            \"FriendlyName\": \"SUSE Linux Enterprise 15 SP6\",\n            \"StoreAppId\": \"9N738KZGNB91\",\n            \"Amd64\": true,\n            \"Arm64\": false,\n            \"Amd64PackageUrl\": \"https://github.com/SUSE/WSL-instarball/releases/download/v20250618.0/SUSE-Linux-Enterprise-15-SP6-15.6-WSL.x86_64-156.3.148.0-Build3.148.appx\",\n            \"Arm64PackageUrl\": null,\n            \"PackageFamilyName\": \"46932SUSE.SUSELinuxEnterprise15SP6_022rs5jcyhyac\"\n        },\n        {\n            \"Name\": \"openSUSE-Tumbleweed\",\n            \"FriendlyName\": \"openSUSE Tumbleweed\",\n            \"StoreAppId\": \"9MSSK2ZXXN11\",\n            \"Amd64\": true,\n            \"Arm64\": true,\n            \"Amd64PackageUrl\": \"https://github.com/openSUSE/WSL-instarball/releases/download/v20260106.0/openSUSE-Tumbleweed-20260103-WSL.x86_64-26003.9.1368.0-Build9.1368.appx\",\n            \"Arm64PackageUrl\": \"https://github.com/openSUSE/WSL-instarball/releases/download/v20260106.0/openSUSE-Tumbleweed-20260103-WSL.aarch64-26003.9.741.0-Build9.741.appx\",\n            \"PackageFamilyName\": \"46932SUSE.openSUSETumbleweed_022rs5jcyhyac\"\n        }\n    ]\n}\n"
  },
  {
    "path": "distributions/requirements.txt",
    "content": "python-magic==0.4.27\r\nclick==8.1.3\r\nGitPython==3.1.41\r\nPyGithub==2.5.0\r\njson-cfg==0.4.2"
  },
  {
    "path": "distributions/validate-modern.py",
    "content": "import click\r\nimport jsoncfg\r\nfrom jsoncfg.config_classes import ConfigJSONObject, ConfigJSONArray, ConfigJSONScalar\r\nimport requests\r\nimport tempfile\r\nimport hashlib\r\nimport tarfile\r\nimport configparser\r\nimport magic\r\nimport os.path\r\nimport git\r\nimport re\r\nimport sys\r\nfrom github import Github\r\n\r\n\r\nUSR_LIB_WSL = '/usr/lib/wsl'\r\nUSR_LIBEXEC_WSL = '/usr/libexec/wsl'\r\nUSR_SHARE_WSL = '/usr/share/wsl'\r\n\r\nMAGIC = magic.Magic()\r\nX64_ELF_MAGIC = re.compile('^ELF 64-bit.* x86-64, version 1')\r\nARM64_ELF_MAGIC = re.compile('^ELF 64-bit.* ARM aarch64, version 1')\r\n\r\nKNOWN_TAR_FORMATS = {'^XZ compressed data.*': True, '^gzip compressed data.*': True}\r\n\r\nDISCOURAGED_SYSTEM_UNITS = ['systemd-resolved.service',\r\n                            'systemd-networkd.service',\r\n                            'systemd-networkd-wait-online.service',\r\n                            'systemd-tmpfiles-setup.service',\r\n                            'systemd-tmpfiles-clean.service',\r\n                            'systemd-tmpfiles-setup-dev-early.service',\r\n                            'systemd-tmpfiles-setup-dev.service',\r\n                            'tmp.mount',\r\n                            'NetworkManager.service',\r\n                            'NetworkManager-wait-online.service',\r\n                            'networking.service',\r\n                            'hypervkvpd.service']\r\n\r\nWSL1_UNSUPPORTED_XATTRS = ['security.selinux', 'security.ima', 'security.evm']\r\n\r\nWSL_CONF_KEYS = ['automount.cgroups',\r\n                 'automount.enabled',\r\n                 'automount.ldconfig',\r\n                 'automount.mountfstab',\r\n                 'automount.options',\r\n                 'automount.root',\r\n                 'boot.command',\r\n                 'boot.protectbinfmt',\r\n                 'boot.systemd',\r\n                 'fileserver.enabled',\r\n                 'filesystem.umask',\r\n                 'general.hostname',\r\n                 'gpu.appendlibpath',\r\n                 'gpu.enabled',\r\n                 'interop.appendwindowspath',\r\n                 'interop.enabled',\r\n                 'network.generatehosts',\r\n                 'network.generateresolvconf',\r\n                 'network.hostname',\r\n                 'time.usewindowstimezone',\r\n                 'user.default']\r\n\r\nerrors = {}\r\nwarnings = {}\r\n\r\ndef subset(inner, outer) -> bool:\r\n    for key, value in inner:\r\n        if key not in outer:\r\n            return True\r\n\r\n        if not node_equals(value, outer[key]):\r\n            return False\r\n\r\n    return True\r\n\r\ndef node_equals(left, right):\r\n    if isinstance(left, ConfigJSONScalar):\r\n        return left() == right()\r\n\r\n    elif isinstance(left, ConfigJSONArray):\r\n        return len(left) == len(right) and all(node_equals(l, r) for l, r in zip(left, right))\r\n    else:\r\n        return subset(left, right) and subset(right, left)\r\n\r\n@click.command()\r\n@click.option('--manifest', default=None)\r\n@click.option('--tar', default=None)\r\n@click.option('--compare-with-branch')\r\n@click.option('--repo-path', '..')\r\n@click.option('--arm64', is_flag=True)\r\n@click.option('--debug', is_flag=True)\r\ndef main(manifest: str, tar: str, compare_with_branch: str, repo_path: str, arm64: bool, debug: bool):\r\n    try:\r\n        if tar is not None:\r\n            with open(tar, 'rb') as fd:\r\n                read_tar(None, fd, ARM64_ELF_MAGIC if arm64 else  X64_ELF_MAGIC)\r\n        else:\r\n            if manifest is None:\r\n                raise RuntimeError('Either --tar or --manifest is required')\r\n\r\n            manifest_content = jsoncfg.load_config(manifest)\r\n\r\n            baseline_manifest = None\r\n            if compare_with_branch is not None:\r\n                repo = git.Repo(repo_path)\r\n                baseline_json = repo.commit(compare_with_branch).tree / 'distributions/DistributionInfo.json'\r\n                baseline_manifest = jsoncfg.loads_config(baseline_json.data_stream.read().decode())['ModernDistributions']\r\n\r\n            for flavor, versions in manifest_content[\"ModernDistributions\"]:\r\n                baseline_flavor = baseline_manifest[flavor] if baseline_manifest and flavor in baseline_manifest else None\r\n\r\n                for e in versions:\r\n                    name = e['Name']() if 'Name' in e else None\r\n\r\n                    if name is None:\r\n                        error(flavor, 'Found nameless distribution')\r\n                        continue\r\n\r\n                    if baseline_flavor is not None:\r\n                        baseline_version = next((entry for entry in baseline_flavor if entry['Name']() == name), None)\r\n                        if baseline_version is None:\r\n                            click.secho(f'Found new entry for flavor \"{flavor}\": {name}', fg='green', bold=True)\r\n                        elif not node_equals(baseline_version, e):\r\n                            click.secho(f'Found changed entry for flavor \"{flavor}\": {name}', fg='green', bold=True)\r\n                        else:\r\n                            click.secho(f'Distribution entry \"{flavor}/{name}\" is unchanged, skipping')\r\n                            continue\r\n\r\n                    click.secho(f'Reading information for distribution: {name}', bold=True)\r\n                    if 'FriendlyName' not in e:\r\n                        error(e, 'Manifest entry is missing a \"FriendlyName\" entry')\r\n\r\n                    if not name.startswith(flavor):\r\n                        error(e, f'Name should start with \"{flavor}\"')\r\n\r\n                    url_found = False\r\n\r\n                    if 'Amd64Url' in e:\r\n                       read_url(e['Amd64Url'], X64_ELF_MAGIC)\r\n                       url_found = True\r\n\r\n                    if 'Arm64Url' in e:\r\n                       read_url(e['Arm64Url'], ARM64_ELF_MAGIC)\r\n                       url_found = True\r\n\r\n                    if not url_found:\r\n                        error(flavor, 'No URL found')\r\n\r\n                    expectedKeys = ['Name', 'FriendlyName', 'Default', 'Amd64Url', 'Arm64Url']\r\n                    for key, value in e:\r\n                        if key not in expectedKeys:\r\n                            error(e, f'Unexpected key: \"{key}\"')\r\n\r\n                default_entries = sum(1 for e in versions if 'Default' in e and e['Default']())\r\n                if default_entries != 1:\r\n                    error(e, f'Found no default distribution for \"{flavor}\"' if default_entries == 0 else f'Found multiple default distributions for \"{flavor}\"')\r\n\r\n            report_status_on_pr(manifest)\r\n\r\n            sys.exit(1 if errors else 0)\r\n\r\n    except:\r\n        if debug:\r\n            import traceback\r\n            traceback.print_exc()\r\n            import pdb\r\n            pdb.post_mortem()\r\n        else:\r\n            raise\r\n\r\ndef report_status_on_pr(manifest: str):\r\n    def format_list(entries: list) -> str:\r\n        if len(entries) == 1:\r\n            return entries[0]\r\n\r\n        output = ''\r\n        for e in entries:\r\n            output += f'\\n* {e}'\r\n\r\n        return output\r\n\r\n    for line, text in errors.items():\r\n        escaped = format_list(text).replace('\\n', '%0A')\r\n        print(f'::error file={manifest},line={line}::Error: {escaped}')\r\n\r\n    for line, text in warnings.items():\r\n        escaped = format_list(text).replace('\\n', '%0A')\r\n        print(f'::warning file={manifest},line={line}::Warning: {escaped}')\r\n\r\n\r\ndef read_config_keys(config: configparser.ConfigParser) -> dict:\r\n    keys = {}\r\n\r\n    for section in config.sections():\r\n        for key in config[section].keys():\r\n            keys[f'{section}.{key.lower()}'] = config[section][key]\r\n\r\n    return keys\r\n\r\ndef read_passwd(node, default_uid: int, fd):\r\n    def read_passwd_line(line: str):\r\n        fields = line.split(':')\r\n\r\n        if len(fields) != 7:\r\n            error(node, f'Invalid passwd entry: {line}')\r\n            return None, None\r\n        try:\r\n            uid = int(fields[2])\r\n        except ValueError:\r\n            error(node, f'Invalid passwd entry: {line}')\r\n            return None, None\r\n\r\n        return uid, fields\r\n\r\n    entries = {}\r\n\r\n    for line in fd.readlines():\r\n        uid, fields = read_passwd_line(line.decode())\r\n\r\n        if uid in entries:\r\n            error(node, f'found duplicated uid in /etc/passw: {uid}')\r\n        else:\r\n            entries[uid] = fields\r\n\r\n    if 0 not in entries:\r\n        error(node, f'No root (uid=0) found in /etc/passwd')\r\n    elif entries[0][0] != 'root':\r\n        error(node, f'/etc/passwd has a uid=0, but it is not root: {entries[0][0]}')\r\n\r\n    if default_uid is not None and default_uid in entries:\r\n        warning(node, f'/etc/passwd already has an entry for default uid: {entries[default_uid]}')\r\n\r\n# This logic isn't perfect at listing all boot units, but parsing all of systemd configuration would be too complex.\r\ndef read_systemd_enabled_units(node, tar) -> dict:\r\n    config_dirs = ['/usr/local/lib/systemd/system', '/usr/lib/systemd/system', '/etc/systemd/system']\r\n\r\n    all_files = tar.getnames()\r\n\r\n    def link_target(unit_path: str):\r\n        try:\r\n            info = tar.getmember(unit_path)\r\n        except KeyError:\r\n            info = tar.getmember('.' + unit_path)\r\n\r\n        if not info.issym():\r\n            return unit_path\r\n        else:\r\n            if info.linkpath.startswith('/'):\r\n                return get_tar_file(tar, linux_real_path(info.linkpath), follow_symlink=True)[1]\r\n            else:\r\n                return get_tar_file(tar, linux_real_path(os.path.dirname(unit_path) + '/' + info.linkpath), follow_symlink=True)[1]\r\n\r\n    def list_directory(path: str):\r\n        files = []\r\n        for e in all_files:\r\n            if e.startswith(path):\r\n                files.append(e[len(path) + 1:])\r\n            elif e.startswith('.' + path):\r\n                files.append(e[len(path) + 2:])\r\n\r\n        return files\r\n\r\n    def is_dev_null(path: str) -> bool:\r\n        return path == './dev/null' or path == '/dev/null'\r\n\r\n    def is_masked(unit: str):\r\n        try:\r\n            target = link_target(f'/etc/systemd/system/{unit}')\r\n        except KeyError:\r\n            return False # No symlink found, unit is not masked\r\n\r\n        return is_dev_null(target)\r\n\r\n    units = {}\r\n    for config_dir in config_dirs:\r\n        targets = [e for e in list_directory(config_dir) if e.endswith('.target.wants')]\r\n\r\n        for target in targets:\r\n            for e in list_directory(f'{config_dir}/{target}'):\r\n                fullpath = f'{config_dir}/{target}/{e}'\r\n\r\n                unit_target = link_target(fullpath)\r\n\r\n                if is_dev_null(unit_target) and not is_masked(e):\r\n                    units[e] = fullpath\r\n\r\n    return units\r\n\r\n# Manually implemented because os.path.realpath tries to resolve local symlinks\r\ndef linux_real_path(path: str):\r\n    components = path.split('/')\r\n\r\n    result = []\r\n    for e in components:\r\n        if e == '.' or not e:\r\n            continue\r\n        elif e == '..':\r\n            if result:\r\n                del result[-1]\r\n            continue\r\n\r\n        result.append(e)\r\n\r\n    real_path = '/'.join(result)\r\n    if path and path[0] == '/':\r\n        return '/' + real_path\r\n    else:\r\n        return real_path\r\n\r\ndef get_tar_file(tar, path: str, follow_symlink=False, symlink_depth=10):\r\n    if symlink_depth < 0:\r\n        print(f'Warning: Exceeded maximum symlink depth when reading: {path}')\r\n        return None, None\r\n\r\n    # Tar members can be formatted as /{path}, {path}, or ./{path}\r\n    if path.startswith('/'):\r\n        paths = [path, '.' + path, path[1:]]\r\n    elif path.startswith('./'):\r\n        paths = [path, path[1:], path[2:]]\r\n    else:\r\n        paths = [path, './' + path, '/' + path]\r\n\r\n    def follow_if_symlink(info, path: str):\r\n        if follow_symlink and info.issym():\r\n            if info.linkpath.startswith('/'):\r\n                return get_tar_file(tar, info.linkpath, follow_symlink=True, symlink_depth=symlink_depth - 1)\r\n            else:\r\n                return get_tar_file(tar, linux_real_path(os.path.dirname(path) + '/' + info.linkpath), follow_symlink=True, symlink_depth=symlink_depth -1)\r\n        else:\r\n            return info, path\r\n\r\n    # First try accessing the file directly\r\n    for e in paths:\r\n        try:\r\n            return follow_if_symlink(tar.getmember(e), e)\r\n        except KeyError:\r\n            continue\r\n\r\n    if not follow_symlink:\r\n        return None, None\r\n\r\n    # Then look for symlinks\r\n    # The path might be covered by a symlink, check if parent exists and is a symlink\r\n    parent_path = os.path.dirname(path)\r\n    if parent_path != path:\r\n        try:\r\n            parent_info, real_parent_path = get_tar_file(tar, parent_path, follow_symlink=True, symlink_depth=symlink_depth - 1)\r\n            if real_parent_path is not None and real_parent_path != parent_path:\r\n                return get_tar_file(tar, f'{real_parent_path}/{os.path.basename(path)}', follow_symlink=True, symlink_depth=symlink_depth -1)\r\n        except KeyError:\r\n            pass\r\n\r\n    return None, None\r\n\r\ndef find_unsupported_attrs(tar):\r\n    found_xattrs = set()\r\n    first_file = None\r\n\r\n    for e in tar.getmembers():\r\n        for name in e.pax_headers:\r\n            if any(name.startswith('SCHILY.xattr.' + xattr) for xattr in WSL1_UNSUPPORTED_XATTRS):\r\n                found_xattrs.add(name.replace('SCHILY.xattr.', ''))\r\n\r\n                if first_file is None:\r\n                    first_file  = e.name\r\n\r\n    return first_file, found_xattrs\r\n\r\n\r\ndef read_tar(node, file, elf_magic: str):\r\n    with tarfile.open(fileobj=file) as tar:\r\n\r\n        def validate_mode(path: str, mode, uid, gid, max_size = None, optional = False, follow_symlink = False, magic = None, parse_method = None):\r\n            info, real_path = get_tar_file(tar, path, follow_symlink)\r\n\r\n            if info is None:\r\n                if not optional:\r\n                    error(node, f'File \"{path}\" not found in tar')\r\n                return False\r\n\r\n            permissions = oct(info.mode)\r\n            if permissions not in mode:\r\n                warning(node, f'file: \"{path}\" has unexpected mode: {permissions} (expected: {mode})')\r\n\r\n            if info.uid != uid:\r\n                warning(node, f'file: \"{path}\" has unexpected uid: {info.uid} (expected: {uid})')\r\n\r\n            if gid is not None and info.gid != gid:\r\n                warning(node, f'file: \"{path}\" has unexpected gid: {info.gid} (expected: {gid})')\r\n\r\n            if max_size is not None and info.size > max_size:\r\n                error(node, f'file: \"{path}\" is too big (info.size), max: {max_size}')\r\n\r\n            if magic is not None or parse_method is not None:\r\n                content = tar.extractfile(real_path)\r\n\r\n                if parse_method is not None:\r\n                    parse_method(content)\r\n\r\n                if magic is not None:\r\n                    content.seek(0)\r\n                    buffer = content.read(256)\r\n                    file_magic = MAGIC.from_buffer(buffer)\r\n                    if not magic.match(file_magic):\r\n                        error(node, f'file: \"{path}\" has unexpected magic type: {file_magic} (expected: {magic})')\r\n\r\n            return True\r\n\r\n        def validate_config(path: str, valid_keys: list):\r\n            _, path = get_tar_file(tar, path, follow_symlink=True)\r\n            if path is None:\r\n                error(node, f'File \"{file}\" not found in tar')\r\n                return None\r\n\r\n            content = tar.extractfile(path)\r\n            config = configparser.ConfigParser()\r\n            config.read_string(content.read().decode())\r\n\r\n            keys = read_config_keys(config)\r\n\r\n            unexpected_keys = [e for e in keys if e.casefold() not in valid_keys]\r\n            if unexpected_keys:\r\n                error(node, f'Found unexpected_keys in \"{path}\": {unexpected_keys}')\r\n            else:\r\n                click.secho(f'Found valid keys in \"{path}\": {list(keys.keys())}')\r\n\r\n            return keys\r\n\r\n        defaultUid = None\r\n        if validate_mode('/etc/wsl-distribution.conf', [oct(0o664), oct(0o644)], 0, 0, follow_symlink=True):\r\n            config = validate_config('/etc/wsl-distribution.conf', ['oobe.command', 'oobe.defaultuid', 'shortcut.icon', 'shortcut.enabled', 'oobe.defaultname', 'windowsterminal.profiletemplate', 'windowsterminal.enabled'])\r\n\r\n            if oobe_command := config.get('oobe.command', None):\r\n                validate_mode(oobe_command, [oct(0o775), oct(0o755)], 0, 0)\r\n\r\n                if not oobe_command.startswith(USR_LIB_WSL) and not oobe_command.startswith(USR_LIBEXEC_WSL):\r\n                    warning(node, f'value for oobe.command is not under {USR_LIB_WSL} or {USR_LIBEXEC_WSL}: \"{oobe_command}\"')\r\n\r\n            if defaultUid := config.get('oobe.defaultuid', None):\r\n                if defaultUid != '1000':\r\n                    warning(node, f'Default UID is not 1000. Found: {defaultUid}')\r\n\r\n                defaultUid = int(defaultUid)\r\n\r\n            if shortcut_icon := config.get('shortcut.icon', None):\r\n                validate_mode(shortcut_icon, [oct(0o664), oct(0o644)], 0, 0, 1024 * 1024)\r\n\r\n                if not shortcut_icon.startswith(USR_LIB_WSL) and not shortcut_icon.startswith(USR_SHARE_WSL):\r\n                    warning(node, f'value for shortcut.icon is not under {USR_LIB_WSL} or {USR_SHARE_WSL}: \"{shortcut_icon}\"')\r\n            else:\r\n                warning(node, 'No shortcut.icon provided')\r\n\r\n            if terminal_profile := config.get('windowsterminal.profileTemplate', None):\r\n                validate_mode(terminal_profile, [oct(0o660), oct(0o640)], 0, 0, 1024 * 1024)\r\n\r\n                if not terminal_profile.startswith(USR_LIB_WSL):\r\n                    warning(node, f'value for windowsterminal.profileTemplate is not under {USR_LIB_WSL}: \"{terminal_profile}\"')\r\n\r\n        if validate_mode('/etc/wsl.conf', [oct(0o664), oct(0o644)], 0, 0, optional=True, follow_symlink=True):\r\n            config = validate_config('/etc/wsl.conf', WSL_CONF_KEYS)\r\n            if config.get('boot.systemd', False):\r\n                validate_mode('/sbin/init', [oct(0o775), oct(0o755), oct(0o555)], 0, 0, magic=elf_magic, follow_symlink=True)\r\n\r\n            if (default_user := config.get('user.default')) is not None:\r\n                warning(node, f'Found discouraged wsl.conf key: user.default={default_user}')\r\n\r\n        validate_mode('/etc/passwd', [oct(0o664), oct(0o644)], 0, 0, parse_method = lambda fd: read_passwd(node, defaultUid, fd))\r\n        validate_mode('/etc/shadow', [oct(0o640), oct(0o600), oct(0)], 0, None)\r\n        validate_mode('/bin/bash', [oct(0o755), oct(0o775), oct(0o555)], 0, 0, magic=elf_magic, follow_symlink=True, optional=True)\r\n        validate_mode('/bin/sh', [oct(0o755), oct(0o775), oct(0o555)], 0, 0, magic=elf_magic, follow_symlink=True)\r\n\r\n        enabled_systemd_units = read_systemd_enabled_units(node, tar)\r\n        for unit, path in enabled_systemd_units.items():\r\n            if unit in DISCOURAGED_SYSTEM_UNITS:\r\n                warning(node, f'Found discouraged system unit: {path}')\r\n\r\n        first_file, found_xattrs = find_unsupported_attrs(tar)\r\n        if first_file is not None:\r\n            warning(node, f'Found extended attributes that are not supported in WSL1: {found_xattrs}. Sample file: {first_file}')\r\n\r\ndef read_url(url: dict, elf_magic):\r\n     hash = hashlib.sha256()\r\n     address = url['Url']()\r\n\r\n     if not address.endswith('.wsl'):\r\n         warning(url, f'Url does not point to a .wsl file: {address}')\r\n\r\n     tar_format = None\r\n     if address.startswith('file://'):\r\n         with open(address.replace('file:///', '').replace('file://', ''), 'rb') as fd:\r\n            while True:\r\n                e = fd.read(4096 * 4096 * 10)\r\n                if not e:\r\n                    break\r\n\r\n                hash.update(e)\r\n\r\n                if tar_format is None:\r\n                    tar_format = MAGIC.from_buffer(e)\r\n\r\n            fd.seek(0, 0)\r\n            read_tar(url, fd, elf_magic)\r\n     else:\r\n         with requests.get(address, stream=True) as response:\r\n\r\n            try:\r\n                response.raise_for_status()\r\n            except Exception as e:\r\n                error(url, str(e))\r\n                return\r\n\r\n            with tempfile.NamedTemporaryFile() as file:\r\n                for e in response.iter_content(chunk_size=4096 * 4096):\r\n                    file.write(e)\r\n                    hash.update(e)\r\n\r\n                    if tar_format is None:\r\n                        tar_format = MAGIC.from_buffer(e)\r\n\r\n                file.seek(0, 0)\r\n                read_tar(url, file, elf_magic)\r\n\r\n\r\n     expected_sha = url['Sha256']() if 'Sha256' in url else None\r\n     if expected_sha is None:\r\n         error(url, 'URL is missing \"Sha256\"')\r\n     else:\r\n         if expected_sha.startswith('0x'):\r\n             expected_sha = expected_sha[2:]\r\n\r\n         sha = hash.digest()\r\n         if bytes.fromhex(expected_sha) != sha:\r\n            error(url, f'URL {address} Sha256 does not match. Expected: {expected_sha}, actual: {hash.hexdigest()}')\r\n         else:\r\n             click.secho(f'Hash for {address} matches ({expected_sha})', fg='green')\r\n\r\n     known_format = next((value for key, value in KNOWN_TAR_FORMATS.items() if re.match(key, tar_format)), None)\r\n     if known_format is None:\r\n        error(url, f'Unknown tar format: {tar_format}')\r\n     elif not known_format:\r\n        warning(url, f'Tar format not supported by WSL1: {tar_format}')\r\n\r\ndef error(node, message: str):\r\n    if node is None:\r\n        click.secho(f'Error: {message}', fg='red')\r\n    else:\r\n        global errors\r\n\r\n        line = jsoncfg.node_location(node).line\r\n        click.secho(f'Error on line {line}: {message}', fg='red')\r\n\r\n        errors[line] = errors.get(line, []) + [message]\r\n\r\ndef warning(node, message: str):\r\n    if node is None:\r\n        click.secho(f'Warning: {message}', fg='yellow')\r\n    else:\r\n        global warnings\r\n\r\n        line = jsoncfg.node_location(node).line\r\n        click.secho(f'Warning on line {line}: {message}', fg='yellow')\r\n\r\n        warnings[line] = warnings.get(line, []) + [message]\r\n\r\nif __name__ == \"__main__\":\r\n    main()\r\n"
  },
  {
    "path": "distributions/validate.py",
    "content": "import requests\nimport json\nimport sys\nimport hashlib\nimport difflib\nfrom urllib.request import urlretrieve\nfrom xml.etree import ElementTree\nimport tempfile\nimport zipfile\n\ndef download_and_get_manifest(url: str):\n    print(f'Downloading {url}')\n\n    filename, _ = urlretrieve(url)\n    with zipfile.ZipFile(filename) as archive:\n        try:\n            with archive.open('AppxManifest.xml') as manifest:\n                return ElementTree.fromstring(manifest.read())\n        except KeyError:\n            # In the case of a bundle\n            with archive.open('AppxMetadata/AppxBundleManifest.xml') as manifest:\n                return ElementTree.fromstring(manifest.read())\n\ndef validate_package_url(url: str, family_name: str, platform: str):\n    manifest = download_and_get_manifest(url)\n    identity = manifest.find('.//{http://schemas.microsoft.com/appx/manifest/foundation/windows10}Identity')\n    dependencies = manifest.find('.//{http://schemas.microsoft.com/appx/manifest/foundation/windows10}PackageDependency')\n    if identity is not None:\n        # Check the architecture if the package isn't bundled\n        assert platform == identity.attrib['ProcessorArchitecture']\n    else:\n        # Only check the package name for bundles\n        identity =  manifest.find('.//{http://schemas.microsoft.com/appx/2013/bundle}Identity')\n        dependencies =  manifest.find('.//{http://schemas.microsoft.com/appx/2013/bundle}PackageDependency')\n\n    # Packages uploaded to the CDN shouldn't have dependencies since they can't be installed automatically on Server SKU's.\n    assert dependencies is None\n\n    # Validate the package family_name (the last part is based on a custom hash of the publisher)\n    publisher_hash = hashlib.sha256(identity.attrib['Publisher'].encode('utf-16le')).digest()[:8]\n    encoded_string = ''.join(['{0:b}'.format(e).rjust(8, '0') for e in publisher_hash] + ['0'])\n    encoded_hash = ''\n    charset = \"0123456789abcdefghjkmnpqrstvwxyz\"\n    for i in range(0, len(encoded_string), 5):\n        encoded_hash += charset[int(encoded_string[i:i + 5], 2)]\n\n    assert family_name.startswith(identity.attrib[\"Name\"])\n    assert family_name.endswith('_' + encoded_hash)\n\ndef validate_distro(distro: dict):\n    if distro['Amd64PackageUrl'] is not None:\n        validate_package_url(distro['Amd64PackageUrl'], distro['PackageFamilyName'], 'x64')\n\n    if distro['Arm64PackageUrl'] is not None:\n        validate_package_url(distro['Arm64PackageUrl'], distro['PackageFamilyName'], 'arm64')\n\ndef is_unique(collection: list):\n    unique_list = set(collection)\n    return len(collection) == len(unique_list)\n\n\nif __name__ == \"__main__\":\n    if len(sys.argv) < 2:\n        print(f'Usage: {sys.argv[0]} /path/to/file [distroName]', file=sys.stderr)\n        exit(1)\n\n    with open(sys.argv[1]) as fd:\n        data = fd.read()\n        content = json.loads(data)\n        diff = difflib.unified_diff(\n            data.splitlines(keepends=True),\n            (json.dumps(content, indent=4) + \"\\n\").splitlines(keepends=True),\n            fromfile=\"a\" + sys.argv[1],\n            tofile=\"b\" + sys.argv[1],\n        )\n        diff = \"\".join(diff)\n        assert diff == \"\", diff\n\n    distros = content['Distributions']\n    assert is_unique([e.get('StoreAppId') for e in distros if e])\n    assert is_unique([e.get('Name') for e in distros if e])\n    \n    if len(sys.argv) > 2:\n        # Filter the distros to only the one we want to validate\n        content = { \"Distributions\": [e for e in content['Distributions'] if e['Name'] == sys.argv[2]] }\n        if not content['Distributions']: \n                raise RuntimeError(f'No distro found for name {sys.argv[2]}')\n\n\n    for e in content['Distributions']:\n        validate_distro(e)\n\n    print(\"All checks completed successfully\")\n"
  },
  {
    "path": "doc/README.md",
    "content": "# WSL open source documentation\n\nBuild instructions: \n\n```\n$ pip install mkdocs mkdocs-mermaid2-plugin\n$ mkdocs serve\n```\n\nYou can then view the documentation at `http://127.0.0.1:8000/`."
  },
  {
    "path": "doc/docs/debugging.md",
    "content": "# Debugging WSL\n\n## Logging\n\nThere are multiple sources of logging in WSL. The main one is the ETL trace that is emitted from Windows processes.\n\nTo collect an ETL trace, run ([link to wsl.wprp](https://github.com/microsoft/WSL/blob/master/diagnostics/wsl.wprp)):\n\n```\nwpr -start wsl.wprp -filemode\n\n[reproduce the issue]\n\nwpr -stop logs.ETL\n```\n\nThe consolidated `wsl.wprp` file includes multiple profiles for different scenarios:\n- `WSL` - General WSL tracing (default)\n- `WSL-Storage` - Enhanced storage tracing\n- `WSL-Networking` - Comprehensive networking tracing\n- `WSL-HvSocket` - HvSocket-specific tracing\n\nTo use a specific profile, append `!ProfileName` to the wprp file, e.g., `wpr -start wsl.wprp!WSL-Networking -filemode`\n\nOnce the log file is saved, you can use [WPA](https://apps.microsoft.com/detail/9n58qrw40dfw?hl=en-US&gl=US) to view the logs.\n\nNotable ETL providers: \n\n- `Microsoft.Windows.Lxss.Manager`: Logs emitted from wslservice.exe\n    Important events: \n    - `GuestLog`: Logs from the vm's dmesg\n    - `Error`: Unexpected errors\n    - `CreateVmBegin`, `CreateVmEnd`: Virtual machine lifetime\n    - `CreateNetworkBegin`, `CreateNetworkEnd`: Networking configuration\n    - `SentMessage`, `ReceivedMessage`: Communication on the hvsocket channels with Linux.\n    \n- `Microsoft.Windows.Subsystem.Lxss`: Other WSL executables (wsl.exe, wslg.exe, wslconfig.exe, wslrelay.exe, ...)\n    Important events:\n    - `UserVisibleError`: An error was displayed to the user \n\n- `Microsoft.Windows.Plan9.Server`: Logs from the Windows plan9 server (used when accessing /mnt/ shares and running Windows)\n\n\nOn the Linux side, the easiest way to access logs is to look at `dmesg` or use the debug console, which can be enabled by writing:\n\n```\n[wsl2]\ndebugConsole=true\n```\n\nto `%USERPROFILE%/.wslconfig` and restarting WSL\n\n\n## Attaching debuggers\n\nUsermode can be attached to WSL Windows processes (wsl.exe, wslservice.exe, wslrelay.exe, ...). The symbols are available under the `bin/<platform>/<target>` folder. \nYou can also use [this trick](https://github.com/microsoft/WSL/blob/master/CONTRIBUTING.md#11-reporting-a-wsl-process-crash) to automatically collect crash dumps when processes crash.\n\n## Linux debugging\n\n`gdb` can be attached to Linux processes (see [man gdb](https://man7.org/linux/man-pages/man1/gdb.1.html)). \n\nThe simplest way to debug a WSL process with gdb is to use the `/mnt` mountpoints to access the code from gdb. \nOnce started, just use `dir /path/to/wsl/source` in gdb to connect the source files.\n\n## Root namespace debugging\n\nSome WSL processes such as `gns` or `mini_init` aren't accessible from within WSL distributions. To attach a debugger to those, use the debug shell via:\n\n```\nwsl --debug-shell\n```\n\nYou can then install `gdb` by running `tdnf install gdb` and start debugging processes."
  },
  {
    "path": "doc/docs/dev-loop.md",
    "content": "# Building WSL\n\n## Prerequisites \n\nThe following tools are required to build WSL: \n\n- CMake >= 3.25\n    - Can be installed with `winget install Kitware.CMake`\n- Visual Studio with the following components:\n    - Windows SDK 26100\n    - MSBuild\n    - Universal Windows platform support for v143 build tools (X64 and ARM64)\n    - MSVC v143 - VS 2022 C++ ARM64 build tools (Latest + Spectre) (X64 and ARM64)\n    - C++ core features\n    - C++ ATL for latest v143 tools (X64 and ARM64)\n    - C++ Clang compiler for Windows\n    - .NET desktop development\n    - .NET WinUI app development tools\n\n- Building WSL requires support for symbolic links. To ensure this capability, enable [Developer Mode](https://learn.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development) in Windows Settings or execute the build process with Administrator privileges.\n\n### ARM64 development\n\nWhen building on ARM64 Windows, the [WiX](https://wixtoolset.org/) toolset (`wix.exe`) requires the **x64 .NET 6.0 runtime** because it is an x64 binary. The ARM64 .NET runtime alone is not sufficient.\n\nTo install the x64 .NET 6.0 runtime, run the following commands in PowerShell:\n\n```powershell\n# Download the official dotnet-install script\nInvoke-WebRequest -Uri \"https://dot.net/v1/dotnet-install.ps1\" -OutFile \"$env:TEMP\\dotnet-install.ps1\"\n\n# Install the x64 .NET 6.0 runtime\npowershell -ExecutionPolicy Bypass -File \"$env:TEMP\\dotnet-install.ps1\" -Channel 6.0 -Runtime dotnet -Architecture x64 -InstallDir \"C:\\Program Files\\dotnet\\x64\"\n```\n\nThen set the `DOTNET_ROOT_X64` environment variable so the runtime is discoverable:\n\n```powershell\n# Set for the current session\n$env:DOTNET_ROOT_X64 = \"C:\\Program Files\\dotnet\\x64\"\n\n# Set permanently for your user\n[System.Environment]::SetEnvironmentVariable(\"DOTNET_ROOT_X64\", \"C:\\Program Files\\dotnet\\x64\", \"User\")\n```\n\n> **Note:** You may need to restart VS Code or open a new terminal for the environment variable to take effect.\n\n## Building WSL\n\nOnce you have cloned the repository, generate the Visual Studio solution by running:\n\n```\ncmake .\n```\n\nThis will generate a `wsl.sln` file that you can build either with Visual Studio, or via `cmake --build .`.\n\nBuild parameters:\n\n- `cmake . -A arm64`: Build a package for ARM64\n- `cmake . -DCMAKE_BUILD_TYPE=Release`: Build for release\n- `cmake . -DBUILD_BUNDLE=TRUE`: Build a bundle msix package (requires building ARM64 first)\n\nNote: To build and deploy faster during development, see options in `UserConfig.cmake`.\n\n\n## Deploying WSL \n\nOnce the build is complete, you can install WSL by installing the MSI package found under `bin\\<platform>\\<target>\\wsl.msi`, or by running `powershell tools\\deploy\\deploy-to-host.ps1`.\n\nTo deploy on a Hyper-V virtual machine, you can use `powershell tools\\deploy\\deploy-to-vm.ps1 -VmName <vm> -Username <username> -Password <password>`\n\n## Running tests\n\nTo run unit tests, run: `bin\\<platform>\\<target>\\test.bat`. There's quite a lot of tests so you probably don't want to run everything. Here's a reasonable subset:\n`bin\\<platform>\\<target>\\test.bat /name:*UnitTest*`\n\nTo run a specific test case run:\n`bin\\<platform>\\<target>\\test.bat /name:<class>::<test>`\nExample: `bin\\x64\\debug\\test.bat /name:UnitTests::UnitTests::ModernInstall` \n\nTo run the tests for WSL1, add `-Version 1`. \nExample: `bin\\x64\\debug\\test.bat -Version 1` \n\n\nAfter running the tests once, you can add `-f` to skip the package installation, which makes the tests faster (this requires test_distro to be the default WSL distribution).\n\nExample:\n\n```\nwsl --set-default test_distro\nbin\\x64\\debug\\test.bat /name:*UnitTest* -f\n```\n\n## Debugging tests\n\nSee [debugging](debugging.md) for general debugging instructions.\n\nTo attach a debugger to the unit test process, use: `/waitfordebugger` when calling `test.bat`. \nUse `/breakonfailure` to automatically break on the first test failure. \n\n## Tips and tricks\n\n**Building and deploying faster** \n\nTo iterate faster, create a copy of [```UserConfig.cmake.sample```](https://github.com/microsoft/WSL/blob/master/UserConfig.cmake.sample):\n\n```\ncopy UserConfig.cmake.sample UserConfig.cmake\n```\n\nAnd uncomment this line:\n\n```\n# set(WSL_DEV_BINARY_PATH \"C:/wsldev\")\n```\n\nThis will change the build logic to build a smaller package that installs faster.\nAlso see:\n\n- `WSL_BUILD_THIN_PACKAGE` to build an even smaller package\n- `WSL_POST_BUILD_COMMAND` to automatically deploy the package during build\n\n**Code formatting**\n\nEvery pull request needs to be clang-formatted before it can be merged.\n\nThe code can be manually formatted by running: `powershell .\\FormatSource.ps1 -ModifiedOnly $false`.\nTo automatically check formatting when creating a commit, run: `tools\\SetupClangFormat.bat`\n"
  },
  {
    "path": "doc/docs/index.md",
    "content": "# WSL open source documentation\n\nThis site contains the developer documentation for the Windows Subsystem for Linux. \n\nFor user documentation, including installation and configuration, see [https://learn.microsoft.com/windows/wsl/](https://learn.microsoft.com/windows/wsl/).\n\n\nTo get started developing (building, testing and deploying), see [Getting started](dev-loop.md).\n\nTo learn more about how WSL works, see [technical documentation](technical-documentation/index.md).\n"
  },
  {
    "path": "doc/docs/technical-documentation/boot-process.md",
    "content": "# The WSL2 boot process\n\nThis page describes the steps in the WSL2 process, from the user invoking [wsl.exe](wsl.exe.md) to the user's Linux shell (bash in this example), in the WSL2 distribution.\n\n## Overview \n\nThe below diagram shows the sequence of events to start bash within a WSL2 distribution. See [WSL architecture](index.md) for details about what each process does.\n\n```mermaid\nsequenceDiagram\n    wsl.exe->>wslservice.exe: CreateInstance(<distro>)\n    wslservice.exe->>wsl.exe: S_OK\n    wsl.exe->>wslservice.exe: CreateLxProcess(<distro>, <command line>, <env>, ...)\n    create participant mini_init\n    wslservice.exe->>mini_init: LxMiniInitMessageEarlyConfig\n    create participant gns\n    mini_init-->>gns: fork(), exec(\"/gns\")\n    wslservice.exe->>gns: LxGnsMessageInterfaceConfiguration\n    gns->>wslservice.exe: LxGnsMessageResult\n    wslservice.exe->>mini_init: LxMiniInitMessageInitialConfig\n    wslservice.exe->>mini_init: LxMiniInitMessageLaunchInit\n    create participant init\n    mini_init-->>init: fork(), exec(\"/init\")\n    init->>wslservice.exe: LxMiniInitMessageCreateInstanceResult\n    wslservice.exe->>init: LxInitMessageCreateSession\n    create participant session leader\n    init-->>session leader: fork()\n    session leader->>wslservice.exe: LxInitMessageCreateSessionResponse\n    wslservice.exe->>session leader: InitCreateProcessUtilityVm\n    create participant relay\n    session leader-->>relay: fork()\n    relay->>wslservice.exe: LxMessageResultUint32 (hvsocket connect port)\n    wslservice.exe->>relay: connect hvsockets for STDIN, STDOUT, STDERR\n    create participant bash\n    relay-->>bash: fork(), exec(\"/bin/bash\")\n    relay<<-->>bash: relay STDIN, STDOUT, STDERR\n    wslservice.exe-->>wsl.exe: S_OK + hvsockets for STDIN, STDOUT, STDERR\n    wsl.exe<<->>relay: Relay STDIN, STDOUT, STDERR\n    destroy bash\n    relay-->>bash: waitpid()\n    relay->>wsl.exe: LxInitMessageExitStatus (process exit code)\n```\n\n## CreateInstance()\n\nWhen [wslservice.exe](wslservice.exe.md) receives the CreateInstance() call via COM, it will:\n\n1) Identify which distribution the user wants to create. This is done by looking up the `DistributionRegistration` (see `src/windows/service/exe/DistributionRegistration.cpp`) in the Windows registry, matching either on the distribution ID, or using the default if none is provided.\n\n2) Based on the type of distribution (WSL1 or WSL2), either create a WSL1 instance, or start up a WSL2 virtual machine.\n\n3) Associate the newly creating distribution to the calling process (see `src/windows/service/exe/Lifetime.cpp`)\n\n\n## Starting the WSL2 virtual machine\n\nTo start a WSL2 distribution, [wslservice.exe](wslservice.exe.md) needs a virtual machine. If the virtual machine isn't already running, it will be created as part of the `CreateInstance()` call. \n\nThe WSL2 virtual machine is created via the [Host Compute System (HCS) service](https://learn.microsoft.com/virtualization/api/hcs/overview) (see `src/windows/service/exe/WslCoreVm.cpp`).\n\nTo create a new virtual machine, [wslservice.exe](wslservice.exe.md) generates a JSON string, which describes the virtual machine configuration. This JSON is then passed to [HcsCreateComputeSystem()](https://learn.microsoft.com/virtualization/api/hcs/reference/hcscreatecomputesystem) to create a new virtual machine.\n\nSee `src/windows/common/hcs_schema.h` for more details on the HCS JSON schema.\n\nPart of the JSON configuration includes:\n\n- The kernel: WSL will use its built-in kernel, usually installed in `C:\\Program Files\\WSL\\tools\\kernel`, or a custom kernel if overridden via [.wslconfig](https://learn.microsoft.com/windows/wsl/wsl-config)\n- The initramfs: WSL uses its own initramfs (usually installed in `C:\\Program Files\\WSL\\tools\\initrd.img`). It's an image that only contains the [mini_init](mini_init.md) binary\n- The resources accessible to the virtual machine such as CPU, RAM, GPU, etc\n\nWhen started, the virtual machine will boot into the provided kernel, and then execute [mini_init](mini_init.md).\n\n## The Linux boot process\n\n[mini_init](mini_init.md) is the process that performs usermode initialization inside the virtual machine. After performing various configurations, `mini_init` receives a `LxMiniInitMessageEarlyConfig` message from the [wslservice.exe](wslservice.exe.md) which contains the following information: \n\n- Identifiers for the system VHD, swap VHD and kernel modules VHD if any\n- The machine's hostname\n- The configured memory reclaim mode and page reporting order\n\n[mini_init](mini_init.md) then creates the [gns process](gns.md), which is responsible for networking configuration and then receives a `LxMiniInitMessageInitialConfig` message, which contains: \n\n- An entropy buffer, to seed the virtual machine's entropy\n- Information about the GPU drivers shares to mount, if any\n- Whether [wslg](https://github.com/microsoft/wslg) is enabled\n\nAfter applying all the configuration requested by [wslservice.exe](wslservice.exe.md), the virtual machine is ready to start Linux distributions.\n\n## Starting a Linux distribution\n\nTo start a new distribution, [wslservice.exe](wslservice.exe.md) sends a `LxMiniInitMessageLaunchInit` message to [mini_init](mini_init.md), which then mounts the distribution vhd and starts [init](init.md). See [init](init.md) for more details on WSL2 distributions configuration.\n\nOnce running, [wslservice.exe](wslservice.exe.md) can then send a `LxInitMessageCreateSession` message to start a new [session leader](session-leader.md) inside that distribution, which can be used to launch Linux processes\n\n## Relaying the Linux process's input and output to Windows\n\nOnce the user's Linux process has been created, [wslservice.exe](wslservice.exe.md) can return from `CreateLxProcess()` back to [wsl.exe](wsl.exe.md). In the case of WSL2, [wsl.exe](wsl.exe.md) receives the following HANDLES: \n\n- STDIN\n- STDOUT\n- STDERR\n- Control channel\n- Interop channel\n\nThe `STDIN`, `STDOUT` and `STDERR` handles are used to relay input and output from the Linux process to the Windows terminal. Depending on the type of handle (terminal, pipe, file, ...), [wsl.exe](wsl.exe.md) will apply different relaying logics (see `src/windows/common/relay.cpp`) to achieve the best compatibility between Windows & Linux. \n\nThe `Control channel` is used to notify the Linux process of a change in the terminal (for instance when [wsl.exe's](wsl.exe.md) terminal window is resized) so these changes can be applied to the Linux process as well. \n\nThe `Interop channel` has two usages: \n\n- Create Windows processes from Linux (see [interop](interop.md))\n- Notify [wsl.exe](wsl.exe.md) when the Linux process has exited (see `LxInitMessageExitStatus`)\n\nOnce the Linux process has exited, [wsl.exe](wsl.exe.md) flushes all remaining IO, and exits with the same exit code as the Linux process. \n\nIf [wsl.exe](wsl.exe.md) is terminated before the Linux process exits, [wslhost.exe](wslhost.exe.md) will take over the `Interop channel` and continue to handle requests to execute Windows processes."
  },
  {
    "path": "doc/docs/technical-documentation/drvfs.md",
    "content": "# Accessing Windows drives from Linux\n\nWSL offers mountpoints to access Windows drives from Linux. These mountpoints are mounted under `/mnt` by default, and point to the root of Windows drives.\n\n## Elevated vs non-elevated mountpoints \n\nWithin a distribution, WSL separates between Linux processes that have been created from an elevated (as in administrator level) and from a non-elevated (user level) context. \n\nThis is done by having two separate [mount namespaces](https://man7.org/linux/man-pages/man7/mount_namespaces.7.html) within the distribution. One of them offers an elevated access to Windows drives, and the other offers a non-elevated access to Windows drives.\n\nWhen a Linux process is created, [wslservice.exe](wslservice.exe.md) determines its elevation status, and then tells [init](init.md) to create the process in the appropriate mount namespace.\n\n## Mounting a Windows drive\n\n*Note: This section only applies to WSL2 distributions. *\n\nWhen a [session leader](session-leader.md) is created, [wslservice.exe](wslservice.exe.md) starts a [plan9](https://9fans.github.io/plan9port/man/man9/intro.html) file server. This file server can be connected to from the WSL2 virtual machine to mount Windows drives. \n\nWhen the WSL distribution is created, [wslservice.exe](wslservice.exe.md) uses the `LX_INIT_CONFIGURATION_INFORMATION` message to indicate whether the process that created the distribution is elevated or not. Based on this, [init](init.md) will mount either the elevated, or un-elevated version of the plan9 server.\n\nLater when the first command is created in the namespace that hasn't been mounted yet, (either elevated, or non-elevated), [wslservice.exe](wslservice.exe.md) sends a `LxInitMessageRemountDrvfs` to [init](init.md), which tells `init` to mount the other namespace. \n\nSee: `src/windows/service/exe/WslCoreInstance.cpp` and `src/linux/drvfs.cpp`. \n\n## Mounting a drive from Linux \n\nAs long as the Windows plan9 server is running, drives can be mounted simply by calling [mount](https://linux.die.net/man/8/mount). For instance mounting the C: drive manually can be done via: \n\n```\nmount -t drvfs C: /tmp/my-mount-point\n```\n\nInternally, this is handled by `/usr/sbin/mount.drvfs`, which is a symlink to `/init`. When `/init` starts, it looks at `argv[0]` to determine which entrypoint to run. If `argv[0]` is `mount.drvfs`, then `/init` runs the `mount.drvfs` entrypoint (see `MountDrvfsEntry()` in `src/linux/init/drvfs.cpp`).\n\nDepending on the distribution configuration, `mount.drvfs` will either mount the drive as `drvfs` (WSL1), or `plan9`, `virtio-plan9` or `virtiofs` (WSL), depending on [.wslconfig](https://learn.microsoft.com/windows/wsl/wsl-config)."
  },
  {
    "path": "doc/docs/technical-documentation/gns.md",
    "content": "# GNS\n\n`gns` is a process created by `mini_init`. Its job is to configure networking within the WSL2 virtual machine. \n\n## Networking configuration \n\nNetworking settings are shared by all WSL2 distributions. While WSL2 is running, `gns` maintains an hvsocket channel to [wslservice.exe](wslservice.exe.md), which is used to send various networking related configurations such as:\n\n- Interface IP configuration\n- Routing table entries\n- DNS configuration\n- MTU size configuration\n\nWhen DNS tunneling is enabled, `gns` is also responsible for replying to DNS requests.\n\nSee `src/linux/init/GnsEngine.cpp` and `src/windows/service/exe/GnsChannel.cpp`"
  },
  {
    "path": "doc/docs/technical-documentation/index.md",
    "content": "# WSL Overview\n\nWSL is comprised of a set of executables, APIs and protocols. This page offers an overview of the different components, and how they're connected.\nClick on any component to get more details.\n\n\n```mermaid\n%%{ init: {\n    'flowchart': { 'curve': 'stepBefore' },\n    'theme': 'neutral'\n    }\n}%%\ngraph\n  subgraph Windows[\"<b><p style=\"font-size:30px\">Windows</p></b>\"]\n      C:\\Windows\\System32\\wsl.exe[\"C:\\Windows\\System32\\wsl.exe\"]---|\"CreateProcess()\"|wsl.exe;\n      wsl.exe[<a href=\"wsl.exe\">wsl.exe</a>]---|COM|wslservice.exe;\n      wslg.exe[<a href=\"wslg.exe\">wslg.exe</a>]---|COM|wslservice.exe;\n      wslconfig.exe[<a href=\"wslconfig.exe\">wslconfig.exe</a>]---|COM|wslservice.exe;\n      wslapi.dll[<a href=\"https://learn.microsoft.com/windows/win32/api/wslapi/\">wslapi.dll</a>]---|COM|wslservice.exe;\n      id[debian.exe, ubuntu.exe, ]---|\"LoadLibrary()\"|wslapi.dll;\n      wslservice.exe[<a href=\"wslservice.exe\">wslservice.exe</a>]---|\"CreateProcessAsUser()\"|wslrelay.exe[<a href=\"wslrelay.exe\">wslrelay.exe</a>];\n      wslservice.exe---|\"CreateProcessAsUser()\"|wslhost.exe[<a href=\"wslhost.exe\">wslhost.exe</a>];\n      fs[\"Windows filesystem (//wsl.localhost)\"]\n  end\n  \n  wslservice.exe -----|hvsocket| mini_init\n  wslservice.exe -----|hvsocket| gns\n  fs---|hvsocket|plan9\n\n  wsl.exe---|hvsocket|relay\n  \n  subgraph Linux[\"<b><p style=\"font-size:30px\">Linux</p></b>\"]\n      mini_init[<a href=\"mini_init\">mini_init</a>]---|\"exec()\"|gns[<a href=\"gns\">gns</a>]\n      mini_init---|\"exec()\"|init[<a href=\"init\">init</a>];\n      mini_init---|\"exec()\"|localhost[<a href=\"localhost\">localhost</a>];\n      \n      subgraph \"Linux Distribution\"[\"<b><p style=\"font-size:23px\">Linux Distribution</p></b>\"]\n\n          init[<a href=\"init\">init</a>]---|\"exec()\"|plan9[<a href=\"plan9\">plan9</a>];\n          init---|\"exec()\"|sid[session leader];\n          sid[<a href=\"session-leader\">session leader</a>]---|\"exec()\"|relay\n          relay[<a href=\"relay\">relay</a>]---|\"exec()\"|cid[\"User command (bash, curl)\"]\n      end\n\n  end\n```"
  },
  {
    "path": "doc/docs/technical-documentation/init.md",
    "content": "# Init\n\nInit is top level process of a WSL distribution. For WSL1 distributions, it is launched by [wslservice](wslservice.exe.md) (see `src/windows/service/LxssInstance.cpp`) and for WSL2 distributions, it is launched by [mini_init](mini_init.md).\n\n## WSL2 specific distributions startup\n\nEach WSL2 distributions runs in a separate mount, pid and UTS namespace. This allows distributions to run in parallel, without \"seeing\" each other. \n\nWhen a WSL2 distribution starts, [mini_init](mini_init.md):\n\n- Mounts the distribution VHD\n- Clones into a child namespace\n- Chroots in the VHD mountpoint\n- Executes init (see the `LxMiniInitMessageLaunchInit` message).\n\nWhile each distribution runs in its own mount namespace, the `/mnt/wsl` mountpoint is shared between all distributions.\n\n## Distribution initialization\n\nOnce started, the `init` process performs various initialization tasks such as:\n\n- Mounting `/proc`, `/sys` and `/dev`\n- Configuring cgroups\n- Registering the binfmt interpreter (see [interop](interop.md))\n- Parsing [/etc/wsl.conf](https://learn.microsoft.com/windows/wsl/wsl-config)\n- Starting systemd (see [systemd](systemd.md))\n- Mounting `drvfs` drives (See [drvfs](drvfs.md))\n- Configuring `wslg` (see [wslg](https://github.com/microsoft/wslg))\n\n## Running the distribution\n\nOnce ready, `init` establishes either an `lxbus` (WSL1) or an `hvsocket` (WSL2) connection to [wslservice](wslservice.exe.md). This channel is used to transmit various commands to `init` (see `src/shared/inc/lxinitshared.h`), such as:\n\n- `LxInitMessageInitialize`: Configure the distribution\n- `LxInitMessageCreateSession`: Create a new session leader. See [session leader](session-leader.md)\n- `LxInitMessageTerminateInstance`: Terminate the distribution"
  },
  {
    "path": "doc/docs/technical-documentation/interop.md",
    "content": "# Running Windows executables from Linux\n\nThe ability to launch Windows processes from Linux is controlled by 2 different levels of settings: \n\n- The `HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\LxssManager\\DistributionFlags` registry value, which controls the settings for all Windows users (setting the lowest significance bit disables interop)\n- The `[interop]` section in [/etc/wsl.conf](https://learn.microsoft.com/windows/wsl/wsl-config#wslconf), which controls the setting for a given WSL distribution.\n\n## binfmt interpreters for Windows executables \n\nTo allow Windows process creation from Linux, WSL registers a [binfmt interpreter](https://docs.kernel.org/admin-guide/binfmt-misc.html), which tells the kernel to execute an arbitrary command when a specific type of executable is launched via `exec*()` system calls.\n\nTo perform the registration, WSL writes to `/proc/sys/fs/binfmt_misc` and creates a `WSLInterop` entry, which points to `/init`. For WSL1 registration, the entry is written by [init](init.md) for each distribution, for WSL2 [mini_init](mini_init.md) registers the binfmt interpreter at the virtual machine level. \n\nNote: The `/init` executable is the entrypoint for different WSL processes ([init](init.md), [plan9](plan9.md), [localhost](localhost.md), etc). This executable looks at `argv[0]` to determine which logic to run. In the case of interop, `/init` will run the Windows process creation logic if its `argv[0]` value doesn't match any of the known entrypoints.\n\nSee: `WslEntryPoint()` in `src/linux/init.cpp`.\n\n## Connecting to interop servers\n\nWhen the user tries to execute a Windows process, the kernel will launch `/init` with the Windows process's command line as arguments. \n\nTo start a new Windows process `/init` needs to connect to an interop server. Interop servers are special Linux processes that act as bridges between Linux and Windows. They maintain secure communication channels (through hvsocket connections) with Windows processes ([wsl.exe](wsl.exe.md) or [wslhost.exe](wslhost.exe.md)) to launch Windows executables.\n\nInside Linux, each [session leader](session-leader.md), and each instance of [init](init.md) has an associated interop server, which is serving via an unix socket under `/run/WSL`.\n\n`/init` uses the `$WSL_INTEROP` environment variable to know which server to connect to. If the variable is not set, `/init` will try to connect to `/run/WSL/${pid}_interop`, with its own PID. If that doesn't work, `/init` will try its parent's pid, and then will continue to go up the chain until it reached [init](init.md).\n\nOnce connected `/init` sends a `LxInitMessageCreateProcess` (WSL1) or a `LxInitMessageCreateProcessUtilityVm` (WSL2), which then forwards that message to the associated Windows process, which will launch the requested command and relay its output to `/init`. \n\nSee `src/linux/init/binfmt.cpp`"
  },
  {
    "path": "doc/docs/technical-documentation/localhost.md",
    "content": "# Localhost\n\n`localhost` is a WSL2 Linux process, created by [mini_init](mini_init.md). Its role is to forward network traffic between the WSL2 virtual machine, and Windows.\n\n\n## NAT networking \n\nWhen `wsl2.networkingMode` is set to NAT, `localhost` will watch for bound TCP ports, and relay the network traffic to Windows via [wslrelay.exe](wslrelay.exe.md)\n\n## Mirrored networking\n\nIn mirrored mode, `localhost` registers a BPF program to intercept calls to `bind()`, and forward the calls to Windows via [wslservice.exe](wslservice.exe.md) so Windows can route the network traffic directly to the WSL2 virtual machine.\n\nSee `src/linux/localhost.cpp`."
  },
  {
    "path": "doc/docs/technical-documentation/mini_init.md",
    "content": "# mini_init\n\nmini_init is the first executable that's launched when the WSL2 virtual machine starts. See [WSL2 boot process](boot-process.md) for more details.\n\n## Virtual machine setup\n\nmini_init is started when the kernel is done booting, and calls `/init`, which is `mini_init`. Like other standard linux `init` executables, `mini_init` starts by mounting `/proc`, `/sys`, `/dev` and other standard mountpoints.\n\n`mini_init` then performs various configuration such as enabling crash dump collection, configuring logging via `/dev/console` and tty configuration.\n\nOnce everything is ready, `mini_init` connects two hvsockets to [wslservice](wslservice.exe.md). \n\nOne of them, called the \"mini_init\" channel is used for messages sent by `wslservice.exe`. See `src/shared/inc/lxinitshared.h` for a list of messages and responses. Common messages are:\n\n- `LxMiniInitMessageLaunchInit`: Mount a virtual disk and start a new distribution. See [`init`](init.md) for more details\n- `LxMiniInitMessageMount`: Mount a disk in `/mnt/wsl` (used for wsl --mount)\n- `EJECT_VHD_MESSAGE`: Eject a disk\n- `LxMiniInitMessageImport`: Import a distribution\n- `LxMiniInitMessageExport`: Export a distribution\n\nThe other hvsocket channel is used to send notifications to [wslservice.exe](wslservice.exe.md). This is used mainly to report when linux processes exit (which wslservice uses to know when distributions are terminated).\n\n## Networking configuration\n\nAs part of the boot process, `mini_init` also launches the [gns binary](gns.md) which manages networking configuration\n\n## Other tasks\n\n`mini_init` performs various other maintenance tasks such as:\n\n- Reclaiming unused memory\n- Launching the debug shell tty\n- Synchronizing IO when the virtual machine terminates\n- Resizing filesystem (for wsl --manage <distro> --resize)\n- Formatting disks (used when installing new distributions)\n\n"
  },
  {
    "path": "doc/docs/technical-documentation/plan9.md",
    "content": "# Plan 9\n\nPlan9 is a Linux process that hosts a plan9 filesystem server for WSL1 and WSL2 distributions. It's created by [init](init.md) in each distribution.\n\n## WSL 1 \n\nIn WSL1 distributions, `plan9` serves its filesystem through a unix socket, which can then be connected to from Windows.\n\n## WSL2 \n\nIn WSL2 distributions, `plan9` runs its filesystem through an `hvsocket`\n\n## Accessing the distribution files from Windows\n\nFrom Windows, a special redirector driver (p9rdr.sys) registers both `\\\\wsl$` and `\\\\wsl.localhost`. When either of those paths are accessed, `p9rdr.sys` calls [wslservice.exe](wslservice.exe.md) to list the available distributions for a given Windows user.\n\nWhen a distribution path is accessed (like `\\\\wsl.localhost\\debian`), `p9rdr.sys` calls into [wslservice.exe](wslservice.exe.md) via COM to start the distribution, and connect to its plan9 server, which allows the files to be accessed from Windows. \n\nSee `src/linux/init/plan9.cpp`"
  },
  {
    "path": "doc/docs/technical-documentation/relay.md",
    "content": "# Relay\n\nRelay is a WSL2 Linux process created by a [session leader](session-leader.md). Its job is to create a Linux process on behalf of the user, and relay its output back to Windows. \n\n## Creating a user process\n\nA relay is created when a `LxInitMessageCreateProcessUtilityVm` message is sent to a [session leader](session-leader.md). Once created, the `relay` creates multiple `hvsocket` channels with [wslservice.exe](wslservice.exe.md).\n\nThese channels are used to:\n\n- Relay standard file descriptors (stdin, stdout, stderr)\n- Relay information about the terminal (for instance when the terminal window is resized from Windows)\n- Notify Windows when the Linux process exits\n\nOnce those channels are configured, the `relay` forks() into two processes: \n\n- The parent, which will read & write to the child's standard file descriptors and relay it to Windows\n- The child, which calls `exec()` and starts the user process"
  },
  {
    "path": "doc/docs/technical-documentation/session-leader.md",
    "content": "# Session leader\n\nA session leader is a linux process, which is forked from [init](init.md) after receiving a `LxInitMessageCreateSession` message (see `src/linux/init.cpp`)\n\n## Creating linux processes\n\nSession leaders are used to create linux processes on behalf of the user. Each linux session leader is associated to a Windows console. \n\nTo create a user process, [wslservice.exe](wslservice.exe.md) sends a `LxInitMessageCreateProcess` message (WSL1) or a `LxInitMessageCreateProcessUtilityVm` message (WSL2), which contains details about the process to create such as:\n\n- Command line\n- Current directory\n- Environment variables \n- User name\n\n### Creating a WSL1 process\n\nWhen running in a WSL1 distribution, the session leader forks(), and uses the child process to `exec()` into the user linux process. Before calling `exec()`, child configures various settings such as:\n\n- The user and group id\n- The current directory\n- The standard file descriptors (stdin, stdout, stderr)\n\n### Creating a WSL2 process\n\nWhen running in a WSL2 distribution, the session leader forks() to create a [relay](relay.md) process, which is responsible for creating the user process and relaying its output back to [wsl.exe](wsl.exe.md)"
  },
  {
    "path": "doc/docs/technical-documentation/systemd.md",
    "content": "# Systemd\n\nSystemd support for a WSL distribution can be enabled by setting the following in `/etc/wsl.conf`:\n\n```\n[boot]\nsystemd=true\n```\n\nWhen enabled, [init](init.md) will launch `/sbin/init` (which points to systemd's init) when the distribution starts. One key difference when this setting is enabled is that [init](init.md) won't be pid 1 in the given distribution, since systemd's init requires running as pid 1, so [init](init.md) will fork(), and launch systemd in the parent while continuing WSL configuration in the child process. \n\nAfter launching `/sbin/init`, [init](init.md) waits for systemd to be ready by waiting for `systemctl is-system-running` to return either `running`, or `degraded`. After a given amount of time, WSL will time out and allow the distribution to continue starting, even if systemd isn't ready.\n\n## User sessions\n\nWhen systemd is enabled, WSL tries to synchronize launching processes with systemd user sessions. This is currently done by launching `login -f <user>` to start the associated systemd user session.\n\n## Additional systemd configuration \n\nTo improve compatibility with systemd, WSL creates various systemd configuration files during boot (under `/run`). These configurations files are used to:\n\n- Protect the WSL [binfmt interpreter](interop.md) from being deleted by `systemd-binfmt.service`\n- Protect the X11 socket from being deleted by `systemd-tmpfiles-setup.service`\n"
  },
  {
    "path": "doc/docs/technical-documentation/wsl.exe.md",
    "content": "# wsl.exe\n\nwsl.exe is the main command line entrypoint for WSL. Its job is to:\n\n- Parse the command line arguments (See `src/windows/common/wslclient.cpp`)\n- Call [wslservice.exe](wslservice.exe.md) via COM to launch WSL (see `src/windows/common/svccomm.cpp`)\n- Relay stdin / stdout / stderr from and to the linux process\n\n"
  },
  {
    "path": "doc/docs/technical-documentation/wslconfig.exe.md",
    "content": "# Wslconfig.exe\n\n`wslconfig.exe` is a Windows executable that can be used to configure WSL distributions. "
  },
  {
    "path": "doc/docs/technical-documentation/wslg.exe.md",
    "content": "# Wslg.exe\n\n`wslg.exe` is a Windows executable that is used mostly to run graphical applications with WSL. \n\nIts behavior is exactly the same as [wsl.exe](wsl.exe.md) with the difference that it's a win32 application, and not a console application, which allows it to start without creating a console.\n"
  },
  {
    "path": "doc/docs/technical-documentation/wslhost.exe.md",
    "content": "# Wslhost.exe \n\n`wslhost.exe` is a Windows executable that's used to display desktop notifications, and run Linux processes in the background.\n\n## COM server\n\nWhen running as COM server, `wslhost.exe` registers a [NotificationActivatorFactory](https://learn.microsoft.com/dotnet/api/microsoft.toolkit.uwp.notifications.notificationactivator?view=win-comm-toolkit-dotnet-7.1), which is then used to display desktop notifications to the user.\n\nNotifications can be used to:\n\n- Notify the user about a WSL update\n- Warn the user about a configuration error\n- Notify the user about a proxy change\n\nSee: `src/windows/common/notifications.cpp`\n\n## Background processes \n\nWhen [wsl.exe](wsl.exe.md) terminates before the associated Linux process terminates, `wslhost.exe` takes over the lifetime of the Linux process. \n\nThis allows Linux processes to keep running Windows commands and access the terminal even after the associated `wsl.exe` terminates. \n\nSee `src/windows/wslhost/main.cpp` and [interop](interop.md)"
  },
  {
    "path": "doc/docs/technical-documentation/wslrelay.exe.md",
    "content": "# Wslrelay.exe\n\n`wslrelay.exe` is a windows executable that is used to relay network and debug console traffic from Linux to Windows. \n\nIt is responsible for:\n\n- Relaying localhost traffic between Linux and Windows in NAT mode (see [localhost](localhost.md))\n- Displaying the debug console output when `wsl2.debugConsole` is set to `true`\n\nSee `src/windows/wslrelay/main.cpp`"
  },
  {
    "path": "doc/docs/technical-documentation/wslservice.exe.md",
    "content": "# wslservice.exe\n\nWslService is a session 0 service, running as SYSTEM. Its job is to manage WSL sessions, communicate with the WSL2 virtual machine and configure WSL distributions. \n\n## COM Interface\n\nClients can connect to WslService via its COM interface, ILxssUserSession. Its definition can be found in `src/windows/service/inc/wslservice.idl`.\n\nWhen a COM client calls [CoCreateInstance()](https://learn.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance) on this interface, the service receives the requests via `LxssUserSessionFactory` (see `src/windows/service/LxssUserSessionFactory.cpp`) and returns an instance of `LxssUserSession` (see `src/windows/service/LxssUserSession.cpp`) per Windows user (calling CoCreateInstance() multiple times from the same Windows user accounts returns the same instance).\n\nThe client can then use its `ILxssUserSession` instance to call methods into the service, such as:\n\n- `CreateInstance()`: Launch a WSL distribution\n- `CreateLxProcess()`: Launch a process inside a distribution\n- `RegisterDistribution()`: Register a new WSL distribution\n- `Shutdown()`: Terminate all WSL distributions\n\n## WSL2 Virtual machine\n\nWslService manages the WSL2 Virtual Machine. The virtual machine management logic can be found in `src/windows/service/WslCoreVm.cpp`. \n\nOnce booted, WslService maintains an [hvsocket](https://learn.microsoft.com/virtualization/hyper-v-on-windows/user-guide/make-integration-service) with the Virtual Machine which it uses to send various commands to Linux processes (see [mini_init](mini_init.md) for more details). \n\n## WSL2 Distributions \n\nOnce the virtual machine is running, WSL distributions can be started by calling `WslCoreVm::CreateInstance`. Each running distribution is represented by a `WslCoreInstance` (see `src/windows/service/WslCoreInstance.cpp`).\n\nEach `WslCoreInstance` maintains an hvsocket connection to [init](init.md) which allows WslService to perform various tasks such as:\n\n- Launching processes inside the distribution\n- Be notified when the distribution exits\n- Mount drvfs shares (/mnt/*)\n- Stop the distribution"
  },
  {
    "path": "doc/mkdocs.yml",
    "content": "site_name: WSL\n\nnav:\n    - 'Home': 'index.md'\n    - 'Getting started':\n       - 'Building and testing WSL': 'dev-loop.md'\n       - 'Debugging WSL': 'debugging.md'\n    - 'WSL architecture':\n       - 'Overview': technical-documentation/index.md\n       - 'wsl.exe': technical-documentation/wsl.exe.md\n       - 'wslg.exe': technical-documentation/wslg.exe.md\n       - 'wslconfig.exe': technical-documentation/wslconfig.exe.md\n       - 'wslhost.exe': technical-documentation/wslhost.exe.md\n       - 'wslrelay.exe': technical-documentation/wslrelay.exe.md\n       - 'wslservice.exe': technical-documentation/wslservice.exe.md\n       - 'mini_init': technical-documentation/mini_init.md\n       - 'init': technical-documentation/init.md\n       - 'session leader': technical-documentation/session-leader.md\n       - 'relay': technical-documentation/relay.md\n       - 'gns': technical-documentation/gns.md\n       - 'localhost': technical-documentation/localhost.md\n       - 'plan9': technical-documentation/plan9.md  \n    - 'Technical documentation':\n       - 'Boot process': technical-documentation/boot-process.md\n       - 'Interop': technical-documentation/interop.md\n       - 'Drvfs & Plan9': technical-documentation/drvfs.md\n       - 'Systemd': technical-documentation/systemd.md\n      \n      \nplugins:\n    - search\n    - mermaid2:\n        version: 11.4.1\n        arguments:\n          securityLevel: 'loose'\n\ntheme:\n    name: readthedocs\n"
  },
  {
    "path": "intune/WSL.admx",
    "content": "<policyDefinitions revision=\"1.0\" schemaVersion=\"1.0\">\n  <policyNamespaces>\n    <target prefix=\"WSL\" namespace=\"Microsoft.WSL\" />\n    <using prefix=\"windows\" namespace=\"Microsoft.Policies.Windows\" />\n\n  </policyNamespaces>\n  <resources minRequiredRevision=\"0.1\" fallbackCulture=\"en-US\"/>\n  <categories>\n    <category name=\"WSL\" displayName=\"$(string.WindowsSubsystemForLinux)\"/>\n  </categories>\n  <policies>\n    <policy name=\"AllowWSL\" class=\"Machine\" displayName=\"$(string.AllowWSL)\" explainText=\"$(string.AllowWSLExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowWSL\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"AllowInboxWSL\" class=\"Machine\" displayName=\"$(string.AllowInboxWSL)\" explainText=\"$(string.AllowInboxWSLExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowInboxWSL\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"AllowWSL1\" class=\"Machine\" displayName=\"$(string.AllowWSL1)\" explainText=\"$(string.AllowWSL1Explain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowWSL1\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"CustomKernelUserSettingConfigurable\" class=\"Machine\" displayName=\"$(string.CustomKernelUserSettingConfigurable)\" explainText=\"$(string.CustomKernelExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowKernelUserSetting\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"CustomSystemDistroUserSettingConfigurable\" class=\"Machine\" displayName=\"$(string.CustomSystemDistroUserSettingConfigurable)\" explainText=\"$(string.CustomSystemDistroExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowSystemDistroUserSetting\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"CustomKernelCommandLineUserSettingConfigurable\" class=\"Machine\" displayName=\"$(string.CustomKernelCommandLineUserSettingConfigurable)\" explainText=\"$(string.CustomKernelCommandLineExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowKernelCommandLineUserSetting\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"AllowDebugShell\" class=\"Machine\" displayName=\"$(string.AllowDebugShell)\" explainText=\"$(string.AllowDebugShellExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowDebugShell\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"NestedVirtualizationUserSettingConfigurable\" class=\"Machine\" displayName=\"$(string.NestedVirtualizationUserSettingConfigurable)\" explainText=\"$(string.NestedVirtualizationExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowNestedVirtualization\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"KernelDebugUserSettingConfigurable\" class=\"Machine\" displayName=\"$(string.KernelDebugUserSettingConfigurable)\" explainText=\"$(string.KernelDebugExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowKernelDebugUserSetting\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"CustomNetworkingUserSettingConfigurable\" class=\"Machine\" displayName=\"$(string.CustomNetworkingUserSettingConfigurable)\" explainText=\"$(string.CustomNetworkingExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowNetworkingModeUserSetting\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"FirewallUserSettingConfigurable\" class=\"Machine\" displayName=\"$(string.FirewallUserSettingConfigurable)\" explainText=\"$(string.FirewallExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowFirewallUserSetting\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"AllowDiskMount\" class=\"Machine\" displayName=\"$(string.AllowDiskMount)\" explainText=\"$(string.AllowDiskMountExplain)\" key=\"Software\\Policies\\WSL\" valueName=\"AllowDiskMount\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <enabledValue>\n        <decimal value=\"1\" />\n      </enabledValue>\n      <disabledValue>\n        <decimal value=\"0\" />\n      </disabledValue>\n    </policy>\n\n    <policy name=\"DefaultNetworkingMode\" class=\"Machine\" displayName=\"$(string.DefaultNetworkingMode)\" explainText=\"$(string.DefaultNetworkingModeExplain)\" presentation=\"$(presentation.DefaultNetworkingMode)\" key=\"Software\\Policies\\WSL\" valueName=\"DefaultNetworkingMode\">\n      <parentCategory ref=\"WSL\" />\n      <supportedOn ref=\"windows:SUPPORTED_Windows10\" />\n      <elements>\n        <enum id=\"DefaultNetworkingMode_Dropdown\" valueName=\"DefaultNetworkingMode\">\n          <item displayName=\"$(string.NetworkingModeNone)\">\n            <value>\n              <decimal value=\"0\" />\n            </value>\n          </item>\n          <item displayName=\"$(string.NetworkingModeNAT)\">\n            <value>\n              <decimal value=\"1\" />\n            </value>\n          </item>\n          <!-- Bridged networking is intentionally left out because it requires additional configuration -->\n          <item displayName=\"$(string.NetworkingModeMirrored)\">\n            <value>\n              <decimal value=\"3\" />\n            </value>\n          </item>\n          <item displayName=\"$(string.NetworkingModeVirtioProxy)\">\n            <value>\n              <decimal value=\"4\" />\n            </value>\n          </item>\n        </enum>\n      </elements>\n    </policy>\n\n  </policies>\n</policyDefinitions>\n"
  },
  {
    "path": "intune/en-US/WSL.adml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--  (c) 2006 Microsoft Corporation  -->\n<policyDefinitionResources xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" revision=\"0.1\" schemaVersion=\"0.1\" xmlns=\"http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions\">\n  <displayName>WSL</displayName>\n  <description>Windows Subsystem for Linux</description>\n  <resources>\n    <stringTable>\n        <string id=\"WindowsSubsystemForLinux\">Windows Subsystem for Linux</string>\n\n        <string id=\"AllowWSL\">Allow the Windows Subsystem for Linux</string>\n        <string id=\"AllowWSLExplain\">When set to disabled, this policy disables access to the Windows Subsystem for Linux for all users on the machine.</string>\n\n        <string id=\"AllowInboxWSL\">Allow the Inbox version of the Windows Subsystem for Linux</string>\n        <string id=\"AllowInboxWSLExplain\">When set to disabled, this policy disables the inbox version (optional component) of the Windows Subsystem for Linux. If this policy is disabled, only the store version of WSL can be used.</string>\n\n        <string id=\"AllowWSL1\">Allow WSL1</string>\n        <string id=\"AllowWSL1Explain\">When set to disabled, this policy disables WSL1. When disabled, only WSL2 distributions can be used.</string>\n\n        <string id=\"CustomKernelUserSettingConfigurable\">Allow custom kernel configuration</string>\n        <string id=\"CustomKernelExplain\">When set to disabled, this policy disables custom kernel configuration via .wslconfig (wsl2.kernel). This policy only applies to Store WSL.</string>\n\n        <string id=\"CustomSystemDistroUserSettingConfigurable\">Allow custom system distribution configuration</string>\n        <string id=\"CustomSystemDistroExplain\">When set to disabled, this policy disables custom system distribution configuration via .wslconfig (wsl2.systemDistro). This policy only applies to Store WSL.</string>\n\n        <string id=\"CustomKernelCommandLineUserSettingConfigurable\">Allow kernel command line configuration</string>\n        <string id=\"CustomKernelCommandLineExplain\">When set to disabled, this policy disables kernel command line configuration via .wslconfig (wsl2.kernelCommandLine). This policy only applies to Store WSL.</string>\n\n        <string id=\"AllowDebugShell\">Allow the debug shell</string>\n        <string id=\"AllowDebugShellExplain\">When set to disabled, this policy disables the debug shell (wsl.exe --debug-shell). This policy only applies to Store WSL.</string>\n\n        <string id=\"NestedVirtualizationUserSettingConfigurable\">Allow nested virtualization</string>\n        <string id=\"NestedVirtualizationExplain\">When set to disabled, this policy disables nested virtualization configuration via .wslconfig (wsl2.nestedVirtualization). This policy only applies to Store WSL.</string>\n\n        <string id=\"KernelDebugUserSettingConfigurable\">Allow kernel debugging</string>\n        <string id=\"KernelDebugExplain\">When set to disabled, this policy disables kernel debugging configuration via .wslconfig (wsl2.kernelDebugPort). This policy only applies to Store WSL.</string>\n\n        <string id=\"CustomNetworkingUserSettingConfigurable\">Allow custom networking configuration</string>\n        <string id=\"CustomNetworkingExplain\">When set to disabled, this policy disables custom networking configuration via .wslconfig (wsl2.networkingmode). This policy only applies to Store WSL.</string>\n\n        <string id=\"FirewallUserSettingConfigurable\">Allow user setting firewall configuration</string>\n        <string id=\"FirewallExplain\">When set to disabled, this policy disables firewall configuration via .wslconfig (wsl2.firewall). This policy only applies to Store WSL.</string>\n\n        <string id=\"AllowDiskMount\">Allow passthrough disk mount</string>\n        <string id=\"AllowDiskMountExplain\">When set to disabled, this policy disables passthrough disk mounting in WSL2 (wsl.exe --mount). This policy only applies to Store WSL.</string>\n\n        <string id=\"DefaultNetworkingMode\">Configure default networking mode</string>\n        <string id=\"DefaultNetworkingModeExplain\">This policy specifies the default networking mode to be used for WSL2.</string>\n        <string id=\"NetworkingModeNone\">None</string>\n        <string id=\"NetworkingModeNAT\">NAT</string>\n        <string id=\"NetworkingModeMirrored\">Mirrored</string>\n        <string id=\"NetworkingModeVirtioProxy\">VirtioProxy</string>\n    </stringTable>\n    <presentationTable>\n      <presentation id=\"DefaultNetworkingMode\">\n        <dropdownList refId=\"DefaultNetworkingMode_Dropdown\" noSort=\"true\" defaultItem=\"1\">Default Networking Mode</dropdownList>\n      </presentation>\n    </presentationTable>\n  </resources>\n</policyDefinitionResources>\n"
  },
  {
    "path": "localization/CMakeLists.txt",
    "content": "set(LOCALIZATION_PS1 ${PROJECT_SOURCE_DIR}/tools/generateLocalizationHeader.ps1)\nset (MESSAGESHEAD ${GENERATED_DIR}/Localization.h)\nadd_custom_command(\n    OUTPUT ${MESSAGESHEAD} ${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/localization\n    COMMAND powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive  ${LOCALIZATION_PS1} -OutFile ${MESSAGESHEAD}\n    COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/localization\"\n    DEPENDS ${CMAKE_CURRENT_LIST_DIR}/strings/en-US/Resources.resw ${LOCALIZATION_PS1} # Make sure the head file is rebuilt if any of the resources change\n    VERBATIM\n)\n\nadd_custom_target(localization DEPENDS ${MESSAGESHEAD} ${LOCALIZATION_PS1} SOURCES ${CMAKE_CURRENT_LIST_DIR}/strings/en-US/Resources.resw)"
  },
  {
    "path": "localization/strings/cs-CZ/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Subsystém Windows pro Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Subsystém Windows pro Linux umožňuje vývojářům spouštět prostředí GNU/Linux - včetně většiny nástrojů příkazového řádku, nástrojů a aplikací - přímo ve Windows, bez úprav, bez režie tradičního virtuálního počítače nebo nastavení duálního spouštění.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Odpojení disku se nezdařilo: {}. Pro více podrobností prosím spusťte 'dmesg' uvnitř WSL2.\nChcete-li přinutit WSL2 zastavit a odpojit disk, spusťte 'wsl.exe {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Disk '{}' je již připojený.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Tento svazek je již připojen do WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Disk s tímto názvem je již připojen. Odpojte prosím disk nebo zvolte nový název a zkuste to znovu.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Zadaný název připojení obsahuje neplatný znak /. Zkuste to prosím znovu bez neplatného znaku.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Disk byl úspěšně připojen jako /mnt/wsl/{}.\nPoznámka: Pokud jste změnili nastavení automount.root v /etc/wsl.conf, umístění se bude lišit.\nPokud chcete disk odpojit a odpojit, spusťte příkaz wsl.exe {} {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Disk se připojil, ale nepovedlo se ho připojit: {}.\nDalší podrobnosti získáte spuštěním příkazu dmesg v rámci WSL2.\nPokud chcete disk odpojit, spusťte wsl.exe {} {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Následuje seznam platných distribucí, které lze nainstalovat.\nNainstalovat pomocí souboru wsl.exe {} &lt;Distro&gt;.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Název distribuce již byl nastaven.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>Zadané umístění instalace je již používáno.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Distribuce se zadaným názvem již existuje. Pomocí --name zvolte jiný název.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Neexistuje žádná distribuce se zadaným názvem.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Připojení disku vyžaduje přístup správce.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Export distribuce se nezdařil.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Probíhá jednorázový upgrade systému souborů Subsystém Windows pro Linux pro tuto distribuci...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Nelze spustit, protože je spuštěna jiná instance se zvýšenými oprávněními.  Instance se zvýšenými oprávněními a bez zvýšených oprávnění není povoleno spouštět současně.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Import distribuce se nezdařil.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Instalace volitelné součásti Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Stahování: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Probíhá instalace: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importování distribuce</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>Soubor {} se stáhl.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Subsystém Windows pro Linux obnovuje předchozí instalace...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Neplatný název distribuce: '{}'.\nPokud chcete získat seznam platných distribucí, použijte příkaz 'wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Instance Subsystém Windows pro Linux byla ukončena.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Neplatný argument příkazového řádku: {}\nPokud chcete získat seznam podporovaných argumentů, použijte --help' {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>Argument příkazového řádku {} vyžaduje hodnotu.\nPokud chcete získat seznam podporovaných argumentů, použijte --help' {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Nepodporované nastavení konzoly Pokud chcete používat tuto funkci, musí být starší verze konzoly zakázaná.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} není platné celé číslo.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Pro tuto distribuci probíhá instalace, odinstalace nebo převod.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Spouštění {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Nelze spustit, protože je spuštěna jiná instance se zvýšenými oprávněními.  Instance se zvýšenými oprávněními a bez zvýšených oprávnění není povoleno spouštět současně.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Subsystém Windows pro Linux nemá žádné nainstalované distribuce.\nTento problém můžete vyřešit tak, že nainstalujete distribuci s následujícími pokyny:\n\nSeznam dostupných distribucí zobrazíte pomocí wsl.exe --list --online' .\na wsl.exe --install &lt;Distro&gt;.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Nejsou spuštěny žádné distribuce.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Výchozí)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Distribuce Subsystému Windows pro Linux:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Výchozí distribuce: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Výchozí verze: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Rušení registrace.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Uživatel nenalezen.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Informace o hlavních rozdílech oproti WSL 2 naleznete na adrese https://aka.ms/wsl2.</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Probíhá převod. Může to trvat několik minut.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>Distribuce je již požadovanou verzí.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>Starší verze distribuce nepodporuje WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>WSL2 nelze spustit, protože na tomto počítači není povolena virtualizace.\nUjistěte se, že je povolena volitelná součást „Virtual Machine Platform“ a že je virtualizace zapnutá v nastavení firmwaru vašeho počítače.\n\nPovolte „Virtual Machine Platform“ spuštěním: wsl.exe --install --no-distribution\n\nDalší informace najdete na https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Je připojeno příliš mnoho virtuálních pevných disků nebo fyzických disků.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD se už připojil přes wsl.exe --mount, Před spuštěním prosím disk odpojte.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 se s aktuální konfigurací počítače nepodporuje.\nPokud chcete používat WSL1, povolte prosím volitelnou komponentu Subsystém Windows pro Linux.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Tuto operaci podporuje jenom WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Využití:\n    --networking-mode\n       Zobrazení aktuálního síťového režimu.\n\n    --msal-proxy-path\n        Zobrazení cesty k aplikaci proxy MSAL.\n\n    --vm-id\n        Zobrazí ID virtuálního počítače WSL.\n\n    --version\n        Zobrazí verzi balíčku WSL.\n\n    -n\n        Netisknout nový řádek.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Zvyk:\n    -a\n        Vynutí formát absolutní cesty výsledku.\n    -u\n        Převede cestu z Windows na cestu WSL (výchozí).\n    -w\n        Umožňuje přeložit z cesty WSL na cestu Windows.\n    -m\n        Převést z cesty WSL na cestu Windows, kde se místo \\\\přeloží znak /\n\nPříklad: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Provádí operace správy na Subsystém Windows pro Linux\n\nVyužití:\n    /l, /list [Option]\n        Zobrazí seznam registrovaných distribucí.\n        /all - Vypíše všechny distribuce, včetně distribucí, které se\n               právě instalují nebo odinstalovávají.\n\n        /running - Vypíše pouze distribuce, které jsou aktuálně spuštěné.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Nastaví distribuci jako výchozí.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Ukončí distribuci.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Zruší registraci distribuce a odstraní kořenový systém souborů.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Všechna práva vyhrazena.\nInformace o ochraně osobních údajů k tomuto produktu najdete na stránce https://aka.ms/privacy.\n\nPoužití: wsl.exe [Argument] [Options...] [CommandLine]\n\nArgumenty pro spouštění binárních souborů Linuxu:\n\n    Pokud není zadaný žádný příkazový řádek, spustí wsl.exe výchozí prostředí.\n\n    --exec, -e &lt;CommandLine&gt;\n        Spustí zadaný příkaz bez použití výchozího linuxového prostředí.\n\n    --shell-type &lt;standard|login|none&gt;\n        Spustí zadaný příkaz se zadaným typem prostředí.\n\n    --\n        Předá zbývající příkazový řádek tak, jak je.\n\nMožnosti:\n    --cd &lt;Directory&gt;\n        Nastaví zadaný adresář jako aktuální pracovní adresář.\n        Pokud se použije ~, použije se domovská cesta uživatele Linuxu. Pokud cesta začíná\n        znakem /, bude interpretována jako absolutní cesta Linuxu.\n        V opačném případě musí být hodnota absolutní cestou systému Windows.\n\n    --distribution, -d &lt;DistroName&gt;\n        Spustí zadanou distribuci.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Spustí ID zadané distribuce.\n\n    --user, -u &lt;UserName&gt;\n        Spustí jako zadaný uživatel.\n\n    --system\n        Spustí prostředí pro distribuci systému.\n\nArgumenty pro správu Subsystému Windows pro Linux:\n\n    --help\n        Zobrazí informace o použití.\n\n    --debug-shell\n        Otevře prostředí ladění WSL2 pro diagnostické účely.\n\n    --install [Distro] [Options...]\n        Nainstaluje distribuci Subsystému Windows pro Linux.\n        Seznam platných distribucí zobrazíte pomocí příkazu 'wsl.exe --list --online'.\n\n        Možnosti:\n            --enable-wsl1\n                Povolí podporu WSL1.\n\n            --fixed-vhd\n                Vytvořte disk s pevnou velikostí pro uložení distribuce.\n\n            --from-file &lt;Path&gt;\n                Nainstaluje distribuci z místního souboru.\n\n            --legacy\n                Použije starší verzi manifestu distribuce.\n\n            --location &lt;Location&gt;\n                Nastaví instalační cestu pro distribuci.\n\n            --name &lt;Name&gt;\n                Nastaví název distribuce.\n\n            --no-distribution\n                Nainstaluje pouze požadované volitelné součásti, nenainstaluje distribuci.\n\n            --no-launch, -n\n                Po instalaci nespustí distribuci.\n\n            --version &lt;Version&gt;\n                Určuje verzi, která se má použít pro novou distribuci.\n\n            --vhd-size &lt;MemoryString&gt;\n                Určuje velikost disku pro uložení distribuce.\n\n            --web-download\n                Stáhne distribuci z internetu namísto z Microsoft Storu.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Změní specifické možnosti distribuce.\n\n        Možnosti:\n            --move &lt;Location&gt;\n                Přesuňte distribuci do nového umístění.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Nastavte virtuální pevný disk distribuce na zhuštěný, což umožní automatické uvolnění místa na disku.\n\n            --set-default-user &lt;Username&gt;\n                Nastaví výchozího uživatele distribuce.\n\n            --resize &lt;MemoryString&gt;\n                Změní velikost disku distribuce na zadanou velikost.\n\n    --mount &lt;Disk&gt;\n        Propojí a připojí fyzický nebo virtuální disk ve všech distribucích WSL 2.\n\n        Možnosti:\n            --vhd\n                Určuje, že &lt;Disk&gt; odkazuje na virtuální pevný disk.\n\n            --bare\n                Propojí disk s WSL2, ale nepřipojí ho.\n\n            --name &lt;Name&gt;\n                Připojí disk pomocí vlastního názvu přípojného bodu.\n\n            --type &lt;Type&gt;\n                Systém souborů, který se má použít při připojování disku. Pokud není zadaný, výchozí hodnota je ext4.\n\n            --options &lt;Options&gt;\n                Další možnosti připojení.\n\n            --partition &lt;Index&gt;\n                Index oddílu, který se má připojit. Pokud není zadaný, výchozí hodnota je celý disk.\n\n    --set-default-version &lt;Version&gt;\n        Změní výchozí verzi instalace pro nové distribuce.\n\n    --shutdown\n        Okamžitě ukončí všechny spuštěné distribuce a\n        odlehčený užitkový virtuální počítač WSL 2.\n\n        Možnosti:\n            --force\n                Ukončí virtuální stroj WSL 2, i když probíhá operace. Může způsobit ztrátu dat.\n\n    --status\n        Zobrazí stav Subsystému Windows pro Linux.\n\n    --unmount [Disk]\n        Odpojí disk a zruší jeho propojení u všech distribucí WSL2.\n        Pokud proběhlo volání bez argumentu, odpojí se všechny disky a zruší se jejich propojení.\n\n    --uninstall\n        Odinstaluje balíček Subsystému Windows pro Linux z tohoto počítače.\n\n    --update\n        Aktualizuje balíček Subsystému Windows pro Linux.\n\n        Možnosti:\n            --pre-release\n                Stáhne předběžnou verzi, pokud je k dispozici.\n\n    --version, -v\n        Zobrazí informace o verzi.\n\nArgumenty pro správu distribucí v Subsystému Windows pro Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Exportuje distribuci do souboru tar.\n        Název souboru může být - pro stdout.\n\n        Možnosti:\n            --format &lt;Format&gt;\n                Určuje formát exportu. Podporované hodnoty: tar, tar.gz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        Importuje zadaný soubor tar jako novou distribuci.\n        Název souboru může být - pro stdin.\n\n        Možnosti:\n            --version &lt;Version&gt;\n                Určuje verzi, která se má použít pro novou distribuci.\n\n            --vhd\n                Určuje, že poskytnutý soubor je soubor .vhd nebo .vhdx, nikoli soubor tar.\n                Tato operace vytvoří kopii souboru VHD v zadaném umístění instalace.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Importuje zadaný soubor .VHD jako novou distribuci.\n        Tento virtuální pevný disk musí být naformátovaný typem systému souborů ext4.\n\n    --list, -l [Options]\n        Vypíše distribuce.\n\n        Možnosti:\n            --all\n                Vypíše všechny distribuce, včetně distribucí, které se\n                právě instalují nebo odinstalovávají.\n\n            --running\n                Vypíše pouze distribuce, které jsou aktuálně spuštěné.\n\n            --quiet, -q\n                Zobrazí pouze názvy distribucí.\n\n            --verbose, -v\n                Zobrazí podrobné informace o všech distribucích.\n\n            --online, -o\n                Zobrazí seznam dostupných distribucí pro instalaci pomocí 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Nastaví distribuci jako výchozí.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Změní verzi zadané distribuce.\n\n    --terminate, -t &lt;Distro&gt;\n        Ukončí zadanou distribuci.\n\n    --unregister &lt;Distro&gt;\n        Zruší registraci distribuce a odstraní kořenový systém souborů.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>Verze WSL: {}\nVerze jádra: {}\nVerze WSLg: {}\nVerze MSRDC: {}\nVerze Direct3D: {}\nVerze DXCore: {}\nVerze systému Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>Verze MSBuildu: {}\nPotvrzení změn: {}\nČas sestavení: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>Vlastní jádro zadané v {} nebylo nalezeno: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>VHD vlastních modulů jádra v {} se nenašel: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>Vlastní distribuce systému zadaná v {} nebyla nalezena nebo nemá správný formát.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Všechna práva vyhrazena.\nInformace o ochraně osobních údajů k tomuto produktu najdete na stránce https://aka.ms/privacy.\n\nPoužití: wslg.exe [Argument] [Options...] [CommandLine]\n\nArgumenty:\n    --cd &lt;Directory&gt;\n        Nastaví zadaný adresář jako aktuální pracovní adresář.\n        Pokud se použije ~, použije se domovská cesta uživatele Linuxu. Pokud cesta začíná\n        znakem /, bude interpretována jako absolutní cesta Linuxu.\n        V opačném případě musí být hodnota absolutní cestou systému Windows.\n\n    --distribution, -d &lt;Distro&gt;\n        Spustí zadanou distribuci.\n\n    --user, -u &lt;UserName&gt;\n        Spuštění jako zadaný uživatel.\n\n    --shell-type &lt;standard|login|none&gt;\n        Spustí zadaný příkaz se zadaným typem prostředí.\n\n    --help\n        Zobrazí informace o použití.\n\n    --\n        Předá zbývající příkazový řádek tak, jak je.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Pokračujte stisknutím libovolné klávesy...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>V argumentu {} chybí povinný parametr.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Probíhá export. Může to trvat několik minut.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Probíhá import. Může to trvat několik minut.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>Podpora aplikací grafického rozhraní (GUI) je zakázána prostřednictvím {} nebo /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Zjišťuje se dostupnost aktualizací.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Tato distribuce je k dispozici pouze z Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount na arm64 vyžaduje Windows verze 27653 nebo novější.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Už máte nainstalovanou nejnovější verzi Subsystému Windows pro Linux.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Aktualizuje se Subsystém Windows pro Linux na verzi: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Tato aplikace vyžaduje volitelnou komponentu Subsystém Windows pro Linux.\nNainstalujte ji spuštěním příkazu: wsl.exe --install --no-distribution\nMůže být nutné restartovat systém, aby se změny projevily.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Zadaný soubor musí mít příponu {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Zadaný soubor musí mít příponu {} nebo {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>VmSwitch '{}' nebyl nalezen. Dostupné přepínače: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Přemostění sítě vyžaduje nastavení přepínače wsl2.vmSwitch.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Nepovedlo se načíst distribuční seznam z {}. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Nepovedlo se připojit disk '{}' k WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Změna velikosti disku se nezdařila.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Nebyla nalezena žádná hodnota.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Verze Windows {} nepodporuje zabalenou verzi Subsystém Windows pro Linux.\nNainstalujte požadovanou aktualizaci prostřednictvím služby Windows Update nebo prostřednictvím: {}\nInformace najdete na https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Spuštění ladicího prostředí vyžaduje spuštění wsl.exe jako správce.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Proces instalace pro distribuci '{}' selhal s ukončovacím kódem: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Neplatný formát globálně jedinečného identifikátoru: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Neplatný název oddílu v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Neplatný název klíče v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Bylo očekáváno {} v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Neplatný řídicí znak: '{}' v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Neznámá klávesa '{}' v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Neplatná celočíselná hodnota '{}' pro klíč '{}' v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Neplatná hodnota IP adresy '{}' pro klíč '{}' v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Neplatná logická hodnota '{}' pro klíč '{}' v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Neplatná adresa mac '{}' pro klíč '{}' v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Nelze otevřít konfigurační soubor {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Při spouštění WSL došlo k chybám</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Nepodařilo se spustit systémovou relaci uživatele pro {}. Další podrobnosti najdete v seznamu deníků.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Otevřít EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Tato distribuce neobsahuje výchozí název. Pokud chcete zvolit název distribuce, použijte --name .</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Argumenty {} a {} nelze zadat současně.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>Argument {} vyžaduje argument {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Neplatný název distribuce: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Používá se starší verze registrace distribuce. Zvažte místo toho použití distribuce založené na formátu tar.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Starší registrace distribuce nepodporují argument --version .</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>Distribuce \"{}\" je poskytována manifestem přepsání.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>Hodnota hash distribuce se neshoduje. Očekáváno: {}, skutečná hodnota hash: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Neplatný šestnáctkový řetězec: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>'{}' se při instalaci starších distribucí nepodporuje.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Distribuce byla úspěšně nainstalována. Dá se spustit přes wsl.exe -d {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Neplatný řetězec paměti '{}' pro položku .wslconfig '{}' v {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Nepovedlo se vytvořit prohození disku v '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Vnořená virtualizace není v tomto počítači podporována.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Nepovedlo se vytvořit koncový bod sítě s adresou '{}', byla přiřazena nová adresa: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Nepovedlo se vytvořit virtuální síť s rozsahem adres '{}', vytvořila se nová síť s rozsahem: '{}', {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>NOUZOVÝ REŽIM POVOLEN – mnoho funkcí bude zakázáno.</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Zrcadlený síťový režim není podporován: {}.\nNávrat k sítím NAT.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Vyžaduje se linuxové jádro verze 5.10 nebo novější.</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Verze Systému Windows {}. {} nemá požadované funkce.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Brána firewall technologie Hyper-V není podporována.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>Tunelování DNS není podporováno.</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>Nastavení wsl2.localhostForwarding nemá žádný vliv při použití zrcadleného síťového režimu</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Na hostiteli byla zjištěna změna proxy serveru HTTP. Restartujte WSL, aby se změna použila.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Byla zjištěna konfigurace proxy serveru localhost, ale nebyla zrcadlena do WSL. WSL v režimu NAT nepodporuje proxy localhost.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Byla zjištěna konfigurace proxy serveru IPv6, ale nebyla zrcadlena do WSL. WSL v režimu NAT nepodporuje protokol IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Byla zjištěna konfigurace proxy serveru IPv6 místního hostitele, ale nebyla zrcadlena do WSL. WSL nepodporuje proxy localhost IPv6.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Při pokusu o překlad nastavení proxy serveru došlo k neočekávané chybě. Nastavení proxy serveru nebylo zrcadleno do WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Při přístupu k registru došlo k chybě. Cesta: '{}'. Chyba: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Nepovedlo se spustit proces přenosu localhost. Chyba: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Spuštění virtuální sítě se nezdařilo . Nainstalujte volitelnou součást Platformy virtuálního počítače spuštěním příkazu: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Nepovedlo se nakonfigurovat síť (networkingMode {}), návrat k režimu networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Nepovedlo se povolit součást systému Windows {} (ukončovací kód {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Modul plug-in '{}' vrátil závažnou chybu</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Modul plug-in '{}' vrátil závažnou chybu. Chybová zpráva: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Modul plug-in {} vyžaduje novější verzi WSL. Spusťte prosím:  wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>Nastavení .wslconfig '{}' je zakázáno zásadami počítače.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>Ladicí prostředí je zakázáno zásadami počítače.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount je zakázáno zásadami počítače.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>Protokol WSL1 je zakázán zásadami počítače.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Spuštěním příkazu 'wsl.exe --set-version {} 2' upgradujte na WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL dokončuje upgrade…</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Aktualizace se nezdařila (ukončovací kód: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Odinstalování se nezdařilo (ukončovací kód: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Soubor protokolu: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Nepodařilo se odebrat balíček MSIX (chyba: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Nepodařilo se nainstalovat balíček MSIX (chyba: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Volitelné součásti potřebné ke spuštění WSL nejsou nainstalovány.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Nainstalujte chybějící komponenty.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Importovaný soubor není platná distribuce Linuxu.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Stisknutím libovolné klávesy ukončíte...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Je k dispozici nová verze Subsystému Windows pro Linux.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Aktualizujte na verzi {}, nebo si níže zobrazte poznámky k verzi.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Aktualizovat</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Zobrazit dokumentaci</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Operaci nešlo dokončit, protože se VHD právě používá. Vynucení ukončení používání WSL: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} není platná logická hodnota, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Zhuštěný VHD je podporován pouze pro WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>Spouštění WSL jako místního systému se nepodporuje.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Tip pro lepší výkon:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Použití operace náročné na vstupně-výstupní operace, jako je {} na jednotkách systému Windows, bude mít nízký výkon. Zvažte přesunutí souborů projektu do souborového systému Linux, abyste dosáhli vyššího výkonu. Pro další informace klikněte níže.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Zobrazit dokumenty</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Příště nezobrazovat</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>Došlo k chybě virtuálního počítače WSL2.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>Trasování zásobníku se uložilo do: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Spuštění distribuce se nezdařilo. Kód chyby: {}, krok selhání: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Spuštění distribuce se nezdařilo, protože její virtuální disk je poškozený.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Duplicitní konfigurační klíč {} v {}:{} (konfliktní klíč: {} v {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Neplatná hodnota {} pro konfigurační klíč {} v {}:{} (platné hodnoty: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Čeká se na dokončení příkazu OOBE pro distribuci „{}“…</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Nepodařilo se přečíst vlastnost {} z distribuce {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Vypadá to jako soubor VHD. K importu virtuálního pevného disku místo tar použijte --vhd </value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Nepovedlo se nainstalovat {} z Microsoft Store: {}\nProbíhá pokus o stažení webu...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Nebyla nakonfigurována žádná výchozí distribuce. Zadejte prosím distribuci, která se má nainstalovat.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>Wsl2.virtio9p je zakázáno, probíhá návrat na 9p s přenosem vsock.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>Instalace WSL je pravděpodobně poškozena (kód chyby: {}).\nStisknutím libovolné klávesy opravíte WSL nebo CTRL-C akci zrušíte.\nČasový limit této výzvy vyprší za 60 sekund.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Při registraci distribuce se nepovedlo parsovat profil terminálu: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Neplatná velikost: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nKód chyby: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Neplatný dokument JSON. Chyba analýzy: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>Hodnota wsl2.processors nemůže překročit počet logických procesorů v systému ({} &gt; {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Dotaz na síťový režim se nezdařil</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Byly zadány přizpůsobené moduly jádra bez zadání přizpůsobeného jádra. Další informace najdete https://aka.ms/wslcustomkernel.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Kvůli aktuálnímu problému s kompatibilitou s klientem globálního bezpečného přístupu je tunelování DNS zakázané.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Podpora prořídlého VHD je v současné době zakázána z důvodu možného poškození dat.\nPokud chcete distribuci vynutit použití prořídlého VHD, spusťte:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Nepovedlo se připojit {}. Další podrobnosti najdete v dmesg.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Zpracování /etc/fstab s připojením -a se nezdařilo.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Při připojování distribučního disku došlo k chybě. Jako náhradní disk byl připojen jen pro čtení.\nZobrazit pokyny pro obnovení: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Překlad '{}' se nezdařil</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Něco se nepovedlo. Zkuste to znovu později.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>O modulu snap-in</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>O nastavení Subsystému Windows pro Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Všechna práva vyhrazena.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Nastavení Subsystému Windows pro Linux</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Nastavení Subsystému Windows pro Linux umožňuje vývojářům spravovat soubor .wslconfig pomocí aplikace založené na grafickém uživatelském rozhraní.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Automatické uvolnění paměti</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Automaticky uvolní paměť uloženou v mezipaměti po zjištění nečinné spotřeby procesoru. Nastavte na postupné pomalé uvolňování a dropcache pro okamžité uvolnění paměti uložené v mezipaměti.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatické uvolnění paměti.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Automaticky uvolní paměť uloženou v mezipaměti po zjištění nečinné spotřeby procesoru. Nastavte na postupné pomalé uvolňování a dropcache pro okamžité uvolnění paměti uložené v mezipaměti.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Automatický proxy server povolen</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Povoluje WSL používat informace o proxy serveru HTTP systému Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatický proxy server povolen.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Povoluje WSL používat informace o proxy serveru HTTP systému Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Využití analýzy DNS s maximálním úsilím</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je vlastnost wsl2.dnsTunneling nastavena na hodnotu true. Pokud je tato hodnota nastavená na true, systém Windows extrahuje otázku z požadavku DNS a pokusí se ji vyřešit a ignoruje neznámé záznamy.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Využijte parsování DNS s maximálním úsilím.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je vlastnost wsl2.dnsTunneling nastavena na hodnotu true. Pokud je tato hodnota nastavená na true, systém Windows extrahuje otázku z požadavku DNS a pokusí se ji vyřešit a ignoruje neznámé záznamy.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Vlastní jádro</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Absolutní cesta Windows k vlastnímu jádru Linuxu.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procházet jádra</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Vlastní jádro</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Absolutní cesta Windows k vlastnímu jádru Linuxu.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Vlastní moduly jádra</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Absolutní cesta Windows k vlastním modulům jádra VHD Linuxu.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procházet moduly jádra</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Vlastní moduly jádra</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Absolutní cesta Windows k vlastním modulům jádra VHD Linuxu.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Vlastní distribuce systému</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procházet distribuce</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Určete cestu k virtuálnímu pevnému disku, který se načte jako vlastní distribuce systému a primárně se použije používá k napájení aplikací s grafickým uživatelským rozhraním ve WSL. [Další informace o distribucích systému najdete tady].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Vlastní distribuce systému</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Určete cestu k virtuálnímu pevnému disku, který se načte jako vlastní distribuce systému a primárně se použije používá k napájení aplikací s grafickým uživatelským rozhraním ve WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Povolit konzolu ladění</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Logická hodnota pro zapnutí okna výstupní konzoly, které zobrazuje obsah dmesg při spuštění instance distribuce WSL 2.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Povolte konzoli ladění.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Logická hodnota pro zapnutí okna výstupní konzoly, které zobrazuje obsah diagnostických zpráv při spuštění instance distribuce WSL 2</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Výchozí velikost virtuálního pevného disku</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Výchozí maximální velikost rozšiřitelného virtuálního pevného disku (VHD) WSL pouze pro nově vytvořené distribuce</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetovat velikost</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Výchozí velikost virtuálního pevného disku</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Výchozí maximální velikost zadaná v megabajtech pro rozšiřitelný virtuální pevný disk WSL (VHD) pouze pro nově vytvořené distribuce.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Vývojář</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentace</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Změnit režim DrvFS</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Změní implementaci přístupu k souborům mezi operačními systémy v WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy server DNS je povolený</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je vlastnost wsl2.networkingMode nastavena na hodnotu NAT. Logická hodnota pro informování WSL o konfiguraci serveru DNS v Linuxu na překlad adres (NAT) na hostiteli. Nastavení na false bude zrcadlit servery DNS z Windows do Linuxu.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy server DNS je povolený.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je vlastnost wsl2.networkingMode nastavena na hodnotu NAT. Logická hodnota pro informování WSL o konfiguraci serveru DNS v Linuxu na překlad adres (NAT) na hostiteli. Nastavení na false bude zrcadlit servery DNS z Windows do Linuxu.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>Tunelování DNS povoleno</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Změní způsob, jakým jsou požadavky DNS proxy z WSL na Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tunelování DNS povoleno.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Změní způsob, jakým jsou požadavky DNS proxy z WSL na Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Systém souborů</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Povolit aplikace s grafickým uživatelským rozhraním</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Logická hodnota pro zapnutí nebo vypnutí podpory aplikací s grafickým uživatelským rozhraním ([WSLg]) v WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Povolte aplikace s grafickým uživatelským rozhraním.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Logická hodnota pro zapnutí nebo vypnutí podpory aplikací s grafickým uživatelským rozhraním (taktéž WSL g) ve WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Povolit čítače výkonu hardwaru</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Povolí čítače výkonu hardwaru pro Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Povoltečítače výkonu hardwaru.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Povolí čítače výkonu hardwaru pro Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Brána firewall technologie Hyper-V povolena</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Povolí bránu firewall technologie Hyper-V, která umožňuje filtrovat síťový provoz WSL pomocí pravidel brány Windows Firewall a pravidel specifických pro přenosy technologie Hyper-V.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Brána firewall technologie Hyper-V povolena.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Povolí bránu firewall technologie Hyper-V, která umožňuje filtrovat síťový provoz WSL pomocí pravidel brány Windows Firewall a pravidel specifických pro přenosy technologie Hyper-V.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Zpětná smyčka adresy hostitele</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je wsl2.networkingMode nastaveno na zrcadlené. Když se tato možnost nastaví na True, kontejner se bude moct připojit k hostiteli nebo hostiteli, aby se připojil ke kontejneru pomocí IP adresy přiřazené k hostiteli. Poznámka: Vždy je možné použít adresu zpětné smyčky 127.0.0.1 – tato možnost umožňuje používat i všechny dodatečně přiřazené místní IP adresy.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Zpětná smyčka adresy hostitele.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je vlastnost wsl2.networkingMode nastavena na zrcadlený režim. Pokud je nastavená hodnota True, umožní kontejneru připojit se k hostiteli nebo hostiteli připojit se ke kontejneru pomocí IP adresy přiřazené k hostiteli. Upozorňujeme, že adresu zpětné smyčky 127.0.0.1 je možné vždy použít – tato možnost umožňuje použití všech dodatečně přiřazených místních IP adres.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Ignorované porty</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je vlastnost wsl2.networkingMode nastavena na zrcadlený režim. Určuje porty, na které se můžou linuxové aplikace vázat, a které se nebudou automaticky přesměrovávat ani zvažovat ve Windows. Měl by být formátován v seznamu odděleném čárkami, například: 3000 9000 9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetovat porty</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ignorované porty</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je vlastnost wsl2.networkingMode nastavena na zrcadlený režim. Určuje porty, na které se můžou linuxové aplikace vázat a které se nebudou automaticky přesměrovávat ani brát v potaz ve Windows. Měl by být formátován v seznamu odděleném čárkami, například: 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Časový limit počátečního automatického proxy serveru</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je vlastnost wsl2.autoProxy nastavena na hodnotu true. Konfiguruje, jak dlouho (v milisekundách) WSL počká na načtení informací o proxy serveru HTTP při spuštění kontejneru WSL. Pokud se nastavení proxy serveru po této době vyřeší, musí se instance WSL restartovat, aby se použila načtená nastavení proxy serveru.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetovat časový limit</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Časový limit počátečního automatického proxy serveru</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Lze použít pouze v případě, že je vlastnost wsl2.autoProxy nastavena na hodnotu true. Konfiguruje, jak dlouho (v milisekundách) WSL počká na načtení informací o proxy serveru HTTP při spuštění kontejneru WSL. Pokud se nastavení proxy serveru po této době vyřeší, musí se instance WSL restartovat, aby se použila načtená nastavení proxy serveru.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Příkazový řádek jádra</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Další argumenty příkazového řádku jádra.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Povolit přeposílání localhost</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Logická hodnota určující, jestli se porty vázané na zástupný znak nebo localhost ve virtuálním počítači WSL 2 mají připojit z hostitele přes localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Povolte přeposílání localhost.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Logická hodnota určující, jestli se porty vázané na zástupný znak nebo localhost ve virtuálním počítači WSL 2 mají připojit z hostitele přes localhost dvojtečka port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Paměť a procesor</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Velikost paměti</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Kolik paměti se má přiřadit virtuálnímu počítači WSL 2.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetovat velikost</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Velikost paměti</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Kolik paměti v megabajtech se má přiřadit virtuálnímu počítači WSL 2.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} milisekund</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Povolit vloženou virtualizaci</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Logická hodnota pro zapnutí nebo vypnutí vnořené virtualizace, která umožňuje spouštění jiných vnořených virtuálních počítačů v rámci WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Povolte vloženou virtualizaci.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Logická hodnota pro zapnutí nebo vypnutí vnořené virtualizace, která umožňuje spouštění jiných vnořených virtuálních počítačů v rámci WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Síťový režim</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Určuje síťový režim pro WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Síťový režim.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Určuje síťový režim pro WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Sítě</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Se všemi soubory můžete pracovat napříč operačními systémy!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Přístup k souborům napříč operačními systémy</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Přístup k souborům Windows z Linuxu</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>K souborům ve Windows můžete přistupovat z Linuxu tak, že přejdete na /mnt a pak na písmeno jednotky ve Windows, například na jednotku C:\n\ncd /mnt/c</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Přístup k souborům Linuxu pomocí Průzkumníka souborů</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Linuxové soubory si můžete zobrazit v Průzkumníku souborů tak, že přejdete na \\\\wsl.localhost\\ nebo kliknete na ikonu Linux.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Spuštění souborů a programů Windows z WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>I když používáte WSL, můžete spustit spustitelné soubory Windows přímo z bashe. Zkuste spustit\npowershell.exe /c start . a otevře se Průzkumník souborů v aktuální složce.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Přístup k síťovým aplikacím pro Linux z Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Pokud vytváříte síťovou aplikaci (například aplikaci spuštěnou na NodeJS nebo SQL Serveru) v linuxové distribuci, můžete k ní přistupovat z aplikace pro Windows (jako jsou internetové prohlížeče Edge nebo Chrome) pomocí localhost (stejně jako obvykle). To znamená, že pokud jste spustili linuxový server, který naslouchá portu 3000, můžete přejít na [http://localhost:3000] v Edgi ve Windows a získat k němu přístup.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Sítě v zrcadleném režimu</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL také obsahuje nový síťový režim nazývaný zrcadlený režim, který přidává pokročilé funkce, jako jsou podpora protokolu IPv6 a možnost přístupu k síťovým aplikacím v místní síti.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o přístupu k souborům napříč operačními systémy</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Vítá vás Subsystém Windows pro Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL je skvělý způsob, jak vyzkoušet různé distribuce Linuxu.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Správa distribuce</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o základních příkazech WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o importu libovolné distribuce Linuxu</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Příkaz pro zobrazení seznamu instalovatelných distribucí WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe -l -o</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Příkaz pro instalaci jmenovaných distribucí WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe --install &lt;Název distribuce&gt;</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Příkaz pro zobrazení seznamu dostupných distribucí WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe -l</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop funguje skvěle s WSL a pomůže vám při vývoji díky linuxovým kontejnerům.\n\nMezi výhody používání aplikace Docker Desktop s WSL patří:\n\n• Příkazy Dockeru můžete spouštět ve WSL nebo Windows pomocí stejného démona a imagí Dockeru.\n• Soubory a složky můžete bez problémů sdílet mezi Windows a Linuxem pomocí automatického připojení jednotek Windows ve WSL.\n• Díky interoperabilitě WSL můžete pomocí preferovaných nástrojů a editorů Windows pracovat na linuxovém kódu a souborech a naopak.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Integrace aplikace Docker Desktop</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o používání WSL s Dockerem</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Subsystém Windows pro Linux (WSL) umožňuje spouštět oblíbené linuxové nástroje, pomůcky, aplikace a pracovní postupy přímo ve Windows.\n\nVěnujte chvíli zobrazení náhledu některých oblíbených funkcí komunity nebo si prohlédněte naši komplexní dokumentaci.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Vítá vás WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Osvědčené postupy instalace</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Začínáme s Linuxem</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentace k subsystému Windows pro Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Můžete používat grafické linuxové aplikace s nativní interakcí s Windows, jako jsou alt-tab, spuštění nabídky Start, připnutí na hlavní panel a podpora vyjmutí a vložení.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>Aplikace s grafickým rozhraním (GUI)</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL může využívat GPU Windows pro pracovní postupy strojového učení \n\nBinární soubory Linuxu spuštěné ve WSL můžou automaticky používat GPU ve Windows ke zrychlení výkonu. Tyto pracovní postupy stačí nainstalovat a spustit stejným způsobem jako na běžném počítači s Linuxem. Existuje mnoho různých způsobů, jak začít používat CUDA v kontejneru Dockeru, pokud máte grafickou kartu NVIDIA, ke spuštění PyTorch nebo TensorFlow s DirectML na grafické kartě AMD, Intel nebo NVIDIA. Další informace najdete níže v naší příručce Začínáme.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>Akcelerace GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o akceleraci GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o aplikacích s grafickým rozhraním (GUI) WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Tady je seznam aplikací, které můžete vyzkoušet (všechny tyto aplikace můžete nainstalovat v Ubuntu zadáním sudo apt install &lt;Název aplikace&gt;)\n\n    • gedit – základní textový editor\n    • audacity – záznam a úprava zvukových souborů\n    • blender – vytváření 3D animací a vizualizací\n    • gimp – editor fotek\n    • nautilus – průzkumník souborů pro Linux\n    • vlc – přehrávač videa</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>K síťovým aplikacím můžete snadno přistupovat v operačních systémech Windows a Linux.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Integrace sítí</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o síťových aplikacích</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o síti v zrcadleném režimu</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>WSL můžete používat jako plnohodnotné vývojové prostředí přímo z VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>Integrace VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o používání WSL s VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Postup instalace</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Po instalaci nástroje VS Code můžete nainstalovat rozšíření Remote WSL z terminálu Windows:\n\ncode –install-extension ms-vscode-remote.remote-wsl</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Otevření projektu WSL ve Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Chcete-li otevřít projekt v nástroji VS Code z distribuce WSL, otevřete příkazový řádek distribuce a spuštěním příkazu code . otevřete soubor projektu.\n\nK dalším možnostem VS Code Remote můžete přistupovat také prostřednictvím palety příkazů v samotném VS Code. Stisknutím SHIFT + CTRL + P na klávesnici otevřete paletu příkazů a zadáním Remote-WSL zobrazíte seznam dostupných možností VS Code Remote, které umožňují znovu otevřít složku ve vzdálené relaci, určit distribuci, kterou chcete otevřít, a mnohem více.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Jak používat</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Volitelné funkce</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Prohlášení o zásadách ochrany osobních údajů</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Počet procesorů</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Kolik logických procesorů se má přiřadit virtuálnímu počítači WSL 2.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetovat počet</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Počet procesorů</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Kolik logických procesorů se má přiřadit virtuálnímu počítači WSL 2.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Související propojení</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Poznámky k verzi</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Povolit nouzový režim</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Spusťte WSL v nouzovém režimu, který zakáže mnoho funkcí a je určen k obnovení distribucí, které jsou ve špatném stavu.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Povolte nouzový režim.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Spusťte WSL v nouzovém režimu, který zakáže mnoho funkcí a je určen k obnovení distribucí, které jsou ve špatném stavu.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>O modulu snap-in</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Vývojář</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Správa distribuce</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Integrace aplikace Docker Desktop</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Systém souborů</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Obecné</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Akcelerace GPU</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>Aplikace s grafickým rozhraním (GUI)</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Spustit wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Paměť a procesor</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Sítě</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Integrace sítí</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Vítá vás WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Volitelné funkce</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Nastavení</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>Integrace VS Code</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Novinky</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Práce v souborových systémech</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problémy</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Ve výchozím nastavení povolit zhuštěný virtuální pevný disk</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Všechny nově vytvořené virtuální pevné disky budou při povolení automaticky nastaveny na zhuštěné.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ve výchozím nastavení povolte zhuštěný virtuální pevný disk.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Všechny nově vytvořené virtuální pevné disky budou při povolení automaticky nastaveny na zhuštěné.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Prohození umístění souboru</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Absolutní cesta systému Windows k prohození virtuálního pevného disku.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procházet soubory prohození</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Prohození umístění souboru</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Absolutní cesta systému Windows k prohození virtuálního pevného disku.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Velikost prohození</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Kolik místa prohození se má přidat do virtuálního počítače WSL 2, 0 pro žádný odkládací soubor. Prohození úložiště je disková paměť RAM, která se používá v případě, že poptávka po paměti překračuje limit hardwarového zařízení.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetovat velikost</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Velikost prohození</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Kolik místa prohození zadaného v megabajtech, které se má přidat do virtuálního počítače WSL 2 0 pro žádný odkládací soubor Stránkovací úložiště je paměť RAM založená na disku, která se používá v případě, že požadavky na paměť překračují limit hardwarového zařízení.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Povolit VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Pro přístup k hostitelským souborům používejte virtiofy místo plánu 9, což zvyšuje rychlost.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Povolit Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Povolí připojení systému souborů 9P z hostitele pomocí přenosu virtio.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Časový limit nečinnosti virtuálního počítače</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Počet milisekund, po které je virtuální počítač nečinný, než se vypne.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetovat časový limit</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Časový limit nečinnosti virtuálního počítače</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Počet milisekund, po které je virtuální počítač nečinný, než se vypne.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Vytvářejte, spouštějte, laďte a profilujte své aplikace běžící na WSL z Visual Studia</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Spouštějte a laďte své aplikace ve WSL z Visual Studia</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o WSL ve Visual Studiu pro vývojáře .NET</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Další informace o Visual Studiu a WSL pro vývojáře v C++</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Pomocí subsystému Windows pro Linux (WSL) můžete v Linuxu snadno spouštět a ladit své aplikace .NET Core a multiplatformní aplikace C++ přímo ve Visual Studiu, aniž byste museli opustit toto prostředí. Pokud jste multiplatformní vývojář, můžete tuto metodu využít jako jednoduchý způsob testování více cílových prostředí.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Integrace Visual Studia</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Integrace Visual Studia</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/da-DK/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Windows-undersystem til Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Windows-undersystem til Linux giver udviklere mulighed for at køre et GNU/Linux environment – herunder de fleste kommandolinjeværktøjer, hjælpeprogrammer og programmer – direkte på Windows, uændret, uden spild af en traditionel virtuel maskine eller dualboot-konfiguration.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Disken kunne ikke fjernes: {}. Du kan få flere oplysninger ved at køre 'dmesg' i WSL2.\nHvis du vil tvinge WSL2 til at stoppe og fjerne disken, skal du køre 'wsl.exe {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Disken '{}' er allerede tilsluttet.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Diskenheden er allerede insat inde i WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Der er allerede tilsluttet en disk med dette navn. frakoble disken, eller vælg et nyt navn, og prøv igen.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Det angivne tilslutningsnavn indeholder et ugyldigt '/'-tegn. Prøv igen uden det ugyldige tegn.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Disken blev tilsluttet som '/mnt/wsl/{}'.\nBemærk: Placeringen vil være anderledes, hvis du har ændret indstillingen automount.root i /etc/wsl.conf.\nHvis du vil frakoble disken og fjerne den, skal du køre 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Disken var tilsluttet, men kunne ikke tilsluttes: {}.\nDu kan få flere oplysninger ved at køre 'dmesg' i WSL2.\nKør 'wsl.exe {} {}' for at fjerne disken.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Nedenfor vises en liste over gyldige distributioner, der kan installeres.\nInstaller ved hjælp af 'wsl.exe {} &lt;Distro&gt;'.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Distributionsnavnet er allerede angivet.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>Den angivne installationsplacering er allerede i brug.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Der findes allerede en distribution med det angivne navn. Brug --name til at vælge et andet navn.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Der er ingen distribution med det angivne navn.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Der kræves administratoradgang for at tilslutte en disk.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Eksport af distributionen mislykkedes.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Udfører engangsopgradering af Windows-undersystem til Linux-filsystemet for denne distributions…</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Der kan ikke startes, fordi en anden forekomst kører uden administratorrettigheder.  Forekomster med administratorrettigheder og forekomster uden administratorrettigheder har ikke tilladelse til at køre samtidigt.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Import af distributionen mislykkedes.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Installation af valgfri komponent i Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Downloader: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Installerer: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importerer distribution</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} er blevet downloadet.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Windows-undersystem til Linux fortsætter en tidligere.installation…</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Ugyldigt distributionsnavn: '{}'.\nHvis du vil hente en liste over gyldige distributioner, skal du bruge 'wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Windows-undersystem til Linux-forekomsten er afsluttet.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Ugyldigt kommandolinjeargument: {}\nBrug '{} --help' til at få en liste over understøttede argumenter.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>Kommandolinjeargumentet {} kræver en værdi.\nBrug '{} --help' til at få en liste over understøttede argumenter.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Ikke-understøttede konsolindstillinger. Hvis du vil bruge denne funktion, skal den ældre konsol deaktiveres.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} er ikke et gyldigt heltal.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>En installation, fjernelse eller konvertering er i gang for denne distribution.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Starter {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Der kan ikke startes, fordi en anden forekomst kører med administratorrettigheder.  Forekomster med administratorrettigheder og forekomster uden administratorrettigheder har ikke tilladelse til at køre samtidigt.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Windows-undersystem til Linux har ingen installerede distributioner.\nDu kan løse problemet ved at installere en distribution ved hjælp af nedenstående vejledning:\n\nBrug 'wsl.exe --list --online' til at få vist tilgængelige distributioner\nog \"wsl.exe --install &lt;Distro&gt;\" for at installere.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Der er ingen kørende distributioner.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Standard)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Windows-undersystem til Linux-distributioner:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Standarddistribution: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Standardversion: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Fjerner registrering.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Bruger blev ikke fundet.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Besøg https://aka.ms/wsl2 for at få oplysninger om nøgleforskelle i WSL 2</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Konverteringen er i gang. Dette kan tage et par minutter.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>Distributionen er allerede den anmodede version.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>Den ældre distribution understøtter ikke WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>WSL2 kan ikke starte, da virtualisering ikke er aktiveret på denne maskine.\nKontrollér, at den valgfri komponent \"Virtuel maskinplatform\" er aktiveret, og at virtualisering er slået til i computerens firmwareindstillinger.\n\nAktivér \"Virtuel maskinplatform\" ved at køre: wsl.exe --install --no-distribution\n\nSe https://aka.ms/enablevirtualization for at få mere at vide</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Der er tilsluttet for mange virtuelle harddiske eller fysiske diske.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD er allerede indsat via wsl.exe --mount, afbryd disken, før du starter.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 understøttes ikke med den aktuelle maskinkonfiguration.\nAktivér den valgfrie komponent \"Windows-undersystem til Linux\" for at bruge WSL1.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Denne handling understøttes kun af WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Brug:\n    --networking-mode\n       Vis aktuel netværkstilstand.\n\n    --msal-proxy-path\n        Vis stien til MSAL-proxyprogrammet.\n\n    --vm-id\n        Vis WSL VM ID.\n\n    --version\n        Vis versionen af WSL-pakken.\n\n    -n\n        Udskriv ikke en ny linje.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Brug:\n    -a\n        Gennemtving resultatet til absolut stiformat.\n    -u\n        Oversæt fra en Windows-sti til en WSL-sti (standard).\n    -w\n        Oversæt fra en WSL-sti til en Windows-sti (standard).\n    -m\n        Oversæt fra en WSL-sti til en Windows-sti, med \"/\" i stedet for \"\\\\\"\n\nEksempel: wslpath \"c:\\\\brugere\"</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Udfører administrative handlinger på Windows-undersystem til Linux\n\nBrug:\n    /l, /list [Indstilling]\n        Viser registrerede distributioner.\n        /all – Vis valgfrit alle distributioner, herunder distributioner, som\n               installeres eller fjernes i øjeblikket.\n\n        /running – Vis kun distributioner, der kører i øjeblikket.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Angiver distributionen som standard.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Afslutter distributionen.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Fjerner distributionen og sletter rodfilsystemet.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Alle rettigheder forbeholdes.\nDu kan finde oplysninger om beskyttelse af personlige oplysninger for dette produkt på https://aka.ms/privacy.\n\nBrug: wsl.exe [Argument] [Options...] [CommandLine]\n\nArgumenter for kørsel af binære Linux-filer:\n\n    Hvis der ikke er angivet en kommandolinje, starter wsl.exe en standardshell.\n\n    --exec, -e &lt;CommandLine&gt;\n        Kør den angivne kommando uden brug af Linux-standardshellen.\n\n    --shell-type &lt;standard|login|none&gt;\n        Udfør den angivne kommando med den angivne shelltype.\n\n    --\n        Overfør den resterende kommandolinje, som den er.\n\nIndstillinger:\n    --cd &lt;Directory&gt;\n        Fastsætter den angivne mappe som den aktuelle arbejdsmappe.\n        Hvis ~ bruges, bruges Linux-brugerens sti til hjem. Hvis stien starter\n        med tegnet /, fortolkes den som en absolut Linux-sti.\n        Ellers skal værdien være en absolut Windows-sti.\n\n    --distribution, -d &lt;DistroName&gt;\n        Kør den angivne distribution.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Kør det angivne distributions-id.\n\n    --user, -u &lt;UserName&gt;\n        Kør som den angivne bruger.\n\n    --system\n        Starter en shell for systemdistributionen.\n\nArgumenter til administration af Windows-undersystem til Linux:\n\n    --help\n        Vis brugsoplysninger.\n\n    --debug-shell\n        Åbn en WSL2-fejlfindingsshell til diagnosticeringsformål.\n\n    --install [Distro] [Options...]\n        Installér en distribution af Windows-undersystem til Linux.\n        Du kan se en liste over gyldige distributioner ved at bruge 'wsl.exe --list --online'.\n\n        Indstillinger:\n            --enable-wsl1\n                Aktivér WSL1-support.\n\n            --fixed-vhd\n                Opret en disk med fast størrelse til lagring af distribution.\n\n            --from-file &lt;Path&gt;\n                Installér en distribution fra en lokal fil.\n\n            --legacy\n                Brug det ældre distributionsmanifest.\n\n            --location &lt;Location&gt;\n                Angiv installationsstien for distributionen.\n\n            --name &lt;Name&gt;\n                Angiv navnet på distributionen.\n\n            --no-distribution\n                Installér kun de påkrævede valgfrie komponenter. Der installeres ikke en distribution.\n\n            --no-launch, -n\n                Start ikke distributionen efter installationen.\n\n            --version &lt;Version&gt;\n                Angiver den version, der skal bruges til den nye distribution.\n\n            --vhd-size &lt;MemoryString&gt;\n                Angiver størrelsen på disken, hvor distributionen skal gemmes.\n\n            --web-download\n                Download distributionen fra internettet i stedet for Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Ændrer distributionsspecifikke indstillinger.\n\n        Indstillinger:\n            --move &lt;Location&gt;\n                Flyt distributionen til en ny placering.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Angiv VHD for distribution til sparse, hvilket gør det muligt at frigøre diskplads automatisk.\n\n            --set-default-user &lt;Username&gt;\n                Angiv standardbrugeren af distributionen.\n\n            --resize &lt;MemoryString&gt;\n                Tilpas størrelsen på disken til distributionen til den angivne størrelse.\n\n    --mount &lt;Disk&gt;\n        Tilknytter og indsætter en fysisk eller virtuel disk i alle WSL 2-distributioner.\n\n        Indstillinger:\n            --vhd\n                Angiver, at &lt;Disk&gt; refererer til en virtuel harddisk.\n\n            --bare\n                Indsætter disken til WSL2, men tilslutter den ikke.\n\n            --name &lt;Name&gt;\n                Indsætter disken ved hjælp af et brugerdefineret navn på tilslutningspunktet.\n\n            --type &lt;Type&gt;\n                Filsystem, der skal bruges, når en disk indsættes, hvis den ikke som standard er angivet til ext4.\n\n            --options &lt;Options&gt;\n                Yderligere indsætningsindstillinger.\n\n            --partition &lt;Index&gt;\n                Indeks for partitionen, der skal indsættes, hvis der ikke som standard er angivet for hele disken.\n\n    --set-default-version &lt;Version&gt;\n        Ændrer standardinstallationsversionen for nye distributioner.\n\n    --shutdown\n        Afslutter straks alle kørende distributioner og WSL 2\n        virtuel maskine med letvægtsfunktioner.\n\n        Indstillinger:\n            --force\n                Afslut den virtuelle WSL 2-maskine, selvom en handling er i gang. Kan medføre tab af data.\n\n    --status\n        Vis status for Windows-undersystem til Linux.\n\n    --unmount [Disk]\n        Frakobler og fjerner tilknytningen mellem en disk og alle WSL2-distributioner.\n        Frakobler og fjerner tilknytningen til alle diske, hvis de kaldes uden et argument.\n\n    --uninstall\n        Fjerner Windows-undersystem til Linux-pakken fra denne computer.\n\n    --update\n        Opdater pakken med Windows-undersystem til Linux.\n\n        Indstillinger:\n            --pre-release\n                Download en pre-release, hvis den er tilgængelig.\n\n    --version, -v\n        Vis versionsoplysninger.\n\nArgumenter for administration af distributioner i Windows-undersystem til Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Eksporterer distributionen til en TAR-fil.\n        Filnavnet kan være - for stdout.\n\n        Indstillinger:\n            --format &lt;Format&gt;\n                Angiver eksportformatet. Understøttede værdier: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        Importerer den angivne TAR-fil som en ny distribution.\n        Filnavnet kan være - for stdin.\n\n        Indstillinger:\n            --version &lt;Version&gt;\n                Angiver den version, der skal bruges til den nye distribution.\n\n            --vhd\n                Angiver, at den angivne fil er en .vhd- eller.vhdx-fil, ikke en tar-fil.\n                Denne handling opretter en kopi af VHD-filen på den angivne installationsplacering.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Importerer den angivne VHD-fil som en ny distribution.\n        Denne virtuelle harddisk skal være formateret med ext4-filsystemtypen.\n\n    --list, -l [Options]\n        Lister distributioner.\n\n        Indstillinger:\n            --all\n                Vis alle distributioner, herunder distributioner, der\n                installeres eller fjernes i øjeblikket.\n\n            --running\n                Vis kun distributioner, der kører i øjeblikket.\n\n            --quiet, -q\n                Vis kun distributionsnavne.\n\n            --verbose, -v\n                Vis detaljerede oplysninger om alle distributioner.\n\n            --online, -o\n                Viser en liste over tilgængelige distributioner for installationer med 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Angiver distributionen som standard.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Ændrer versionen af den angivne distribution.\n\n    --terminate, -t &lt;Distro&gt;\n        Afslutter den angivne distribution.\n\n    --unregister &lt;Distro&gt;\n        Fjerner distributionen og sletter rodfilsystemet.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL-version: {}\nKerneversion: {}\nWSLg-version: {}\nMSRDC-version: {}\nDirect3D-version: {}\nDXCore-version: {}\nWindows-version: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild-version: {}\nBekræftelse: {}\nBuildtidspunkt: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>Den brugerdefinerede kerne, der er angivet i {}, blev ikke fundet: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>Virtuel harddisk for de brugerdefinerede kernemoduler i {} blev ikke fundet: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>Den brugerdefinerede systemdistribution, der er angivet i {}, blev ikke fundet eller har ikke det korrekte format.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Alle rettigheder forbeholdes.\nDu kan finde oplysninger om beskyttelse af personlige oplysninger for dette produkt ved at gå til https://aka.ms/privacy.\n\nBrug: wslg.exe [Argument] [Options...] [CommandLine]\n\nArgumenter:\n    --cd &lt;Directory&gt;\n        Fastsætter det angivne bibliotek som det aktuelle aktive bibliotek.\n        Hvis ~ bruges, bruges Linux-brugerens sti til hjemmemappen. Hvis stien starter\n        med tegnet /, fortolkes den som en absolut Linux-sti.\n        Ellers skal værdien være en absolut Windows-sti.\n\n    --distribution, -d &lt;Distro&gt;\n        Kør den angivne distribution.\n\n    --user, -u &lt;UserName&gt;\n        Kør som den angivne bruger.\n\n    --shell-type &lt;standard|login|none&gt;\n        Udfør den angivne kommando med den angivne shelltype.\n\n    --help\n        Vis forbrugsoplysninger.\n\n    --\n        Overfør den resterende kommandolinje, som den er.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Tryk på en tast for at fortsætte...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Argumentet {} mangler en påkrævet parameter.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Eksport er i gang. Dette kan tage et par minutter.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Importen er i gang. Dette kan tage et par minutter.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>Understøttelse af GUI-programmer er deaktiveret via {} eller /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Søger efter opdateringer.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Denne distribution er kun tilgængelig fra Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount på ARM64 kræver Windows version 27653 eller nyere.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Den nyeste version af Windows-undersystem til Linux er allerede installeret.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Opdaterer Windows-undersystem til Linux til version: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Dette program kræver Windows-undersystem til Linux valgfri komponent.\nInstaller den ved at køre: wsl.exe --install --no-distribution\nSystemet skal muligvis genstartes, så ændringerne kan træde i kraft.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Den angivne fil skal have filtypenavnet {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Den angivne fil skal have filtypenavnet {} eller {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>VmSwitch \"{}\" blev ikke fundet. Tilgængelige switches: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Sammenknyttet netværk kræver, at wsl2.vmSwitch er angivet.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Distributionslisten kunne ikke hentes fra '{}'. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Disken '{}' kunne ikke knyttes til WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Diskens størrelsen kunne ikke tilpasses.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Der blev ikke fundet nogen værdi.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Windows-versionen {} understøtter ikke den pakkede version af Windows-undersystem til Linux.\nInstallér den påkrævede opdatering via Windows-opdatering eller via: {}\nBesøg https://aka.ms/wslinstall for at få flere oplysninger</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Kørsel af fejlfindingsshell kræver, at der køres wsl.exe som administrator.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Installationsprocessen for distributionen '{}' mislykkedes med returkoden: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Ugyldigt GUID-format: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Ugyldigt sektionsnavn i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Ugyldigt nøglenavn i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Forventede {} i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Escape-tegnet er ugyldigt: '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Ukendt nøgle '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Ugyldig heltalsværdi '{}' for nøglen '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Ugyldig IP-værdi '{}' for nøglen '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Ugyldig boolesk værdi '{}' for nøglen '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Ugyldig MAC-adresse '{}' for nøglen '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Konfigurationsfilen {}, {} kunne ikke åbnes</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Der opstod fejl under WSL-selvstart</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Systembrugersessionen for '{}' kunne ikke startes. Se journalctl for at få flere oplysninger.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Åbn EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Denne distribution indeholder ikke et standardnavn. Brug --name til at vælge distributionsnavnet.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Argumenterne {} og {} kan ikke angives samtidig.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>Argumentet {} kræver argumentet {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Ugyldigt distributionsnavn: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Bruger ældre distributionsregistrering. Overvej at bruge en tar-baseret distribution i stedet.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Ældre distributionsregistreringer understøtter ikke argumentet --version .</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>Distributionen \"{}\" leveres af et tilsidesættelsesmanifest.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>Distributionshash'en stemmer ikke overens. Forventet: {}, faktisk hash: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Ugyldig hex-streng: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>'{}' understøttes ikke ved installation af ældre distributioner.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Distributionen blev installeret. Den kan startes via 'wsl.exe -d {}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Ugyldig hukommelsesstreng '{}' for .wslconfig-posten '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Swapdisken kunne ikke oprettes i '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Indlejret virtualisering understøttes ikke på denne maskine.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Der kunne ikke oprettes et netværksslutpunkt med adressen: '{}', tildelt ny adresse: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Virtuelt netværk med adresseområde: '{}' blev ikke oprettet. Der blev oprettet et nyt netværk med området: '{}', {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>FEJLSIKRET TILSTAND ER AKTIVERET – mange funktioner vil blive deaktiveret</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Spejlet netværkstilstand understøttes ikke: {}.\nGår tilbage til NAT-netværk.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Linux Kernel version 5.10 eller nyere er påkrævet</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows-version {}. {} har ikke de nødvendige funktioner</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Hyper-V-firewall understøttes ikke</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>DNS-tunnelføring understøttes ikke</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>Indstillingen wsl2.localhostForwarding har ingen effekt, når der bruges spejlet netværkstilstand</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Der er registreret en http-proxyændring på værten. Genstart WSL for at anvende ændringen.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Der blev registreret en localhost-proxykonfiguration, men den blev ikke spejlet til WSL. WSL i NAT-tilstand understøtter ikke localhost-proxyer.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Der blev registreret en IPv6-proxykonfiguration, men den blev ikke spejlet til WSL. WSL i NAT-tilstand understøtter ikke IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Der blev registreret en localhost IPv6-proxykonfiguration, men den blev ikke spejlet til WSL. WSL understøtter ikke localhost IPv6-proxyer.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Der opstod en uventet fejl under forsøg på at fortolke proxyindstillingerne. Proxyindstillingerne blev ikke spejlet til WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Der opstod en fejl under forsøg på at få adgang til registreringsdatabasen. Sti: '{}'. Fejl: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Relay-processen for localhost kunne ikke startes. Fejl: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Det virtuelle netværk kunne ikke startes. Installer den valgfrie platform til virtuel maskine ved at køre: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Netværket kunne ikke konfigureres (networkingMode {}). Går tilbage til networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Windows-komponenten '{}' kunne ikke aktiveres (returkode {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Plug-in'en '{}' returnerede en alvorlig fejl</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Plug-in'en '{}' returnerede en alvorlig fejl. Fejlmeddelelse: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Plug-in'en '{}' kræver en nyere version af WSL. Kør: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>Indstillingen \"{}\" for .wslconfig er deaktiveret af computerpolitikken.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>Fejlfindingsshell er deaktiveret af computerpolitikken.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount er deaktiveret af computerpolitikken.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>WSL1 er deaktiveret af computerpolitikken.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Kør 'wsl.exe --set-version {} 2' for at opgradere til WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL afslutter en opgradering...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Opdateringen mislykkedes (afslutningskode: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Fjernelsen mislykkedes (udgangskode: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Logfil: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>MSIX-pakken kunne ikke fjernes (fejl: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>MSIX-pakken kunne ikke installeres (fejl: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Valgfrie komponenter, der kræves for at køre WSL, er ikke installeret.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Installer manglende komponenter.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Den importerede fil er ikke en gyldig Linux-distribution.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Tryk på en tast for at afslutte...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Der findes en ny version af Windows-undersystem til Linux.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Opdater til version {}, eller se dens produktbemærkninger nedenfor.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Opdater</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Se dokumenter</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Handlingen kunne ikke fuldføres, fordi VHD'en i øjeblikket er i brug. For at tvinge WSL til at stoppe skal du bruge: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} er ikke en gyldig boolesk værdi, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Sparse VHD understøttes kun på WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>Kørsel af WSL som lokalt system understøttes ikke.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Tip til ydeevne:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Brug af en I/O-krævende handling som {} på dine Windows-drev vil have dårlig ydeevne. Overvej at flytte dine projektfiler til Linux-filsystemet for at opnå en bedre ydeevne. Klik nedenfor for at få mere at vide.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Vis dokumenter</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Vis ikke igen</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>Den virtuelle WSL2-maskine gik ned.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>Staksporingen er gemt i: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Distributionen kunne ikke startes. Fejlkode: {}, fejltrin: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Distributionen kunne ikke starte, fordi den virtuelle disk er beskadiget.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Konfigurationsnøglen '{}' er duplikeret i {}:{} (uoverensstemmende nøgle: '{}' i {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Ugyldig værdi '{}' for konfigurationsnøglen '{}' i {}:{} (Gyldige værdier: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Venter på, at OOBE-kommandoen fuldføres for distributionen \"{}\"...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Det lykkedes ikke at læse egenskaben \"{}\" fra distributionen {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Dette ligner en VHD-fil. Brug --vhd til at importere en VHD i stedet for en tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>{} kunne ikke installeres fra Microsoft Store: {}\nForsøger at downloade...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Der er ikke konfigureret nogen standarddistribution. Angiv en distribution, der skal installeres.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p er deaktiveret og falder tilbage til 9p med vsock-transport.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>WSL-installationen ser ud til at være beskadiget (fejlkode: {}).\nTryk på en tast for at reparere WSL, eller tryk CTRL-C for at annullere.\nDenne prompt får timeout om 60 sekunder.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Terminalprofilen kunne ikke fortolkes under registrering af distribution: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Ugyldig størrelse: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nFejlkode: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Ugyldigt JSON-dokument. Fortolkningsfejl: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors kan ikke overskride antallet af logiske processorer på systemet ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Der kunne ikke forespørges i netværkstilstand</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Der blev leveret tilpassede kernemoduler uden angivelse af en tilpasset kerne. Se https://aka.ms/wslcustomkernel for at få flere oplysninger.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>På grund af et aktuelt kompatibilitetsproblem med Global sikker adgang-klienten er DNS-tunneling deaktiveret.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Understøttelse af sparse VHD er i øjeblikket deaktiveret på grund af potentiel datakorruption.\nFor at tvinge en distribution til at bruge en sparse VHD, skal du køre:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>{} kunne ikke indsættes. Se dmesg for at få flere oplysninger.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Behandling af /etc/fstab med indsættelse af -a mislykkedes.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Der opstod en fejl under tilslutning af distributionsdisken. Den blev tilsluttet som fallback.\nSe genoprettelsesinstruktioner på: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>\"{}\" kunne ikke oversættes</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Noget gik galt. Prøv igen senere.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Om</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Om indstillinger for Windows-undersystem til Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Alle rettigheder forbeholdes.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>indstillinger for Windows-undersystem til Linux</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Indstillinger til Windows-undersystem til Linux giver udviklere mulighed for at administrere .wslconfig-filen ved hjælp af et GUI-baseret program.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Automatisk genoprettelse af hukommelse</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Frigiver automatisk cachelagret hukommelse efter registrering af inaktiv CPU-brug. Indstillet til gradvis for langsom udgivelse og dropcache for øjeblikkelig frigivelse af cachelagret hukommelse.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatisk genoprettelse af hukommelse.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Frigiver automatisk cachelagret hukommelse efter registrering af inaktiv CPU-brug. Indstillet til gradvis for langsom udgivelse og dropcache for øjeblikkelig frigivelse af cachelagret hukommelse.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Automatisk proxy er aktiveret</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Muliggør, at WSL kan bruge http-proxyoplysninger fra Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatisk proxy er aktiveret.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Muliggør, at WSL kan bruge http-proxyoplysninger fra Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Brug DNS-parsing, der gør det bedst muligt</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.dnsTunneling er indstillet til sand. Når den er indstillet til sand, udtrækker Windows spørgsmålet fra DNS-anmodningen og forsøger at løse det og ignorerer de ukendte poster.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Brug DNS-parsing, der gør det bedst muligt.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.dnsTunneling er angivet til true. Når den er indstillet til sand, udtrækker Windows spørgsmålet fra DNS-anmodningen og forsøger at løse det og ignorerer de ukendte poster.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Brugerdefineret kerne</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>En absolut Windows-sti til en brugerdefineret Linux-kerne.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Gennemse kerner</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Brugerdefineret kerne</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En absolut Windows-sti til en brugerdefineret Linux-kerne.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Brugerdefineret kernemoduler</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>En absolut Windows-sti til en brugerdefineret VHD for Linux-kernemoduler.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Gennemse kernemoduler</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Brugerdefineret kernemoduler</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En absolut Windows-sti til en brugerdefineret VHD for Linux-kernemoduler.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Distribution af brugerdefineret system</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Gennemse distributioner</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Angiv en sti til en VHD, der indlæses som en brugerdefineret system distro, der primært bruges til at styrke GUI-apps i WSL. [Få mere at vide om system distros her].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Distribution af brugerdefineret system</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Angiv en sti til en VHD, der indlæses som en brugerdefineret system distro, der primært bruges til at styrke GUI-apps i WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Aktivér fejlfindingskonsol</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Boolesk værdi til at aktivere et outputkonsolvindue, der viser indholdet af dmesg ved start af en WSL 2-distroforekomst.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivér fejlfindingskonsol.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolesk værdi til at aktivere et outputkonsolvindue, der viser indholdet af diagnosticeringsmeddelelser ved start af en WSL 2-distroforekomst.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Standardstørrelse for virtuel harddisk</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Den maksimale standardstørrelse for den virtuelle WSL-harddisk, der kan udvides, kun for nyoprettede distributioner.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Nulstil størrelse</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Standardstørrelse for virtuel harddisk</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Den maksimale standardstørrelse, angivet i megabyte, for den udvidede virtuelle WSL-harddisk (VHD) for nyoprettede distributioner.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Softwareudvikler</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentation</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Skift DrvFS-tilstand</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Ændrer implementering af filadgang på tværs af operativsystemer i WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS-proxy er aktiveret</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.networkingMode er indstillet til NAT. Boolesk for at informere WSL om at konfigurere DNS-serveren i Linux til NAT på værten. Hvis du angiver indstillingen til falsk, spejles DNS-servere fra Windows til Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-proxy er aktiveret.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.networkingMode er indstillet til NAT. Boolesk at informere WSL om at konfigurere DNS-serveren i Linux til NAT på værten. Hvis du angiver indstillingen til falsk, spejles DNS-servere fra Windows til Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>DNS-tunnelføring er aktiveret</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Ændrer, hvordan DNS-anmodninger proxyes fra WSL til Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-tunnelføring er aktiveret.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ændrer, hvordan DNS-anmodninger proxyes fra WSL til Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Filsystem</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Aktivér GUI-programmer</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Boolesk værdi til at aktivere eller deaktivere understøttelse af GUI-programmer ([WSLg]) i WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivér GUI-programmer.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolesk værdi til at aktivere eller deaktivere understøttelse af GUI-programmer (kendt som WSL g) i WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Aktivér tællere for hardwareydeevne</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Aktiverer tællere for hardwareydeevne til Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivér tællere for hardwareydeevne.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Aktiverer tællere for hardwareydeevne til Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Hyper-V Firewall er aktiveret</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Aktiverer Hyper-V-firewall, der tillader, at WSL-netværkstrafik filtreres i reglerne for Windows Firewall samt regler, der er specifikke for Hyper-V-trafik.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hyper-V Firewall er aktiveret.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Aktiverer Hyper-V-firewall, der tillader, at WSL-netværkstrafik filtreres i reglerne for Windows Firewall samt regler, der er specifikke for Hyper-V-trafik.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Tilbagekobling af værtsadresse</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.networkingMode er indstillet til spejlet. Når den er indstillet til Sand, kan objektbeholderen oprette forbindelse til værten eller værten for at oprette forbindelse til objektbeholderen af en IP-adresse, der er tildelt værten. Bemærk, at tilbagekoblingsadressen 127.0.0.1 altid kan bruges – denne indstilling gør det muligt også at bruge alle yderligere tildelte lokale IP-adresser.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tilbagekobling af værtsadresse.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.networkingMode er indstillet til spejlet. Når den er indstillet til Sand, kan objektbeholderen oprette forbindelse til værten eller værten for at oprette forbindelse til objektbeholderen af en IP-adresse, der er tildelt værten. Bemærk, at tilbagekoblingsadressen 127.0.0.1 altid kan bruges – denne indstilling gør det muligt også at bruge alle yderligere tildelte lokale IP-adresser.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Ignorerede porte</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.networkingMode er indstillet til spejlet. Angiver, hvilke porte Linux-programmer kan binde til, som ikke automatisk videresendes eller tages i betragtning i Windows. Skal formateres i en kommasepareret liste, f.eks.: 3000,9000,9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Nulstil porte</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ignorerede porte</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.networkingMode er indstillet til spejlet. Angiver, hvilke porte Linux-programmer kan binde til, som ikke automatisk videresendes eller tages i betragtning i Windows. Skal formateres i en kommasepareret liste, f.eks.: 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Indledende timeout for automatisk proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.autoProxy er indstillet til sand. Konfigurerer, hvor længe (i millisekunder) WSL venter på at hente HTTP-proxyoplysninger, når en WSL-objektbeholder startes. Hvis proxyindstillingerne fortolkes efter dette tidspunkt, skal WSL-forekomsten genstartes for at bruge de hentede proxyindstillinger.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Nulstil timeout</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Indledende timeout for automatisk proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gælder kun, når wsl2.autoProxy er angivet til true. Konfigurerer, hvor længe WSL i millisekunder venter på at hente HTTP-proxyoplysninger, når en WSL-objektbeholder startes. Hvis proxyindstillingerne fortolkes efter dette tidspunkt, skal WSL-forekomsten genstartes for at bruge de hentede proxyindstillinger.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Kernekommandolinje</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Yderligere argumenter for kernekommandolinjen.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Aktivér videresendelse af localhost</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Boolesk værdi, der angiver, om porte, der er bundet til jokertegn eller localhost i WSL 2 VM, skal kunne forbindes fra værten via localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivér videresendelse af localhost.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolesk værdi, der angiver, om porte, der er bundet til jokertegn eller localhost i WSL 2 VM, skal kunne forbindes fra værten via localhost, colon, port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Hukommelse og processor</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Størrelse på hukommelse</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Hvor meget hukommelse der skal tildeles til WSL 2 VM'en.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Nulstil størrelse</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Størrelse på hukommelse</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hvor meget hukommelse, angivet i megabyte, der skal tildeles til WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} millisekunder</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Aktivér indlejret virtualisering</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Boolesk værdi til aktivering eller deaktivering af indlejret virtualisering, så andre indlejrede VM'er kan køre i WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivér indlejret virtualisering.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolesk værdi til at slå indlejret virtualisering til eller fra, så andre indlejrede VM'er kan køre i WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Netværkstilstand</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Angiver netværkstilstanden for WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Netværkstilstand.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Angiver netværkstilstanden for WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Netværk</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Du kan arbejde på tværs af operativsystemer med alle dine filer!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Adgang til filer på tværs af operativsystemer</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Få adgang til dine Windows-filer fra Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Du kan få adgang til dine Windows-filer fra Linux ved at navigere til \"/mnt\" og derefter til dit Windows-drevbogstav, som dette eksempel på C-drevet:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Få adgang til dine Linux-filer med Stifinder</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Du kan få vist dine Linux-filer fra Stifinder ved at navigere til '\\\\wsl.localhost\\' eller klikke på ikonet ‘Linux’.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Start Windows-filer og -programmer fra WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Selv når du bruger WSL, kan du køre dine eksekverbare Windows-filer direkte fra bash. Prøv at køre\n'powershell.exe /c start .' for at åbne Stifinder i din aktuelle mappe.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Adgang til Linux-netværksapps fra Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Hvis du bygger en netværksapp (f.eks. en app, der kører på en NodeJS eller SQL-server) i din Linux-distribution, kan du få adgang til den fra en Windows-app (f.eks. din Edge- eller Chrome-internetbrowser) ved hjælp af localhost (ligesom du normalt ville gøre). Det betyder, at hvis du startede en Linux-server, der lytter til port 3000, kan du gå til [http://localhost:3000] i Edge på Windows for at få adgang til den.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Netværk i spejlet tilstand</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL inkluderer også en ny netværkstilstand, kaldet spejlet tilstand, som tilføjer avancerede funktioner som F.eks. IPv6-understøttelse og muligheden for at få adgang til dine netværksprogrammer i dit lokalnetværk.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om adgang til filer på tværs af operativsystemer</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Velkommen til Windows-undersystem til Linux.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL er en god måde at afprøve forskellige Linux-distributioner på.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Distro-administration</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om grundlæggende WSL-kommandoer</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om import af Linux-distribution</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Vis WSL Distros-kommando, der kan installeres</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Installer en navngivet WSL Distro-kommando</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Vis tilgængelig WSL Distros-kommando</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker-skrivebord fungerer godt sammen med WSL for at hjælpe dig med at udvikle med Linux-objektbeholdere.\n\nNogle af fordelene ved at bruge Docker-skrivebord med WSL er:\n\n• Du kan køre Docker-kommandoer i WSL eller i Windows ved hjælp af den samme Docker-daemon og billeder.\n• Du kan dele filer og mapper mellem Windows og Linux uden problemer ved hjælp af automatisk indsættelse af Windows-drev i WSL.\n• Du kan bruge dine foretrukne Windows-værktøjer og -editorer til at arbejde på Linux-kode og -filer og omvendt takket være WSL's interoperabilitet.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker-skrivebordsintegration</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om brug af WSL med Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Med WSL (Windows-undersystem til Linux) kan du køre dine foretrukne Linux-værktøjer, -hjælpeprogrammer, -programmer og -arbejdsprocesser direkte på Windows.\n\nBrug et øjeblik på at få vist nogle af communityets favoritfunktioner, eller se vores omfattende dokumentation.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Velkommen til WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Bedste fremgangsmåder til konfiguration</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Introduktion med Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentation til Windows-undersystem til Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Du kan bruge grafiske baserede Linux-programmer med oprindelige Windows-interaktioner som f.eks. alt-Tabulator, start af menuen, fastgørelse af proceslinjen og understøttelse af klip og sæt ind.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>GUI Apps</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL kan udnytte din Windows GPU til Machine Learning arbejdsprocesser\n\nBinære Linux-filer, der kører i WSL, kan automatisk bruge din GPU i Windows til at øge ydeevnen. Du skal blot installere og køre disse arbejdsprocesser på samme måde som på en almindelig Linux-maskine. Der er mange forskellige måder at komme i gang på, fra at køre CUDA i en docker-objektbeholder, hvis du har et NVIDIA-grafikkort, til at køre PyTorch eller TensorFlow med DirectML på dit AMD-, Intel- eller NVIDIA-grafikkort. Se vores introduktionsguide nedenfor for at få mere at vide.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>GPU-acceleration</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om GPU-acceleration</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om WSL GUI-apps</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Her er en liste over apps, som du kan prøve (Du kan installere alle disse i Ubuntu ved at skrive 'sudo apt install &lt;The App Name&gt;')\n\n    • gedit – Basic teksteditor\n    • audacity – Optag og rediger lydfiler\n    • blender – Lav 3D-animationer og visualiseringer\n    • gimp – Rediger fotos\n    • nautilus – A Linux-stifinder\n    • vlc – Video-afspiller</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Du kan nemt få adgang til netværksapps på tværs af Windows- og Linux-operativsystemer.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Netværksintegration</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om netværksprogrammer</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om netværk i spejlet tilstand</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Du kan bruge WSL som dit fuldtidsudviklingsmiljø direkte fra VS-kode.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS-kodeintegration</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om brug af WSL med VS-kode</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Sådan installeres</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Når du har installeret VS-kode, kan du installere Fjern-WSL-udvidelsen fra Windows Terminal:\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Åbn et WSL-projekt i Visual Studio-kode</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Hvis du vil åbne et projekt i VS-kode fra WSL-distributionen, skal du åbne distributionens kommandolinje og køre 'code .' for at åbne en projektfil.\n\nDu kan også få adgang til flere Fjernindstillinger for VS-kode via kommandopaletten i selve VS-koden. Tryk på \"SKIFT+CTRL+P\" på tastaturet for at åbne kommandopaletten, og skriv \"Remote-WSL\" for at få vist en liste over tilgængelige Fjernindstillinger for VS-kode, så du kan åbne mappen igen i en fjernsession, angive, hvilken distribution du vil åbne og meget mere.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Sådan bruges det</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Valgfrie funktioner</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Erklæring om beskyttelse af oplys.</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Processorantal</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Hvor mange logiske processorer der skal tildeles til WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Nulstil antal</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Processorantal</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hvor mange logiske processorer der skal tildeles til WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Relaterede links</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Produktbemærkninger</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Aktivér fejlsikret tilstand</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Kør WSL i \"Fejlsikret tilstand\", som deaktiverer mange funktioner og er beregnet til at blive brugt til at genoprette distributioner, der er i beskadigede tilstande.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivér fejlsikret tilstand.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Kør WSL i \"Fejlsikret tilstand\", som deaktiverer mange funktioner og er beregnet til at blive brugt til at genoprette distributioner, der er i beskadigede tilstande.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Om</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Softwareudvikler</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Distro-administration</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Docker-skrivebordsintegration</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Filsystem</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Generelt</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>GPU-acceleration</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>GUI Apps</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Start wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Hukommelse og processor</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Netværk</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Netværksintegration</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Velkommen til WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Valgfrie funktioner</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Indstillinger</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS-kodeintegration</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Nyheder</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Arbejder på tværs af filsystemer</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problemer</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Aktivér sparse virtuel harddisk som standard</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Enhver nyoprettet VHD indstilles til sparse automatisk, når den aktiveres.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivér sparse virtuel harddisk som standard.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Enhver nyoprettet VHD indstilles til sparse automatisk, når den aktiveres.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Byt om på filplacering</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>En absolut Windows-sti til ombytningen af den virtuelle harddisk.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Gennemse swapfiler</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Byt om på filplacering</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En absolut Windows-sti til ombytningen af den virtuelle harddisk.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Swap-størrelse</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Hvor meget swapplads, der skal føjes til VM'en WSL 2, 0 for ingen swapfil. Swaplageret er diskbaseret RAM, der bruges, når hukommelsesefterspørgslen overskrider grænsen på hardwareenheden.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Nulstil størrelse</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Swap-størrelse</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hvor meget swapplads skal der tilføjes til WSL 2 VM. 0 for en swapfil. Swaplageret er diskbaseret RAM, der bruges, når hukommelsesefterspørgslen overskrider grænsen på hardwareenheden.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Aktivér VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Brug virtiofs i stedet for plan 9 til at få adgang til værtsfiler, hvilket øger hastigheden.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Aktivér Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Aktiverer tilslutning af 9P-filsystemet fra værten ved hjælp af virtio-transporten.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Timeout for inaktiv VM</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Det antal millisekunder, en VM er inaktiv, før den lukkes.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Nulstil timeout</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Timeout for inaktiv VM</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Det antal millisekunder, en VM er inaktiv, før den lukkes.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Byg, kør, foretag fejlfinding og profilér dine apps, der kører på WSL, fra Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Kør og foretag fejlfinding af dine apps på WSL fra Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om WSL i Visual Studio til .NET-udviklere</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Få mere at vide om Visual Studio og WSL til C++-udviklere</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Du kan nemt køre og foretage fejlfinding af dine .NET Core- og C++-apps på tværs af platforme i Linux uden at forlade Visual Studio ved hjælp af WSL (Windows-undersystem til Linux). Hvis du er udvikler på tværs af platforme, kan du bruge denne metode som en enkel måde at teste flere af dine destinationsmiljøer på.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studio-integration</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studio-integration</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/de-DE/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<root>\r\n  <!--\r\n    Microsoft ResX Schema\r\n\r\n    Version 2.0\r\n\r\n    The primary goals of this format is to allow a simple XML format\r\n    that is mostly human readable. The generation and parsing of the\r\n    various data types are done through the TypeConverter classes\r\n    associated with the data types.\r\n\r\n    Example:\r\n\r\n    ... ado.net/XML headers & schema ...\r\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\r\n    <resheader name=\"version\">2.0</resheader>\r\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\r\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\r\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\r\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\r\n    </data>\r\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\r\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r\n        <comment>This is a comment</comment>\r\n    </data>\r\n\r\n    There are any number of \"resheader\" rows that contain simple\r\n    name/value pairs.\r\n\r\n    Each data row contains a name, and value. The row also contains a\r\n    type or mimetype. Type corresponds to a .NET class that support\r\n    text/value conversion through the TypeConverter architecture.\r\n    Classes that don't support this are serialized and stored with the\r\n    mimetype set.\r\n\r\n    The mimetype is used for serialized objects, and tells the\r\n    ResXResourceReader how to depersist the object. This is currently not\r\n    extensible. For a given mimetype the value must be set accordingly:\r\n\r\n    Note - application/x-microsoft.net.object.binary.base64 is the format\r\n    that the ResXResourceWriter will generate, however the reader can\r\n    read any of the formats listed below.\r\n\r\n    mimetype: application/x-microsoft.net.object.binary.base64\r\n    value   : The object must be serialized with\r\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.soap.base64\r\n    value   : The object must be serialized with\r\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.bytearray.base64\r\n    value   : The object must be serialized into a byte array\r\n            : using a System.ComponentModel.TypeConverter\r\n            : and then encoded with base64 encoding.\r\n    -->\r\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\r\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\r\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\r\n      <xsd:complexType>\r\n        <xsd:choice maxOccurs=\"unbounded\">\r\n          <xsd:element name=\"metadata\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"assembly\">\r\n            <xsd:complexType>\r\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"data\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"resheader\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n        </xsd:choice>\r\n      </xsd:complexType>\r\n    </xsd:element>\r\n  </xsd:schema>\r\n  <resheader name=\"resmimetype\">\r\n    <value>text/microsoft-resx</value>\r\n  </resheader>\r\n  <resheader name=\"version\">\r\n    <value>2.0</value>\r\n  </resheader>\r\n  <resheader name=\"reader\">\r\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <resheader name=\"writer\">\r\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <data name=\"AppName\" xml:space=\"preserve\">\r\n    <value>Windows-Subsystem für Linux</value>\r\n  </data>\r\n  <data name=\"AppShortName\" xml:space=\"preserve\">\r\n    <value>WSL</value>\r\n  </data>\r\n  <data name=\"AppDescription\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem für Linux ermöglicht Entwicklern das Ausführen einer GNU/Linux-Umgebung -- einschließlich der meisten Befehlszeilentools, Hilfsprogramme und Anwendungen -- direkt unter Windows, unverändert, ohne den Mehraufwand einer herkömmlichen virtuellen Maschine oder einem dualen Boot-Setup.</value>\r\n  </data>\r\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Trennen des Datenträgers: {}. Führen Sie \"dmesg\" in WSL2 aus, um weitere Informationen zu erhalten.\r\nFühren Sie \"wsl.exe {}\" aus, um das Beenden und Trennen des Datenträgers durch WSL2 zu erzwingen.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\r\n    <value>Der Datenträger „{}“ ist bereits angeschlossen.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\r\n    <value>Das Volume ist bereits innerhalb von WSL2 bereitgestellt.</value>\r\n  </data>\r\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\r\n    <value>Ein Datenträger mit diesem Namen ist bereits bereitgestellt. Heben Sie die Bereitstellung des Datenträgers auf, oder wählen Sie einen neuen Namen aus, und versuchen Sie es nochmal.</value>\r\n  </data>\r\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\r\n    <value>Der angegebene Bereitstellungsname enthält ein ungültiges \"/\"-Zeichen. Wiederholen Sie den Vorgang ohne das ungültige Zeichen.</value>\r\n  </data>\r\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\r\n    <value>Der Datenträger wurde erfolgreich als \"/mnt/wsl/{}\" bereitgestellt.\r\nHinweis: Der Speicherort ist anders, wenn Sie die Einstellung \"automount.root\" in \"/etc/wsl.conf\" geändert haben.\r\nUm die Bereitstellung des Datenträgers aufzuheben und zu trennen, führen Sie \"wsl.exe {} {}\" aus.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\r\n    <value>Der Datenträger wurde angefügt, konnte aber nicht eingebunden werden: {}.\r\nFühren Sie \"dmesg\" in WSL2 aus, um weitere Informationen zu erhalten.\r\nFühren Sie \"wsl.exe {} {}\" aus, um den Datenträger zu trennen.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\r\n    <value>Im Folgenden finden Sie eine Liste gültiger Distributionen, die installiert werden können.\r\nMit \"wsl.exe {} &lt;Distro&gt;\" installieren.\r\n</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\r\n    <value>Der Distributionsname wurde bereits festgelegt.</value>\r\n  </data>\r\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\r\n    <value>Der angegebene Installationsspeicherort wird bereits verwendet.</value>\r\n  </data>\r\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\r\n    <value>Eine Distribution mit dem angegebenen Namen ist bereits vorhanden. Verwenden Sie --name , um einen anderen Namen zu wählen.</value>\r\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\r\n    <value>Es ist keine Distribution mit dem angegebenen Namen vorhanden.</value>\r\n  </data>\r\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\r\n    <value>Zum Bereitstellen eines Datenträgers ist Administratorzugriff erforderlich.</value>\r\n  </data>\r\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Exportieren der Distribution.</value>\r\n  </data>\r\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\r\n    <value>Es wird ein einmaliges Upgrade des Windows-Subsystem für Linux-Dateisystems dieser Distribution ausgeführt...\r\n</value>\r\n  </data>\r\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\r\n    <value>Es kann nicht gestartet werden, da eine andere Instanz ohne erhöhte Rechte ausgeführt wird. Es ist nicht zulässig, Instanzen mit erhöhten Rechten und ohne erhöhte Rechte gleichzeitig auszuführen.\r\n</value>\r\n  </data>\r\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Importieren der Distribution.</value>\r\n  </data>\r\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\r\n    <value>Optionale Windows-Komponente wird installiert: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\r\n    <value>Herunterladen: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\r\n    <value>Wird installiert: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\r\n    <value>Distribution importieren</value>\r\n  </data>\r\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\r\n    <value>{} wurde heruntergeladen.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\r\n    <value>Windows-Subsystem für Linux setzt eine vorherige Installation... fort.</value>\r\n  </data>\r\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\r\n    <value>Ungültiger Distributionsname: '{}'.\r\nUm eine Liste gültiger Distributionen abzurufen, verwenden Sie \"wsl.exe --list --online'.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\r\n    <value>Die Windows-Subsystem für Linux Instanz wurde beendet.</value>\r\n  </data>\r\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\r\n    <value>Ungültiges Befehlszeilenargument: {}\r\nVerwenden Sie \"{}\" --help', um eine Liste der unterstützten Argumente abzurufen.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\r\n    <value>Für das Befehlszeilenargument „{}“ ist ein Wert erforderlich.\r\nBitte verwenden Sie '{} --help', um eine Liste der unterstützten Argumente abzurufen.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\r\n    <value>Konsoleneinstellungen werden nicht unterstützt. Zur Verwendung dieses Features muss die Legacykonsole deaktiviert werden. \r\n</value>\r\n  </data>\r\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\r\n    <value>{} ist keine gültige ganze Zahl.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\r\n    <value>Für diese Distribution wird ein Installations-, Deinstallations- oder Konvertierungsvorgang ausgeführt.\r\n</value>\r\n  </data>\r\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\r\n    <value>{} wird gestartet...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\r\n    <value>Es kann nicht gestartet werden, da eine andere Instanz mit erhöhten Rechten ausgeführt wird. Es ist nicht zulässig, Instanzen mit erhöhten Rechten und ohne erhöhte Rechte gleichzeitig auszuführen.\r\n</value>\r\n  </data>\r\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\r\n    <value>Windows-Subsystem für Linux verfügt über keine installierten Distributionen.\r\nSie können dies beheben, indem Sie eine Distribution mit den folgenden Anweisungen installieren:\r\n\r\n\"wsl.exe --list --online' zum Auflisten verfügbarer Distributionen verwenden\r\nund \"wsl.exe --install &lt;Distro&gt;\".</value>\r\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\r\n    <value>Es werden keine Distributionen ausgeführt.</value>\r\n  </data>\r\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\r\n    <value>{} (Standard)</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem für Linux-Distributionen:</value>\r\n  </data>\r\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\r\n    <value>Standarddistribution: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\r\n    <value>Standardversion: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\r\n    <value>Registrierung wird aufgehoben.</value>\r\n  </data>\r\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\r\n    <value>Benutzer nicht gefunden.</value>\r\n  </data>\r\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\r\n    <value>Informationen zu den wichtigsten Unterschieden zu WSL 2 finden Sie unter https://aka.ms/wsl2\r\n</value>\r\n  </data>\r\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\r\n    <value>Konvertierung wird ausgeführt. Dieser Vorgang kann einige Minuten dauern.</value>\r\n  </data>\r\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\r\n    <value>Die Distribution ist bereits die angeforderte Version.</value>\r\n  </data>\r\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\r\n    <value>WSL 2 wird von der Legacy Distribution nicht unterstütztt.</value>\r\n  </data>\r\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\r\n    <value>WSL2 kann nicht gestartet werden, da die Virtualisierung auf diesem Computer nicht aktiviert ist.\r\nStellen Sie sicher, dass die optionale Komponente „VM-Plattform“ aktiviert ist und die Virtualisierung in den Firmwareeinstellungen Ihres Computers eingeschaltet ist.\r\n\r\nAktivieren Sie „VM-Plattform“, indem Sie folgenden Befehl ausführen: wsl.exe --install --no-distribution\r\n\r\nWeitere Informationen finden Sie unter https://aka.ms/enablevirtualization</value>\r\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\r\n    <value>Es sind zu viele virtuelle Festplatten oder physische Datenträger angefügt.</value>\r\n  </data>\r\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\r\n    <value>Die VHD wurde bereits über wsl.exe --mount, bereitgestellt. Heben Sie die Bereitstellung des Datenträgers vor dem Start auf.</value>\r\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\r\n    <value>WSL1 wird mit Ihrer aktuellen Computer-Konfiguration nicht unterstützt.\r\nAktivieren Sie bitte die optionale Komponente „Windows Subsystem für Linux“, um WSL1 zu verwenden.</value>\r\n  </data>\r\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\r\n    <value>Dieser Vorgang wird nur von WSL2 unterstützt.</value>\r\n  </data>\r\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\r\n    <value>Syntax:\r\n    --networking-mode\r\n       Zeigt den aktuellen Netzwerkmodus an.\r\n\r\n    --msal-proxy-path\r\n        Zeigt den Pfad zur MSAL-Proxyanwendung an.\r\n\r\n    --vm-id\r\n        Zeigt die WSL-VM-ID an.\r\n\r\n    --version\r\n        Zeigt die Version des WSL-Pakets an.\r\n\r\n    -n\r\n        Keinen Zeilenumbruch drucken.</value>\r\n    <comment>{Locked=\"--networking-mode\r\n\"}{Locked=\"--msal-proxy-path\r\n\"}{Locked=\"--vm-id\r\n\"}{Locked=\"--version\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\r\n    <value>Verwendung:\r\n    -a\r\n        Ergebnis im Format mit absolutem Pfad erzwingen.\r\n    -u\r\n        Windows-Pfad in WSL-Pfad umwandeln (Standard).\r\n    -w\r\n        WSL-Pfad in Windows-Pfad umwandeln.\r\n    -m\r\n        WSL-Pfad in Windows-Pfad umwandeln, mit \"/\" anstelle von \"\\\\\".\r\n\r\nBeispiel: wslpath 'c:\\\\users'</value>\r\n    <comment>{Locked=\"-a\r\n\"}{Locked=\"-u\r\n\"}{Locked=\"-w\r\n\"}{Locked=\"-m\r\n\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\r\n    <value>Führt Verwaltungsvorgänge für Windows-Subsystem für Linux\raus\n\r\nVerbrauch:\r\n    /l, /list [Option]\r\n        Listet registrierte Distributionen auf.\r\n       /all – Optional alle Distributionen auflisten, einschließlich Distributionen, die\r\n               zurzeit installiert oder deinstalliert werden.\r\n\r\n        /running – Listet nur Distributionen auf, die derzeit ausgeführt werden.\r\n\r\n    /s, /setdefault &lt;DistributionName&gt;\r\n        Legt die Distribution als Standard fest.\r\n\r\n    /t, /terminate &lt;DistributionName&gt;\r\n        Beendet die Distribution.\r\n\r\n    /u, /unregister &lt;DistributionName&gt;\r\n        Hebt die Registrierung der Distribution auf und löscht das Stammdateisystem.</value>\r\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\r\n    <value>Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten.\r\nDatenschutzinformationen zu diesem Produkt finden Sie unter https://aka.ms/privacy.\r\n\r\nSyntax: wsl.exe [Argument] [Optionen...] [CommandLine]\r\n\r\nArgumente für die Ausführung von Linux-Binärdateien:\r\n\r\n    Wenn keine Befehlszeile angegeben ist, startet wsl.exe die Standardshell.\r\n\r\n    --exec, -e &lt;CommandLine&gt;\r\n        Den angegebenen Befehl ausführen, ohne die Linux-Standardshell zu verwenden.\r\n\r\n    --shell-type &lt;standard|login|none&gt;\r\n        Den angegebenen Befehl mit dem angegebenen Shelltyp ausführen.\r\n\r\n    --\r\n        Die verbleibende Befehlszeile unverändert übergeben.\r\n\r\nOptionen:\r\n    --cd &lt;Directory&gt;\r\n        Legt das angegebene Verzeichnis als aktuelles Arbeitsverzeichnis fest.\r\n        Wenn ~ verwendet wird, wird der Heimatpfad des Linux-Benutzers verwendet. Wenn der Pfad\r\n        mit einem /-Zeichen beginnt, wird er als absoluter Linux-Pfad interpretiert.\r\n        Andernfalls muss der Wert ein absoluter Windows-Pfad sein.\r\n\r\n    --distribution, -d &lt;DistroName&gt;\r\n        Die angegebene Verteilung ausführen.\r\n\r\n    --distribution-id &lt;DistroGuid&gt;\r\n        Die angegebene Verteilungs-ID ausführen.\r\n\r\n    --user, -u &lt;UserName&gt;\r\n        Als angegebener Benutzer ausführen.\r\n\r\n    --system\r\n        Startet eine Shell für die Systemverteilung.\r\n\r\nArgumente für die Verwaltung des Windows-Subsystems für Linux:\r\n\r\n    --help\r\n        Nutzungsinformationen anzeigen.\r\n\r\n    --debug-shell\r\n        Eine WSL2-Debugshell zu Diagnosezwecken öffnen.\r\n\r\n    --install [Verteilung] [Optionen...]\r\n        Ein Windows-Subsystem für die Linux-Verteilung installieren.\r\n        Verwenden Sie 'wsl.exe --list --online', um eine Liste gültiger Verteilungen zu erhalten.\r\n\r\n        Optionen:\r\n            --enable-wsl1\r\n                WSL1-Unterstützung aktivieren.\r\n\r\n            --fixed-vhd\r\n                Erstellen Sie einen Datenträger mit fester Größe, um die Verteilung zu speichern.\r\n\r\n            --from-file &lt;Path&gt;\r\n                Eine Verteilung aus einer lokalen Datei installieren.\r\n\r\n            --legacy\r\n                Das Legacy-Verteilungsmanifest verwenden.\r\n\r\n            --location &lt;Location&gt;\r\n                Den Installationspfad für die Verteilung festlegen.\r\n\r\n            --name &lt;Name&gt;\r\n                Den Namen der Verteilung festlegen.\r\n\r\n            --no-distribution\r\n                Nur die erforderlichen optionalen Komponenten und keine Distribution installieren.\r\n\r\n            --no-launch, -n\r\n                Starten Sie die Distribution nach der Installation nicht.\r\n\r\n            --version &lt;Version&gt;\r\n                Gibt die Version an, die für die neue Distribution verwendet werden soll.\r\n\r\n            --vhd-size &lt;MemoryString&gt;\r\n                Gibt die Größe des Datenträgers an, auf dem die Verteilung gespeichert werden soll.\r\n\r\n            --web-download\r\n                Laden Sie die Distribution aus dem Internet und nicht aus dem Microsoft Store herunter.\r\n\r\n    --manage &lt;Distro&gt; &lt;Options...&gt;\r\n        Ändert distributionsspezifische Optionen.\r\n\r\n        Optionen:\r\n            --move &lt;Location&gt;\r\n                Verschieben Sie die Distribution an einen neuen Speicherort.\r\n\r\n            --set-sparse, -s &lt;true|false&gt;\r\n                Stellen Sie die VHDX der Distribution auf „Geringe Dichte“ ein, damit Speicherplatz automatisch zurückgewonnen werden kann.\r\n\r\n            --set-default-user &lt;Username&gt;\r\n                Den Standardbenutzer der Verteilung festlegen.\r\n\r\n            --resize &lt;MemoryString&gt;\r\n                Passen Sie die Größe des Datenträgers der Distribution auf die angegebene Größe an.\r\n\r\n    --mount &lt;Disk&gt;\r\n        Fügt einen physischen oder virtuellen Datenträger an alle WSL2-Distributionen an und bindet ihn ein.\r\n\r\n        Optionen:\r\n            --vhd\r\n                Gibt an, dass &lt;Disk&gt; auf eine virtuelle Festplatte verweist.\r\n\r\n            --bare\r\n                Fügen Sie den Datenträger an WSL2 an, aber binden Sie ihn nicht ein.\r\n\r\n            --name &lt;Name&gt;\r\n                Binden Sie den Datenträger unter Verwendung eines benutzerdefinierten Namens für den Einbindepunkt ein.\r\n\r\n            --type &lt;Type&gt;\r\n                Beim Einbinden eines Datenträgers zu verwendendes Dateisystem. Wenn nicht angegeben, wird standardmäßig ext4 verwendet.\r\n\r\n            --options &lt;Options&gt;\r\n                Zusätzliche Optionen zum Einbinden.\r\n\r\n            --partition &lt;Index&gt;\r\n                Der Index der einzubindenden Partition. Sofern nicht angegeben, wird standardmäßig der gesamte Datenträger verwendet.\r\n\r\n    --set-default-version &lt;Version&gt;\r\n        Ändert die Standardinstallationsversion für neue Distributionen.\r\n\r\n    --shutdown\r\n        Beendet sofort alle ausgeführten Distributionen und die WSL 2.\r\n        virtueller Computer mit einfachem Hilfsprogramm.\r\n\r\n        Optionen:\r\n            --force\r\n                Beenden Sie den virtuellen WSL 2-Computer, auch wenn gerade ein Vorgang ausgeführt wird. Kann zu Datenverlust führen.\r\n\r\n    --status\r\n        Zeigt den Status des Windows-Subsystems für Linux an.\r\n\r\n    --unmount [Datenträger]\r\n        Demountet und trennt einen Datenträger von allen WSL2-Distributionen.\r\n        Demountet und trennt alle Datenträger, wenn der Aufruf ohne Argument erfolgt.\r\n\r\n    --uninstall\r\n        Deinstalliert das Paket „Windows-Subsystem für Linux“ von diesem Computer.\r\n\r\n    --update\r\n        Aktualisieren Sie das Paket „Windows-Subsystem für Linux.“\r\n\r\n        Optionen:\r\n            --pre-release\r\n                Laden Sie gegebenenfalls eine Vorabversion herunter.\r\n\r\n    --version, -v\r\n        Versionsinformationen anzeigen.\r\n\r\nArgumente für die Verwaltung von Distributionen in Windows-Subsystem für Linux:\r\n\r\n    --export &lt;Distro&gt; &lt;FileName&gt; [Optionen]\r\n        Exportiert die Distribution in eine TAR-Datei.\r\n        Der Dateiname kann „-“ für stdout sein.\r\n\r\n        Optionen:\r\n            --format &lt;Format&gt;\r\n                Gibt das Exportformat an. Unterstützte Werte: tar, tar.gz, tar.xz, vhd.\r\n\r\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Optionen]\r\n        Importiert die angegebene TAR-Datei als neue Distribution.\r\n        Der Dateiname kann „-“ für stdin sein.\r\n\r\n        Optionen:\r\n            --version &lt;Version&gt;\r\n                Gibt die Version an, die für die neue Distribution verwendet werden soll.\r\n\r\n            --vhd\r\n                Gibt an, dass es sich bei der bereitgestellten Datei um eine VHDX-Datei handelt, nicht um eine TAR-Datei.\r\n                Dieser Vorgang erstellt eine Kopie der VHDX-Datei am angegebenen Installationsspeicherort.\r\n\r\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\r\n        Importiert die angegebene VHDX-Datei als neue Distribution.\r\n        Diese virtuelle Festplatte muss mit dem Dateisystemtyp EXT4 formatiert sein.\r\n\r\n    --list, -l [Optionen]\r\n        Listet Distributionen auf.\r\n\r\n        Optionen:\r\n            --all\r\n                Listet alle Distributionen auf, einschließlich Distributionen, die\r\n                derzeit installiert oder deinstalliert werden.\r\n\r\n            --running\r\n                Listet nur Distributionen auf, die derzeit ausgeführt werden.\r\n\r\n            --quiet, -q\r\n                Nur Distributionsnamen anzeigen.\r\n\r\n            --verbose, -v\r\n                Detaillierte Informationen zu allen Distributionen anzeigen.\r\n\r\n            --online, -o\r\n                Zeigt eine Liste der verfügbaren Distributionen für die Installation mit 'wsl.exe --install' an.\r\n\r\n    --set-default, -s &lt;Distro&gt;\r\n        Legt die Distribution als Standard fest.\r\n\r\n    --set-version &lt;Distro&gt; &lt;Version&gt;\r\n        Ändert die Version der angegebenen Distribution.\r\n\r\n    --terminate, -t &lt;Distro&gt;\r\n        Beendet die angegebene Distribution.\r\n\r\n    --unregister &lt;Distro&gt;\r\n        Hebt die Registrierung der Distribution auf und löscht das Stammdateisystem.</value>\r\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\r\n\"}{Locked=\"--help\r\n\"}{Locked=\"--debug-shell\r\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\r\n\"}{Locked=\"--fixed-vhd\r\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\r\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\r\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\r\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\r\n\"}{Locked=\"--bare\r\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\r\n\"}{Locked=\"--force\r\n\"}{Locked=\"--status\r\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\r\n\"}{Locked=\"--update\r\n\"}{Locked=\"--pre-release\r\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\r\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\r\n\"}{Locked=\"--running\r\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\r\n    <value>WSL-Version: {}\r\nKernelversion: {}\r\nWSLg-Version: {}\r\nMSRDC-Version: {}\r\nDirect3D-Version: {}\r\nDXCore-Version: {}\r\nWindows-Version: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\r\n    <value>MSBuild-Version: {}\r\nCommit: {}\r\nBuildzeit: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\r\n    <value>Der in „{}“ angegebene benutzerdefinierte Kernel wurde nicht gefunden: „{}“.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\r\n    <value>Die benutzerdefinierte Kernelmodule-VHD in {} wurde nicht gefunden: „{}“.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\r\n    <value>Die in {} angegebene benutzerdefinierte Systemdistribution wurde nicht gefunden oder weist nicht das richtige Format auf.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\r\n    <value>Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten.\r\nDatenschutzinformationen zu diesem Produkt finden Sie unter https://aka.ms/privacy.\r\n\r\nSyntax: wslg.exe [Argument] [Optionen...] [Befehlszeile]\r\n\r\nArgumente:\r\n    --cd &lt;Directory&gt;\r\n        Legt das angegebene Verzeichnis als aktuelles Arbeitsverzeichnis fest.\r\n        Wenn ~ verwendet wird, wird der Heimatpfad des Linux-Benutzers verwendet. Wenn der Pfad\r\n        mit einem /-Zeichen beginnt, wird er als absoluter Linux-Pfad interpretiert.\r\n        Andernfalls muss der Wert ein absoluter Windows-Pfad sein.\r\n\r\n    --distribution, -d &lt;Distro&gt;\r\n        Führen Sie die angegebene Distribution aus.\r\n\r\n    --user, -u &lt;UserName&gt;\r\n        Als angegebener Benutzer ausführen.\r\n\r\n    --shell-type &lt;standard|login|none&gt;\r\n        Führen Sie den angegebenen Befehl mit dem angegebenen Shelltyp aus.\r\n\r\n    --help\r\n        Zeigen Sie Nutzungsinformationen an.\r\n\r\n    --\r\n        Übergeben Sie die verbleibende Befehlszeile unverändert.</value>\r\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\r\n    <value>Drücken Sie eine beliebige Taste, um den Vorgang fortzusetzen...</value>\r\n  </data>\r\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\r\n    <value>Im Argument {} fehlt ein erforderlicher Parameter.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\r\n    <value>Export wird ausgeführt. Dieser Vorgang kann einige Minuten dauern.</value>\r\n  </data>\r\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\r\n    <value>Import wird ausgeführt. Dieser Vorgang kann einige Minuten dauern.</value>\r\n  </data>\r\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\r\n    <value>Die Unterstützung von GUI-Anwendungen ist über {} oder /etc/wsl.conf deaktiviert.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\r\n    <value>Es wird nach Updates gesucht.</value>\r\n  </data>\r\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\r\n    <value>Diese Distribution ist nur im Microsoft Store verfügbar.</value>\r\n  </data>\r\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\r\n    <value>wsl.exe --mount auf ARM64 erfordert Windows 27653 oder höher.</value>\r\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\r\n    <value>Die neueste Version von Windows-Subsystem für Linux ist bereits installiert.</value>\r\n  </data>\r\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\r\n    <value>Windows-Subsystem für Linux wird auf Version {} aktualisiert.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\r\n    <value>Diese Anwendung erfordert die Windows-Subsystem für Linux optionale Komponente.\r\nInstallieren Sie das System durch Ausführen von \": wsl.exe --install --no-distribution\r\nDas System muss möglicherweise neu gestartet werden, damit die Änderungen wirksam werden.</value>\r\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\r\n    <value>Die angegebene Datei muss die Dateierweiterung {} aufweisen.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\r\n    <value>Die angegebene Datei muss die Dateierweiterung {} oder {} besitzen.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\r\n    <value>VmSwitch „{}“ wurde nicht gefunden. Verfügbare Switches: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\r\n    <value>Für Bridged Networking muss wsl2.vmSwitch eingestellt sein.</value>\r\n  </data>\r\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\r\n    <value>Fehler beim Abrufen der Verteilerliste von '{}'. {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\r\n    <value>Fehler beim Anfügen des Datenträgers \"{}\" an WSL2: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\r\n    <value>Fehler beim Ändern der Größe des Datenträgers.</value>\r\n  </data>\r\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\r\n    <value>Kein Wert gefunden.</value>\r\n  </data>\r\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\r\n    <value>Die verpackte Version von Windows-Subsystem für Linux wird von Windows, Version {}, nicht unterstützt.\r\nInstallieren Sie das erforderliche Update über Windows Update oder über: {}\r\nWeitere Informationen finden Sie unter https://aka.ms/wslinstall</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\r\n    <value>Zum Ausführen der Debug-Shell muss wsl.exe als Administrator ausgeführt werden.</value>\r\n  </data>\r\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Installationsprozess für die Distribution „{}“. Exitcode: {}.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\r\n    <value>Ungültiges GUID-Format: „{}“</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\r\n    <value>Ungültiger Abschnittsname in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\r\n    <value>Ungültiger Schlüsselname in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\r\n    <value>{} in {}:{} erwartet</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\r\n    <value>Ungültiges Escapezeichen: „{}“ in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\r\n    <value>Unbekannter Schlüssel „{}“ in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\r\n    <value>Ungültiger ganzzahliger Wert \"{}\" für Schlüssel \"{}\" in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\r\n    <value>Ungültiger IP-Wert '{}' für Schlüssel '{}' in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\r\n    <value>Ungültiger boolescher Wert \"{}\" für Schlüssel \"{}\" in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\r\n    <value>Ungültige Mac-Adresse \"{}\" für Schlüssel \"{}\" in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\r\n    <value>Fehler beim Öffnen der Konfigurationsdatei: {}, {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\r\n    <value>Fehler beim WSL-Start.</value>\r\n  </data>\r\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\r\n    <value>Starten der systemd-Benutzersitzung für „{}“ fehlgeschlagen. Weitere Details finden Sie in journalctl.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\r\n    <value>Open EventViewer</value>\r\n  </data>\r\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\r\n    <value>Diese Distribution enthält keinen Standardnamen. Verwenden Sie --name , um den Distributionsnamen zu wählen.</value>\r\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\r\n    <value>Die Argumente {} und {} können nicht gleichzeitig angegeben werden.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\r\n    <value>Das Argument {} erfordert das {}-Argument.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\r\n    <value>Ungültiger Distributionsname: „{}“.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\r\n    <value>Es wird die Legacy-Verteilungsregistrierung verwendet. Erwägen Sie, stattdessen eine TAR-basierte Distribution zu verwenden.</value>\r\n  </data>\r\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\r\n    <value>Ältere Distributionsregistrierungen unterstützen das Argument --version nicht.</value>\r\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\r\n    <value>Die Distribution \"{}\" wird durch ein Überschreibungsmanifest bereitgestellt.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\r\n    <value>Der Distributionshash stimmt nicht überein. Erwartet: {}, tatsächlicher Hash: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\r\n    <value>Ungültige Hex-Zeichenfolge: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\r\n    <value>„{}“ wird bei der Installation von Legacy-Distributionen nicht unterstützt.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\r\n    <value>Die Distribution wurde erfolgreich installiert. Sie kann über \"wsl.exe -d {}\" gestartet werden.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\r\n    <value>Ungültige Speicherzeichenfolge \"{}\" für .wslconfig Eintrag \"{}\" in \"{}:{}\".</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\r\n    <value>Fehler beim Erstellen des Austauschdatenträgers in \"{}\": {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\r\n    <value>Geschachtelte Virtualisierung wird auf diesem Computer nicht unterstützt.</value>\r\n  </data>\r\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\r\n    <value>Fehler beim Erstellen des Netzwerkendpunkts mit folgender Adresse: \"{}\", zugewiesene neue Adresse: \"{}\"</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\r\n    <value>Fehler beim Erstellen des virtuellen Netzwerks mit dem Adressbereich \"{}\". Es wurde ein neues Netzwerk mit folgendem Bereich erstellt: \"{}\", {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\r\n    <value>ABGESICHERTER MODUS AKTIVIERT – viele Features werden deaktiviert</value>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\r\n    <value>Der gespiegelte Netzwerkmodus wird nicht unterstützt: {}.\r\nFallback auf NAT-Netzwerk.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\r\n    <value>Linux-Kernel, Version 5.10 oder höher, ist erforderlich.</value>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\r\n    <value>Windows-Version {}. {} verfügt nicht über die erforderlichen Features.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\r\n    <value>Hyper-V-Firewall wird nicht unterstützt</value>\r\n  </data>\r\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\r\n    <value>DNS-Tunneln wird nicht unterstützt</value>\r\n  </data>\r\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\r\n    <value>Die Einstellung \"wsl2.localhostForwarding\" hat keine Auswirkungen, wenn der gespiegelte Netzwerkmodus verwendet wird.</value>\r\n  </data>\r\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\r\n    <value>Auf dem Host wurde eine Http-Proxy-Änderung erkannt. Bitte starten Sie WSL neu, um die Änderung anzuwenden.</value>\r\n  </data>\r\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\r\n    <value>Eine Localhost-Proxykonfiguration wurde erkannt, aber nicht in WSL gespiegelt. WSL im NAT-Modus unterstützt keine Localhost-Proxys.</value>\r\n  </data>\r\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\r\n    <value>Eine IPv6-Proxykonfiguration wurde erkannt, aber nicht in WSL gespiegelt. WSL im NAT-Modus unterstützt IPv6 nicht.</value>\r\n  </data>\r\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\r\n    <value>Eine Localhost-IPv6-Proxykonfiguration wurde erkannt, aber nicht in WSL gespiegelt. WSL unterstützt keine Localhost-IPv6-Proxys.</value>\r\n  </data>\r\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\r\n    <value>Ein unerwarteter Fehler ist beim Auflösen der Proxyeinstellungen aufgetreten. Die Proxyeinstellungen wurden nicht in WSL gespiegelt.</value>\r\n  </data>\r\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\r\n    <value>Fehler beim Zugriff auf die Registrierung. Pfad: '{}'. Fehler: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Starten des localhost-Relayprozesses. Fehler: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\r\n    <value>Fehler beim Starten des virtuellen Netzwerks. Installieren Sie die optionale Komponente „Virtual Machine Platform“, indem Sie „wsl.exe --install --no-distribution“ ausführen.</value>\r\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\r\n    <value>Fehler beim Konfigurieren des Netzwerks (networkingMode {}). Fallback auf networkingMode {}.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Aktivieren der Windows-Komponente \"{}\" (Exitcode {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\r\n    <value>Vom Plug-In „{}“ wurde ein schwerwiegender Fehler zurückgegeben</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\r\n    <value>Das Plug-In \"{}\" hat einen schwerwiegenden Fehler zurückgegeben. Fehlermeldung: \"{}\"</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\r\n    <value>Das Plug-In \"{}\" erfordert eine neuere Version von WSL. Führen Sie \": wsl.exe --update\" aus.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\r\n    <value>Die .wslconfig-Einstellung „{}“ ist durch die Computerrichtlinie deaktiviert.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\r\n    <value>Die Debugshell ist durch die Computerrichtlinie deaktiviert.</value>\r\n  </data>\r\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\r\n    <value>wsl.exe --mount ist durch die Computerrichtlinie deaktiviert.</value>\r\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\r\n    <value>WSL1 ist durch die Computerrichtlinie deaktiviert.</value>\r\n  </data>\r\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\r\n    <value>Führen Sie \"wsl.exe --set-version {} 2\" aus, um ein Upgrade auf WSL2 durchzuführen.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\r\n    <value>WSL beendet ein Upgrade...</value>\r\n  </data>\r\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Aktualisieren (Exitcode: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\r\n    <value>Fehler bei der Deinstallation (Exitcode: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\r\n    <value>Protokolldatei: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\r\n    <value>Fehler beim Entfernen des MSIX-Pakets (Fehler: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\r\n    <value>Fehler beim Installieren des MSIX-Pakets (Fehler: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\r\n    <value>Optionale Komponenten, die zum Ausführen von WSL erforderlich sind, sind nicht installiert.</value>\r\n  </data>\r\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\r\n    <value>Fehlende Komponenten installieren.</value>\r\n  </data>\r\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\r\n    <value>Die importierte Datei ist keine gültige Linux-Distribution.</value>\r\n  </data>\r\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\r\n    <value>Drücken Sie eine beliebige Taste, um den Vorgang zu beenden...</value>\r\n  </data>\r\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\r\n    <value>Eine neue Version von Windows-Subsystem für Linux ist verfügbar.</value>\r\n  </data>\r\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\r\n    <value>Aktualisieren Sie auf Version {}, oder zeigen Sie unten die Versionshinweise an.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\r\n    <value>Update</value>\r\n  </data>\r\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\r\n    <value>Dokumente anzeigen</value>\r\n  </data>\r\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\r\n    <value>Der Vorgang konnte nicht abgeschlossen werden, da die VHD zurzeit verwendet wird. So erzwingen Sie, dass WSL die Verwendung beendet: wsl.exe --shutdown</value>\r\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\r\n    <value>{} ist kein gültiger boolescher Wert, &lt;true|false&gt;</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\r\n    <value>VHD mit geringer Dichte wird nur unter WSL2 unterstützt.</value>\r\n  </data>\r\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\r\n    <value>Das Ausführen von WSL als lokales System wird nicht unterstützt.</value>\r\n  </data>\r\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\r\n    <value>Leistungstipp:</value>\r\n  </data>\r\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\r\n    <value>Die Verwendung eines E/A-intensive Vorgangs wie {} auf Ihren Windows-Laufwerken führt zu schlechter Leistung. Erwägen Sie, Ihre Projektdateien in das Linux-Dateisystem zu verschieben, um die Leistung zu verbessern. Klicken Sie unten, um weitere Informationen zu erhalten.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\r\n    <value>Dokumente anzeigen</value>\r\n  </data>\r\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\r\n    <value>Nicht mehr anzeigen</value>\r\n  </data>\r\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\r\n    <value>Der virtuelle WSL2-Computer ist abgestürzt.</value>\r\n  </data>\r\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\r\n    <value>Die Stapelüberwachung wurde gespeichert in: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\r\n    <value>Fehler beim Starten der Distribution. Fehlercode: {}, Fehlerschritt: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\r\n    <value>Die Distribution konnte nicht gestartet werden, da der virtuelle Datenträger beschädigt ist.</value>\r\n  </data>\r\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\r\n    <value>Doppelter Konfigurationsschlüssel '{}' in {}:{} (widersprüchlicher Schlüssel: '{}' in {}:{})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\r\n    <value>Ungültiger Wert '{}' für den Konfigurationsschlüssel '{}' in {}:{} (Gültige Werte: {})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\r\n    <value>Warten auf den Abschluss des OOBE-Befehls für die Distribution \"{}\"...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\r\n    <value>Fehler beim Lesen der Eigenschaft „{}“ aus der Distribution {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\r\n    <value>Dies sieht wie eine VHD-Datei aus. Verwenden Sie „--vhd “, um eine VHD anstelle eines „tar“ zu importieren.</value>\r\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Installieren von {} aus dem Microsoft Store: {}\r\nEs wird versucht,...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\r\n    <value>Es wurde keine Standarddistribution konfiguriert. Geben Sie eine Zu installierende Distribution an.</value>\r\n  </data>\r\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\r\n    <value>wsl2.virtio9p ist deaktiviert und fällt mit vsock-Transport auf 9p zurück.</value>\r\n  </data>\r\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\r\n    <value>Die WSL-Installation scheint beschädigt zu sein (Fehlercode: {}).\r\nDrücken Sie eine beliebige Taste, um WSL zu reparieren, oder STRG-C, um den Vorgang abzubrechen.\r\nTimeout für diese Eingabeaufforderung in 60 Sekunden.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\r\n    <value>Fehler beim Analysieren des Terminalprofils beim Registrieren der Distribution: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\r\n    <value>Ungültige Größe: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\r\n    <value>{}\r\nFehlercode: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\r\n    <value>Ungültiges JSON-Dokument. Fehler beim Analysieren der Befehlszeile: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\r\n    <value>wsl2.processors darf die Anzahl logischer Prozessoren auf dem System nicht überschreiten ({} &gt; {})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\r\n    <value>Fehler beim Abfragen des Netzwerkmodus</value>\r\n  </data>\r\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\r\n    <value>Angepasste Kernelmodule wurden ohne Angabe eines angepassten Kernels bereitgestellt. Weitere Informationen finden Sie in https://aka.ms/wslcustomkernel.</value>\r\n  </data>\r\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\r\n    <value>Aufgrund eines aktuellen Kompatibilitätsproblems mit dem globalen Client für sicheren Zugriff ist DNS-Tunneling deaktiviert.</value>\r\n  </data>\r\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\r\n    <value>Die Unterstützung von Sparse VHD ist derzeit wegen möglicher Datenbeschädigung deaktiviert.\r\nUm eine Distribution zu zwingen, eine spärliche VHD zu verwenden, führen Sie bitte Folgendes aus:\r\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\r\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Bereitstellen von {}. Weitere Informationen finden Sie in der DMESG.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\r\n    <value>Fehler beim Verarbeiten von „/etc/fstab“ mit „mount -a“.</value>\r\n  </data>\r\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\r\n    <value>Fehler beim Einbinden des Distributionsdatenträgers. Er wurde schreibgeschützt als Fallback bereitgestellt.\r\nAnweisungen zur Wiederherstellung finden Sie unter: https://aka.ms/wsldiskmountrecovery</value>\r\n  </data>\r\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\r\n    <value>Fehler beim Übersetzen von „{}“</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\r\n    <value>Da hat etwas nicht geklappt, versuchen Sie es später noch einmal.</value>\r\n  </data>\r\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Info</value>\r\n  </data>\r\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\r\n    <value>Informationen zu Windows-Subsystem für Linux-Einstellungen</value>\r\n  </data>\r\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\r\n    <value>© Microsoft. Alle Rechte vorbehalten.</value>\r\n  </data>\r\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\r\n    <value>Windows-Subsystem für Linux-Einstellungen</value>\r\n  </data>\r\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\r\n    <value>Mit dem Windows-Subsystem für Linux-Einstellungen können Entwickler die .wslconfig-Datei mit einer GUI-basierten Anwendung verwalten.</value>\r\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\r\n    <value>Automatische Speicherrückbeanspruchung</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\r\n    <value>Gibt automatisch zwischengespeicherten Arbeitsspeicher frei, nachdem die Leerlauf-CPU-Auslastung erkannt wurde. Legen Sie diese Option auf „Schrittweise“ für die langsame Freigabe und auf „Dropcache“ fest, um den zwischengespeicherten Speicher sofort freizugeben.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Automatische Speicherrückbeanspruchung.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Gibt automatisch zwischengespeicherten Arbeitsspeicher frei, nachdem die Leerlauf-CPU-Auslastung erkannt wurde. Legen Sie diese Option auf „Schrittweise“ für die langsame Freigabe und auf „Dropcache“ fest, um den zwischengespeicherten Speicher sofort freizugeben.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\r\n    <value>Automatischer Proxy aktiviert</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\r\n    <value>Ermöglicht WSL die Verwendung der HTTP-Proxyinformationen von Windows.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Automatischer Proxy aktiviert.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Ermöglicht WSL die Verwendung der HTTP-Proxyinformationen von Windows.</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\r\n    <value>Bestmögliche DNS-Analyse verwenden</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn „wsl2.dnsTunneling“ auf „TRUE“ festgelegt ist. Wenn diese Option auf „TRUE“ festgelegt ist, extrahiert Windows die Frage aus der DNS-Anfrage und versucht, sie aufzulösen, wobei die unbekannten Einträge ignoriert werden.</value>\r\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Bestmögliche DNS-Analyse verwenden.</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn „wsl2.dnsTunneling“ auf „TRUE“ festgelegt ist. Wenn diese Option auf „TRUE“ festgelegt ist, extrahiert Windows die Frage aus der DNS-Anfrage und versucht, sie aufzulösen, wobei die unbekannten Einträge ignoriert werden.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\r\n    <value>Benutzerdefinierter Kernel</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\r\n    <value>Ein absoluter Windows-Pfad zu einem benutzerdefinierten Linux-Kernel.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>Kernels durchsuchen</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Benutzerdefinierter Kernel</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Ein absoluter Windows-Pfad zu einem benutzerdefinierten Linux-Kernel.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\r\n    <value>Benutzerdefinierte Kernelmodule</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\r\n    <value>Ein absoluter Windows-Pfad zu einer benutzerdefinierten VHD mit Linux-Kernelmodulen.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>Kernelmodule durchsuchen</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Benutzerdefinierte Kernelmodule</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Ein absoluter Windows-Pfad zu einer benutzerdefinierten VHD mit Linux-Kernelmodulen.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\r\n    <value>Benutzerdefinierte Systemverteiler</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>Distributionen durchsuchen</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\r\n    <value>Geben Sie einen Pfad zu einer VHD an, die als benutzerdefinierte Systemdistribution geladen wird, die in erster Linie zum Betrieb von GUI-Anwendungen in der WSL verwendet wird. [Weitere Informationen zu Systemverteilern finden Sie hier].</value>\r\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgsystemdistro</value>\r\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Benutzerdefinierte Systemverteiler</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Geben Sie einen Pfad zu einer VHD an, die als benutzerdefinierte Systemdistribution geladen wird, die hauptsächlich zum Betreiben von GUI-Apps in WSL verwendet wird.</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\r\n    <value>Aktivieren der Debugkonsole</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\r\n    <value>Boolescher Wert zum Aktivieren eines Ausgabekonsolenfensters, das den Inhalt von „dmesg“ beim Start einer WSL 2-Verteilerinstanz anzeigt.</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Aktivieren der Debugkonsole.</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Boolescher Wert zum Aktivieren eines Ausgabekonsolenfensters, das den Inhalt von Diagnosemeldungen beim Start einer WSL 2-Distributionsinstanz anzeigt.</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\r\n    <value>VHD-Standardgröße</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\r\n    <value>Die maximale Standardgröße für die erweiterbare virtuelle WSL-Festplatte (VHD) nur für neu erstellte Distributionen.</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Größe zurücksetzen</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>VHD-Standardgröße</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Die standardmäßige maximale Größe (in MB) für die erweiterbare virtuelle WSL-Festplatte (VHD) nur für neu erstellte Distributionen.</value>\r\n  </data>\r\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Entwickler</value>\r\n  </data>\r\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\r\n    <value>Dokumentation</value>\r\n  </data>\r\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocs</value>\r\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\r\n    <value>DrvFS-Modus ändern</value>\r\n  </data>\r\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\r\n    <value>Ändert die betriebssystemübergreifende Dateizugriffsimplementierung in WSL.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\r\n    <value>DNS-Proxy aktiviert</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn „wsl2.networkingMode“ auf NAT festgelegt ist. Boolescher Wert, um WSL über die Konfiguration des DNS-Servers in Linux für die NAT auf dem Host zu informieren. Die Einstellung „FALSE“ spiegelt DNS-Server von Windows auf Linux.</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>DNS-Proxy aktiviert.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn „wsl2.networkingMode“ auf NAT festgelegt ist. Boolescher Wert, um WSL über die Konfiguration des DNS-Servers in Linux für die NAT auf dem Host zu informieren. Die Einstellung „FALSE“ spiegelt DNS-Server von Windows auf Linux.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\r\n    <value>DNS-Tunneling aktiviert</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\r\n    <value>Ändert, wie DNS-Anforderungen von WSL an Windows übermittelt werden.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>DNS-Tunneling aktiviert.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Ändert, wie DNS-Anforderungen von WSL an Windows übermittelt werden.</value>\r\n  </data>\r\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Dateisystem</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\r\n    <value>Aktivieren von GUI-Anwendungen</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\r\n    <value>Boolescher Wert zum Aktivieren oder Deaktivieren der Unterstützung für GUI-Anwendungen ([WSLg]) in WSL.</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgproject</value>\r\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Aktivieren von GUI-Anwendungen.</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Boolescher Wert zum Aktivieren oder Deaktivieren der Unterstützung für GUI-Anwendungen (bekannt als WSL g) in WSL.</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\r\n    <value>Aktivieren von Hardwareleistungsindikatoren</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\r\n    <value>Aktiviert Hardwareleistungsindikatoren für Linux.</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Aktivieren von Hardwareleistungsindikatoren.</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Aktiviert Hardwareleistungsindikatoren für Linux.</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\r\n    <value>Hyper-V-Firewall aktiviert</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\r\n    <value>Aktiviert die Hyper-V-Firewall, mit der die Windows-Firewall-Regeln sowie regeln, die für Hyper-V-Datenverkehr spezifisch sind, den WSL-Netzwerkdatenverkehr filtern.</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Hyper-V-Firewall aktiviert.</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Aktiviert die Hyper-V-Firewall, mit der die Windows-Firewall-Regeln sowie regeln, die für Hyper-V-Datenverkehr spezifisch sind, den WSL-Netzwerkdatenverkehr filtern.</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\r\n    <value>Hostadress-Loopback</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn wsl2.networkingMode auf \"gespiegelt\" festgelegt ist. Bei Festlegung auf TRUE kann der Container über eine ip-Adresse, die dem Host zugewiesen ist, eine Verbindung mit dem Host herstellen oder vom Host eine Verbindung mit dem Container herstellen. Beachten Sie, dass die Loopbackadresse 127.0.0.1 immer verwendet werden kann. Mit dieser Option können auch alle zusätzlich zugewiesenen lokalen IP-Adressen verwendet werden.</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Hostadressen-Loopback.</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn \"wsl2.networkingMode\" auf \"mirrored\" festgelegt ist. Bei Festlegung auf TRUE kann der Container über eine ip-Adresse, die dem Host zugewiesen ist, eine Verbindung mit dem Host herstellen oder vom Host eine Verbindung mit dem Container herstellen. Beachten Sie, dass die Loopbackadresse 127.0.0.1 immer verwendet werden kann. Mit dieser Option können auch alle zusätzlich zugewiesenen lokalen IP-Adressen verwendet werden.</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\r\n    <value>Ignorierte Ports</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn wsl2.networkingMode auf \"gespiegelt\" festgelegt ist. Gibt an, an welche Ports Linux-Anwendungen binden können, die nicht automatisch weitergeleitet oder in Windows berücksichtigt werden. Muss in einer durch Kommas getrennten Liste formatiert werden, z. B.: 3000.9000.9090.</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Ports zurücksetzen</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Ignorierte Ports</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn wsl2.networkingMode auf gespiegelt festgelegt ist. Gibt an, an welche Ports Linux-Anwendungen gebunden werden können, die in Windows nicht automatisch weitergeleitet oder berücksichtigt werden. Muss in einer durch Komma getrennten Liste formatiert werden, z. B. 3000, 9000, 9090.</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\r\n    <value>Timeout für den anfänglichen automatischen Proxy</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn „wsl2.autoProxy“ auf „TRUE“ festgelegt ist. Konfiguriert, wie lange (in Millisekunden) WSL beim Starten eines WSL-Containers auf das Abrufen von HTTP-Proxyinformationen wartet. Wenn die Proxyeinstellungen nach dieser Zeit aufgelöst werden, muss die WSL-Instanz neu gestartet werden, um die abgerufenen Proxy-Einstellungen zu verwenden.</value>\r\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Timeout zurücksetzen</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Timeout für den anfänglichen automatischen Proxy</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Gilt nur, wenn „wsl2.autoProxy“ auf „TRUE“ festgelegt ist. Konfiguriert, wie lange (in Millisekunden) WSL beim Starten eines WSL-Containers auf das Abrufen von HTTP-Proxyinformationen wartet. Wenn die Proxyeinstellungen nach dieser Zeit aufgelöst werden, muss die WSL-Instanz neu gestartet werden, um die abgerufenen Proxy-Einstellungen zu verwenden.</value>\r\n  </data>\r\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\r\n    <value>Kernelbefehlszeile</value>\r\n  </data>\r\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\r\n    <value>Zusätzliche Kernelbefehlszeilenargumente.</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\r\n    <value>Aktivieren der Weiterleitung des lokalen Hosts</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\r\n    <value>Boolescher Wert, der angibt, ob die in der WSL 2-VM an den Platzhalter oder Localhost gebundenen Ports vom Host aus über „localhost:port“ erreichbar sein sollen.</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Aktivieren Sie die Localhost-Weiterleitung.</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Boolescher Wert, der angibt, ob Ports, die an Platzhalter oder Localhost in der WSL 2-VM gebunden sind, über localhost, colon oder port vom Host aus verbunden werden können.</value>\r\n  </data>\r\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\r\n    <value>{0} MB</value>\r\n  </data>\r\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Arbeitsspeicher und Prozessor</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\r\n    <value>Speichergröße</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\r\n    <value>Gibt an, wie viel Arbeitsspeicher dem virtuellen WSL 2-Computer zugewiesen werden soll.</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Größe zurücksetzen</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Speichergröße</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Gibt an, wie viel Arbeitsspeicher (in MB) der WSL 2-VM zugewiesen werden soll.</value>\r\n  </data>\r\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\r\n    <value>{0} Millisekunden</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\r\n    <value>Geschachtelte Virtualisierung aktivieren</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\r\n    <value>Boolescher Wert zum Aktivieren oder Deaktivieren der geschachtelten Virtualisierung, sodass andere geschachtelte VMs innerhalb von WSL 2 ausgeführt werden können.</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Geschachtelte Virtualisierung aktivieren.</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Boolescher Wert zum Aktivieren oder Deaktivieren der geschachtelten Virtualisierung, sodass andere geschachtelte VMs innerhalb von WSL 2 ausgeführt werden können.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\r\n    <value>Netzwerkmodus</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\r\n    <value>Gibt den Netzwerkmodus für WSL an.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Netzwerkmodus.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Gibt den Netzwerkmodus für WSL an.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Netzwerk</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\r\n    <value>Sie können mit allen Ihren Dateien betriebssystemübergreifend arbeiten!</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\r\n    <value>Betriebssystemübergreifender Dateizugriff</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\r\n    <value>Von Linux aus auf Ihre Windows-Dateien zugreifen</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\r\n    <value>Sie können auf Ihre Windows-Dateien unter Linux zugreifen, indem Sie zu „/mnt“ und dann zu Ihrem Windows-Laufwerksbuchstaben navigieren, wie in diesem Beispiel für das Laufwerk C:\r\n\r\n„cd /mnt/c“</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\r\n    <value>Zugriff auf Ihre Linux-Dateien mit dem Datei-Explorer</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\r\n    <value>Sie können Ihre Linux-Dateien im Datei-Explorer anzeigen, indem Sie zu „\\\\wsl.localhost\\“ navigieren oder auf das „Linux“-Symbol klicken.</value>\r\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\r\n    <value>Windows-Dateien und -Programme über WSL starten</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\r\n    <value>Selbst bei Verwendung von WSL können Sie Ihre ausführbaren Windows-Dateien direkt von der Bash ausführen. Versuchen Sie, \rauszuführen.\n„powershell.exe /c start .“, um den Datei-Explorer in Ihrem aktuellen Ordner zu öffnen.</value>\r\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\r\n    <value>Zugreifen auf Linux-Netzwerk-Apps über Windows</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\r\n    <value>Wenn Sie eine Netzwerk-App (z. B. eine App, die auf einem NodeJS oder SQL Server ausgeführt wird) in Ihrer Linux-Distribution erstellen, können Sie über eine Windows-App (z. B. Ihren Edge- oder Chrome-Internetbrowser) mithilfe von Localhost (wie Sie es normalerweise tun würden) darauf zugreifen. Das heißt, wenn Sie einen Linux-Server gestartet haben, der Port 3000 überwacht, können Sie zu Edge unter Windows zu [http://localhost:3000] wechseln, um darauf zuzugreifen.</value>\r\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\r\n    <value>http://localhost:3000</value>\r\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\r\n    <value>Netzwerk im gespiegelten Modus</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\r\n    <value>WSL enthält auch einen neuen Netzwerkmodus, der als gespiegelter Modus bezeichnet wird und erweiterte Funktionen wie IPv6-Unterstützung und die Möglichkeit bietet, auf Ihre Netzwerkanwendungen in Ihrem lokalen Netzwerk zuzugreifen.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zum betriebssystemübergreifenden Dateizugriff</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslfilesystems</value>\r\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\r\n    <value>Willkommen bei Windows-Subsystem für Linux</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\r\n   <value>WSL ist eine hervorragende Möglichkeit, verschiedene Linux-Distributionen auszuprobieren.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\r\n    <value>Distro Management</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zu grundlegenden WSL-Befehlen</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslcommands</value>\r\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zum Importieren beliebiger Linux-Distributionen</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslcustomdistro</value>\r\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>Installierbaren WSL-Distros-Befehl auflisten</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>„wsl.exe -l -o“</value>\r\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>Benannten WSL-Distro-Befehl installieren</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>„wsl.exe --install &lt;DistroName&gt;“</value>\r\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>Befehl zum Auflisten verfügbarer WSL-Distributionen</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>„wsl.exe -l“</value>\r\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\r\n    <value>Docker Desktop funktioniert hervorragend mit WSL, um Sie bei der Entwicklung mit Linux-Containern zu unterstützen.\r\n\r\nEinige der Vorteile der Verwendung von Docker Desktop mit WSL sind:\r\n\r\n• Sie können Docker-Befehle in WSL oder unter Windows ausführen, indem Sie den gleichen Docker-Daemon und die gleichen Images verwenden.\r\n• Sie können Dateien und Ordner nahtlos zwischen Windows und Linux freigeben, indem Sie die automatische Einbindung von Windows-Laufwerken in WSL verwenden.\r\n• Dank der Interoperabilität von WSL können Sie Ihre bevorzugten Windows-Tools und -Editoren verwenden, um an Linux-Code und -Dateien zu arbeiten und umgekehrt.</value>\r\n </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\r\n    <value>Docker-Desktopintegration</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zur Verwendung von WSL mit Docker</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocker</value>\r\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\r\n    <value>Mit dem Windows-Subsystem für Linux (WSL) können Sie Ihre bevorzugten Linux-Tools, -Hilfsprogramme, -Anwendungen und -Workflows direkt unter Windows ausführen.\r\n\r\nNehmen Sie sich einen Moment Zeit, um eine Vorschau einiger der beliebtesten Features der Community zu sehen, oder sehen Sie sich unsere umfassende Dokumentation an.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\r\n    <value>Willkommen bei WSL</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\r\n    <value>Bewährte Methoden für Setup</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslsetup</value>\r\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\r\n    <value>Erste Schritte mit Linux</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgettingstarted</value>\r\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\r\n    <value>Dokumentation zu Windows-Subsystem für Linux (WSL)</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocs</value>\r\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\r\n    <value>Sie können grafisch basierte Linux-Anwendungen mit nativen Windows-Interaktionen wie Alt-Tab, Starten des Startmenüs, Anheften in der Taskleiste und Unterstützung für Ausschneiden und Einfügen verwenden.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\r\n    <value>GUI-Apps</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\r\n    <value>WSL kann Ihre Windows-GPU für Machine Learning Workflows nutzen\r\n\r\nLinux-Binärdateien, die in WSL ausgeführt werden, können Ihre GPU in Windows automatisch verwenden, um ihre Leistung zu beschleunigen. Sie müssen diese Workflows nur auf dieselbe Weise installieren und ausführen wie auf einem regulären Linux-Computer. Es gibt viele verschiedene Einstiegsmöglichkeiten: von der Ausführung von CUDA in einem Docker-Container, wenn Sie über eine NVIDIA-Grafikkarte verfügen, bis hin zur Ausführung von PyTorch oder TensorFlow mit DirectML auf Ihrer AMD-, Intel- oder NVIDIA-Grafikkarte. Weitere Informationen finden Sie unten in unserem Leitfaden zu den ersten Schritten.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\r\n    <value>GPU-Beschleunigung</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zur GPU-Beschleunigung</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgpu</value>\r\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zu WSL GUI-Apps</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslguiapps</value>\r\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\r\n    <value>Hier ist eine Liste mit Apps, die Sie ausprobieren können (Sie können sie alle in Ubuntu installieren, indem Sie „sudo apt install &lt;Name der App&gt;“ eingeben).\r\n\r\n    • gedit – Einfacher Texteditor\r\n    • audacity – Audiodateien aufnehmen und bearbeiten\r\n    • blender – Erstellen von 3D-Animationen und -Darstellungen\r\n    • gimp – Fotos bearbeiten\r\n    • nautilus – Ein Linux-Dateiexplorer\r\n    • vlc – Videoplayer</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\r\n    <value>Sie können problemlos auf Netzwerk-Apps unter Windows- und Linux-Betriebssystemen zugreifen.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\r\n    <value>Netzwerkintegration</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zu Netzwerkanwendungen</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslnetworking</value>\r\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zum Netzwerkbetrieb im gespiegelten Modus</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslmirroredmode</value>\r\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\r\n   <value>Sie können WSL als Vollzeitentwicklungsumgebung direkt aus VS Code verwenden.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\r\n    <value>VS Code-Integration</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zur Verwendung von WSL mit VS Code</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslvscode</value>\r\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\r\n    <value>Installationsanleitung</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\r\n    <value>Nachdem Sie VS Code installiert haben, können Sie die Remote-WSL-Erweiterung über das Windows-Terminal installieren:\r\n\r\n„code –install-extension ms-vscode-remote.remote-wsl“</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\r\n    <value>WSL-Projekt in Visual Studio Code öffnen</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\r\n    <value>Um ein Projekt in VS Code von Ihrer WSL-Distribution aus zu öffnen, öffnen Sie die Befehlszeile der Distribution und führen Sie „code .“ aus, um eine Projektdatei zu öffnen.\r\n\r\nSie können auch über die Befehlspalette in VS Code selbst auf weitere VS Code Remoteoptionen zugreifen. Drücken Sie „UMSCHALT+STRG+P“ auf Ihrer Tastatur, um die Befehlspalette zu öffnen, und geben Sie „Remote-WSL“ ein, um eine Liste der verfügbaren VS Code Remoteoptionen anzuzeigen, sodass Sie den Ordner in einer Remotesitzung erneut öffnen und angeben können, welche Distribution Sie öffnen möchten und mehr.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\r\n    <value>Anleitung</value>\r\n  </data>\r\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Optionale Features</value>\r\n  </data>\r\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\r\n    <value>Datenschutzbestimmung</value>\r\n  </data>\r\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\r\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\r\n  </data>\r\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\r\n    <value>Prozessoranzahl</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\r\n    <value>Gibt an, wie viele logische Prozessoren der WSL 2-VM zugewiesen werden sollen.</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Anzahl zurücksetzen</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Prozessoranzahl</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Gibt an, wie viele logische Prozessoren der WSL 2-VM zugewiesen werden sollen.</value>\r\n  </data>\r\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\r\n    <value>Verwandte Links</value>\r\n  </data>\r\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\r\n    <value>Versionshinweise</value>\r\n  </data>\r\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslreleases</value>\r\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\r\n  </data>\r\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\r\n    <value>Aktivieren des abgesicherten Modus</value>\r\n  </data>\r\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\r\n    <value>Führen Sie die WSL im „abgesicherten Modus“ aus, in dem viele Features deaktiviert sind. Dieser Modus ist für die Wiederherstellung von fehlerhaften Distributionen gedacht.</value>\r\n  </data>\r\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Aktivieren des abgesicherten Modus.</value>\r\n  </data>\r\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Führen Sie die WSL im „abgesicherten Modus“ aus, in dem viele Features deaktiviert sind. Dieser Modus ist für die Wiederherstellung von fehlerhaften Distributionen gedacht.</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\r\n    <value>Info</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\r\n    <value>Entwickler</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\r\n    <value>Distro Management</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Docker-Desktopintegration</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\r\n    <value>Dateisystem</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\r\n    <value>Allgemein</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\r\n    <value>GPU-Beschleunigung</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\r\n    <value>GUI-Apps</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\r\n    <value>Starten von wsl.exe</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\r\n    <value>Arbeitsspeicher und Prozessor</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\r\n    <value>Netzwerk</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Netzwerkintegration</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\r\n    <value>Willkommen bei WSL</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\r\n    <value>Optionale Features</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\r\n    <value>Einstellungen</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\r\n    <value>VS Code-Integration</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\r\n    <value>Neuigkeiten</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\r\n    <value>Dateisystemübergreifendes Arbeiten</value>\r\n  </data>\r\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\r\n    <value>Probleme</value>\r\n  </data>\r\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslproject</value>\r\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\r\n  </data>\r\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\r\n    <value>Sparse VHD standardmäßig aktivieren</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\r\n    <value>Jede neu erstellte VHD wird automatisch auf „geringe Dichte“ festgelegt, wenn sie aktiviert ist.</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Sparse VHD wird standardmäßig aktiviert.</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Jede neu erstellte VHD wird automatisch auf „geringe Dichte“ festgelegt, wenn sie aktiviert ist.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\r\n    <value>Speicherort der Auslagerungsdatei</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\r\n    <value>Ein absoluter Windows-Pfad zur virtuellen Auslagerungsfestplatte.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>Auslagerungsdateien durchsuchen</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Speicherort der Auslagerungsdatei</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Ein absoluter Windows-Pfad zur virtuellen Auslagerungsfestplatte.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\r\n    <value>Swap-Größe</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\r\n    <value>Gibt an, wie viel Auslagerungsspeicherplatz der VM WSL 2 hinzugefügt werden soll, 0 für keine Auslagerungsdatei. Der Auslagerungsspeicher ist datenträgerbasierter RAM, der verwendet wird, wenn der Arbeitsspeicherbedarf das Limit auf dem Hardwaregerät überschreitet.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Größe zurücksetzen</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Swap-Größe</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Wie viel Auslagerungsspeicher in Megabyte der WSL 2-VM hinzugefügt werden soll. 0 für keine Auslagerungsdatei. Der Auslagerungsspeicher ist datenträgerbasierter RAM, der verwendet wird, wenn der Arbeitsspeicherbedarf das Limit auf dem Hardwaregerät überschreitet.</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\r\n    <value>Aktivieren von VirtIO</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\r\n    <value>Verwenden Sie „virtiofs“ anstelle von „plan 9“ für den Zugriff auf Hostdateien, um die Geschwindigkeit zu erhöhen.</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\r\n    <value>Aktivieren von Virtio 9p</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\r\n    <value>Aktiviert die Einbindung des 9P-Dateisystems vom Host über den Virtio-Transport.</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\r\n    <value>VM-Leerlauftimeout</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\r\n    <value>Die Anzahl von Millisekunden, die eine VM im Leerlauf ist, bevor sie heruntergefahren wird.</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Timeout zurücksetzen</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>VM-Leerlauftimeout</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Die Anzahl von Millisekunden, die eine VM im Leerlauf ist, bevor sie heruntergefahren wird.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\r\n    <value>Erstellen, Ausführen, Debuggen und Profilieren von Apps, die auf WSL laufen, direkt in Visual Studio</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\r\n    <value>Ausführen und Debuggen Ihrer Apps auf WSL aus Visual Studio</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zu WSL in Visual Studio für .NET-Entwickelnde</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\r\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\r\n    <value>Weitere Informationen zu Visual Studio und WSL für C++-Entwickelnde</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\r\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\r\n    <value>Mithilfe des Windows-Subsystems für Linux (WSL) können Sie Ihre .NET Core- und plattformübergreifenden C++-Apps problemlos unter Linux ausführen und debuggen, ohne Visual Studio verlassen zu müssen. Wenn Sie plattformübergreifend entwickeln, ist diese Methode eine einfache Möglichkeit, weitere Ihrer Zielumgebungen zu testen.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\r\n    <value>Visual Studio-Integration</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Visual Studio-Integration</value>\r\n  </data>\r\n</root>"
  },
  {
    "path": "localization/strings/en-GB/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux lets developers run a GNU/Linux environment -- including most command-line tools, utilities, and applications -- directly on Windows, unmodified, without the overhead of a traditional virtual machine or dualboot setup.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>The disk failed to detach: {}. For more details, please run 'dmesg' inside WSL2.\nTo force WSL2 to stop and detach the disk, run 'wsl.exe {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>The disk '{}' is already attached.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>That volume is already mounted inside WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>A disk with that name is already mounted; please unmount the disk or choose a new name and try again.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>The specified mount name contains an invalid '/' character. Please retry without the invalid character.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>The disk was successfully mounted as '/mnt/wsl/{}'.\nNote: The location will be different if you have modified the automount.root setting in /etc/wsl.conf.\nTo unmount and detach the disk, run 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>The disk was attached but failed to mount: {}.\nFor more details, run 'dmesg' inside WSL2.\nTo detach the disk, run 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>The following is a list of valid distributions that can be installed.\nInstall using 'wsl.exe {} &lt;Distro&gt;'.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>The distribution name has already been set.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>The supplied install location is already in use.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>A distribution with the supplied name already exists. Use --name to chose a different name.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>There is no distribution with the supplied name.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Administrator access is needed to mount a disk.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Exporting the distribution failed.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Performing one-time upgrade of the Windows Subsystem for Linux file system for this distribution...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Cannot launch because another instance is running un-elevated.  Elevated and un-elevated instances are not permitted to run simultaneously.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Importing the distribution failed.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Installing Windows optional component: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Downloading: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Installing: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importing distribution</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} has been downloaded.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux is resuming a previous installation...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Invalid distribution name: '{}'.\nTo get a list of valid distributions, use 'wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>The Windows Subsystem for Linux instance has terminated.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Invalid command line argument: {}\nPlease use '{} --help' to get a list of supported arguments.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>Command line argument {} requires a value.\nPlease use '{} --help' to get a list of supported arguments.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Unsupported console settings. In order to use this feature, the legacy console must be disabled.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} is not a valid integer.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>An install, uninstall, or conversion is in progress for this distribution.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Launching {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Cannot launch because another instance is running elevated.  Elevated and un-elevated instances are not permitted to run simultaneously.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux has no installed distributions.\nYou can resolve this by installing a distribution with the instructions below:\n\nUse 'wsl.exe --list --online' to list available distributions\nand 'wsl.exe --install &lt;Distro&gt;' to install.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>There are no running distributions.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Default)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux Distributions:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Default Distribution: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Default Version: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Unregistering.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>User not found.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>For information on key differences with WSL 2 please visit https://aka.ms/wsl2</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Conversion in progress, this may take a few minutes.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>The distribution is already the requested version.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>The Legacy distribution does not support WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>WSL2 is unable to start since virtualisation is not enabled on this machine.\nPlease ensure the \"Virtual Machine Platform\" optional component is enabled and virtualisation is turned on in your computer's firmware settings.\n\nEnable \"Virtual Machine Platform\" by running: wsl.exe --install --no-distribution\n\nFor information please visit https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Too many virtual hard disks or physical disks are attached.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD already mounted via wsl.exe --mount, please unmount the disk before launching.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 is not supported with your current machine configuration.\nPlease enable the \"Windows Subsystem for Linux\" optional component to use WSL1.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>This operation is only supported by WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Usage:\n    --networking-mode\n       Display current networking mode.\n\n    --msal-proxy-path\n        Display the path to the MSAL proxy application.\n\n    --vm-id\n        Display the WSL VM ID.\n\n    --version\n        Display the version of the WSL package.\n\n    -n\n        Do not print a newline.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Usage:\n    -a\n        Force result to absolute path format.\n    -u\n        Translate from a Windows path to a WSL path (default).\n    -w\n        Translate from a WSL path to a Windows path.\n    -m\n        Translate from a WSL path to a Windows path, with '/' instead of '\\\\'\n\nExample: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Performs administrative operations on Windows Subsystem for Linux\n\nUsage:\n    /l, /list [Option]\n        Lists registered distributions.\n        /all - Optionally list all distributions, including distributions that\n               are currently being installed or uninstalled.\n\n        /running - List only distributions that are currently running.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Sets the distribution as the default.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Terminates the distribution.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Unregisters the distribution and deletes the root filesystem.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. All rights reserved.\nFor privacy information about this product please visit https://aka.ms/privacy.\n\nUsage: wsl.exe [Argument] [Options...] [CommandLine]\n\nArguments for running Linux binaries:\n\n    If no command line is provided, wsl.exe launches the default shell.\n\n    --exec, -e &lt;CommandLine&gt;\n        Execute the specified command without using the default Linux shell.\n\n    --shell-type &lt;standard|login|none&gt;\n        Execute the specified command with the provided shell type.\n\n    --\n        Pass the remaining command line as-is.\n\nOptions:\n    --cd &lt;Directory&gt;\n        Sets the specified directory as the current working directory.\n        If ~ is used the Linux user's home path will be used. If the path begins\n        with a / character, it will be interpreted as an absolute Linux path.\n        Otherwise, the value must be an absolute Windows path.\n\n    --distribution, -d &lt;DistroName&gt;\n        Run the specified distribution.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Run the specified distribution ID.\n\n    --user, -u &lt;UserName&gt;\n        Run as the specified user.\n\n    --system\n        Launches a shell for the system distribution.\n\nArguments for managing Windows Subsystem for Linux:\n\n    --help\n        Display usage information.\n\n    --debug-shell\n        Open a WSL2 debug shell for diagnostics purposes.\n\n    --install [Distro] [Options...]\n        Install a Windows Subsystem for Linux distribution.\n        For a list of valid distributions, use 'wsl.exe --list --online'.\n\n        Options:\n            --enable-wsl1\n                Enable WSL1 support.\n\n            --fixed-vhd\n                Create a fixed-size disk to store the distribution.\n\n            --from-file &lt;Path&gt;\n                Install a distribution from a local file.\n\n            --legacy\n                Use the legacy distribution manifest.\n\n            --location &lt;Location&gt;\n                Set the install path for the distribution.\n\n            --name &lt;Name&gt;\n                Set the name of the distribution.\n\n            --no-distribution\n                Only install the required optional components, does not install a distribution.\n\n            --no-launch, -n\n                Do not launch the distribution after install.\n\n            --version &lt;Version&gt;\n                Specifies the version to use for the new distribution.\n\n            --vhd-size &lt;MemoryString&gt;\n                Specifies the size of the disk to store the distribution.\n\n            --web-download\n                Download the distribution from the internet instead of the Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Changes distro specific options.\n\n        Options:\n            --move &lt;Location&gt;\n                Move the distribution to a new location.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Set the VHD of distro to be sparse, allowing disk space to be automatically reclaimed.\n\n            --set-default-user &lt;Username&gt;\n                Set the default user of the distribution.\n\n            --resize &lt;MemoryString&gt;\n                Resize the disk of the distribution to the specified size.\n\n    --mount &lt;Disk&gt;\n        Attaches and mounts a physical or virtual disk in all WSL 2 distributions.\n\n        Options:\n            --vhd\n                Specifies that &lt;Disk&gt; refers to a virtual hard disk.\n\n            --bare\n                Attach the disk to WSL2, but don't mount it.\n\n            --name &lt;Name&gt;\n                Mount the disk using a custom name for the mountpoint.\n\n            --type &lt;Type&gt;\n                Filesystem to use when mounting a disk, if not specified defaults to ext4.\n\n            --options &lt;Options&gt;\n                Additional mount options.\n\n            --partition &lt;Index&gt;\n                Index of the partition to mount, if not specified defaults to the whole disk.\n\n    --set-default-version &lt;Version&gt;\n        Changes the default install version for new distributions.\n\n    --shutdown\n        Immediately terminates all running distributions and the WSL 2\n        lightweight utility virtual machine.\n\n        Options:\n            --force\n                Terminate the WSL 2 virtual machine even if an operation is in progress. Can cause data loss.\n\n    --status\n        Show the status of Windows Subsystem for Linux.\n\n    --unmount [Disk]\n        Unmounts and detaches a disk from all WSL2 distributions.\n        Unmounts and detaches all disks if called without argument.\n\n    --uninstall\n        Uninstalls the Windows Subsystem for Linux package from this machine.\n\n    --update\n        Update the Windows Subsystem for Linux package.\n\n        Options:\n            --pre-release\n                Download a pre-release version if available.\n\n    --version, -v\n        Display version information.\n\nArguments for managing distributions in Windows Subsystem for Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Exports the distribution to a tar file.\n        The filename can be - for stdout.\n\n        Options:\n            --format &lt;Format&gt;\n                Specifies the export format. Supported values: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        Imports the specified tar file as a new distribution.\n        The filename can be - for stdin.\n\n        Options:\n            --version &lt;Version&gt;\n                Specifies the version to use for the new distribution.\n\n            --vhd\n                Specifies that the provided file is a .vhd or .vhdx file, not a tar file.\n                This operation makes a copy of the VHD file at the specified install location.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Imports the specified VHD file as a new distribution.\n        This virtual hard disk must be formatted with the ext4 filesystem type.\n\n    --list, -l [Options]\n        Lists distributions.\n\n        Options:\n            --all\n                List all distributions, including distributions that are\n                currently being installed or uninstalled.\n\n            --running\n                List only distributions that are currently running.\n\n            --quiet, -q\n                Only show distribution names.\n\n            --verbose, -v\n                Show detailed information about all distributions.\n\n            --online, -o\n                Displays a list of available distributions for install with 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Sets the distribution as the default.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Changes the version of the specified distribution.\n\n    --terminate, -t &lt;Distro&gt;\n        Terminates the specified distribution.\n\n    --unregister &lt;Distro&gt;\n        Unregisters the distribution and deletes the root filesystem.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL version: {}\nKernel version: {}\nWSLg version: {}\nMSRDC version: {}\nDirect3D version: {}\nDXCore version: {}\nWindows version: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild version: {}\nCommit: {}\nBuild time: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>The custom kernel specified in {} was not found: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>The customised kernel modules VHD in {} was not found: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>The customised system distribution specified in {} was not found or is not the correct format.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. All rights reserved.\nFor privacy information about this product please visit https://aka.ms/privacy.\n\nUsage: wslg.exe [Argument] [Options...] [CommandLine]\n\nArguments:\n    --cd &lt;Directory&gt;\n        Sets the specified directory as the current working directory.\n        If ~ is used the Linux user's home path will be used. If the path begins\n        with a / character, it will be interpreted as an absolute Linux path.\n        Otherwise, the value must be an absolute Windows path.\n\n    --distribution, -d &lt;Distro&gt;\n        Run the specified distribution.\n\n    --user, -u &lt;UserName&gt;\n        Run as the specified user.\n\n    --shell-type &lt;standard|login|none&gt;\n        Execute the specified command with the provided shell type.\n\n    --help\n        Display usage information.\n\n    --\n        Pass the remaining command line as-is.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Press any key to continue...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Argument {} is missing a required parameter.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Export in progress, this may take a few minutes.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Import in progress, this may take a few minutes.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>GUI application support is disabled via {} or /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Checking for updates.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>This distribution is only available from the Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount on ARM64 requires Windows version 27653 or newer.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>The most recent version of Windows Subsystem for Linux is already installed.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Updating Windows Subsystem for Linux to version: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>This application requires the Windows Subsystem for Linux Optional Component.\nInstall it by running: wsl.exe --install --no-distribution\nThe system may need to be restarted so the changes can take effect.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>The specified file must have the {} file extension.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>The specified file must have the {} or {} file extension.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>The VmSwitch '{}' was not found. Available switches: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Bridged networking requires wsl2.vmSwitch to be set.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Failed to fetch the distribution list from '{}'. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Failed to attach disk '{}' to WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Failed to resize disk.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>No value found.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Windows version {} does not support the packaged version of Windows Subsystem for Linux.\nInstall the required update via Windows update or via: {}\nFor information please visit https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Running the debug shell requires running wsl.exe as Administrator.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>The installation process for distribution '{}' failed with exit code: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Invalid GUID format: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Invalid section name in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Invalid key name in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Expected {} in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Invalid escaped character: '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Unknown key '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Invalid integer value '{}' for key '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Invalid IP value '{}' for key '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Invalid boolean value '{}' for key '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Invalid mac address '{}' for key '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Failed to open config file {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Errors occurred during WSL startup</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Failed to start the systemd user session for '{}'. See journalctl for more details.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Open EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>This distribution doesn't contain a default name. Use --name to chose the distribution name.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Arguments {} and {} can't be specified at same time.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>Argument {} requires the {} argument.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Invalid distribution name: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Using legacy distribution registration. Consider using a tar based distribution instead.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Legacy distribution registrations do not support the --version argument.</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>The distribution \"{}\" is provided by an override manifest.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>The distribution hash doesn't match. Expected: {}, actual hash: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Invalid hex string: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>'{}' is not supported when installing legacy distributions.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Distribution successfully installed. It can be launched via 'wsl.exe -d {}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Invalid memory string '{}' for .wslconfig entry '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Failed to create the swap disk in '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Nested virtualisation is not supported on this machine.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Failed to create network endpoint with address: '{}', assigned new address: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Failed to create virtual network with address range: '{}', created new network with range: '{}', {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>SAFE MODE ENABLED - many features will be disabled</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Mirrored networking mode is not supported: {}.\nFalling back to NAT networking.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Linux Kernel version 5.10 or newer is required</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows version {}.{} does not have the required features</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Hyper-V firewall is not supported</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>DNS Tunneling is not supported</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>The wsl2.localhostForwarding setting has no effect when using mirrored networking mode</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>An Http Proxy change has been detected on the host. Please restart WSL to apply the change.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>A localhost proxy configuration was detected but not mirrored into WSL. WSL in NAT mode does not support localhost proxies.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>An IPv6 proxy configuration was detected but not mirrored into WSL. WSL in NAT mode does not support IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>A localhost IPv6 proxy configuration was detected but not mirrored into WSL. WSL does not support localhost IPv6 proxies.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>An unexpected error occurred while attempting to resolve proxy settings, proxy settings were not mirrored into WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>An error occurred accessing the registry. Path: '{}'. Error: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Failed to launch the localhost relay process. Error: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Failed to start virtual networking - please install the optional component Virtual Machine Platform by running: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Failed to configure network (networkingMode {}), falling back to networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Failed to enable Windows component '{}' (exit code {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>A fatal error was returned by plug-in '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>A fatal error was returned by plug-in '{}'. Error message: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>The plug-in '{}' requires a newer version of WSL. Please run: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>The .wslconfig setting '{}' is disabled by the computer policy.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>The debug shell is disabled by the computer policy.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount is disabled by the computer policy.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>WSL1 is disabled by the computer policy.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Please run 'wsl.exe --set-version {} 2' to upgrade to WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL is finishing an upgrade...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Update failed (exit code: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Uninstall failed (exit code: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Log file: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Failed to remove the MSIX package (error: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Failed to install the MSIX package (error: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Optional components needed to run WSL are not installed.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Install missing components.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>The imported file is not a valid Linux distribution.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Press any key to exit...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>A new version of Windows Subsystem for Linux is available.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Update to version {} or view its release notes below.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Update</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>See Docs</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>The operation could not be completed because the VHD is currently in use. To force WSL to stop use: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} is not a valid boolean, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Sparse VHD is supported on WSL2 only.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>Running WSL as local system is not supported.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Performance Tip:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Using an I/O intensive operation like {} on your Windows drives will have poor performance. Consider moving your project files to the Linux file system for better performance. Click below to learn more.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>View Docs</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Don't Show Again</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>The WSL2 Virtual Machine crashed.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>The stack trace has been saved in: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>The distribution failed to start. Error code: {}, failure step: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>The distribution failed to start because its virtual disk is corrupted.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Duplicated config key '{}' in {}:{} (Conflicting key: '{}' in {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Invalid value '{}' for config key '{}' in {}:{} (Valid values: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Waiting for OOBE command to complete for distribution \"{}\"...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Failed to read property '{}' from distribution {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>This looks like a VHD file. Use --vhd to import a VHD instead of a tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Failed to install {} from the Microsoft Store: {}\nAttempting web download...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>No default distribution has been configured. Please provide a distribution to install.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p is disabled, falling back to 9p with vsock transport.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>WSL installation appears to be corrupted (Error code: {}).\nPress any key to repair WSL, or CTRL-C to cancel.\nThis prompt will time out in 60 seconds.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Failed to parse terminal profile while registering distribution: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Invalid size: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nError code: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Invalid JSON document. Parse error: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors cannot exceed the number of logical processors on the system ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Failed to query networking mode</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Customised kernel modules were supplied without specifying a customised kernel. Please see https://aka.ms/wslcustomkernel for more information.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Due to a current compatibility issue with Global Secure Access Client, DNS Tunneling is disabled.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Sparse VHD support is currently disabled due to potential data corruption.\nTo force a distribution to use a sparse VHD, please run:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Failed to mount {}, see dmesg for more details.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Processing /etc/fstab with mount -a failed.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>An error occurred mounting the distribution disk, it was mounted read-only as a fallback.\nSee recovery instructions on: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Failed to translate '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Something went wrong. Try again later.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>About</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>About Windows Subsystem for Linux Settings</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. All rights reserved.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux Settings</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux Settings lets developers manage the .wslconfig file using a GUI-based application.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Auto memory reclaim</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Automatically releases cached memory after detecting idle CPU usage. Set to gradual for slow release, and dropcache for instant release of cached memory.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Auto memory reclaim.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Automatically releases cached memory after detecting idle CPU usage. Set to gradual for slow release, and dropcache for instant release of cached memory.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Auto Proxy enabled</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Enables WSL to use Windows' HTTP proxy information.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Auto Proxy enabled.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Enables WSL to use Windows' HTTP proxy information.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Use best effort DNS parsing</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.dnsTunneling is set to true. When set to true, Windows will extract the question from the DNS request and attempt to resolve it, ignoring the unknown records.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Use best effort DNS parsing.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.dnsTunneling is set to true. When set to true, Windows will extract the question from the DNS request and attempt to resolve it, ignoring the unknown records.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Custom kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>An absolute Windows path to a customised Linux kernel.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Browse kernels</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Custom kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>An absolute Windows path to a custom Linux kernel.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Customised kernel modules</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>An absolute Windows path to a customised Linux kernel modules VHD.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Browse kernel modules</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Customised kernel modules</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>An absolute Windows path to a customised Linux kernel modules VHD.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Custom system distro</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Browse distros</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Specify a path to a VHD which will load as a custom system distro, primarily used to power GUI apps in WSL. [Learn more about system distros here].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Custom system distro</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Specify a path to a VHD which will load as a custom system distro, primarily used to power GUI apps in WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Enable debug console</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Boolean to turn on an output console window that shows the contents of dmesg upon start of a WSL 2 distro instance.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Enable debug console.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolean to turn on an output console window that shows the contents of diagnostic messages upon start of a WSL 2 distro instance.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Default VHD Size</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>The default max size for the expandable WSL virtual hard disk (VHD) for newly created distributions only.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Reset size</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Default VHD Size</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>The default max size, specified in megabytes, for the expandable WSL virtual hard disk (VHD) for newly created distributions only.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Developer</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentation</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Change DrvFS mode</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Changes cross-OS file access implementation in WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS Proxy enabled</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.networkingMode is set to NAT. Boolean to inform WSL to configure the DNS Server in Linux to the NAT on the host. Setting to false will mirror DNS servers from Windows to Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS Proxy enabled.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.networkingMode is set to NAT. Boolean to inform WSL to configure the DNS Server in Linux to the NAT on the host. Setting to false will mirror DNS servers from Windows to Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>DNS Tunneling enabled</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Changes how DNS requests are proxied from WSL to Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS Tunneling enabled.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Changes how DNS requests are proxied from WSL to Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>File System</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Enable GUI applications</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Boolean to turn on or off support for GUI applications ([WSLg]) in WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Enable GUI applications.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolean to turn on or off support for GUI applications (known as WSL g) in WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Enable hardware performance counters</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Enables hardware performance counters for Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Enable hardware performance counters.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Enables hardware performance counters for Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Hyper-V Firewall enabled</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Enables Hyper-V firewall which allows the Windows Firewall rules, as well as rules specific to Hyper-V traffic, to filter WSL network traffic.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hyper-V Firewall enabled.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Enables Hyper-V firewall which allows the Windows Firewall rules, as well as rules specific to Hyper-V traffic, to filter WSL network traffic.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Host Address Loopback</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.networkingMode is set to mirrored. When set to True, will allow the Container to connect to the Host, or the Host to connect to the Container, by an IP address that's assigned to the Host. Note that the 127.0.0.1 loopback address can always be used - this option allows for all additionally assigned local IP addresses to be used as well.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Host Address Loopback.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.networkingMode is set to mirrored. When set to True, will allow the Container to connect to the Host, or the Host to connect to the Container, by an IP address that's assigned to the Host. Note that the 127.0.0.1 loopback address can always be used - this option allows for all additionally assigned local IP addresses to be used as well.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Ignored ports</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.networkingMode is set to mirrored. Specifies which ports Linux applications can bind to that will not be automatically forwarded or considered in Windows. Should be formatted in a comma separated list, e.g: 3000,9000,9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Reset ports</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ignored ports</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.networkingMode is set to mirrored. Specifies which ports Linux applications can bind to that will not be automatically forwarded or considered in Windows. Should be formatted in a comma separated list, for example, 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Initial Auto Proxy timeout</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.autoProxy is set to true. Configures how long (in milliseconds) WSL will wait for retrieving HTTP proxy information when starting a WSL container. If proxy settings are resolved after this time, the WSL instance must be restarted to use the retrieved proxy settings.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Reset timeout</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Initial Auto Proxy timeout</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Only applicable when wsl2.autoProxy is set to true. Configures how long, in milliseconds, WSL will wait for retrieving HTTP proxy information when starting a WSL container. If proxy settings are resolved after this time, the WSL instance must be restarted to use the retrieved proxy settings.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Kernel Command Line</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Additional kernel command line arguments.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Enable localhost forwarding</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Boolean specifying if ports bound to wildcard or localhost in the WSL 2 VM should be connectable from the host via localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Enable localhost forwarding.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolean specifying if ports bound to wildcard or localhost in the WSL 2 VM should be connectable from the host via localhost, colon, port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Memory and processor</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Memory Size</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>How much memory to assign to the WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Reset size</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Memory Size</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>How much memory, specified in megabytes, to assign to the WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} milliseconds</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Enable nested virtualisation</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Boolean to turn on or off nested virtualisation, enabling other nested VMs to run inside WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Enable nested virtualisation.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolean to turn on or off nested virtualisation, enabling other nested VMs to run inside WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Networking mode</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Specifies the networking mode for WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Networking mode.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Specifies the networking mode for WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Networking</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>You can work across operating systems with all of your files!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Cross OS File Access</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Access your Windows files from Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>You can access your Windows files from within Linux by navigating to ‘/mnt’ and then to your Windows drive letter, like this example for the C drive:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Access your Linux files with File Explorer</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>You can view your Linux files from File Explorer by navigating to '\\\\wsl.localhost\\' or clicking on the ‘Linux’ icon.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Launch Windows files and programmes from WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Even while using WSL you can run your Windows executables directly from bash. Try running\n'powershell.exe /c start .' to open up File Explorer in your current folder.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Accessing Linux Networking Apps from Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>If you are building a networking app (for example an app running on a NodeJS or SQL server) in your Linux distribution, you can access it from a Windows app (like your Edge or Chrome internet browser) using localhost (just like you normally would). This means if you started a Linux server that is listening to port 3000, you can go to [http://localhost:3000] in Edge on Windows to access it.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Mirrored Mode Networking</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL also includes a new networking mode, called mirrored mode which adds advanced capabilities like IPv6 support and the ability to access your networking applications in your local area network.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Learn More About Cross OS File Access</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Welcome to Windows Subsystem for Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL is a great way to try out different Linux distributions.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Distro Management</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Learn more about basic WSL commands</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Learn more about importing any Linux distribution</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>List Installable WSL Distros Command</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Install a Named WSL Distro Command</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>List Available WSL Distros Command</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop works great with WSL to help you develop with Linux containers.\n\nSome of the benefits of using Docker Desktop with WSL are:\n\n• You can run Docker commands in WSL or in Windows, using the same Docker daemon and images.\n• You can share files and folders between Windows and Linux seamlessly, using the automatic mount of Windows drives in WSL.\n• You can use your preferred Windows tools and editors to work on Linux code and files, and vice versa, thanks to the interoperability of WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker Desktop Integration</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Learn More About Using WSL with Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>The Windows Subsystem for Linux (WSL) lets you run your favourite Linux tools, utilities, applications, and workflows directly on Windows.\n\nTake a moment to preview some of the community’s favourite features or view our comprehensive documentation.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Welcome to WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Best Practises for Setup</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Getting Started with Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux (WSL) Documentation</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>You can use graphical based Linux applications with native Windows interactions like alt-tab, start menu launching, task bar pinning, and cut &amp; paste support.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>GUI Apps</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL can leverage your Windows GPU for Machine Learning workflows\n\nLinux binaries running in WSL can automatically use your GPU in Windows to speed up their performance. You just need to install and run those workflows the same way that you would on a regular Linux machine. There are lots of different ways to get started from running CUDA in a docker container if you have a NVIDIA graphics card, to running PyTorch or TensorFlow with DirectML on your AMD, Intel, or NVIDIA graphics card. See our getting started guide below to learn more.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>GPU Acceleration</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Learn More About GPU Acceleration</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Learn More About WSL GUI Apps</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Here’s a list of apps to try (You can install all of these in Ubuntu by typing 'sudo apt install &lt;The App Name&gt;')\n\n    • gedit – Basic text editor\n    • audacity – Record and edit audio files\n    • blender – Make 3D animations and visualisations\n    • gimp – Edit photos\n    • nautilus – A Linux file explorer\n    • vlc – Video player</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>You can easily access networking apps across Windows and Linux operating systems.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Networking Integration</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Learn More About Networking Applications</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Learn More About Mirrored Mode Networking</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>You can use WSL as your full-time development environment directly from VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS Code Integration</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Learn More About Using WSL with VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>How to install</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>After you’ve installed VS Code, you can install the Remote WSL extension from Windows Terminal:\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Open a WSL Project in Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>To open a project in VS Code from your WSL distribution, open the distribution’s command line and run 'code .' to open a project file.\n\nYou can also access more VS Code Remote options through the command palette within VS Code itself. Hit “SHIFT+CTRL+P” on your keyboard to open the command palette and type ‘Remote-WSL’ to see a list of the VS Code Remote options available, allowing you to reopen the folder in a remote session, specify which distribution you want to open, and more.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>How to use</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Optional Features</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Privacy Statement</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Processor Count</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>How many logical processors to assign to the WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Reset count</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Processor Count</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>How many logical processors to assign to the WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Related links</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Release notes</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Enable safe mode</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Run WSL in \"Safe Mode\" which disables many features and is intended to be used to recover distributions that are in bad states.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Enable safe mode.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Run WSL in \"Safe Mode\" which disables many features and is intended to be used to recover distributions that are in bad states.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>About</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Developer</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Distro Management</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Docker Desktop Integration</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>File System</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>General</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>GPU Acceleration</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>GUI Apps</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Launch wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Memory and processor</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Networking</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Networking Integration</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Welcome to WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Optional Features</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Settings</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS Code Integration</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>What's new</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Working Across File Systems</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Issues</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Enable sparse VHD by default</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Any newly created VHD will be set to sparse automatically when enabled.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Enable sparse VHD by default.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Any newly created VHD will be set to sparse automatically when enabled.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Swap File Location</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>An absolute Windows path to the swap virtual hard disk.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Browse swap files</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Swap File Location</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>An absolute Windows path to the swap virtual hard disk.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Swap Size</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>How much swap space to add to the WSL 2 VM, 0 for no swap file. Swap storage is disk-based RAM used when memory demand exceeds limit on hardware device.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Reset size</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Swap Size</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>How much swap space, specified in megabytes, to add to the WSL 2 VM. 0 for no swap file. Swap storage is disk-based RAM used when memory demand exceeds limit on hardware device.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Enable VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Use virtiofs instead of plan 9 to access host files, increasing speed.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Enable Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Enables mount of the 9P filesystem from the host using the virtio transport.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>VM Idle timeout</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>The number of milliseconds that a VM is idle, before it is shut down.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Reset timeout</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>VM Idle timeout</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>The number of milliseconds that a VM is idle, before it is shut down.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Build, run, debug, and profile your apps running on WSL from Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Run and debug your apps on WSL from Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Learn more about WSL in Visual Studio for .NET developers</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Learn more about Visual Studio and WSL for C++ developers</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>You can easily run and debug your .NET Core and cross platform C++ apps in Linux without leaving Visual Studio using Windows Subsystem for Linux (WSL). If you are a cross-platform developer, you can use this method as a simple way to test more of your target environments.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studio Integration</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studio Integration</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/en-US/Resources.resw",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<root>\r\n  <!--\r\n    Microsoft ResX Schema\r\n\r\n    Version 2.0\r\n\r\n    The primary goals of this format is to allow a simple XML format\r\n    that is mostly human readable. The generation and parsing of the\r\n    various data types are done through the TypeConverter classes\r\n    associated with the data types.\r\n\r\n    Example:\r\n\r\n    ... ado.net/XML headers & schema ...\r\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\r\n    <resheader name=\"version\">2.0</resheader>\r\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\r\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\r\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\r\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\r\n    </data>\r\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\r\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r\n        <comment>This is a comment</comment>\r\n    </data>\r\n\r\n    There are any number of \"resheader\" rows that contain simple\r\n    name/value pairs.\r\n\r\n    Each data row contains a name, and value. The row also contains a\r\n    type or mimetype. Type corresponds to a .NET class that support\r\n    text/value conversion through the TypeConverter architecture.\r\n    Classes that don't support this are serialized and stored with the\r\n    mimetype set.\r\n\r\n    The mimetype is used for serialized objects, and tells the\r\n    ResXResourceReader how to depersist the object. This is currently not\r\n    extensible. For a given mimetype the value must be set accordingly:\r\n\r\n    Note - application/x-microsoft.net.object.binary.base64 is the format\r\n    that the ResXResourceWriter will generate, however the reader can\r\n    read any of the formats listed below.\r\n\r\n    mimetype: application/x-microsoft.net.object.binary.base64\r\n    value   : The object must be serialized with\r\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.soap.base64\r\n    value   : The object must be serialized with\r\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.bytearray.base64\r\n    value   : The object must be serialized into a byte array\r\n            : using a System.ComponentModel.TypeConverter\r\n            : and then encoded with base64 encoding.\r\n    -->\r\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\r\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\r\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\r\n      <xsd:complexType>\r\n        <xsd:choice maxOccurs=\"unbounded\">\r\n          <xsd:element name=\"metadata\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"assembly\">\r\n            <xsd:complexType>\r\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"data\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"resheader\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n        </xsd:choice>\r\n      </xsd:complexType>\r\n    </xsd:element>\r\n  </xsd:schema>\r\n  <resheader name=\"resmimetype\">\r\n    <value>text/microsoft-resx</value>\r\n  </resheader>\r\n  <resheader name=\"version\">\r\n    <value>2.0</value>\r\n  </resheader>\r\n  <resheader name=\"reader\">\r\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <resheader name=\"writer\">\r\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <data name=\"AppName\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem for Linux</value>\r\n  </data>\r\n  <data name=\"AppShortName\" xml:space=\"preserve\">\r\n    <value>WSL</value>\r\n  </data>\r\n  <data name=\"AppDescription\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem for Linux lets developers run a GNU/Linux environment -- including most command-line tools, utilities, and applications -- directly on Windows, unmodified, without the overhead of a traditional virtual machine or dualboot setup.</value>\r\n  </data>\r\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\r\n    <value>The disk failed to detach: {}. For more details, please run 'dmesg' inside WSL2.\r\nTo force WSL2 to stop and detach the disk, run 'wsl.exe {}'.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\r\n    <value>The disk '{}' is already attached.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\r\n    <value>That volume is already mounted inside WSL2.</value>\r\n  </data>\r\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\r\n    <value>A disk with that name is already mounted; please unmount the disk or choose a new name and try again.</value>\r\n  </data>\r\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\r\n    <value>The specified mount name contains an invalid '/' character. Please retry without the invalid character.</value>\r\n  </data>\r\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\r\n    <value>The disk was successfully mounted as '/mnt/wsl/{}'.\r\nNote: The location will be different if you have modified the automount.root setting in /etc/wsl.conf.\r\nTo unmount and detach the disk, run 'wsl.exe {} {}'.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\r\n    <value>The disk was attached but failed to mount: {}.\r\nFor more details, run 'dmesg' inside WSL2.\r\nTo detach the disk, run 'wsl.exe {} {}'.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\r\n    <value>The following is a list of valid distributions that can be installed.\r\nInstall using 'wsl.exe {} &lt;Distro&gt;'.\r\n</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\r\n    <value>The distribution name has already been set.</value>\r\n  </data>\r\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\r\n    <value>The supplied install location is already in use.</value>\r\n  </data>\r\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\r\n    <value>A distribution with the supplied name already exists. Use --name to chose a different name.</value>\r\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\r\n    <value>There is no distribution with the supplied name.</value>\r\n  </data>\r\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\r\n    <value>Administrator access is needed to mount a disk.</value>\r\n  </data>\r\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\r\n    <value>Exporting the distribution failed.</value>\r\n  </data>\r\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\r\n    <value>Performing one-time upgrade of the Windows Subsystem for Linux file system for this distribution...</value>\r\n  </data>\r\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\r\n    <value>Cannot launch because another instance is running un-elevated.  Elevated and un-elevated instances are not permitted to run simultaneously.</value>\r\n  </data>\r\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\r\n    <value>Importing the distribution failed.</value>\r\n  </data>\r\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\r\n    <value>Installing Windows optional component: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\r\n    <value>Downloading: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\r\n    <value>Installing: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\r\n    <value>Importing distribution</value>\r\n  </data>\r\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\r\n    <value>{} has been downloaded.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem for Linux is resuming a previous installation...</value>\r\n  </data>\r\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\r\n    <value>Invalid distribution name: '{}'.\r\nTo get a list of valid distributions, use 'wsl.exe --list --online'.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\r\n    <value>The Windows Subsystem for Linux instance has terminated.</value>\r\n  </data>\r\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\r\n    <value>Invalid command line argument: {}\r\nPlease use '{} --help' to get a list of supported arguments.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\r\n    <value>Command line argument {} requires a value.\r\nPlease use '{} --help' to get a list of supported arguments.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\r\n    <value>Unsupported console settings. In order to use this feature, the legacy console must be disabled.</value>\r\n  </data>\r\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\r\n    <value>{} is not a valid integer.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\r\n    <value>An install, uninstall, or conversion is in progress for this distribution.</value>\r\n  </data>\r\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\r\n    <value>Launching {}...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\r\n    <value>Cannot launch because another instance is running elevated.  Elevated and un-elevated instances are not permitted to run simultaneously.</value>\r\n  </data>\r\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem for Linux has no installed distributions.\r\nYou can resolve this by installing a distribution with the instructions below:\r\n\r\nUse 'wsl.exe --list --online' to list available distributions\r\nand 'wsl.exe --install &lt;Distro&gt;' to install.</value>\r\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\r\n    <value>There are no running distributions.</value>\r\n  </data>\r\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\r\n    <value>{} (Default)</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem for Linux Distributions:</value>\r\n  </data>\r\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\r\n    <value>Default Distribution: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\r\n    <value>Default Version: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\r\n    <value>Unregistering.</value>\r\n  </data>\r\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\r\n    <value>User not found.</value>\r\n  </data>\r\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\r\n    <value>For information on key differences with WSL 2 please visit https://aka.ms/wsl2</value>\r\n  </data>\r\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\r\n    <value>Conversion in progress, this may take a few minutes.</value>\r\n  </data>\r\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\r\n    <value>The distribution is already the requested version.</value>\r\n  </data>\r\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\r\n    <value>The Legacy distribution does not support WSL 2.</value>\r\n  </data>\r\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\r\n    <value>WSL2 is unable to start since virtualization is not enabled on this machine.\r\nPlease ensure the \"Virtual Machine Platform\" optional component is enabled and virtualization is turned on in your computer's firmware settings.\r\n\r\nEnable \"Virtual Machine Platform\" by running: wsl.exe --install --no-distribution\r\n\r\nFor information please visit https://aka.ms/enablevirtualization</value>\r\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\r\n    <value>Too many virtual hard disks or physical disks are attached.</value>\r\n  </data>\r\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\r\n    <value>VHD already mounted via wsl.exe --mount, please unmount the disk before launching.</value>\r\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\r\n    <value>WSL1 is not supported with your current machine configuration.\r\nPlease enable the \"Windows Subsystem for Linux\" optional component to use WSL1.</value>\r\n  </data>\r\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\r\n    <value>This operation is only supported by WSL2.</value>\r\n  </data>\r\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\r\n    <value>Usage:\r\n    --networking-mode\r\n       Display current networking mode.\r\n\r\n    --msal-proxy-path\r\n        Display the path to the MSAL proxy application.\r\n\r\n    --vm-id\r\n        Display the WSL VM ID.\r\n\r\n    --version\r\n        Display the version of the WSL package.\r\n\r\n    -n\r\n        Do not print a newline.</value>\r\n    <comment>{Locked=\"--networking-mode\r\n\"}{Locked=\"--msal-proxy-path\r\n\"}{Locked=\"--vm-id\r\n\"}{Locked=\"--version\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\r\n    <value>Usage:\r\n    -a\r\n        Force result to absolute path format.\r\n    -u\r\n        Translate from a Windows path to a WSL path (default).\r\n    -w\r\n        Translate from a WSL path to a Windows path.\r\n    -m\r\n        Translate from a WSL path to a Windows path, with '/' instead of '\\\\'\r\n\r\nExample: wslpath 'c:\\\\users'</value>\r\n    <comment>{Locked=\"-a\r\n\"}{Locked=\"-u\r\n\"}{Locked=\"-w\r\n\"}{Locked=\"-m\r\n\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\r\n    <value>Performs administrative operations on Windows Subsystem for Linux\r\n\r\nUsage:\r\n    /l, /list [Option]\r\n        Lists registered distributions.\r\n        /all - Optionally list all distributions, including distributions that\r\n               are currently being installed or uninstalled.\r\n\r\n        /running - List only distributions that are currently running.\r\n\r\n    /s, /setdefault &lt;DistributionName&gt;\r\n        Sets the distribution as the default.\r\n\r\n    /t, /terminate &lt;DistributionName&gt;\r\n        Terminates the distribution.\r\n\r\n    /u, /unregister &lt;DistributionName&gt;\r\n        Unregisters the distribution and deletes the root filesystem.</value>\r\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\r\n    <value>Copyright (c) Microsoft Corporation. All rights reserved.\r\nFor privacy information about this product please visit https://aka.ms/privacy.\r\n\r\nUsage: wsl.exe [Argument] [Options...] [CommandLine]\r\n\r\nArguments for running Linux binaries:\r\n\r\n    If no command line is provided, wsl.exe launches the default shell.\r\n\r\n    --exec, -e &lt;CommandLine&gt;\r\n        Execute the specified command without using the default Linux shell.\r\n\r\n    --shell-type &lt;standard|login|none&gt;\r\n        Execute the specified command with the provided shell type.\r\n\r\n    --\r\n        Pass the remaining command line as-is.\r\n\r\nOptions:\r\n    --cd &lt;Directory&gt;\r\n        Sets the specified directory as the current working directory.\r\n        If ~ is used the Linux user's home path will be used. If the path begins\r\n        with a / character, it will be interpreted as an absolute Linux path.\r\n        Otherwise, the value must be an absolute Windows path.\r\n\r\n    --distribution, -d &lt;DistroName&gt;\r\n        Run the specified distribution.\r\n\r\n    --distribution-id &lt;DistroGuid&gt;\r\n        Run the specified distribution ID.\r\n\r\n    --user, -u &lt;UserName&gt;\r\n        Run as the specified user.\r\n\r\n    --system\r\n        Launches a shell for the system distribution.\r\n\r\nArguments for managing Windows Subsystem for Linux:\r\n\r\n    --help\r\n        Display usage information.\r\n\r\n    --debug-shell\r\n        Open a WSL2 debug shell for diagnostics purposes.\r\n\r\n    --install [Distro] [Options...]\r\n        Install a Windows Subsystem for Linux distribution.\r\n        For a list of valid distributions, use 'wsl.exe --list --online'.\r\n\r\n        Options:\r\n            --enable-wsl1\r\n                Enable WSL1 support.\r\n\r\n            --fixed-vhd\r\n                Create a fixed-size disk to store the distribution.\r\n\r\n            --from-file &lt;Path&gt;\r\n                Install a distribution from a local file.\r\n\r\n            --legacy\r\n                Use the legacy distribution manifest.\r\n\r\n            --location &lt;Location&gt;\r\n                Set the install path for the distribution.\r\n\r\n            --name &lt;Name&gt;\r\n                Set the name of the distribution.\r\n\r\n            --no-distribution\r\n                Only install the required optional components, does not install a distribution.\r\n\r\n            --no-launch, -n\r\n                Do not launch the distribution after install.\r\n\r\n            --version &lt;Version&gt;\r\n                Specifies the version to use for the new distribution.\r\n\r\n            --vhd-size &lt;MemoryString&gt;\r\n                Specifies the size of the disk to store the distribution.\r\n\r\n            --web-download\r\n                Download the distribution from the internet instead of the Microsoft Store.\r\n\r\n    --manage &lt;Distro&gt; &lt;Options...&gt;\r\n        Changes distro specific options.\r\n\r\n        Options:\r\n            --move &lt;Location&gt;\r\n                Move the distribution to a new location.\r\n\r\n            --set-sparse, -s &lt;true|false&gt;\r\n                Set the VHD of distro to be sparse, allowing disk space to be automatically reclaimed.\r\n\r\n            --set-default-user &lt;Username&gt;\r\n                Set the default user of the distribution.\r\n\r\n            --resize &lt;MemoryString&gt;\r\n                Resize the disk of the distribution to the specified size.\r\n\r\n    --mount &lt;Disk&gt;\r\n        Attaches and mounts a physical or virtual disk in all WSL 2 distributions.\r\n\r\n        Options:\r\n            --vhd\r\n                Specifies that &lt;Disk&gt; refers to a virtual hard disk.\r\n\r\n            --bare\r\n                Attach the disk to WSL2, but don't mount it.\r\n\r\n            --name &lt;Name&gt;\r\n                Mount the disk using a custom name for the mountpoint.\r\n\r\n            --type &lt;Type&gt;\r\n                Filesystem to use when mounting a disk, if not specified defaults to ext4.\r\n\r\n            --options &lt;Options&gt;\r\n                Additional mount options.\r\n\r\n            --partition &lt;Index&gt;\r\n                Index of the partition to mount, if not specified defaults to the whole disk.\r\n\r\n    --set-default-version &lt;Version&gt;\r\n        Changes the default install version for new distributions.\r\n\r\n    --shutdown\r\n        Immediately terminates all running distributions and the WSL 2\r\n        lightweight utility virtual machine.\r\n\r\n        Options:\r\n            --force\r\n                Terminate the WSL 2 virtual machine even if an operation is in progress. Can cause data loss.\r\n\r\n    --status\r\n        Show the status of Windows Subsystem for Linux.\r\n\r\n    --unmount [Disk]\r\n        Unmounts and detaches a disk from all WSL2 distributions.\r\n        Unmounts and detaches all disks if called without argument.\r\n\r\n    --uninstall\r\n        Uninstalls the Windows Subsystem for Linux package from this machine.\r\n\r\n    --update\r\n        Update the Windows Subsystem for Linux package.\r\n\r\n        Options:\r\n            --pre-release\r\n                Download a pre-release version if available.\r\n\r\n    --version, -v\r\n        Display version information.\r\n\r\nArguments for managing distributions in Windows Subsystem for Linux:\r\n\r\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\r\n        Exports the distribution to a tar file.\r\n        The filename can be - for stdout.\r\n\r\n        Options:\r\n            --format &lt;Format&gt;\r\n                Specifies the export format. Supported values: tar, tar.gz, tar.xz, vhd.\r\n\r\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\r\n        Imports the specified tar file as a new distribution.\r\n        The filename can be - for stdin.\r\n\r\n        Options:\r\n            --version &lt;Version&gt;\r\n                Specifies the version to use for the new distribution.\r\n\r\n            --vhd\r\n                Specifies that the provided file is a .vhd or .vhdx file, not a tar file.\r\n                This operation makes a copy of the VHD file at the specified install location.\r\n\r\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\r\n        Imports the specified VHD file as a new distribution.\r\n        This virtual hard disk must be formatted with the ext4 filesystem type.\r\n\r\n    --list, -l [Options]\r\n        Lists distributions.\r\n\r\n        Options:\r\n            --all\r\n                List all distributions, including distributions that are\r\n                currently being installed or uninstalled.\r\n\r\n            --running\r\n                List only distributions that are currently running.\r\n\r\n            --quiet, -q\r\n                Only show distribution names.\r\n\r\n            --verbose, -v\r\n                Show detailed information about all distributions.\r\n\r\n            --online, -o\r\n                Displays a list of available distributions for install with 'wsl.exe --install'.\r\n\r\n    --set-default, -s &lt;Distro&gt;\r\n        Sets the distribution as the default.\r\n\r\n    --set-version &lt;Distro&gt; &lt;Version&gt;\r\n        Changes the version of the specified distribution.\r\n\r\n    --terminate, -t &lt;Distro&gt;\r\n        Terminates the specified distribution.\r\n\r\n    --unregister &lt;Distro&gt;\r\n        Unregisters the distribution and deletes the root filesystem.</value>\r\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\r\n\"}{Locked=\"--help\r\n\"}{Locked=\"--debug-shell\r\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\r\n\"}{Locked=\"--fixed-vhd\r\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\r\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\r\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\r\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\r\n\"}{Locked=\"--bare\r\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\r\n\"}{Locked=\"--force\r\n\"}{Locked=\"--status\r\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\r\n\"}{Locked=\"--update\r\n\"}{Locked=\"--pre-release\r\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\r\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\r\n\"}{Locked=\"--running\r\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\r\n    <value>WSL version: {}\r\nKernel version: {}\r\nWSLg version: {}\r\nMSRDC version: {}\r\nDirect3D version: {}\r\nDXCore version: {}\r\nWindows version: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\r\n    <value>MSBuild version: {}\r\nCommit: {}\r\nBuild time: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\r\n    <value>The custom kernel specified in {} was not found: '{}'.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\r\n    <value>The custom kernel modules VHD in {} was not found: '{}'.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\r\n    <value>The custom system distribution specified in {} was not found or is not the correct format.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\r\n    <value>Copyright (c) Microsoft Corporation. All rights reserved.\r\nFor privacy information about this product please visit https://aka.ms/privacy.\r\n\r\nUsage: wslg.exe [Argument] [Options...] [CommandLine]\r\n\r\nArguments:\r\n    --cd &lt;Directory&gt;\r\n        Sets the specified directory as the current working directory.\r\n        If ~ is used the Linux user's home path will be used. If the path begins\r\n        with a / character, it will be interpreted as an absolute Linux path.\r\n        Otherwise, the value must be an absolute Windows path.\r\n\r\n    --distribution, -d &lt;Distro&gt;\r\n        Run the specified distribution.\r\n\r\n    --user, -u &lt;UserName&gt;\r\n        Run as the specified user.\r\n\r\n    --shell-type &lt;standard|login|none&gt;\r\n        Execute the specified command with the provided shell type.\r\n\r\n    --help\r\n        Display usage information.\r\n\r\n    --\r\n        Pass the remaining command line as-is.</value>\r\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\r\n    <value>Press any key to continue...</value>\r\n  </data>\r\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\r\n    <value>Argument {} is missing a required parameter.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\r\n    <value>Export in progress, this may take a few minutes.</value>\r\n  </data>\r\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\r\n    <value>Import in progress, this may take a few minutes.</value>\r\n  </data>\r\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\r\n    <value>GUI application support is disabled via {} or /etc/wsl.conf.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\r\n    <value>Checking for updates.</value>\r\n  </data>\r\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\r\n    <value>This distribution is only available from the Microsoft Store.</value>\r\n  </data>\r\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\r\n    <value>wsl.exe --mount on ARM64 requires Windows version 27653 or newer.</value>\r\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\r\n    <value>The most recent version of Windows Subsystem for Linux is already installed.</value>\r\n  </data>\r\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\r\n    <value>Updating Windows Subsystem for Linux to version: {}.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\r\n    <value>This application requires the Windows Subsystem for Linux Optional Component.\r\nInstall it by running: wsl.exe --install --no-distribution\r\nThe system may need to be restarted so the changes can take effect.</value>\r\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\r\n    <value>The specified file must have the {} file extension.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\r\n    <value>The specified file must have the {} or {} file extension.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\r\n    <value>The VmSwitch '{}' was not found. Available switches: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\r\n    <value>Bridged networking requires wsl2.vmSwitch to be set.</value>\r\n  </data>\r\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\r\n    <value>Failed to fetch the distribution list from '{}'. {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\r\n    <value>Failed to attach disk '{}' to WSL2: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\r\n    <value>Failed to resize disk.</value>\r\n  </data>\r\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\r\n    <value>No value found.</value>\r\n  </data>\r\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\r\n    <value>Windows version {} does not support the packaged version of Windows Subsystem for Linux.\r\nInstall the required update via Windows update or via: {}\r\nFor information please visit https://aka.ms/wslinstall</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\r\n    <value>Running the debug shell requires running wsl.exe as Administrator.</value>\r\n  </data>\r\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\r\n    <value>The installation process for distribution '{}' failed with exit code: {}.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\r\n    <value>Invalid GUID format: '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\r\n    <value>Invalid section name in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\r\n    <value>Invalid key name in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\r\n    <value>Expected {} in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n    <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\r\n    <value>Invalid escaped character: '{}' in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\r\n    <value>Unknown key '{}' in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\r\n    <value>Invalid integer value '{}' for key '{}' in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\r\n    <value>Invalid IP value '{}' for key '{}' in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\r\n    <value>Invalid boolean value '{}' for key '{}' in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\r\n    <value>Invalid mac address '{}' for key '{}' in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\r\n    <value>Failed to open config file {}, {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\r\n    <value>Errors occurred during WSL startup</value>\r\n  </data>\r\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\r\n    <value>Failed to start the systemd user session for '{}'. See journalctl for more details.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\r\n    <value>Open EventViewer</value>\r\n  </data>\r\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\r\n    <value>This distribution doesn't contain a default name. Use --name to chose the distribution name.</value>\r\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\r\n    <value>Arguments {} and {} can't be specified at same time.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\r\n    <value>Argument {} requires the {} argument.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\r\n    <value>Invalid distribution name: \"{}\".</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\r\n    <value>Using legacy distribution registration. Consider using a tar based distribution instead.</value>\r\n  </data>\r\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\r\n    <value>Legacy distribution registrations do not support the --version argument.</value>\r\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\r\n    <value>The distribution \"{}\" is provided by an override manifest.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\r\n    <value>The distribution hash doesn't match. Expected: {}, actual hash: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\r\n    <value>Invalid hex string: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\r\n    <value>'{}' is not supported when installing legacy distributions.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\r\n    <value>Distribution successfully installed. It can be launched via 'wsl.exe -d {}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\r\n    <value>Invalid memory string '{}' for .wslconfig entry '{}' in {}:{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\r\n    <value>Failed to create the swap disk in '{}': {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\r\n    <value>Nested virtualization is not supported on this machine.</value>\r\n  </data>\r\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\r\n    <value>Failed to create network endpoint with address: '{}', assigned new address: '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\r\n    <value>Failed to create virtual network with address range: '{}', created new network with range: '{}', {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\r\n    <value>SAFE MODE ENABLED - many features will be disabled</value>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\r\n    <value>Mirrored networking mode is not supported: {}.\r\nFalling back to NAT networking.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\r\n    <value>Linux Kernel version 5.10 or newer is required</value>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\r\n    <value>Windows version {}.{} does not have the required features</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\r\n    <value>Hyper-V firewall is not supported</value>\r\n  </data>\r\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\r\n    <value>DNS Tunneling is not supported</value>\r\n  </data>\r\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\r\n    <value>The wsl2.localhostForwarding setting has no effect when using mirrored networking mode</value>\r\n  </data>\r\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\r\n    <value>An Http Proxy change has been detected on the host. Please restart WSL to apply the change.</value>\r\n  </data>\r\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\r\n    <value>A localhost proxy configuration was detected but not mirrored into WSL. WSL in NAT mode does not support localhost proxies.</value>\r\n  </data>\r\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\r\n    <value>An IPv6 proxy configuration was detected but not mirrored into WSL. WSL in NAT mode does not support IPv6.</value>\r\n  </data>\r\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\r\n    <value>A localhost IPv6 proxy configuration was detected but not mirrored into WSL. WSL does not support localhost IPv6 proxies.</value>\r\n  </data>\r\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\r\n    <value>An unexpected error occurred while attempting to resolve proxy settings, proxy settings were not mirrored into WSL.</value>\r\n  </data>\r\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\r\n    <value>An error occurred accessing the registry. Path: '{}'. Error: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\r\n    <value>Failed to launch the localhost relay process. Error: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\r\n    <value>Failed to start virtual networking - please install the optional component Virtual Machine Platform by running: wsl.exe --install --no-distribution</value>\r\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\r\n    <value>Failed to configure network (networkingMode {}), falling back to networkingMode {}.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\r\n    <value>Failed to enable Windows component '{}' (exit code {})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\r\n    <value>A fatal error was returned by plugin '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\r\n    <value>A fatal error was returned by plugin '{}'. Error message: '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\r\n    <value>The plugin '{}' requires a newer version of WSL. Please run: wsl.exe --update</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\r\n    <value>The .wslconfig setting '{}' is disabled by the computer policy.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\r\n    <value>The debug shell is disabled by the computer policy.</value>\r\n  </data>\r\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\r\n    <value>wsl.exe --mount is disabled by the computer policy.</value>\r\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\r\n    <value>WSL1 is disabled by the computer policy.</value>\r\n  </data>\r\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\r\n    <value>Please run 'wsl.exe --set-version {} 2' to upgrade to WSL2.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\r\n    <value>WSL is finishing an upgrade...</value>\r\n  </data>\r\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\r\n    <value>Update failed (exit code: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\r\n    <value>Uninstall failed (exit code: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\r\n    <value>Log file: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\r\n    <value>Failed to remove the MSIX package (error: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\r\n    <value>Failed to install the MSIX package (error: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\r\n    <value>Optional components needed to run WSL are not installed.</value>\r\n  </data>\r\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\r\n    <value>Install missing components.</value>\r\n  </data>\r\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\r\n    <value>The imported file is not a valid Linux distribution.</value>\r\n  </data>\r\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\r\n    <value>Press any key to exit...</value>\r\n  </data>\r\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\r\n    <value>A new version of Windows Subsystem for Linux is available.</value>\r\n  </data>\r\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\r\n    <value>Update to version {} or view its release notes below.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\r\n    <value>Update</value>\r\n  </data>\r\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\r\n    <value>See Docs</value>\r\n  </data>\r\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\r\n    <value>The operation could not be completed because the VHD is currently in use. To force WSL to stop use: wsl.exe --shutdown</value>\r\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\r\n    <value>{} is not a valid boolean, &lt;true|false&gt;</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\r\n    <value>Sparse VHD is supported on WSL2 only.</value>\r\n  </data>\r\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\r\n    <value>Running WSL as local system is not supported.</value>\r\n  </data>\r\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\r\n    <value>Performance Tip:</value>\r\n  </data>\r\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\r\n    <value>Using an I/O intensive operation like {} on your Windows drives will have poor performance. Consider moving your project files to the Linux file system for better performance. Click below to learn more.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\r\n    <value>View Docs</value>\r\n  </data>\r\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\r\n    <value>Don't Show Again</value>\r\n  </data>\r\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\r\n    <value>The WSL2 Virtual Machine crashed.</value>\r\n  </data>\r\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\r\n    <value>The stack trace has been saved in: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\r\n    <value>The distribution failed to start. Error code: {}, failure step: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\r\n    <value>The distribution failed to start because its virtual disk is corrupted.</value>\r\n  </data>\r\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\r\n    <value>Duplicated config key '{}' in {}:{} (Conflicting key: '{}' in {}:{})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\r\n    <value>Invalid value '{}' for config key '{}' in {}:{} (Valid values: {})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n    <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\r\n    <value>Waiting for OOBE command to complete for distribution \"{}\"...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\r\n    <value>Failed to read property '{}' from distribution {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\r\n    <value>This looks like a VHD file. Use --vhd to import a VHD instead of a tar.</value>\r\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\r\n    <value>Failed to install {} from the Microsoft Store: {}\r\nAttempting web download...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\r\n    <value>No default distribution has been configured. Please provide a distribution to install.</value>\r\n  </data>\r\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\r\n    <value>wsl2.virtio9p is disabled, falling back to 9p with vsock transport.</value>\r\n  </data>\r\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\r\n    <value>WSL installation appears to be corrupted (Error code: {}).\r\nPress any key to repair WSL, or CTRL-C to cancel.\r\nThis prompt will time out in 60 seconds.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\r\n    <value>Failed to parse terminal profile while registering distribution: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n    <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\r\n    <value>Invalid size: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\r\n    <value>{}\r\nError code: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n    <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\r\n    <value>Invalid JSON document. Parse error: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\r\n    <value>wsl2.processors cannot exceed the number of logical processors on the system ({} &gt; {})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\r\n    <value>Failed to query networking mode</value>\r\n  </data>\r\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\r\n    <value>Customised kernel modules were supplied without specifying a customised kernel. Please see https://aka.ms/wslcustomkernel for more information.</value>\r\n  </data>\r\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\r\n    <value>Due to a current compatibility issue with Global Secure Access Client, DNS Tunneling is disabled.</value>\r\n  </data>\r\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\r\n    <value>Sparse VHD support is currently disabled due to potential data corruption.\r\nTo force a distribution to use a sparse VHD, please run:\r\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\r\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\r\n    <value>Failed to mount {}, see dmesg for more details.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\r\n    <value>Processing /etc/fstab with mount -a failed.</value>\r\n  </data>\r\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\r\n    <value>An error occurred mounting the distribution disk, it was mounted read-only as a fallback.\r\nSee recovery instructions on: https://aka.ms/wsldiskmountrecovery</value>\r\n  </data>\r\n    <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\r\n    <value>Failed to translate '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\r\n    <value>Something went wrong. Try again later.</value>\r\n  </data>\r\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>About</value>\r\n  </data>\r\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\r\n    <value>About Windows Subsystem for Linux Settings</value>\r\n  </data>\r\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\r\n    <value>© Microsoft. All rights reserved.</value>\r\n  </data>\r\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem for Linux Settings</value>\r\n  </data>\r\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem for Linux Settings lets developers manage the .wslconfig file using a GUI-based application.</value>\r\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\r\n    <value>Auto memory reclaim</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\r\n    <value>Automatically releases cached memory after detecting idle CPU usage. Set to gradual for slow release, and dropcache for instant release of cached memory.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Auto memory reclaim.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Automatically releases cached memory after detecting idle CPU usage. Set to gradual for slow release, and dropcache for instant release of cached memory.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\r\n    <value>Auto Proxy enabled</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\r\n    <value>Enables WSL to use Windows' HTTP proxy information.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Auto Proxy enabled.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Enables WSL to use Windows' HTTP proxy information.</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\r\n    <value>Use best effort DNS parsing</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.dnsTunneling is set to true. When set to true, Windows will extract the question from the DNS request and attempt to resolve it, ignoring the unknown records.</value>\r\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Use best effort DNS parsing.</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.dnsTunneling is set to true. When set to true, Windows will extract the question from the DNS request and attempt to resolve it, ignoring the unknown records.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\r\n    <value>Custom kernel</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\r\n    <value>An absolute Windows path to a custom Linux kernel.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>Browse kernels</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Custom kernel</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>An absolute Windows path to a custom Linux kernel.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\r\n    <value>Custom kernel modules</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\r\n    <value>An absolute Windows path to a custom Linux kernel modules VHD.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>Browse kernel modules</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Custom kernel modules</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>An absolute Windows path to a custom Linux kernel modules VHD.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\r\n    <value>Custom system distro</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>Browse distros</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\r\n    <value>Specify a path to a VHD which will load as a custom system distro, primarily used to power GUI apps in WSL. [Learn more about system distros here].</value>\r\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgsystemdistro</value>\r\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Custom system distro</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Specify a path to a VHD which will load as a custom system distro, primarily used to power GUI apps in WSL.</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\r\n    <value>Enable debug console</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\r\n    <value>Boolean to turn on an output console window that shows the contents of dmesg upon start of a WSL 2 distro instance.</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Enable debug console.</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Boolean to turn on an output console window that shows the contents of diagnostic messages upon start of a WSL 2 distro instance.</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\r\n    <value>Default VHD Size</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\r\n    <value>The default max size for the expandable WSL virtual hard disk (VHD) for newly created distributions only.</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Reset size</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Default VHD Size</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>The default max size, specified in megabytes, for the expandable WSL virtual hard disk (VHD) for newly created distributions only.</value>\r\n  </data>\r\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Developer</value>\r\n  </data>\r\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\r\n    <value>Documentation</value>\r\n  </data>\r\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocs</value>\r\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\r\n    <value>Change DrvFS mode</value>\r\n  </data>\r\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\r\n    <value>Changes cross-OS file access implementation in WSL.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\r\n    <value>DNS Proxy enabled</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.networkingMode is set to NAT. Boolean to inform WSL to configure the DNS Server in Linux to the NAT on the host. Setting to false will mirror DNS servers from Windows to Linux.</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>DNS Proxy enabled.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.networkingMode is set to NAT. Boolean to inform WSL to configure the DNS Server in Linux to the NAT on the host. Setting to false will mirror DNS servers from Windows to Linux.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\r\n    <value>DNS Tunneling enabled</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\r\n    <value>Changes how DNS requests are proxied from WSL to Windows.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>DNS Tunneling enabled.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Changes how DNS requests are proxied from WSL to Windows.</value>\r\n  </data>\r\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>File System</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\r\n    <value>Enable GUI applications</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\r\n    <value>Boolean to turn on or off support for GUI applications ([WSLg]) in WSL.</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgproject</value>\r\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Enable GUI applications.</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Boolean to turn on or off support for GUI applications (known as WSL g) in WSL.</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\r\n    <value>Enable hardware performance counters</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\r\n    <value>Enables hardware performance counters for Linux.</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Enable hardware performance counters.</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Enables hardware performance counters for Linux.</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\r\n    <value>Hyper-V Firewall enabled</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\r\n    <value>Enables Hyper-V firewall which allows the Windows Firewall rules, as well as rules specific to Hyper-V traffic, to filter WSL network traffic.</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Hyper-V Firewall enabled.</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Enables Hyper-V firewall which allows the Windows Firewall rules, as well as rules specific to Hyper-V traffic, to filter WSL network traffic.</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\r\n    <value>Host Address Loopback</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.networkingMode is set to mirrored. When set to True, will allow the Container to connect to the Host, or the Host to connect to the Container, by an IP address that's assigned to the Host. Note that the 127.0.0.1 loopback address can always be used - this option allows for all additionally assigned local IP addresses to be used as well.</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Host Address Loopback.</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.networkingMode is set to mirrored. When set to True, will allow the Container to connect to the Host, or the Host to connect to the Container, by an IP address that's assigned to the Host. Note that the 127.0.0.1 loopback address can always be used - this option allows for all additionally assigned local IP addresses to be used as well.</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\r\n    <value>Ignored ports</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.networkingMode is set to mirrored. Specifies which ports Linux applications can bind to that will not be automatically forwarded or considered in Windows. Should be formatted in a comma separated list, e.g: 3000,9000,9090.</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Reset ports</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Ignored ports</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.networkingMode is set to mirrored. Specifies which ports Linux applications can bind to that will not be automatically forwarded or considered in Windows. Should be formatted in a comma separated list, for example, 3000, 9000, 9090.</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\r\n    <value>Initial Auto Proxy timeout</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.autoProxy is set to true. Configures how long (in milliseconds) WSL will wait for retrieving HTTP proxy information when starting a WSL container. If proxy settings are resolved after this time, the WSL instance must be restarted to use the retrieved proxy settings.</value>\r\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Reset timeout</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Initial Auto Proxy timeout</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Only applicable when wsl2.autoProxy is set to true. Configures how long, in milliseconds, WSL will wait for retrieving HTTP proxy information when starting a WSL container. If proxy settings are resolved after this time, the WSL instance must be restarted to use the retrieved proxy settings.</value>\r\n  </data>\r\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\r\n    <value>Kernel Command Line</value>\r\n  </data>\r\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\r\n    <value>Additional kernel command line arguments.</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\r\n    <value>Enable localhost forwarding</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\r\n    <value>Boolean specifying if ports bound to wildcard or localhost in the WSL 2 VM should be connectable from the host via localhost:port.</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Enable localhost forwarding.</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Boolean specifying if ports bound to wildcard or localhost in the WSL 2 VM should be connectable from the host via localhost, colon, port.</value>\r\n  </data>\r\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\r\n    <value>{0} MB</value>\r\n  </data>\r\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Memory and processor</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\r\n    <value>Memory Size</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\r\n    <value>How much memory to assign to the WSL 2 VM.</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Reset size</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Memory Size</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>How much memory, specified in megabytes, to assign to the WSL 2 VM.</value>\r\n  </data>\r\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\r\n    <value>{0} milliseconds</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\r\n    <value>Enable nested virtualization</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\r\n    <value>Boolean to turn on or off nested virtualization, enabling other nested VMs to run inside WSL 2.</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Enable nested virtualization.</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Boolean to turn on or off nested virtualization, enabling other nested VM's to run inside WSL 2.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\r\n    <value>Networking mode</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\r\n    <value>Specifies the networking mode for WSL.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Networking mode.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Specifies the networking mode for WSL.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Networking</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\r\n    <value>You can work across operating systems with all of your files!</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\r\n    <value>Cross OS File Access</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\r\n    <value>Access your Windows files from Linux</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\r\n    <value>You can access your Windows files from within Linux by navigating to ‘/mnt’ and then to your Windows drive letter, like this example for the C drive:\r\n\r\n'cd /mnt/c'</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\r\n    <value>Access your Linux files with File Explorer</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\r\n    <value>You can view your Linux files from File Explorer by navigating to '\\\\wsl.localhost\\' or clicking on the ‘Linux’ icon.</value>\r\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\r\n    <value>Launch Windows files and programs from WSL</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\r\n    <value>Even while using WSL you can run your Windows executables directly from bash. Try running\r\n'powershell.exe /c start .' to open up File Explorer in your current folder.</value>\r\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\r\n    <value>Accessing Linux Networking Apps from Windows</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\r\n    <value>If you are building a networking app (for example an app running on a NodeJS or SQL server) in your Linux distribution, you can access it from a Windows app (like your Edge or Chrome internet browser) using localhost (just like you normally would). This means if you started a Linux server that is listening to port 3000, you can go to [http://localhost:3000] in Edge on Windows to access it.</value>\r\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\r\n    <value>http://localhost:3000</value>\r\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\r\n    <value>Mirrored Mode Networking</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\r\n    <value>WSL also includes a new networking mode, called mirrored mode which adds advanced capabilities like IPv6 support and the ability to access your networking applications in your local area network.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn More About Cross OS File Access</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslfilesystems</value>\r\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\r\n    <value>Welcome to Windows Subsystem for Linux</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\r\n   <value>WSL is a great way to try out different Linux distributions.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\r\n    <value>Distro Management</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn more about basic WSL commands</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslcommands</value>\r\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn more about importing any Linux distribution</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslcustomdistro</value>\r\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>List Installable WSL Distros Command</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>'wsl.exe -l -o'</value>\r\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>Install a Named WSL Distro Command</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\r\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>List Available WSL Distros Command</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>'wsl.exe -l'</value>\r\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\r\n    <value>Docker Desktop works great with WSL to help you develop with Linux containers.\r\n\r\nSome of the benefits of using Docker Desktop with WSL are:\r\n\r\n• You can run Docker commands in WSL or in Windows, using the same Docker daemon and images.\r\n• You can share files and folders between Windows and Linux seamlessly, using the automatic mount of Windows drives in WSL.\r\n• You can use your preferred Windows tools and editors to work on Linux code and files, and vice versa, thanks to the interoperability of WSL.</value>\r\n </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\r\n    <value>Docker Desktop Integration</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn More About Using WSL with Docker</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocker</value>\r\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\r\n    <value>The Windows Subsystem for Linux (WSL) lets you run your favorite Linux tools, utilities, applications, and workflows directly on Windows.\r\n\r\nTake a moment to preview some of the community’s favorite features or view our comprehensive documentation.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\r\n    <value>Welcome to WSL</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\r\n    <value>Best Practices for Setup</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslsetup</value>\r\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\r\n    <value>Getting Started with Linux</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgettingstarted</value>\r\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem for Linux (WSL) Documentation</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocs</value>\r\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\r\n    <value>You can use graphical based Linux applications with native Windows interactions like alt-tab, start menu launching, task bar pinning, and cut &amp; paste support.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\r\n    <value>GUI Apps</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\r\n    <value>WSL can leverage your Windows GPU for Machine Learning workflows\r\n\r\nLinux binaries running in WSL can automatically use your GPU in Windows to speed up their performance. You just need to install and run those workflows the same way that you would on a regular Linux machine. There are lots of different ways to get started from running CUDA in a docker container if you have a NVIDIA graphics card, to running PyTorch or TensorFlow with DirectML on your AMD, Intel, or NVIDIA graphics card. See our getting started guide below to learn more.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\r\n    <value>GPU Acceleration</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn More About GPU Acceleration</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgpu</value>\r\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn More About WSL GUI Apps</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslguiapps</value>\r\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\r\n    <value>Here’s a list of apps to try (You can install all of these in Ubuntu by typing 'sudo apt install &lt;The App Name&gt;')\r\n\r\n    • gedit – Basic text editor\r\n    • audacity – Record and edit audio files\r\n    • blender – Make 3D animations and visualizations\r\n    • gimp – Edit photos\r\n    • nautilus – A Linux file explorer\r\n    • vlc – Video player</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\r\n    <value>You can easily access networking apps across Windows and Linux operating systems.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\r\n    <value>Networking Integration</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn More About Networking Applications</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslnetworking</value>\r\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn More About Mirrored Mode Networking</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslmirroredmode</value>\r\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\r\n   <value>You can use WSL as your full-time development environment directly from VS Code.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\r\n    <value>VS Code Integration</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn More About Using WSL with VS Code</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslvscode</value>\r\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\r\n    <value>How to install</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\r\n    <value>After you’ve installed VS Code, you can install the Remote WSL extension from Windows Terminal:\r\n\r\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\r\n    <value>Open a WSL Project in Visual Studio Code</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\r\n    <value>To open a project in VS Code from your WSL distribution, open the distribution’s command line and run 'code .' to open a project file.\r\n\r\nYou can also access more VS Code Remote options through the command palette within VS Code itself. Hit “SHIFT+CTRL+P” on your keyboard to open the command palette and type ‘Remote-WSL’ to see a list of the VS Code Remote options available, allowing you to reopen the folder in a remote session, specify which distribution you want to open, and more.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\r\n    <value>How to use</value>\r\n  </data>\r\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>Optional Features</value>\r\n  </data>\r\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\r\n    <value>Privacy Statement</value>\r\n  </data>\r\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\r\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\r\n  </data>\r\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\r\n    <value>Processor Count</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\r\n    <value>How many logical processors to assign to the WSL 2 VM.</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Reset count</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Processor Count</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>How many logical processors to assign to the WSL 2 VM.</value>\r\n  </data>\r\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\r\n    <value>Related links</value>\r\n  </data>\r\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\r\n    <value>Release notes</value>\r\n  </data>\r\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslreleases</value>\r\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\r\n  </data>\r\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\r\n    <value>Enable safe mode</value>\r\n  </data>\r\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\r\n    <value>Run WSL in \"Safe Mode\" which disables many features and is intended to be used to recover distributions that are in bad states.</value>\r\n  </data>\r\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Enable safe mode.</value>\r\n  </data>\r\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Run WSL in \"Safe Mode\" which disables many features and is intended to be used to recover distributions that are in bad states.</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\r\n    <value>About</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\r\n    <value>Developer</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\r\n    <value>Distro Management</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Docker Desktop Integration</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\r\n    <value>File System</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\r\n    <value>General</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\r\n    <value>GPU Acceleration</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\r\n    <value>GUI Apps</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\r\n    <value>Launch wsl.exe</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\r\n    <value>Memory and processor</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\r\n    <value>Networking</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Networking Integration</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\r\n    <value>Welcome to WSL</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\r\n    <value>Optional Features</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\r\n    <value>Settings</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\r\n    <value>VS Code Integration</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\r\n    <value>What's new</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\r\n    <value>Working Across File Systems</value>\r\n  </data>\r\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\r\n    <value>Issues</value>\r\n  </data>\r\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslproject</value>\r\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\r\n  </data>\r\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\r\n    <value>Enable sparse VHD by default</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\r\n    <value>Any newly created VHD will be set to sparse automatically when enabled.</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Enable sparse VHD by default.</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Any newly created VHD will be set to sparse automatically when enabled.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\r\n    <value>Swap File Location</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\r\n    <value>An absolute Windows path to the swap virtual hard disk.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>Browse swap files</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Swap File Location</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>An absolute Windows path to the swap virtual hard disk.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\r\n    <value>Swap Size</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\r\n    <value>How much swap space to add to the WSL 2 VM, 0 for no swap file. Swap storage is disk-based RAM used when memory demand exceeds limit on hardware device.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Reset size</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Swap Size</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>How much swap space, specified in megabytes, to add to the WSL 2 VM. 0 for no swap file. Swap storage is disk-based RAM used when memory demand exceeds limit on hardware device.</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\r\n    <value>Enable VirtIO</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\r\n    <value>Use virtiofs instead of plan 9 to access host files, increasing speed.</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\r\n    <value>Enable Virtio 9p</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\r\n    <value>Enables mount of the 9P filesystem from the host using the virtio transport.</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\r\n    <value>VM Idle timeout</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\r\n    <value>The number of milliseconds that a VM is idle, before it is shut down.</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\r\n    <value>Reset timeout</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>VM Idle timeout</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>The number of milliseconds that a VM is idle, before it is shut down.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\r\n    <value>Build, run, debug, and profile your apps running on WSL from Visual Studio</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\r\n    <value>Run and debug your apps on WSL from Visual Studio</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn more about WSL in Visual Studio for .NET developers</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\r\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\r\n    <value>Learn more about Visual Studio and WSL for C++ developers</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\r\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\r\n    <value>You can easily run and debug your .NET Core and cross platform C++ apps in Linux without leaving Visual Studio using Windows Subsystem for Linux (WSL). If you are a cross-platform developer, you can use this method as a simple way to test more of your target environments.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\r\n    <value>Visual Studio Integration</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Visual Studio Integration</value>\r\n  </data>\r\n</root>\r\n"
  },
  {
    "path": "localization/strings/es-ES/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Subsistema de Windows para Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>El subsistema de Windows para Linux permite a los desarrolladores ejecutar un entorno GNU/Linux, que incluye la mayoría de las herramientas, utilidades y aplicaciones de la línea de comandos, directamente en Windows, sin modificar, sin la sobrecarga de una máquina virtual tradicional o una instalación de arranque dual.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>El disco no se pudo desasociar: {}. Para obtener más detalles, ejecute 'dmesg' dentro de WSL2.\nPara forzar que WSL2 detenga y desasocia el disco, ejecute 'wsl.exe {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Ese disco {} ya está expuesto.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Ese volumen ya está montado dentro de WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Ya hay un disco montado con ese nombre; desmonte el disco o elija un nuevo nombre e inténtelo de nuevo.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>El nombre de montaje especificado contiene un carácter '/' no válido. Vuelva a intentarlo sin el carácter no válido.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>El disco se montó correctamente como '/mnt/wsl/{}'.\nNota: la ubicación será diferente si ha modificado la configuración automount.root en /etc/wsl.conf.\nPara desmontar y desasociar el disco, ejecute 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>El disco se conectó, pero no se pudo montar: {}.\nPara obtener más detalles, ejecute 'dmesg' dentro de WSL2.\nPara desasociar el disco, ejecute 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>A continuación se muestra una lista de distribuciones válidas que se pueden instalar.\nInstalar con \"wsl.exe {} &lt;Distro&gt;\".\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Ya se ha establecido el nombre de distribución.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>La ubicación de instalación proporcionada ya está en uso.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Ya existe una distribución con el nombre proporcionado. Use --name para elegir un nombre diferente.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>No hay ninguna distribución con el nombre proporcionado.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Se necesita acceso de administrador para montar un disco.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Error al exportar la distribución.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Realizando la actualización única del subsistema de Windows para el sistema de archivos Linux para esta distribución...\n</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>No se puede iniciar porque se está ejecutando otra instancia sin privilegios elevados. No se pueden ejecutar instancias con y sin privilegios elevados a la vez.\n</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Error al importar la distribución.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Instalando componente opcional de Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Descargando: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Instalando: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importando distribución</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} Se ha descargado.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Subsistema de Windows para Linux está reanudando una instalación anterior...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Nombre de distribución no válido: '{}'.\nPara obtener una lista de distribuciones válidas, use 'wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>La instancia de Subsistema de Windows para Linux ha finalizado.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Argumento de línea de comandos no válido: {}\nUse '{} --help' para obtener una lista de los argumentos admitidos.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>El argumento {} de la línea de comandos requiere un valor.\nUse '{} --help' para obtener una lista de los argumentos admitidos.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>La configuración de la consola no se admite. Para poder usar esta característica, la consola heredada debe estar deshabilitada.\n</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} no es un número entero válido.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Hay una instalación, desinstalación o conversión en curso para esta distribución.\n</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Iniciando {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>No se puede iniciar porque se está ejecutando otra instancia con privilegios elevados. No se pueden ejecutar instancias con y sin privilegios elevados a la vez.\n</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Subsistema de Windows para Linux no tiene distribuciones instaladas.\nPara resolverlo, instale una distribución con las instrucciones siguientes:\n\nUse 'wsl.exe --list --online' para enumerar las distribuciones disponibles\ny \"wsl.exe --install &lt;Distro&gt;\" para instalar.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>No hay distribuciones en ejecución.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Predeterminado)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Distribuciones de subsistema de Windows para Linux:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Distribución predeterminada: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Versión predeterminada: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Anulando el registro.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>No se encontró al usuario.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Para obtener información sobre las diferencias clave con WSL 2, visita https://aka.ms/wsl2\n</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Conversión en curso. Esta operación puede tardar unos minutos.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>La distribución ya es la versión solicitada.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>La distribución heredada no admite WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>WSL2 no se puede iniciar porque la virtualización no está habilitada en esta máquina.\nAsegúrese de que el componente opcional \"Plataforma de máquina virtual\" esté habilitado y de que la virtualización esté activada en la configuración de firmware del equipo.\n\nHabilitar \"Plataforma de máquina virtual\" ejecutando: wsl.exe --install --no-distribution\n\nPara obtener información, visite https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Hay demasiados discos duros virtuales o físicos conectados.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD ya montado a través de wsl.exe --mount, desmonte el disco antes de iniciarlo.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 no es compatible con la configuración actual del equipo.\nHabilita el componente opcional \"Subsistema de Windows para Linux\" para usar WSL1.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Esta operación solo es compatible con WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Uso:\n    --networking-mode\n       Muestra el modo de red actual.\n\n    --msal-proxy-path\n        Muestra la ruta de acceso a la aplicación proxy MSAL.\n\n    --vm-id\n        Muestra el ID de la máquina virtual de WSL.\n\n    --version\n        Muestra la versión del paquete de WSL.\n\n    -n\n        No imprima una nueva línea.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Uso:\n    -a\n        Forzar el resultado al formato de ruta de acceso absoluta.\n    -u\n        Traducir de una ruta de acceso de Windows a una ruta de acceso WSL (valor predeterminado).\n    -w\n        Traducir de una ruta de acceso de WSL a una ruta de acceso de Windows.\n    -m\n        Traducir de una ruta de acceso WSL a una ruta de acceso de Windows, con '/' en lugar de '\\\\'\n\nEjemplo: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Realiza operaciones administrativas en el Subsistema de Windows para Linux\n\nUso:\n    /l, /list [Option]\n        Lista las distribuciones registradas.\n        /all - Opcionalmente, lista todas las distribuciones, incluidas las distribuciones que\n               se están instalando o desinstalando actualmente.\n\n        /running - Lista solo las distribuciones que se están ejecutando actualmente.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Establece la distribución como predeterminada.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Finaliza la distribución.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Anula el registro de la distribución y elimina el sistema de archivos raíz.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Todos los derechos reservados.\nPara obtener información de privacidad sobre este producto, visite https://aka.ms/privacy.\n\nUso: wsl.exe [Argumento] [Opciones...] [Línea de comandos]\n\nArgumentos para ejecutar archivos binarios de Linux:\n\n    Si no se proporciona ninguna línea de comandos, wsl.exe inicia el shell predeterminado.\n\n    --exec, -e &lt;CommandLine&gt;\n        Ejecute el comando especificado sin usar el shell de Linux predeterminado.\n\n    --shell-type &lt;standard|login|none&gt;\n        Ejecute el comando especificado con el tipo de shell proporcionado.\n\n    --\n        Pasa la línea de comandos restante tal cual.\n\nOpciones:\n    --cd &lt;Directory&gt;\n        Establece el directorio especificado como directorio de trabajo actual.\n        Si se usa ~, se usará la ruta de acceso principal del usuario de Linux. Si la ruta de\n        acceso comienza con un carácter /, se interpretará como una ruta de acceso absoluta de Linux.\n        De lo contrario, el valor debe ser una ruta de acceso absoluta de Windows.\n\n    --distribution, -d &lt;DistroName&gt;\n        Ejecute la distribución especificada.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Ejecute el id. de distribución especificado.\n\n    --user, -u &lt;UserName&gt;\n        Ejecutar como el usuario especificado.\n\n    --system\n        Inicia un shell para la distribución del sistema.\n\nArgumentos para administrar Subsistema de Windows para Linux:\n\n    --help\n    Mostrar información de uso.\n\n    --debug-shell\n        Abra un shell de depuración WSL2 con fines de diagnóstico.\n\n    --install [Distribución] [Opciones...]\n        Instale una distribución de Subsistema de Windows para Linux.\n        Para obtener una lista de distribuciones válidas, use 'wsl.exe --list --online'.\n\n        Opciones:\n            --enable-wsl1\n                Habilitar compatibilidad con WSL1.\n\n            --fixed-vhd\n                Crea un disco de tamaño fijo para almacenar la distribución.\n\n            --from-file &lt;Path&gt;\n                Instala una distribución desde un archivo local.\n\n            --legacy\n                Usa el manifiesto de distribución heredado.\n\n            --location &lt;Location&gt;\n                Establece la ruta de instalación para la distribución.\n\n            --name &lt;Name&gt;\n                Establece el nombre de la distribución.\n\n            --no-distribution\n                Solo instalar los componentes opcionales necesarios, no instala una distribución.\n\n            --no-launch, -n\n                No inicies la distribución después de la instalación.\n\n            --version &lt;Version&gt;\n                Especifica la versión que se va a usar para la nueva distribución.\n\n            --vhd-size &lt;MemoryString&gt;\n                Especifica el tamaño del disco para almacenar la distribución.\n\n            --web-download\n                Descarga la distribución desde Internet en lugar de la Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Cambia las opciones específicas de distribución.\n\n        Opciones:\n            --move &lt;Location&gt;\n                Mueve la distribución a una nueva ubicación.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Establece el VHD de distribución en disperso, lo que permite recuperar automáticamente el espacio en disco.\n\n            --set-default-user &lt;Username&gt;\n                Establece el usuario predeterminado de la distribución.\n\n            --resize &lt;MemoryString&gt;\n                Cambia el tamaño del disco de la distribución al tamaño especificado.\n\n    --mount &lt;Disk&gt;\n        Conecta y monta un disco físico o virtual en todas las distribuciones de WSL 2.\n\n        Opciones:\n            --vhd\n                Especifica que &lt;Disk&gt; hace referencia a un disco duro virtual.\n\n            --bare\n                Conecta el disco a WSL2, pero no lo montes.\n\n            --name &lt;Name&gt;\n                Monta el disco con un nombre personalizado para el punto de montaje.\n\n            --type &lt;Tipo&gt;\n                Sistema de archivos que se va a usar al montar un disco. Si no se especifica, el valor predeterminado es ext4.\n\n            --options &lt;Options&gt;\n                Opciones de montaje adicionales.\n\n            --partition &lt;Index&gt;\n                Índice de la partición que se va a montar. Si no se especifica, el valor predeterminado es todo el disco.\n\n    --set-default-version &lt;Version&gt;\n        Cambia la versión de instalación predeterminada para las nuevas distribuciones.\n\n    --shutdown\n        Finaliza inmediatamente todas las distribuciones en ejecución y la máquina virtual\n        de la utilidad ligera WSL 2.\n\n        Opciones:\n             --force\n                Finaliza la máquina virtual WSL 2 incluso si hay una operación en curso. Puede provocar la pérdida de datos.\n\n    --status\n        Muestra el estado de Subsistema de Windows para Linux.\n\n    --unmount [Disk]\n        Desmonta y desasocia un disco de todas las distribuciones de WSL2.\n        Desmonta y desasocia todos los discos si se llama sin argumento.\n\n        --uninstall\n            Desinstala el paquete de Subsistema de Windows para Linux de este equipo.\n\n        --update\n            Actualizar el paquete del Subsistema de Windows para Linux.\n\n        Opciones:\n            --pre-release\n                 Descargar una versión preliminar si está disponible.\n\n            --version, -v\n                Mostrar información de versión.\n\nArgumentos para administrar distribuciones en el Subsistema de Windows para Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Exporta la distribución a un archivo tar.\n        El nombre de archivo puede ser - para stdout.\n\n        Opciones:\n            --format &lt;Format&gt;\n                Especifica el formato de exportación. Valores admitidos: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        Importa el archivo tar especificado como una nueva distribución.\n        El nombre de archivo puede ser - para stdin.\n\n        Opciones:\n            --version &lt;Version&gt;\n                Especifica la versión que se va a usar para la nueva distribución.\n\n            --vhd\n                Especifica que el archivo proporcionado es un archivo .vhd o .vhdx, no un archivo tar.\n                Esta operación realiza una copia del archivo VHD en la ubicación de instalación especificada.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Importa el archivo VHD especificado como una nueva distribución.\n        Este disco duro virtual debe tener el formato del tipo de sistema de archivos ext4.\n\n    --list, -l [Options]\n        Enumera las distribuciones.\n\n        Opciones:\n            --all\n                Enumera todas las distribuciones, incluidas las distribuciones que se están\n                instalando o desinstalando actualmente.\n\n            --running\n                Enumera solo las distribuciones que se están ejecutando actualmente.\n\n            --quiet, -q\n                Mostrar solo nombres de distribución.\n\n            --verbose, -v\n                Muestra información detallada sobre todas las distribuciones.\n\n            --online, -o\n                Muestra una lista de distribuciones disponibles para instalar con 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Establece la distribución como predeterminada.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Cambia la versión de la distribución especificada.\n\n    --terminate, -t &lt;Distro&gt;\n        Finaliza la distribución especificada.\n\n    --unregister &lt;Distro&gt;\n        Anula el registro de la distribución y elimina el sistema de archivos raíz.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>Versión de WSL: {}\nVersión de kernel: {}\nVersión de WSLg: {}\nVersión de MSRDC: {}\nVersión de Direct3D: {}\nVersión de DXCore: {}\nVersión de Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>Versión de MSBuild: {}\nConfirmar: {}\nTiempo de compilación: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>No se encontró el kernel personalizado especificado en {}: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>No se encontró el vhd de los módulos de kernel personalizados en {}: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>No se encontró la distribución del sistema personalizada especificada en {} o no tiene el formato correcto.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Todos los derechos reservados.\nPara obtener información de privacidad sobre este producto: https://aka.ms/privacy.\n\nUso: wslg.exe [Argumento] [Opciones...] [Línea de comandos]\n\nArgumentos:\n    --cd &lt;Directory&gt;\n        Establece el directorio especificado como el directorio de trabajo actual.\n        Si se usa ~, se usará la ruta de acceso principal del usuario de Linux. Si comienza la ruta de acceso\n        con un carácter /, se interpretará como una ruta de acceso absoluta de Linux.\n        De lo contrario, el valor debe ser una ruta de acceso absoluta de Windows.\n\n    --distribution, -d &lt;Distro&gt;\n        Ejecutar la distribución especificada.\n\n    --user, -u &lt;UserName&gt;\n        Ejecutar como el usuario especificado.\n\n    --shell-type &lt;standard|login|none&gt;\n        Ejecute el comando especificado con el tipo de shell proporcionado.\n\n    --help\n        Muestra la información de uso.\n\n    --\n        Pase la línea de comandos restante tal cual.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Presione cualquier tecla para continuar...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Falta un parámetro obligatorio en el argumento {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Exportación en curso. Esta operación puede tardar unos minutos.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Importación en curso. Esta operación puede tardar unos minutos.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>La compatibilidad con aplicaciones GUI está deshabilitada a través de {} o /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Comprobando actualizaciones.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Esta distribución solo está disponible desde Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount en ARM64 requiere Windows versión 27653 o posterior.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>La versión más reciente de Subsistema de Windows para Linux ya está instalada.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Actualizando el Subsistema de Windows para Linux a la versión {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Esta aplicación requiere el componente opcional Subsistema de Windows para Linux.\nInstálelo ejecutando: wsl.exe --install --no-distribution\nEs posible que sea necesario reiniciar el sistema para que los cambios surtan efecto.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>El archivo especificado debe tener la extensión de archivo {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>El archivo especificado debe tener la extensión de archivo {} o {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>No se encontró el VmSwitch '{}'. Modificadores disponibles: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Las redes puente requieren que se establezca wsl2.vmSwitch.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>No se pudo capturar la lista de distribución de \"{}\". {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>No se pudo conectar el disco \"{}\" a WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Error al cambiar el tamaño del disco.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>No se encontró ningún valor.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>La versión {} de Windows no admite la versión empaquetada de Subsistema de Windows para Linux.\nInstalar la actualización necesaria a través de Windows Update o a través de: {}\nPara obtener información, visite https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>La ejecución del shell de depuración requiere ejecutar wsl.exe como administrador.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Error en el proceso de instalación de la distribución \"{}\". Código de salida: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Formato de GUID no válido: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Nombre de la sección no válido in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Nombre de clave no válido en {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Se esperaba {} en {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Carácter de escape no válido: '{}' en {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Clave '{}' desconocida en {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Valor entero \"{}\" no válido para la clave \"{}\" en {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Valor IP \"{}\" no válido para la clave \"{}\" en {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Valor booleano \"{}\" no válido para la clave \"{}\" en {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Dirección mac \"{}\" no válida para la clave \"{}\" en {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Error al abrir el archivo de configuración: {}, {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Se han producido errores durante el inicio de WSL</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>No se pudo iniciar la sesión de usuario systemd para '{}'. Consulte journalctl para obtener más detalles.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Abrir EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Esta distribución no contiene un nombre predeterminado. Use --name para elegir el nombre de distribución.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>No se pueden especificar los argumentos {} y {} al mismo tiempo.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>El argumento {} requiere el argumento {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Nombre de distribución no válido: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Usando registro de distribución heredado. Considere la posibilidad de usar una distribución basada en tar en su lugar.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Los registros de distribución heredados no admiten el argumento --version .</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>Un manifiesto de invalidación proporciona la distribución \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>El hash de distribución no coincide. Se esperaba: {}, hash real: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Cadena HEX no válida: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>No se admite '{}' al instalar distribuciones heredadas.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Distribución instalada correctamente. Se puede iniciar a través de \"wsl.exe -d {}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Cadena de memoria \"{}\" no válida para la entrada .wslconfig \"{}\" en {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>No se pudo crear el disco de intercambio en '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>No se admite la virtualización anidada en esta máquina.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>No se pudo crear el punto de conexión de red con la dirección \"{}\". Dirección nueva asignada: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>No se pudo crear la red virtual con el intervalo de direcciones \"{}\", se creó una nueva red con el intervalo \"{}\", {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>MODO SEGURO HABILITADO: muchas características se deshabilitarán</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>No se admite el modo de red reflejada: {}.\nRevirtiendo a las redes NAT.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Se requiere linux kernel versión 5.10 o posterior</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Versión {}de Windows. {} no tiene las características necesarias</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>No se admite el firewall de Hyper-V</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>No se admite la tunelización del servicio de nombres de dominio</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>La configuración wsl2.localhostForwarding no tiene ningún efecto al usar el modo de red reflejada</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Se detectó un cambio de proxy HTTP en el host. Reinicie WSL para aplicar el cambio.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Se detectó una configuración de proxy localhost, pero no se refleja en WSL. WSL en modo NAT no admite servidores proxy localhost.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Se detectó una configuración de proxy IPv6, pero no se reflejó en WSL. WSL en modo NAT no admite IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Se detectó una configuración de proxy IPv6 localhost, pero no se reflejó en WSL. WSL no admite servidores proxy IPv6 localhost.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Error inesperado al intentar resolver la configuración de proxy. La configuración de proxy no se reflejó en WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Error al obtener acceso al Registro. Ruta de acceso: '{}'. Error: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>No se pudo iniciar el proceso de retransmisión localhost. Error: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Error al iniciar las redes virtuales. Instale el componente opcional Plataforma de máquina virtual ejecutando: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>No se pudo configurar la red (networkingMode {}), revirtiendo a networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>No se pudo habilitar el componente de Windows \"{}\" (código de salida {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>El complemento \"{}\" devolvió un error irrecuperable</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>El complemento '{}' devolvió un error irrecuperable. Mensaje de error: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>El complemento '{}' requiere una versión más reciente de WSL. Ejecute: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>La directiva de equipo deshabilitó el valor .wslconfig \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>La directiva de equipo deshabilitó el shell de depuración.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount está deshabilitado por la directiva de equipo.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>La directiva de equipo deshabilitó WSL1.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Ejecute \"wsl.exe --set-version {} 2\" para actualizar a WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL está finalizando una actualización...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Error de actualización (código de salida: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Error de desinstalación (código de salida: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Archivo de registro: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>No se pudo quitar el paquete MSIX (error: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>No se pudo instalar el paquete MSIX (error: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Los componentes opcionales necesarios para ejecutar WSL no están instalados.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Instala los componentes que faltan.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>El archivo importado no es una distribución válida de Linux.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Presiona cualquier tecla para salir...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Ya está disponible una nueva versión de Windows Subsystem para Linux.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Actualice a la versión {} o vea sus notas de la versión a continuación.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Actualizar</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Ver documentos</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>No se pudo completar la operación porque el VHD está actualmente en uso. Para forzar que WSL deje de usar: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} no es un booleano válido, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>El VHD disperso solo se admite en WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>No se admite la ejecución de WSL como sistema local.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Sugerencia de rendimiento:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>El uso de una operación intensiva de E/S como {} en las unidades de Windows tendrá un rendimiento deficiente. Considere la posibilidad de mover los archivos del proyecto al sistema de archivos de Linux para mejorar el rendimiento. Haga clic a continuación para obtener más información.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Ver documentos</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>No volver a mostrar</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>La máquina virtual WSL2 se bloqueó.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>El seguimiento de la pila se ha guardado en: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>No se pudo iniciar la distribución. Código de error: {}, paso de error: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>No se pudo iniciar la distribución porque su disco virtual está dañado.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Clave de configuración duplicada \"{}\" en {}:{} (clave en conflicto: \"{}\" en {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Valor \"{}\" no válido para la clave de configuración \"{}\" en {}:{} (valores válidos: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Esperando a que se complete el comando OOBE para la distribución \"{}\"...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>No se pudo leer la propiedad \"{}\" de la distribución {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Parece un archivo VHD. Usa --vhd para importar un VHD en lugar de una tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Error al instalar la aplicación {} de la Microsoft Store: {}\nIntentando la descarga web...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>No se ha configurado ninguna distribución predeterminada. Proporcione una distribución para instalar.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p está deshabilitado, revirtiendo a 9p con el transporte de vsock.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>Parece que la instalación de WSL está dañada (código de error: {}).\nPresione cualquier tecla para reparar WSL o CTRL-C para cancelar.\nEsta solicitud agotará el tiempo de espera en 60 segundos.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>No se pudo analizar el perfil de terminal al registrar la distribución: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Tamaño no válido: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nCódigo de error: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Documento JSON no válido. Error de análisis: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors no puede superar el número de procesadores lógicos del sistema ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>No se pudo consultar el modo de red</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Se proporcionaron módulos de kernel personalizados sin especificar un kernel personalizado. Consulte https://aka.ms/wslcustomkernel para obtener más información.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Debido a un problema de compatibilidad actual con el cliente de Acceso global seguro, la tunelización DNS está deshabilitada.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>La compatibilidad con VHD disperso está deshabilitada actualmente debido a posibles daños en los datos.\nPara forzar que una distribución use un VHD disperso, ejecuta:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>No se pudo montar {}, consulte dmesg para obtener más detalles.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>El procesamiento de /etc/fstab con mount -a ha fallado.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Error al montar el disco de distribución. Se montó en modo de solo lectura como reserva.\nConsulte las instrucciones de recuperación en: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Error al traducir \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Hubo un problema. Prueba otra vez más tarde.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Acerca de</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Sobre la configuración del subsistema de Windows para Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Todos los derechos reservados.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Configuración del Subsistema de Windows para Linux</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Subsistema de Windows para Linux Configuración permite a los desarrolladores administrar el archivo .wslconfig mediante una aplicación basada en GUI.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Recuperación automática de memoria</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Libera automáticamente la memoria en caché después de detectar el uso de CPU inactivo. Establézcalo en gradual para liberación lenta y dropcache para liberación instantánea de la memoria almacenada en caché.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Recuperación automática de memoria.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Libera automáticamente la memoria en caché después de detectar el uso de CPU inactivo. Establézcalo en gradual para liberación lenta y dropcache para liberación instantánea de la memoria almacenada en caché.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy automático habilitado</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Habilita a WSL para que use la información de proxy HTTP de Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy automático habilitado.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Habilita a WSL para que use la información de proxy HTTP de Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Usar el mejor esfuerzo de análisis de DNS</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.dnsTunneling se establece en true. Cuando se establece en true, Windows extraerá la pregunta de la solicitud DNS e intentará resolverla, omitiendo los registros desconocidos.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Usar el mejor esfuerzo de análisis de DNS.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.dnsTunneling está establecido en true. Cuando se establece en true, Windows extraerá la pregunta de la solicitud DNS e intentará resolverla, omitiendo los registros desconocidos.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Kernel personalizado</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Ruta de acceso absoluta de Windows a un kernel de Linux personalizado.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Examinar kernels</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Kernel personalizado</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ruta de acceso absoluta de Windows a un kernel de Linux personalizado.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Módulos de kernel personalizados</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Una ruta de acceso absoluta de Windows a un VHD de módulos de kernel de Linux personalizados.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Explorar módulos de kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Módulos de kernel personalizados</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Una ruta de acceso absoluta de Windows a un VHD de módulos de kernel de Linux personalizados.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Pulsación del sistema personalizada</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Examinar distribuciones</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Especifique una ruta de acceso a un VHD que se cargará como una pulsación del sistema personalizada, que se usa principalmente para encender aplicaciones GUI en WSL. [Obtenga más información sobre las distribuciones del sistema aquí].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Pulsación del sistema personalizada</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Especifique una ruta de acceso a un VHD que se cargará como una pulsación del sistema personalizada, que se usa principalmente para encender aplicaciones GUI en WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Habilitar consola de depuración</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Booleano para activar una ventana de consola de salida que muestra el contenido de dmesg al iniciar una instancia de distribución de WSL 2.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar consola de depuración.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleano para activar una ventana de consola de salida que muestra el contenido de los mensajes de diagnóstico al iniciar una instancia de distribución de WSL 2.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Tamaño de VHD predeterminado</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Tamaño máximo predeterminado para el disco duro virtual (VHD) de WSL ampliable solo para distribuciones recién creadas.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Restablecer tamaño</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tamaño de VHD predeterminado</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Tamaño máximo predeterminado, especificado en megabytes, para el disco duro virtual (VHD) WSL ampliable solo para distribuciones recién creadas.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Programador</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentación</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Cambiar el modo DrvFS</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Cambia la implementación de acceso a archivos entre sistemas operativos en WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy Servicio de nombres de dominio habilitado</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.networkingMode se establece en NAT. Booleano para informar a WSL de que debe configurar el servidor DNS en Linux a nat en el host. Si se establece en false, se reflejarán los servidores DNS de Windows a Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy DNS habilitado.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.networkingMode está establecido en NAT. Booleano para informar a WSL para configurar el servidor DNS en Linux a la NAT en el host. Si se establece en false, se reflejarán los servidores DNS de Windows a Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>Tunelización de DNS habilitada</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Cambia la forma en que las solicitudes DNS se redirigen mediante proxy de WSL a Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tunelización de DNS habilitada.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Cambia la forma en que las solicitudes DNS se redirigen mediante proxy de WSL a Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Sistema de archivos</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Habilitar aplicaciones GUI</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Booleano para activar o desactivar la compatibilidad con aplicaciones GUI ([WSLg]) en WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar aplicaciones GUI.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleano para activar o desactivar la compatibilidad con aplicaciones GUI (conocidas como WSL g) en WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Habilitar contadores de rendimiento de hardware</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Habilita los contadores de rendimiento de hardware para Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar contadores de rendimiento de hardware.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Habilita los contadores de rendimiento de hardware para Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Firewall de Hyper-V habilitado</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Habilita el firewall de Hyper-V, que permite que las reglas de Firewall de Windows, así como las reglas específicas del tráfico de Hyper-V, filtre el tráfico de red de WSL.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Firewall de Hyper-V habilitado.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Habilita el firewall de Hyper-V, que permite que las reglas de Firewall de Windows, así como las reglas específicas del tráfico de Hyper-V, filtre el tráfico de red de WSL.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Bucle invertido de dirección de host</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.networkingMode se establece en reflejado. Cuando se establece en True, permitirá que el contenedor se conecte al host o que el host se conecte al contenedor mediante una dirección IP asignada al host. Tenga en cuenta que siempre se puede usar la dirección de bucle invertido 127.0.0.1. Esta opción también permite usar todas las direcciones IP locales asignadas adicionalmente.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Bucle invertido de dirección de host.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.networkingMode está establecido en reflejado. Cuando se establece en True, permitirá que el contenedor se conecte al host o que el host se conecte al contenedor mediante una dirección IP asignada al host. Tenga en cuenta que siempre se puede usar la dirección de bucle invertido 127.0.0.1. Esta opción también permite usar todas las direcciones IP locales asignadas adicionalmente.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Puertos omitidos</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.networkingMode se establece en reflejado. Especifica a qué puertos se pueden enlazar las aplicaciones de Linux que no se reenviarán ni considerarán automáticamente en Windows. Debe formatearse en una lista separada por comas, por ejemplo: 3000.9000.9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Restablecer puertos</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Puertos omitidos</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.networkingMode está establecido en reflejado. Especifica a qué puertos se pueden enlazar las aplicaciones de Linux que no se reenviarán ni considerarán automáticamente en Windows. Debe formatearse en una lista separada por comas, por ejemplo, 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Tiempo de espera inicial del proxy automático</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.autoProxy se establece en true. Configura el tiempo (en milisegundos) que WSL esperará para recuperar la información del proxy HTTP al iniciar un contenedor de WSL. Si la configuración de proxy se resuelve después de este tiempo, la instancia de WSL debe reiniciarse para usar la configuración de proxy recuperada.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Restablecer tiempo de espera</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tiempo de espera inicial del proxy automático</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Solo es aplicable cuando wsl2.autoProxy está establecido en true. Configura el tiempo, en milisegundos, que WSL esperará para recuperar la información del proxy HTTP al iniciar un contenedor de WSL. Si la configuración de proxy se resuelve después de este tiempo, la instancia de WSL debe reiniciarse para usar la configuración de proxy recuperada.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Línea de comandos completa</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Argumentos adicionales de la línea de comandos del kernel.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Habilitar reenvío de localhost</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Valor booleano que especifica si los puertos enlazados a un carácter comodín o localhost en la VM WSL 2 deben poder conectarse desde el host a través de localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar reenvío de localhost.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valor booleano que especifica si los puertos enlazados a un carácter comodín o localhost en la VM WSL 2 deben poder conectarse desde el host a través de localhos:port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Memoria y procesador</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Tamaño de memoria</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Cantidad de memoria que se asignará a la VM de WSL 2.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Restablecer tamaño</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tamaño de memoria</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Cantidad de memoria, especificada en megabytes, que se asignará a la VM WSL 2.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} milisegundos</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Habilitar la virtualización anidada</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Booleano para activar o desactivar la virtualización anidada, lo que permite que otras VM anidadas se ejecuten dentro de WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar la virtualización anidada.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleano para activar o desactivar la virtualización anidada, lo que permite que otras VM anidadas se ejecuten dentro de WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Modo de red</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Especifica el modo de red para WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Modo de red.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Especifica el modo de red para WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Funciones de red</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Puede trabajar en todos los sistemas operativos con todos sus archivos.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Acceso a archivos entre sistemas operativos</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Acceso a los archivos de Windows desde Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Para acceder a los archivos de Windows desde Linux, ve a '/mnt' y, a continuación, a la letra de unidad de Windows, como en este ejemplo para la unidad C:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Acceda a los archivos de Linux con el Explorador de archivos</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Para ver los archivos de Linux desde Explorador de archivos, vaya a '\\\\wsl.localhost\\'' o haga clic en el icono \"Linux\".</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Iniciar archivos y programas de Windows desde WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Incluso al usar WSL, puedes ejecutar los archivos ejecutables de Windows directamente desde Bash. Intenta ejecutar\n'powershell.exe /c start .' para abrir Explorador de archivos en la carpeta actual.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Acceso a aplicaciones de red de Linux desde Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Si va a compilar una aplicación de red (por ejemplo, una aplicación que se ejecuta en un servidor SQL Server o NodeJS) en la distribución de Linux, puede acceder a ella desde una aplicación de Windows (como el explorador de Internet Edge o Chrome) mediante localhost (como lo haría normalmente). Esto significa que si inició un servidor Linux que escucha el puerto 3000, puede ir a [http://localhost:3000] en Edge en Windows para acceder a él.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Redes en modo reflejado</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL también incluye un nuevo modo de red, denominado modo reflejado, que agrega funcionalidades avanzadas como compatibilidad con IPv6 y la capacidad de acceder a las aplicaciones de red en la red de área local.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Obtenga más información sobre el acceso a archivos entre sistemas operativos</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Le damos la bienvenida al Subsistema de Windows para Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL es una excelente manera de probar diferentes distribuciones de Linux.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Administración de distribuciones</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Obtenga más información sobre los comandos básicos de WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Obtenga más información sobre la importación de cualquier distribución de Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Comando Enumerar distribuciones de WSL instalables</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Instalar un comando de distribución WSL con nombre</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Comando Enumerar distribuciones WSL disponibles</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop funciona perfectamente con WSL para ayudarle a desarrollar con contenedores de Linux.\n\nAlgunas de las ventajas de usar Docker Desktop con WSL son:\n\n• Puede ejecutar comandos de Docker en WSL o en Windows, con el mismo demonio de Docker e imágenes.\n• Puede compartir archivos y carpetas entre Windows y Linux sin problemas, mediante el montaje automático de unidades de Windows en WSL.\n• Puede usar sus herramientas y editores de Windows preferidos para trabajar en código y archivos de Linux, y viceversa, gracias a la interoperabilidad de WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Integración de Docker Desktop</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Obtenga más información sobre el uso de WSL con Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>El Subsistema de Windows para Linux (WSL) le permite ejecutar sus herramientas, utilidades, aplicaciones y flujos de trabajo favoritos de Linux directamente en Windows.\n\nDedique un momento a obtener una vista previa de algunas de las características favoritas de la comunidad o vea nuestra documentación completa.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Te damos la bienvenida a WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Procedimientos recomendados para la configuración</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Introducción a Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentación de Subsistema de Windows para Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Puede usar aplicaciones Linux basadas en gráficos con interacciones nativas de Windows, como alt-tab, inicio del menú Inicio, anclaje de la barra de tareas y compatibilidad con cortar y pegar.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>Aplicaciones de GUI</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL puede aprovechar la GPU de Windows para flujos de trabajo de Machine Learning\n\nLos archivos binarios de Linux que se ejecutan en WSL pueden usar automáticamente la GPU en Windows para acelerar su rendimiento. Solo tiene que instalar y ejecutar esos flujos de trabajo de la misma manera que lo haría en una máquina Linux normal. Hay muchas maneras diferentes de empezar a ejecutar CUDA en un contenedor de Docker si tiene una tarjeta gráfica NVIDIA, hasta ejecutar PyTorch o TensorFlow con DirectML en la tarjeta gráfica AMD, Intel o NVIDIA. Consulte la guía de introducción a continuación para obtener más información.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>Aceleración de la GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Obtenga más información sobre la aceleración de GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Obtenga más información sobre las aplicaciones GUI de WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Esta es una lista de aplicaciones para probar (puede instalarlas todas en Ubuntu escribiendo 'sudo apt install &lt;Nombre de aplicación&gt;')\n\n    • gedit: editor de texto básico\n    • audacity: grabar y editar archivos de audio\n    • blender: crear animaciones y visualizaciones 3D\n    • gimp: editar fotos\n    • nautilus: explorador de archivos de Linux\n    • vlc: reproductor de vídeo</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Puede acceder fácilmente a las aplicaciones de red en los sistemas operativos Windows y Linux.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Integración de redes</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Obtenga más información sobre las aplicaciones de red</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Obtenga más información sobre las redes en modo reflejado</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Puede usar WSL como entorno de desarrollo a tiempo completo directamente desde VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>Integración de VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Obtener más información sobre el uso de WSL con VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Cómo instalarlo</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Después de instalar VS Code, puedes instalar la extensión WSL remota desde el Terminal Windows:\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Abrir un proyecto WSL en Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Para abrir un proyecto en VS Code desde su distribución WSL, abra la línea de comando de la distribución y ejecute 'code .' para abrir un archivo de proyecto.\n\nTambién puedes acceder a más opciones remotas de VS Code mediante la paleta de comandos dentro de VS Code. Presiona \"MAYÚS+CTRL+P\" en el teclado para abrir la paleta de comandos y escribe \"Remote-WSL\" para ver una lista de las opciones remotas de VS Code disponibles, lo que te permite volver a abrir la carpeta en una sesión remota, especificar qué distribución deseas abrir y mucho más.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Cómo usarla</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Funciones opcionales</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Declaración privacidad</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Recuento del procesador</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Número de procesadores lógicos que se asignarán a la VM WSL 2.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Restablecer cuenta</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Recuento del procesador</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Número de procesadores lógicos que se asignarán a la VM WSL 2.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Vínculos relacionados</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Notas de la versión</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Habilitar el modo seguro</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Ejecute WSL en \"Modo seguro\" que deshabilite muchas características y que esté pensado para su uso para recuperar distribuciones en mal estado.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar el modo seguro.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ejecute WSL en \"Modo seguro\" que deshabilite muchas características y que esté pensado para su uso para recuperar distribuciones en mal estado.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Acerca de</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Programador</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Administración de distribuciones</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Integración de Docker Desktop</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Sistema de archivos</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>General</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Aceleración de la GPU</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>Aplicaciones de GUI</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Iniciar wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Memoria y procesador</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Funciones de red</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Integración de redes</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Te damos la bienvenida a WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Funciones opcionales</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Configuración</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>Integración de VS Code</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Novedades</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Trabajando en sistemas de archivos</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problemas</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Habilitar VHD disperso de forma predeterminada</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Cualquier VHD recién creado se establecerá en disperso automáticamente cuando se habilite.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar VHD disperso de forma predeterminada.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Cualquier VHD recién creado se establecerá en disperso automáticamente cuando se habilite.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Abrir ubicación del archivo</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Ruta de acceso absoluta de Windows al disco duro virtual de intercambio.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Examinar archivos de intercambio</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Abrir ubicación del archivo</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ruta de acceso absoluta de Windows al disco duro virtual de intercambio.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Tamaño de intercambio</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Cantidad de espacio de intercambio que se agregará a la VM WSL 2, 0 para ningún archivo de intercambio. El almacenamiento de intercambio es una RAM basada en disco usada cuando la demanda de memoria supera el límite en el dispositivo de hardware.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Restablecer tamaño</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tamaño de intercambio</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Cantidad de espacio de intercambio, especificado en megabytes, que se agregará a la VM WSL 2. 0 para ningún archivo de intercambio. El almacenamiento de intercambio es una RAM basada en disco usada cuando la demanda de memoria supera el límite en el dispositivo de hardware.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Enable VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Use virtiofs en lugar del plan 9 para acceder a los archivos host, lo que aumenta la velocidad.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Habilitar Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Habilita el montaje del sistema de archivos 9P desde el host mediante el transporte virtual.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Tiempo de espera de inactividad de SS</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Número de milisegundos que una VM está inactiva antes de apagarse.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Restablecer tiempo de espera</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tiempo de espera de inactividad de SS</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Número de milisegundos que una VM está inactiva antes de apagarse.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Compila, ejecuta, depura y analiza el perfil de tus aplicaciones que se ejecutan en WSL desde Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Ejecuta y depura tus aplicaciones en WSL desde Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Más información sobre WSL en Visual Studio para desarrolladores de .NET</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Más información sobre Visual Studio y WSL para desarrolladores de C++</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Puedes ejecutar y depurar fácilmente tus aplicaciones .NET Core y C++ multiplataforma en Linux sin salir de Visual Studio utilizando el Subsistema de Windows para Linux (WSL). Si eres un desarrollador multiplataforma, puedes utilizar este método como una forma sencilla de probar más entornos de destino.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Integración de Visual Studio</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Integración de Visual Studio</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/fi-FI/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille -teknologian avulla kehittäjät voivat suorittaa GNU/Linux-ympäristön, mukaan lukien useimmat komentorivityökalut, apuohjelmat ja sovellukset, suoraan Windowsissa muokkaamattomana ja ilman perinteisen virtuaalikoneen tai kaksoiskäynnistysasennuksen aiheuttamaa kuormitusta.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Levyn irrottaminen epäonnistui: {}. Saat lisätietoja suorittamalla dmesg-komennon WSL2:ssa.\nJos haluat pakottaa WSL2:n pysäyttämään levyn ja irrottamaan sen, suorita wsl.exe {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Levy {} on jo liitetty.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Asema on jo otettu käyttöön WSL2:ssa.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Samanniminen levy on jo otettu käyttöön; poista levy käytöstä tai valitse uusi nimi ja yritä uudelleen.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Määritetty käyttöönottonimi sisältää virheellisen /-merkin. Yritä uudelleen ilman virheellistä merkkiä.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Levyn käyttöönotto nimellä /mnt/wsl/{} onnistui.\nHuomautus: Sijainti ei ole sama, jos olet muokannut automount.root-asetusta kohteessa /etc/wsl.conf.\nJos haluat poistaa levyn käytöstä ja irrottaa sen, suorita wsl.exe {} {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Levy liitettiin, mutta käyttöönotto epäonnistui: {}.\nSaat lisätietoja suorittamalla dmesg-määrityksen WSL2:ssa.\nJos haluat irrottaa levyn, suorita wsl.exe {} {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Seuraava on luettelo kelvollisista jakeluista, jotka voidaan asentaa.\nAsenna käyttämällä tiedostoa wsl.exe {} &lt;Distro&gt;.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Jakelunimi on jo määritetty.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>Annettu asennussijainti on jo käytössä.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Jakelu annetulla nimellä on jo olemassa. Valitse toinen nimi --name avulla.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Annetulla nimellä ei ole jakelua.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Järjestelmänvalvojan oikeudet tarvitaan levyn käyttöönottoon.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Jakelun vienti epäonnistui.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Suoritetaan Windows-alijärjestelmä Linuxille ‑tiedostojärjestelmän kertapäivitystä tälle jakelulle...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Ei voida käynnistää, koska toista esiintymää suoritetaan laajentamattomana.  Laajennettuja ja laajentamattomia esiintymiä ei voida suorittaa samanaikaisesti.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Jakelun tuonti epäonnistui.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Windowsin valinnaisen osan asentaminen: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Ladataan: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Asennetaan: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Tuodaan jakelua</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} on ladattu.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille jatkaa aiempaa asennusta...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Virheellinen jakelunimi: {}.\nJos haluat luettelon kelvollisista jakeluista, käytä wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille -esiintymä on lopetettu.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Komentorivin argumentti ei kelpaa: {}\nHae tuettujen argumenttien luettelo käyttämällä komentoa '{} --help'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>Komentoriviargumentti {} vaatii arvon.\nHae tuettujen argumenttien luettelo käyttämällä komentoa '{} --help'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Konsolin asetuksia ei tueta. Tämän ominaisuuden käyttäminen edellyttää, että vanha konsoli on poistettu käytöstä.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} ei ole kelvollinen kokonaisluku.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Tämän jakelun asennus, asennuksen poistaminen tai muuntaminen on käynnissä.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Käynnistetään {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Käynnistäminen ei onnistu, koska toista esiintymää suoritetaan järjestelmänvalvojana.  Laajennettuja ja ylentämättömiä esiintymiä ei sallita suoritettavaksi samanaikaisesti.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille ei ole asennettuja jakeluja.\nVoit ratkaista ongelman asentamalla jakelun alla olevien ohjeiden avulla:\n\nNäytä käytettävissä olevien jakeluiden luettelo wsl.exe --list --online' -toiminnolla\nja asenna wsl.exe --install &lt;Distro&gt;.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Jakeluja ei ole käynnissä.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Oletus)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille -jakelut:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Oletusjakelu: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Oletusversio: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Poistetaan rekisteröintiä.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Käyttäjää ei löydy.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Lisätietoja WSL 2:n tärkeistä eroista on osoitteessa https://aka.ms/wsl2</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Muuntaminen on meneillään. Tämä voi kestää muutamia minuutteja.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>Jakelu on jo pyydetty versio.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>Vanha jakelu ei tue WSL 2 -koodausta.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>WSL2 ei käynnisty, koska virtualisointi ei ole käytössä tässä koneessa.\nVarmista, että valinnainen Virtuaalikonealusta on otettu käyttöön ja virtualisointi on aktivoitu tietokoneen laiteohjelmistoasetuksissa.\n\nOta Virtuaalikonealusta käyttöön suorittamalla: wsl.exe --install --no-distribution\n\nLisätietoja löytyy osoitteesta https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Liian monta virtuaalista tai fyysistä kiintolevyä liitetty.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>Virtuaalikiintolevy (VHD) on jo otettu käyttöön komennolla wsl.exe --mount, Poista levy käytöstä ennen käynnistämistä.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1:tä ei tueta nykyisessä tietokoneen kokoonpanossa.\nOta valinnainen Windows-alijärjestelmä Linuxille -osa käyttöön, jotta voit käyttää WSL1:tä.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Vain WSL2 tukee tätä toimintoa.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Käyttö:\n    --networking-mode\n       Näytä nykyinen verkkotila.\n\n    --msal-proxy-path\n        Näytä MSAL-välityspalvelinsovelluksen polku.\n\n    --vm-id\n        Näytä WSL-näennäiskoneen tunnus.\n\n    --version\n        Näytä WSL-paketin versio.\n\n    -n\n        Älä tulosta uutta riviä.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Käyttö:\n    -a\n        Pakota tulos absoluuttiseen polkumuotoon.\n    -u\n        Käännä Windows-polusta WSL-polkuun (oletus).\n    -w\n        Käännä WSL-polusta Windows-polkuun.\n    -m\n        Käännä WSL-polusta Windows-polkuun käyttäen '/'-merkkiä '\\\\'-merkin sijaan\n\nEsimerkki: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Suorittaa hallintatoimintoja Windows-alijärjestelmä Linuxille\n\nKäyttö:\n    /l, /list [Vaihtoehto]\n        Luetteloi rekisteröidyt jakelut.\n        /all - Näytä luettelo kaikista jakeluista, mukaan lukien jakelut, joita\n               asennetaan tai joiden asennusta poistetaan parhaillaan.\n\n        /running - Näytä vain parhaillaan käynnissä olevien jakeluiden luettelo.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Määrittää jakelun oletukseksi.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Lopettaa määritetyn jakelun.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Poistaa jakelun rekisteröinnin ja päätiedostojärjestelmän.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Tekijänoikeus (c) Microsoft Corporation. Kaikki oikeudet pidätetään.\nLisätietoja tämän tuotteen tietosuojasta löytyy osoitteesta https://aka.ms/privacy.\n\nKäyttö: wsl.exe [Argumentti] [Asetukset...] [Komentorivi]\n\nLinux-binaaritiedostojen suoritusargumentit:\n\n    Jos komentoriville ei anneta tekstiä, wsl.exe käynnistää oletusliittymän.\n\n    --exec, -e &lt;CommandLine&gt;\n        Suorita määritetty komento käyttämättä oletusarvoista Linux-liittymää.\n\n    --shell-type &lt;standard|login|none&gt;\n        Suorita määritetty komento annetulla liittymätyypillä.\n\n    --\n        Välitä jäljellä oleva komentorivi sellaisenaan.\n\nAsetukset:\n    --cd &lt;Directory&gt;\n        Asettaa määritetyn hakemiston nykyiseksi työhakemistoksi.\n        Jos ~ on käytössä, käytetään Linux-käyttäjän kotipolkua. Jos polku alkaa\n        merkillä /, se tulkitaan absoluuttiseksi Linux-poluksi.\n        Muussa tapauksessa arvon on oltava absoluuttinen Windows-polku.\n\n    --distribution, -d &lt;DistroName&gt;\n        Suorita määritetty jakelu.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Suorita määritetty jakelutunnus.\n\n    --user, -u &lt;UserName&gt;\n        Suorita määritettynä käyttäjänä.\n\n    --system\n        Käynnistää liittymän järjestelmän jakelua varten.\n\nArgumentit Windows-alijärjestelmän Linuxille hallintaan:\n\n    --help\n        Näytä käyttötiedot.\n\n    --debug-shell\n        Avaa WSL2-virheenkorjausliittymä diagnostiikkatarkoitusta varten.\n\n    --install [Jakelu] [Asetukset...]\n        Asenna jakelu Windows-alijärjestelmään Linuxille.\n        Jos haluat luettelon kelvollisista jakeluista, käytä argumenttia 'wsl.exe --list --online'.\n\n        Asetukset:\n            --enable-wsl1\n                Ota WSL1-tuki käyttöön.\n\n            --fixed-vhd\n                Luo kooltaan kiinteä levy jakelun tallentamista varten.\n\n            --from-file &lt;Path&gt;\n                Asenna jakelu paikallisesta tiedostosta.\n\n            --legacy\n                Käytä vanhoja jakelun kokoonpanotietoja.\n\n            --location &lt;Location&gt;\n                Määritä jakelun asennuspolku.\n\n            --name &lt;Name&gt;\n                Määritä jakelun nimi.\n\n            --no-distribution\n                Asenna ainoastaan pakolliset valinnaiset komponentit, älä jakelua.\n\n            --no-launch, -n\n                Älä käynnistä jakelua asennuksen jälkeen.\n\n            --version &lt;Version&gt;\n                Määrittää uutta jakelua varten käytettävän version.\n\n            --vhd-size &lt;MemoryString&gt;\n                Määrittää jakelun tallennuslevyn koon.\n\n            --web-download\n                Lataa jakelu Internetistä Microsoft Storen sijaan.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Muuttaa tietyn jakelun asetuksia.\n\n        Asetukset:\n            --move &lt;Location&gt;\n                Siirrä jakelu uuteen sijaintiin.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Määritä jakelun virtuaalikiintolevy harvaksi, jolloin levytilaa voidaan vapauttaa automaattisesti.\n\n            --set-default-user &lt;Username&gt;\n                Määritä jakelun oletuskäyttäjä.\n\n            --resize &lt;MemoryString&gt;\n                Muuta jakelun levyn koko määritettyyn kokoon.\n\n    --mount &lt;Disk&gt;\n        Liittää ja ottaa käyttöön fyysisen tai virtuaalilevyn kaikissa WSL2-jakeluissa.\n\n        Asetuksia:\n            --vhd\n                Määrittää, että &lt;Disk&gt; viittaa virtuaalikiintolevyyn.\n\n            --bare\n                Liitä levy WSL2:een, mutta älä ota sitä käyttöön.\n\n            --name &lt;Name&gt;\n                Ota levy käyttöön käyttäen mukautettua nimeä käyttöönottopisteelle.\n\n            --type &lt;Type&gt;\n                Tiedostojärjestelmä levyn käyttöönoton yhteydessä. Jos sitä ei ole määritetty, käytetään oletusarvoa ext4.\n\n            --options &lt;Options&gt;\n                Käyttöönoton lisäasetukset.\n\n            --partition &lt;Index&gt;\n                Käyttöön otettavan osion indeksi. Jos sitä ei ole määritetty, käytetään oletusarvoisesti koko levyä.\n\n    --set-default-version &lt;Version&gt;\n        Muuttaa uusien jakeluiden oletusasennusversiota.\n\n    --shutdown\n        Lopettaa kaikki käynnissä olevat jakelut ja WSL 2:n\n        kevyen käytön näennäiskoneen.\n\n        Asetuksia:\n            --force\n                Lopeta WSL 2 -virtuaalikone, vaikka toiminto olisi käynnissä. Voi aiheuttaa tietojen menettämisen.\n\n    --status\n        Näytä Windows-alijärjestelmän Linuxille tila.\n\n    --unmount [Levy]\n        Poistaa levyn käytöstä ja irrottaa sen kaikista WSL2-jakeluista.\n        Poistaa käytöstä kaikki levyt ja irrottaa ne, jos sitä kutsutaan ilman argumenttia.\n\n    --uninstall\n        Poistaa Windows-alijärjestelmä Linuxille -paketin tästä tietokoneesta.\n\n    --update\n        Päivitä Windows-alijärjestelmä Linuxille -paketti.\n\n        Asetuksia:\n            --pre-release\n                Lataa ennakkoversio, jos se on saatavilla.\n\n    --version, -v\n        Näytä versiotiedot.\n\nArgumentit Windows-alijärjestelmän Linuxille jakelujen hallintaan:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Asetukset]\n        Vie jakelun tar-tiedostoon.\n        Tiedostonimi voi olla - STDOUT-siirrännälle.\n\n        Asetuksia:\n            --format &lt;Format&gt;\n                Määrittää vientimuodon. Tuetut arvot: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Asetukset]\n        Tuo määritetyn tar-tiedoston uutena jakeluna.\n        Tiedostonimi voi olla - STDIN-siirrännälle.\n\n        Asetuksia:\n            --version &lt;Version&gt;\n                Määrittää uudelle jakelulle käytettävän version.\n\n            --vhd\n                Määrittää, että annettu tiedosto on .vhd- tai .vhdx-tiedosto, ei tar-tiedosto.\n                Tämä toiminto tekee VHD-tiedostosta kopion määritetyssä asennussijainnissa.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Tuo määritetyn VHD-tiedoston uutena jakeluna.\n        Tämä virtuaalikiintolevy on alustettava ext4-tiedostojärjestelmän tyypillä.\n\n    --list, -l [Asetukset]\n        Näyttää luettelon jakeluista.\n\n        Asetuksia:\n            --all\n                Näytä luettelo kaikista jakeluista, mukaan lukien jakelut, joita\n                asennetaan tai joiden asennusta poistetaan parhaillaan.\n\n            --running\n                Näytä vain parhaillaan käynnissä olevien jakeluiden luettelo.\n\n            --quiet, -q\n                Näytä vain jakeluiden nimet.\n\n            --verbose, -v\n                Näytä yksityiskohtaiset tiedot kaikista jakeluista.\n\n            --online, -o\n                Näyttää luettelon asennettavissa olevista jakeluista argumentilla wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Määrittää jakelun oletukseksi.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Muuttaa määritetyn jakelun versiota.\n\n    --terminate, -t &lt;Distro&gt;\n        Lopettaa määritetyn jakelun.\n\n    --unregister &lt;Distro&gt;\n        Poistaa jakelun rekisteröinnin ja päätiedostojärjestelmän.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL-versio: {}\nYtimen versio: {}\nWSLg-versio: {}\nMSRDC-versio: {}\nDirect3D-versio: {}\nDXCore-versio: {}\nWindows-versio: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild-versio: {}\nCommit: {}\nKoontiaika: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>Kohteessa {} määritettyä mukautettua ydintä ei löytynyt: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>Kohteen {} mukautettuja ydinmoduulien VHD:tä ei löytynyt: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>Tiedostossa {} määritettyä mukautettua järjestelmän jakelua ei löytynyt, tai se ei ole oikeassa muodossa.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Tekijänoikeus (c) Microsoft Corporation. Kaikki oikeudet pidätetään.\nLisätietoja tämän tuotteen tietosuojasta on osoitteessa https://aka.ms/privacy.\n\nKäyttö: wslg.exe [Argumentti] [Asetukset...] [Komentorivi]\n\nArgumentit:\n    --cd &lt;Directory&gt;\n        Asettaa määritetyn hakemiston nykyiseksi työhakemistoksi.\n        Jos ~ on käytössä, käytetään Linux-käyttäjän kotipolkua. Jos polku alkaa\n        merkillä /, se tulkitaan absoluuttiseksi Linux-poluksi.\n        Muussa tapauksessa arvon on oltava absoluuttinen Windows-polku.\n\n    --distribution, -d &lt;Distro&gt;\n        Suorita määritetty jakelu.\n\n    --user, -u &lt;UserName&gt;\n        Suorita määritettynä käyttäjänä.\n\n    --shell-type &lt;standard|login|none&gt;\n        Suorita määritetty komento annetulla liittymätyypillä.\n\n    --help\n        Näytä käyttötiedot.\n\n    --\n        Välitä jäljellä oleva komentorivi sellaisenaan.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Jatka painamalla mitä tahansa näppäintä...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Argumentista {} puuttuu pakollinen parametri.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Vienti on meneillään. Tämä voi kestää muutaman minuutin.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Tuonti on käynnissä. Tämä voi kestää muutaman minuutin.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>Gui-sovelluksen tuki on poistettu käytöstä toiminnolla {} tai /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Tarkistetaan päivityksiä.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Tämä jakelu on käytettävissä vain Microsoft Storesta.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount ARM64:ssä edellyttää Windows-versiota 27653 tai uudempaa.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Uusin versio Windows-alijärjestelmästä Linuxille on jo asennettu.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Päivitetään Windows-alijärjestelmä Linuxille versioksi: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Tämä sovellus edellyttää Windows-alijärjestelmä Linuxille valinnaisen osan.\nAsenna se suorittamalla: wsl.exe --install --no-distribution\nJärjestelmä on ehkä käynnistettävä uudelleen, jotta muutokset tulevat voimaan.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Määritetyllä tiedostolla on oltava tiedostotunniste {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Määritetyllä tiedostolla on oltava tiedostotunniste {} tai {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>VmSwitch-kohdetta {} ei löytynyt. Käytettävissä olevat valitsimet: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Sillattu verkkotoiminto edellyttää wsl2.vmSwitchin määrittämistä.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Jakeluluetteloa ei voitu noutaa kohteesta '{}'. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Levyn {} liittäminen WSL2:een epäonnistui: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Levyn koon muuttaminen epäonnistui.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Arvoa ei löytynyt.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Windows-versio {} ei tue Windows-alijärjestelmä Linuxille pakattua versiota.\nAsenna pakollinen päivitys Windows Updaten kautta tai seuraavan kautta: {}\nSaat lisätietoja osoitteesta https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Virheenkorjausliittymän suorittaminen edellyttää wsl.exe suorittamista järjestelmänvalvojana.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Jakelun {} asennusprosessi epäonnistui, lopetuskoodi: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Virheellinen GUID-muoto: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Virheellinen osan nimi kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Virheellinen avaimen nimi kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Odotettiin kohdetta {} kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Virheellinen ohjausmerkki: {} kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Tuntematon avain {} kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Virheellinen kokonaislukuarvo {} avaimelle {} kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Virheellinen IP-arvo {} avaimelle {} kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Virheellinen totuusarvo {} avaimelle {} kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Virheellinen mac-osoite {} avaimelle {} kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Kokoonpanotiedostoa {}, {} ei voitu avata</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>WSL-käynnistyksen aikana tapahtui virheitä</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Järjestelmäkäyttäjäistuntoa ei voitu käynnistää kohteelle {}. Lisätietoja on journalctl-kohteessa.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Avaa EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Tämä jakelu ei sisällä oletusnimeä. Valitse jakelun nimi --name avulla.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Argumentteja {} ja {} ei voi määrittää samanaikaisesti.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>Argumentti {} edellyttää argumenttia {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Virheellinen jakelunimi: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Käytetään vanhaa jakelurekisteröintiä. Harkitse sen sijaan tervapohjaista jakaumaa.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Vanhat jakelurekisteröinnit eivät tue --version argumenttia.</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>Jakelun {} tarjoaa ohitusluettelo.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>Jakauman hajautusarvo ei täsmää. Odotettu: {}, todellinen hajautusarvo: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Virheellinen heksadesimaalimerkkijono: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>Kohdetta {} ei tueta asennettaessa vanhoja jakeluja.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Jakelun asennus onnistui. Se voidaan käynnistää kohteella wsl.exe -d {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Virheellinen muistimerkkijono {} .wslconfig-merkinnälle {} kohteessa {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Vaihtolevyn luominen kohteessa {}: {} epäonnistui</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Sisäkkäistä virtualisointia ei tueta tässä tietokoneessa.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Verkon päätepisteen luominen osoitteella {} epäonnistui, määritetty uusi osoite: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Virtuaaliverkkoa, jonka osoitealue on {}, ei voitu luoda, luotiin uusi verkko, jonka alue on {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>VIKASIETOTILA KÄYTÖSSÄ - monet ominaisuudet poistetaan käytöstä</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Peilatun verkon tilaa ei tueta: {}.\nPalataan nat-verkkopalveluun.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Linux-ytimen versio 5.10 tai uudempi vaaditaan</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows-versio {}. {} ei sisällä tarvittavia ominaisuuksia</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Hyper-V-palomuuria ei tueta</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>DNS-tunnelointia ei tueta</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>wsl2.localhostForwarding-asetuksella ei ole vaikutusta käytettäessä peilatun verkon tilaa</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Isännässä on havaittu HTTP-välityspalvelimen muutos. Ota muutos käyttöön käynnistämällä WSL uudelleen.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Havaittiin localhost-välityspalvelimen määritys, mutta sitä ei peilattu WSL:ään. WSL NAT-tilassa ei tue localhost-välityspalvelimia.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Havaittiin IPv6-välityspalvelimen määritys, mutta sitä ei peilattu WSL:ään. NAT-tilassa WSL ei tue IPv6:ta.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Havaittiin localhost IPv6 -välityspalvelimen määritys, mutta sitä ei peilattu WSL:ään. WSL ei tue localhost IPv6 -välityspalvelimia.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Odottamaton virhe yritettäessä selvittää välityspalvelimen asetuksia, välityspalvelimen asetuksia ei peilattu WSL:ksi.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Virhe käytettäessä rekisteriä. Polku: {}. Virhe: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Localhost-välitysprosessin käynnistäminen epäonnistui. Virhe: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Virtuaaliverkon käynnistäminen epäonnistui – asenna valinnainen virtuaalikoneympäristö suorittamalla: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Verkon (networkingMode {}) määrittäminen epäonnistui. Palataan takaisin networkingMode-kohteeseen {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Windows-osan {} käyttöönotto epäonnistui (lopetuskoodi {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Laajennus {} palautti vakavan virheen</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Laajennus {} palautti vakavan virheen. Virhesanoma: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Laajennus {} edellyttää Uudempaa WSL-versiota. Suorita: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>Tietokonekäytäntö on poistanut .wslconfig asetuksen {} käytöstä.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>Tietokoneen käytäntö on poistanut virheenkorjausliittymän käytöstä.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>Tietokoneen voimassa olevan käytännön vuoksi wsl.exe --mount -komento ei ole käytettävissä.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>Tietokonekäytäntö on poistanut WSL1:n käytöstä.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Päivitä WSL2-versioksi suorittamalla wsl.exe --set-version {} 2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL viimeistelee päivitystä...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Päivitys epäonnistui (lopetuskoodi: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Asennuksen poistaminen epäonnistui (lopetuskoodi: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Lokitiedosto: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>MSIX-paketin poistaminen epäonnistui (virhe: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>MSIX-paketin asentaminen epäonnistui (virhe: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>WSL:n suorittamiseen tarvittavia valinnaisia osia ei ole asennettu.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Asenna puuttuvat osat.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Tuotu tiedosto ei ole kelvollinen Linux-jakelu.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Poistu painamalla mitä tahansa näppäintä…</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Uusi versio Windows-alijärjestelmästä Linuxille on saatavilla.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Päivitä versioon {} tai tarkastele sen julkaisutietoja alla.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Päivitä</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Näytä asiakirjat</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Toimintoa ei voitu suorittaa loppuun, koska VHD on parhaillaan käytössä. WSL:n käytön lopettaminen: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} ei ole kelvollinen totuusarvo, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Harvaa virtuaalikiintolevyä tuetaan vain WSL2:ssa.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>WSL:n suorittamista paikallisena järjestelmänä ei tueta.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Suorituskykyvihje:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Paljon I/O-toimintoa, kuten {} Windows-asemille, käytetään heikkoa suorituskykyä. Harkitse projektitiedostojen siirtämistä Linux-tiedostojärjestelmään suorituskyvyn parantamiseksi. Saat lisätietoja napsauttamalla alla.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Näytä asiakirjat</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Älä näytä uudelleen</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>WSL2-virtuaalikone kaatui.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>Pinon jäljitys on tallennettu sijaintiin {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Jakelun käynnistäminen epäonnistui. Virhekoodi: {}, virheen vaihe: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Jakelu ei käynnistynyt, koska sen näennäislevy on vioittunut.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Määritysavaimen {} kaksoiskappale kohteessa {}:{} (ristiriitainen avain: {} kohteessa {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Virheellinen arvo {} määritysavaimelle {} kohteessa {}:{} (kelvolliset arvot: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Odotetaan, että OOBE-komento valmistuu jakelulle {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Ominaisuuden {} lukeminen jakelusta {} epäonnistui</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Tämä näyttää VHD-tiedostolta. --vhd -näppäimellä voit tuoda VHD:n tar-tiedoston sijaan.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Kohteen {} asentaminen epäonnistui Microsoft Store: {}\nYritetään ladata verkkoa...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Oletusjakelua ei ole määritetty. Anna jakelu asennettavaksi.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p on poistettu käytöstä, palataan takaisin 9p:hen vsock-siirron avulla.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>WSL-asennus vaikuttaa olevan vioittunut (virhekoodi: {}).\nKorjaa WSL painamalla mitä tahansa näppäintä tai peruuta CTRL-C.\nTämä kehote aikakatkaistiin 60 sekunnin kuluttua.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Pääteprofiilin jäsentäminen epäonnistui jakelua rekisteröitäessä: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Virheellinen koko: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nVirhekoodi: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Virheellinen JSON-asiakirja. Jäsennysvirhe: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors ei voi ylittää järjestelmän loogisten suorittimien määrää ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Verkkotilan kysely epäonnistui</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Mukautetut ydinmoduulit toimitettiin määrittämättä mukautettua ydintä. Lisätietoja on https://aka.ms/wslcustomkernel.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Globaalin suojatun käytön asiakasohjelman nykyisen yhteensopivuusongelman vuoksi DNS-tunnelointi on poistettu käytöstä.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Harva virtuaalikiintolevyn tuki on tällä hetkellä poistettu käytöstä tietojen mahdollisen vioittumisen vuoksi.\nJos haluat pakottaa jakelun käyttämään harvaa virtuaalikiintolevyä, suorita seuraavasti:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Kohteen {} käyttöönotto epäonnistui, katso lisätietoja dmesg:stä.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Kohteen /etc/fstab käsitteleminen mount -a -komennolla epäonnistui.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Jakelulevyn asennuksessa tapahtui virhe. Se otettiin käyttöön vain luku -tilassa varalevynä.\nKatso palautusohjeet: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Kääntäminen epäonnistui {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Tapahtui jokin virhe. Yritä myöhemmin uudelleen.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Tietoja</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Tietoja Windows-alijärjestelmän asetuksista Linuxille</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Kaikki oikeudet pidätetään.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille asetukset</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille -asetusten avulla kehittäjät voivat hallita .wslconfig-tiedostoa GUI-pohjaisen sovelluksen avulla.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Automaattinen muistin palauttaminen</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Vapauttaa välimuistiin tallennetun muistin automaattisesti käyttämättömän suoritinkäytön havaitsemisen jälkeen. Aseta gradual, jos haluat hidasta vapautusta, ja dropcache, jos haluat välitöntä vapautusta välimuistista.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automaattinen muistin palauttaminen.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Vapauttaa välimuistiin tallennetun muistin automaattisesti käyttämättömän suoritinkäytön havaitsemisen jälkeen. Aseta gradual, jos haluat hidasta vapautusta, ja dropcache, jos haluat välitöntä vapautusta välimuistista.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Automaattinen välityspalvelin käytössä</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Sallii WSL:n käyttää Windowsin HTTP-välityspalvelintietoja.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automaattinen välityspalvelin käytössä.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Sallii WSL:n käyttää Windowsin HTTP-välityspalvelintietoja.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Käytä parasta mahdollista DNS-jäsennystä</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.dnsTunneling on true. Kun asetus on true, Windows poimii kysymyksen DNS-pyynnöstä ja yrittää ratkaista sen ohittaen tuntemattomat tietueet.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Käytä parasta mahdollista DNS-jäsennystä.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.dnsTunneling-asetus on true. Kun asetus on true, Windows poimii kysymyksen DNS-pyynnöstä ja yrittää ratkaista sen ohittaen tuntemattomat tietueet.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Mukautettu ydin</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Absoluuttinen Windows-polku mukautettuun Linux-ytimeen.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Selaa ytimiä</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Mukautettu ydin</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Absoluuttinen Windows-polku mukautettuun Linux-ytimeen.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Mukautetut ydinmoduulit</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Absoluuttinen Windows-polku mukautettuun Linux-ydinmoduulien VHD:hen.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Selaa ydinmoduuleja</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Mukautetut ydinmoduulit</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Absoluuttinen Windows-polku mukautettuun Linux-ydinmoduulien VHD:hen.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Mukautettu järjestelmän distro</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Selaa distroja</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Määritä polku VHD-levylle, joka ladataan mukautettuna järjestelmädistrona, jota käytetään ensisijaisesti WSL:n GUI-sovellusten käyttämiseen. [Lisätietoja järjestelmädistroista täältä].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Mukautettu järjestelmän distro</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Määritä polku VHD-levylle, joka ladataan mukautettuna järjestelmädistrona, jota käytetään ensisijaisesti WSL:n GUI-sovellusten käyttämiseen.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Ota virheenkorjauskonsoli käyttöön</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Totuusarvo, joka ottaa käyttöön tulostuskonsolin ikkunan, joka näyttää dmesg:n sisällön WSL 2 -distro-esiintymän käynnistyksen yhteydessä.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ota virheenkorjauskonsoli käyttöön.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Totuusarvo, joka ottaa käyttöön tulostekonsolin ikkunan, joka näyttää diagnostiikkaviestien sisällön WSL 2 -distroesiintymän käynnistyksen yhteydessä.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Virtuaalikiintolevyn oletuskoko</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Laajennettavan WSL-virtuaalikiintolevyn (VHD) oletusarvoinen enimmäiskoko vain äskettäin luoduille jakeluille.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Palauta koko</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Virtuaalikiintolevyn oletuskoko</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Laajennettavan WSL-virtuaalikiintolevyn (VHD) oletusarvoinen enimmäiskoko, megatavuina määritettynä, vain äskettäin luoduille jakeluille.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Kehittäjä</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentaatio</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Muuta DrvFS-tilaa</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Muuttaa käyttöjärjestelmien välisten tiedostojen käytön toteutusta WSL:ssä.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS-välityspalvelin käytössä</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.networkingMode-asetuksena on NAT. Totuusarvo, joka ilmoittaa WSL:lle DNS-palvelimen määrittämisestä Linuxissa NAT-kohteelle isännässä. Jos arvoksi määritetään epätosi, DNS-palvelimet peilataan Windowsista Linuxiin.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-välityspalvelin käytössä.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.networkingMode-asetuksena on NAT. Totuusarvo, joka ilmoittaa WSL:lle DNS-palvelimen määrittämisestä Linuxissa NAT-kohteelle isännässä. Jos arvoksi määritetään epätosi, DNS-palvelimet peilataan Windowsista Linuxiin.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>DNS-tunnelointi käytössä</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Muuttaa tapaa, miten DNS-pyyntöjä käytetään WSL:stä Windowsiin.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-tunnelointi käytössä.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Muuttaa tapaa, miten DNS-pyyntöjä käytetään WSL:stä Windowsiin.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Tiedostojärjestelmä</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Ota graafiset käyttöliittymäsovellukset käyttöön</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Totuusarvo, jolla GUI-sovellusten ([WSLg]) tuki WSL:ssä otetaan käyttöön tai poistetaan käytöstä.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ota GUI-sovellukset käyttöön.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Totuusarvo, jolla GUI-sovellusten tuki (tunnetaan nimellä WSL g) WSL:ssä otetaan käyttöön tai poistetaan käytöstä.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Ota laitteiston resurssilaskurit käyttöön</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Ottaa Linuxin laitteiston suorituskykylaskurit käyttöön.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ota laitteiston suorituskykylaskurit käyttöön.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ottaa Linuxin laitteiston suorituskykylaskurit käyttöön.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Hyper-V-palomuuri käytössä</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Ottaa käyttöön Hyper-V-palomuurin, joka sallii Windowsin palomuurin sääntöjen ja Hyper-V-liikennettä koskevien sääntöjen suodattaa WSL-verkkoliikennettä.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hyper-V-palomuuri käytössä.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ottaa käyttöön Hyper-V-palomuurin, joka sallii Windowsin palomuurin sääntöjen ja Hyper-V-liikennettä koskevien sääntöjen suodattaa WSL-verkkoliikennettä.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Isäntäosoitteen silmukka</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.networkingMode on asetettu peilatuksi. Kun asetus on True, säilö voi muodostaa yhteyden isäntään tai isäntään muodostamaan yhteyden säilöön isäntään määritetyllä IP-osoitteella. Huomaa, että 127.0.0.1-silmukkaosoitetta voi aina käyttää – tämä vaihtoehto mahdollistaa myös kaikkien lisäksi määritettyjen paikallisten IP-osoitteiden käyttämisen.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Isäntäosoitteen silmukka.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.networkingMode-asetus on peilattu. Kun asetus on True, säilö voi muodostaa yhteyden isäntään tai isäntään muodostamaan yhteyden säilöön isäntään määritetyllä IP-osoitteella. Huomaa, että 127.0.0.1-silmukkaosoitetta voi aina käyttää – tämä vaihtoehto mahdollistaa myös kaikkien lisäksi määritettyjen paikallisten IP-osoitteiden käyttämisen.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Ohitetut portit</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.networkingMode on asetettu peilatuksi. Määrittää, mihin portteihin Linux-sovellukset voivat sitoa, joita ei lähetetä automaattisesti edelleen tai oteta huomioon Windowsissa. Tulee muotoilla pilkuin eroteltuna luettelona, esimerkiksi 3000,9000,9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Palauta portit</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ohitetut portit</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.networkingMode on asetettu peilatuksi. Määrittää, mihin portteihin Linux-sovellukset voivat sitoa, joita ei lähetetä automaattisesti edelleen tai oteta huomioon Windowsissa. Tulee muotoilla pilkuin eroteltuna luettelona, esimerkiksi 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Alkuperäinen automaattisen välityspalvelimen aikakatkaisu</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.autoProxy-asetuksena on tosi. Määrittää, kuinka kauan (millisekunteina) WSL odottaa HTTP-välityspalvelintietojen noutamista WSL-säilöä käynnistettäessä. Jos välityspalvelimen asetukset ratkeavat tämän ajan kuluttua, WSL-esiintymä on käynnistettävä uudelleen, jotta noudetut välityspalvelimen asetukset voidaan käyttää.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Palauta aikakatkaisu</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Alkuperäinen automaattisen välityspalvelimen aikakatkaisu</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Käytettävissä vain, kun wsl2.autoProxy-asetuksena on tosi. Määrittää, kuinka kauan (millisekunteina) WSL odottaa HTTP-välityspalvelintietojen noutamista WSL-säilöä käynnistettäessä. Jos välityspalvelimen asetukset ratkeavat tämän ajan kuluttua, WSL-esiintymä on käynnistettävä uudelleen, jotta noudetut välityspalvelimen asetukset voidaan käyttää.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Ytimen komentorivi</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Ytimen komentorivin lisäargumentit.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Ota localhostin edelleenlähetys käyttöön</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Totuusarvo, joka määrittää, tuleeko WSL 2 VM:n yleismerkkiin tai localhostiin sidottujen porttien olla yhdistettävissä isännästä localhost:portin kautta.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ota localhostin edelleenlähetys käyttöön.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Totuusarvo, joka määrittää, tuleeko WSL 2 VM:n yleismerkkiin tai localhostiin sidottujen porttien olla yhdistettävissä isännästä localhostin, kaksoispisteen ja portin kautta.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} Mt</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Muisti ja suoritin</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Muistin koko</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM:lle määritettävän muistin määrä.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Palauta koko</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Muistin koko</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM:ään määritettävän muistin määrä megatavuina.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} millisekuntia</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Ota käyttöön sisäkkäinen virtualisointi</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Totuusarvo, joka ottaa sisäkkäisen virtualisoinnin käyttöön tai poistaa sen käytöstä, jolloin muut sisäkkäiset virtuaalikoneet voidaan suorittaa WSL 2:ssa.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ota käyttöön sisäkkäinen virtualisointi.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Totuusarvo, joka ottaa sisäkkäisen virtualisoinnin käyttöön tai poistaa sen käytöstä, jolloin muut sisäkkäiset virtuaalikoneet voidaan suorittaa WSL 2:ssa.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Verkkotila</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Määrittää WSL:n verkkotilan.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Verkkotila.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Määrittää WSL:n verkkotilan.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Verkko</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Voit käsitellä kaikkia tiedostojasi eri käyttöjärjestelmissä!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Käyttöjärjestelmätiedostojen välinen käyttö</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Käytä Windows-tiedostoja Linuxista</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Voit käyttää Windows-tiedostoja Linuxissa siirtymällä kohteeseen /mnt ja sitten Windows-aseman kirjaimeen, kuten tässä esimerkissä C-asemaan:\n\ncd /mnt/c</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Käytä Linux-tiedostoja Resurssienhallinnalla</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Voit tarkastella Linux-tiedostoja Resurssienhallinnassa siirtymällä kohtaan \\\\wsl.localhost\\ tai napsauttamalla Linux-kuvaketta.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Käynnistä Windows-tiedostot ja -ohjelmat WSL:stä</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Vaikka käyttäisit WSL:ää, voit suorittaa Windows-suoritettavat kohteet suoraan bashista. Yritä suorittaa\nAvaa Resurssienhallinta nykyisessä kansiossa valitsemalla 'powershell.exe /c start .'.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Linux-verkkosovellusten käyttäminen Windowsista</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Jos olet luomassa verkkosovellusta (esimerkiksi NodeJS- tai SQL-palvelimella suoritettavaa sovellusta) Linux-jakelussa, voit käyttää sitä Windows-sovelluksesta (kuten Edgestä tai Chrome-Internet-selaimesta) localhostin avulla (aivan kuten normaalisti). Tämä tarkoittaa, että jos olet käynnistänyt Linux-palvelimen, joka kuuntelee porttia 3000, voit siirtyä Windowsin Edgessä kohtaan [http://localhost:3000] käyttääksesi sitä.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Peilatun tilan verkko</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL sisältää myös uuden verkkotilan eli peilatun tilan, joka lisää lisäominaisuuksia, kuten IPv6-tuen ja mahdollisuuden käyttää verkkosovelluksiasi lähiverkossa.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Lisätietoja tiedoston käytöstä käyttöjärjestelmien välisesti</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Tervetuloa Windows-alijärjestelmään Linuxille</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL on erinomainen tapa kokeilla eri Linux-jakeluja.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Distro-hallinta</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Lisätietoja WSL-peruskomennosta</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Lisätietoja Linux-jakelun tuomisesta</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Luettelo asennettavista WSL-distroista -komento</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe -l -o</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Asenna nimetty WSL-distrokomento</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe --install &lt;DistroName&gt;</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Luettelo käytettävissä olevista WSL-distroista -komento</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe -l</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop toimii hyvin WSL:n kanssa ja auttaa sinua kehittämään Linux-säilöjen avulla.\n\nDocker Desktopin ja WSL:n käytön etuja ovat seuraavat:\n\n• Voit suorittaa Docker-komentoja WSL:ssä tai Windowsissa käyttämällä samaa Docker-daemonia ja kuvia.\n• Voit jakaa tiedostoja ja kansioita saumattomasti Windowsin ja Linuxin välillä käyttämällä Windows-asemien automaattista käyttöönottoa WSL:ssä.\n• Voit käyttää ensisijaisia Windows-työkaluja ja -editoreja Linux-koodin ja -tiedostojen kanssa ja päinvastoin WSL:n yhteentoimivuuden ansiosta.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker-työpöytäintegrointi</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Lisätietoja WSL:n käyttämisestä Dockerin kanssa</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille (WSL) -sovelluksen avulla voit suorittaa Linux-suosikkityökalujasi, -apuohjelmiasi, -sovelluksiasi ja -työnkulkujasi suoraan Windowsissa.\n\nEsikatsele yhteisön suosikkiominaisuuksia tai tutustu kattavaan dokumentaatioomme.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Tervetuloa käyttämään WSL:ää</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Määrityksen parhaat käytännöt</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Aloitusopas Linuxiin</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Windows-alijärjestelmä Linuxille (WSL) -ohjeet</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Voit käyttää graafisia Linux-sovelluksia, joissa on alkuperäisiä Windows-vuorovaikutuksia, kuten alt-tab, aloitusvalikon käynnistäminen, tehtäväpalkin kiinnittäminen ja leikkaa ja liitä -tuki.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>GUI-sovellukset</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL voi hyödyntää Windowsin grafiikkasuoritinta koneoppimisen työnkuluissa\n\nWSL:ssä toimivat Linux-binaarit voivat nopeuttaa suorituskykyään automaattisesti Windowsin grafiikkasuorittimella. Sinun tarvitsee vain asentaa ja suorittaa nämä työnkulut samalla tavalla kuin tavallisessa Linux-koneessa. Voit aloittaa CUDA:n käyttämisen docker-säilössä useilla eri tavoilla, jos sinulla on NVIDIA-näytönohjain, ja suorittaa PyTorchin tai TensorFlow'n DirectML-toiminnolla AMD-, Intel- tai NVIDIA-näytönohjaimessa. Lisätietoja on alla olevassa aloitusoppaassa.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>Grafiikkasuorittimen kiihdytys</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Lisätietoja grafiikkasuorittimen kiihdytyksestä</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Lisätietoja WSL GUI -sovelluksista</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Tässä on luettelo sovelluksissa, joita voit kokeilla (voit asentaa kaikki nämä Ubuntussa kirjoittamalla sudo apt install &lt;sovelluksen nimi&gt;)\n\n    • gedit – Perustekstieditori\n    • audacity – Tallenna ja muokkaa äänitiedostoja\n    • blender – Tee 3D-animaatioita ja visualisointeja\n    • gimp – Muokkaa valokuvia\n    • nautilus – Linuxin resurssienhallinta\n    • vlc – Videosoitin</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Voit käyttää verkkosovelluksia helposti Windows- ja Linux-käyttöjärjestelmissä.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Verkkointegrointi</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Lisätietoja verkkosovelluksista</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Lisätietoja peilatun tilan verkkotoiminnoista</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Voit käyttää WSL:ää täysipäiväisenä kehitysympäristönä suoraan VS Codesta.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS-koodin integrointi</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Lisätietoja WSL:n käyttämisestä VS Coden kanssa</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Asennusohje</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Kun olet asentanut VS Coden, voit asentaa WSL-etälaajennuksen Windows-päätteestä:\n\ncode –install-extension ms-vscode-remote.remote-wsl</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Avaa WSL-projekti Visual Studio koodissa</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Jos haluat avata projektin VS Codessa WSL-jakelusta, avaa jakelun komentorivi ja avaa projektitiedosto suorittamalla koodi .\n\nVoit käyttää myös muita VS Code Remote -asetuksia VS Coden komentovalikoiman kautta. Avaa komentovalikoima painamalla näppäinyhdistelmää VAIHTO+CTRL+P ja kirjoita Remote-WSL, niin näet luettelon käytettävissä olevista VS Code Remote -asetuksista, joiden avulla voit avata kansion uudelleen etäistunnossa, määrittää avattavan jakelun ja paljon muuta.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Toimintaohjeet</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Valinnaiset ominaisuudet</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Tietosuojatiedot</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Suorittimen määrä</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM:lle määritettävät loogiset suorittimet.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Palauta määrä</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Suorittimen määrä</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM:lle määritettävät loogiset suorittimet.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Aiheeseen liittyviä linkkejä</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Julkaisutiedot</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Ota käyttöön vikasietotila</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Suorita WSL vikasietotilassa, joka poistaa käytöstä monia ominaisuuksia ja jota on tarkoitus käyttää palauttamaan jakaumia, jotka ovat virheellisessä tilassa.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ota käyttöön vikasietotila.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Suorita WSL vikasietotilassa, joka poistaa käytöstä monia ominaisuuksia ja jota on tarkoitus käyttää palauttamaan jakaumia, jotka ovat virheellisessä tilassa.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Tietoja</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Kehittäjä</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Distro-hallinta</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Docker-työpöytäintegrointi</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Tiedostojärjestelmä</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Yleiset</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Grafiikkasuorittimen kiihdytys</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>GUI-sovellukset</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Käynnistä wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Muisti ja suoritin</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Verkko</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Verkkointegrointi</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Tervetuloa käyttämään WSL:ää</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Valinnaiset ominaisuudet</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Asetukset</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS-koodin integrointi</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Uudet ominaisuudet</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Työskentely tiedostojärjestelmien välillä</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Ongelmat</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Ota käyttöön harva virtuaalikiintolevy oletusarvon mukaan</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Uudet luodut virtuaalikiintolevyt asetetaan harvaksi automaattisesti, kun ne ovat käytössä.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ota käyttöön harva virtuaalikiintolevy oletusarvoisesti.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Uudet luodut virtuaalikiintolevyt asetetaan harvaksi automaattisesti, kun ne ovat käytössä.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Sivutustiedoston sijainti</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Absoluuttinen Windows-polku virtuaalikiintolevyn sivutustiedostoon.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Selaa sivutustiedostoja</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Sivutustiedoston sijainti</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Absoluuttinen Windows-polku virtuaalikiintolevyn sivutustiedostoon.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Sivutustiedoston koko</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Kuinka paljon sivutustiedostotilaa WSL 2 VM:ään lisätään, 0 sivutustiedostoa ei ole. Sivutustiedostontallennustila on levypohjainen RAM-muisti, jota käytetään, kun muistintarve ylittää laitteistolaitteen rajoituksen.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Palauta koko</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Sivutustiedoston koko</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM:ään lisättävän sivutustilan määrä megatavuina. 0, jos sivutustiedostoa ei ole. Sivutustiedoston tallennustila on levypohjainen RAM-muisti, jota käytetään, kun muistintarve ylittää laitteistolaitteen rajoituksen.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Ota VirtIO käyttöön</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Käytä virtiof-kohteita suunnitelman 9 sijaan isäntätiedostojen käyttämiseen, mikä lisää nopeutta.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Ota käyttöön Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Ottaa käyttöön 9P-tiedostojärjestelmän käyttöönoton isännästä virtio-siirron avulla.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Näennäiskoneen käyttämättömyystilanteen aikakatkaisu</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Virtuaalikoneen käyttämättömyysaika millisekunteina, ennen kuin se sammutetaan.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Palauta aikakatkaisu</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Näennäiskoneen käyttämättömyystilanteen aikakatkaisu</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Virtuaalikoneen käyttämättömyysaika millisekunteina, ennen kuin se sammutetaan.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Kehitä, suorita ja profiloi sovelluksiasi, jotka toimivat WSL:ssä, sekä korjaa niiden virheitä Visual Studion kautta</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Suorita sovelluksiasi WSL:ssä sekä korjaa niiden virheitä Visual Studion kautta</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Lue lisää WSL:stä Visual Studiossa .NET-kehittäjille</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Lue lisää Visual Studiosta ja WSL:stä C++-kehittäjille</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Voit helposti suorittaa .NET Core- ja käyttöympäristöjen välisiä C++-sovelluksia sekä korjata niiden virheitä Linuxissa ilman, että sinun tarvitsee poistua Visual Studiosta, käyttämällä Windows-alijärjestelmää Linuxille (WSL). Jos kehität käyttöympäristöjen välisiä sovelluksia, tällä tavoin voit yksinkertaisesti testata useampia kohdeympäristöjä.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studion integrointi</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studion integrointi</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/fr-FR/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Sous-système Windows pour Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Le Sous-système Windows pour Linux permet aux développeurs d’exécuter un environnement GNU/Linux -- y compris la plupart des outils en ligne de commande, utilitaires et applications) -- directement sur Windows, sans modification, sans surcharge d’une machine virtuelle traditionnelle ou de la configuration d’un double démarrage.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Échec du détachement du disque : {}. Pour plus d’informations, exécutez « dmesg » dans WSL2.\nPour forcer WSL2 à arrêter et détacher le disque, exécutez « wsl.exe {} ».</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Le disque « {} » est déjà joint.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Ce volume est déjà monté dans WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Un disque portant ce nom est déjà monté ; démontez le disque ou choisissez un nouveau nom, puis réessayez.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Le nom de montage spécifié contient un caractère '/' non valide. Réessayez sans le caractère non valide.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Le disque a été correctement monté en tant que « /mnt/wsl/{} ».\nRemarque : l’emplacement sera différent si vous avez modifié le paramètre automount.root de /etc/wsl.conf.\nPour démonter et détacher le disque, exécutez « wsl.exe {} {} ».</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Le disque était attaché mais n’a pas pu être monté : {}.\nPour plus d’informations, exécutez « dmesg » dans WSL2.\nPour détacher le disque, exécutez « wsl.exe {} {} ».</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Voici la liste des distributions valides qui peuvent être installées.\nInstallez à l’aide de 'wsl.exe {} &lt;Distro&gt;'.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Le nom de distribution a déjà été défini.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>L’emplacement d’installation fourni est déjà utilisé.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Une distribution portant le nom fourni existe déjà. Utilisez --name pour choisir un autre nom.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Il n’existe aucune distribution avec le nom fourni.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>L’accès administrateur est nécessaire pour monter un disque.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Échec de l’exportation de la distribution.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Exécution d'une mise à jour unique du système de fichiers du sous-système Windows pour Linux pour cette distribution...\n</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Lancement impossible, car une autre instance s’exécute avec élévation de privilèges. Des instances avec et sans élévation de privilèges ne peuvent pas s’exécuter simultanément.\n</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Échec de l’importation de la distribution.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Installation du composant facultatif Windows : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Téléchargement : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Installation : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importation de la distribution</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} a été téléchargé.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Sous-système Windows pour Linux reprend une... d’installation précédente</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Nom de distribution non valide : « {} ».\nPour obtenir la liste des distributions valides, utilisez ' wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>L’instance Sous-système Windows pour Linux s’est arrêtée.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Argument de ligne de commande non valide : {}\nUtilisez « {} --help' pour obtenir la liste des arguments pris en charge.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>L’argument de ligne de commande {} requiert une valeur.\nUtilisez « {} --help' pour obtenir la liste des arguments pris en charge.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Paramètres de console non pris en charge. Pour pouvoir utiliser cette fonctionnalité, la console héritée doit être désactivée.\n</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} n’est pas un nombre entier valide.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Une installation, une désinstallation ou une conversion est en cours pour cette distribution.\n</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Lancement : {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Lancement impossible, car une autre instance s’exécute avec élévation de privilèges. Des instances avec et sans élévation de privilèges ne peuvent pas s’exécuter simultanément.\n</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Sous-système Windows pour Linux n’a aucune distribution installée.\nVous pouvez résoudre ce problème en installant une distribution avec les instructions ci-dessous :\n\nUtiliser wsl.exe --list --online' pour répertorier les distributions disponibles\net 'wsl.exe --install &lt;Distro&gt;' à installer.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Aucune distribution en cours d'exécution.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (par défaut)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Distributions du Sous-système Windows pour Linux :</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Distribution par défaut : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Version par défaut : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Annulation de l’inscription.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Utilisateur introuvable.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Pour plus d'informations concernant les différences principales avec WSL 2, consultez https://aka.ms/wsl2\n</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Conversion en cours. Cette opération peut prendre quelques minutes.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>La distribution présente déjà la version demandée.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>La distribution héritée ne prend pas en charge WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>WSL2 ne peut pas démarrer, car la virtualisation n’est pas activée sur cet ordinateur.\nVérifiez que le composant facultatif « Plateforme d’ordinateur virtuel » est activé et que la virtualisation est activée dans les paramètres du microprogramme de votre ordinateur.\n\nActiver « Plateforme d’ordinateur virtuel » en exécutant : wsl.exe --install --no-distribution\n\nPour plus d’informations, visitez https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Trop de disques durs virtuels ou de disques physiques sont attachés.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD déjà monté via wsl.exe --mount, veuillez démonter le disque avant de le lancer.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 n’est pas pris en charge avec votre configuration d’ordinateur actuelle.\nVeuillez activer le composant facultatif « Windows Subsystem for Linux » pour utiliser WSL1.\n</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Cette opération est uniquement prise en charge par WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Utilisation : \n    --networking-mode\n       Afficher le mode réseau actuel.\n\n    --msal-proxy-path\n        Affichez le chemin d’accès à l’application proxy MSAL.\n\n    --vm-id\n        Afficher l’ID de machine virtuelle WSL.\n\n    --version\n        Affichez la version du package WSL.\n\n    -n\n        N’imprimez pas de saut de ligne.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Utilisation : \n    -a\n        Force le résultat au format de chemi n d'accès absolu.\n    -u\n        Traduire d’un chemin d’accès Windows en un chemin d’accès WSL (par défaut).\n    -w\n        Traduire d’un chemin d’accès WSL en chemin d’accès Windows.\n    -m\n        Traduire d’un chemin d’accès WSL vers un chemin d’accès Windows, avec « / » au lieu de « \\\\ »\n\nExemple : wslpath 'c :\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Effectue des opérations d’administration sur Sous-système Windows pour Linux\n\nUtilisation : \n    /l, /list [Option]\n        Répertorie les distributions inscrites.\n        /all – Répertoriez éventuellement toutes les distributions, y compris les distributions qui\n               sont en cours d’installation ou de désinstallation.\n\n        /running – Répertoriez uniquement les distributions en cours d’exécution.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Définit la distribution comme valeur par défaut.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Met fin à la distribution.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Annule l'enregistrement de la distribution et supprime le système de fichiers racine.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Tous droits réservés.\nPour plus d’informations sur la confidentialité de ce produit, veuillez consulter https://aka.ms/privacy.\n\nUtilisation : wsl.exe [Argument] [Options...] [Ligne de commande]\n\nArguments pour l’exécution des fichiers binaires Linux :\n\n    Si aucune ligne de commande n’est fournie, wsl.exe lance l’interpréteur de commandes par défaut.\n\n    --exec, -e &lt;CommandLine&gt;\n        Exécute la commande spécifiée sans utiliser l’interpréteur de commandes Linux par défaut.\n\n    --shell-type &lt;standard|login|none&gt;\n        Exécute la commande spécifiée avec le type d’interpréteur de commandes fourni.\n\n    --\n        Passe la ligne de commande restante « en l’état ».\n\nOptions :\n    --cd &lt;Directory&gt;\n        Définit le répertoire spécifié comme répertoire de travail actuel.\n        Si ~ est utilisé, le chemin d’accès personnel de l’utilisateur Linux est utilisé. Si le chemin commence\n        par un caractère /, il est interprété en tant que chemin d’accès Linux absolu.\n        Sinon, la valeur doit être un chemin Windows absolu.\n\n    --distribution, -d &lt;DistroName&gt;\n        Exécute la distribution spécifiée.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Exécute l’ID de distribution spécifié.\n\n    --user, -u &lt;UserName&gt;\n        Exécute en tant que l’utilisateur spécifié.\n\n    --system\n        Lance un interpréteur de commandes pour la distribution du système.\n\nArguments pour la gestion du sous-système Windows pour Linux :\n\n    --help\n        Affiche les informations d’utilisation.\n\n    --debug-shell\n        Ouvre un interpréteur de commandes de débogage WSL2 pour le diagnostic.\n\n    --install [Distro] [Options...]\n        Installe une distribution de sous-système Windows pour Linux.\n        Pour obtenir une liste des distributions valides, utilisez 'wsl.exe --list --online'.\n\n        Options :\n            --enable-wsl1\n                Active la prise en charge de WSL1.\n\n            --fixed-vhd\n                Crée un disque de taille fixe pour stocker la distribution.\n\n            --from-file &lt;Path&gt;\n                Installe une distribution à partir d’un fichier local.\n\n            --legacy\n                Utilise le manifeste de distribution hérité.\n\n            --location &lt;Location&gt;\n                Définit le chemin d’installation de la distribution.\n\n            --name &lt;Name&gt;\n                Définit le nom de la distribution.\n\n            --no-distribution\n                Installe uniquement les composants facultatifs requis ; n’installe pas une distribution.\n\n            --no-launch, -n\n                Ne lance pas la distribution après l’installation.\n\n            --version &lt;Version&gt;\n                Spécifie la version à utiliser pour la nouvelle distribution.\n\n            --vhd-size &lt;MemoryString&gt;\n                Spécifie la taille du disque pour stocker la distribution.\n\n            --web-download\n                Télécharge la distribution à partir d’Internet au lieu de la Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Modifie les options spécifiques à la distribution.\n\n        Options :\n            --move &lt;Location&gt;\n                Déplace la distribution vers un nouvel emplacement.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Définit le disque dur virtuel de la distribution pour qu’il soit clairsemé. Cela permet de récupérer automatiquement de l’espace disque.\n\n            --set-default-user &lt;Username&gt;\n                Définit l’utilisateur par défaut de la distribution.\n\n            --resize &lt;MemoryString&gt;\n                Redimensionne le disque de la distribution à la taille spécifiée.\n\n    --mount &lt;Disk&gt;\n        Attache et monte un disque physique ou virtuel dans toutes les distributions WSL 2.\n\n        Options :\n            --vhd\n                Spécifie que &lt;Disque&gt; fait référence à un disque dur virtuel.\n\n            --bare\n                Attache le disque à WSL2, mais ne le monte pas.\n\n            --name &lt;Name&gt;\n                Monte le disque à l’aide d’un nom personnalisé pour le point de montage.\n\n            --type &lt;Type&gt;\n                Le système de fichiers à utiliser lors du montage d’un disque, s’il n’est pas spécifié par défaut, est ext4.\n\n            --options &lt;Options&gt;\n                Options de montage supplémentaires.\n\n            --partition &lt;Index&gt;\n                L’index de la partition à monter, s’il n’est pas spécifié par défaut, correspond à l’ensemble du disque.\n\n    --set-default-version &lt;Version&gt;\n        Modifie la version d’installation par défaut pour les nouvelles distributions.\n\n    --shutdown\n        Arrête immédiatement toutes les distributions en cours d’exécution ainsi que WSL 2\n        machine virtuelle utilitaire légère.\n\n        Options :\n            --force\n                Terminez la machine virtuelle WSL 2 même si une opération est en cours. Peut entraîner une perte de données.\n\n    --status\n        Afficher l’état du Sous-système Windows pour Linux.\n\n    --unmount [Disk]\n        Démonte et détache un disque de toutes les distributions WSL2.\n        Démonte et détache tous les disques s’ils sont appelés sans argument.\n\n    --uninstall\n        Désinstalle le package Sous-système Windows pour Linux de cet ordinateur.\n\n    --update\n        Mettez à jour le package Sous-système Windows pour Linux.\n\n        Options : \n            --pre-release\n                Téléchargez une version préliminaire si disponible.\n\n    --version, -v\n        Affichez les informations de version.\n\nArguments pour la gestion des distributions dans Sous-système Windows pour Linux :\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Exporte la distribution dans un fichier tar.\n        Le nom de fichier peut être - pour stdout.\n\n        Options :\n            --format &lt;Format&gt;\n                Spécifie le format d’exportation. Valeurs prises en charge : tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        Importe le fichier tar spécifié en tant que nouvelle distribution.\n        Le nom de fichier peut être : pour stdin.\n\n        Options :\n            --version &lt;Version&gt;\n                Spécifie la version à utiliser pour la nouvelle distribution.\n\n            --vhd\n                Spécifie que le fichier fourni est un fichier .vhd ou .vhdx, et non un fichier tar.\n                Cette opération effectue une copie du fichier VHD à l’emplacement d’installation spécifié.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Importe le fichier VHD spécifié en tant que nouvelle distribution.\n        Ce disque dur virtuel doit être formaté avec le type de système de fichiers ext4.\n\n    --list, -l [Options]\n        Répertorie les distributions.\n\n        Options :\n            --all\n                Répertorier toutes les distributions, y compris les distributions qui sont\n                en cours d’installation ou de désinstallation.\n\n            --running\n                Répertorie uniquement les distributions en cours d’exécution.\n\n            --quiet, -q\n                Afficher uniquement les noms de distribution.\n\n            --verbose, -v\n                Afficher des informations détaillées sur toutes les distributions.\n\n            --online, -o\n                Affiche la liste des distributions disponibles pour l’installation avec 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Définit la distribution comme valeur par défaut.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Modifie la version de la distribution spécifiée.\n\n    --terminate, -t &lt;Distro&gt;\n        Met fin à la distribution spécifiée.\n\n    --unregister &lt;Distro&gt;\n        Annule l’inscription de la distribution et supprime le système de fichiers racine.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>Version WSL : {}\nVersion du noyau : {}\nVersion WSLg : {}\nVersion MSRDC : {}\nVersion direct3D : {}\nVersion de DXCore : {}\nVersion de Windows : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>Version de MSBuild : {}\nValidation : {}\nHeure de génération : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>Le noyau personnalisé spécifié dans {} est introuvable : « {} ».</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>Le disque dur virtuel des modules du noyau personnalisé dans {} est introuvable : « {} ».</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>La distribution système personnalisée spécifiée dans {} est introuvable ou son format est incorrect.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Tous droits réservés.\nPour plus d’informations sur la confidentialité de ce produit, visitez https://aka.ms/privacy.\n\nUtilisation : wslg.exe [Argument] [Options...] [Ligne de commande]\n\nArguments :\n    --cd &lt;Directory&gt;\n        Définit le répertoire spécifié comme répertoire de travail actuel.\n        Si ~ est utilisé, le chemin d’accès d’accueil de l’utilisateur Linux sera utilisé. Si le chemin d’accès commence\n        avec un / caractère, il sera interprété comme un chemin d’accès Linux absolu.\n        Sinon, la valeur doit être un chemin d’accès Windows absolu.\n\n    --distribution, -d &lt;Distro&gt;\n        Exécutez la distribution spécifiée.\n\n    --user, -u &lt;UserName&gt;\n        Exécutez en tant qu’utilisateur spécifié.\n\n    --shell-type &lt;standard|login|none&gt;\n        Exécutez la commande spécifiée avec le type d’interpréteur de commandes fourni.\n\n    --help\n        Affichez les informations d’utilisation.\n\n    --\n        Passez la ligne de commande restante telle qu’elle est.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Appuyez sur n'importe quelle touche pour continuer...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Un paramètre obligatoire est manquant dans l’argument {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Exportation en cours. Cette opération peut prendre quelques minutes.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Importation en cours. Cette opération peut prendre quelques minutes.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>La prise en charge de l’application GUI est désactivée via {} ou /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Recherche des mises à jour.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Cette distribution est disponible uniquement à partir du Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount sur ARM64 nécessite Windows version 27653 ou ultérieure.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>La version la plus récente de Sous-système Windows pour Linux est déjà installée.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Mise à jour de Sous-système Windows pour Linux vers la version : {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Cette application nécessite le composant facultatif Sous-système Windows pour Linux.\nInstallez-le en exécutant : wsl.exe --install --no-distribution\nLe système devra peut-être être redémarré pour que les modifications prennent effet.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Le fichier spécifié doit avoir l’extension de fichier {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Le fichier spécifié doit avoir l’extension de fichier {} ou {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>Le VmSwitch « {} » est introuvable. Commutateurs disponibles : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>La mise en réseau bridged nécessite la définition de wsl2.vmSwitch.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Échec de la récupération de la liste de distribution à partir de « {} ». {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Échec de l’attachement du disque « {} » à WSL2 : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Échec de redimensionnement de disque.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Valeur non trouvée.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Windows version {} ne prend pas en charge la version empaquetée de Sous-système Windows pour Linux.\nInstaller la mise à jour requise via Windows Update ou via : {}\nPour plus d’informations, visitez https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>L'exécution du shell de débogage nécessite l'exécution de wsl.exe en tant qu'administrateur.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Le processus d’installation de la distribution « {} » a échoué avec le code de sortie : {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Format GUID non valide : « {} »</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Nom de section non valide de {} : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Nom de clé de non valide de {} : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>{} de {} attendu : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Caractère d’échappement non valide : « {} » de {} :{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Touche inconnue « {} » de {} : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Valeur entière « {} » non valide pour la clé « {} » dans {} :{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Valeur IP « {} » non valide pour la clé « {} » dans {} :{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Valeur booléenne « {} » non valide pour la clé « {} » dans {} :{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Adresse Mac « {} » non valide pour la clé « {} » dans {} :{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Échec de l'ouverture du fichier de configuration {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Des erreurs se sont produites au démarrage de WSL</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Échec du démarrage de la session utilisateur système pour « {} ». Pour plus d’informations, consultez journalctl.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Ouvrir EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Cette distribution ne contient pas de nom par défaut. Utilisez --name pour choisir le nom de distribution.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Les arguments de {} et {} ne peuvent pas être spécifiés en même temps.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>L’argument {} nécessite l’argument {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Nom de distribution non valide : « {} ».</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Utilisation de l’inscription de distribution héritée. Envisagez d’utiliser une distribution basée sur le tar à la place.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Les inscriptions de distribution héritées ne prennent pas en charge l’argument --version .</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>La distribution « {} » est fournie par un manifeste de remplacement.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>Le code de hachage de la distribution ne correspond pas. {}, code de hachage réel : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Chaîne hexadécimal non valide : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>« {} » n’est pas pris en charge lors de l’installation des distributions héritées.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>La distribution a été installée. Il peut être lancé via 'wsl.exe -d {}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Chaîne mémoire « {} » non valide pour .wslconfig'entrée « {} » dans {} :{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Échec de la création du disque d’échange dans « {} » : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>La virtualisation imbriquée n’est pas prise en charge sur cet ordinateur.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Échec de la création du point de terminaison réseau avec l’adresse : « {} », nouvelle adresse affectée : « {} »</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Échec de la création du réseau virtuel avec la plage d’adresses : « {} », réseau créé avec la plage « {} », {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>MODE SANS ÉCHEC ACTIVÉ - De nombreuses fonctionnalités seront désactivées</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Le mode réseau mis en miroir n’est pas pris en charge : {}.\nRetour à la mise en réseau NAT.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Le noyau Linux version 5.10 ou ultérieure est obligatoire</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows version {}. {} ne dispose pas des fonctionnalités requises</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Le pare-feu Hyper-V n’est pas pris en charge</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>Le tunneling DNS n’est pas pris en charge</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>Le paramètre wsl2.localhostForwarding n’a aucun effet lors de l’utilisation du mode réseau mis en miroir</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Une modification de proxy HTTP a été détectée sur l’hôte. Redémarrez WSL pour appliquer la modification.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Une configuration de proxy localhost a été détectée, mais n’a pas été mise en miroir dans WSL. WSL en mode NAT ne prend pas en charge les proxys localhost.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Une configuration de proxy IPv6 a été détectée, mais n’a pas été mise en miroir dans WSL. WSL en mode NAT ne prend pas en charge IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Une configuration de proxy IPv6 localhost a été détectée, mais n’a pas été mise en miroir dans WSL. WSL ne prend pas en charge les proxys IPv6 localhost.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Une erreur inattendue s’est produite lors de la tentative de résolution des paramètres de proxy. Les paramètres de proxy n’ont pas été mis en miroir dans WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Une erreur s’est produite lors de l’accès au Registre. Chemin d’accès : « {} ». Erreur : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Échec du lancement du processus de relais localhost. Erreur : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Échec du démarrage de la mise en réseau virtuelle. Installez le composant facultatif Plateforme d’ordinateur virtuel en exécutant : wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Échec de la configuration du réseau (networkingMode {}), retour à networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Échec de l’activation du composant Windows « {} » (code de sortie {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Une erreur irrécupérable a été retournée par le plug-in « {} »</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Une erreur irrécupérable a été renvoyée par le plug-in « {} ». Message d’erreur : « {} »</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Le plug-in « {} » nécessite une version plus récente de WSL. Exécutez : wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>Le paramètre .wslconfig « {} » est désactivé par la stratégie de l’ordinateur.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>La stratégie de l’ordinateur a désactivé l’interpréteur de commandes de débogage.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount est désactivé par la stratégie de l’ordinateur.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>La stratégie de l’ordinateur a désactivé WSL1.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Exécutez « wsl.exe --set-version {} 2 » pour effectuer la mise à niveau vers WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL termine une mise à niveau...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Échec de la mise à jour (code de sortie : {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Échec de la désinstallation (code de sortie : {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Fichier journal :{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Échec de la suppression du package MSIX (erreur : {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Échec de l’installation du package MSIX (erreur : {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Les composants facultatifs nécessaires pour exécuter WSL ne sont pas installés.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Installez les composants manquants.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Le fichier importé n’est pas une distribution Linux valide.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Appuyez sur une touche pour quitter...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Une nouvelle version de Sous-système Windows pour Linux est disponible.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Effectuez une mise à jour vers la version {} ou consultez ses notes de publication ci-dessous.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Mettre à jour</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Voir les documents</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Impossible d’effectuer l’opération, car le disque dur virtuel est en cours d’utilisation. Pour forcer WSL à arrêter l’utilisation : wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} n’est pas un booléen valide, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Le VHD partiellement alloué est pris en charge uniquement sur WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>L’exécution de WSL en tant que système local n’est pas prise en charge.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Conseil sur les performances :</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>L’utilisation d’une opération intensive d’E/S comme {} sur vos lecteurs Windows aura des performances médiocres. Envisagez de déplacer vos fichiers projet vers le système de fichiers Linux pour de meilleures performances. Cliquez ci-dessous pour en savoir plus.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Afficher les documents</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Ne plus afficher</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>La machine virtuelle WSL2 a planté.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>Le rapport des appels de procédure a été enregistré dans : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Échec du démarrage de distribution. Code d’erreur : {}, étape d’échec : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Échec du démarrage de la distribution, car son disque virtuel est endommagé.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Clé de configuration en double « {} » dans {} :{} (clé en conflit : « {} » dans {} :{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Valeur « {} » non valide pour la clé de configuration « {} » dans {} :{} (Valeurs valides : {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>En attente de la fin de la commande OOBE pour la distribution « {} » ...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Échec de la lecture de la propriété « {} » à partir de la distribution {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Cela ressemble à un fichier VHD. Utilisez --vhd pour importer un VHD au lieu d’un tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Échec de l’installation de {} à partir du Microsoft Store : {}\nTentative d'... de téléchargement web</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Aucune distribution par défaut n’a été configurée. Fournissez une distribution à installer.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p est désactivé et revient à 9p avec le transport vsock.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>L’installation de WSL semble endommagée (code d’erreur : {}).\nAppuyez sur une touche pour réparer WSL ou CTRL-C pour annuler.\nCette invite expirera dans 60 secondes.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Échec de l’analyse du profil de terminal lors de l’inscription de la distribution : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Taille non valide : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nCode d'erreur : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Document JSON invalide. Erreur d’analyse : {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors ne peut pas dépasser le nombre de processeurs logiques sur le système ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>La requête du mode de mise en réseau n’a pas abouti</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Des modules de noyau personnalisés ont été fournis sans spécifier de noyau personnalisé. Pour plus d’informations, consultez https://aka.ms/wslcustomkernel.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>En raison d’un problème de compatibilité actuel avec le client Accès global sécurisé, le tunneling DNS est désactivé.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>La prise en charge des VHD espacés est actuellement désactivée en raison d’un risque potentiel de corruption des données.\nPour forcer une distribution à utiliser un VHD espacé, veuillez exécuter : \nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Échec du montage {}. Pour plus de détails, consultez dmesg.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Échec du traitement de /etc/fstab avec un montage -a.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Une erreur s’est produite lors du montage du disque de distribution. Il a été monté en lecture seule en secours.\nVoir les instructions de récupération sur : https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Échec de la traduction de « {} »</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Un problème s’est produit. Réessayez ultérieurement.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>À propos de</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>À propos des paramètres Sous-système Windows pour Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Tous droits réservés.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Paramètres de la préversion du Sous-système Windows pour Linux</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Les paramètres du sous-système Windows pour Linux permettent aux développeurs de gérer le fichier .wslconfig à l'aide d'une application basée sur une interface graphique utilisateur (GUI).</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Récupération automatique de la mémoire</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Libère automatiquement la mémoire mise en cache après avoir détecté l’utilisation de l’UC inactive. La valeur graduelle est utilisée pour une libération lente, et la valeur drop cache pour une libération instantanée de la mémoire en cache.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Récupération automatique de la mémoire.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Libère automatiquement la mémoire mise en cache après avoir détecté l’utilisation de l’UC inactive. La valeur graduelle est utilisée pour une libération lente, et la valeur drop cache pour une libération instantanée de la mémoire en cache.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy automatique activé</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Permet à WSL d’utiliser les informations de proxy HTTP de Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy automatique activé.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Permet à WSL d’utiliser les informations de proxy HTTP de Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Utiliser l'analyse Domain Name Service (DNS) la plus efficace possible</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Applicable uniquement lorsque wsl2.dnsTunneling a la valeur true. Lorsque la valeur est true, Windows extrait la question de la requête Domain Name Service (DNS) et tente de la résoudre, en ignorant les enregistrements inconnus.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Utilisez le meilleur effort d’analyse DNS.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicable uniquement lorsque wsl2.dnsTunneling a la valeur true. Lorsque la valeur est true, Windows extrait la question de la requête Domain Name Service (DNS) et tente de la résoudre, en ignorant les enregistrements inconnus.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Noyau personnalisé</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Chemin d’accès Windows absolu à un noyau Linux personnalisé.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Parcourir les noyaux</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Noyau personnalisé</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Chemin d’accès Windows absolu à un noyau Linux personnalisé.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Modules de noyau personnalisés</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Chemin Windows absolu vers un disque dur virtuel de modules de noyau Linux personnalisés.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Parcourir les modules de noyau</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Modules de noyau personnalisés</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Chemin Windows absolu vers un disque dur virtuel de modules de noyau Linux personnalisés.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Distribution système personnalisée</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Parcourir les distributions</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Indiquez le chemin d'accès à un VHD qui sera chargé en tant que distribution système personnalisée, principalement utilisée pour alimenter les applications GUI dans WSL. [Pour en savoir plus sur les distributions système, cliquez ici].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Distribution système personnalisée</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Spécifiez un chemin d’accès à un disque dur virtuel qui sera chargé en tant que distribution système personnalisée, principalement utilisée pour alimenter les applications GUI dans WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Activer la console de débogage</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Valeur booléenne permettant d’activer une fenêtre de console de sortie qui affiche le contenu de dmesg au démarrage d’une instance de distribution WSL 2.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Activez la console de débogage.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valeur booléenne permettant d’activer une fenêtre de console de sortie qui affiche le contenu des messages de diagnostic au démarrage d’une instance de distribution WSL 2.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Taille par défaut du disque dur virtuel</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Taille maximale par défaut du disque dur virtuel WSL extensible (VHD) pour les distributions nouvellement créées uniquement.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Réinitialiser la taille</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Taille par défaut du disque dur virtuel</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Taille maximale par défaut, spécifiée en mégaoctets, pour le disque dur virtuel WSL extensible (VHD) pour les distributions nouvellement créées uniquement.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Développeur</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentation</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Modifier le mode DrvFS</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Modifie l’implémentation de l’accès aux fichiers entre les systèmes d’exploitation dans WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy Domain Name Service (DNS) activé</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Applicable uniquement lorsque wsl2.networkingMode est défini sur NAT. Booléenne pour informer WSL de la configuration du serveur DNS dans Linux sur le NAT sur l’hôte. La valeur définie sur false reflète les serveurs DNS de Windows vers Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy DNS activé.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicable uniquement lorsque wsl2.networkingMode est défini sur NAT. Booléenne pour informer WSL de la configuration du serveur DNS dans Linux sur le NAT sur l’hôte. La valeur définie sur false reflète les serveurs DNS de Windows vers Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>Tunneling DNS activé</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Modifie la façon dont les requêtes DNS sont transmises par proxy de WSL à Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tunneling DNS activé.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Modifie la façon dont les requêtes DNS sont transmises par proxy de WSL à Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Système de fichiers</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Activer les applications d’interface utilisateur graphique</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Valeur booléenne permettant d’activer ou de désactiver la prise en charge des applications interface graphique (GUI) ([WSLg]) dans WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Activez les applications GUI.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valeur booléenne permettant d’activer ou de désactiver la prise en charge des applications gui (appelées WSL g) dans WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Activer les compteurs de performances matérielles</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Active les compteurs de performances matérielles pour Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Activez les compteurs de performances matérielles.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Active les compteurs de performances matérielles pour Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Pare-feu Hyper-V activé</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Active le pare-feu Hyper-V qui permet aux règles du Pare-feu Windows, ainsi qu’aux règles spécifiques au trafic Hyper-V, de filtrer le trafic réseau WSL.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Pare-feu Hyper-V activé.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Active le pare-feu Hyper-V qui permet aux règles du Pare-feu Windows, ainsi qu’aux règles spécifiques au trafic Hyper-V, de filtrer le trafic réseau WSL.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Adresse d’hôte Loop</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Applicable uniquement lorsque wsl2.networkingMode est défini sur la mise en miroir. Quand la valeur est True, le conteneur peut se connecter à l’hôte ou à l’hôte pour se connecter au conteneur, par une adresse IP attribuée à l’hôte. Notez que l’adresse de bouclage 127.0.0.1 peut toujours être utilisée . Cette option permet également d’utiliser toutes les adresses IP locales affectées en plus.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Host Address Loopback.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicable uniquement quand wsl2.networkingMode a la valeur mirrored. Quand la valeur est True, le conteneur peut se connecter à l’hôte ou à l’hôte pour se connecter au conteneur, par une adresse IP attribuée à l’hôte. Notez que l’adresse de bouclage 127.0.0.1 peut toujours être utilisée . Cette option permet également d’utiliser toutes les adresses IP locales affectées en plus.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Ports ignorés</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Applicable uniquement lorsque wsl2.networkingMode est défini sur mise en miroir. Spécifie les ports auxquels les applications Linux peuvent se lier et qui ne seront pas automatiquement transférés ou pris en compte dans Windows. Doit être mis en forme dans une liste séparée par des virgules, par exemple : 3 000,9 000,9 090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Réinitialiser les ports</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ports ignorés</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicable uniquement lorsque wsl2.networkingMode est défini sur mise en miroir. Spécifie les ports auxquels les applications Linux peuvent se lier et qui ne seront pas automatiquement transférés ou pris en compte dans Windows. Doit être mis en forme dans une liste séparée par des virgules, par exemple, 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Délai d’expiration initial du proxy automatique</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Applicable uniquement lorsque wsl2.autoProxy a la valeur true. Configure la durée (en millisecondes) pendant laquelle WSL attend la récupération des informations du proxy HTTP au démarrage d’un conteneur WSL. Si les paramètres de proxy sont résolus après cette heure, le instance WSL doit être redémarré pour utiliser les paramètres de proxy récupérés.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Réinitialiser le délai d’expiration</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Délai d’expiration initial du proxy automatique</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicable uniquement quand wsl2.autoProxy a la valeur true. Configure la durée, en millisecondes, pendant laquelle WSL attend la récupération des informations du proxy HTTP au démarrage d’un conteneur WSL. Si les paramètres de proxy sont résolus après cette heure, l’instance WSL doit être redémarrée pour utiliser les paramètres de proxy récupérés.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Ligne de commande du noyau</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Arguments de ligne de commande de noyau supplémentaires.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Activer le transfert localhost</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Booléen spécifiant si les ports liés à un caractère générique ou à un localhost dans la VM WSL 2 doivent pouvoir être connectés depuis l'hôte via localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Activez le transfert localhost.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valeur booléenne spécifiant si les ports liés à un caractère générique ou localhost dans la machine virtuelle WSL 2 doivent être connectables à partir de l’hôte via localhost, deux-points, port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} Mo</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Mémoire et processeur</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Taille de la mémoire</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Quantité de mémoire à affecter à la machine virtuelle WSL 2.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Réinitialiser la taille</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Taille de la mémoire</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Quantité de mémoire, spécifiée en mégaoctets, à affecter à la machine virtuelle WSL 2.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} millisecondes</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Activer la virtualisation imbriquée</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Valeur booléenne permettant d'activer ou de désactiver la virtualisation imbriquée, ce qui permet à d'autres VM imbriquées de s'exécuter à l'intérieur du WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Activez la virtualisation imbriqué.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valeur booléenne permettant d'activer ou de désactiver la virtualisation imbriquée, ce qui permet à d'autres VM imbriquées de s'exécuter à l'intérieur du WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Mode réseau</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Spécifie le mode réseau pour WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Mode réseau.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Spécifie le mode réseau pour WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Gestion de réseau</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Vous pouvez travailler sur tous vos systèmes d’exploitation avec tous vos fichiers !</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Accès aux fichiers multi-OS</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Accédez à vos fichiers Windows depuis Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Vous pouvez accéder à vos fichiers Windows depuis Linux en accédant à « /mnt » puis à la lettre de votre lecteur Windows, comme dans cet exemple pour le lecteur C :\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Accédez à vos fichiers Linux avec l'explorateur de fichiers</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Vous pouvez afficher vos fichiers Linux à partir de l'Explorateur de fichiers en accédant à '\\\\wsl.localhost\\' ou en cliquant sur l'icône « Linux ».</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Lancer des fichiers et des programmes Windows à partir de WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Même en utilisant WSL, vous pouvez exécuter vos exécutables Windows directement depuis bash. Essayez d’exécuter\n'powershell.exe /c start .' pour ouvrir l'Explorateur de fichiers dans votre dossier actuel.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Accéder aux Applications Azure Networking Linux à partir de Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Si vous créez une application réseau (par exemple une application exécutée sur un serveur NodeJS ou langage SQL) dans votre distribution Linux, vous pouvez y accéder à partir d'une application Windows (comme votre navigateur Internet Edge ou Chrome) à l'aide de localhost (comme vous le feriez normalement). Cela signifie que si vous avez démarré un serveur Linux qui écoute le port 3000, vous pouvez accéder à [http://localhost:3000] dans Edge sous Windows pour y accéder.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Mise en réseau en mode miroir</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL inclut également un nouveau mode réseau, appelé mode miroir, qui ajoute des fonctionnalités avancées telles que la prise en charge IPv6 et la possibilité d'accéder à vos applications réseau sur votre réseau local.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur l'accès aux fichiers multi-OS</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Bienvenue dans le sous-système Windows pour Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL est un excellent moyen d'essayer différentes distributions Linux.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Gestion de la distribution</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur les commandes WSL de base</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur l'importation de n'importe quelle distribution Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Liste des commandes de distribution WSL installables</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Installer une commande de distribution WSL nommée</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Liste des commandes de distribution WSL disponibles</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop fonctionne parfaitement avec WSL pour vous aider à développer avec des conteneurs Linux.\n\nCertains des avantages de l’utilisation de Docker Desktop avec WSL sont :\n\n• Vous pouvez exécuter des commandes Docker dans WSL ou sous Windows, en utilisant le même démon Docker et les mêmes images.\n• Vous pouvez partager des fichiers et des dossiers entre Windows et Linux de manière transparente, en utilisant le montage automatique des lecteurs Windows dans WSL.\n• Vous pouvez utiliser vos outils et éditeurs Windows préférés pour travailler sur du code et des fichiers Linux, et vice versa, grâce à l'interopérabilité de WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Intégration de Docker Desktop</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur l'utilisation de WSL avec Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Le sous-système Windows pour Linux (WSL) vous permet d’exécuter vos outils, utilitaires, applications et flux de travail Linux préférés directement sur Windows.\n\nPrenez un moment pour prévisualiser certaines des fonctionnalités préférées de la communauté ou consultez notre documentation complète.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Bienvenue dans WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Bonnes pratiques pour la configuration</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Prise en main avec Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentation du sous-système Windows pour Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Vous pouvez utiliser des applications Linux graphiques avec des interactions Windows natives telles que Alt-Tab, le lancement du menu Démarrer, l'épinglage de la barre des tâches et la prise en charge du copier-coller.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>Applications d'interface utilisateur graphique</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL peut exploiter votre GPU Windows pour les flux de travail Machine Learning\n\nLes binaires Linux exécutés dans WSL peuvent utiliser automatiquement votre GPU sous Windows pour accélérer leurs performances. Il vous suffit d’installer et d’exécuter ces flux de travail de la même manière que vous le feriez sur une machine Linux classique. Il existe de nombreuses façons différentes de commencer, que ce soit en exécutant CUDA dans un conteneur Docker si vous avez une carte graphique NVIDIA, ou en utilisant PyTorch ou TensorFlow avec DirectML sur votre carte graphique AMD, Intel ou NVIDIA. Consultez notre guide de démarrage ci-dessous pour en savoir plus.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>Accélération GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur l'accélération GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur les applications WSL GUI</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Voici une liste d'applications à essayer (vous pouvez les installer toutes dans Ubuntu en tapant « sudo apt install &lt;Le nom de l'application&gt; »)\n\n    • gedit – Éditeur de texte de base\n    • audacity – Enregistrer et éditer des fichiers audio\n    • blender – Créez des animations et des visualisations 3D\n    • gimp – Retouche de photos\n    • nautilus – Un explorateur de fichiers Linux\n    • vlc – Lecteur vidéo</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Vous pouvez facilement accéder aux applications réseau sur les systèmes d’exploitation Windows et Linux.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Intégration Azure Networking</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur les applications Azure Networking</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur la mise en Azure Networking en mode miroir</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Vous pouvez utiliser WSL comme environnement de développement à temps plein directement depuis VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>Intégration de VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur l'utilisation de WSL avec VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Comment installer</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Après avoir installé VS Code, vous pouvez installer l’extension Remote WSL à partir du terminal Windows :\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Ouvrir un Projet WSL dans Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Pour ouvrir un projet dans VS Code à partir de votre distribution WSL, ouvrez la ligne de commande de la distribution et exécutez « code . » pour ouvrir un fichier de projet.\n\nVous pouvez également accéder à davantage d'options VS Code Remote via la palette de commandes dans VS Code lui-même. Appuyez sur « MAJ+CTRL+P » sur votre clavier pour ouvrir la palette de commandes et tapez « Remote-WSL » pour voir une liste des options VS Code Remote disponibles, vous permettant de rouvrir le dossier dans une session à distance, de spécifier la distribution que vous souhaitez ouvrir, et plus encore.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Procédure d’utilisation</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Fonctionnalités facultatives</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Déclaration</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Nombre de processeurs</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Nombre de processeurs logiques à affecter à la machine virtuelle WSL 2.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Nombre de réinitialisations</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Nombre de processeurs</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Nombre de processeurs logiques à affecter à la machine virtuelle WSL 2.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Liens connexes</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Notes de publication</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Activer le mode sécurisé</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Exécutez WSL en mode sans échec, ce qui désactive de nombreuses fonctionnalités et est destiné à être utilisé pour récupérer les distributions dont l’état est incorrect.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Activez le mode sans échec.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Exécutez WSL en mode sans échec, ce qui désactive de nombreuses fonctionnalités et est destiné à être utilisé pour récupérer les distributions dont l’état est incorrect.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>À propos de</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Développeur</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Gestion des distributions</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Intégration de Docker Desktop</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Système de fichiers</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Général</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Accélération GPU</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>Applications GUI</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Lancer wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Mémoire et processeur</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Gestion de réseau</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Intégration au réseau</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Bienvenue dans WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Fonctionnalités facultatives</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Paramètres</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>Intégration VS Code</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Nouveautés</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Travailler sur plusieurs systèmes de fichiers</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problèmes</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Activer le disque dur virtuel partiellement alloué par défaut</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Tous les disques durs virtuels nouvellement créés sont automatiquement alloués lorsqu’ils sont activés.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Activez le disque dur virtuel partiellement alloué par défaut.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Tous les disques durs virtuels nouvellement créés sont automatiquement alloués lorsqu’ils sont activés.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Emplacement du fichier de permutation</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Chemin d’accès Windows absolu au disque dur virtuel d’échange.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Parcourir les fichiers d’échange</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Emplacement du fichier de permutation</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Chemin d’accès Windows absolu au disque dur virtuel d’échange.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Taille d’échange</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Quantité de mémoire tampon à ajouter à la VM WSL 2, 0 sans fichier de permutation. La mémoire tampon est une mémoire vive sur disque utilisée lorsque la demande de mémoire dépasse la limite du périphérique matériel.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Réinitialiser la taille</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Taille d’échange</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Quantité d’espace d’échange, spécifiée en mégaoctets, à ajouter à la machine virtuelle WSL 2. 0 pour aucun fichier d’échange. La mémoire tampon est une mémoire vive sur disque utilisée lorsque la demande de mémoire dépasse la limite du périphérique matériel.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Activer VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Utilisez virtiofs au lieu du plan 9 pour accéder aux fichiers hôtes, ce qui augmente la vitesse.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Activer Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Active le montage du système de fichiers 9P à partir de l’hôte à l’aide du transport virtio.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Délai d’inactivité de la machine virtuelle</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Nombre de millisecondes pendant lesquelles une machine virtuelle est inactive, avant d’être éteinte.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Réinitialiser le délai d’expiration</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Délai d’inactivité de la machine virtuelle</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Nombre de millisecondes pendant lesquelles une machine virtuelle est inactive, avant d’être éteinte.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Créer, exécuter, déboguer et profiler vos applications exécutées sur WSL dans Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Exécuter et déboguer vos applications sur WSL dans Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur WSL dans Visual Studio pour les développeurs .NET</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>En savoir plus sur Visual Studio et WSL pour les développeurs C++</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Vous pouvez facilement exécuter et déboguer vos applications .NET Core et C++ multiplateformes sous Linux sans quitter Visual Studio grâce au Sous-système Windows pour Linux (WSL). Si vous êtes un développeur multiplateforme, vous pouvez utiliser cette méthode pour tester un nombre plus important de vos environnements cibles.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Intégration de Visual Studio</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Intégration de Visual Studio</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/hu-HU/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Linuxos Windows-alrendszer</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>A Linuxos Windows-alrendszer lehetővé teszi, hogy a fejlesztők GNU/Linux környezetet futtassanak -- beleértve a legáltalánosabb parancssori eszközöket, segédprogramokat és alkalmazásokat -- közvetlenül a Windowsban, módosítások nélkül, a hagyományos virtuális gépek vagy kettős rendszerindítási beállítások okozta többletterhelés nélkül.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Nem sikerült leválasztani a lemezt: {}. További részletekért futtassa a dmesg parancsot a WSL2-ben.\nA WSL2 leállításának és leválasztásának kényszerítéséhez futtassa a wsl.exe {} parancsot.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>A(z) {} lemez már csatlakoztatva van.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>A kötet már csatlakoztatva van a WSL2-ben.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Már van ilyen nevű lemez csatlakoztatva; válassza le a lemezt, vagy válasszon egy új nevet, és próbálkozzon újra.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>A megadott csatlakoztatásnév érvénytelen \"/\" karaktert tartalmaz. Próbálkozzon újra az érvénytelen karakter nélkül.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>A lemez sikeresen csatlakoztatva a következőként: /mnt/wsl/{}.\nMegjegyzés: A hely eltérő lesz, ha módosította az automount.root beállítást az /etc/wsl.conf fájlban.\nA lemez csatlakoztatásának bontásához és leválasztásához futtassa a 'wsl.exe {} {}' parancsot.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>A lemez csatlakoztatva van, de nem sikerült csatlakoztatni: {}.\nTovábbi részletekért futtassa a \"dmesg\" parancsot a WSL2-ben.\nA lemez leválasztásához futtassa a wsl.exe {} {} parancsot.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Az alábbi lista az érvényes, telepíthető disztribúciókat tartalmazza.\nTelepítés a wsl.exe {} &lt;Distro&gt; használatával.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>A disztribúciónév már be van állítva.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>A megadott telepítési hely már használatban van.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Már létezik a megadott nevű terjesztés. A --name használatával válasszon másik nevet.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Nincs a megadott névvel rendelkező terjesztés.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>A lemez csatlakoztatásához rendszergazdai hozzáférés szükséges.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>A disztribúció exportálása nem sikerült.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>A disztribúció Linuxos Windows-alrendszer fájlrendszerének egyszeri frissítése...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Nem indítható el, mert egy másik példány nem emelt szintű jogosultságszinten fut.  Emelt szintű és nem emelt szintű példányok nem futtathatók egyszerre.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>A disztribúció importálása nem sikerült.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Választható Windows-összetevő telepítése: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Letöltés: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Telepítés: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Terjesztés importálása</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} letöltve.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>A Linuxos Windows-alrendszer folytat egy korábbi telepítést...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Érvénytelen disztribúciónév: {}.\nAz érvényes disztribúciók listájának eléréséhez használja a 'wsl.exe --list --online' parancsot.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>A Linuxos Windows-alrendszer példánya leállt.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Érvénytelen parancssori argumentum: {}\nA támogatott argumentumok listájának beolvasásához használja a(z) {} --help'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>A(z) {} parancssori argumentumnak értékre van szüksége.\nA támogatott argumentumok listájának lekéréséhez használja a '{} --help' parancsot.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Nem támogatott konzolbeállítások. A funkció használatához le kell tiltani az örökölt konzolt.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>A(z) {} nem érvényes egész szám.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>A disztribúció telepítése, eltávolítása vagy konvertálása folyamatban van.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>A(z) {} indítása...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Nem indítható el, mert egy másik példány emelt szintű jogosultságszinten fut.  Emelt szintű és nem emelt szintű példányok nem futtathatók egyszerre.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>A Linuxos Windows-alrendszer nem rendelkezik telepített disztribúciókkal.\nA probléma megoldásához telepítsen egy disztribúciót az alábbi utasításokat követve:\n\nA 'wsl.exe --list --online' parancs használatával listázhatja az elérhető disztribúciókat\nés a 'wsl.exe --install &lt;Distro&gt;' parancs használatával végezheti el a telepítést.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Nincsenek futó disztribúciók.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (alapértelmezett)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Linuxos Windows-alrendszer disztribúciók:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Alapértelmezett disztribúció: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Alapértelmezett verzió: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Regisztráció megszüntetése folyamatban.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>A felhasználó nem található.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>A WSL 2 fő különbségeivel kapcsolatos információkért látogasson el a https://aka.ms/wsl2 oldalra</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>A konvertálás folyamatban van, ez eltarthat néhány percig.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>A disztribúció már a kért verzióval rendelkezik.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>Az örökölt disztribúció nem támogatja a WSL 2-t.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>A WSL2 nem indítható el, mert a virtualizáció nincs engedélyezve ezen a gépen.\nGyőződjön meg arról, hogy a „Virtuálisgép-platform” választható összetevő engedélyezve van, és a virtualizáció be van kapcsolva a számítógép firmware-beállításaiban.\n\nA „Virtuálisgép-platform” engedélyezéséhez futtassa a következőt: wsl.exe --install --no-distribution\n\nTovábbi információért látogasson el ide: https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Túl sok virtuális merevlemez vagy fizikai lemez van csatlakoztatva.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>A virtuális merevlemez már csatlakoztatva van a következővel: wsl.exe --mount, Kérjük, az indítás előtt válassza le a lemezt.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>A jelenlegi gépkonfiguráció nem támogatja a WSL1-et.\nA WSL1 használatához engedélyezze a Linuxos Windows-alrendszer választható összetevőt.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Ezt a műveletet csak a WSL2 támogatja.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Használat:\n    --networking-mode\n       Az aktuális hálózati mód megjelenítése.\n\n    --msal-proxy-path\n        Az MSAL proxyalkalmazás elérési útjának megjelenítése.\n\n    --vm-id\n        A WSL VM-azonosítójának megjelenítése.\n\n    --version\n        A WSL-csomag verziójának megjelenítése.\n\n    -n\n        Ne nyomtasson új sort.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Használat:\n    -a\n        Az eredmény kényszerítése abszolút elérésiút-formátumra.\n    -u\n        Fordítás Windows-útvonalról WSL-útvonalra (alapértelmezett).\n    -w\n        Fordítás WSL-útvonalról Windows-útvonalra.\n    -m\n        Fordítás WSL-útvonalról Windows-útvonalra '\\\\' helyett '/' karakterrel\n\nPélda: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Felügyeleti műveletek végrehajtása Linuxos Windows-alrendszeren\n\nHasználat:\n    /l, /list [Option]\n        A regisztrált disztribúciók listázása.\n        /all – Az összes disztribúció listázása, beleértve \n               jelenleg telepítés vagy eltávolítás alatt állókat is.\n\n        /running – Csak a jelenleg futó disztribúciók listázása.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Az adott disztribúció beállítása alapértelmezettként.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Megszakítja a disztribúciót.\n\n    /u, /unregister &lt;DistributionName&gt;\n        A disztribúció regisztrációjának megszüntetése és a gyökérszintű fájlrendszer törlése.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Szerzői jog (c) Microsoft Corporation. Minden jog fenntartva.\nTovábbi információ a termékről: https://aka.ms/privacy.\n\nHasználat: wsl.exe [Argumentum] [Kapcsolók...] [Parancssor]\n\nLinuxos bináris fájlok futtatására szolgáló argumentumok:\n\n    Ha nincs megadva parancssor, a wsl.exe az alapértelmezett rendszerhéjat indítja el.\n\n    --exec, -e &lt;CommandLine&gt;\n        A megadott parancs végrehajtása az alapértelmezett Linux-rendszerhéj használata nélkül.\n\n    --shell-type &lt;standard|login|none&gt;\n        A megadott parancs végrehajtása a megadott rendszerhéjtípussal.\n\n    --\n        A parancssor fennmaradó részének átadása változtatás nélkül.\n\nKapcsolók:\n    --cd &lt;Könyvtár&gt;\n        A megadott könyvtár aktuális munkakönyvtárként való beállítása.\n        Tilde (~) karakter használata esetén a rendszer a Linux-felhasználó kezdőkönyvtárának elérési útját használja. Ha az elérési út\n        perjel (/) karakterrel kezdődik, a rendszer az elérési utat linuxos abszolút elérési útként értelmezi.\n        Ellenkező esetben az értéknek windowsos abszolút elérési útnak kell lennie.\n\n    --distribution, -d &lt;DisztribúcióNév&gt;\n        A megadott disztribúció futtatása.\n\n    --distribution-id &lt;DisztribúcióGuid&gt;\n        A megadott disztribúcióazonosító futtatása.\n\n    --user, -u &lt;Felhasználónév&gt;\n        Futtatás a megadott felhasználóként.\n\n    --system\n        A rendszer-disztribúció rendszerhéjának indítása.\n\nA Linuxos Windows-alrendszer kezelésére szolgáló argumentumok:\n\n    --help\n        Használati információk megjelenítése.\n\n    --debug-shell\n        A WSL2 hibakeresési rendszerhéjának diagnosztikai célból történő megnyitása.\n\n    --install [Disztribúció] [Kapcsolók...]\n        WSL-disztribúció telepítése.\n        Az érvényes disztribúciók listája a 'wsl.exe --list --online' paranccsal jeleníthető meg.\n\n        Kapcsolók:\n            --enable-wsl1\n                A WSL1-támogatás engedélyezése.\n\n            --fixed-vhd\n                Rögzített méretű lemez létrehozása a disztribúció tárolásához.\n\n            --from-file &lt;ElérésiÚt&gt;\n                Disztribúció telepítése helyi fájlból.\n\n            --legacy\n                Az örökölt disztribúciós jegyzékfájl használata.\n\n            --location &lt;Hely&gt;\n                A disztribúció telepítési útvonalának beállítása.\n\n            --name &lt;Név&gt;\n                A disztribúció nevének beállítása.\n\n            --no-distribution\n                Csak a szükséges választható összetevők telepítése, a disztribúció telepítésének mellőzése.\n\n            --no-launch, -n\n                A disztribúció telepítés utáni elindításának mellőzése.\n\n            --version &lt;Verzió&gt;\n                Az új disztribúcióhoz használandó verzió megadása.\n\n            --vhd-size &lt;MemoriaSztring&gt;\n                A disztribúció tárolására szolgáló lemez méretének megadása.\n\n            --web-download\n                A disztribúció letöltése az internetről a Microsoft Store helyett.\n\n    --manage &lt;Disztribúció&gt; &lt;Kapcsolók...&gt;\n        A disztribúcióspecifikus beállítások módosítása.\n\n        Kapcsolók:\n            --move &lt;Hely&gt;\n                A disztribúció áthelyezése egy új helyre.\n\n            --set-sparse, -s &lt;true|false&gt;\n                A disztribúció VHD-fájljának sparse értékre állítása, hogy a lemezterület automatikusan visszanyerhető legyen.\n\n            --set-default-user &lt;Felhasználónév&gt;\n                A disztribúció alapértelmezett felhasználójának beállítása.\n\n            --resize &lt;MemoriaSztring&gt;\n                A disztribúció lemezének átméretezése a megadott méretre.\n\n    --mount &lt;Lemez&gt;\n        Fizikai vagy virtuális lemez hozzárendelése és csatlakoztatása az összes WSL 2-disztribúcióban.\n\n        Kapcsolók:\n            --vhd\n                Annak megadása, hogy a &lt;Lemez&gt; virtuális merevlemezre vonatkozik.\n\n            --bare\n                A lemezt WSL2-höz való hozzárendelése csatlakoztatás nélkül.\n\n            --name &lt;Név&gt;\n                A lemez csatlakoztatása egyéni csatlakoztatásipont-név használatával.\n\n            --type &lt;Típus&gt;\n                Lemez csatlakoztatásához használandó fájlrendszer. Ha nincs megadva, az alapértelmezett érték az ext4.\n\n            --options &lt;Kapcsolók&gt;\n                További csatlakoztatási lehetőségek.\n\n            --partition &lt;Index&gt;\n                A csatlakoztatni kívánt partíció indexe. Ha nincs megadva, az alapértelmezett érték a teljes lemez.\n\n    --set-default-version &lt;Verzió&gt;\n        Az új disztribúciók alapértelmezett telepítési verziójának módosítása.\n\n    --shutdown\n        Az összes futó disztribúció és a WSL 2\n        egyszerűsített segédprogram virtuális gépének azonnali leállítása.\n\n        Kapcsolók:\n            --force\n                A WSL 2 virtuális gép leállítása még akkor is, ha egy művelet folyamatban van. Adatvesztést okozhat.\n\n    --status\n        A Linuxos Windows-alrendszer állapotának megjelenítése.\n\n    --unmount [Lemez]\n        Lemez leválasztása és és a hozzárendelés megszüntetése a WSL2 összes disztribúciójára vonatkozóan.\n        Az összes lemez leválasztása és a hozzárendelésük megszüntetése, ha a meghívás argumentum nélkül történik.\n\n    --uninstall\n        A Linuxos Windows-alrendszer csomag eltávolítása erről a gépről.\n\n    --update\n        0A Linuxos Windows-alrendszer csomagjának frissítése.\n\n        Kapcsolók:\n            --pre-release\n                Előzetes verzió letöltése, ha elérhető.\n\n    --version, -v\n        Verzióinformációk megjelenítése.\n\nA disztribúciók Linuxos Windows-alrendszerben való kezelésére szolgáló argumentumok:\n\n    --export &lt;Disztribúció&gt; &lt;Fájlnév&gt; [Kapcsolók]\n        A disztribúció exportálása .tar fájlba.\n        A fájlnév stdout esetén kötőjel (-) is lehet.\n\n        Kapcsolók:\n            --format &lt;Formátum&gt;\n                Az exportálási formátum megadása. Támogatott értékek: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Disztribúció&gt; &lt;TelepítésiHely&gt; &lt;Fájlnév&gt; [Kapcsolók]\n        A megadott .tar fájl importálása új disztribúcióként.\n        A fájlnév stdin esetén kötőjel (-) is lehet.\n\n        Kapcsolók:\n            --version &lt;Verzió&gt;\n                Az új disztribúcióhoz használandó verzió megadása.\n\n            --vhd\n                Azt adja meg, hogy a megadott fájl .vhd vagy .vhdx fájl, nem .tar fájl.\n                Ez a művelet másolatot készít a VHD-fájlról a megadott telepítési helyen.\n\n    --import-in-place &lt;Disztribúció&gt; &lt;Fájlnév&gt;\n        A megadott VHD-fájl importálása új disztribúcióként.\n        Ezt a virtuális merevlemezt az ext4 fájlrendszertípussal kell formázni.\n\n    --list, -l [Kapcsolók]\n        A disztribúciók listázása.\n\n        Kapcsolók:\n            --all\n                Az összes disztribúciót listázza, beleértve a\n                jelenleg telepítés vagy eltávolítás alatt állókat is.\n\n            --running\n                Csak a jelenleg futó disztribúciók listázása.\n\n            --quiet, -q\n                Csak a disztribúciók nevének megjelenítése.\n\n            --verbose, -v\n                Az összes disztribúció részletes adatainak megjelenítése.\n\n            --online, -o\n                A 'wsl.exe --install' paranccsal telepíthető elérhető disztribúciók listájának megjelenítése.\n\n    --set-default, -s &lt;Disztribúció&gt;\n        A disztribúció beállítása alapértelmezettként.\n\n    --set-version &lt;Disztribúció&gt; &lt;Verzió&gt;\n        A megadott disztribúció verziójának módosítása.\n\n    --terminate, -t &lt;Disztribúció&gt;\n        A megadott disztribúció leállítása.\n\n    --unregister &lt;Disztribúció&gt;\n        A disztribúció regisztrációjának megszüntetése és a gyökérszintű fájlrendszer törlése.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL-verzió: {}\nKernelverzió: {}\nWSLg-verzió: {}\nMSRDC-verzió: {}\nDirect3D-verzió: {}\nDXCore-verzió: {}\nWindows-verzió: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild verziója: {}\nVéglegesítés: {}\nLétrehozás időpontja: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>A(z) {} helyen megadott egyéni kernel nem található: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>A(z) {} helyen lévő egyéni kernelmodulok virtuális merevlemeze nem található: „{}”.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>A(z) {} helyen megadott egyéni rendszerterjesztés nem található vagy nem megfelelő formátumú.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Minden jog fenntartva.\nA termékre vonatkozó adatvédelmi információkért keresse fel a következőt: https://aka.ms/privacy.\n\nSzintaxis: wslg.exe [Argumentum] [Beállítások...] [Parancssor]\n\nArgumentumok:\n    --cd &lt;Directory&gt;\n        A megadott könyvtárat állítja be aktuális munkakönyvtárként.\n        Ha ~ van használatban, a rendszer a Linux-felhasználó kezdőkönyvtárának elérési útját használja. Ha az elérési út\n        a / karakterrel kezdődik, a parancs linuxos abszolút elérési útként értelmezi.\n        Ellenkező esetben az értéknek abszolút Windows-elérési útnak kell lennie.\n\n    --distribution, -d &lt;Distro&gt;\n        Futtassa a megadott disztribúciót.\n\n    --user, -u &lt;USerName&gt;\n        Futtatás a megadott felhasználóként.\n\n    --shell-type &lt;standard|login|none&gt;\n        A megadott parancs végrehajtása a megadott rendszerhéjtípussal.\n\n    --help\n        Használati információk megjelenítése.\n\n    --\n        A parancssor fennmaradó részének átadása változtatás nélkül.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>A folytatáshoz nyomjon meg egy billentyűt...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>A(z) {} argumentumból hiányzik egy kötelező paraméter.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Az exportálás folyamatban van, ez eltarthat néhány percig.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Az importálás folyamatban van, ez eltarthat néhány percig.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>A grafikus felhasználói felületű alkalmazások támogatása le van tiltva a következőn keresztül: {} vagy /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Frissítések keresése.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Ez a terjesztés csak a Microsoft Store áruházban érhető el.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>az ARM64-wsl.exe --mount a Windows 27653-es vagy újabb verzióját igényli.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Az Linuxos Windows-alrendszer legújabb verziója már telepítve van.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>A Linuxos Windows-alrendszer frissítése a(z) {} verzióra.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Ehhez az alkalmazáshoz szükség van a Linuxos Windows-alrendszer választható összetevőre.\nTelepítse a következő futtatásával: wsl.exe --install --no-distribution\nLehet, hogy újra kell indítani a rendszert, hogy a módosítások érvénybe lépjenek.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>A megadott fájlnak {} kiterjesztésűnek kell lennie.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>A megadott fájlnak {} vagy {} kiterjesztésűnek kell lennie.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>A(z) {} VmSwitch nem található. Elérhető kapcsolók: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>A hálózati hídhoz be kell állítani a wsl2.vmSwitch kapcsolót.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Nem sikerült beolvasni a terjesztési listát innen: {}. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Nem sikerült csatlakoztatni a(z) {} lemezt a WSL2-höz: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Nem sikerült a lemez átméretezése.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Nem található érték.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>A(z) {} Windows-verzió nem támogatja a Linuxos Windows-alrendszer csomagolt verzióját.\nTelepítse a szükséges frissítést a Windows Update-en vagy a következőn keresztül: {}\nTovábbi információért látogasson el a https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>A hibakeresési felület futtatásához rendszergazdai wsl.exe fájlt kell futtatni.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>A(z) {} disztribúció telepítési folyamata a következő kilépési kóddal meghiúsult: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Érvénytelen GUID-formátum: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Érvénytelen szakasznév itt: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Érvénytelen kulcsnév itt: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Várt érték: {} itt: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Érvénytelen feloldott karakter: {} itt: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Ismeretlen {} kulcs itt: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Érvénytelen egész érték ({}) a(z) {}kulcshoz a következőben: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Érvénytelen IP-érték ({}) a(z) {} kulcshoz a következőben: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Érvénytelen logikai érték ({}) a(z) {}kulcshoz a következőben: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Érvénytelen MAC-cím ({}) a(z) {} kulcshoz a következőben: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Nem sikerült megnyitni a(z) {}, {} konfigurációs fájlt</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Hiba történt a WSL indításakor</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Nem sikerült elindítani a(z) „{}” rendszer felhasználói munkamenetét. További részletekért tekintse meg a journalctl dokumentumot.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Eseménynapló megnyitása</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Ez a terjesztés nem tartalmaz alapértelmezett nevet. A terjesztési név kiválasztásához használja a --name .</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>A(z) {} és a(z) {} argumentum nem adható meg egyszerre.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>A(z) {} argumentumhoz a(z) {} argumentum szükséges.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Érvénytelen disztribúciónév: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Örökölt disztribúciós regisztráció használata. Fontolja meg inkább tar-alapú disztribúció használatát.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Az örökölt terjesztési regisztrációk nem támogatják a --version argumentumot.</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>A(z) {} eloszlást egy felülbírálási jegyzék biztosítja.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>A terjesztési kivonat nem egyezik. Várt: {}, tényleges kivonat: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Érvénytelen hexadecimális karakterlánc: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>A(z) {} nem támogatott örökölt disztribúciók telepítésekor.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>A terjesztés telepítése sikerült. A wsl.exe -d {} használatával indítható el</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Érvénytelen {} memóriasztring a(z) {} .wslconfig bejegyzéshez itt: {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Nem sikerült létrehozni a felcserélési lemezt itt: {}: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>A beágyazott virtualizálás nem támogatott ezen a gépen.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Nem sikerült létrehozni a(z) {} című hálózati végpontot. A hozzárendelt új cím: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Nem sikerült létrehozni a(z) {} címtartományú virtuális hálózatot, új hálózat létrehozva a következő tartománnyal: {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>CSÖKKENTETT MÓD ENGEDÉLYEZVE – számos funkció le lesz tiltva</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>A tükrözött hálózati mód nem támogatott: {}.\nVisszaállás NAT-hálózatkezelésre.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>A Linux Kernel 5.10-es vagy újabb verziójára van szükség</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows-verzió: {}. {} nem rendelkezik a szükséges funkciókkal</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>A Hyper-V tűzfal nem támogatott</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>A DNS-alagút nem támogatott</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>A wsl2.localhostForwarding beállításnak nincs hatása tükrözött hálózati mód használatakor</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>A rendszer HTTP-proxymódosítást észlelt a gazdagépen. A módosítás alkalmazásához indítsa újra a WSL-t.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>A rendszer localhost-proxykonfigurációt észlelt, de nem tükrözte a WSL-be. A NAT módban futó WSL nem támogatja a localhost proxykat.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>A rendszer egy IPv6-proxykonfigurációt észlelt, de nem tükrözte a WSL-be. A WSL NAT-módban nem támogatja az IPv6 proxyt.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>A rendszer localhost IPv6-proxykonfigurációt észlelt, de nem tükrözte a WSL-be. A WSL nem támogatja a localhost IPv6-proxykat.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Váratlan hiba történt a proxybeállítások feloldása közben. A proxybeállítások nem lettek tükrözve a WSL-be.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Hiba történt a beállításjegyzék elérésekor. Elérési út: {}. Hiba: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Nem sikerült elindítani a localhost továbbítófolyamatot. Hiba: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Nem sikerült elindítani a virtuális hálózatot – telepítse a nem kötelező virtuálisgép-platform összetevőt a következő futtatásával: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Nem sikerült konfigurálni a hálózatot (networkingMode {}), visszatérés a networkingMode {} módba.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Nem sikerült engedélyezni a(z) {} Windows-összetevőt (kilépési kód: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>A(z) {} beépülő modul végzetes hibát adott vissza</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>A(z) {} beépülő modul végzetes hibát adott vissza. Hibaüzenet: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>A(z) {} beépülő modulhoz a WSL újabb verziója szükséges. Futtassa a következőt: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>A(z) {} .wslconfig beállítást letiltotta a számítógép-házirend.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>A számítógép-házirend letiltotta a hibakeresési felületet.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount műveletet a számítógép-házirend letiltotta.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>A WSL1-et a számítógép-házirend letiltotta.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>A WSL2-re való frissítéshez futtassa a wsl.exe --set-version {} 2 parancsot.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>A WSL befejezi a frissítést...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>A frissítés nem sikerült (kilépési kód: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Az eltávolítás nem sikerült (kilépési kód: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Naplófájl: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Nem sikerült eltávolítani az MSIX-csomagot (hiba: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Nem sikerült telepíteni az MSIX-csomagot (hiba: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>A WSL futtatásához szükséges választható összetevők nincsenek telepítve.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Hiányzó összetevők telepítése.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Az importált fájl nem érvényes Linux-disztribúció.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Nyomjon meg egy billentyűt a kilépéshez...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Elérhető a Linuxos Windows-alrendszer új verziója.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Frissítsen a(z) {} verzióra, vagy tekintse meg alább a kibocsátási megjegyzéseit.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Frissítés</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Dokumentumok megtekintése</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>A művelet nem hajtható végre, mert a virtuális merevlemez jelenleg használatban van. A WSL használatának kényszerítése: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>A(z) {} nem érvényes logikai érték, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>A ritka VHD csak WSL2 esetén támogatott.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>A WSL helyi rendszerként való futtatása nem támogatott.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Teljesítménytipp:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Ha I/O-intenzív műveletet hajt végre, például {} a Windows-meghajtókon, gyenge lesz a teljesítménye. Fontolja meg a projektfájlok Linux fájlrendszerbe való áthelyezését a jobb teljesítmény érdekében. További információért kattintson az alábbi gombra.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Dokumentumok megtekintése</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Ne jelenjen meg többé</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>A WSL2 virtuális gép összeomlott.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>A veremkivonat a következő helyre lett mentve: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Nem sikerült elindítani a terjesztést. Hibakód: {}, hibalépés: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>A terjesztés nem indult el, mert a virtuális lemeze sérült.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Duplikált konfigurációs kulcs ({}) a következőben: {}:{} (Ütköző kulcs: {}, {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Érvénytelen \"{}\" érték a(z) \"{}\" konfigurációs kulcshoz a következőben: {}:{} (Érvényes értékek: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Várakozás a(z) „{}” disztribúció OOBE parancsának befejezésére...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Nem sikerült beolvasni a(z) {} tulajdonságot a(z) {} disztribúcióból</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Ez egy VHD-fájlnak tűnik. VHD importálásához használja a --vhd formátumot tar helyett.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Nem sikerült telepíteni a(z) {} alkalmazást a Microsoft Store: {}\nWebes letöltési... megkísérlése</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Nincs konfigurálva alapértelmezett terjesztés. Adja meg a telepítendő terjesztést.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>A wsl2.virtio9p le van tiltva, vsock átvitellel visszaesik 9p értékre.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>Úgy tűnik, hogy a WSL-telepítés sérült (hibakód: {}).\nNyomjon meg egy billentyűt a WSL javításához, vagy CTRL-C a megszakításhoz.\nEz a kérés 60 másodpercen belül túllépi az időkorlátot.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Nem sikerült elemezni a terminálprofilt a következő terjesztés regisztrálásakor: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Érvénytelen méret: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nHibakód: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Érvénytelen JSON-dokumentum. Elemzési hiba: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>A wsl2.processzorok száma nem haladhatja meg a rendszer logikai processzorainak számát ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Nem sikerült lekérdezni a hálózati módot</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Az egyéni rendszermagmodulok egyéni rendszermag megadása nélkül lettek megadva. További információért tekintse meg a https://aka.ms/wslcustomkernel.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>A Globális biztonságos hozzáférés ügyféllel kapcsolatos jelenlegi kompatibilitási probléma miatt a DNS-alagútképzés le van tiltva.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>A ritka VHD-támogatás jelenleg le van tiltva az adatok esetleges sérülése miatt.\nHa egy disztribúciót ritka VHD használatára szeretne kényszeríteni, futtassa a következőt:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>A(z) {} csatlakoztatása nem sikerült. További részletekért tekintse meg a dmesg fájlt.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>A /etc/fstab feldolgozása a mount -a parancs használatával nem sikerült.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Hiba történt a terjesztési lemez csatlakoztatása közben. A lemez tartalékként írásvédettként lett csatlakoztatva.\nHelyreállítási utasítások megtekintése a következőn: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Nem sikerült lefordítani a következőt: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Hiba történt. Próbálkozzon újra később.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Névjegy</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Tudnivalók a Linuxos Windows-alrendszer Gépházról</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Minden jog fenntartva.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Linuxos Windows-alrendszer Gépház</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>A Linuxos Windows-alrendszer Gépházban a fejlesztők grafikus felhasználói felületre épülő alkalmazással kezelhetik a .wslconfig fájlt.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Automatikus memória-visszanyerés</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Automatikusan felszabadítja a gyorsítótárazott memóriát az üresjárati CPU-használat észlelése után. Lassú felszabadítás esetén gradual, a gyorsítótárazott memória azonnali felszabadításához pedig dropcache értékre állítsa.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatikus memória-visszanyerés.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Automatikusan felszabadítja a gyorsítótárazott memóriát az üresjárati CPU-használat észlelése után. Lassú felszabadítás esetén gradual, a gyorsítótárazott memória azonnali felszabadításához pedig dropcache értékre állítsa.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Automatikus proxy engedélyezve</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Lehetővé teszi a WSL-nek a Windows HTTP-proxyadatainak használatát.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatikus proxy engedélyezve.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Lehetővé teszi a WSL-nek a Windows HTTP-proxyadatainak használatát.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>A lehető legjobb DNS-elemzést használja</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha a wsl2.dnsTunneling értéke true. Ha igaz értékre van állítva, a Windows kinyeri a kérdést a DNS-kérelemből, és megkísérli a megoldását, figyelmen kívül hagyva az ismeretlen rekordokat.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>A lehető legjobb DNS-elemzést használja.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha a wsl2.dnsTunneling értéke true. Ha igaz értékre van állítva, a Windows kinyeri a kérdést a DNS-kérelemből, és megkísérli a megoldását, figyelmen kívül hagyva az ismeretlen rekordokat.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Egyéni kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Egy egyéni Linux-kernel abszolút Windows-elérési útja.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Kernelek tallózása</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Egyéni kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Egy egyéni Linux-kernel abszolút Windows-elérési útja.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Egyedi kernelmodulok</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Egyéni Linux-kernel modulok virtuális merevlemezének abszolút Windows-elérési útja.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Kernelmodulok böngészése</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Egyedi kernelmodulok</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Egyéni Linux-kernel modulok virtuális merevlemezének abszolút Windows-elérési útja.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Egyéni rendszer disztribúciója</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Böngészés a disztribúciók között</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Adja meg egy olyan VHD elérési útját, amely egyéni rendszer-disztribúcióként lesz betöltve, amelyet elsősorban a WSL grafikus felhasználói felületi alkalmazásaihoz használnak. [További információ a rendszer disztribúcióiról itt].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Egyéni rendszer disztribúciója</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Adja meg egy olyan VHD elérési útját, amely egyéni rendszer-disztribúcióként lesz betöltve, amelyet elsősorban a WSL grafikus felhasználói felületi alkalmazásaihoz használnak.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Hibakeresési konzol engedélyezése</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Logikai érték egy kimeneti konzolablak bekapcsolásához, amely megjeleníti a dmesg tartalmát egy WSL 2 disztribúciós példány indításakor.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hibakeresési konzol engedélyezése.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Logikai érték egy kimeneti konzolablak bekapcsolásához, amely megjeleníti a diagnosztikai üzenetek tartalmát egy WSL 2 disztribúciós példány indításakor.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Alapértelmezett VHD-méret</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>A bővíthető WSL virtuális merevlemez (VHD) alapértelmezett maximális mérete csak az újonnan létrehozott disztribúciókhoz.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Méret visszaállítása</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Alapértelmezett VHD-méret</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>A bővíthető WSL virtuális merevlemez (VHD) alapértelmezett megabájtban mért maximális mérete csak az újonnan létrehozott disztribúciókhoz.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Fejlesztő</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentáció</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>DrvFS mód módosítása</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Az operációs rendszerek közötti fájlhozzáférés implementálásának módosítása a WSL-ben.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS proxy engedélyezve</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha a wsl2.networkingMode NAT értékre van beállítva. Logikai érték, amely tájékoztatja a WSL-t, hogy konfigurálja a DNS-kiszolgálót Linux rendszeren a gazdagép NAT-jára. A false értékre állítás a DNS-kiszolgálókat Windowsról Linuxra tükrözi.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS proxy engedélyezve.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha a wsl2.networkingMode NAT értékre van beállítva. Logikai érték, amely tájékoztatja a WSL-t, hogy konfigurálja a DNS-kiszolgálót Linux rendszeren a gazdagép NAT-jára. A false értékre állítás a DNS-kiszolgálókat Windowsról Linuxra tükrözi.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>DNS-alagútkezelés engedélyezve</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>A DNS-kérések WSL-ről Windowsra történő proxyzása szerint változik.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-alagútkezelés engedélyezve.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>A DNS-kérések WSL-ről Windowsra történő proxyzása szerint változik.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Fájlrendszer</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Grafikus felhasználói felületű alkalmazások engedélyezése</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Logikai érték a grafikus felhasználói felületi alkalmazások ([WSLg]) WSL-ben való támogatásának be- vagy kikapcsolásához.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Grafikus felhasználói felületű alkalmazások engedélyezése.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Logikai érték a grafikus felhasználói felületi alkalmazások (más néven WSL g) WSL-ben való támogatásának be- vagy kikapcsolásához.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Hardverteljesítmény-számlálók engedélyezése</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Lehetővé teszi a linuxos hardverteljesítmény-számlálók használatát.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hardverteljesítmény-számlálók engedélyezése.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Lehetővé teszi a linuxos hardverteljesítmény-számlálók használatát.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Hyper-V tűzfal engedélyezve</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Engedélyezi a Hyper-V tűzfalat, amely lehetővé teszi a Windows tűzfal szabályait, valamint a Hyper-V-forgalomra vonatkozó szabályokat a WSL hálózati forgalom szűréséhez.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hyper-V tűzfal engedélyezve.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Engedélyezi a Hyper-V tűzfalat, amely lehetővé teszi a Windows tűzfal szabályait, valamint a Hyper-V-forgalomra vonatkozó szabályokat a WSL hálózati forgalom szűréséhez.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Állomáscím visszacsatolása</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha wsl2.networkingMode tükrözöttre van beállítva. Ha Igaz értékre van állítva, a tároló egy, a gazdagéphez rendelt IP-cím használatával csatlakozhat a gazdagéphez, vagy a gazdagép csatlakozhat a tárolóhoz. Vegye figyelembe, hogy a 127.0.0.1 visszacsatolási cím mindig használható – ez a beállítás lehetővé teszi az összes további hozzárendelt helyi IP-cím használatát is.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Állomáscím visszacsatolása.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha a wsl2.networkingMode tükrözött értékre van állítva. Ha Igaz értékre van állítva, a tároló egy, a gazdagéphez rendelt IP-cím használatával csatlakozhat a gazdagéphez, vagy a gazdagép csatlakozhat a tárolóhoz. Vegye figyelembe, hogy a 127.0.0.1 visszacsatolási cím mindig használható – ez a beállítás lehetővé teszi az összes további hozzárendelt helyi IP-cím használatát is.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Figyelmen kívül hagyott portok</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha a wsl2.networkingMode mirrored értékre van beállítva. Megadja, hogy mely portokhoz kapcsolódhatnak a Linux-alkalmazások, amelyek nem lesznek automatikusan továbbítva vagy figyelembe véve a Windowsban. Vesszővel tagolt listában kell formázni, például 3000,9000,9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Portok alaphelyzetbe állítása</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Figyelmen kívül hagyott portok</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha a wsl2.networkingMode mirrored értékre van beállítva. Megadja, hogy mely portokhoz kapcsolódhatnak a Linux-alkalmazások, amelyek nem lesznek automatikusan továbbítva vagy figyelembe véve a Windowsban. Vesszővel tagolt listában kell formázni, például 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Kezdeti automatikus proxy időtúllépése</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha a wsl2.autoProxy igaz értékre van állítva. Konfigurálja, hogy a WSL mennyi ideig (ezredmásodpercben) várja meg a HTTP-proxyadatok lekérését a WSL-tároló indításakor. Ha ezt követően feloldja a proxybeállításokat, a WSL-példányt újra kell indítani a lekért proxybeállítások használatához.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Időtúllépés alaphelyzetbe állítása</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Kezdeti automatikus proxy időtúllépése</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Csak akkor alkalmazható, ha a wsl2.autoProxy igaz értékre van állítva. Konfigurálja, hogy a WSL mennyi ideig (ezredmásodpercben) várja meg a HTTP-proxyadatok lekérését a WSL-tároló indításakor. Ha ezt követően feloldja a proxybeállításokat, a WSL-példányt újra kell indítani a lekért proxybeállítások használatához.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Kernel parancssora</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>További kernel parancssori argumentumok.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Localhost-továbbítás engedélyezése</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Logikai érték, amely azt határozza meg, hogy a WSL 2 virtuális gép helyettesítő karaktereihez vagy localhost eleméhez kötött portok csatlakoztathatók legyenek-e a gazdagépről a localhost:port használatával.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Localhost-továbbítás engedélyezése.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Logikai érték, amely azt határozza meg, hogy a WSL 2 virtuális gép helyettesítő karaktereihez vagy localhost eleméhez kötött portok csatlakoztathatók legyenek-e a gazdagépről a localhost, kettőspont, port használatával.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Memória és processzor</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Memóriaméret</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>A WSL 2 virtuális géphez hozzárendelendő memória mennyisége.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Méret visszaállítása</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Memóriaméret</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>A WSL 2 virtuális géphez rendelendő, megabájtban megadott memóriamennyiség.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} ezredmásodperc</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Beágyazott virtualizálás engedélyezése</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Logikai érték a beágyazott virtualizálás be- vagy kikapcsolásához, ami lehetővé teszi más beágyazott virtuális gépek futtatását a WSL 2-ben.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Beágyazott virtualizálás engedélyezése.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Logikai érték a beágyazott virtualizálás be- vagy kikapcsolásához, ami lehetővé teszi más beágyazott virtuális gépek futtatását a WSL 2-ben.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Hálózati mód</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Megadja a WSL hálózati módját.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hálózati mód.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Megadja a WSL hálózati módját.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Hálózatkezelés</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Az összes fájljával különböző operációs rendszereken dolgozhat!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Operációs rendszerek közötti fájlhozzáférés</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Windows-fájlok elérése Linuxból</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>A Windows-fájlokat a Linuxon belül érheti el, ha a „/mnt” helyre, majd a Windows meghajtóbetűjelére lép, a C-meghajtó alábbi példájához hasonlóan:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>A Linux-fájlok elérése a Fájlkezelőben</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>A Linux-fájlokat a Fájlkezelőben a '\\\\wsl.localhost\\' mappában vagy a Linux ikonra kattintva tekintheti meg.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Windows-fájlok és -programok indítása WSL-ből</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>A WSL használata közben is közvetlenül a Bash-ből futtathatja a Windows végrehajtható fájljait. Próbálja futtatni\na 'powershell.exe /c start .' parancsot a Fájlkezelő megnyitásához az aktuális mappában.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Linux hálózati alkalmazások elérése a Windowsból</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Ha hálózati alkalmazást (például NodeJS-en vagy SQL-kiszolgálón futó alkalmazást) hoz létre a Linux-disztribúcióban, a localhost használatával elérheti azt egy Windows-alkalmazásból (például a Microsoft Edge vagy a Chrome böngészőből). Ez azt jelenti, hogy ha olyan Linux-kiszolgálót indított el, amely a 3000-es portot figyeli, a windowsos [http://localhost:3000] elérési úton érheti el.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Tükrözött módú hálózat</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>A WSL egy új, tükrözött módot is tartalmaz, amely fejlett funkciókat biztosít, például IPv6-támogatást, valamint hozzáférést a hálózati alkalmazásokhoz a helyi hálózaton.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>További információ az operációs rendszerek közötti fájlhozzáférésről</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Üdvözli a Linuxos Windows-alrendszer</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>A WSL kiváló módja a különböző Linux-disztribúciók kipróbálásának.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Disztribúciókezelés</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>További információ az alapszintű WSL-parancsokról</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>További információ a linuxos disztribúciók importálásáról</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Telepíthető WSL-disztribúciók listázása parancs</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Nevesített WSL-disztribúció telepítése parancs</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Elérhető WSL-disztribúciók listázása parancs</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>A Docker Desktop nagyszerűen működik együtt a WSL-lel, hogy segítsen a Linux-tárolókkal való fejlesztésben.\n\nA Docker Desktop WSL-lel való használatának néhány előnye:\n\n• Docker-parancsokat futtathat WSL-ben vagy Windowsban, ugyanazzal a Docker-démonnal és lemezképekkel.\n• A windowsos meghajtók WSL-beli automatikus csatlakoztatásával zökkenőmentesen oszthat meg fájlokat és mappákat Windows és Linux között.\n• A WSL együttműködési képességének köszönhetően a kívánt Windows-eszközökkel és -szerkesztőkkel dolgozhat Linux-kódon és -fájlokon, vagy fordítva.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker Desktop-integráció</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>További információ a WSL használatáról Dockerrel</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>A Linuxos Windows-alrendszer (WSL) segítségével közvetlenül a Windowson futtathatja kedvenc Linux-eszközeit, segédprogramjait, alkalmazásait és munkafolyamatait.\n\nSzánjon egy kis időt a közösség néhány kedvenc funkciójának előzetes megtekintésére, vagy tekintse meg átfogó dokumentációnkat.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Üdvözli a WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Ajánlott eljárások a beállításhoz</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Első lépések Linuxszal</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>A linuxos Windows-alrendszer (WSL) dokumentációja</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Grafikus linuxos alkalmazásokat használhat natív Windows-interakciókkal, például alt-tab billentyűkombinációval, start menü indítása, tálca kitűzése, valamint a kivágás és beillesztés támogatása.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>Grafikus felhasználói felületű alkalmazások</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>A WSL képes kihasználni a Windows GPU-t gépi tanulási munkafolyamatokhoz\n\nA WSL-ben futó Linux bináris fájlok automatikusan használhatják a GPU-t a Windowsban a teljesítményük gyorsításához. Ezeket a munkafolyamatokat ugyanúgy kell telepítenie és futtatnia, mint egy normál linuxos gépen. Számos különböző módon kezdheti meg a CUDA futtatását egy Docker-tárolóban, ha NVIDIA grafikus kártyával rendelkezik, a PyTorch vagy a TensorFlow DirectML-vel való futtatásához AMD- vagy Intel- vagy NVIDIA-videokártyán. További információért tekintse meg az alábbi kezdő útmutatót.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>GPU-gyorsítás</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>További információ a GPU-gyorsításról</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>További információ a WSL GUI-alkalmazásokról</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Íme a kipróbálandó alkalmazások listája (az összes alkalmazást telepítheti Ubuntuban a 'sudo apt install &lt;Az alkalmazás neve&gt;' beírásával)\n\n    • gedit – Basic szövegszerkesztő\n    • audacity – Hangfájlok rögzítése és szerkesztése\n    • blender – 3D animációk és vizualizációk készítése\n    • gimp – Fényképek szerkesztése\n    • nautilus – Egy Linux fájlkezelő\n    • vlc – Videolejátszó</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>A windowsos és a linuxos operációs rendszereken is könnyedén elérheti a hálózati alkalmazásokat.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Hálózati integráció</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>További információ a hálózati alkalmazásokról</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>További információ a tükrözött módú hálózatkezelésről</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>A WSL-t közvetlenül a VS Code-ból teljes munkaidős fejlesztési környezetként is használhatja.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS Code-integráció</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>További információ a WSL használatáról VS Code-dal</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>A telepítés módja</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>A VS Code telepítése után telepítheti a távoli WSL-bővítményt a Windows terminálból:\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>WSL-projekt megnyitása Visual Studio Code-ban</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Ha a WSL-disztribúcióból szeretne megnyitni egy projektet a VS Code-ban, nyissa meg a terjesztési parancssort, és futtassa a 'code .' parancsot egy projektfájl megnyitásához.\n\nA VS Code-ban található parancskatalógusban további VS Code Remote beállításokat is elérhet. A „SHIFT+CTRL+P” billentyűkombinációval nyissa meg a parancspalettát, és írja be a \"Remote-WSL\" parancsot a rendelkezésre álló VS Code Remote-beállítások listájának megtekintéséhez, lehetővé téve a mappa újbóli megnyitását egy távoli munkamenetben, adja meg a megnyitni kívánt disztribúciót és egyebeket.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Használati útmutató</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Választható funkciók</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Adatvédelmi nyilatkozat</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Processzorok száma</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>A WSL 2 virtuális géphez hozzárendelni kívánt logikai processzorok száma.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Szám alaphelyzetbe állítása</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Processzorok száma</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>A WSL 2 virtuális géphez hozzárendelni kívánt logikai processzorok száma.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Kapcsolódó hivatkozások</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Kibocsátási megjegyzések</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Csökkentett mód engedélyezése</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Futtatja a WSL-t csökkentett módban, amely számos funkciót letilt, és a rossz állapotú disztribúciók helyreállítására szolgál.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Csökkentett mód engedélyezése.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Futtatja a WSL-t csökkentett módban, amely számos funkciót letilt, és a rossz állapotú disztribúciók helyreállítására szolgál.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Névjegy</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Fejlesztő</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Disztribúciókezelés</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Docker Desktop-integráció</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Fájlrendszer</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Általános</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>GPU-gyorsítás</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>Grafikus felhasználói felületű alkalmazások</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Wsl.exe indítása</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Memória és processzor</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Hálózatkezelés</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Hálózati integráció</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Üdvözli a WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Választható funkciók</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Beállítás</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS Code-integráció</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Újdonságok</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Fájlrendszerek közötti munka</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problémák</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Ritka VHD engedélyezése alapértelmezés szerint</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Az újonnan létrehozott virtuális merevlemezek automatikus ritkításra lesznek beállítva, ha engedélyezve vannak.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ritka VHD engedélyezése alapértelmezés szerint.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Az újonnan létrehozott virtuális merevlemezek automatikus ritkításra lesznek beállítva, ha engedélyezve vannak.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Fájl helyének felcserélése</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>A virtuális merevlemez felcserélésére szolgáló abszolút Windows-elérési út.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Tallózás a felcserélési fájlok között</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Fájl helyének felcserélése</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>A virtuális merevlemez felcserélésére szolgáló abszolút Windows-elérési út.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Felcserélési méret</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Mennyi lapozóterületet kell hozzáadni a WSL 2 virtuális géphez, 0-t a felcserélés nélküli fájlhoz. A felcserélési tároló lemezalapú RAM, amit akkor használ a rendszer ha a memóriaigény meghaladja a hardvereszközökre vonatkozó korlátot.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Méret visszaállítása</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Felcserélési méret</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>A WSL 2 virtuális géphez hozzáadandó lapozótár megabájtban megadva. 0 a felcserélés nélküli fájlhoz. A felcserélési tároló lemezalapú RAM, amit akkor használ a rendszer ha a memóriaigény meghaladja a hardvereszközökre vonatkozó korlátot.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>VirtIO engedélyezése</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>A 9. csomag helyett használjon virtuális kódot a gazdagépfájlok eléréséhez, ezzel növelve a sebességet.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Virtio 9p engedélyezése</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Engedélyezi a 9P-fájlrendszer csatlakoztatását a gazdagépről a virtio átvitellel.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Virtuális gép üresjárati időkorlátja</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>A virtuális gép üresjárati ideje ezredmásodpercben a leállítás előtt.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Időtúllépés alaphelyzetbe állítása</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Virtuális gép üresjárati időkorlátja</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>A virtuális gép üresjárati ideje ezredmásodpercben a leállítás előtt.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>WSL-en futó alkalmazásokat készíthet, futtathat, profilozhat és végezhet rajtuk hibakeresést a Visual Studio segítségével</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Alkalmazások futtatása és hibakeresés végzése a WSL-ben a Visual Studio segítségével</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>További információ a WSL-ről a .NET-fejlesztőknek készült Visual Studióban</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>További információ a Visual Studióról és a WSL-ről C++ fejlesztők számára</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>A Linuxos Windows-alrendszer (WSL) használatával könnyedén futtathatja és hibakeresést végezhet a .NET Core és platformfüggetlen C++ alkalmazásain Linuxon, anélkül, hogy elhagyná a Visual Studiót. Ha Ön platformfüggetlen fejlesztő, ez a módszer egyszerű módot kínál arra, hogy több célkörnyezetet is teszteljen.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studio-integráció</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studio-integráció</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/it-IT/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Sottosistema Windows per Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Il Sottosistema Windows per Linux consente agli sviluppatori di eseguire un ambiente GNU/Linux, inclusi la maggior parte degli strumenti da riga di comando, le utilità e le applicazioni, direttamente in Windows, senza modifiche, senza il sovraccarico di una macchina virtuale tradizionale o di una configurazione dualboot.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Impossibile scollegare il disco: {}. Per altri dettagli, eseguire 'dmesg' in WSL2.\nPer forzare l'arresto e lo scollegamento del disco in WSL2, eseguire 'wsl.exe {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Il disco '{}' è già collegato.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Il volume è già montato all'interno di WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Un disco con questo nome è già montato; smontare il disco o scegliere un nuovo nome e riprovare.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Il nome di montaggio specificato contiene un carattere '/' non valido. Riprovare senza il carattere non valido.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Montaggio del disco come '/mnt/wsl/{}' completato.\nNota: il percorso sarà diverso se è stata modificata l'impostazione automount.root in /etc/wsl.conf.\nPer smontare e scollegare il disco, eseguire 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Il disco è stato collegato ma non è stato possibile montarlo: {}.\nPer altri dettagli, eseguire 'dmesg' all'interno di WSL2.\nPer scollegare il disco, eseguire 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Di seguito è riportato un elenco di distribuzioni valide che è possibile installare.\nInstallare con 'wsl.exe {} &lt;Distro&gt;'.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Il nome di distribuzione è già stato impostato.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>Il percorso di installazione specificato è già in uso.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Esiste già una distribuzione con il nome specificato. Utilizzare --name per scegliere un nome diverso.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Nessuna distribuzione con il nome specificato.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Per montare un disco è necessario l'accesso amministratore.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Esportazione della distribuzione non riuscita.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Esecuzione dell'aggiornamento una tantum del file system del sottosistema Windows per Linux per questa distribuzione...\n</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Non è possibile eseguire l'avvio perché un'altra istanza è in esecuzione con privilegi non elevati. Non è possibile eseguire contemporaneamente istanze con privilegi elevati e non elevati.\n</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Importazione della distribuzione non riuscita.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Installazione del componente facoltativo di Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Download in corso: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Installazione: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importazione distribuzione</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} è stato scaricato.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>sottosistema Windows per Linux sta riprendendo un'installazione precedente...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Nome di distribuzione non valido: '{}'.\nPer ottenere un elenco di distribuzioni valide, usare 'wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Istanza di sottosistema Windows per Linux terminata.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Argomento della riga di comando non valido: {}\nUsare '{} --help' per ottenere un elenco di argomenti supportati.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>L'argomento {} della riga di comando richiede un valore.\nUsare '{} --help' per ottenere un elenco di argomenti supportati.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Impostazioni della console non supportate. Per utilizzare questa funzionalità, la console legacy deve essere disabilitata.\n</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} non è un numero intero valido.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>È in corso un'installazione, una disinstallazione o una conversione per questa distribuzione.\n</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Avvio di {} in corso...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Non è possibile eseguire l'avvio perché un'altra istanza è in esecuzione con privilegi elevati. Non è possibile eseguire contemporaneamente istanze con privilegi elevati e non elevati.\n</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>sottosistema Windows per Linux non ha distribuzioni installate.\nPer risolvere il problema, installare una distribuzione con le istruzioni seguenti:\n\nUsare 'wsl.exe --list --online' per elencare le distribuzioni disponibili\ne 'wsl.exe --install &lt;Distro&gt;' per l'installazione.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Nessuna distribuzione in corso.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Predefinito)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Distribuzioni del Sottosistema Windows per Linux:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Distribuzione predefinita: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Versione predefinita: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Annullamento della registrazione.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Utente non trovato.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Per informazioni sulle differenze delle chiavi con WSL 2, visita https://aka.ms/wsl2\n</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Conversione in corso. L'operazione potrebbe richiedere alcuni minuti.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>La distribuzione è già nella versione richiesta.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>La distribuzione legacy non supporta WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>Impossibile avviare WSL2 perché la virtualizzazione non è abilitata in questo computer.\nVerificare che il componente facoltativo \"Piattaforma macchina virtuale\" sia abilitato e che la virtualizzazione sia attivata nelle impostazioni del firmware del computer.\n\nAbilitare \"Virtual Machine Platform\" eseguendo: wsl.exe --install --no-distribution\n\nPer informazioni, visitare https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Sono collegati troppi dischi rigidi virtuali o dischi fisici.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD già montato tramite wsl.exe --mount, smontare il disco prima del lancio.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 non è supportato con la configurazione del computer corrente.\nPer usare WSL1, è necessario abilitare il componente facoltativo \"Sottosistema Windows per Linux\".</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Questa operazione è supportata solo da WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Utilizzo:\n    --networking-mode\n       Visualizza la modalità di rete corrente.\n\n    --msal-proxy-path\n        Visualizza il percorso dell'applicazione proxy MSAL.\n\n    --vm-id\n        Visualizza l'ID della macchina virtuale WSL.\n\n    --version\n        Visualizza la versione del pacchetto WSL.\n\n    -n\n        Non stampa una nuova riga.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Uso:\n    -a\n        Forza il risultato al formato di percorso assoluto.\n    -u\n        Converti da un percorso windows a un percorso WSL (impostazione predefinita).\n    -w\n        Converti da un percorso WSL a un percorso di Windows.\n    -m\n        Traduci da un percorso WSL a un percorso di Windows, con '/' invece di '\\\\'\n\nEsempio: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Esegue operazioni amministrative su sottosistema Windows per Linux\n\nUtilizzo:\n    /l, /list [Opzione]\n        Elenca le distribuzioni registrate.\n        /all - Elenca facoltativamente tutte le distribuzioni, incluse quelle che\n               sono attualmente in fase di installazione o disinstallazione.\n\n        /running - Elenca solo le distribuzioni attualmente in esecuzione.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Imposta la distribuzione come predefinita.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Termina la distribuzione.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Annulla la registrazione della distribuzione ed elimina il file system radice.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Tutti i diritti sono riservati.\nPer informazioni sulla privacy di questo prodotto, visita https://aka.ms/privacy.\n\nSintassi: wsl.exe [Argument] [Options...] [CommandLine]\n\nArgomenti per l'esecuzione di file binari Linux:\n\n    Se non viene specificata alcuna riga di comando, wsl.exe avvia la shell predefinita.\n\n    --exec, -e &lt;CommandLine&gt;\n        Esegui il comando specificato senza usare la shell Linux predefinita.\n\n    --shell-type &lt;standard|login|none&gt;\n        Esegui il comando specificato con il tipo di shell fornito.\n\n    --\n        Passa la riga di comando rimanente senza modifiche.\n\nOpzioni:\n    --cd &lt;Directory&gt;\n        Imposta la directory specificata come directory di lavoro corrente.\n        Se viene usato ~, verrà usato il percorso home dell'utente Linux. Se il percorso inizia\n        con un carattere /, verrà interpretato come un percorso Linux assoluto.\n        In caso contrario, il valore deve essere un percorso assoluto di Windows.\n\n    --distribution, -d &lt;DistroName&gt;\n        Esegui la distribuzione specificata.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Esegui l'ID distribuzione specificato.\n\n    --user, -u &lt;UserName&gt;\n        Esegui il comando con il nome utente specificato.\n\n    --system\n        Avvia una shell per la distribuzione del sistema.\n\nArgomenti per la gestione del sottosistema Windows per Linux:\n\n    --help\n        Visualizza le informazioni sulla sintassi.\n\n    --debug-shell\n        Apri una shell di debug WSL2 a scopo di diagnostica.\n\n    --install [Distro] [Options...]\n        Installa una distribuzione di un sottosistema Windows per Linux.\n        Per un elenco di distribuzioni valide, usa 'wsl.exe --list --online'.\n\n        Opzioni:\n            --enable-wsl1\n                Abilita il supporto di WSL1.\n\n            --fixed-vhd\n                Crea un disco a dimensione fissa per archiviare la distribuzione.\n\n            --from-file &lt;Path&gt;\n                Installa una distribuzione da un file locale.\n\n            --legacy\n                Usa il manifesto della distribuzione legacy.\n\n            --location &lt;Location&gt;\n                Imposta il percorso di installazione per la distribuzione.\n\n            --name &lt;Name&gt;\n                Imposta il nome della distribuzione.\n\n            --no-distribution\n                Installa solo i componenti facoltativi necessari, senza installare una distribuzione.\n\n            --no-launch, -n\n                Non avviare la distribuzione dopo l'installazione.\n\n            --version &lt;Version&gt;\n                Specifica la versione da usare per la nuova distribuzione.\n\n            --vhd-size &lt;MemoryString&gt;\n                Specifica le dimensioni del disco in cui archiviare la distribuzione.\n\n            --web-download\n                Scarica la distribuzione da Internet anziché dal Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Modifica le opzioni specifiche della distribuzione.\n\n        Opzioni:\n            --move &lt;Location&gt;\n                Sposta la distribuzione in una nuova posizione.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Imposta il VHD della distribuzione su sparse, consentendo il recupero automatico dello spazio su disco.\n\n            --set-default-user &lt;Username&gt;\n                Imposta l'utente predefinito della distribuzione.\n\n            --resize &lt;MemoryString&gt;\n                Ridimensiona il disco della distribuzione alle dimensioni specificate.\n\n    --mount &lt;Disk&gt;\n        Collega e monta un disco fisico o virtuale in tutte le distribuzioni WSL2.\n\n        Opzioni:\n            --vhd\n                Specifica che &lt;Disk&gt; fa riferimento a un disco rigido virtuale.\n\n            --bare\n                Collega il disco a WSL2, senza montarlo.\n\n            --name &lt;Name&gt;\n                Monta il disco usando un nome personalizzato per il punto di montaggio.\n\n            --type &lt;Type&gt;\n                File system da usare durante il montaggio di un disco. Se non è specificato, il valore predefinito è ext4.\n\n            --options &lt;Options&gt;\n                Opzioni di montaggio aggiuntive.\n\n            --partition &lt;Index&gt;\n                Indice della partizione da montare. Se non è specificato, il valore predefinito è l'intero disco.\n\n    --set-default-version &lt;Version&gt;\n        Modifica la versione di installazione predefinita per le nuove distribuzioni.\n\n    --shutdown\n        Termina immediatamente tutte le distribuzioni in esecuzione e\n        la macchina virtuale dell'utilità leggera WSL2.\n\n        Opzioni:\n            --force\n                Termina la macchina virtuale WSL 2 anche se è in corso un'operazione. Può causare la perdita di dati.\n\n    --status\n        Mostra lo stato del sottosistema Windows per Linux.\n\n    --unmount [Disk]\n        Smonta e scollega un disco da tutte le distribuzioni WSL2.\n        Smonta e scollega tutti i dischi se viene chiamata senza argomento.\n\n    --uninstall\n        Disinstalla il pacchetto sottosistema Windows per Linux da questo computer.\n\n    --update\n        Aggiorna il pacchetto del sottosistema Windows per Linux.\n\n        Opzioni:\n            --pre-release\n                Scarica una versione non definitiva, se disponibile.\n\n    --version, -v\n        Mostra le informazioni sulla versione.\n\nArgomenti per la gestione delle distribuzioni del sottosistema Windows per Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Esporta la distribuzione in un file TAR.\n        Il nome file può essere - per StdOut.\n\n        Opzioni:\n            --format &lt;Format&gt;\n                Specifica il formato di esportazione. Valori supportati: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        Importa il file TAR specificato come nuova distribuzione.\n        Il nome file può essere - per STDIN.\n\n        Opzioni:\n            --version &lt;Version&gt;\n                Specifica la versione da usare per la nuova distribuzione.\n\n            --vhd\n                Specifica che il file fornito è un file con estensione vhd o vhdx, non un file TAR.\n                Questa operazione crea una copia del file VHD nel percorso di installazione specificato.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Importa il file VHD specificato come nuova distribuzione.\n        Questo disco rigido virtuale deve essere formattato con il tipo di file system ext4.\n\n    --list, -l [Options]\n        Elenca le distribuzioni.\n\n        Opzioni:\n            --all\n                Elenca tutte le distribuzioni, incluse quelle che al momento\n                sono in fase di installazione o disinstallazione.\n\n            --running\n                Elenca solo le distribuzioni attualmente in esecuzione.\n\n            --quiet, -q\n                Mostra solo i nomi delle distribuzioni.\n\n            --verbose, -v\n                Mostra informazioni dettagliate su tutte le distribuzioni.\n\n            --online, -o\n                Visualizza un elenco di distribuzioni disponibili per l'installazione con 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Imposta la distribuzione come predefinita.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Cambia la versione della distribuzione specificata.\n\n    --terminate, -t &lt;Distro&gt;\n        Termina la distribuzione specificata.\n\n    --unregister &lt;Distro&gt;\n        Annulla la registrazione della distribuzione ed elimina il file system radice.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>Versione WSL: {}\nVersione kernel: {}\nVersione WSLg: {}\nVersione MSRDC: {}\nVersione Direct3D: {}\nVersione DXCore: {}\nVersione di Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>Versione di MSBuild: {}\nCommit: {}\nTempo di compilazione: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>Il kernel personalizzato specificato in {} non è stato trovato: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>Il disco rigido virtuale dei moduli kernel personalizzati in {} non è stato trovato: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>La distribuzione del sistema personalizzata specificata in {} non è stata trovata o non è nel formato corretto.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Tutti i diritti sono riservati.\nPer informazioni sulla privacy di questo prodotto, visita https://aka.ms/privacy.\n\nSintassi: wslg.exe [Argument] [Options...] [CommandLine]\n\nArgomenti:\n    --cd &lt;Directory&gt;\n        Imposta la directory specificata come directory di lavoro corrente.\n        Se si usa ~, verrà usato il percorso home dell'utente Linux. Se il percorso inizia\n        con un carattere /, verrà interpretato come un percorso Linux assoluto.\n        In caso contrario, il valore deve essere un percorso assoluto di Windows.\n\n    --distribution, -d &lt;Distribuzione&gt;\n        Esegue la distribuzione specificata.\n\n    --user, -u &lt;Nome_utente&gt;\n        Esegue il comando con il nome utente specificato.\n\n    --shell-type &lt;standard|login|none&gt;\n        Esegue il comando specificato con il tipo di shell fornito.\n\n    --help\n        Visualizza le informazioni sulla sintassi.\n\n    --\n        Passa la riga di comando rimanente senza modifiche.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Premere un tasto qualsiasi per continuare...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Nell'argomento {} manca un parametro obbligatorio.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Esportazione in corso. L'operazione potrebbe durare alcuni minuti.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Importazione in corso. L'operazione potrebbe durare alcuni minuti.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>Il supporto dell'applicazione GUI è disabilitato tramite {} o /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Verifica della disponibilità di aggiornamenti.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Questa distribuzione è disponibile solo dal Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount in ARM64 richiede Windows versione 27653 o successiva.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>La versione più recente di sottosistema Windows per Linux è già installata.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Aggiornamento della sottosistema Windows per Linux alla versione: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Questa applicazione richiede il componente facoltativo sottosistema Windows per Linux.\nInstallarlo eseguendo: wsl.exe --install --no-distribution\nPotrebbe essere necessario riavviare il sistema per rendere effettive le modifiche.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Il file specificato deve avere l'estensione {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Il file specificato deve avere l'estensione {} o {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>VmSwitch '{}' non è stato trovato. Switch disponibili: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>La rete bridged richiede l'impostazione di wsl2.vmSwitch.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Recupero dell'elenco di distribuzione da '{}'. {} non riuscito</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Non è stato possibile collegare il disco '{}' a WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Non è possibile ridimensionare il disco.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Nessun valore trovato.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>La versione di Windows {} non supporta la versione in pacchetto di sottosistema Windows per Linux.\nInstalla l'aggiornamento richiesto tramite Windows Update o tramite: {}\nPer informazioni, visitare https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>L'esecuzione della shell di debug richiede l'esecuzione wsl.exe come amministratore.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Il processo di installazione per la distribuzione '{}' non è riuscito con codice di uscita: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Formato GUID non valido: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Nome di sezione non valido in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Nome di chiave non valido in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Previsto {} in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Carattere di escape non valido: '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Chiave sconosciuta '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Valore integer '{}' non valido per la chiave '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Valore IP '{}' non valido per la chiave '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Valore booleano '{}' non valido per la chiave '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Indirizzo Mac '{}' non valido per la chiave '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Non è possibile aprire il file di configurazione {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Si sono verificati errori durante l'avvio di WSL</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Non è stato possibile avviare la sessione utente di sistema per '{}'. Per ulteriori dettagli, vedere journalctl.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Apri EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Questa distribuzione non contiene un nome predefinito. Usare --name per scegliere il nome della distribuzione.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Non è possibile specificare contemporaneamente gli argomenti {} e {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>L’argomento {} richiede l’argomento {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Nome di distribuzione non valido: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Verrà usata la registrazione della distribuzione legacy. Provare a usare una distribuzione basata su tar.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Le registrazioni di distribuzione legacy non supportano --version argument.</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>La distribuzione \"{}\" è fornita da un manifesto di override.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>L'hash di distribuzione non corrisponde. Previsto: {}, hash effettivo: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Stringa Hex non valida: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>'{}' non è supportato durante l'installazione di distribuzioni legacy.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Installazione della distribuzione completata. Può essere avviato tramite 'wsl.exe -d {}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Stringa di memoria '{}' non valida per .wslconfig voce '{}' in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Non è stato possibile creare il disco di scambio in '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>La virtualizzazione annidata non è supportata in questo computer.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Non è stato possibile creare l'endpoint di rete con indirizzo: '{}', nuovo indirizzo assegnato: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Non è stato possibile creare la rete virtuale con l'intervallo di indirizzi: '{}', è stata creata una nuova rete con intervallo: '{}', {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>MODALITÀ PROVVISORIA ABILITATA - Molte funzionalità verranno disabilitate</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>La modalità di rete con mirroring non è supportata: {}.\nFallback alla rete NAT.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>È necessario il kernel Linux versione 5.10 o successiva</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Versione di Windows {}. {} non dispone delle funzionalità necessarie</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Il firewall Hyper-V non è supportato</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>Tunneling DNS non è supportato</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>L'impostazione wsl2.localhostForwarding non ha effetto quando si usa la modalità di rete con mirroring</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>È stata rilevata una modifica del proxy HTTP nell'host. Riavviare WSL per applicare la modifica.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>È stata rilevata una configurazione proxy localhost ma non è stato eseguito il mirroring in WSL. WSL in modalità NAT non supporta i proxy localhost.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>È stata rilevata una configurazione proxy IPv6 ma non è stato eseguito il mirroring in WSL. WSL in modalità NAT non supporta IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>È stata rilevata una configurazione proxy IPv6 localhost, ma non è stato eseguito il mirroring in WSL. WSL non supporta i proxy IPv6 localhost.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Si è verificato un errore imprevisto durante il tentativo di risolvere le impostazioni proxy; le impostazioni proxy non è stato eseguito il mirroring in WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Errore durante l'accesso al Registro di sistema. Percorso: '{}'. Errore: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Non è stato possibile avviare il processo di inoltro localhost. Errore: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Non è stato possibile avviare la rete virtuale. Installare il componente facoltativo Virtual Machine Platform eseguendo: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Non è stato possibile configurare la rete (networkingMode {}). Verrà eseguito il fallback alla modalità di rete {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Non è stato possibile abilitare il componente di Windows '{}' (codice di uscita {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Il plug-in '{}' ha restituito un errore irreversibile</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Il plug-in '{}' ha restituito un errore irreversibile. Messaggio di errore: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Il plug-in '{}' richiede una versione più recente di WSL. Eseguire: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>L'impostazione .wslconfig '{}' è disabilitata dal criterio del computer.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>La shell di debug è disabilitata dai criteri del computer.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount è disabilitato dai criteri del computer.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>WSL1 è disabilitato dai criteri del computer.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Eseguire 'wsl.exe --set-version {} 2' per eseguire l'aggiornamento a WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL sta ultimando un aggiornamento...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Aggiornamento non riuscito (codice di uscita: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Disinstallazione non riuscita (codice di uscita: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>File di registro: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Impossibile rimuovere il pacchetto MSIX (errore: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Impossibile installare il pacchetto MSIX (errore: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>I componenti facoltativi necessari per eseguire WSL non sono installati.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Installa i componenti mancanti.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Il file importato non è una distribuzione Linux valida.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Premi un tasto qualsiasi per uscire...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>È disponibile una nuova versione del sottosistema Windows per Linux.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Eseguire l'aggiornamento alla versione {} o visualizzarne le note sulla versione di seguito.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Aggiorna</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Visualizza documenti</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Impossibile completare l'operazione perché il disco rigido virtuale è attualmente in uso. Per forzare l'arresto di WSL, usare: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} non è un valore booleano valido, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Disco rigido virtuale di tipo sparse supportato solo in WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>L'esecuzione di WSL come sistema locale non è supportata.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Suggerimento per le prestazioni:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>L'utilizzo di un'operazione di I/O intensivo come {} nelle unità Windows avrà prestazioni ridotte. Per prestazioni migliori, provare a spostare i file di progetto in Linux file system. Fare clic di seguito per altre informazioni.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Visualizza documenti</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Non visualizzare più questo messaggio</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>La macchina virtuale WSL2 ha subito un arresto anomalo.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>L'analisi dello stack è stata salvata in: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Impossibile avviare la distribuzione. Codice errore: {}, passaggio errore: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Impossibile avviare la distribuzione. Il disco virtuale è danneggiato.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Chiave di configurazione '{}' duplicata in {}:{} (chiave in conflitto: '{}' in {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Valore '{}' non valido per la chiave di configurazione '{}' in {}:{} (valori validi: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>In attesa del completamento del comando OOBE per la distribuzione \"{}\"...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Non è stato possibile leggere la proprietà '{}' dalla distribuzione {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Questo sembra un file disco rigido virtuale. Usa --vhd per importare un disco rigido virtuale anziché un tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Non è stato possibile installare {} dal Microsoft Store: {}\nTentativo di download web...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Non è stata configurata alcuna distribuzione predefinita. Specificare una distribuzione da installare.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p è disabilitato. Verrà eseguito il fallback a 9p con il trasporto VSOCK.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>L'installazione di WSL sembra danneggiata (codice di errore: {}).\nPremere un tasto qualsiasi per ripristinare WSL oppure CTRL-C per annullare.\nQuesto prompt smetterà di essere visualizzato tra 60 secondi.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Non è possibile analizzare il profilo del terminale durante la registrazione della distribuzione: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Dimensioni non valide: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nCodice errore: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Documento JSON non valido. Errore di analisi: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processor non può superare il numero di processori logici nel sistema ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Non è possibile eseguire query sulla modalità di rete</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Sono stati forniti moduli kernel personalizzati senza specificare un kernel personalizzato. Per altre informazioni, vedere https://aka.ms/wslcustomkernel.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>A causa di un problema di compatibilità con Accesso globale sicuro, il tunneling DNS è attualmente disabilitato.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Il supporto disco rigido virtuale di tipo sparse attualmente è disabilitato a causa di un potenziale danneggiamento dei dati.\nPer forzare una distribuzione per usare un disco rigido virtuale sparse, esegui:\nwsl.exe --manage &lt;NomeDistribuzione&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Non è stato possibile montare {}. Per altri dettagli, vedere dmesg.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Elaborazione di /etc/fstab con mount -a non riuscita.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Si è verificato un errore durante il montaggio del disco di distribuzione. È stato montato in sola lettura come fallback.\nVedere le istruzioni per il ripristino in: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Traduzione di '{}' non riuscita</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>C'è un problema. Riprova più tardi.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Informazioni su</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Sulle impostazioni del sottosistema Windows per Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Tutti i diritti sono riservati.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Impostazioni del sottosistema Windows per Linux</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Il sottosistema Windows per Linux Settings consente agli sviluppatori di gestire il file con estensione .wslconfig usando un'applicazione basata su GUI.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Recupero automatico memoria</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Rilascia automaticamente la memoria memorizzata nella cache dopo il rilevamento dell'utilizzo della CPU inattiva. Impostare su graduale per il rilascio lento e su DropCache per il rilascio istantaneo della memoria memorizzata nella cache.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Recupero automatico memoria.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Rilascia automaticamente la memoria memorizzata nella cache dopo il rilevamento dell'utilizzo della CPU inattiva. Impostare su graduale per il rilascio lento e su DropCache per il rilascio istantaneo della memoria memorizzata nella cache.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy automatico abilitato</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Consente a WSL di utilizzare le informazioni sul proxy HTTP di Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy automatico abilitato.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Consente a WSL di utilizzare le informazioni sul proxy HTTP di Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Usa l'analisi DNS più efficiente</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.dnsTunneling è impostato su true. Se impostato su true, Windows estrarrà la domanda dalla richiesta DNS e tenterà di risolverla ignorando i record sconosciuti.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Usa l'analisi DNS più efficiente.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.dnsTunneling è impostato su true. Se impostato su true, Windows estrarrà la domanda dalla richiesta DNS e tenterà di risolverla ignorando i record sconosciuti.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Kernel personalizzato</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Percorso assoluto di Windows per un kernel Linux personalizzato.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Esplora kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Kernel personalizzato</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Percorso assoluto di Windows per un kernel Linux personalizzato.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Moduli kernel personalizzati</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Un percorso assoluto di Windows a un VHD di moduli kernel Linux personalizzati.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Sfoglia moduli kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Moduli kernel personalizzati</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Un percorso assoluto di Windows a un VHD di moduli kernel Linux personalizzati.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Distribuzione di sistema personalizzata</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Esplora distribuzioni</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Specificare un percorso di un disco rigido virtuale che verrà caricato come distribuzione personalizzata del sistema, usato principalmente per l'alimentazione delle app GUI in WSL. [Altre informazioni sulle distribuzioni di sistema sono disponibili qui].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Distribuzione di sistema personalizzata</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Specificare un percorso di un disco rigido virtuale che verrà caricato come distribuzione personalizzata del sistema, usato principalmente per l'alimentazione delle app GUI in WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Abilita la console di debug</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Valore booleano per attivare una finestra della console di output che mostra il contenuto di dmesg all'inizio di un'istanza di distribuzione WSL 2.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Abilita la console di debug.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valore booleano per attivare una finestra della console di output che mostra il contenuto dei messaggi di diagnostica all'avvio di un'istanza di distribuzione WSL 2.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Dimensioni predefinite del disco rigido virtuale</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Dimensioni massime predefinite per il disco rigido virtuale WSL espandibile (VHD) solo per le distribuzioni appena create.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Reimposta dimensioni</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Dimensioni predefinite del disco rigido virtuale</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Dimensioni massime predefinite, specificate in megabyte, per il disco rigido virtuale WSL espandibile (VHD) solo per le distribuzioni appena create.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Sviluppatore</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentazione</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Modifica la modalità DrvFS</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Modifica l'implementazione dell'accesso ai file tra sistemi operativi in WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy DNS abilitato</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.networkingMode è impostato su NAT. Valore booleano per informare WSL di configurare il server DNS in Linux nel NAT nell'host. L'impostazione su false eseguirà il mirroring dei server DNS da Windows a Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy DNS abilitato.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.networkingMode è impostato su NAT. Valore booleano per informare WSL di configurare il server DNS in Linux sul NAT nell'host. L'impostazione su false eseguirà il mirroring dei server DNS da Windows a Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>Tunneling DNS abilitato</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Modifica la modalità di inoltro tramite proxy delle richieste DNS da WSL a Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tunneling DNS abilitato.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Modifica la modalità di inoltro tramite proxy delle richieste DNS da WSL a Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>File system</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Abilita le applicazioni GUI</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Valore booleano per attivare o disattivare il supporto per le applicazioni GUI ([WSLg]) in WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Abilita le applicazioni GUI.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valore booleano per attivare o disattivare il supporto per le applicazioni GUI (note come WSL g) in WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Abilita i contatori delle prestazioni hardware</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Abilita i contatori delle prestazioni hardware per Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Abilita i contatori delle prestazioni hardware.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Abilita i contatori delle prestazioni hardware per Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Firewall Hyper-V abilitato</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Abilita il firewall Hyper-V, che consente alle regole di Windows Firewall, nonché alle regole specifiche del traffico Hyper-V, di filtrare il traffico di rete WSL.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Firewall Hyper-V abilitato.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Abilita il firewall Hyper-V, che consente alle regole di Windows Firewall, nonché alle regole specifiche del traffico Hyper-V, di filtrare il traffico di rete WSL.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Loopback degli indirizzi host</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.networkingMode è impostato su mirroring. Se impostato su True, consentirà al contenitore di connettersi all'host o all'host di connettersi al contenitore tramite un indirizzo IP assegnato all'host. Si noti che è sempre possibile usare l'indirizzo di loopback 127.0.0.1. Questa opzione consente anche l'uso di tutti gli indirizzi IP locali assegnati.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Loopback degli indirizzi host.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.networkingMode è impostato su mirroring. Se impostato su True, consentirà al contenitore di connettersi all'host o all'host di connettersi al contenitore tramite un indirizzo IP assegnato all'host. Si noti che è sempre possibile usare l'indirizzo di loopback 127.0.0.1. Questa opzione consente anche l'uso di tutti gli indirizzi IP locali assegnati.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Porte ignorate</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.networkingMode è impostato su mirroring. Specifica a quali porte possono essere associate applicazioni Linux che non verranno inoltrate o considerate automaticamente in Windows. Deve essere formattato in un elenco delimitato da virgole, ad esempio 3000,9000,9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Reimposta porte</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Porte ignorate</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.networkingMode è impostato su mirroring. Specifica le porte a cui le applicazioni Linux possono eseguire il binding che non verranno inoltrate o considerate automaticamente in Windows. Deve essere formattato in un elenco delimitato da virgole, ad esempio 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Timeout proxy automatico iniziale</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.autoProxy è impostato su true. Consente di configurare per quanto tempo (in millisecondi) WSL attenderà il recupero delle informazioni sul proxy HTTP all'avvio di un contenitore WSL. Se le impostazioni proxy vengono risolte dopo questo periodo di tempo, è necessario riavviare l'istanza di WSL per utilizzare le impostazioni del proxy recuperate.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Reimposta timeout</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Timeout proxy automatico iniziale</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Applicabile solo quando wsl2.autoProxy è impostato su true. Configura per quanto tempo, in millisecondi, WSL attenderà il recupero delle informazioni sul proxy HTTP all'avvio di un contenitore WSL. Se le impostazioni proxy vengono risolte dopo questo periodo di tempo, è necessario riavviare l'istanza di WSL per utilizzare le impostazioni del proxy recuperate.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Riga di comando del kernel</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Argomenti aggiuntivi della riga di comando del kernel.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Abilita l'inoltro localhost</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Valore booleano che specifica se le porte associate a caratteri jolly o localhost nella macchina virtuale WSL 2 devono essere connettibili dall'host tramite localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Abilita l'inoltro localhost.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valore booleano che specifica se le porte associate a caratteri jolly o localhost nella macchina virtuale WSL 2 devono essere connettibili dall'host tramite localhost, due punti, porta.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Memoria e processore</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Dimensione memoria</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Quantità di memoria da assegnare alla macchina virtuale WSL 2.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Reimposta dimensioni</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Dimensione memoria</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Quantità di memoria, specificata in megabyte, da assegnare alla macchina virtuale WSL 2.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} millisecondi</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Abilita virtualizzazione annidata</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Valore booleano per attivare o disattivare la virtualizzazione annidata, consentendo l'esecuzione di altre macchine virtuali annidate in WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Abilita virtualizzazione annidata.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valore booleano per attivare o disattivare la virtualizzazione annidata, consentendo l'esecuzione di altre macchine virtuali annidate in WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Modalità di rete</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Specifica la modalità di rete per WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Modalità di rete.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Specifica la modalità di rete per WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Reti</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Puoi lavorare su tutti i file tra più sistemi operativi.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Accesso ai file tra più sistemi operativi</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Accedi ai file di Windows da Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Puoi accedere ai file di Windows da Linux passando a ‘/mnt’ e quindi alla lettera di unità di Windows, come nell'esempio seguente per l'unità C:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Accedi ai file Linux con Esplora file</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Puoi visualizzare i file Linux da Esplora file passando a '\\\\wsl.localhost\\' o facendo clic sull'icona 'Linux'.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Avvia i file e i programmi di Windows da WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Anche durante l'uso di WSL, puoi eseguire i file eseguibili di Windows direttamente da bash. Prova a eseguire\n'powershell.exe /c start .' per aprire Esplora file nella cartella corrente.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Accesso alle app di rete Linux da Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Se stai creando un'app di rete (ad esempio un'app in esecuzione in un server NodeJS o SQL) nella distribuzione Linux, puoi accedervi da un'app di Windows (come il browser Internet Edge o Chrome) usando localhost (come faresti normalmente). Pertanto, se hai avviato un server Linux in ascolto della porta 3000, puoi passare a [http://localhost:3000] in Microsoft Edge in Windows per accedervi.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Rete in modalità mirroring</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL include anche una nuova modalità di rete, denominata modalità mirroring, che aggiunge funzionalità avanzate come il supporto IPv6 e la possibilità di accedere alle applicazioni di rete nella rete locale.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni sull'accesso ai file tra più sistemi operativi</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Benvenuto in Sottosistema Windows per Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL è un ottimo modo per provare diverse distribuzioni Linux.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Gestione distribuzione</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni sui comandi WSL di base</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni sull'importazione delle distribuzioni Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Elenca comando distribuzioni WSL installabili</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Installa un comando di distribuzione WSL denominato</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Elenca comando distribuzioni WSL disponibili</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop funziona perfettamente con WSL per facilitare lo sviluppo con i contenitori Linux.\n\nEcco alcuni dei vantaggi di usare Docker Desktop con WSL:\n\n• È possibile eseguire comandi Docker in WSL o in Windows usando lo stesso daemon Docker e le stesse immagini.\n• È possibile condividere file e cartelle tra Windows e Linux senza problemi, usando il montaggio automatico delle unità Windows in WSL.\n• È possibile usare gli editor e gli strumenti di Windows preferiti per lavorare su codice e file Linux e viceversa, grazie all'interoperabilità di WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Integrazione di Docker Desktop</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni sull'uso di WSL con Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Sottosistema Windows per Linux (WSL) consente di eseguire strumenti, utilità, applicazioni e flussi di lavoro Linux preferiti direttamente in Windows.\n\nPrenditi un momento per visualizzare in anteprima alcune delle funzionalità preferite della community o leggere la documentazione completa.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Ti diamo il benvenuto in WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Procedure consigliate per l'installazione</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Introduzione a Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentazione di Sottosistema Windows per Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Puoi usare applicazioni Linux basate su grafica con interazioni native di Windows come ALT-TAB, avvio del menu Start, aggiunta alla barra delle applicazioni e supporto taglia e incolla.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>App GUI</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL può sfruttare la GPU di Windows per flussi di lavoro Machine Learning \n\nI file binari Linux in esecuzione in WSL possono usare automaticamente la GPU in Windows per velocizzarne le prestazioni. È sufficiente installare ed eseguire questi flussi di lavoro come in un normale computer Linux. Esistono molti modi diversi per iniziare, tra cui eseguire CUDA in un contenitore Docker se si dispone di una scheda grafica NVIDIA, eseguire PyTorch o TensorFlow con DirectML sulla scheda grafica AMD, Intel o NVIDIA. Per altre informazioni, vedi la guida introduttiva riportata di seguito.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>Accelerazione GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni sull'accelerazione GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni sulle app WSL GUI</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Ecco un elenco di app da provare (puoi installarle tutte in Ubuntu digitando `sudo apt install &lt;Nome dell'app&gt;')\n\n    • gedit: editor di testo di base\n    • audacity: registra e modifica i file audio\n    • blender: crea animazioni e visualizzazioni 3D\n    • gimp: modifica foto\n    • nautilus: esplora i file Linux\n    • vlc: lettore video</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>È possibile accedere facilmente alle app di rete in tutti i sistemi operativi Windows e Linux.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Integrazione di rete</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni sulle applicazioni di rete</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni sulla rete in modalità mirroring</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Puoi usare WSL come ambiente di sviluppo a tempo pieno direttamente da VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>Integrazione VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni sull'uso di WSL con VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Come installare</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Dopo aver installato VS Code, è possibile installare l'estensione WSL remota da Terminale Windows:\n\n'code –install-extension ms-vscode-remote.remote-wsl`</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Apri un progetto WSL in Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Per aprire un progetto in VS Code dalla distribuzione WSL, apri la riga di comando della distribuzione ed esegui 'code .' per aprire un file di progetto.\n\nPuoi anche accedere a più opzioni remote di VS Code tramite il riquadro comandi all'interno di VS Code. Premi \"MAIUSC+CTRL+P\" sulla tastiera per aprire il riquadro comandi e digita 'Remote-WSL' per visualizzare un elenco delle opzioni remote di VS Code disponibili; ciò consente di riaprire la cartella in una sessione remota, specificare la distribuzione da aprire e altro ancora.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Modalità di utilizzo</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Funzionalità facoltative</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Informativa sulla privacy</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Conteggio processori</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Numero di processori logici da assegnare alla macchina virtuale WSL 2.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Numero di reimpostazioni</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Conteggio processori</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Numero di processori logici da assegnare alla macchina virtuale WSL 2.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Collegamenti correlati</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Note sulla versione</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Abilita modalità provvisoria</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Eseguire WSL in \"Modalità provvisoria\" che disabilita molte funzionalità e deve essere utilizzato per ripristinare distribuzioni in stato non valido.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Abilita modalità provvisoria.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Eseguire WSL in \"Modalità provvisoria\" che disabilita molte funzionalità e deve essere utilizzato per ripristinare distribuzioni in stato non valido.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Informazioni su</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Sviluppatore</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Gestione distribuzione</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Integrazione di Docker Desktop</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>File system</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Generale</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Accelerazione GPU</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>App GUI</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Avvia wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Memoria e processore</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Reti</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Integrazione di rete</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Ti diamo il benvenuto in WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Funzionalità facoltative</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Impostazioni</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>Integrazione VS Code</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Novità</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Operazioni su più file system</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problemi</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Abilita il disco rigido virtuale di tipo sparse per impostazione predefinita</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Se abilitata, qualsiasi disco rigido virtuale appena creato verrà impostato su sparse automaticamente.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Abilita il disco rigido virtuale di tipo sparse per impostazione predefinita.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Se abilitata, qualsiasi disco rigido virtuale appena creato verrà impostato su sparse automaticamente.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Scambia percorso file</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Percorso assoluto di Windows per il disco rigido virtuale di scambio.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Sfoglia file di scambio</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Scambia percorso file</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Percorso assoluto di Windows per il disco rigido virtuale di scambio.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Dimensioni scambio</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Quantità di spazio di scambio da aggiungere alla macchina virtuale WSL 2, 0 per nessun file di scambio. L'archiviazione di scambio è la RAM basata su disco usata quando la richiesta di memoria supera il limite nel dispositivo hardware.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Reimposta dimensioni</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Dimensioni scambio</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Quantità di spazio di scambio, specificata in megabyte, da aggiungere alla macchina virtuale WSL 2. 0 per nessun file di scambio. L'archiviazione di scambio è la RAM basata su disco usata quando la richiesta di memoria supera il limite nel dispositivo hardware.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Abilita VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Usare i virtiof invece del piano 9 per accedere ai file host, aumentando la velocità.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Abilita Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Abilita il montaggio del file system 9P dall'host usando il trasporto virtio.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Timeout di inattività della macchina virtuale</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Numero di millisecondi di inattività di una macchina virtuale prima dell'arresto.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Reimposta timeout</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Timeout di inattività della macchina virtuale</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Numero di millisecondi di inattività di una macchina virtuale prima dell'arresto.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Compila, esegui, esegui il debug e profila le app in esecuzione in WSL da Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Esegui ed esegui il debug delle app in WSL da Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni su WSL in Visual Studio per sviluppatori .NET</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Altre informazioni su Visual Studio e WSL per sviluppatori C++</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Puoi eseguire ed eseguire il debug delle app .NET Core e C++ multipiattaforma in Linux con facilità senza uscire Visual Studio usando il sottosistema Windows per Linux (WSL). Gli sviluppatori multipiattaforma possono usare questo metodo come approccio semplice per testare più ambienti di destinazione.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Integrazione di Visual Studio</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Integrazione di Visual Studio</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/ja-JP/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステムを使用すると、開発者は、ほとんどのコマンドライン ツール、ユーティリティ、アプリケーションを含む GNU/Linux 環境を、従来の仮想マシンやデュアル ブート セットアップのオーバーヘッドなしに、変更せずに Windows 上で直接実行できます。</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>ディスクをデタッチできませんでした: {}詳細については、WSL2 内で 'dmesg' を実行してください。\nWSL2 でディスクを強制的に停止してデタッチするには、'wsl.exe {}' を実行します。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>ディスク '{}' は既にアタッチされています。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>そのボリュームは WSL2 内で既にマウントされています。</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>その名前のディスクは既にマウントされています。ディスクのマウントを解除するか、新しい名前を選択してやり直してください。</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>指定されたマウント名に無効な '/' 文字が含まれています。無効な文字を含めずに再試行してください。</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>ディスクは '/mnt/wsl/{}' として正常にマウントされました。\n注: /etc/wsl.conf で automount.root 設定を変更した場合、場所は異なります。\nディスクのマウントを解除してデタッチするには、'wsl.exe {} {}' を実行します。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>ディスクがアタッチされましたが、マウントできませんでした: {}。\n詳細については、WSL2 内で 'dmesg' を実行してください。\nディスクをデタッチするには、'wsl.exe {} {}' を実行します。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>インストールできる有効なディストリビューションの一覧を次に示します。\n'wsl.exe {} &lt;Distro&gt;' を使用してインストールします。\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>ディストリビューション名は既に設定されています。</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>指定されたインストール場所は既に使用されています。</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>指定された名前のディストリビューションは既に存在します。--name を使用して別の名前を選択してください。</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>指定された名前のディストリビューションはありません。</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>ディスクをマウントするには、管理者アクセス権が必要です。</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>ディストリビューションのエクスポートに失敗しました。</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>このディストリビューションの Linux ファイル システム用 Windows サブシステムのワンタイム アップグレードを実行しています...\n</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>別のインスタンスが管理者特権なしで実行されているため、起動できません。管理者特権のあるインスタンスと管理者特権のないインスタンスを同時に実行することは許可されていません。\n</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>ディストリビューションのインポートに失敗しました。</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Windows オプション コンポーネントをインストールしています: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>ダウンロードしています: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>インストールしています: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>配布リストをインポート中</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} がダウンロードされました。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム が以前のインストールを再開しています...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>ディストリビューション名 '{}' が無効です。\n有効なディストリビューションの一覧を取得するには、'wsl.exe --list --online' を使用します。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステムインスタンスが終了しました。</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>無効なコマンド ライン引数: {}\nサポートされている引数の一覧を取得するには、'{} --help' を使用してください。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>コマンド ライン引数 {} には値が必要です。\nサポートされている引数の一覧を取得するには、'{} --help' を使用してください。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>サポートされていないコンソール設定です。この機能を使用するには、従来のコンソールを無効にする必要があります。\n</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} は有効な整数ではありません。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>このディストリビューションのインストール、アンインストール、または変換が進行中です。\n</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>{} を起動しています...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>別のインスタンスが管理者特権で実行されているため、起動できません。管理者特権のあるインスタンスと管理者特権のないインスタンスを同時に実行することは許可されていません。\n</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステムにインストールされているディストリビューションはありません。\nこの問題を解決するには、以下の手順に従ってディストリビューションをインストールしてください:\n\n'wsl.exe --list --online' を使用して利用可能な配布を一覧表示する\nおよび 'wsl.exe --install &lt;Distro&gt;' を使用してインストールしてください。</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>実行中のディストリビューションはありません。</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (既定値)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム ディストリビューション:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>既定のディストリビューション: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>既定のバージョン: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>登録解除。</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>ユーザーが見つかりません。</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>WSL 2 との主な違いについては、https://aka.ms/wsl2\n を参照してください</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>変換中です。これには数分かかる場合があります。</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>ディストリビューションは既に、要求されているバージョンです。</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>レガシ ディストリビューションは WSL 2 をサポートしていません。</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>このコンピューターで仮想化が有効になっていないため、WSL2 を開始できません。\n[仮想マシン プラットフォーム] オプション　コンポーネントが有効になっており、コンピューターのファームウェア設定で仮想化がオンになっていることを確認してください。\n\n次のコマンドを実行して [仮想マシン プラットフォーム] を有効にします: wsl.exe --install --no-distribution\n\n詳細については、https://aka.ms/enablevirtualization をご覧ください</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>接続されている仮想ハード ディスクまたは物理ディスクが多すぎます。</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD は wsl.exe --mount, を介して既にマウントされています。起動する前に、ディスクのマウントを解除してください。</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 は、現在のマシン構成ではサポートされていません。\nWSL1 を使用するには、\"Linux 用 Windows サブシステム\" オプション コンポーネントを有効にしてください。</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>この操作は WSL2 でのみサポートされています。</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>使用: \n    --networking-mode\n       現在のネットワーク モードを表示します。\n\n    --msal-proxy-path\n        MSAL プロキシ アプリケーションへのパスを表示します。\n\n    --vm-id\n        WSL VM ID を表示します。\n\n    --version\n        WSL パッケージのバージョンを表示します。\n\n    -n\n        改行を出力しません。</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>使用法：\n    -a\n        結果を絶対パス形式に強制します。\n    -u\n        Windows パスから WSL パスに変換します (既定)。\n    -w\n        WSL パスから Windows パスに変換します。\n    -m\n        WSL パスから Windows パスに変換します ('\\\\' ではなく '/' を使用)\n\n例: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム上で管理操作を実行します\n\n使用法:\n    /l, /list [オプション]\n        登録済みのディストリビューションを一覧表示します。\n        /all - オプションで、すべてのディストリビューションを一覧表示します。これには、\n               現在インストール中またはアンインストール中のものが含まれます。\n\n        /running - 現在実行中のディストリビューションのみを一覧表示します。\n\n    /s, /setdefault &lt;DistributionName&gt;\n        ディストリビューションを既定として設定します。\n\n    /t, /terminate &lt;DistributionName&gt;\n        ディストリビューションを終了します。\n\n    /u, /unregister &lt;DistributionName&gt;\n        ディストリビューションの登録を解除し、ルート ファイルシステムを削除します。</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation.All rights reserved.\nこの製品のプライバシーに関する情報については、https://aka.ms/privacy にアクセスしてください。\n\n使用法: wsl.exe [Argument] [Options...] [CommandLine]\n\nLinux バイナリを実行するための引数:\n\n    コマンド ラインが指定されていない場合、wsl.exe は既定のシェルを起動します。\n\n    --exec, -e &lt;CommandLine&gt;\n        既定の Linux シェルを使用せずに、指定されたコマンドを実行します。\n\n    --shell-type &lt;standard|login|none&gt;\n        指定されたコマンドを、指定されたシェルの種類で実行します。\n\n    --\n        残りのコマンド ラインをそのまま渡します。\n\nオプション:\n    --cd &lt;Directory&gt;\n        指定されたディレクトリを現在の作業ディレクトリとして設定します。\n        ~ が使用されている場合、Linux ユーザーのホーム パスが使用されます。パスが\n        / 文字で始まる場合、絶対 Linux パスとして解釈されます。\n        それ以外の場合、値は絶対 Windows パスである必要があります。\n\n    --distribution, -d &lt;DistroName&gt;\n        指定されたディストリビューションを実行します。\n\n    --distribution-id &lt;DistroGuid&gt;\n        指定されたディストリビューション ID を実行します。\n\n    --user, -u &lt;UserName&gt;\n        指定されたユーザーとして実行します。\n\n    --system\n        システム ディストリビューションのシェルを起動します。\n\nLinux 用 Windows サブシステムを管理するための引数:\n\n    --help\n        使用方法に関する情報を表示します。\n\n    --debug-shell\n        診断のために WSL2 デバッグ シェルを開きます。\n\n    --install [Distro] [Options...]\n        Linux 用 Windows サブシステム ディストリビューションをインストールします。\n        有効なディストリビューションの一覧を表示するには、'wsl.exe --list --online' を使用します。\n\n        オプション:\n            --enable-wsl1\n                WSL1 サポートを有効にします。\n\n            --fixed-vhd\n                ディストリビューションを保存するための固定サイズのディスクを作成します。\n\n            --from-file &lt;Path&gt;\n                ローカル ファイルからディストリビューションをインストールします。\n\n            --legacy\n                レガシ ディストリビューション マニフェストを使用します。\n\n            --location &lt;Location&gt;\n                ディストリビューションのインストール パスを設定します。\n\n            --name &lt;Name&gt;\n                ディストリビューションの名前を設定します。\n\n            --no-distribution\n                必要なオプション コンポーネントのみをインストールし、ディストリビューションはインストールしません。\n\n            --no-launch, -n\n                インストール後にディストリビューションを起動しません。\n\n            --version &lt;Version&gt;\n                新しいディストリビューションに使用するバージョンを指定します。\n\n            --vhd-size &lt;MemoryString&gt;\n                ディストリビューションを保存するディスクのサイズを指定します。\n\n            --web-download\n                Microsoft Store ではなく、インターネットからディストリビューションをダウンロードします。\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        ディストリビューション固有のオプションを変更します。\n\n        オプション:\n            --move &lt;Location&gt;\n                ディストリビューションを新しい場所に移動します。\n\n            --set-sparse, -s &lt;true|false&gt;\n                ディストリビューションの VHD をスパースに設定し、ディスク領域を自動的に解放できるようにします。\n\n            --set-default-user &lt;Username&gt;\n                ディストリビューションの既定のユーザーを設定します。\n\n            --resize &lt;MemoryString&gt;\n                ディストリビューションのディスクのサイズを指定したサイズに変更します。\n\n    --mount &lt;Disk&gt;\n        物理ディスクまたは仮想ディスクをすべての WSL 2 ディストリビューションにアタッチしてマウントします。\n\n        オプション:\n            --vhd\n                &lt;Disk&gt; が仮想ハード ディスクを参照することを指定します。\n\n            --bare\n                ディスクを WSL2 にアタッチしますが、マウントはしません。\n\n            --name &lt;Name&gt;\n                マウントポイントにカスタム名を使用してディスクをマウントします。\n\n            --type &lt;Type&gt;\n                ディスクをマウントするときに使用するファイルシステム。指定しない場合は既定で ext4 になります。\n\n            --options &lt;Options&gt;\n                追加のマウント オプション。\n\n            --partition &lt;Index&gt;\n                マウントするパーティションのインデックス。指定しない場合は既定でディスク全体になります。\n\n    --set-default-version &lt;Version&gt;\n        新しいディストリビューションの既定のインストール バージョンを変更します。\n\n    --shutdown\n        実行中のすべてのディストリビューションと WSL 2 の\n        軽量ユーティリティ仮想マシンを直ちに終了します。\n\n        オプション:\n            --force\n                操作が進行中であっても、WSL 2 仮想マシンを終了します。データ損失を引き起こす可能性があります。\n\n    --status\n        Linux 用 Windows サブシステムの状態を表示します。\n\n    --unmount [Disk]\n        すべての WSL2 ディストリビューションからディスクのマウントを解除してデタッチします。\n        引数を指定せずに呼び出した場合、すべてのディスクのマウントを解除してデタッチします。\n\n    --uninstall\n        このマシンから Linux 用 Windows サブシステム パッケージをアンインストールします。\n\n    --update\n        Linux 用 Windows サブシステム パッケージを更新します。\n\n        オプション:\n            --pre-release\n                利用可能な場合、プレリリース バージョンをダウンロードします。\n\n    --version, -v\n        バージョン情報を表示します。\n\nLinux 用 Windows サブシステムでディストリビューションを管理するための引数:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        ディストリビューションを tar ファイルにエクスポートします。\n        stdout の場合は、ファイル名に - を使用できます。\n\n        オプション:\n            --format &lt;Format&gt;\n                エクスポート形式を指定します。サポートされている値は、tar、tar.gz、tar.xz、vhd です。\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        指定された tar ファイルを新しいディストリビューションとしてインポートします。\n        stdin の場合は、ファイル名に - を使用できます。\n\n        オプション:\n            --version &lt;Version&gt;\n                新しいディストリビューションに使用するバージョンを指定します。\n\n            --vhd\n                指定されたファイルが tar ファイルではなく、.vhd または .vhdx ファイルであることを指定します。\n                この操作により、指定したインストール場所に VHD ファイルのコピーが作成されます。\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        指定した VHD ファイルを新しいディストリビューションとしてインポートします。\n        この仮想ハード ディスクは、ext4 ファイルシステムの種類でフォーマットする必要があります。\n\n    --list, -l [Options]\n        ディストリビューションを一覧表示します。\n\n        オプション:\n            --all\n                すべてのディストリビューションを一覧表示します。これには、\n                現在インストール中またはアンインストール中のディストリビューションが含まれます。\n\n            --running\n                現在実行中のディストリビューションのみを一覧表示します。\n\n            --quiet, -q\n                ディストリビューション名のみを表示します。\n\n            --verbose, -v\n                すべてのディストリビューションに関する詳細情報を表示します。\n\n            --online, -o\n                'wsl.exe --install' を使用してインストールできるディストリビューションの一覧を表示します。\n\n    --set-default, -s &lt;Distro&gt;\n        このディストリビューションを既定として設定します。\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        指定されたディストリビューションのバージョンを変更します。\n\n    --terminate, -t &lt;Distro&gt;\n        指定されたディストリビューションを終了します。\n\n    --unregister &lt;Distro&gt;\n        このディストリビューションの登録を解除し、ルート ファイルシステムを削除します。</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL バージョン: {}\nカーネル バージョン: {}\nWSLg バージョン: {}\nMSRDC バージョン: {}\nDirect3D バージョン: {}\nDXCore バージョン: {}\nWindows バージョン: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild バージョン: {}\nコミット: {}\nビルド時間: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>{} で指定されたカスタム カーネルが見つかりませんでした: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>{} にあるカスタム カーネル モジュール VHD が見つかりませんでした: '{}'。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>{} で指定されたカスタム システムディストリビューションが見つからなかったか、正しい形式ではありません。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation.All rights reserved.\nこの製品のプライバシー情報については、https://aka.ms/privacy をご覧ください。\n\n使用方法: wslg.exe [引数] [オプション...] [コマンドライン]\n\n引数:\n   --cd &lt;ディレクトリ&gt;\n       指定されたディレクトリを現在の作業ディレクトリとして設定します。\n       ~ が使用されている場合、Linux ユーザーのホーム パスが使用されます。パスが\n       / 文字で始まる場合、絶対 Linux パスとして解釈されます。\n       それ以外の場合、値は絶対 Windows パスである必要があります。\n\n   --distribution, -d &lt;ディストリビューション&gt;\n       指定されたディストリビューションを実行します。\n\n   --user, -u &lt;ユーザー名&gt;\n       指定されたユーザーとして実行します。\n\n   --shell-type &lt;standard|login|none&gt;\n       指定されたシェルの種類で、指定されたコマンドを実行します。\n\n   --help\n       使用方法に関する情報を表示します。\n\n   --\n       残りのコマンド ラインをそのまま渡します。</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>続行するには何かキーを押してください...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>引数 {} に必須パラメーターがありません。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>エクスポートが進行中です。これには数分かかる場合があります。</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>インポート中です。この処理には数分かかることがあります。</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>GUI アプリケーションのサポートは、{} または /etc/wsl.conf により無効化されています。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>更新プログラムを確認しています。</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>この配布は、Microsoft Store からのみ使用できます。</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>ARM64 で wsl.exe --mount するには、Windows バージョン 27653 以降が必要です。</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステムの最新バージョンは既にインストールされています。</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステムをバージョン {} に更新しています。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>このアプリケーションには、Linux 用 Windows サブシステムオプション コンポーネントが必要です。\n次を実行してインストールする: wsl.exe --install --no-distribution\n変更を有効にするには、システムの再起動が必要な場合があります。</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>指定されたファイルには {} ファイル拡張子が必要です。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>指定されたファイルには、{} または {} のファイル拡張子が必要です。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>VmSwitch '{}' が見つかりませんでした。使用可能なスイッチ: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>ブリッジ ネットワークを使用するには、wsl2.vmSwitch を設定する必要があります。</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>'{}' から配布リストをフェッチできませんでした。{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>ディスク '{}' を WSL2 にアタッチできませんでした: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>ディスクのサイズを変更できませんでした。</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>値が見つかりません。</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Windows バージョン {} は、パッケージ化されたバージョンのLinux 用 Windows サブシステムをサポートしていません。\nWindows Update または {} 経由で必要な更新プログラムをインストールします\n詳細については、https://aka.ms/wslinstall をご覧ください</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>デバッグ シェルを実行するには、wsl.exe を管理者として実行する必要があります。</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>ディストリビューション '{}' のインストール プロセスが次の終了コードで失敗しました: {}。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>GUID の形式が無効です: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>{} の無効なセクション名: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>{} の無効なキー名: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>{} の予期された {}: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>無効なエスケープ文字: {} の '{}':{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>{} の不明なキー '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>{}:{} のキー '{}' の整数値 '{}' が無効です</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>{}:{} のキー '{}' の IP 値 '{}' が無効です</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>{}:{} のキー '{}' のブール値 '{}' が無効です</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>{}:{} のキー '{}' の無効な MAC アドレス '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>構成ファイル {}、{} を開けませんでした</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>WSL の起動中にエラーが発生しました</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>'{}' のシステム ユーザー セッションを開始できませんでした。詳細については journalctl を参照してください。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>EventViewer を開く</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>このディストリビューションには既定の名前が含まれていません。--name を使用して、ディストリビューション名を選択します。</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>引数 {} と {} を同時に指定することはできません。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>引数 {} には {} 引数が必要です。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>無効なディストリビューション名: \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>レガシの配布登録を使用しています。代わりに tar ベースの配布を使用することを検討してください。</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>レガシディストリビューションの登録では、--version 引数はサポートされていません。</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>ディストリビューション \"{}\" はオーバーライド マニフェストによって提供されます。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>ディストリビューション ハッシュが一致しません。予期された値は {}、実際の値は {} です</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>無効な 16 進数文字列: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>レガシ ディストリビューションをインストールする場合、'{}' はサポートされません。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>ディストリビューションが正常にインストールされました。'wsl.exe -d {}' を使用して起動できます</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>{} の .wslconfig エントリ '{}' の無効なメモリ文字列 '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>'{}' にスワップ ディスクを作成できませんでした: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>ネスト化された仮想化はこのマシンではサポートされていません。</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>アドレス '{}'、割り当てられた新しいアドレス '{}' のネットワーク エンドポイントを作成できませんでした</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>アドレス範囲 '{}' を使用して仮想ネットワークを作成できませんでした。範囲 '{}'、{} を使用して新しいネットワークを作成しました</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>セーフ モードが有効 - 多くの機能が無効になります</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>ミラー化されたネットワーク モードはサポートされていません: {}\nNAT ネットワークにフォールバックしています。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Linux カーネル バージョン 5.10 以降が必要です</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows バージョン {}.{} には必要な機能がありません</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Hyper-V ファイアウォールはサポートされていません</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>DNS トンネリングはサポートされていません</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>ミラー化されたネットワーク モードを使用している場合、wsl2.localhostForwarding 設定は無効です</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>HTTP プロキシの変更がホストで検出されました。変更を適用するには、WSL を再起動してください。</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>ローカルホスト プロキシ構成が検出されましたが、WSL にミラーリングされませんでした。NAT モードの WSL は、ローカルホスト プロキシをサポートしません。</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>IPv6 プロキシ構成が検出されましたが、WSL にミラーリングされませんでした。NAT モードの WSL は IPv6 をサポートしません。</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>ローカルホスト IPv6 プロキシ構成が検出されましたが、WSL にミラーリングされませんでした。WSL はローカルホスト IPv6 プロキシをサポートしません。</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>プロキシ設定を解決しようとしたときに予期しないエラーが発生しました。プロキシ設定は WSL にミラーリングされませんでした。</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>レジストリへのアクセス中にエラーが発生しました。パス: '{}'エラー: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>localhost リレー プロセスを起動できませんでした。エラー: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>仮想ネットワークを開始できませんでした。次を実行して、オプションのコンポーネント Virtual Machine Platform をインストールしてください: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>ネットワーク (networkingMode {}) を構成できませんでした。networkingMode {} にフォールバックします。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Windows コンポーネント '{}' を有効にできませんでした (終了コード {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>プラグイン '{}' によって致命的なエラーが返されました</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>プラグイン '{}' によって致命的なエラーが返されました。エラー メッセージ: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>プラグイン '{}' には、新しいバージョンの WSL が必要です。次を実行してください: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>.wslconfig 設定 '{}' はコンピューター ポリシーによって無効にされています。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>デバッグ シェルはコンピューター ポリシーによって無効にされています。</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount はコンピューター ポリシーによって無効にされています。</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>WSL1 はコンピューター ポリシーによって無効にされています。</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>WSL2 にアップグレードするには、'wsl.exe --set-version {} 2' を実行してください。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL はアップグレードを終了しています...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>更新に失敗しました (終了コード: {})。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>アンインストールに失敗しました (終了コード: {})。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>ログ ファイル: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>MSIX パッケージを削除できませんでした (エラー: {})。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>MSIX パッケージをインストールできませんでした (エラー: {})。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>WSL の実行に必要なオプション コンポーネントがインストールされていません。</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>不足コンポーネントをインストールする</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>インポートされたファイルは有効な Linux ディストリビューションではありません。</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>任意のキーを押すと終了します。。。</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>新しいバージョンの Linux 用 Windows サブシステムが利用できるようになりました。</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>バージョン {} に更新するか、下のリリース ノートを表示します。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>アップデート</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>ドキュメントを表示</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>VHD が現在使用中のため、操作を完了できませんでした。WSL の使用を強制的に停止するには: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} は有効なブール値 &lt;true|false&gt; ではありません</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>スパース VHD は WSL2 のみでサポートされています。</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>ローカル システムとして WSL を実行することはサポートされていません。</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>パフォーマンスのヒント:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Windows ドライブで {} などの I/O 集中操作を使用すると、パフォーマンスが低下します。パフォーマンスを向上させるために、プロジェクト ファイルを Linux ファイル システムに移動することを検討してください。詳細については、下をクリックしてください。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>ドキュメントの表示</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>今後表示しない</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>WSL2 仮想マシンがクラッシュしました。</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>スタック トレースが次の場所に保存されました: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>ディストリビューションを開始できませんでした。エラー コード: {}、エラーステップ: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>仮想ディスクが壊れているため、ディストリビューションを開始できませんでした。</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>{}:{} の構成キー '{}' が重複しています (競合するキー: {}:{} の '{}')</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>{}:{} の構成キー '{}' の値 '{}' が無効です (有効な値: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>ディストリビューション \"{}\" の OOBE コマンドの完了を待機しています...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>ディストリビューション {} からプロパティ '{}' を読み取ることができませんでした</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>これは VHD ファイルのようです。--vhd を使用して、tar ではなく VHD をインポートします。</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Microsoft Storeから {} をインストールできませんでした: {}\nWeb ダウンロード... を試行しています</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>既定のディストリビューションが構成されていません。インストールするディストリビューションを指定してください。</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p は無効です。vsock トランスポートを使用して 9p にフォールバックします。</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>WSL インストールが壊れている可能性があります (エラー コード: {})。\nWSL を修復するには任意のキーを押すか、取り消すには CTRL-C。\nこのプロンプトは 60 秒後にタイムアウトします。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>ディストリビューションの登録中にターミナル プロファイルを解析できませんでした: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>無効なサイズ: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nエラー コード: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>無効な JSON ドキュメント。解析エラー: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors はシステム上の論理プロセッサの数を超えることはできません ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>ネットワーク モードを照会できませんでした</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>カスタマイズされたカーネルを指定せずに、カスタマイズされたカーネル モジュールが指定されました。詳細については、https://aka.ms/wslcustomkernel を参照してください。</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>グローバル セキュア アクセス クライアントとの互換性に関する現在の問題により、DNS トンネリングは無効です。</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>スパース VHD のサポートは、データの破損の可能性があるため、現在無効になっています。\nディストリビューションでスパース VHD を強制的に使用するには、次を実行してください。\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>{} をマウントできませんでした。詳細については dmesg を参照してください。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>マウント -a による /etc/fstab の処理に失敗しました。</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>ディストリビューション ディスクのマウント中にエラーが発生しました。フォールバックとして読み取り専用でマウントされました。\n次の回復手順を参照してください: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>翻訳に失敗しました '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>問題が発生しました。後でやり直してください。</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>バージョン情報</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム設定について</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft.All rights reserved.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム設定</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム設定を使用すると、開発者は GUI ベースのアプリケーションを使用して .wslconfig ファイルを管理できます。</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>オート メモリ再利用</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>アイドル状態の CPU 使用率を検出した後、キャッシュされたメモリを自動的に解放します。低速リリースの場合は gradual に設定し、キャッシュされたメモリの即時解放では dropcache に設定します。</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>オート メモリ再利用。</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>アイドル状態の CPU 使用率を検出した後、キャッシュされたメモリを自動的に解放します。低速リリースの場合は gradual に設定し、キャッシュされたメモリの即時解放では dropcache に設定します。</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>オート プロキシが有効</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>WSL で Windows の HTTP プロキシ情報を使用できるようにします。</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>オート プロキシが有効です。</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL で Windows の HTTP プロキシ情報を使用できるようにします。</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>ベスト エフォート DNS 解析を使用する</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>wsl2.dnsTunneling が true に設定されている場合にのみ適用されます。true に設定すると、Windows は DNS 要求から質問を抽出し、不明なレコードを無視して解決を試みます。</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>ベスト エフォート DNS 解析を使用します。</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>wsl2.dnsTunneling が true に設定されている場合にのみ適用されます。true に設定すると、Windows は DNS 要求から質問を抽出し、不明なレコードを無視して解決を試みます。</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>カスタム カーネル</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>カスタム Linux カーネルへの Windows の絶対パス。</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>カーネルの参照</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>カスタム カーネル</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>カスタム Linux カーネルへの Windows の絶対パス。</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>カスタム カーネル モジュール</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>カスタム Linux カーネル モジュール VHD への Windows 絶対パス。</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>カーネル モジュールを参照する</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>カスタム カーネル モジュール</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>カスタム Linux カーネル モジュール VHD への Windows 絶対パス。</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>カスタム システム ディストリビューション</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>ディストリビューションの参照</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>カスタム システム ディストリビューションとして読み込む VHD へのパスを指定します。このパスは、主に WSL の GUI アプリを強化するために使用されます。[システム ディストリビューションの詳細については、こちらを参照してください]。</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>カスタム システム ディストリビューション</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>カスタム システム ディストリビューションとして読み込む VHD へのパスを指定します。このパスは、主に WSL の GUI アプリを強化するために使用されます。</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>デバッグ コンソールを有効にする</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>WSL 2 ディストリビューション インスタンスの開始時に、dmesg の内容を表示する出力コンソール ウィンドウを有効にするブール値。</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>デバッグ コンソールを有効にします。</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 ディストリビューション インスタンスの開始時に診断メッセージの内容を表示する出力コンソール ウィンドウを有効にするブール値です。</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>既定の仮想ハード ディスクのサイズ</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>新しく作成されたディストリビューションのみの展開可能な WSL 仮想ハード ディスク (VHD) の既定の最大サイズです。</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>サイズのリセット</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>既定の仮想ハード ディスクのサイズ</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>新しく作成されたディストリビューションに対してのみ展開可能な WSL 仮想ハード ディスク (VHD) の既定の最大サイズ (MB 単位) です。</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>開発者</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>ドキュメント</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>DrvFS モードの変更</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>WSL での OS 間のファイル アクセスの実装を変更します。</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS プロキシが有効</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>wsl2.networkingMode が NAT に設定されている場合にのみ適用されます。Linux の DNS サーバーをホスト上の NAT に構成するよう WSL に通知するブール値です。false に設定すると、DNS サーバーが Windows から Linux にミラーされます。</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS プロキシが有効です。</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>wsl2.networkingMode が NAT に設定されている場合にのみ適用されます。Linux の DNS サーバーをホスト上の NAT に構成するよう WSL に通知するブール値です。false に設定すると、DNS サーバーが Windows から Linux にミラーされます。</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>DNS トンネリングが有効</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>DNS 要求を WSL から Windows にプロキシする方法を変更します。</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS トンネリングが有効です。</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>DNS 要求を WSL から Windows にプロキシする方法を変更します。</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>ファイル システム</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>GUI アプリケーションを有効にする</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>WSL での GUI アプリケーション ([WSLg]) のサポートを有効または無効にするブール値。</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>GUI アプリケーションを有効にします。</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL での GUI アプリケーション (WSL g と呼ばれる) のサポートを有効または無効にするブール値。</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>ハードウェア パフォーマンス カウンターを有効にする</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Linux のハードウェア パフォーマンス カウンターを有効にします。</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>ハードウェア パフォーマンス カウンターを有効にします。</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Linux のハードウェア パフォーマンス カウンターを有効にします。</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Hyper-V ファイアウォールが有効</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Windows ファイアウォール規則および Hyper-V トラフィックに固有の規則を許可して WSL ネットワーク トラフィックをフィルター処理できるようにする Hyper-V ファイアウォールを有効にします。</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hyper-V ファイアウォールが有効です。</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Windows ファイアウォール規則および Hyper-V トラフィックに固有の規則を許可して WSL ネットワーク トラフィックをフィルター処理できるようにする Hyper-V ファイアウォールを有効にします。</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>ホスト アドレスのループバック</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>wsl2.networkingMode が mirrored に設定されている場合にのみ適用されます。True に設定すると、ホストに割り当てられている IP アドレスによって、コンテナーがホストに接続するか、ホストがコンテナーに接続できるようになります。127.0.0.1 ループバック アドレスは常に使用できることに注意してください。このオプションでは、追加で割り当てられたすべてのローカル IP アドレスも使用できます。</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>ホスト アドレスのループバック。</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>wsl2.networkingMode が mirrored に設定されている場合にのみ適用されます。True に設定すると、ホストに割り当てられている IP アドレスによって、コンテナーがホストに接続するか、ホストがコンテナーに接続できるようになります。127.0.0.1 ループバック アドレスは常に使用できることに注意してください。このオプションでは、追加で割り当てられたすべてのローカル IP アドレスも使用できます。</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>無視されたポート</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>wsl2.networkingMode がミラー化に設定されている場合にのみ適用できます。Windows で自動的に転送または考慮されない Linux アプリケーションがバインドできるポートを指定します。コンマ区切りの一覧で書式設定する必要があります (例: 3000,9000,9090)。</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>ポートのリセット</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>無視されたポート</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>wsl2.networkingMode がミラー化に設定されている場合にのみ適用されます。Windows で自動的に転送または考慮されない Linux アプリケーションがバインドできるポートを指定します。3000、9000、9090 などのコンマ区切りの一覧で書式設定する必要があります。</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>初期のオートプロキシ タイムアウト</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>wsl2.autoProxy が true に設定されている場合にのみ適用されます。WSL コンテナーの開始時に WSL が HTTP プロキシ情報の取得を待機する時間 (ミリ秒) を構成します。この時間が経過した後にプロキシ設定が解決された場合、取得したプロキシ設定を使用するには、WSL インスタンスを再起動する必要があります。</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>タイムアウトのリセット</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>初期のオートプロキシ タイムアウト</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>wsl2.autoProxy が true に設定されている場合にのみ適用されます。WSL コンテナーの開始時に WSL が HTTP プロキシ情報の取得を待機する時間をミリ秒単位で構成します。この時間が経過した後にプロキシ設定が解決された場合、取得したプロキシ設定を使用するには、WSL インスタンスを再起動する必要があります。</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>カーネル コマンド ライン</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>追加のカーネル コマンド ライン引数。</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>localhost 転送を有効にする</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM のワイルドカードまたは localhost にバインドされたポートを localhost:port 経由でホストから接続できるかどうかを指定するブール値です。</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>localhost 転送を有効にします。</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM のワイルドカードまたは localhost にバインドされたポートを localhost、colon、port 経由でホストから接続できるかどうかを指定するブール値。</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>メモリとプロセッサ</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>メモリ サイズ</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM に割り当てるメモリの量。</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>サイズのリセット</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>メモリ サイズ</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM に割り当てるメモリの量 (MB 単位)。</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} ミリ秒</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>入れ子になった仮想化を有効にする</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>入れ子になった仮想化を有効または無効にするブール値を使用すると、入れ子になった他の VM を WSL 2 内で実行できるようにします。</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>入れ子になった仮想化を有効にします。</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>入れ子になった仮想化を有効または無効にするブール値を使用すると、入れ子になった他の VM を WSL 2 内で実行できるようにします。</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>ネットワーク モード</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>WSL のネットワーク モードを指定します。</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>ネットワーク モード。</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL のネットワーク モードを指定します。</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>ネットワーク</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>すべてのファイルを使って、異なるオペレーティング システム間で作業できます。</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>クロス OS ファイル アクセス</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Linux から Windows ファイルにアクセスする</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Linux 内から Windows ファイルにアクセスするには、'/mnt' に移動し、次に示す C ドライブの例のように Windows ドライブ文字に移動します: \n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>エクスプローラーを使用して Linux ファイルにアクセスする</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>'\\\\wsl.localhost\\' に移動するか、'Linux' アイコンをクリックすると、エクスプローラーから Linux ファイルを表示できます。</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>WSL から Windows ファイルとプログラムを起動する</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>WSL を使用している間でも、Bash から直接 Windows 実行可能ファイルを実行できます。以下を試してみましょう: \n'powershell.exe /c start .' を実行すると、現在のフォルダーでエクスプローラーを開きます。</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Windows から Linux ネットワーク アプリへのアクセス</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Linux ディストリビューションでネットワーク アプリ (NodeJS や SQL Server で実行されているアプリなど) を構築する場合は、localhost (通常と同様) を使用して Windows アプリ (Edge や Chrome インターネット ブラウザーなど) からアクセスできます。これは、ポート 3000 をリッスンしている Linux サーバーを起動した場合、Windows 上の Edge で [http://localhost:3000] にアクセスできることを意味します。</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>ミラー モード ネットワーク</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL には、ミラー モードと呼ばれる新しいネットワーク モードも含まれており、IPv6 サポートやローカル エリア ネットワーク内のネットワーク アプリケーションにアクセスする機能などの高度な機能が追加されています。</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>クロス OS ファイル アクセスに関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステムにようこそ</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL は、さまざまな Linux ディストリビューションを試す優れた方法です。</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>ディストリビューションの管理</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>基本的な WSL コマンドに関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Linux ディストリビューションのインポートに関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>インストール可能な WSL Distros コマンドの一覧表示</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>名前付き WSL Distro コマンドのインストール</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>使用可能な WSL Distros コマンドの一覧表示</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker デスクトップは WSL と連携して、Linux コンテナーでの開発に役立ちます。\n\nWSL で Docker デスクトップを使用するいくつかの利点を以下に示します: \n\n• 同じ Docker デーモンとイメージを使用して、WSL または Windows で Docker コマンドを実行できます。\n• WSL での Windows ドライブの自動マウントを使用して、Windows と Linux の間でファイルとフォルダーをシームレスに共有できます。\n• WSL の相互運用性のおかげで、好みの Windows ツールとエディターを使用して Linux のコードとファイルを操作できます。また、その逆も可能です。</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker Desktop の統合</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Docker での WSL の使用に関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム (WSL) を使用すると、お気に入りの Linux ツール、ユーティリティ、アプリケーション、ワークフローを Windows で直接実行できます。\n\n少しの時間を取って、コミュニティのお気に入りの機能をいくつかプレビューしたり、包括的なドキュメントを表示したりしてみてください。</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>WSL へようこそ</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>セットアップのベスト プラクティス</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Linux を使い始める</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム (WSL) ドキュメント</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>グラフィカル ベースの Linux アプリケーションは、alt-tab、スタート メニューの起動、タスク バーのピン留め、切り取り/貼り付けのサポートなど、Windows のネイティブな操作で使用できます。</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>GUI アプリ</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL では、Machine Learning ワークフローに Windows GPU を活用できます\n\nWSL で実行されている Linux バイナリは、Windows で GPU を自動的に使用してパフォーマンスを向上させることができます。通常の Linux マシンの場合と同じ方法で、これらのワークフローをインストールして実行する必要があります。NVIDIA グラフィックス カードがある場合は Docker コンテナーで CUDA を実行するという方法から、AMD、Intel、または NVIDIA グラフィックス カードで DirectML を使用して PyTorch または TensorFlow を実行するという方法まで、さまざまな方法で開始できます。詳細については、以下の作業の開始ガイドをご覧ください。</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>GPU アクセラレータ</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>GPU アクセラレーションに関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>WSL GUI アプリに関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>ここでは、試しているアプリの一覧を示します ('sudo apt install &lt;アプリ名&gt;' と入力すると、Ubuntu でこれらすべてをインストールできます)\n\n    • gedit – 基本テキスト エディター\n    • audacity – オーディオ ファイルの録音と編集\n    • blender – 3D アニメーションと視覚エフェクトの作成\n    • gimp – 写真の編集\n    • nautilus – Linux ファイル エクスプローラー\n    • vlc – ビデオ プレーヤー</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Windows と Linux のオペレーティング システム間でネットワーク アプリに簡単にアクセスできます。</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>ネットワーク統合</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>ネットワーク アプリケーションに関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>ミラー モード ネットワークに関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>WSL は、VS Code から直接、フルタイムの開発環境として使用できます。</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS Code の統合</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>VS Code での WSL の使用に関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>インストールの方法</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>VS Code をインストールしたら、Windows ターミナルからリモート WSL 拡張機能をインストールできます: \n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Visual Studio Code で WSL プロジェクトを開く</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>WSL ディストリビューションから VS Code でプロジェクトを開くには、ディストリビューションのコマンド ラインを開き、'code .' を実行してプロジェクト ファイルを開きます。\n\nVS Code 自体のコマンド パレットから、より多くの VS Code リモート オプションにアクセスすることもできます。キーボードで “SHIFT + Ctrl + P” キーを押してコマンド パレットを開き、‘Remote-WSL’ と入力すると、使用可能な VS Code リモート オプションの一覧が表示されます。これにより、リモート セッションでフォルダーを再度開いたり、開くディストリビューションを指定したりできます。</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>使用方法</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>オプション機能</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>プライバシーに関する声明</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>プロセッサ数</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM に割り当てる論理プロセッサの数。</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>カウントのリセット</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>プロセッサ数</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM に割り当てる論理プロセッサの数。</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>関連するリンク</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>リリース ノート</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>セーフ モードを有効にする</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>WSL を \"セーフ モード\" で実行すると、多くの機能が無効になり、無効な状態のディストリビューションの回復に使用されます。</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>セーフ モードを有効にします。</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL を \"セーフ モード\" で実行すると、多くの機能が無効になり、無効な状態のディストリビューションの回復に使用されます。</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>バージョン情報</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>開発者</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>ディストリビューションの管理</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Docker Desktop の統合</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>ファイル システム</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>全般</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>GPU アクセラレータ</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>GUI アプリ</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>wsl.exe を起動する</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>メモリとプロセッサ</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>ネットワーク</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>ネットワーク統合</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>WSL へようこそ</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>オプション機能</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>設定</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS Code の統合</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>新着情報</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>ファイル システム間での作業</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>問題が生じています</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>スパース仮想ハード ディスクを既定で有効にする</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>新しく作成された VHD は、有効にすると自動的にスパースに設定されます。</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>スパース仮想ハード ディスクを既定で有効にします。</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>新しく作成された VHD は、有効にすると自動的にスパースに設定されます。</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>ページ ファイルの場所</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>スワップ仮想ハード ディスクへの Windows の絶対パス。</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>スワップ ファイルの参照</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>ページ ファイルの場所</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>スワップ仮想ハード ディスクへの Windows の絶対パス。</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>スワップ サイズ</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM に追加するスワップ領域の量 (スワップ ファイルがない場合は 0)。スワップ ストレージは、メモリの需要がハードウェア デバイスの制限を超えたときに使用されるディスク ベースの RAM です。</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>サイズのリセット</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>スワップ サイズ</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM に追加するスワップ領域のサイズ (MB 単位)。スワップ ファイルがない場合は 0。スワップ ストレージは、メモリ要求がハードウェア デバイスの制限を超えたときに使用されるディスク ベースの RAM です。</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>VirtIO を有効にする</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>プラン 9 ではなく virtiofs を使用してホスト ファイルにアクセスすると、速度が向上します。</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Virtio 9p を有効にする</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>virtio トランスポートを使用して、ホストから 9P ファイルシステムへのマウントを有効にします。</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>VM アイドル タイムアウト</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>VM がシャットダウンされるまでのアイドル時間 (ミリ秒) です。</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>タイムアウトのリセット</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>VM アイドル タイムアウト</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>VM がシャットダウンされるまでのアイドル時間 (ミリ秒) です。</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Visual Studio から WSL 上で動作するアプリをビルド、実行、デバッグ、プロファイル</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Visual Studio から WSL 上でアプリを実行およびデバッグします</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>.NET 開発者向けの Visual Studio での WSL に関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>C++ 開発者向けの Visual Studio と WSL に関する詳細情報</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Linux 用 Windows サブシステム (WSL) を使うと、Visual Studio を離れることなく、.NET Core アプリやクロスプラットフォームの C++ アプリを Linux 上で簡単に実行しデバッグできます。クロスプラットフォーム開発者の場合は、より多くのターゲット環境をテストする簡単な方法として、これを使用できます。</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studio 統合</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studio 統合</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/ko-KR/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<root>\r\n  <!--\r\n    Microsoft ResX Schema\r\n\r\n    Version 2.0\r\n\r\n    The primary goals of this format is to allow a simple XML format\r\n    that is mostly human readable. The generation and parsing of the\r\n    various data types are done through the TypeConverter classes\r\n    associated with the data types.\r\n\r\n    Example:\r\n\r\n    ... ado.net/XML headers & schema ...\r\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\r\n    <resheader name=\"version\">2.0</resheader>\r\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\r\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\r\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\r\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\r\n    </data>\r\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\r\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r\n        <comment>This is a comment</comment>\r\n    </data>\r\n\r\n    There are any number of \"resheader\" rows that contain simple\r\n    name/value pairs.\r\n\r\n    Each data row contains a name, and value. The row also contains a\r\n    type or mimetype. Type corresponds to a .NET class that support\r\n    text/value conversion through the TypeConverter architecture.\r\n    Classes that don't support this are serialized and stored with the\r\n    mimetype set.\r\n\r\n    The mimetype is used for serialized objects, and tells the\r\n    ResXResourceReader how to depersist the object. This is currently not\r\n    extensible. For a given mimetype the value must be set accordingly:\r\n\r\n    Note - application/x-microsoft.net.object.binary.base64 is the format\r\n    that the ResXResourceWriter will generate, however the reader can\r\n    read any of the formats listed below.\r\n\r\n    mimetype: application/x-microsoft.net.object.binary.base64\r\n    value   : The object must be serialized with\r\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.soap.base64\r\n    value   : The object must be serialized with\r\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.bytearray.base64\r\n    value   : The object must be serialized into a byte array\r\n            : using a System.ComponentModel.TypeConverter\r\n            : and then encoded with base64 encoding.\r\n    -->\r\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\r\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\r\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\r\n      <xsd:complexType>\r\n        <xsd:choice maxOccurs=\"unbounded\">\r\n          <xsd:element name=\"metadata\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"assembly\">\r\n            <xsd:complexType>\r\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"data\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"resheader\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n        </xsd:choice>\r\n      </xsd:complexType>\r\n    </xsd:element>\r\n  </xsd:schema>\r\n  <resheader name=\"resmimetype\">\r\n    <value>text/microsoft-resx</value>\r\n  </resheader>\r\n  <resheader name=\"version\">\r\n    <value>2.0</value>\r\n  </resheader>\r\n  <resheader name=\"reader\">\r\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <resheader name=\"writer\">\r\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <data name=\"AppName\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템</value>\r\n  </data>\r\n  <data name=\"AppShortName\" xml:space=\"preserve\">\r\n    <value>WSL</value>\r\n  </data>\r\n  <data name=\"AppDescription\" xml:space=\"preserve\">\r\n    <value>Windows Subsystem for Linux를 사용하면 개발자가 -- 대부분의 명령줄 도구, 유틸리티 및 응용 프로그램을 포함한 -- GNU/Linux 환경을 수정하지 않고 기존 가상 머신이나 이중 부팅 설정의 오버헤드 없이 Windows에서 직접 실행할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\r\n    <value>디스크를 분리하지 못함: {}. 자세한 내용은 WSL2 내에서 'dmesg'를 실행하세요.\r\nWSL2가 강제로 디스크를 중지하고 분리하려면 'wsl.exe {}'을(를) 실행합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\r\n    <value>디스크 '{}'이(가) 이미 연결되어 있습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\r\n    <value>해당 볼륨은 이미 WSL2 내부에 탑재되어 있습니다.</value>\r\n  </data>\r\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\r\n    <value>해당 이름의 디스크가 이미 탑재되어 있습니다. 디스크를 분리하거나 새 이름을 선택하고 다시 시도하십시오.</value>\r\n  </data>\r\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\r\n    <value>지정한 탑재 이름에 잘못된 '/' 문자가 있습니다. 잘못된 문자 없이 다시 시도하세요.</value>\r\n  </data>\r\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\r\n    <value>디스크가 '/mnt/wsl/{}'(으)로 탑재되었습니다.\r\n참고: /etc/wsl.conf에서 automount.root 설정을 수정한 경우 위치가 달라집니다.\r\n디스크를 마운트 해제하고 분리하려면 'wsl.exe {} {}'을(를) 실행합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\r\n    <value>디스크가 연결되었지만 탑재하지 못함: {}.\r\n자세한 내용은 WSL2 내에서 'dmesg'를 실행합니다.\r\n디스크를 분리하려면 'wsl.exe {} {}'을(를) 실행합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\r\n    <value>다음은 설치할 수 있는 유효한 배포 목록입니다.\r\n'wsl.exe {} &lt;Distro&gt;'을 사용하여 설치합니다.\r\n</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\r\n    <value>배포 이름이 이미 설정되어 있습니다.</value>\r\n  </data>\r\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\r\n    <value>제공된 설치 위치가 이미 사용 중입니다.</value>\r\n  </data>\r\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\r\n    <value>제공된 이름의 배포가 이미 있습니다. --name 사용하여 다른 이름을 선택하십시오.</value>\r\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\r\n    <value>제공된 이름의 배포가 없습니다.</value>\r\n  </data>\r\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\r\n    <value>디스크를 탑재하려면 관리자 액세스 권한이 필요합니다.</value>\r\n  </data>\r\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\r\n    <value>배포를 내보내지 못했습니다.</value>\r\n  </data>\r\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\r\n    <value>이 배포에 대한 Linux용 Windows 하위 시스템 파일 시스템의 일회성 업그레이드를 수행하는 중...\r\n</value>\r\n  </data>\r\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\r\n    <value>또 다른 인스턴스가 관리자 권한 없이 실행 중이므로 시작할 수 없습니다. 관리자 권한으로 실행하는 인스턴스는 관리자 권한 없이 실행 중인 인스턴스와 동시에 실행할 수 없습니다.\r\n</value>\r\n  </data>\r\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\r\n    <value>배포를 가져오지 못했습니다.</value>\r\n  </data>\r\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\r\n    <value>Windows 선택적 구성 요소 설치 중: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\r\n    <value>다운로드 중: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\r\n    <value>설치 중: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\r\n    <value>배포 가져오기</value>\r\n  </data>\r\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\r\n    <value>{}이(가) 다운로드되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 이전 설치... 다시 시작하는 중입니다.</value>\r\n  </data>\r\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\r\n    <value>배포 이름 '{}'이(가) 잘못되었습니다.\r\n유효한 배포 목록을 보려면 'wsl.exe --list --online' 사용하세요.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 인스턴스가 종료되었습니다.</value>\r\n  </data>\r\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\r\n    <value>잘못된 명령줄 인수: {}\r\n지원되는 인수 목록을 가져오려면 '{} --help' 사용하세요.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\r\n    <value>명령줄 인수 {}에는 값이 필요합니다.\r\n지원되는 인수 목록을 가져오려면 '{} --help' 사용하세요.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\r\n    <value>지원되지 않는 콘솔 설정입니다. 이 기능을 사용하려면, 레거시 콘솔을 사용하지 않도록 설정해야만 합니다.\r\n</value>\r\n  </data>\r\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\r\n    <value>{}(은)는 올바른 정수가 아닙니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\r\n    <value>이 배포에 대한 설치, 제거 또는 변환이 진행 중입니다.\r\n</value>\r\n  </data>\r\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\r\n    <value>{} 시작하는 중...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\r\n    <value>또 다른 인스턴스가 관리자 권한으로 실행 중이므로 시작할 수 없습니다. 관리자 권한으로 실행하는 인스턴스는 관리자 권한 없이 실행하는 인스턴스와 동시에 실행할 수 없습니다.\r\n</value>\r\n  </data>\r\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 설치된 배포가 없습니다.\r\n아래 지침에 따라 배포를 설치하여 이 resolve 수 있습니다.\r\n\r\n사용 가능한 배포를 나열하려면 'wsl.exe --list --online' 사용\r\n설치하려면 'wsl.exe --install &lt;Distro&gt;'를 선택하세요.</value>\r\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\r\n    <value>실행 중인 배포가 없습니다.</value>\r\n  </data>\r\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\r\n    <value>{}(기본값)</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 배포:</value>\r\n  </data>\r\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\r\n    <value>기본 배포: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\r\n    <value>기본 버전: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\r\n    <value>등록 취소 중입니다.</value>\r\n  </data>\r\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\r\n    <value>사용자를 찾을 수 없습니다.</value>\r\n  </data>\r\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\r\n    <value>WSL 2와의 주요 차이점에 대한 자세한 내용은 https://aka.ms/wsl2를 참조하세요\r\n</value>\r\n  </data>\r\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\r\n    <value>변환이 진행 중입니다. 이 작업은 몇 분 정도 걸릴 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\r\n    <value>배포는 이미 요청된 버전입니다.</value>\r\n  </data>\r\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\r\n    <value>레거시 배포에서 WSL 2를 지원하지 않습니다.</value>\r\n  </data>\r\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\r\n    <value>가상화가 활성화되지 않은 이 컴퓨터에서는 WSL2를 시작할 수 없습니다.\r\n컴퓨터의 펌웨어 설정에서 ‘가상 머신 플랫폼’ 선택적 구성 요소가 활성화되고, 가상화가 켜져 있는지 확인하세요.\r\n\r\n‘가상 머신 플랫폼’을 활성화기 위해 다음 명령 실행: wsl.exe --install --no-distribution\r\n\r\n자세한 내용을 알아보려면 https://aka.ms/enablevirtualization을 방문하세요.</value>\r\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\r\n    <value>너무 많은 가상 하드 디스크 또는 실제 디스크가 연결되어 있습니다.</value>\r\n  </data>\r\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\r\n    <value>VHD가 이미 wsl.exe --mount, 를 통해 마운트되었습니다. 시작하기 전에 디스크를 마운트 해제하세요.</value>\r\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\r\n    <value>현재 컴퓨터 구성에서는 WSL1이 지원되지 않습니다.\r\nWSL1을 사용하려면 \"Windows Subsystem for Linux” 선택적 구성 요소를 사용하세요.</value>\r\n  </data>\r\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\r\n    <value>이 작업은 WSL2에서만 지원됩니다.</value>\r\n  </data>\r\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\r\n    <value>사용량: \r\n    --networking-mode\r\n       현재 네트워킹 모드를 표시합니다.\r\n\r\n    --msal-proxy-path\r\n        MSAL 프록시 애플리케이션의 경로를 표시합니다.\r\n\r\n    --vm-id\r\n        WSL VM ID를 표시합니다.\r\n\r\n    --version\r\n        WSL 패키지의 버전을 표시합니다.\r\n\r\n    -n\r\n        줄 바꿈하지 마세요.</value>\r\n    <comment>{Locked=\"--networking-mode\r\n\"}{Locked=\"--msal-proxy-path\r\n\"}{Locked=\"--vm-id\r\n\"}{Locked=\"--version\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\r\n    <value>사용법:\r\n    -a\r\n        결과를 절대 경로 형식으로 강제 적용합니다.\r\n    -u\r\n        Windows 경로에서 WSL 경로로 변환합니다(기본값).\r\n    -w\r\n        WSL 경로에서 Windows 경로로 변환합니다.\r\n    -m\r\n        WSL 경로에서 '\\\\' 대신 '/'를 사용하여 Windows 경로로 변환\r\n\r\n예: wslpath 'c:\\\\users'</value>\r\n    <comment>{Locked=\"-a\r\n\"}{Locked=\"-u\r\n\"}{Locked=\"-w\r\n\"}{Locked=\"-m\r\n\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 관리 작업을 수행합니다.\r\n\r\n사용 현황:\r\n    /l, /list [Option]\r\n        등록된 배포를 나열합니다.\r\n        /all - 필요에 따라 현재 설치 또는 제거 중인 배포를 포함하여\r\n               모든 배포를 나열합니다.\r\n\r\n        /running - 현재 실행 중인 배포만 나열합니다.\r\n\r\n    /s, /setdefault &lt;DistributionName&gt;\r\n        배포를 기본값으로 설정합니다.\r\n\r\n    /t, /terminate &lt;DistributionName&gt;\r\n        배포를 종료합니다.\r\n\r\n    /u, /unregister &lt;DistributionName&gt;\r\n        배포를 등록 취소하고 루트 파일 시스템을 삭제합니다.</value>\r\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\r\n    <value>Copyright (c) Microsoft Corporation. All rights reserved.\r\n이 제품의 개인 정보 보호에 관한 정보는 https://aka.ms/privacy에서 확인하세요.\r\n\r\n사용법: wsl.exe [Argument] [Options...] [CommandLine]\r\n\r\nLinux 이진 파일을 실행하기 위한 인수:\r\n\r\n    명령줄이 제공되지 않으면 wsl.exe는 기본 셸을 시작합니다.\r\n\r\n    --exec, -e &lt;CommandLine&gt;\r\n        기본 Linux 셸을 사용하지 않고 지정된 명령을 실행합니다.\r\n\r\n    --shell-type &lt;standard|login|none&gt;\r\n        제공된 셸 형식으로 지정된 명령을 실행합니다.\r\n\r\n    --\r\n        나머지 명령줄을 있는 그대로 전달합니다.\r\n\r\n옵션:\r\n    --cd &lt;Directory&gt;\r\n        지정된 디렉터리를 현재 작업 디렉터리로 설정합니다.\r\n        ~가 사용되는 경우 Linux 사용자의 홈 경로가 사용됩니다. 경로가\r\n        / 문자로 시작하면 절대 Linux 경로로 해석됩니다.\r\n        그렇지 않으면 값이 절대 Windows 경로여야 합니다.\r\n\r\n    --distribution, -d &lt;DistroName&gt;\r\n        지정된 배포를 실행합니다.\r\n\r\n    --distribution-id &lt;DistroGuid&gt;\r\n        지정된 배포 ID를 실행합니다.\r\n\r\n    --user, -u &lt;UserName&gt;\r\n        지정된 사용자로 실행합니다.\r\n\r\n    --system\r\n        시스템 배포에 대한 셸을 시작합니다.\r\n\r\nLinux용 Windows 하위 시스템을 관리하기 위한 인수:\r\n\r\n    --help\r\n        사용 정보를 표시합니다.\r\n\r\n    --debug-shell\r\n        진단을 위해 WSL2 디버그 셸을 엽니다.\r\n\r\n    --install [Distro] [Options...]\r\n        Linux 배포용 Windows 하위 시스템을 설치합니다.\r\n        유효한 배포 목록의 경우 'wsl.exe --list --online'을 사용합니다.\r\n\r\n        옵션:\r\n            --enable-wsl1\r\n                WSL1 지원을 사용하도록 설정합니다.\r\n\r\n            --fixed-vhd\r\n                배포를 저장할 고정 크기 디스크를 만듭니다.\r\n\r\n            --from-file &lt;Path&gt;\r\n                로컬 파일에서 배포를 설치합니다.\r\n\r\n            --legacy\r\n                레거시 배포 매니페스트를 사용합니다.\r\n\r\n            --location &lt;Location&gt;\r\n                배포에 대한 설치 경로를 설정합니다.\r\n\r\n            --name &lt;Name&gt;\r\n                배포의 이름을 설정합니다.\r\n\r\n            --no-distribution\r\n                필요한 선택적 구성 요소만 설치하고 배포를 설치하지 않습니다.\r\n\r\n            --no-launch, -n\r\n                설치 후 배포를 시작하지 마세요.\r\n\r\n            --version &lt;Version&gt;\r\n                새 배포에 사용할 버전을 지정합니다.\r\n\r\n            --vhd-size &lt;MemoryString&gt;\r\n                배포를 저장할 디스크의 크기를 지정합니다.\r\n\r\n            --web-download\r\n                Microsoft Store 대신 인터넷에서 배포를 다운로드합니다.\r\n\r\n    --manage &lt;Distro&gt; &lt;Options...&gt;\r\n        배포판 관련 옵션을 변경합니다.\r\n\r\n        옵션:\r\n            --move &lt;Location&gt;\r\n                배포를 새 위치로 이동합니다.\r\n\r\n            --set-sparse, -s &lt;true|false&gt;\r\n                배포판의 VHD를 스파스로 설정하여 디스크 공간을 자동으로 회수할 수 있도록 합니다.\r\n\r\n            --set-default-user &lt;Username&gt;\r\n                배포의 기본 사용자를 설정합니다.\r\n\r\n            --resize &lt;MemoryString&gt;\r\n                배포 디스크의 크기를 지정된 크기로 조정합니다.\r\n\r\n    --mount &lt;Disk&gt;\r\n        모든 WSL 2 배포에서 실제 또는 가상 디스크를 연결하고 탑재합니다.\r\n\r\n        옵션:\r\n            --vhd\r\n                &lt;디스크&gt;가 가상 하드 디스크를 참조하도록 지정합니다.\r\n\r\n            --bare\r\n                디스크를 WSL2에 연결하고 탑재하지는 마세요.\r\n\r\n            --name &lt;Name&gt;\r\n                탑재 지점의 사용자 지정 이름을 사용하여 디스크를 탑재합니다.\r\n\r\n            --type &lt;Type&gt;\r\n                디스크를 탑재할 때 사용할 파일 시스템이 지정되지 않은 경우 기본적으로 ext4입니다.\r\n\r\n            --options &lt;Options&gt;\r\n                추가 탑재 옵션입니다.\r\n\r\n            --partition &lt;Index&gt;\r\n                탑재할 파티션의 인덱스가 지정되지 않은 경우 기본값은 전체 디스크입니다.\r\n\r\n    --set-default-version &lt;Version&gt;\r\n        새 배포에 대한 기본 설치 버전을 변경합니다.\r\n\r\n    --shutdown\r\n        실행 중인 모든 배포와 WSL 2을 즉시 종료합니다.\r\n        경량 유틸리티 가상 머신입니다.\r\n\r\n        옵션:\r\n            --force\r\n                작업이 진행 중인 경우에도 WSL 2 가상 머신을 종료합니다. 데이터 손실을 초래할 수 있습니다.\r\n\r\n    --status\r\n        Linux용 Windows 하위 시스템 상태를 표시합니다.\r\n\r\n    --unmount [Disk]\r\n        모든 WSL2 배포에서 디스크를 해제하고 분리합니다.\r\n        인수 없이 호출되는 경우 모든 디스크를 해제하고 분리합니다.\r\n\r\n    --uninstall\r\n        이 컴퓨터에서 Linux용 Windows 하위 시스템 패키지를 제거합니다.\r\n\r\n    --update\r\n        Linux용 Windows 하위 시스템 패키지를 업데이트합니다.\r\n\r\n        옵션:\r\n            --pre-release\r\n                사용 가능한 경우 시험판 버전을 다운로드합니다.\r\n\r\n    --version, -v\r\n        버전 정보를 표시합니다.\r\n\r\nLinux용 Windows 하위 시스템 배포를 관리하기 위한 인수:\r\n\r\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\r\n        배포를 tar 파일로 내보냅니다.\r\n        파일 이름은 - for stdout 일 수 있습니다.\r\n\r\n        옵션:\r\n            --format &lt;Format&gt;\r\n                내보내기 형식을 지정합니다. 지원되는 값: tar, tar.gz, tar.xz, vhd.\r\n\r\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\r\n        지정된 tar 파일을 새 배포로 가져옵니다.\r\n        파일 이름은 - for stdin 일 수 있습니다.\r\n\r\n        옵션:\r\n            --version &lt;Version&gt;\r\n                새 배포에 사용할 버전을 지정합니다.\r\n\r\n            --vhd\r\n                제공된 파일이 tar 파일이 아닌 .vhd 또는 .vhdx 파일임을 지정합니다.\r\n                이 작업은 지정된 설치 위치에 VHD 파일의 복사본을 만듭니다.\r\n\r\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\r\n        지정된 VHD 파일을 새 배포판으로 가져옵니다.\r\n        이 가상 하드 디스크는 ext4 파일 시스템 형식으로 포맷해야 합니다.\r\n\r\n    --list, -l [Options]\r\n        배포를 나열합니다.\r\n\r\n        옵션:\r\n            --all\r\n                \r배포를 포함하여 모든 배포를 나열합니다.\n                현재 설치 중이거나 제거되고 있습니다.\r\n\r\n            --running\r\n                현재 실행 중인 배포만 나열합니다.\r\n\r\n            --quiet, -q\r\n                배포 이름만 표시합니다.\r\n\r\n            --verbose, -v\r\n                모든 배포에 대한 자세한 정보를 표시합니다.\r\n\r\n            --online, -o\r\n                'wsl.exe --install'을 사용하여 설치에 사용할 수 있는 배포 목록을 표시합니다.\r\n\r\n    --set-default, -s &lt;Distro&gt;\r\n        배포를 기본값으로 설정합니다.\r\n\r\n    --set-version &lt;Distro&gt; &lt;Version&gt;\r\n        지정된 배포의 버전을 변경합니다.\r\n\r\n    --terminate, -t &lt;Distro&gt;\r\n        지정된 배포를 종료합니다.\r\n\r\n    --unregister &lt;Distro&gt;\r\n        배포를 등록 취소하고 루트 파일 시스템을 삭제합니다.</value>\r\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\r\n\"}{Locked=\"--help\r\n\"}{Locked=\"--debug-shell\r\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\r\n\"}{Locked=\"--fixed-vhd\r\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\r\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\r\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\r\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\r\n\"}{Locked=\"--bare\r\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\r\n\"}{Locked=\"--force\r\n\"}{Locked=\"--status\r\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\r\n\"}{Locked=\"--update\r\n\"}{Locked=\"--pre-release\r\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\r\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\r\n\"}{Locked=\"--running\r\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\r\n    <value>WSL 버전: {}\r\n커널 버전: {}\r\nWSLg 버전: {}\r\nMSRDC 버전: {}\r\nDirect3D 버전: {}\r\nDXCore 버전: {}\r\nWindows 버전: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\r\n    <value>MSBuild 버전: {}\r\n커밋: {}\r\n빌드 시간: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\r\n    <value>{}에 지정된 사용자 지정 커널 '{}'을(를) 찾을 수 없습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\r\n    <value>{}에서 사용자 지정 커널 모듈 vhd를 찾을 수 없습니다. '{}'.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\r\n    <value>{}에 지정된 사용자 지정 시스템 배포가 없거나 올바른 형식이 아닙니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\r\n    <value>저작권(c) Microsoft Corporation. All rights reserved.\r\n이 제품에 대한 개인 정보 보호 정보는 https://aka.ms/privacy를 방문하세요.\r\n\r\n사용법: wslg.exe [인수] [옵션...] [CommandLine]\r\n\r\n인수:\r\n    --cd &lt;디렉터리&gt;\r\n        지정된 디렉터리를 현재 작업 디렉터리로 설정합니다.\r\n        ~ 가 사용되는 경우 Linux 사용자의 홈 경로가 사용됩니다. 경로가 시작되면\r\n        / 문자를 사용하면 절대 Linux 경로로 해석됩니다.\r\n        그렇지 않으면 값이 절대 Windows 경로여야 합니다.\r\n\r\n    --distribution, -d &lt;Distro&gt;\r\n        지정된 배포를 실행합니다.\r\n\r\n    --user, -u &lt;UserName&gt;\r\n        지정된 사용자로 실행합니다.\r\n\r\n    --shell-type &lt;standard|login|none&gt;\r\n        제공된 셸 형식으로 지정된 명령을 실행합니다.\r\n\r\n    --help\r\n        사용 정보를 표시합니다.\r\n\r\n    --\r\n        나머지 명령줄을 있는 그대로 전달합니다.</value>\r\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\r\n    <value>계속하려면 아무 키나 누르세요...</value>\r\n  </data>\r\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\r\n    <value>{} 인수에 필수 매개 변수가 없습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\r\n    <value>내보내기가 진행 중입니다. 이 작업은 몇 분 정도 걸릴 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\r\n    <value>가져오기가 진행 중입니다. 이 작업은 몇 분 정도 걸릴 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\r\n    <value>{} 또는 /etc/wsl.conf를 통해 GUI 응용 프로그램 지원을 사용할 수 없습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\r\n    <value>업데이트 확인 중입니다.</value>\r\n  </data>\r\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\r\n    <value>이 배포는 Microsoft Store에서만 사용할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\r\n    <value>ARM64의 wsl.exe --mount Windows 버전 27653 이상이 필요합니다.</value>\r\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 최신 버전이 이미 설치되어 있습니다.</value>\r\n  </data>\r\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 버전을 {}(으)로 업데이트하는 중입니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\r\n    <value>이 응용 프로그램을 사용하려면 Linux용 Windows 하위 시스템 선택적 구성 요소가 필요합니다.\r\n실행하여 설치: wsl.exe --install --no-distribution\r\n변경 내용을 적용하려면 시스템을 다시 시작해야 할 수 있습니다.</value>\r\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\r\n    <value>지정한 파일에는 {} 파일 확장명이 있어야 합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\r\n    <value>지정한 파일에는 {} 또는 {} 파일 확장명이 있어야 합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\r\n    <value>VmSwitch '{}'을(를) 찾을 수 없습니다. 사용 가능한 스위치: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\r\n    <value>브리지 네트워킹을 수행하려면 wsl2.vmSwitch를 설정해야 합니다.</value>\r\n  </data>\r\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\r\n    <value>'{}'에서 메일 그룹을 가져오지 못했습니다. {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\r\n    <value>디스크 '{}'을(를) WSL2에 연결하지 못함: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\r\n    <value>디스크 크기를 조정하지 못했습니다.</value>\r\n  </data>\r\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\r\n    <value>값을 찾을 수 없습니다.</value>\r\n  </data>\r\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\r\n    <value>Windows 버전 {}은(는) 패키지된 버전의 Linux용 Windows 하위 시스템 지원하지 않습니다.\r\nWindows 업데이트를 통해 또는 다음을 통해 필요한 업데이트 설치: {}\r\n자세한 내용은 https://aka.ms/wslinstall 참조하세요.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\r\n    <value>디버그 셸을 실행하려면 wsl.exe 관리자 권한으로 실행해야 합니다.</value>\r\n  </data>\r\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\r\n    <value>배포 '{}'에 대한 설치 프로세스가 종료 코드 {}(으)로 인해 실패했습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\r\n    <value>잘못된 GUID 형식: '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\r\n    <value>{}:{}의 섹션 이름이 잘못되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\r\n    <value>{}:{}의 키 이름이 잘못되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\r\n    <value>{}:{}에서 {}이 필요합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\r\n    <value>{}:{}에서 이스케이프된 문자 '{}'이(가) 잘못되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\r\n    <value>{}:{}에 알 수 없는 키 '{}'이(가) 있습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\r\n    <value>{}:{}의 키 '{}'에 대한 정수 값 '{}'이(가) 잘못되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\r\n    <value>{}:{}의 키 '{}'에 대한 IP 값 '{}'이(가) 잘못되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\r\n    <value>{}:{}의 키 '{}'에 대한 부울 값 '{}'이(가) 잘못되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\r\n    <value>{}:{}의 키 '{}'에 대한 mac 주소 '{}'가 잘못되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\r\n    <value>{}, {} 구성 파일을 열지 못했습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\r\n    <value>WSL을 시작하는 동안 오류가 발생했습니다.</value>\r\n  </data>\r\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\r\n    <value>'{}'에 대한 시스템 사용자 세션을 시작하지 못했습니다. 자세한 내용은 journalctl을 참조하십시오.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\r\n    <value>EventViewer 열기</value>\r\n  </data>\r\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\r\n    <value>이 배포에는 기본 이름이 포함되어 있지 않습니다. --name 사용하여 배포 이름을 선택하십시오.</value>\r\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\r\n    <value>{} 및 {} 인수를 동시에 지정할 수 없습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\r\n    <value>인수 {}에는 {} 인수가 필요합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\r\n    <value>잘못된 배포 이름: \"{}\".</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\r\n    <value>레거시 배포 등록을 사용하고 있습니다. tar 기반 배포를 대신 사용하는 것이 좋습니다.</value>\r\n  </data>\r\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\r\n    <value>레거시 배포 등록은 --version 인수를 지원하지 않습니다.</value>\r\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\r\n    <value>\"{}\" 배포는 재정의 매니페스트에서 제공합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\r\n    <value>배포 해시가 일치하지 않습니다. 예상: {}, 실제 해시: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\r\n    <value>잘못된 16진수 문자열: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\r\n    <value>레거시 배포를 설치할 때는 '{}'가 지원되지 않습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\r\n    <value>배포가 설치되었습니다. 'wsl.exe -d {}'을(를) 통해 시작할 수 있습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\r\n    <value>{}:{}의 .wslconfig 항목 '{}'에 대한 메모리 문자열 '{}'이(가) 잘못되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\r\n    <value>'{}'에서 스왑 디스크를 만들지 못함: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\r\n    <value>이 컴퓨터에서는 중첩 가상화가 지원되지 않습니다.</value>\r\n  </data>\r\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\r\n    <value>주소가 '{}'인 네트워크 엔드포인트를 만들지 못했습니다. 할당된 새 주소: '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\r\n    <value>주소 범위가 '{}'인 가상 네트워크를 만들지 못했습니다. 범위가 '{}', {}인 새 네트워크를 만들었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\r\n    <value>안전 모드 사용 - 많은 기능을 사용할 수 없습니다.</value>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\r\n    <value>미러된 네트워킹 모드는 지원되지 않음: {}.\r\nNAT 네트워킹으로 대체합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\r\n    <value>Linux 커널 버전 5.10 이상 필요</value>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\r\n    <value>Windows 버전 {}. {}에 필요한 기능이 없습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\r\n    <value>Hyper-V 방화벽은 지원되지 않습니다.</value>\r\n  </data>\r\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\r\n    <value>DNS 터널링은 지원되지 않습니다.</value>\r\n  </data>\r\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\r\n    <value>미러된 네트워킹 모드를 사용할 때는 wsl2.localhostForwarding 설정이 적용되지 않습니다.</value>\r\n  </data>\r\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\r\n    <value>호스트에서 HTTP 프록시 변경이 검색되었습니다. 변경 내용을 적용하려면 WSL을 다시 시작하세요.</value>\r\n  </data>\r\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\r\n    <value>localhost 프록시 구성이 검색되었지만 WSL로 미러링되지 않았습니다. NAT 모드의 WSL은 localhost 프록시를 지원하지 않습니다.</value>\r\n  </data>\r\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\r\n    <value>IPv6 프록시 구성이 검색되었지만 WSL로 미러링되지 않았습니다. NAT 모드의 WSL은 IPv6을 지원하지 않습니다.</value>\r\n  </data>\r\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\r\n    <value>localhost IPv6 프록시 구성이 검색되었지만 WSL에 미러링되지 않았습니다. WSL은 localhost IPv6 프록시를 지원하지 않습니다.</value>\r\n  </data>\r\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\r\n    <value>프록시 설정을 확인하는 동안 예기치 않은 오류가 발생했습니다. 프록시 설정이 WSL에 미러링되지 않았습니다.</value>\r\n  </data>\r\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\r\n    <value>레지스트리에 액세스하는 동안 오류가 발생했습니다. 경로: '{}'. 오류: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\r\n    <value>localhost 릴레이 프로세스를 시작하지 못했습니다. 오류: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\r\n    <value>가상 네트워킹을 시작하지 못했습니다. wsl.exe --install --no-distribution을 실행하여 선택적 구성 요소 Virtual Machine Platform을 설치하세요.</value>\r\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\r\n    <value>네트워크(networkingMode {})를 구성하지 못했습니다. networkingMode {}(으)로 대체합니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\r\n    <value>Windows 구성 요소 '{}'를 사용하도록 설정하지 못했습니다(종료 코드 {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\r\n    <value>플러그 인 '{}'에서 오류가 반환되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\r\n    <value>플러그 인 '{}'에서 오류가 반환되었습니다. 오류 메시지: '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\r\n    <value>플러그 인 '{}'에는 최신 버전의 WSL이 필요합니다. 다음을 실행하세요. wsl.exe --update</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\r\n    <value>컴퓨터 정책에 따라 .wslconfig 설정 '{}'을(를) 사용할 수 없습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\r\n    <value>컴퓨터 정책에 따라 디버그 셸을 사용할 수 없습니다.</value>\r\n  </data>\r\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\r\n    <value>wsl.exe --mount 가 컴퓨터 정책에 의해 비활성화되었습니다.</value>\r\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\r\n    <value>컴퓨터 정책에 따라 WSL1을 사용할 수 없습니다.</value>\r\n  </data>\r\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\r\n    <value>'wsl.exe --set-version {} 2'를 실행하여 WSL2로 업그레이드하세요.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\r\n    <value>WSL이 업그레이드 마무리 중...</value>\r\n  </data>\r\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\r\n    <value>업데이트하지 못했습니다(종료 코드: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\r\n    <value>제거하지 못했습니다(종료 코드: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\r\n    <value>로그 파일: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\r\n    <value>MSIX 패키지를 제거하지 못했습니다(오류: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\r\n    <value>MSIX 패키지를 설치하지 못했습니다(오류: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\r\n    <value>WSL을 실행하는 데 필요한 선택적 구성 요소가 설치되어 있지 않습니다.</value>\r\n  </data>\r\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\r\n    <value>누락된 구성 요소를 설치하세요.</value>\r\n  </data>\r\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\r\n    <value>가져온 파일은 유효한 Linux 배포가 아닙니다.</value>\r\n  </data>\r\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\r\n    <value>종료하려면 아무 키나 누르세요...</value>\r\n  </data>\r\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\r\n    <value>새 버전의 Linux용 Windows 하위 시스템을 사용할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\r\n    <value>{} 버전으로 업데이트하거나 아래 릴리스 정보를 확인하세요.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\r\n    <value>업데이트</value>\r\n  </data>\r\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\r\n    <value>문서 보기</value>\r\n  </data>\r\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\r\n    <value>VHD가 현재 사용 중이므로 작업을 완료할 수 없습니다. WSL을 강제로 사용 중지하려면: wsl.exe --shutdown</value>\r\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\r\n    <value>{}은(는) 올바른 부울이 아닙니다&lt;true|false&gt;</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\r\n    <value>스파스 VHD는 WSL2에서만 지원됩니다.</value>\r\n  </data>\r\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\r\n    <value>WSL을 로컬 시스템으로 실행할 수 없습니다.</value>\r\n  </data>\r\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\r\n    <value>성능 팁:</value>\r\n  </data>\r\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\r\n    <value>Windows 드라이브에서 {}와 같은 I/O 집약적 작업을 사용하면 성능이 저하됩니다. 성능 향상을 위해 프로젝트 파일을 Linux 파일 시스템으로 이동하는 것이 좋습니다. 자세히 알아보려면 아래를 클릭하세요.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\r\n    <value>문서 보기</value>\r\n  </data>\r\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\r\n    <value>다시 표시 안 함</value>\r\n  </data>\r\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\r\n    <value>WSL2 가상 머신이 충돌했습니다.</value>\r\n  </data>\r\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\r\n    <value>스택 추적이 {}에 저장되었습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\r\n    <value>배포를 시작하지 못했습니다. 오류 코드: {}, 오류 단계: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\r\n    <value>가상 디스크가 손상되어 배포를 시작하지 못했습니다.</value>\r\n  </data>\r\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\r\n    <value>{}:{}의 구성 키 '{}'가 중복되었습니다(충돌하는 키: {}:{}의 '{}').</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\r\n    <value>{}:{}의 구성 키 '{}'에 대한 값 '{}'이(가) 잘못되었습니다(유효한 값: {}).</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\r\n    <value>\"{}\" 배포에 대해 OOBE 명령이 완료되는 것을 기다리는 중 ...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\r\n    <value>배포 {}에서 '{}' 속성을 읽지 못했습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\r\n    <value>VHD 파일인 것 같습니다. --vhd 사용하여 tar 대신 VHD를 가져옵니다.</value>\r\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\r\n    <value>Microsoft Store에서 {}을(를) 설치하지 못함: {}\r\n웹 다운로드를 시도하는 중...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\r\n    <value>기본 배포가 구성되지 않았습니다. 설치할 배포를 제공하세요.</value>\r\n  </data>\r\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\r\n    <value>wsl2.virtio9p를 사용할 수 없습니다. vsock 전송을 사용하여 9p로 대체합니다.</value>\r\n  </data>\r\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\r\n    <value>WSL 설치가 손상된 것 같습니다(오류 코드: {}).\r\n아무 키나 눌러 WSL을 복구하거나 CTRL-C 취소하세요.\r\n이 프롬프트는 60초 후 시간이 초과됩니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\r\n    <value>배포를 등록하는 동안 터미널 프로필을 구문 분석하지 못했습니다. {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\r\n    <value>잘못된 크기: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\r\n    <value>{}\r\n오류 코드: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\r\n    <value>잘못된 JSON 문서입니다. 구문 분석 오류: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\r\n    <value>wsl2.processors는 시스템의 논리 프로세서 수({} &gt; {})를 초과할 수 없습니다.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\r\n    <value>네트워킹 모드를 쿼리하지 못했습니다.</value>\r\n  </data>\r\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\r\n    <value>사용자 지정 커널을 지정하지 않고 사용자 지정 커널 모듈이 제공되었습니다. 자세한 내용은 https://aka.ms/wslcustomkernel 참조하세요.</value>\r\n  </data>\r\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\r\n    <value>현재 전역 보안 액세스 클라이언트와의 호환성 문제가 발생하여 DNS 터널링이 비활성화되었습니다.</value>\r\n  </data>\r\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\r\n    <value>데이터 손상 가능성으로 인해 스파스 VHD 지원은 현재 비활성화되어 있습니다.\r\n배포 시 강제로 스파스 VHD를 사용하도록 하려면 다음을 실행하세요. \r\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\r\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\r\n    <value>{}을(를) 탑재하지 못했습니다. 자세한 내용은 dmesg를 참조하세요.</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\r\n    <value>/etc/fstab을 mount -a로 처리하지 못했습니다.</value>\r\n  </data>\r\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\r\n    <value>배포 디스크를 탑재하는 동안 오류가 발생했습니다. 대체로 읽기 전용으로 탑재되었습니다.\r\n복구 지침 참조: https://aka.ms/wsldiskmountrecovery</value>\r\n  </data>\r\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\r\n    <value>'{}'을(를) 번역하지 못했습니다</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\r\n    <value>오류가 발생했습니다. 나중에 다시 시도하세요.</value>\r\n  </data>\r\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>정보</value>\r\n  </data>\r\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 설정 정보</value>\r\n  </data>\r\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\r\n    <value>© Microsoft. All rights reserved.</value>\r\n  </data>\r\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 설정</value>\r\n  </data>\r\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 설정을 통해 개발자는 GUI 기반 응용 프로그램을 사용하여 .wslconfig 파일을 관리할 수 있습니다.</value>\r\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\r\n    <value>자동 메모리 회수</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\r\n    <value>유휴 CPU 사용량을 감지한 후 캐시된 메모리를 자동으로 해제합니다. 캐시된 메모리를 느리게 해제하려면 gradual로 설정하고 즉시 해제하려면 dropcache로 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>자동 메모리 회수.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>유휴 CPU 사용량을 감지한 후 캐시된 메모리를 자동으로 해제합니다. 캐시된 메모리를 느리게 해제하려면 gradual로 설정하고 즉시 해제하려면 dropcache로 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\r\n    <value>자동 프록시 사용</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\r\n    <value>WSL에서 Windows의 HTTP 프록시 정보를 사용할 수 있도록 합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>자동 프록시를 사용하도록 설정했습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL에서 Windows의 HTTP 프록시 정보를 사용할 수 있도록 합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\r\n    <value>최상의 DNS 구문 분석 사용</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\r\n    <value>wsl2.dnsTunneling true로 설정된 경우에만 적용할 수 있습니다. true로 설정하면 Windows에서 DNS 요청에서 질문을 추출하고 resolve 시도합니다. 알 수 없는 레코드는 무시됩니다.</value>\r\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>최상의 DNS 구문 분석을 사용합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>wsl2.dnsTunneling이 true로 설정된 경우에만 적용할 수 있습니다. true로 설정하면 Windows에서 DNS 요청에서 질문을 추출하고 resolve 시도합니다. 알 수 없는 레코드는 무시됩니다.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\r\n    <value>사용자 지정 커널</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\r\n    <value>사용자 지정 Linux 커널의 절대 Windows 경로입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>커널 찾아보기</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>사용자 지정 커널</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>사용자 지정 Linux 커널의 절대 Windows 경로입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\r\n    <value>사용자 지정 커널 모듈</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\r\n    <value>사용자 지정 Linux 커널 모듈 VHD에 대한 절대 Windows 경로입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>커널 모듈 찾아보기</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>사용자 지정 커널 모듈</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>사용자 지정 Linux 커널 모듈 VHD에 대한 절대 Windows 경로입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\r\n    <value>사용자 지정 시스템 배포판</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>배포판 찾아보기</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\r\n    <value>사용자 지정 시스템 배포판으로 로드되는 VHD의 경로를 지정합니다. 주로 WSL에서 GUI 앱을 제공하는 데 사용됩니다. [] 여기서 시스템 배포판에 대해 자세히 알아보세요.</value>\r\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgsystemdistro</value>\r\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>사용자 지정 시스템 배포판</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL에서 주로 GUI 앱 구동에 사용하는 사용자 지정 시스템 배포판으로 로드할 VHD의 경로를 지정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\r\n    <value>디버그 콘솔 사용</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\r\n    <value>WSL 2 배포판 인스턴스가 시작될 때 dmesg의 내용을 표시하는 출력 콘솔 창을 켜는 부울입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>디버그 콘솔을 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL 2 배포판 인스턴스가 시작될 때 진단 메시지의 내용을 표시하는 출력 콘솔 창을 켜는 부울입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\r\n    <value>기본 VHD 크기</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\r\n    <value>새로 만든 배포에 대해서만 확장 가능한 WSL VHD(가상 하드 디스크)의 기본 최대 크기입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>크기 초기화</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>기본 VHD 크기</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>새로 만든 배포에 대해서만 확장 가능한 WSL VHD(가상 하드 디스크)의 기본 최대 크기(MB)입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>개발자</value>\r\n  </data>\r\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\r\n    <value>설명서</value>\r\n  </data>\r\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocs</value>\r\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\r\n    <value>DrvFS 모드 변경</value>\r\n  </data>\r\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\r\n    <value>WSL에서 OS 간 파일 액세스 구현을 변경합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\r\n    <value>DNS 프록시 사용</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\r\n    <value>wsl2.networkingMode NAT로 설정된 경우에만 적용할 수 있습니다. WSL에 Linux의 DNS 서버를 호스트의 NAT로 구성하도록 알리는 부울입니다. false로 설정하면 DNS 서버가 Windows에서 Linux로 미러.</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>DNS 프록시를 사용하도록 설정했습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>wsl2.networkingMode가 NAT로 설정된 경우에만 적용할 수 있습니다. WSL에 Linux의 DNS 서버를 호스트의 NAT로 구성하도록 알리는 부울입니다. false로 설정하면 DNS 서버가 Windows에서 Linux로 미러.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\r\n    <value>DNS 터널링 사용</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\r\n    <value>DNS 요청이 WSL에서 Windows로 프록시되는 방법을 변경합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>DNS 터널링을 사용하도록 설정했습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>DNS 요청이 WSL에서 Windows로 프록시되는 방법을 변경합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>파일 시스템</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\r\n    <value>GUI 애플리케이션 사용</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\r\n    <value>WSL에서 GUI 애플리케이션([WSLg])에 대한 지원을 켜거나 끄는 부울입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgproject</value>\r\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>GUI 애플리케이션을 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL에서 GUI 애플리케이션(WSL g라고 함)에 대한 지원을 설정하거나 해제하는 부울입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\r\n    <value>하드웨어 성능 카운터 사용</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\r\n    <value>Linux용 하드웨어 성능 카운터를 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>하드웨어 성능 카운터를 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>Linux용 하드웨어 성능 카운터를 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\r\n    <value>Hyper-V 방화벽 사용</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\r\n    <value>WSL 네트워크 트래픽을 필터링하기 위해 Windows 방화벽 규칙과 Hyper-V 트래픽에 대한 규칙을 허용하는 Hyper-V 방화벽을 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>Hyper-V 방화벽을 사용하도록 설정했습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL 네트워크 트래픽을 필터링하기 위해 Windows 방화벽 규칙과 Hyper-V 트래픽에 대한 규칙을 허용하는 Hyper-V 방화벽을 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\r\n    <value>호스트 주소 Loopback</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\r\n    <value>wsl2.networkingMode 미러링으로 설정된 경우에만 적용할 수 있습니다. True로 설정하면 호스트에 할당된 IP 주소로 컨테이너가 호스트에 연결하거나 호스트가 컨테이너에 연결할 수 있습니다. 127.0.0.1 루프백 주소는 항상 사용할 수 있습니다. 이 옵션을 사용하면 추가로 할당된 모든 로컬 IP 주소도 사용할 수 있습니다.</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>호스트 주소 Loopback.</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>wsl2.networkingMode가 미러로 설정된 경우에만 적용할 수 있습니다. True로 설정하면 호스트에 할당된 IP 주소로 컨테이너가 호스트에 연결하거나 호스트가 컨테이너에 연결할 수 있습니다. 127.0.0.1 루프백 주소는 항상 사용할 수 있습니다. 이 옵션을 사용하면 추가로 할당된 모든 로컬 IP 주소도 사용할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\r\n    <value>무시된 포트</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\r\n    <value>wsl2.networkingMode 미러링으로 설정된 경우에만 적용할 수 있습니다. Linux 애플리케이션이 Windows에서 자동으로 전달되거나 고려되지 않는 바인딩할 수 있는 포트를 지정합니다. 쉼표로 구분된 목록(예: 3000,9000,9090)으로 서식을 지정해야 합니다.</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\r\n    <value>포트 초기화</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>무시된 포트</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>wsl2.networkingMode가 미러로 설정된 경우에만 적용할 수 있습니다. Linux 애플리케이션이 Windows에서 자동으로 전달되거나 고려되지 않는 바인딩할 수 있는 포트를 지정합니다. 쉼표로 구분된 목록에서 형식을 지정해야 합니다(예: 3000, 9000, 9090).</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\r\n    <value>초기 자동 프록시 시간 제한</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\r\n    <value>wsl2.autoProxy true로 설정된 경우에만 적용할 수 있습니다. WSL 컨테이너를 시작할 때 WSL에서 HTTP 프록시 정보를 검색할 때까지 대기하는 시간(밀리초)을 구성합니다. 이 시간 후에 프록시 설정이 확인되면 검색된 프록시 설정을 사용하려면 WSL instance 다시 시작해야 합니다.</value>\r\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\r\n    <value>초기화 시간 제한</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>초기 자동 프록시 시간 제한</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>wsl2.autoProxy가 true로 설정된 경우에만 적용할 수 있습니다. WSL 컨테이너를 시작할 때 WSL에서 HTTP 프록시 정보 검색을 기다리는 시간(밀리초)을 구성합니다. 이 시간 후에 프록시 설정이 확인되면 검색된 프록시 설정을 사용하려면 WSL 인스턴스를 다시 시작해야 합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\r\n    <value>커널 명령줄</value>\r\n  </data>\r\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\r\n    <value>추가 커널 명령줄 인수입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\r\n    <value>localhost 전달 사용</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\r\n    <value>WSL 2 VM의 와일드카드 또는 localhost에 바인딩된 포트를 localhost:port를 통해 호스트에서 연결할 수 있는지 여부를 지정하는 부울입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>localhost 전달을 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL 2 VM의 와일드카드 또는 localhost에 바인딩된 포트를 localhost, colon, port를 통해 호스트에서 연결할 수 있는지 여부를 지정하는 부울입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\r\n    <value>{0}MB</value>\r\n  </data>\r\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>메모리 및 프로세서</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\r\n    <value>메모리 크기</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\r\n    <value>WSL 2 VM에 할당할 메모리의 크기입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>크기 초기화</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>메모리 크기</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL 2 VM에 할당할 메모리 양(MB)입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\r\n    <value>{0}밀리초</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\r\n    <value>중첩된 가상화 사용</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\r\n    <value>중첩 가상화를 설정 또는 해제하여 다른 중첩 가상 머신이 WSL 2 내에서 실행될 수 있도록 하는 부울입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>중첩된 가상화를 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>중첩 가상화를 설정 또는 해제하여 다른 중첩 가상 머신이 WSL 2 내에서 실행될 수 있도록 하는 부울입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\r\n    <value>네트워킹 모드</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\r\n    <value>WSL의 네트워킹 모드를 지정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>네트워킹 모드.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL의 네트워킹 모드를 지정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>네트워킹</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\r\n    <value>모든 파일과 함께 운영 체제에서 작업할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\r\n    <value>OS 간 파일 액세스</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\r\n    <value>Linux에서 Windows 파일에 액세스</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\r\n    <value>C 드라이브에 대한 다음 예제와 같이 '/mnt'로 이동한 다음 Windows 드라이브 문자로 이동하여 Linux 내에서 Windows 파일에 액세스할 수 있습니다.\r\n\r\n'cd /mnt/c'</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\r\n    <value>파일 탐색기를 사용하여 Linux 파일에 액세스</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\r\n    <value>파일 탐색기에서 '\\\\wsl.localhost\\'로 이동하거나 'Linux' 아이콘을 클릭하여 Linux 파일을 볼 수 있습니다.</value>\r\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\r\n    <value>WSL에서 Windows 파일 및 프로그램 시작</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\r\n    <value>WSL을 사용하는 동안에도 bash에서 직접 Windows 실행 파일을 실행할 수 있습니다. 실행 시도\r\n'powershell.exe /c start .'를 클릭하여 현재 폴더의 파일 탐색기를 엽니다.</value>\r\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\r\n    <value>Windows에서 Linux 네트워킹 앱 액세스</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\r\n    <value>Linux 배포판에서 네트워킹 앱(예: NodeJS 또는 SQL 서버에서 실행되는 앱)을 빌드하는 경우 평소와 같이 localhost를 사용하여 Windows 앱(예: Edge 또는 Chrome 인터넷 브라우저)에서 액세스할 수 있습니다. 즉, 포트 3000을 수신 대기하는 Linux 서버를 시작한 경우 Windows의 Edge에서 [http://localhost:3000]로 이동하여 액세스할 수 있습니다.</value>\r\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\r\n    <value>http://localhost:3000</value>\r\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\r\n    <value>미러 모드 네트워킹</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\r\n    <value>WSL에는 IPv6 지원 및 LAN에서 네트워킹 애플리케이션에 액세스하는 기능과 같은 고급 기능을 추가하는 미러 모드라는 새로운 네트워킹 모드도 포함되어 있습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\r\n    <value>OS 간 파일 액세스에 대한 자세한 정보</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslfilesystems</value>\r\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템 시작</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\r\n   <value>WSL은 다양한 Linux 배포판을 사용해 보기 좋은 방법입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\r\n    <value>배포판 관리</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\r\n    <value>기본 WSL 명령에 대한 자세한 정보</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslcommands</value>\r\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\r\n    <value>Linux 배포를 가져오는 방법에 대한 자세한 정보</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslcustomdistro</value>\r\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>설치 가능한 WSL 배포판 목록 명령</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>'wsl.exe -l -o'</value>\r\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>명명된 WSL 배포판 설치 명령</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\r\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>사용 가능한 WSL 배포판 목록 명령</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>'wsl.exe -l'</value>\r\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\r\n    <value>Docker Desktop은 Linux 컨테이너를 사용하여 개발하는 데 도움이 되도록 WSL과 잘 작동합니다.\r\n\r\nWSL과 함께 Docker Desktop을 사용할 경우의 이점은 다음과 같습니다.\r\n\r\n• 동일한 Docker 디먼 및 이미지를 사용하여 WSL 또는 Windows에서 Docker 명령을 실행할 수 있습니다.\r\n• WSL에서 Windows 드라이브의 자동 탑재를 사용하여 Windows와 Linux 간에 파일 및 폴더를 원활하게 공유할 수 있습니다.\r\n• WSL의 상호 운용성 덕분에 기본 Windows 도구 및 편집기를 사용하여 Linux 코드 및 파일에서 작업할 수 있으며 그 반대의 경우도 마찬가지입니다.</value>\r\n </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\r\n    <value>Docker 데스크톱 통합</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\r\n    <value>Docker에서 WSL을 사용하는 방법에 대한 자세한 정보</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocker</value>\r\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\r\n    <value>WSL(Linux용 Windows 하위 시스템)을 사용하면 자주 사용하는 Linux 도구, 유틸리티, 애플리케이션 및 워크플로를 Windows에서 직접 실행할 수 있습니다.\r\n\r\n잠시 시간을 내어 커뮤니티의 인기 기능을 미리 살펴보거나 종합적인 문서를 확인해보세요.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\r\n    <value>WSL 시작</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\r\n    <value>설치 모범 사례</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslsetup</value>\r\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\r\n    <value>Linux를 사용한 시작</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgettingstarted</value>\r\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\r\n    <value>WSL(Linux용 Windows 하위 시스템) 설명서</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocs</value>\r\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\r\n    <value>Alt-tab, 시작 메뉴 실행, 작업 표시줄 고정 및 잘라내기 및 붙여넣기 지원과 같은 기본 Windows 상호 작용과 함께 그래픽 기반 Linux 애플리케이션을 사용할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\r\n    <value>GUI 앱</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\r\n    <value>WSL은 Machine Learning 워크플로에 Windows GPU를 활용할 수 있습니다.\r\n\r\nWSL에서 실행되는 Linux 이진 파일은 Windows에서 GPU를 자동으로 사용하여 성능을 높일 수 있습니다. 일반 Linux 머신에서와 동일한 방식으로 해당 워크플로를 설치하고 실행하기만 하면 됩니다. NVIDIA 그래픽 카드가 있는 경우 도커 컨테이너에서 CUDA를 실행하는 것부터 AMD, Intel 또는 NVIDIA 그래픽 카드에서 DirectML을 사용하여 PyTorch 또는 TensorFlow를 실행하는 것까지 다양한 방법으로 시작할 수 있습니다. 자세한 내용은 아래의 시작 가이드를 참조하세요.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\r\n    <value>GPU 가속</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\r\n    <value>GPU 가속에 대한 자세한 정보</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgpu</value>\r\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\r\n    <value>WSL GUI 앱에 대한 자세한 정보</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslguiapps</value>\r\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\r\n    <value>다음은 시도할 앱 목록입니다('sudo apt install &lt;앱 이름&gt;'을 입력하여 Ubuntu에서 이러한 모든 항목을 설치할 수 있습니다.)\r\n\r\n    • gedit – 기본 텍스트 편집기\r\n    • audacity – 오디오 파일 녹음 및 편집\r\n    • blender – 3D 애니메이션 및 시각화 만들기\r\n    • gimp – 사진 편집\r\n    • nautilus – Linux 파일 탐색기\r\n    • vlc – 비디오 플레이어</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\r\n    <value>Windows 및 Linux 운영 체제에서 네트워킹 앱에 쉽게 액세스할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\r\n    <value>네트워킹 통합</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\r\n    <value>네트워킹 애플리케이션에 대한 자세한 정보</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslnetworking</value>\r\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\r\n    <value>미러 모드 네트워킹에 대한 자세한 정보</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslmirroredmode</value>\r\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\r\n   <value>WSL을 VS Code 직접 전체 개발 환경으로 사용할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\r\n    <value>VS Code 통합</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\r\n    <value>VS Code WSL 사용에 대한 자세한 정보</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslvscode</value>\r\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\r\n    <value>설치 방법</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\r\n    <value>VS Code 설치한 후 Windows 터미널 원격 WSL 확장을 설치할 수 있습니다.\r\n\r\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\r\n    <value>Visual Studio Code WSL 프로젝트 열기</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\r\n    <value>WSL 배포에서 VS Code 프로젝트를 열려면 배포의 명령줄을 열고 'code .'를 실행하여 프로젝트 파일을 엽니다.\r\n\r\nVS Code 자체 내의 명령 팔레트를 통해 더 많은 VS Code 원격 옵션에 액세스할 수도 있습니다. 키보드에서 \"SHIFT+CTRL+P\"를 눌러 명령 팔레트를 열고 'Remote-WSL'을 입력하여 사용 가능한 VS Code 원격 옵션 목록을 확인하여 원격 세션에서 폴더를 다시 열고, 열려는 배포를 지정하는 등의 작업을 수행할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\r\n    <value>사용 방법</value>\r\n  </data>\r\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>선택적 기능</value>\r\n  </data>\r\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\r\n    <value>개인 정보 취급 방침</value>\r\n  </data>\r\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\r\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\r\n  </data>\r\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\r\n    <value>프로세서 수</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\r\n    <value>WSL 2 VM에 할당할 논리 프로세서 수입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\r\n    <value>초기화 횟수</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>프로세서 수</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL 2 VM에 할당할 논리 프로세서 수입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\r\n    <value>관련 링크</value>\r\n  </data>\r\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\r\n    <value>릴리스 정보</value>\r\n  </data>\r\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslreleases</value>\r\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\r\n  </data>\r\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\r\n    <value>안전 모드 사용</value>\r\n  </data>\r\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\r\n    <value>많은 기능을 사용하지 않도록 설정하고 잘못된 상태의 배포를 복구하는 데 사용되는 \"안전 모드\"에서 WSL을 실행합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>안전 모드를 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>많은 기능을 사용하지 않도록 설정하고 잘못된 상태의 배포를 복구하는 데 사용되는 \"안전 모드\"에서 WSL을 실행합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\r\n    <value>정보</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\r\n    <value>개발자</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\r\n    <value>배포판 관리</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Docker 데스크톱 통합</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\r\n    <value>파일 시스템</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\r\n    <value>일반</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\r\n    <value>GPU 가속</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\r\n    <value>GUI 앱</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\r\n    <value>wsl.exe 실행</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\r\n    <value>메모리 및 프로세서</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\r\n    <value>네트워킹</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\r\n    <value>네트워킹 통합</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\r\n    <value>WSL 시작</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\r\n    <value>선택적 기능</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\r\n    <value>설정</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\r\n    <value>VS Code 통합</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\r\n    <value>새로운 기능</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\r\n    <value>파일 시스템 간 작업</value>\r\n  </data>\r\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\r\n    <value>문제</value>\r\n  </data>\r\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslproject</value>\r\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\r\n  </data>\r\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\r\n    <value>기본값으로 스파스 VHD 사용</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\r\n    <value>새로 생성된 VHD는 활성화하면 자동으로 스파스로 설정됩니다.</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>기본값으로 스파스 VHD를 사용하도록 설정합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>새로 생성된 VHD는 활성화하면 자동으로 스파스로 설정됩니다.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\r\n    <value>스왑 파일 위치</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\r\n    <value>스왑 가상 하드 디스크의 절대 Windows 경로입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>스왑 파일 찾아보기</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>스왑 파일 위치</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>스왑 가상 하드 디스크의 절대 Windows 경로입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\r\n    <value>스왑 크기</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\r\n    <value>WSL 2 VM에 추가할 스왑 공간의 크기입니다. 스왑 파일이 없으면 0입니다. 스왑 스토리지는 메모리 요구량이 하드웨어 장치의 한도를 초과할 때 사용되는 디스크 기반 RAM입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>크기 초기화</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>전환 크기</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>WSL 2 VM에 추가할 스왑 공간(MB)입니다. 스왑 파일이 없는 경우 0입니다. 스왑 스토리지는 메모리 요구가 하드웨어 장치의 한도를 초과하는 경우 사용되는 디스크 기반 RAM입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\r\n    <value>VirtIO 사용</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\r\n    <value>호스트 파일에 액세스할 때 플랜 9 대신 virtiofs를 사용하면 속도가 빨라집니다.</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\r\n    <value>Virtio 9p 사용</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\r\n    <value>virtio 전송을 사용하여 호스트에서 9P 파일 시스템을 탑재할 수 있도록 합니다.</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\r\n    <value>VM 유휴 시간 제한</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\r\n    <value>VM이 종료되기 전에 유휴 상태인 시간(밀리초)입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\r\n    <value>초기화 시간 제한</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>VM 유휴 시간 제한</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>VM이 종료되기 전에 유휴 상태인 시간(밀리초)입니다.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\r\n    <value>Visual Studio에서 WSL로 실행 중인 앱을 빌드, 실행, 디버그 및 프로파일링하기</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\r\n    <value>Visual Studio에서 WSL로 앱을 실행하고 디버그하기</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\r\n    <value>Visual Studio의 .NET 개발자용 WSL에 대해 자세히 알아보기</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\r\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\r\n    <value>C++ 개발자용 Visual Studio 및 WSL에 대해 자세히 알아보기</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\r\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\r\n    <value>Linux용 Windows 하위 시스템(WSL)을 사용해 Visual Studio를 종료하지 않고도 Linux에서 .NET Core와 크로스 플랫폼 C++ 앱을 쉽게 실행하고 디버그할 수 있습니다. 크로스 플랫폼 개발자라면 이 방법을 통해 더 많은 대상 환경을 간편하게 테스트할 수 있습니다.</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\r\n    <value>Visual Studio 통합</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Visual Studio 통합</value>\r\n  </data>\r\n</root>"
  },
  {
    "path": "localization/strings/nb-NO/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Windows-undersystem for Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Med Windows-undersystem for Linux kan utviklere kjøre et GNU/Linux-miljø – inkludert de fleste kommandolinjeverktøy, verktøy og programmer – direkte på Windows, uendret, uten kostnadene ved en tradisjonell virtuell maskin eller konfigurasjon av dobbeltoppstart.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Kan ikke koble fra disken: {}. Hvis du vil ha mer informasjon, kan du kjøre dmesg i WSL2.\nHvis du vil tvinge WSL2 til å stoppe og koble fra disken, kjører du wsl.exe {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Disken {} er allerede tilkoblet.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Volumet er allerede montert i WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>En disk med dette navnet er allerede montert. demonter disken, eller velg et nytt navn, og prøv på nytt.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Det angitte monteringsnavnet inneholder et ugyldig /-tegn. Prøv på nytt uten det ugyldige tegnet.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Disken er koblet til som «/mnt/wsl/{}».\nObs! Plasseringen vil være en annen hvis du har endret innstillingen automount.root i /etc/wsl.conf.\nKjør wsl.exe {} {} for å koble fra disken.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Disken ble koblet til, men kan ikke monteres: {}.\nHvis du vil ha mer informasjon, kan du kjøre dmesg i WSL2.\nKjør wsl.exe {} {} for å koble fra disken.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Nedenfor vises en liste over gyldige distribusjoner som kan installeres.\nInstaller ved hjelp av wsl.exe {} &lt;Distro&gt;.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Distribusjonsnavnet er allerede angitt.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>Den angitte installasjonsplasseringen er allerede i bruk.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Det finnes allerede en distribusjon med det angitte navnet. Bruk --name til å velge et annet navn.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Det finnes ingen distribusjon med det angitte navnet.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Administratortilgang kreves for å montere en disk.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Eksport av distribusjonen mislyktes.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Utfører engangsoppgradering av Windows-undersystem for Linux-filsystem for denne distribusjonen...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Kan ikke starte fordi en annen forekomst kjører opphøyd.  Forhøyede og uhøyde forekomster har ikke tillatelse til å kjøre samtidig.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Import av distribusjonen mislyktes.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Installerer valgfri Windows-komponent: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Laster ned: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Installerer: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importerer distribusjon</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} er lastet ned.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Windows-undersystem for Linux gjenopptar en tidligere installasjon...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Ugyldig distribusjonsnavn: {}.\nHvis du vil ha en liste over gyldige distribusjoner, kan du bruke 'wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Windows-undersystem for Linux-forekomsten er avsluttet.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Ugyldig kommandolinjeargument: {}\nBruk {} --help' for å få en liste over argumenter som støttes.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>Kommandolinjeargumentet {} krever en verdi.\nBruk {} --help' for å få en liste over argumenter som støttes.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Konsollinnstillinger som ikke støttes. For å kunne bruke denne funksjonen må den eldre konsollen deaktiveres.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} er ikke et gyldig heltall.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>En installering, avinstallering eller konvertering pågår for denne distribusjonen.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Starter {} …</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Kan ikke starte fordi en annen forekomst kjører i opphøyd modus.  Forhøyede og uhøyde forekomster har ikke tillatelse til å kjøre samtidig.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Windows-undersystem for Linux har ingen installerte distribusjoner.\nDu kan løse dette ved å installere en distribusjon med instruksjonene nedenfor:\n\nBruk wsl.exe --list --online' til å vise tilgjengelige distribusjoner\nog \"wsl.exe --install &lt;Distro&gt;\" som skal installeres.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Det finnes ingen distribusjoner som kjører.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (standard)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Windows-undersystem for Linux-distribusjoner:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Standarddistribusjon: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Standardversjon: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Avregistrerer.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Finner ikke bruker.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Hvis du vil ha informasjon om viktige forskjeller med WSL 2, kan du gå til https://aka.ms/wsl2</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Konvertering pågår. Dette kan ta noen minutter.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>Distribusjonen er allerede den forespurte versjonen.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>Eldre distribusjon støtter ikke WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>WSL2 kan ikke starte fordi virtualisering ikke er aktivert på denne maskinen.\nKontroller at den valgfrie komponenten Virtual Machine Platform er aktivert, og at virtualisering er aktivert i datamaskinens fastvareinnstillinger.\n\nAktiver Virtual Machine Platform ved å kjøre: wsl.exe --install --no-distribution\n\nHvis du vil ha mer informasjon, kan du gå til https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>For mange virtuelle harddisker eller fysiske disker er koblet til.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD-disken er allerede montert via wsl.exe --mount, Demonter disken før du starter.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 støttes ikke med gjeldende maskinkonfigurasjon.\nAktiver den valgfrie Windows-undersystem for Linux-komponenten for å bruke WSL1.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Denne operasjonen støttes bare av WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Bruk:\n    --networking-mode\n       Vis gjeldende nettverksmodus.\n\n    --msal-proxy-path\n        Vis banen til MSAL-proxyprogrammet.\n\n    --vm-id\n        Vis VM-ID-en for WSL.\n\n    --version\n        Vis versjonen av WSL-pakken.\n\n    -n\n        Ikke skriv ut et linjeskift.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Bruk: \n    -a\n        Tving resultat til absolutt baneformat.\n    -u\n        Oversett fra en Windows-bane til en WSL-bane (standard).\n    -w\n        Oversett fra en WSL-bane til en Windows-bane.\n    -m\n        Oversett fra en WSL-bane til en Windows-bane, med / stedet for \\\\\n\nEksempel: wslpath c:\\\\users</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Utfører administrative operasjoner på Windows-undersystem for Linux\n\nBruk:\n    /l, /list [Alternativ]\n        Lister opp registrerte distribusjoner.\n        /all – Du kan også liste opp alle distribusjoner, inkludert distribusjoner som\n               for øyeblikket installeres eller avinstalleres.\n\n        /running – Lister bare opp distribusjoner som kjører for øyeblikket.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Setter distribusjonen som standard.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Avslutter distribusjonen.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Avregistrerer distribusjonen og sletter rotfilsystemet.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Opphavsrett (c) Microsoft Corporation. Med enerett.\nHvis du vil ha personverninformasjon om dette produktet, kan du gå til https://aka.ms/privacy.\n\nBruk: wsl.exe [Argument] [Alternativer...] [Kommandolinje]\n\nArgumenter for å kjøre Linux-binærfiler:\n\nHvis ingen kommandolinje er angitt, starter wsl.exe standardgrensesnittet.\n\n--exec, -e &lt;CommandLine&gt;\n        Utfør den angitte kommandoen uten å bruke standard Linux shell.\n\n    --shell-type &lt;standard|login|none&gt;\n        Utfør den angitte kommandoen med den angitte skalltypen.\n\n--\n        Sender gjenstående kommandolinje slik den er.\n\nOptions:\n    --cd &lt;Directory&gt;\n        Angir den angitte mappen som gjeldende arbeidsmappe.\n        Hvis ~ brukes, brukes Linux-brukerens hjemmebane. Hvis banen begynner\n        med et /-tegn, tolkes det som en absolutt Linux-bane.\n        Ellers må verdien være en absolutt Windows-bane.\n\n    --distribution, -d &lt;DistroName&gt;\n        Kjør den angitte distribusjonen.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Kjør den angitte distribusjons-ID-en.\n\n    --user, -u &lt;UserName&gt;\n        Kjør som den angitte brukeren.\n\n    --system\n        Starter et skall for systemdistribusjonen.\n\nArgumenter for behandling av Windows-undersystem for Linux:\n\n    --help\n        Vis bruksinformasjon.\n\n    --debug-shell\n        Åpne et WSL2 feilsøkingsskal til diagnoseformål.\n\n    --install [Distro] [Alternativer ...]\n        Installer et Windows-undersystem for Linux-distribusjon.\n        For en liste over gyldige distribusjoner kan du bruke 'wsl.exe --list --online'.\n\n        Alternativer:\n            --enable-wsl1\n                Aktiver WSL1-støtte.\n\n            --fixed-vhd\n                Opprett en disk med fast størrelse for å lagre distribusjonen.\n\n            --from-file &lt;Path&gt;\n                Installer en distribusjon fra en lokal fil.\n\n            --legacy\n                Bruk det eldre distribusjonsmanifestet.\n\n            --location &lt;Location&gt;\n                Angi installasjonsbanen for distribusjonen.\n\n            --name &lt;Name&gt;\n                Angi navnet på distribusjonen.\n\n            --no-distribution\n                Installer kun de nødvendige valgfrie komponentene, installerer ikke en distribusjon.\n\n            --no-launch, -n\n                Ikke start distribusjonen etter installeringen.\n\n            --version &lt;Version&gt;\n                Angir versjonen som skal brukes til den nye distribusjonen.\n\n            --vhd-size &lt;MemoryString&gt;\n                Angir størrelsen på disken som skal lagre distribusjonen.\n\n            --web-download\n                Last ned distribusjonen fra internett i stedet for Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Endrer bestemte alternativer for Distro.\n\n        Alternativer:\n            --move &lt;Location&gt;\n                Flytt distribusjonen til en ny plassering.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Angi at VHD-en for distro skal være spredt, slik at diskplass kan frigjøres automatisk.\n\n            --set-default-user &lt;Username&gt;\n                Angi standardbruker for distribusjonen.\n\n            --resize &lt;MemoryString&gt;\n                Endre størrelsen på disken i distribusjonen til den angitte størrelsen.\n\n    --mount &lt;Disk&gt;\n        Fester og monterer en fysisk eller virtuell disk i alle WSL 2-distribusjonene.\n\n        Alternativer:\n            --vhd\n                Angir at &lt;Disk&gt; refererer til en virtuell harddisk.\n\n            --bare\n                Fest disken til WSL2, men ikke monter den.\n\n            --name &lt;Name&gt;\n                Monter disken med et egendefinert navn på monteringspunktet.\n\n            --type &lt;Type&gt;\n                Filsystem til bruk ved montering av disk, om ikke angitt settes det som standard til ext4.\n\n            --options &lt;Options&gt;\n                Flere monteringsalternativer.\n\n            --partition &lt;Index&gt;\n                Indeksen for partisjonen som skal monteres, hvis den ikke er angitt som standard for hele disken.\n\n    --set-default-version &lt;Version&gt;\n        Endrer standard installasjonsversjon for nye distribusjoner.\n\n    --shutdown\n        Avslutter umiddelbart alle kjørende distribusjoner og den virtuelle WSL 2\n        lightweight utility-maskinen.\n\n        Alternativer:\n            --force\n                Avslutt den virtuelle WSL 2-maskinen selv om en operasjon pågår. Kan føre til tap av data.\n\n    --status\n        Vis statusen for Windows-undersystem for Linux.\n\n    --unmount [Disk]\n        Demonterer og kobler fra en disk fra alle WSL2-distribusjoner.\n        Demonterer og løsner alle disker hvis de kalles uten argument.\n\n    --uninstall\n        Avinstallerer Windows-undersystem for Linux-pakken fra denne maskinen.\n\n    --update\n        Oppdaterer Windows-undersystem for Linux-pakken.\n\n        Alternativer:\n            --pre-release\n                Last ned en forhåndsversjon hvis tilgjengelig.\n\n    --version, -v\n        Vis versjonsinformasjon.\n\nArgumenter for behandling av distribusjoner i Windows-undersystem for Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Eksporterer distribusjonen til en tjærefil.\n        Filnavnet kan være - for stdout.\n\n        Alternativer:\n            --format &lt;Format&gt;\n                Angir eksportformatet. Støttede verdier: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        Importerer den angitte tjærefilen som en ny distribusjon.\n        Filnavnet kan være - for stdin.\n\n        Alternativer:\n            --version &lt;Version&gt;\n                Angir versjonen som skal brukes i den nye distribusjonen.\n\n            --vhd\n                Angir at den angitte filen er en .vhd- eller .vhdx-fil, ikke en tjærefil.\n                Denne operasjonen lager en kopi av VHD-filen på den angitte installasjonsplasseringen.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Importerer den angitte VHD-filen som en ny distribusjon.\n        Denne virtuelle harddisken må formateres med filsystemtypen ext4.\n\n    --list, -l [Options]\n        Viser distribusjoner.\n\n        Alternativer:\n            --all\n                Viser alle distribusjoner, inkludert distribusjoner som\n                for øyeblikket installeres eller avinstalleres.\n\n            --running\n                Viser kun distribusjoner som kjører for øyeblikket.\n\n            --quiet, -q\n                Viser bare distribusjonsnavn.\n\n            --verbose, -v\n                Viser detaljert informasjon om alle distribusjonene.\n\n            --online, -o\n                Viser en liste over tilgjengelige distribusjoner for installering med 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Angir distribusjonen som standard.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Endrer versjonen for den angitte distribusjonen.\n\n    --terminate, -t &lt;Distro&gt;\n        Avslutter den angitte distribusjonen.\n\n    --unregister &lt;Distro&gt;\n        Avregistrerer distribusjonen og sletter rotfilsystemet.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL-versjon: {}\nKernelversjon: {}\nWSLg-versjon: {}\nMSRDC-versjon: {}\nDirect3D-versjon: {}\nDXCore-versjon: {}\nWindows-versjon: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild-versjon: {}\nCommit: {}\nBuild-tid: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>Finner ikke den egendefinerte kjernen som er definert i {}: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>VHD-en for tilpassede kjernemoduler i {} ble ikke funnet: «{}».</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>Den egendefinerte systemdistribusjonen som er angitt i {} ble ikke funnet, eller har feil format.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Med enerett.\nHvis du vil ha informasjon om personvern vedrørende dette produktet, kan du gå til https://aka.ms/privacy.\n\nBruk: wslg.exe [Argument] [Alternativer...] [Kommandolinje]\n\nArgumenter:\n    --cd &lt;Directory&gt;\n        Angir den angitte mappen som gjeldende arbeidsmappe.\n        Hvis ~ brukes, brukes Linux-brukerens startbane. Hvis banen starter\n        med et /-tegn, blir den tolket som en absolutt Linux-bane.\n        Hvis ikke må verdien være en absolutt Windows-bane.\n\n    --distribution, -d &lt;Distro&gt;\n        Kjør den angitte distribusjonen.\n\n    --user, -u &lt;UserName&gt;\n        Kjør som angitt bruker.\n\n    --shell-type &lt;standard|login|none&gt;\n        Kjør den angitte kommandoen med den angitte skalltypen.\n\n    --help\n        Vis informasjon om bruk.\n\n    --\n        Send gjenstående kommandolinje slik den er.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Trykk på en tast for å fortsette...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Argumentet {} mangler et nødvendig parameter.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Eksport pågår. Dette kan ta noen minutter.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Import pågår. Dette kan ta noen minutter.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>GUI-programstøtte er deaktivert via {} eller /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Ser etter oppdateringer.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Denne distribusjonen er bare tilgjengelig fra Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount på ARM64 krever Windows versjon 27653 eller nyere.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Den nyeste versjonen av Windows-undersystem for Linux er allerede installert.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Oppdaterer Windows-undersystem for Linux til versjon: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Dette programmet krever Windows-undersystem for Linux valgfri komponent.\nInstaller den ved å kjøre: wsl.exe --install --no-distribution\nSystemet må kanskje startes på nytt slik at endringene kan tre i kraft.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Den angitte filen må ha filtypen {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Den angitte filen må ha filendelsen {} eller {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>Finner ikke VmSwitch {}. Tilgjengelige brytere: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Brolagt nettverk krever at wsl2.vmSwitch angis.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Kunne ikke hente distribusjonslisten fra {}. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Kan ikke koble til disken {} til WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Kan ikke endre størrelsen på disken.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Finner ingen verdi.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Windows-versjon {} støtter ikke den pakkede versjonen av Windows-undersystem for Linux.\nInstaller den nødvendige oppdateringen via Windows Update eller via: {}\nHvis du vil ha mer informasjon, kan du gå til https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Kjøring av feilsøkingsskallet krever kjøring wsl.exe som administrator.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Installasjonsprosessen for distribusjonen {} mislyktes med avslutningskoden: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Ugyldig GUID-format: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Ugyldig seksjonsnavn i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Ugyldig nøkkelnavn i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Forventet {} i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Ugyldig escape-tegn: {} i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Ukjent nøkkel {} i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Ugyldig heltallsverdi {} for nøkkelen {} i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Ugyldig IP-verdi {} for nøkkelen {} i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Ugyldig boolsk verdi {} for nøkkelen {} i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Ugyldig mac-adresse {} for nøkkelen {} i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Kan ikke åpne konfigurasjonsfilen {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Det oppstod feil under WSL-oppstart</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Kan ikke starte systembrukerøkten for {}. Se journalctl hvis du vil ha mer informasjon.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Åpne EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Denne distribusjonen inneholder ikke et standardnavn. Bruk --name til å velge distribusjonsnavnet.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Argumentene {} og {} kan ikke angis samtidig.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>Argumentet {} krever argumentet {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Ugyldig distribusjonsnavn: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Bruker eldre distribusjonsregistrering. Vurder å bruke en tar-basert distribusjon i stedet.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Eldre distribusjonsregistreringer støtter ikke --version argumentet.</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>Distribusjonen {} leveres av et overstyringsmanifest.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>Distribusjonshashen samsvarer ikke. Forventet: {}, faktisk hash: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Ugyldig heksadesimalstreng: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>{} støttes ikke ved installering av eldre distribusjoner.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Distribusjonen er installert. Den kan startes via wsl.exe -d {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Ugyldig minnestreng {} for .wslconfig-oppføringen {} i {}: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Kan ikke opprette swapdisken i {}: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Nestet virtualisering støttes ikke på denne maskinen.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Kan ikke opprette et nettverksendepunkt med adressen {}, tilordnet ny adresse: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Kan ikke opprette virtuelt nettverk med adresseområde: {}, opprettet nytt nettverk med området: {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>SIKKERMODUS AKTIVERT – mange funksjoner vil bli deaktivert</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Avspeilet nettverksmodus støttes ikke: {}.\nFaller tilbake til NAT-nettverk.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Linux Kernel versjon 5.10 eller nyere kreves</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows-versjon {}. {} har ikke de nødvendige funksjonene</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Brannmuren støtter ikke Hyper-V</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>DNS-tunnelering støttes ikke</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>Innstillingen wsl2.localhostForwarding har ingen effekt ved bruk av avspeilet nettverksmodus</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Det ble oppdaget en http-proxy-endring på verten. Start WSL på nytt for å bruke endringen.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>En localhost-proxykonfigurasjon ble oppdaget, men ikke avspeilet i Windows Subsystem for Linux. Windows Subsystem for Linux i NAT-modus støtter ikke localhost-proxyer.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>En IPv6-proxykonfigurasjon ble oppdaget, men ikke avspeilet i WSL. WSL i NAT-modus støtter ikke IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>En IPv6-proxy-konfigurasjon for localhost ble oppdaget, men er ikke avspeilet i WSL. WSL støtter ikke IPv6-proxyer for localhost.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Det oppstod en uventet feil under forsøk på å løse proxy-innstillinger. Proxy-innstillinger ble ikke speilet til WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Det oppstod en feil under tilgang til registeret. Bane: {}. Feil: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Kan ikke starte reléprosessen for localhost. Feil: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Kan ikke starte virtuelt nettverk . Installer den valgfrie komponenten Virtual Machine Platform ved å kjøre: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Kan ikke konfigurere nettverk (networkingMode {}), som faller tilbake til networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Kan ikke aktivere Windows-komponenten {} (feilkode {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>En uopprettelig feil ble returnert av plugin-modulen {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Programtillegget returnerte en uopprettelig feil: {}. Feilmelding: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Programtillegget {} krever en nyere versjon av WSL. Kjør wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>.wslconfig-innstillingen {} er deaktivert av datamaskinpolicyen.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>Feilsøkingsskallet er deaktivert av datamaskinpolicyen.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount er deaktivert av datamaskinpolicyen.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>WSL1 er deaktivert av datamaskinpolicyen.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Kjør wsl.exe --set-version {} 2 for å oppgradere til WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL fullfører en oppgradering ...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Oppdateringen mislyktes (feilkode: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Avinstalleringen mislyktes (feilkode: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Loggfil: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Kan ikke fjerne MSIX-pakken (feil: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Kan ikke installere MSIX-pakken (feil: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Valgfrie komponenter som kreves for å kjøre WSL, er ikke installert.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Installer manglende komponenter.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Den importerte filen er ikke en gyldig Linux-distribusjon.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Trykk på en tast for å avslutte...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>En ny versjon av Windows-undersystem for Linux er tilgjengelig.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Oppdater til versjon {} eller vis produktmerknadene nedenfor.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Oppdater</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Vis dokumenter</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Operasjonen kan ikke fullføres fordi VHD-en er i bruk. Slik tvinger du WSL til å stoppe: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} er ikke en gyldig boolsk verdi, &lt;sann|usann&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Sparse VHD støttes bare på WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>Kjøring av WSL som lokalt system støttes ikke.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Ytelsestips:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Hvis du bruker en I/U-intensiv operasjon som {} på Windows-stasjonene, får du dårlig ytelse. Vurder å flytte prosjektfilene til Linux-filsystemet for å få bedre ytelse. Klikk nedenfor for å finne ut mer.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Vis dokumenter</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Ikke vis flere ganger</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>Den virtuelle WSL2-maskinen krasjet.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>Stakksporingen er lagret i: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Distribusjonen startet ikke. Feilkode: {}, feiltrinn: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Distribusjonen startet ikke fordi den virtuelle disken er skadet.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Duplisert konfigurasjonsnøkkel {} i {}:{} (Nøkkelkonflikt: {} i {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Ugyldig verdi {} for konfigurasjonsnøkkel {} i {}:{} (gyldige verdier: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Venter på at OOBE-kommandoen skal fullføres for distribusjonen {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Kan ikke lese egenskapen {} fra distribusjonen {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Dette ser ut som en VHD-fil. Bruk --vhd til å importere en VHD i stedet for en tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Kan ikke installere {} fra Microsoft Store: {}\nForsøker å laste ned...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Ingen standarddistribusjon er konfigurert. Angi en distribusjon som skal installeres.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p er deaktivert, og faller tilbake til 9p med vsock-transport.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>WSL-installasjonen ser ut til å være skadet (feilkode: {}).\nTrykk en tast for å reparere WSL, eller CTRL-C for å avbryte.\nDenne meldingen blir tidsavbrutt om 60 sekunder.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Kan ikke analysere terminalprofil under registrering av distribusjon: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Ugyldig størrelse: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nFeilkode: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Ugyldig JSON-dokument. Analyseringsfeil: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors kan ikke overskride antall logiske prosessorer på systemet ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Kan ikke spørre etter nettverksmodus</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Tilpassede kjernemoduler ble levert uten å angi en tilpasset kjerne. Se https://aka.ms/wslcustomkernel for mer informasjon.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>DNS-tunnelering er deaktivert på grunn av et nåværende kompatibilitetsproblem med Global sikker tilgang-klienten.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Sparsom VHD-støtte er deaktivert på grunn av potensiell dataskade.\nHvis du vil tvinge en distribusjon til å bruke en sparsom VHD, kan du kjøre:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Kan ikke montere {}. Se dmesg hvis du vil ha mer informasjon.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Behandling av /etc/fstab med mount -a mislyktes.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Det oppstod en feil under montering av distribusjonsdisken. Den ble montert som skrivebeskyttet.\nSe gjenopprettingsinstruksjoner om: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Kan ikke oversette {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Noe gikk galt. Prøv på nytt senere.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Om</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Om Windows-undersystem for Linux-innstillinger</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Med enerett.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Windows-undersystem for Linux innstillinger</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Windows-undersystem for Linux Innstillinger lar utviklere administrere .wslconfig filen ved hjelp av et GUI-basert program.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Automatisk minnegjenbruk</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Frigir automatisk hurtigbufret minne etter å ha oppdaget inaktiv CPU-bruk. Angi gradvis for langsom frigivelse, og dropcache for umiddelbar frigivelse av hurtigbufret minne.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatisk minnefrigjøring.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Frigir automatisk hurtigbufret minne etter å ha oppdaget inaktiv CPU-bruk. Angi gradvis for langsom frigivelse, og dropcache for umiddelbar frigivelse av hurtigbufret minne.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Automatisk proxy aktivert</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Gjør det mulig for WSL å bruke HTTP-proxyinformasjon for Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatisk proxy aktivert.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gjør det mulig for WSL å bruke HTTP-proxyinformasjon for Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Bruk beste forsøk på DNS-analyse</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.dnsTunneling er satt til sann. Når satt til sann, trekker Windows ut spørsmålet fra DNS-forespørselen og prøver å løse det, og de ukjente postene ignoreres.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Bruk DNS-analyse med beste innsats.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.dnsTunneling er satt til sann. Når satt til sann, trekker Windows ut spørsmålet fra DNS-forespørselen og prøver å løse det, og de ukjente postene ignoreres.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Egendefinert kjerne</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>En absolutt Windows-bane til en egendefinert Linux-kjerne.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bla gjennom kjerner</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Egendefinert kjerne</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En absolutt Windows-bane til en egendefinert Linux-kjerne.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Egendefinerte kjernemoduler</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>En absolutt Windows-bane til modul-VHD for en egendefinert Linux-kjerne.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bla gjennom kjernemoduler</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Egendefinerte kjernemoduler</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En absolutt Windows-bane til modul-VHD for en egendefinert Linux-kjerne.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Egendefinert systemdeaktivering</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bla gjennom distroer</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Angi en bane til en VHD som lastes inn som en egendefinert systemdeaktivering, først og fremst brukt til å drive GUI-apper i WSL. [Mer informasjon om systemdespsjoner her].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Egendefinert systemdeaktivering</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Angi en bane til en VHD som lastes inn som en egendefinert systemdistro, hovedsakelig brukt til å drive GUI-apper i WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Aktiver feilsøkingskonsoll</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Boolsk for å aktivere et utdatakonsollvindu som viser innholdet i dmesg ved starten av en WSL 2-utgående forekomst.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktiver feilsøkingskonsoll.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolsk verdi for å aktivere et utdatakonsollvindu som viser innholdet i diagnosemeldinger ved start av en WSL 2-distroforekomst.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Standard VHD-størrelse</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Standard maksimal størrelse for den utvidbare virtuelle WSL-harddisken (VHD) bare for nyopprettede distribusjoner.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Tilbakestill størrelse</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Standard VHD-størrelse</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Standard maksimal størrelse, angitt i megabyte, bare for den utvidbare virtuelle WSL-harddisken (VHD) for nylig opprettede distribusjoner.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Utvikler</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentasjon</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Endre DrvFS-modus</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Endrer implementering av filtilgang på tvers av operativsystemer i WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS-proxy aktivert</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.networkingMode er satt til NAT. Boolsk for å informere WSL om å konfigurere DNS-serveren i Linux til NAT på verten. Hvis du setter til Usann, speiler dette DNS-servere fra Windows til Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-proxy aktivert.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.networkingMode er satt til NAT. Boolean for å informere WSL om å konfigurere DNS-serveren i Linux til NAT på verten. Hvis du setter til Usann, speiler dette DNS-servere fra Windows til Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>DNS-tunnelering aktivert</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Endrer hvordan DNS-forespørsler er proxy fra WSL til Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-tunnelering aktivert.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Endrer hvordan DNS-forespørsler er proxy fra WSL til Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Filsystem</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Aktiver GUI-programmer</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Boolsk for å aktivere eller deaktivere støtte for GUI-programmer ([WSLg]) i WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktiver GUI-programmer.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolsk for å aktivere eller deaktivere støtte for GUI-programmer (kjent som WSL g) i WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Aktiver ytelsestellere for maskinvare</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Aktiverer maskinvareytelsestellere for Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktiver maskinvareytelsestellere.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Aktiverer maskinvareytelsestellere for Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Hyper-V-brannmur aktivert</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Aktiverer Hyper-V-brannmur som tillater reglene for Windows-brannmur, i tillegg til regler som er spesifikke for Hyper-V-trafikk, for å filtrere WSL-nettverkstrafikk.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hyper-V-brannmur aktivert.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Aktiverer Hyper-V-brannmur som tillater reglene for Windows-brannmur, i tillegg til regler som er spesifikke for Hyper-V-trafikk, for å filtrere WSL-nettverkstrafikk.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Tilbakekobling for vertsadresse</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.networkingMode er avspeilet. Når satt til Sann, tillates beholderen å koble til verten, eller verten for å koble til beholderen, med en IP-adresse som er tilordnet verten. Vær oppmerksom på at 127.0.0.1-tilbakekoblingsadressen alltid kan brukes . Dette alternativet gjør det mulig å bruke alle ekstra tilordnede lokale IP-adresser også.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Vertsadressetilbakekobling.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.networkingMode er satt til avspeilet. Når satt til Sann, tillates beholderen å koble til verten, eller verten for å koble til beholderen, med en IP-adresse som er tilordnet verten. Vær oppmerksom på at 127.0.0.1-tilbakekoblingsadressen alltid kan brukes . Dette alternativet gjør det mulig å bruke alle ekstra tilordnede lokale IP-adresser også.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Ignorerte porter</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.networkingMode er satt til avspeilet. Angir hvilke porter Linux-programmer kan binde til som ikke videresendes eller vurderes automatisk i Windows. Skal formateres i en kommadelt liste, for eksempel 3000,9000,9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Tilbakestill porter</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ignorerte porter</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.networkingMode er satt til avspeilet. Angir hvilke porter Linux-programmer kan binde til som ikke videresendes eller vurderes automatisk i Windows. Skal formateres i en kommadelt liste, for eksempel 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Første tidsavbrudd for automatisk proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.autoProxy er satt til sann. Konfigurerer hvor lenge (i millisekunder) WSL skal vente på henting av HTTP-proxyinformasjon når du starter en WSL-beholder. Hvis proxy-innstillingene løses etter dette tidspunktet, må WSL-forekomsten startes på nytt for å bruke de hentede proxy-innstillingene.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Tilbakestill tidsavbrudd</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Første tidsavbrudd for automatisk proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gjelder bare når wsl2.autoProxy er satt til sann. Konfigurerer hvor lenge WSL i millisekunder venter på å hente HTTP-proxyinformasjon når en WSL-beholder startes. Hvis proxy-innstillingene løses etter dette tidspunktet, må WSL-forekomsten startes på nytt for å bruke de hentede proxy-innstillingene.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Kjernekommandolinje</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Flere kjernekommandolinjeargumenter.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Aktiver videresending av localhost</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Boolsk verdi som angir om porter som er bundet til jokertegn eller localhost i WSL 2 VM, skal kunne kobles til fra verten via localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktiver videresending av localhost.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolsk verdi som angir om porter som er bundet til jokertegn eller localhost i den virtuelle WSL 2-maskinen, skal kunne kobles til fra verten via localhost, colon, port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Minne og prosessor</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Minnestørrelse</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Hvor mye minne som skal tilordnes WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Tilbakestill størrelse</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Minnestørrelse</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hvor mye minne, angitt i megabyte, som skal tilordnes WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} millisekunder</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Aktiver nestet virtualisering</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Boolsk for å aktivere eller deaktivere nestet virtualisering, slik at andre nestede virtuelle maskiner kan kjøres i WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktiver nestet virtualisering.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boolsk for å aktivere eller deaktivere nestet virtualisering, slik at andre nestede virtuelle maskiner kan kjøres i WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Nettverksmodus</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Angir nettverksmodus for WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Nettverksmodus.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Angir nettverksmodus for WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Nettverk</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Du kan jobbe på tvers av operativsystemer med alle filene dine!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Filtilgang på tvers av operativsystemer</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Få tilgang til Windows-filer fra Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Du kan få tilgang til Windows-filer fra Linux ved å navigere til /mnt og deretter til Windows-stasjonsbokstaven, slik som dette eksemplet for C-stasjonen:\n\ncd /mnt/c</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Få tilgang til Linux-filer med Filutforsker</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Du kan vise Linux-filene fra Filutforsker ved å navigere til \\\\wsl.localhost\\ eller klikke på Linux-ikonet.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Start Windows-filer og -programmer fra WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Selv når du bruker WSL, kan du kjøre filer som kan kjøres i Windows direkte fra feilsøk. Prøv å kjøre\npowershell.exe /c start . for å åpne Filutforsker i gjeldende mappe.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Få tilgang til Linux-nettverksapper fra Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Hvis du bygger en nettverksapp (for eksempel en app som kjører på en NodeJS- eller SQL-server) i Linux-distribusjonen, kan du få tilgang til den fra en Windows-app (for eksempel Edge- eller Chrome-nettleseren) ved hjelp av localhost (på vanlig måte). Dette betyr at hvis du startet en Linux-server som lytter til port 3000, kan du gå til [http://localhost:3000] i Edge på Windows for å få tilgang til den.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Nettverk med avspeilet modus</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL inkluderer også en ny nettverksmodus, kalt avspeilet modus som legger til avanserte funksjoner som IPv6-støtte og muligheten til å få tilgang til nettverksprogrammer i lokalnettet.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om filtilgang på tvers av operativsystemer</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Velkommen til Windows-undersystem for Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL er en flott måte å prøve ut forskjellige Linux-distribusjoner på.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Distro-behandling</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om grunnleggende WSL-kommandoer</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om import av Linux-distribusjoner</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Vis kommando for installerbare WSL-distroer</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe -l -o</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Installer en navngitt WSL Distro-kommando</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Vis kommando for tilgjengelige WSL-distroer</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop fungerer bra med WSL for å hjelpe deg med å utvikle Linux-beholdere.\n\nNoen av fordelene med å bruke Docker Desktop med WSL er:\n\n• Du kan kjøpe Docker-kommandoer i WSL eller i Windows med samme Docker-daemon og -bilder.\n• Du kan dele filer og mapper mellom Windows og Linux sømløst med automatisk montering av Windows-stasjoner i WSL.\n• Du kan bruke dine foretrukne Windows-verktøy og -redigeringsprogrammer til å jobbe på Linux-kode og -filer og omvendt, takket være interoperabiliteten til WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker Desktop-integrasjon</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om hvordan du bruker WSL med Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Med Windows-undersystemet for Linux (WSL) kan du kjøre favorittverktøyene, -programmene og -arbeidsflytene i Linux direkte på Windows.\n\nBruk litt tid på å forhåndsvise noen av fellesskapets favorittfunksjoner, eller se på den omfattende dokumentasjonen.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Velkommen til WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Anbefalte fremgangsmåter for oppsett</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Komme i gang med Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentasjon for Windows-undersystem for Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Du kan bruke grafikkbaserte Linux-programmer med opprinnelige Windows-samhandlinger som alt-tab, starte startmeny, feste oppgavelinje og støtte for klipp ut og lim inn.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>GUI-apper</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL kan utnytte Windows GPU for maskinlæringsarbeidsflyter\n\nLinux-binærfiler som kjører i WSL, kan automatisk bruke GPU i Windows for å øke hastigheten til ytelsen. Du trenger bare å installere og kjøre disse arbeidsflytene på samme måte som på en vanlig Linux-maskin. Det finnes mange ulike måter å komme i gang fra å kjøre CUDA i en docker-beholder hvis du har et NVIDIA-grafikkort, til å kjøre PyTorch eller TensorFlow med DirectML på AMD-, Intel- eller NVIDIA-grafikkortet. Se Komme i gang-guiden nedenfor for mer informasjon.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>GPU-akselerasjon</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om GPU-akselerasjon</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om GUI-apper i WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Her er en liste over apper du kan prøve (Du kan installere alle disse i Ubuntu ved å skrive inn sudo apt install &lt;Navnet på appen&gt;)\n\n    • gedit – grunnleggende tekstredigeringsprogram\n    • audacity – ta opp og rediger lydfiler\n    • blender – lag 3D-animasjoner og visualiseringer\n    • gimp – rediger bilder\n    • nautilus – en Linux-filutforsker\n    • vlc – videospiller</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Du kan enkelt få tilgang til nettverksapper på tvers av Windows- og Linux-operativsystemer.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Nettverksintegrasjon</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om nettverksprogrammer</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om nettverk med avspeilet modus</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Du kan bruke WSL som utviklingsmiljø på heltid direkte fra VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS Code-integrasjon</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om hvordan du bruker WSL med VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Slik installerer du</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Etter at du har installert VS Code, kan du installere den eksterne WSL-utvidelsen fra Windows Terminal:\n\ncode –install-extension ms-vscode-remote.remote-wsl</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Åpne et WSL-prosjekt i Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>For å åpne et prosjekt i VS Code WSL-distribusjonen, åpner du distribusjonens kommandolinje og kjører code . for å åpne en prosjektfil.\n\nDu kan også åpne flere eksterne alternativer for VS Code gjennom kommandopaletten i selve VS Code. Trykk på Skift+Ctrl+P på tastaturet for å åpne kommandopaletten, og skriv inn «Remote-WSL» for å se en liste over de eksterne alternativene for VS Code, slik at du kan åpne mappen på nytt i en ekstern økt, angi hvilken distribusjon du vil åpne, og mer.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Brukerveiledning</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Valgfrie funksjoner</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Personvernerklæring</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Prosessorantall</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Hvor mange logiske prosessorer som skal tilordnes WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Tilbakestill antall</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Prosessorantall</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hvor mange logiske prosessorer som skal tilordnes WSL 2 VM.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Beslektede koblinger</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Produktmerknader</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Aktiver Sikker modus</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Kjør WSL i Sikkermodus som deaktiverer mange funksjoner og er ment å brukes til å gjenopprette distribusjoner som er i ugyldige tilstander.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktiver sikkermodus.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Kjør WSL i Sikkermodus som deaktiverer mange funksjoner og er ment å brukes til å gjenopprette distribusjoner som er i ugyldige tilstander.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Om</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Utvikler</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Distro-håndtering</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Skrivebordsintegrering for Docker</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Filsystem</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Generelt</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Grafikkbehandlingsenhet-akselerasjon</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>Visuelt grensesnitt-apper</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Start wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Minne og prosessor</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Nettverk</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Nettverksintegrasjon</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Velkommen til WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Valgfrie funksjoner</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Innstillinger</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS Code-integrasjon</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Hva er nytt</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Arbeider på tvers av filsystemer</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problemer</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Aktiver sparsommelig VHD som standard</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>En nyopprettet VHD settes til spredt automatisk når den er aktivert.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktiver sparse VHD som standard.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En nyopprettet VHD settes til spredt automatisk når den er aktivert.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Bytt filplassering</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>En absolutt Windows-bane til den virtuelle harddisken for bytting.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bla gjennom vekslefiler</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Bytt filplassering</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En absolutt Windows-bane til den virtuelle harddisken for bytting.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Bytt størrelse</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Hvor mye bytteplass som skal legges til WSL 2 VM, 0 for ingen byttingsfil. Bytt lagring er diskbasert RAM som brukes når minnebehovet overskrider grensen på maskinvareenhet.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Tilbakestill størrelse</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Bytt størrelse</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hvor mye veksleplass, angitt i megabyte, som skal legges til VM-en for WSL 2. 0 for ingen vekslefil. Vekslelagring er diskbasert RAM som brukes når minnebehovet overskrider grensen på maskinvareenheten.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Aktiver VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Bruk virtiofs i stedet for plan 9 for å få tilgang til vertsfiler, noe som øker hastigheten.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Aktiver Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Aktiverer montering av 9P-filsystemet fra verten ved hjelp av virtio-transport.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Tidsavbrudd for inaktiv virtuell maskin</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Antall millisekunder en virtuell maskin er inaktiv, før den avsluttes.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Tilbakestill tidsavbrudd</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tidsavbrudd for inaktiv virtuell maskin</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Antall millisekunder en virtuell maskin er inaktiv, før den avsluttes.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Bygg, kjør, feilsøk og profiler apper som kjører på WSL fra Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Kjør og feilsøk apper på WSL fra Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om WSL i Visual Studio for .NET-utviklere</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Mer informasjon om Visual Studio og WSL for C++-utviklere</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Du kan enkelt kjøre og feilsøke .NET Core- og tverrplattform C++-apper i Linux uten å forlate Visual Studio ved hjelp av Windows-undersystem for Linux (WSL). Som tverrplattformutvikler kan du bruke denne metoden som en enkel måte å teste flere av målmiljøene på.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studio-integrasjon</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studio-integrasjon</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/nl-NL/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Windows-subsysteem voor Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Met Windows Subsystem for Linux kunnen ontwikkelaars een GNU/Linux-omgeving uitvoeren, inclusief de meeste opdrachtregelprogramma's, hulpprogramma's en toepassingen, rechtstreeks in Windows, ongewijzigd, zonder de overhead van een traditionele virtuele machine of dualboot-installatie.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>De schijf kan niet worden losgekoppeld: {}. Voer 'dmesg' uit in WSL2 voor meer informatie.\nVoer wsl.exe {} uit om WSL2 te dwingen de schijf te stoppen en los te koppelen.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>De schijf {} is al gekoppeld.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Dat volume is al gekoppeld in WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Er is al een schijf met die naam gekoppeld; ontkoppel de schijf of kies een nieuwe naam en probeer het opnieuw.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>De opgegeven koppelingsnaam bevat een ongeldig /-teken. Probeer het opnieuw zonder het ongeldige teken.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>De schijf is gekoppeld als /mnt/wsl/{}.\nOpmerking: de locatie is anders als u de instelling automount.root hebt gewijzigd in /etc/wsl.conf.\nAls u de schijf wilt ontkoppelen en ontkoppelen, voert u wsl.exe {} {} uit.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>De schijf is gekoppeld, maar kan niet worden gekoppeld: {}.\nVoer 'dmesg' uit in WSL2 voor meer informatie.\nAls u de schijf wilt ontkoppelen, voert u wsl.exe {} {} uit.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Hier volgt een lijst met geldige distributies die kunnen worden geïnstalleerd.\nInstalleren met wsl.exe {} &lt;Distro&gt;.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>De distributienaam is al ingesteld.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>De opgegeven installatielocatie is al in gebruik.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Er bestaat al een distributie met de opgegeven naam. Gebruik --name om een andere naam te kiezen.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Er is geen distributie met de opgegeven naam.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Beheerderstoegang is vereist om een schijf te koppelen.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Het exporteren van de distributie is mislukt.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Eenmalige upgrade uitvoeren van het Windows-subsysteem voor Linux-bestandssysteem voor deze distributie...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Kan niet starten omdat een ander exemplaar zonder verhoogde bevoegdheden wordt uitgevoerd.  Instanties met verhoogde bevoegdheden en niet-verhoogde bevoegdheden mogen niet tegelijkertijd worden uitgevoerd.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Het importeren van de distributie is mislukt.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Optioneel Windows-onderdeel installeren: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Downloaden: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Installeren: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Distributie importeren</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} is gedownload.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Windows-subsysteem voor Linux hervat een vorige installatie...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Ongeldige distributienaam: {}.\nGebruik wsl.exe --list --online' om een lijst met geldige distributies op te halen.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Het Windows-subsysteem voor Linux exemplaar is beëindigd.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Ongeldig opdrachtregelargument: {}\nGebruik {} --help' om een lijst met ondersteunde argumenten op te halen.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>Voor het opdrachtregelargument {} is een waarde vereist.\nGebruik {} --help' om een lijst met ondersteunde argumenten op te halen.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Niet-ondersteunde console-instellingen. Als u deze functie wilt gebruiken, moet de verouderde console zijn uitgeschakeld.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} is geen geldig geheel getal.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Er wordt een installatie, verwijdering of conversie uitgevoerd voor deze distributie.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>{} starten...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Kan niet starten omdat een ander exemplaar wordt uitgevoerd met verhoogde bevoegdheden.  Instanties met verhoogde bevoegdheden en niet-verhoogde bevoegdheden mogen niet tegelijkertijd worden uitgevoerd.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Windows-subsysteem voor Linux heeft geen geïnstalleerde distributies.\nU kunt dit oplossen door een distributie te installeren met de onderstaande instructies:\n\nGebruik 'wsl.exe --list --online' om een lijst te maken van beschikbare distributies\nen 'wsl.exe --install &lt;Distro&gt;' om te installeren.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Er zijn geen actieve distributies.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Standaard)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Windows-subsysteem voor Linux distributies:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Standaarddistributie: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Standaardversie: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Afmelden.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Gebruiker niet gevonden.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Ga naar https://aka.ms/wsl2 voor informatie over de belangrijkste verschillen met WSL 2</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Conversie wordt uitgevoerd. Dit kan enkele minuten duren.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>De distributie is al de aangevraagde versie.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>WSL 2 wordt niet ondersteund door de verouderde distributie.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>WSL2 kan niet worden gestart omdat virtualisatie niet is ingeschakeld op deze machine.\nZorg dat het optionele onderdeel 'Platform voor virtuele machine' is ingeschakeld en dat virtualisatie aanstaat in de firmware-instellingen van je computer.\n\nSchakel 'Platform voor virtuele machine' in door het volgende uit te voeren: wsl.exe --install --no-distribution\n\nMeer informatie vind je op https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Er zijn te veel virtuele harde schijven of fysieke schijven gekoppeld.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD is al gekoppeld via wsl.exe --mount, Ontkoppel de schijf voordat u start.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 wordt niet ondersteund met de huidige computerconfiguratie.\nSchakel het optionele onderdeel 'Windows Subsystem for Linux' in om WSL1 te gebruiken.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Deze bewerking wordt alleen ondersteund door WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Gebruik:\n    --networking-mode\n       Huidige netwerkmodus weergeven.\n\n    --msal-proxy-path\n        Geef het pad naar de MSAL-proxytoepassing weer.\n\n    --vm-id\n        Geef de WSL VM-id weer.\n\n    --version\n        De versie van het WSL-pakket weergeven.\n\n    -n\n        Druk geen nieuwe regel af.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Gebruik:\n    -a\n        Resultaat forceren naar absolute padindeling.\n    -u\n        Vertalen van een Windows-pad naar een WSL-pad (standaard).\n    -w\n        Vertalen van een WSL-pad naar een Windows-pad.\n    -m\n        Vertalen van een WSL-pad naar een Windows-pad, met '/' in plaats van '\\\\'\n\nVoorbeeld: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Voert beheerbewerkingen uit op Windows-subsysteem voor Linux\n\nGebruik:\n    /l, /list [Optie]\n        Hiermee worden geregistreerde distributies opgenoemd.\n        /all - Optioneel alle distributies vermelden, inclusief distributies die\n               worden momenteel geïnstalleerd of verwijderd.\n\n        /running - Alleen distributies weergeven die momenteel worden uitgevoerd.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Hiermee stelt u de distributie als standaard in.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Hiermee wordt de distributie beëindigd.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Hiermee maakt u de registratie van de distributie ongedaan en wordt het hoofdbestandssysteem verwijderd.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Alle rechten voorbehouden.\nGa naar https://aka.ms/privacy voor privacyinformatie over dit product.\n\nGebruik: wsl.exe [Argument] [Opties...] [Opdrachtregel]\n\nArgumenten voor het uitvoeren van binaire Linux-bestanden:\n\n    Als er geen opdrachtregel is opgegeven, wordt de standaardshell gestart met wsl.exe.\n\n    --exec, -e &lt;CommandLine&gt;\n        De opgegeven opdracht uitvoeren zonder de standaardshell voor Linux te gebruiken.\n\n    --shell-type &lt;standard|login|none&gt;\n        De opgegeven opdracht uitvoeren met het opgegeven shelltype.\n\n    --\n        De resterende opdrachtregel in de huidige staat doorgeven.\n\nOpties:\n    --cd &lt;Directory&gt;\n        De opgegeven map instellen als de huidige werkmap.\n        Als ~ wordt gebruikt, wordt het basispad van de Linux-gebruiker gebruikt. Als het pad begint\n        met een /-teken, wordt het geïnterpreteerd als een absoluut Linux-pad.\n        Anders moet de waarde een absoluut Windows-pad zijn.\n\n    --distribution, -d &lt;DistroName&gt;\n        De opgegeven distributie uitvoeren.\n\n    --distribution-id &lt;DistroGuid&gt;\n        De opgegeven distributie-id uitvoeren.\n\n    --user, -u &lt;UserName&gt;\n        Uitvoeren als de opgegeven gebruiker.\n\n    --system\n        Een shell voor de systeemdistributie starten.\n\nArgumenten voor het beheren van het Windows-subsysteem voor Linux:\n\n    --help\n        Gebruiksgegevens weergeven.\n\n    --debug-shell\n        Een WSL2-foutopsporingsshell voor diagnostische doeleinden openen.\n\n    --install [Distro] [Options...]\n        Een Windows-subsysteem voor Linux-distributie installeren.\n        Gebruik 'wsl.exe --list --online' voor een lijst met geldige distributies.\n\n        Opties:\n            --enable-wsl1\n                WSL1-ondersteuning inschakelen.\n\n            --fixed-vhd\n                Een schijf met een vaste grootte maken om de distributie op te slaan.\n\n            --from-file &lt;Path&gt;\n                Een distributie installeren vanaf een lokaal bestand.\n\n            --legacy\n                Het verouderde distributiemanifest gebruiken.\n\n            --location &lt;Location&gt;\n                Het installatiepad voor de distributie instellen.\n\n            --name &lt;Name&gt;\n                De naam van de distributie instellen.\n\n            --no-distribution\n                Door alleen de vereiste optionele onderdelen te installeren, wordt geen distributie geïnstalleerd.\n\n            --no-launch, -n\n                De distributie niet na de installatie starten.\n\n            --version &lt;Version&gt;\n                Specificeert de versie die moet worden gebruikt voor de nieuwe distributie.\n\n            --vhd-size &lt;MemoryString&gt;\n                Specificeert de grootte van de schijf om de distributie op te slaan.\n\n            --web-download\n                De distributie downloaden van internet in plaats van Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Hiermee worden specifieke opties voor de distributie gewijzigd.\n\n        Opties:\n            --move &lt;Location&gt;\n                De distributie naar een nieuwe locatie verplaatsen.\n\n            --set-sparse, -s &lt;true|false&gt;\n                De VHD van de distributie instellen op sparse, zodat schijfruimte automatisch opnieuw kan worden geclaimd.\n\n            --set-default-user &lt;Username&gt;\n                De standaardgebruiker van de distributie instellen.\n\n            --resize &lt;MemoryString&gt;\n                De grootte van de schijf van de distributie wijzigen in de opgegeven grootte.\n\n    --mount &lt;Disk&gt;\n        Hiermee wordt een fysieke of virtuele schijf in alle WSL 2-distributies toegevoegd en gekoppeld.\n\n        Opties:\n            --vhd\n                Hiermee geef je aan dat &lt;Disk&gt; naar een virtuele harde schijf verwijst.\n\n            --bare\n                De schijf toevoegen aan WSL2, maar niet koppelen.\n\n            --name &lt;Name&gt;\n                De schijf koppelen met een aangepaste naam voor het koppelpunt.\n\n            --type &lt;Type&gt;\n                Het bestandssysteem dat moet worden gebruikt bij het koppelen van een schijf wordt, indien niet opgegeven, standaard ingesteld op ext4.\n\n            --options &lt;Options&gt;\n                Aanvullende koppelingsopties.\n\n            --partition &lt;Index&gt;\n                De index van de partitie die moet worden gekoppeld wordt, indien niet opgegeven, standaard ingesteld op de hele schijf.\n\n    --set-default-version &lt;Version&gt;\n        Hiermee wijzig je de standaardinstallatieversie voor nieuwe distributies.\n\n    --shutdown\n        Hiermee beëindig je onmiddellijk alle actieve distributies en de WSL 2\n        lichtgewicht virtuele machine voor hulpprogramma's.\n\n        Opties:\n            --force\n                De WSL 2-VM beëindigen, zelfs als er een bewerking wordt uitgevoerd. Dit kan gegevensverlies veroorzaken.\n\n    --status\n        De status van het Windows-subsysteem voor Linux weergeven.\n\n    --unmount [Disk]\n        Hiermee wordt een schijf ontkoppeld en losgemaakt van alle WSL2-distributies.\n        Hiermee worden alle schijven ontkoppeld en losgemaakt bij een aanroep zonder argument.\n\n    --uninstall\n        Hiermee verwijder je het Windows-subsysteem voor Linux-pakket van deze computer.\n\n    --update\n        Het Windows-subsysteem voor Linux-pakket bijwerken.\n\n        Opties:\n            --pre-release\n                Een voorlopige versie downloaden, indien beschikbaar.\n\n    --version, -v\n        Versiegegevens weergeven.\n\nArgumenten voor het beheren van distributies in het Windows-subsysteem voor Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Opties]\n        Hiermee exporteer je de distributie naar een TAR-bestand.\n        De bestandsnaam kan - zijn voor stdout.\n\n        Opties:\n            --format &lt;Format&gt;\n                Specificeert de exportindeling. Ondersteunde waarden: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Opties]\n        Hiermee importeer je het opgegeven TAR-bestand als een nieuwe distributie.\n        De bestandsnaam kan - zijn voor stdin.\n\n        Opties:\n            --version &lt;Version&gt;\n                Hiermee geef je de versie op die moet worden gebruikt voor de nieuwe distributie.\n\n            --vhd\n                Hiermee geef je op dat het opgegeven bestand een VHD- of VHDX-bestand is en geen TAR-bestand.\n                Met deze bewerking wordt een kopie gemaakt van het VHD-bestand op de opgegeven installatielocatie.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Hiermee importeer je het opgegeven VHD-bestand als een nieuwe distributie.\n        Deze virtuele harde schijf moet zijn geformatteerd met het ext4-type bestandssysteem.\n\n    --list, -l [Opties]\n        Hiermee wordt een lijst met distributies weergegeven.\n\n        Opties:\n            --all\n                Alle distributies weergeven, inclusief distributies die\n                momenteel worden geïnstalleerd of verwijderd.\n\n            --running\n                Alleen distributies weergeven die momenteel worden uitgevoerd.\n\n            --quiet, -q\n                Alleen distributienamen weergeven.\n\n            --verbose, -v\n                Gedetailleerde informatie over alle distributies weergeven.\n\n            --online, -o\n                Geeft een lijst weer met beschikbare distributies voor installatie met 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Hiermee stel je de distributie als standaard in.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Hiermee wijzig je de versie van de opgegeven distributie.\n\n    --terminate, -t &lt;Distro&gt;\n        Hiermee wordt de opgegeven distributie beëindigd.\n\n    --unregister &lt;Distro&gt;\n        De registratie van de distributie ongedaan maken en het hoofdbestandssysteem verwijderen.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL-versie: {}\nKernelversie: {}\nWSLg-versie: {}\nMSRDC-versie: {}\nDirect3D-versie: {}\nDXCore-versie: {}\nWindows-versie: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild-versie: {}\nDoorvoeren: {}\nBuildtijd: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>De aangepaste kernel die is opgegeven in {} is niet gevonden: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>De aangepaste kernelmodules VHD in {} is niet gevonden: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>De aangepaste systeemdistributie die is opgegeven in {} is niet gevonden of heeft niet de juiste indeling.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Alle rechten voorbehouden.\nGa naar https://aka.ms/privacy voor privacyinformatie over dit product.\n\nGebruik: wslg.exe [Argument] [Opties...] [CommandLine]\n\nArgumenten:\n    --cd &lt;Map&gt;\n        Hiermee stelt u de opgegeven map in als de huidige werkmap.\n        Als ~ wordt gebruikt, wordt het startpad van de Linux-gebruiker gebruikt. Als het pad begint\n        met een /-teken, wordt het geïnterpreteerd als een absoluut Linux-pad.\n        Anders moet de waarde een absoluut Windows-pad zijn.\n\n    --distribution, -d &lt;Distro&gt;\n        Voer de opgegeven distributie uit.\n\n    --user, -u &lt;Gebruikersnaam&gt;\n        Uitvoeren als de opgegeven gebruiker.\n\n    --shell-type &lt;standard|login|none&gt;\n        De opgegeven opdracht uitvoeren met het opgegeven shelltype.\n\n    --help\n        Gebruiksgegevens weergeven.\n\n    --\n        De resterende opdrachtregel ongewijzigd doorgeven.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Druk op een willekeurige toets om door te gaan...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Voor argument {} ontbreekt een vereiste parameter.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Exporteren wordt uitgevoerd. Dit kan enkele minuten duren.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Importeren wordt uitgevoerd. Dit kan enkele minuten duren.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>Ondersteuning voor GUI-toepassingen is uitgeschakeld via {} of /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Controleren op updates.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Deze distributie is alleen beschikbaar vanuit de Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>voor wsl.exe --mount op ARM64 is Windows-versie 27653 of nieuwere versie vereist.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>De meest recente versie van Windows-subsysteem voor Linux is al geïnstalleerd.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Windows-subsysteem voor Linux bijwerken naar versie: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Voor deze toepassing is het Windows-subsysteem voor Linux optionele onderdeel vereist.\nInstalleer het door het uit te voeren: wsl.exe --install --no-distribution\nHet systeem moet mogelijk opnieuw worden opgestart, zodat de wijzigingen van kracht kunnen worden.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Het opgegeven bestand moet de bestandsextensie {} hebben.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Het opgegeven bestand moet de bestandsextensie {} of {} hebben.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>Kan de VmSwitch {} niet vinden. Beschikbare switches: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Voor brugnetwerken moet wsl2.vmSwitch worden ingesteld.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Kan de distributielijst niet ophalen uit {}. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Kan de schijf {} niet koppelen aan WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Kan het formaat van de schijf niet wijzigen.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Geen waarde gevonden.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Windows-versie {} biedt geen ondersteuning voor de pakketversie van Windows-subsysteem voor Linux.\nInstalleer de vereiste update via Windows Update of via: {}\nGa naar https://aka.ms/wslinstall voor meer informatie</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Als u de foutopsporingsshell wilt uitvoeren, moet u wsl.exe als beheerder uitvoeren.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Het installatieproces voor distributie {} is mislukt met afsluitcode: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Ongeldige GUID-indeling: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Ongeldige sectienaam in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Ongeldige sleutelnaam in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>{} wordt verwacht in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Ongeldig escapeteken: {} in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Onbekende sleutel {} in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Ongeldige integerwaarde {} voor sleutel {} in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Ongeldige IP-waarde {} voor sleutel {} in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Ongeldige booleaanse waarde {} voor sleutel {} in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Ongeldig mac-adres {} voor sleutel {} in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Kan configuratiebestand {}, {} niet openen</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Er zijn fouten opgetreden tijdens het opstarten van WSL</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Kan de systeemgebruikerssessie voor {} niet starten. Zie journalctl voor meer informatie.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>EventViewer openen</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Deze distributie bevat geen standaardnaam. Gebruik --name om de distributienaam te kiezen.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Argumenten {} en {} kunnen niet tegelijkertijd worden opgegeven.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>Argument {} vereist het {}-argument.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Ongeldige distributienaam: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Verouderde distributieregistratie wordt gebruikt. Overweeg in plaats daarvan een op teer gebaseerde distributie te gebruiken.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Verouderde distributieregistraties ondersteunen het argument --version niet.</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>De distributie '{}' wordt geleverd door een onderdrukkingsmanifest.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>De distributiehash komt niet overeen. Verwacht: {}, werkelijke hash: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Ongeldige hexadecimale tekenreeks: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>'{}' wordt niet ondersteund bij het installeren van verouderde distributies.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Distributie is geïnstalleerd. Het kan worden gestart via wsl.exe -d {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Ongeldige geheugentekenreeks {} voor .wslconfig-vermelding {} in {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Kan de wisselschijf niet maken in {}: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Geneste virtualisatie wordt niet ondersteund op deze computer.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Kan het netwerkeindpunt met het adres {} niet maken, er is een nieuw adres toegewezen: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Kan geen virtueel netwerk maken met adresbereik: {}, nieuw netwerk gemaakt met bereik: {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>VEILIGE MODUS INGESCHAKELD - veel functies worden uitgeschakeld</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Gespiegelde netwerkmodus wordt niet ondersteund: {}.\nTerugvallen op NAT-netwerken.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Linux Kernel versie 5.10 of nieuwere is vereist</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows-versie {}. {} beschikt niet over de vereiste functies</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Hyper-V-firewall wordt niet ondersteund</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>DNS-tunneling wordt niet ondersteund</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>De instelling wsl2.localhostForwarding heeft geen effect bij het gebruik van de gespiegelde netwerkmodus</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Er is een http-proxywijziging gedetecteerd op de host. Start WSL opnieuw om de wijziging toe te passen.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Er is een localhost-proxyconfiguratie gedetecteerd, maar niet gespiegeld in WSL. WSL in nat-modus biedt geen ondersteuning voor localhostproxy's.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Er is een IPv6-proxyconfiguratie gedetecteerd, maar niet gespiegeld in WSL. WSL in nat-modus biedt geen ondersteuning voor IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Er is een localhost IPv6-proxyconfiguratie gedetecteerd, maar niet gespiegeld in WSL. WSL biedt geen ondersteuning voor localhost IPv6-proxy's.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Er is een onverwachte fout opgetreden tijdens het omzetten van proxy-instellingen. Proxy-instellingen zijn niet gespiegeld in WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Er is een fout opgetreden bij het openen van het register. Pad: {}. Fout: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Kan het relay-proces van localhost niet starten. Fout: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Kan het virtuele netwerk niet starten. Installeer het optionele onderdeel Virtual Machine Platform door wsl.exe --install --no-distribution uit te voeren</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Kan netwerk (networkingMode {}) niet configureren. Er wordt teruggevallen op networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Kan Windows-onderdeel {} niet inschakelen (afsluitcode {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Er is een onherstelbare fout geretourneerd door de invoegtoepassing {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Er is een onherstelbare fout geretourneerd door de invoegtoepassing {}. Foutbericht: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Voor de invoegtoepassing {} is een nieuwere versie van WSL vereist. Uitvoeren: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>De instelling .wslconfig '{}' is uitgeschakeld door het computerbeleid.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>De foutopsporingsshell is uitgeschakeld door het computerbeleid.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount is uitgeschakeld door het computerbeleid.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>WSL1 is uitgeschakeld door het computerbeleid.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Voer wsl.exe --set-version {} 2 uit om een upgrade uit te voeren naar WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL rondt een upgrade af...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Bijwerken is mislukt (afsluitcode: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Installatie ongedaan maken is mislukt (afsluitcode: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Logboekbestand: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Kan het MSIX-pakket niet verwijderen (fout: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Kan het MSIX-pakket niet installeren (fout: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Optionele onderdelen die nodig zijn om WSL uit te voeren, zijn niet geïnstalleerd.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Ontbrekende onderdelen installeren.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Het geïmporteerde bestand is geen geldige Linux-distributie.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Druk op een willekeurige toets om af te sluiten...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Er is een nieuwe versie van Windows-subsysteem voor Linux beschikbaar.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Werk bij naar versie {} of bekijk de releaseopmerkingen hieronder.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Bijwerken</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Documenten weergeven</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>De bewerking kan niet worden voltooid omdat de VHD momenteel in gebruik is. Stoppen van WSL forceren: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} is geen geldige booleaanse waarde, &lt;waar|onwaar&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Sparse VHD wordt alleen ondersteund op WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>Het uitvoeren van WSL als lokaal systeem wordt niet ondersteund.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Prestaties tip:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Het gebruik van een I/O-intensieve bewerking zoals {} op uw Windows-stations heeft slechte prestaties. Overweeg uw projectbestanden naar het Linux-bestandssysteem te verplaatsen voor betere prestaties. Klik hieronder voor meer informatie.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Documenten weergeven</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Niet meer weergeven</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>De WSL2-virtuele machine is vastgelopen.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>De stacktracering is opgeslagen in: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>De distributie kan niet worden gestart. Foutcode: {}, foutstap: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>De distributie kan niet worden gestart omdat de virtuele schijf beschadigd is.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Dubbele configuratiesleutel {} in {}:{} (conflicterende sleutel: {} in {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Ongeldige waarde {} voor configuratiesleutel {} in {}:{} (geldige waarden: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Wachten tot de OOBE-opdracht is voltooid voor distributie {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Kan de eigenschap {} niet lezen uit distributie {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Dit ziet eruit als een VHD-bestand. Gebruik --vhd om een VHD te importeren in plaats van een tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Kan {} niet installeren vanaf de Microsoft Store: {}\nPoging tot downloaden van web...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Er is geen standaarddistributie geconfigureerd. Geef een distributie op om te installeren.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p is uitgeschakeld en valt terug naar 9p met vsock-transport.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>WSL-installatie lijkt beschadigd te zijn (foutcode: {}).\nDruk op een toets om WSL te herstellen of CTRL-C om te annuleren.\nEr treedt over 60 seconden een time-out op voor deze prompt.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Kan het terminalprofiel niet parseren tijdens het registreren van de distributie: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Ongeldige grootte: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nFoutcode: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Ongeldig JSON-document. Fout bij parseren: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors mogen niet groter zijn dan het aantal logische processors op het systeem ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Kan netwerkmodus niet opvragen</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Er zijn kernelmodules geleverd zonder een kernel op te geven. Raadpleeg https://aka.ms/wslcustomkernel voor meer informatie.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Vanwege een huidig compatibiliteitsprobleem met de Wereldwijde Beveiligde Toegang-client is DNS-tunneling uitgeschakeld.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Sparse VHD-ondersteuning is momenteel uitgeschakeld vanwege mogelijk beschadigde gegevens.\nOm een distributie te dwingen een sparse VHD te gebruiken, voer je het volgende uit:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Kan {} niet koppelen. Zie dmesg voor meer informatie.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Verwerken van /etc/fstab met mount -a is mislukt.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Er is een fout opgetreden bij het koppelen van de distributieschijf. Deze is als terugval gekoppeld met het kenmerk Alleen-lezen.\nZie de herstelinstructies voor: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Kan '{}' niet vertalen</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Er is iets foutgegaan. Probeer het later opnieuw.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Info</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Over instellingen voor Windows-subsysteem voor Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Alle rechten voorbehouden.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Windows Subsystem voor Linux-instellingen</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Met Windows-subsysteem voor Linux-instellingen kunnen ontwikkelaars het .wslconfig-bestand beheren met behulp van een GUI-toepassing.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Automatisch geheugen vrijmaken</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Automatisch geheugen in cache vrijmaken na detectie van niet-actief CPU-gebruik. Ingesteld op geleidelijk voor langzame release en dropcache voor directe vrijgave van geheugen in cache.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatisch geheugen vrijmaken.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Automatisch geheugen in cache vrijmaken na detectie van niet-actief CPU-gebruik. Ingesteld op geleidelijk voor langzame release en dropcache voor directe vrijgave van geheugen in cache.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Automatische proxy ingeschakeld</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Hiermee kan WSL de HTTP-proxygegevens van Windows gebruiken.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatische proxy ingeschakeld.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hiermee kan WSL de HTTP-proxygegevens van Windows gebruiken.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>DNS-analyse met de beste poging gebruiken</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.dnsTunneling is ingesteld op true. Als deze optie is ingesteld op true, wordt de vraag geëxtraheerd uit de DNS-aanvraag en wordt geprobeerd deze op te lossen, waarbij de onbekende records worden genegeerd.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-analyse met de beste poging gebruiken.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.dnsTunneling is ingesteld op true. Als deze optie is ingesteld op true, wordt de vraag geëxtraheerd uit de DNS-aanvraag en wordt geprobeerd deze op te lossen, waarbij de onbekende records worden genegeerd.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Aangepaste kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Een absoluut Windows-pad naar een aangepaste Linux-kernel.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Door kernels bladeren</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aangepaste kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Een absoluut Windows-pad naar een aangepaste Linux-kernel.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Aangepaste kernelmodules</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Een absoluut Windows-pad naar een aangepaste Linux-kernel modules VHD.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Door kernelmodules bladeren</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aangepaste kernelmodules</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Een absoluut Windows-pad naar een aangepaste Linux-kernel modules VHD.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Aangepaste systeemdistributie</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bladeren door distro's</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Geef een pad op naar een VHD die wordt geladen als een aangepaste systeemdistributie, die voornamelijk wordt gebruikt om GUI-apps in WSL aan te zetten. [Meer informatie over systeemdistributies hier].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aangepaste systeemdistributie</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Geef een pad op naar een VHD die wordt geladen als een aangepaste systeemdistributie, die voornamelijk wordt gebruikt om GUI-apps in WSL aan te zetten.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Foutopsporingsconsole inschakelen</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Booleaanse waarde voor het inschakelen van een uitvoerconsolevenster waarin de inhoud van dmesg wordt weergegeven bij het begin van een WSL 2-distributie-exemplaar.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Foutopsporingsconsole inschakelen.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleaanse waarde voor het inschakelen van een uitvoerconsolevenster waarin de inhoud van di berichten wordt weergegeven bij het begin van een WSL 2-distributie-exemplaar.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Standaardgrootte van VHD</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>De standaard maximale grootte voor de uitbreidbare virtuele harde WSL-schijf (VHD) alleen voor nieuw gemaakte distributies.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Grootte opnieuw instellen</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Standaardgrootte van VHD</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>De standaard maximale grootte, opgegeven in megabytes, voor de uitbreidbare virtuele harde WSL-schijf (VHD) alleen voor nieuw gemaakte distributies.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Ontwikkelaar</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentatie</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>DrvFS-modus wijzigen</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Hiermee wijzigt u de implementatie van bestandstoegang tussen besturingssystemen in WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS-proxy ingeschakeld</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.networkingMode is ingesteld op NAT. Booleaanse waarde om WSL te informeren over het configureren van de DNS-server in Linux naar nat op de host. Als u instelt op onwaar, worden DNS-servers gespiegeld van Windows naar Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-proxy ingeschakeld.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.networkingMode is ingesteld op NAT. Booleaanse waarde om WSL te informeren over het configureren van de DNS-server in Linux naar nat op de host. Als u instelt op onwaar, worden DNS-servers gespiegeld van Windows naar Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>DNS-tunneling ingeschakeld</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Hiermee wijzigt u de wijze waarop DNS-aanvragen van WSL naar Windows worden geproxy'd.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-tunneling ingeschakeld.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hiermee wijzigt u de wijze waarop DNS-aanvragen van WSL naar Windows worden geproxy'd.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Bestandssysteem</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>GUI-toepassingen inschakelen</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Booleaanse waarde voor het in- of uitschakelen van ondersteuning voor GUI-toepassingen ([WSLg]) in WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>GUI-toepassingen inschakelen.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleaanse waarde voor het in- of uitschakelen van ondersteuning voor GUI-toepassingen (ook wel WSL g genoemd) in WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Prestatiemeteritems voor hardware inschakelen</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Hiermee schakelt u hardwareprestatiemeteritems voor Linux in.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Prestatiemeteritems voor hardware inschakelen.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hiermee schakelt u hardwareprestatiemeteritems voor Linux in.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Hyper-V Firewall ingeschakeld</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Hiermee wordt de Hyper-V-firewall ingeschakeld waarmee de Windows Firewall-regels en regels die specifiek zijn voor Hyper-V-verkeer, WSL-netwerkverkeer kunnen filteren.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hyper-V Firewall ingeschakeld.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hiermee wordt de Hyper-V-firewall ingeschakeld waarmee de Windows Firewall-regels en regels die specifiek zijn voor Hyper-V-verkeer, WSL-netwerkverkeer kunnen filteren.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Loopback van hostadres</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.networkingMode is ingesteld op gespiegeld. Als deze optie is ingesteld op Waar, kan de container verbinding maken met de host of de host om verbinding te maken met de container door een IP-adres dat aan de host is toegewezen. Het loopback-adres 127.0.0.1 kan altijd worden gebruikt. Met deze optie kunnen ook alle extra toegewezen lokale IP-adressen worden gebruikt.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Loopback van hostadres.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.networkingMode is ingesteld op gespiegeld. Als deze optie is ingesteld op Waar, kan de container verbinding maken met de host of de host om verbinding te maken met de container door een IP-adres dat aan de host is toegewezen. Het loopback-adres 127.0.0.1 kan altijd worden gebruikt. Met deze optie kunnen ook alle extra toegewezen lokale IP-adressen worden gebruikt.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Genegeerde poorten</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.networkingMode is ingesteld op gespiegeld. Hiermee geeft u op aan welke poorten Linux-toepassingen kunnen binden, die niet automatisch worden doorgestuurd of overwogen in Windows. Moet worden opgemaakt in een door komma's gescheiden lijst, bijvoorbeeld: 3000.9000.9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Poorten opnieuw instellen</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Genegeerde poorten</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.networkingMode is ingesteld op gespiegeld. Hiermee geeft u op aan welke poorten Linux-toepassingen kunnen binden, die niet automatisch worden doorgestuurd of overwogen in Windows. Moet worden opgemaakt in een door komma's gescheiden lijst, bijvoorbeeld: 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Time-out voor eerste automatische proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.autoProxy is ingesteld op true. Hiermee configureert u hoe lang (in milliseconden) WSL wacht op het ophalen van HTTP-proxygegevens bij het starten van een WSL-container. Als de proxyinstellingen na deze tijd worden omgezet, moet het WSL-exemplaar opnieuw worden gestart om de opgehaalde proxyinstellingen te gebruiken.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Time-out voor opnieuw instellen</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Time-out voor eerste automatische proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Alleen van toepassing wanneer wsl2.autoProxy is ingesteld op true. Hiermee configureert u hoe lang in milliseconden door WSL wordt gewacht op het ophalen van HTTP-proxygegevens bij het starten van een WSL-container. Als de proxyinstellingen na deze tijd worden omgezet, moet het WSL-exemplaar opnieuw worden gestart om de opgehaalde proxyinstellingen te gebruiken.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Kernelopdrachtregel</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Aanvullende kernelopdrachtregelargumenten.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Localhost-doorsturen inschakelen</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Booleaanse waarde die aangeeft of poorten zijn gebonden aan jokertekens of localhost in de WSL 2-VM, kunnen worden verbonden vanaf de host via localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Localhost-doorsturen inschakelen.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleaanse waarde die aangeeft of poorten zijn gebonden aan jokertekens of localhost in de WSL 2-VM, kunnen worden verbonden vanaf de host via localhost, colon, port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Geheugen en processor</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Geheugengrootte</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Hoeveel geheugen moet worden toegewezen aan de VM van WSL 2.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Grootte opnieuw instellen</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Geheugengrootte</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hoeveel geheugen, opgegeven in megabytes, moet worden toegewezen aan de VM van WSL 2.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} milliseconden</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Geneste virtualisatie inschakelen</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Booleaanse waarde voor het in- of uitschakelen van geneste virtualisatie, waardoor andere geneste VM's kunnen worden uitgevoerd in WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Geneste virtualisatie inschakelen.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleaanse waarde voor het in- of uitschakelen van geneste virtualisatie, waardoor andere geneste VM's kunnen worden uitgevoerd in WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Netwerkmodus</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Hiermee geeft u de netwerkmodus voor WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Netwerkmodus.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hiermee geeft u de netwerkmodus voor WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Netwerken</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>U kunt op alle besturingssystemen met al uw bestanden werken.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Bestandstoegang tussen verschillende besturingssystemen</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Toegang tot uw Windows-bestanden vanuit Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>U hebt vanuit Linux toegang tot uw Windows-bestanden door te navigeren naar '/mnt' en vervolgens naar uw Windows-stationsletter, zoals in dit voorbeeld voor het C-station:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Toegang tot uw Linux-bestanden met Bestandenverkenner</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>U kunt uw Linux-bestanden weergeven in Verkenner door te navigeren naar '\\\\wsl.localhost\\' of door op het Linux-pictogram te klikken.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Windows-bestanden en -programma's starten vanuit WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Zelfs wanneer u WSL gebruikt, kunt u de uitvoerbare Windows-bestanden rechtstreeks uitvoeren vanuit Bash. Probeer het volgende uit te voeren:\n'powershell.exe /c start .' om Verkenner te openen in uw huidige map.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Toegang tot Linux-netwerk-apps vanuit Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Als u een netwerk-app bouwt (bijvoorbeeld een app die wordt uitgevoerd op een NodeJS- of SQL-server) in uw Linux-distributie, hebt u toegang tot de app vanuit een Windows-app (zoals uw Edge- of Chrome-internetbrowser) met localhost (net zoals normaal). Dit betekent dat als u een Linux-server hebt gestart die luistert naar poort 3000, u naar [http://localhost:3000] kunt gaan in Edge op Windows om toegang te krijgen tot de server.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Netwerken in gespiegelde modus</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL bevat ook een nieuwe netwerkmodus, de gespiegelde modus, die geavanceerde mogelijkheden toevoegt, zoals IPv6-ondersteuning en de mogelijkheid om toegang te krijgen tot uw netwerktoepassingen in uw bedrijfsnetwerk.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over bestandstoegang tussen verschillende besturingssystemen</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Welkom bij Windows-subsysteem voor Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL is een geweldige manier om verschillende Linux-distributies uit te proberen.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Distro-beheer</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over standaard WSL-opdrachten</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over het importeren van Linux-distributies</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Installeerbare WSL Distros-opdracht weergeven</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Een benoemde WSL-distro-opdracht installeren</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Beschikbare WSL-distros-opdracht weergeven</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop werkt ideaal met WSL om u te helpen ontwikkelen met Linux-containers.\n\nEnkele voordelen van het gebruik van Docker Desktop met WSL zijn:\n\n• U kunt Docker-opdrachten uitvoeren in WSL of in Windows, met gebruik van dezelfde Docker-daemon en -installatiekopieën.\n• U kunt bestanden en mappen soepel delen tussen Windows en Linux met behulp van de automatische koppeling van Windows-stations in WSL.\n• U kunt uw favoriete Windows-hulpprogramma's en -editors gebruiken om te werken aan Linux-code en -bestanden, en omgekeerd, dankzij de interoperabiliteit van WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker Desktop-integratie</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over het gebruik van WSL met Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Met het Windows-subsysteem voor Linux (WSL) kunt u uw favoriete Linux-hulpprogramma's, toepassingen en werkstromen rechtstreeks in Windows uitvoeren.\n\nNeem even de tijd om een voorbeeld te bekijken van de favoriete functies van de community of bekijk onze uitgebreide documentatie.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Welkom bij WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Aanbevolen procedures voor installatie</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Aan de slag met Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentatie voor Windows-subsysteem voor Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>U kunt grafische Linux-toepassingen gebruiken met systeemeigen Windows-interacties, zoals alt-tab, het openen van het startmenu, vastmaken van taakbalk en ondersteuning voor knippen en plakken.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>GUI-apps</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL kan gebruikmaken van je Windows GPU voor Machine Learning-werkstromen\n\nBinaire Linux-bestanden die worden uitgevoerd in WSL kunnen automatisch je GPU in Windows gebruiken om hun prestaties te versnellen. Je hoeft deze werkstromen slechts op dezelfde manier te installeren en uit te voeren als op een normale Linux-computer. Je kunt op verschillende manieren aan de slag gaan, van het uitvoeren van CUDA in een Docker-container als je een NVIDIA-grafische kaart hebt, tot het uitvoeren van PyTorch of TensorFlow met DirectML op je AMD-, Intel- of NVIDIA-grafische kaart. Zie de Aan de slag-gids hieronder voor meer informatie.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>GPU-versnelling</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over GPU-versnelling</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over WSL GUI-apps</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Hier ziet u een lijst met apps die u kunt proberen (u kunt al deze apps installeren in Ubuntu door 'sudo apt install &lt;De app-naam&gt;' te typen)\n\n    • gedit – Eenvoudige teksteditor\n    • audacity – Audiobestanden opnemen en bewerken\n    • blender – 3D-animaties en -visualisaties maken\n    • gimp – Foto's bewerken\n    • nautilus – Een Linux-bestandenverkenner\n    • vlc – Videospeler</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>U hebt eenvoudig toegang tot netwerk-apps in Windows- en Linux-besturingssystemen.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Netwerkintegratie</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over netwerktoepassingen</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over netwerken in gespiegelde modus</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>U kunt WSL rechtstreeks vanuit VS Code gebruiken als uw fulltime-ontwikkelomgeving.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS Code-integratie</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over het gebruik van WSL met VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Installatie-instructies</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Nadat u VS Code hebt geïnstalleerd, kunt u de externe WSL-extensie installeren vanuit Windows Terminal:\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Open een WSL-project in Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Om een project te openen in VS Code vanuit uw WSL-distributie, opent u de opdrachtregel van de distributie en voert u 'code .' uit om een projectbestand te openen.\n\nU hebt ook toegang tot meer externe VS Code-opties via het opdrachtpalet in VS Code zelf. Druk op SHIFT+CTRL+P op het toetsenbord om het opdrachtpalet te openen en typ 'Remote-WSL' om een lijst met beschikbare externe VS Code-opties weer te geven (bijvoorbeeld de map opnieuw openen in een externe sessie, opgeven welke distributie u wilt openen en meer).</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Informatie over het gebruik</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Optionele onderdelen</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Privacyverklaring</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Aantal processors</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Hoeveel logische processors moeten worden toegewezen aan de VM van WSL 2.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Aantal opnieuw instellen</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aantal processors</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hoeveel logische processors moeten worden toegewezen aan de VM van WSL 2.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Verwante koppelingen</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Opmerkingen bij de release</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Veilige modus inschakelen</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Voer WSL uit in de veilige modus. Hierdoor worden veel functies uitgeschakeld en kunnen distributies met een slechte status worden hersteld.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Veilige modus inschakelen.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Voer WSL uit in de veilige modus. Hierdoor worden veel functies uitgeschakeld en kunnen distributies met een slechte status worden hersteld.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Info</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Ontwikkelaar</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Distributiebeheer</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Docker Desktop-integratie</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Bestandssysteem</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Algemeen</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>GPU-versnelling</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>GUI-apps</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>wsl.exe starten</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Geheugen en processor</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Netwerken</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Netwerkintegratie</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Welkom bij WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Optionele onderdelen</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Instellingen</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS Code-integratie</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Nieuwe functies</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Werken tussen bestandssystemen</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problemen</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Sparse VHD standaard inschakelen</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Nieuwe VHD's worden automatisch ingesteld op schaars wanneer deze is ingeschakeld.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Sparse VHD standaard inschakelen.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Nieuwe VHD's worden automatisch ingesteld op schaars wanneer deze is ingeschakeld.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Bestandslocatie wisselen</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Een absoluut Windows-pad naar de verwisselbare virtuele harde schijf.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bladeren door wisselbestanden</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Bestandslocatie wisselen</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Een absoluut Windows-pad naar de verwisselbare virtuele harde schijf.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Formaat wijzigen</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Hoeveel wisselruimte moet worden toegevoegd aan de VM van WSL 2, 0 voor geen wisselbestand. Wisselopslag is ram op schijf die wordt gebruikt wanneer de geheugenvraag de limiet op het hardwareapparaat overschrijdt.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Grootte opnieuw instellen</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Formaat wijzigen</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hoeveel wisselruimte, opgegeven in megabytes, moet worden toegevoegd aan de VM van WSL 2. 0 voor geen wisselbestand. Wisselopslag is ram op schijf die wordt gebruikt wanneer de geheugenvraag de limiet op het hardwareapparaat overschrijdt.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>VirtIO inschakelen</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Gebruik Virtiofs in plaats van plan 9 om toegang te krijgen tot hostbestanden, waardoor de snelheid toeneemt.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Virtio 9p inschakelen</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Maakt het koppelen van het 9P-bestandssysteem vanaf de host mogelijk met behulp van het Virtio-transport.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>VM Idle-time-out</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Het aantal milliseconden dat een VM inactief is voordat deze wordt afgesloten.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Time-out voor opnieuw instellen</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>VM Idle-time-out</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Het aantal milliseconden dat een VM inactief is voordat deze wordt afgesloten.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Apps die op WSL draaien vanuit Visual Studio maken, uitvoeren, profileren en fouten opsporen</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Voer je apps uit en spoor fouten op in WSL vanuit Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over WSL in Visual Studio voor .NET-ontwikkelaars</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Meer informatie over Visual Studio en WSL voor C++ ontwikkelaars</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Met Windows-subsysteem voor Linux (WSL) kun je je .NET Core- en cross-platform C++ apps in Linux eenvoudig uitvoeren en fouten opsporen zonder Visual Studio te verlaten. Als je een platformoverschrijdende ontwikkelaar bent, kun je deze methode gebruiken om op een eenvoudige manier meer van je doelomgevingen te testen.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studio-integratie</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studio-integratie</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/pl-PL/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Podsystem Windows dla systemu Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Podsystem Windows dla systemu Linux umożliwia deweloperom uruchamianie środowiska GNU/Linux — a w tym większości narzędzi wiersza polecenia, narzędzi i aplikacji — bezpośrednio w systemie Windows, bez modyfikacji, bez obciążenia tradycyjnej maszyny wirtualnej lub konfiguracji uruchomienia jednego z kilku systemów operacyjnych.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Nie można odłączyć dysku: {}. Aby uzyskać więcej szczegółów, uruchom polecenie \"dmesg\" wewnątrz podsystemu WSL2.\nAby wymusić zatrzymanie i odłączenie dysku w podsystemie WSL2, uruchom polecenie \"wsl.exe {}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Dysk „{}” jest już dołączony.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Ten wolumin jest już zainstalowany wewnątrz podsystemu WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Dysk o tej nazwie jest już zainstalowany. Odinstaluj dysk lub wybierz nową nazwę i spróbuj ponownie.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Określona nazwa instalacji zawiera nieprawidłowy znak \"/\". Spróbuj ponownie bez nieprawidłowego znaku.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Dysk został pomyślnie zainstalowany jako „/mnt/wsl/{}”.\nUwaga: Lokalizacja będzie inna, jeśli zmodyfikowano ustawienie automount.root w pliku /etc/wsl.conf.\nAby odinstalować i odłączyć dysk, uruchom polecenie „wsl.exe {} {}”.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Dysk został dołączony, ale nie można go zainstalować: {}.\nAby uzyskać więcej szczegółów, uruchom polecenie „dmesg” w podsystemie WSL2.\nAby odłączyć dysk, uruchom polecenie „wsl.exe {} {}”.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Poniżej znajduje się lista prawidłowych dystrybucji, które można zainstalować.\nZainstaluj przy użyciu &lt;Distro&gt; \"wsl.exe {}\".\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Nazwa dystrybucji została już ustawiona.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>Podana lokalizacja instalacji jest już używana.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Dystrybucja o podanej nazwie już istnieje. Użyj --name , aby wybrać inną nazwę.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Brak dystrybucji o podanej nazwie.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Do zainstalowania dysku jest wymagany dostęp administratora.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Eksportowanie dystrybucji nie powiodło się.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Trwa wykonywanie jednorazowego uaktualnienia systemu plików podsystemu Windows dla systemu Linux dla tej dystrybucji...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Nie można uruchomić, ponieważ inne wystąpienie jest uruchomione bez podniesionych uprawnień.  Wystąpienia z podwyższonym poziomem uprawnień i bez niego nie mogą działać jednocześnie.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Importowanie dystrybucji nie powiodło się.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Instalowanie opcjonalnego składnika systemu Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Pobieranie: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Instalowanie: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importowanie dystrybucji</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>Plik {} został pobrany.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Podsystem Windows dla systemu Linux wznawia poprzednią instalację...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Nieprawidłowa nazwa dystrybucji: \"{}\".\nAby uzyskać listę prawidłowych dystrybucji, użyj polecenia \"wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Wystąpienie Podsystem Windows dla systemu Linux zostało zakończone.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Nieprawidłowy argument wiersza polecenia: {}\nUżyj --help' {}, aby uzyskać listę obsługiwanych argumentów.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>Argument wiersza polecenia {} wymaga wartości.\nUżyj --help' {}, aby uzyskać listę obsługiwanych argumentów.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Nieobsługiwanych ustawień konsoli. Aby korzystać z tej funkcji, należy wyłączyć starszą konsolę.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} nie jest prawidłową liczbą całkowitą.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Trwa instalowanie, odinstalowywanie lub konwersja tej dystrybucji.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Trwa uruchamianie {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Nie można uruchomić, ponieważ inne wystąpienie jest uruchomione z podwyższonym poziomem uprawnień. Wystąpienia z podwyższonym poziomem uprawnień i bez niego nie mogą działać jednocześnie.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Podsystem Windows dla systemu Linux nie ma zainstalowanych dystrybucji.\nMożesz rozwiązać ten problem, instalując dystrybucję z poniższymi instrukcjami:\n\nUżyj wsl.exe --list --online' , aby wyświetlić listę dostępnych dystrybucji\ni \"wsl.exe --install &lt;Distro&gt;\" do zainstalowania.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Brak uruchomionych dystrybucji.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (domyślnie)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Podsystem Windows dla dystrybucji systemu Linux:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Dystrybucja domyślna: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Wersja domyślna: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Wyrejestrowywanie.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Nie odnaleziono użytkownika.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Aby uzyskać informacje na temat kluczowych różnic w podsystemie WSL 2, odwiedź stronę https://aka.ms/wsl2</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Konwersja w toku. Może to potrwać kilka minut.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>Dystrybucja jest już żądaną wersją.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>Starsza dystrybucja nie obsługuje protokołu WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>Nie można uruchomić protokołu WSL2, ponieważ wirtualizacja nie jest włączona na tej maszynie.\nUpewnij się, że składnik opcjonalny \"Platforma maszyny wirtualnej\" jest włączony i że wirtualizacja jest włączona w ustawieniach oprogramowania układowego komputera.\n\nWłącz platformę maszyny wirtualnej, uruchamiając polecenie: wsl.exe --install --no-distribution\n\nAby uzyskać informacje, odwiedź https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Dołączono zbyt wiele wirtualnych dysków twardych lub dysków fizycznych.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>Dysk VHD został już zainstalowany za pomocą polecenia wsl.exe --mount, Odinstaluj dysk przed uruchomieniem.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>Podsystem WSL1 nie jest obsługiwany w bieżącej konfiguracji komputera.\nWłącz opcjonalny składnik „Podsystem Windows dla systemu Linux”, aby używać podsystemu WSL1.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Ta operacja jest obsługiwana tylko przez podsystem WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Użycie:\n    --networking-mode\n       Wyświetl bieżący tryb sieciowy.\n\n    --msal-proxy-path\n        Wyświetl ścieżkę do aplikacji serwera proxy MSAL.\n\n    --vm-id\n        Wyświetl identyfikator maszyny wirtualnej WSL.\n\n    --version\n        Wyświetl wersję pakietu WSL.\n\n    -n\n        Nie drukuj nowego wiersza.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Użycie: \n    -a\n        Wymuś wynik do bezwzględnego formatu ścieżki.\n    -u\n        Tłumaczenie ze ścieżki systemu Windows na ścieżkę WSL (domyślna).\n    -w\n        Tłumaczenie ze ścieżki WSL na ścieżkę systemu Windows.\n    -m\n        Tłumaczenie ze ścieżki WSL na ścieżkę systemu Windows ze znakiem „/” zamiast „\\\\”\n\nPrzykład: wslpath „c:\\\\users”</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Wykonuje operacje administracyjne w podsystemie Windows dla systemu Linux\n\nUżycie:\n    /l, /list [Option]\n        Wyświetla listę zarejestrowanych dystrybucji.\n        /all — Opcjonalnie wyświetla wszystkie dystrybucje, w tym dystrybucje, które są\n               aktualnie instalowane lub odinstalowywane.\n\n        /running — Wyświetla tylko dystrybucje, które są obecnie uruchomione.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Ustawia dystrybucję jako domyślną.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Kończy działanie określonej dystrybucji.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Wyrejestrowuje dystrybucję i usuwa system plików katalogu głównego.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Wszelkie prawa zastrzeżone.\nAby zapoznać się z informacjami o ochronie prywatności dotyczącymi tego produktu, odwiedź stronę https://aka.ms/privacy.\n\nUżycie: wsl.exe [Argument] [Options...] [CommandLine]\n\nArgumenty uruchamiania plików binarnych systemu Linux:\n\n    Jeśli nie zostanie podany żaden wiersz polecenia, polecenie wsl.exe uruchomi powłokę domyślną.\n\n    --exec, -e &lt;CommandLine&gt;\n        Wykonuje określone polecenie bez użycia domyślnej powłoki systemu Linux.\n\n    --shell-type &lt;standard|login|none&gt;\n        Wykonuje określone polecenie z podanym typem powłoki.\n\n    --\n        Przekazuje pozostałą część wiersza polecenia w niezmienionej postaci.\n\nOpcje:\n    --cd &lt;Directory&gt;\n        Ustawia określony katalog jako bieżący katalog roboczy.\n        W przypadku użycia znaku ~ zostanie użyta ścieżka macierzysta użytkownika systemu Linux. Jeśli ścieżka rozpoczyna się\n        od znaku /, będzie interpretowana jako ścieżka bezwzględna systemu Linux.\n        W przeciwnym razie wartość musi być ścieżką bezwzględną systemu Windows.\n\n    --distribution, -d &lt;DistroName&gt;\n        Uruchamia określoną dystrybucję.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Uruchamia określony identyfikator dystrybucji.\n\n    --user, -u &lt;UserName&gt;\n        Uruchamia jako określony użytkownik.\n\n    --system\n        Uruchamia powłokę dystrybucji systemu.\n\nArgumenty zarządzania podsystemem Windows dla systemu Linux:\n\n    --help\n        Wyświetla informacje o użyciu.\n\n    --debug-shell\n        Otwiera powłokę debugowania podsystemu WSL2 na potrzeby diagnostyki.\n\n    --install [Distro] [Options...]\n        Instaluje dystrybucję podsystemu Windows dla systemu Linux.\n        Aby zapoznać się z listą prawidłowych dystrybucji, użyj polecenia 'wsl.exe --list --online'.\n\n        Opcje:\n            --enable-wsl1\n                Włącza obsługę podsystemu WSL1.\n\n            --fixed-vhd\n                Tworzy dysk o stałym rozmiarze do przechowywania dystrybucji.\n\n            --from-file &lt;Path&gt;\n                Instaluje dystrybucję z pliku lokalnego.\n\n            --legacy\n                Używa starszego manifestu dystrybucji.\n\n            --location &lt;Location&gt;\n                Ustawia ścieżkę instalacji dystrybucji.\n\n            --name &lt;Name&gt;\n                Ustawia nazwę dystrybucji.\n\n            --no-distribution\n                Instaluje tylko wymagane składniki opcjonalne, nie instaluje dystrybucji.\n\n            --no-launch, -n\n                Nie uruchamia dystrybucji po instalacji.\n\n            --version &lt;Version&gt;\n                Określa wersję do użycia dla nowej dystrybucji.\n\n            --vhd-size &lt;MemoryString&gt;\n                Określa rozmiar dysku do przechowywania dystrybucji.\n\n            --web-download\n                Pobiera dystrybucję z Internetu zamiast ze sklepu Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Zmienia opcje specyficzne dla dystrybucji.\n\n        Opcje:\n            --move &lt;Location&gt;\n                Przenosi dystrybucję do nowej lokalizacji.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Ustawia dysk VHD dystrybucji jako rozrzedzony, dzięki czemu miejsce na dysku jest automatycznie odzyskiwane.\n\n            --set-default-user &lt;Username&gt;\n                Ustawia domyślnego użytkownika dystrybucji.\n\n            --resize &lt;MemoryString&gt;\n                Zmienia rozmiar dysku dystrybucji do określonego rozmiaru.\n\n    --mount &lt;Disk&gt;\n        Dołącza i instaluje dysk fizyczny lub wirtualny we wszystkich dystrybucjach WSL 2.\n\n        Opcje:\n            --vhd\n                Określa, że &lt;Disk&gt; odwołuje się do wirtualnego dysku twardego.\n\n            --bare\n                Dołącza dysk do podsystemu WSL2, ale nie instaluje go.\n\n            --name &lt;Name&gt;\n                Instaluje dysk przy użyciu niestandardowej nazwy punktu instalacji.\n\n            --type &lt;Type&gt;\n                System plików do użycia podczas instalowania dysku; jeśli określone wartości domyślne nie są ustawione na ext4.\n\n            --options &lt;Options&gt;\n                Dodatkowe opcje instalacji.\n\n            --partition &lt;Index&gt;\n                Indeks partycji do zainstalowania; jeśli określone wartości domyślne nie są ustawione na cały dysk.\n\n    --set-default-version &lt;Version&gt;\n        Zmienia domyślną wersję instalacji dla nowych dystrybucji.\n\n    --shutdown\n        Natychmiast kończy działanie wszystkich uruchomionych dystrybucji i WSL 2\n        uproszczona maszyna wirtualna narzędzi.\n\n        Opcje:\n            --force\n                Zakończenie maszyny wirtualnej WSL 2, nawet jeśli operacja jest w toku. Może spowodować utratę danych.\n\n    --status\n        Wyświetl stan podsystemu Windows dla systemu Linux.\n\n    --unmount [Disk]\n        Odinstalowuje i odłącza dysk od wszystkich dystrybucji podsystemu WSL2.\n        W przypadku wywołania bez argumentu odinstalowuje i odłącza wszystkie dyski.\n\n    --uninstall\n        Odinstalowuje pakiet podsystemu systemu Windows dla systemu Linux z tego komputera.\n\n    --update\n        Aktualizowanie pakietu podsystemu systemu Windows dla systemu Linux.\n\n        Opcje:\n            --pre-release\n                Pobieranie wersji wstępnej, jeśli jest dostępna.\n\n    --version, -v\n        Wyświetlanie informacji o wersji.\n\nArgumenty zarządzania dystrybucjami w Podsystemie Windows dla systemu Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Eksportuje dystrybucję do pliku tar.\n        W przypadku wyjścia stdout jako nazwę pliku można wprowadzić -.\n\n        Opcje:\n            --format &lt;Format&gt;\n                Określa format eksportu. Obsługiwane wartości: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        Importuje określony plik tar jako nową dystrybucję.\n        W przypadku wejścia stdin jako nazwę pliku można wprowadzić -.\n\n        Opcje:\n            --version &lt;Version&gt;\n                Określa wersję do użycia dla nowej dystrybucji.\n\n            --vhd\n                Określa, że podany plik jest plikiem .vhd lub .vhdx, a nie plikiem tar.\n                Ta operacja tworzy kopię pliku VHD w określonej lokalizacji instalacji.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Importuje określony plik VHD jako nową dystrybucję.\n        Ten wirtualny dysk twardy musi być sformatowany przy użyciu typu systemu plików ext4.\n\n    --list, -l [Options]\n        Wyświetla listę dystrybucji.\n\n        Opcje:\n            --all\n                Wyświetl wszystkie dystrybucje, w tym dystrybucje, które są\n                aktualnie instalowane lub odinstalowywane.\n\n            --running\n                Wyświetl tylko dystrybucje, które są obecnie uruchomione.\n\n            --quiet, -q\n                Pokaż tylko nazwy dystrybucji.\n\n            --verbose, -v\n                Pokaż szczegółowe informacje o wszystkich dystrybucjach.\n\n            --online, -o\n                Wyświetla listę dystrybucji dostępnych do zainstalowania przy użyciu polecenia 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Ustawia dystrybucję jako domyślną.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Zmienia wersję określonej dystrybucji.\n\n    --terminate, -t &lt;Distro&gt;\n        Kończy działanie określonej dystrybucji.\n\n    --unregister &lt;Distro&gt;\n        Wyrejestrowuje dystrybucję i usuwa główny system plików.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>Wersja podsystemu WSL: {}\nWersja jądra: {}\nWersja usługi WSLg: {}\nWersja MSRDC: {}\nWersja Direct3D: {}\nWersja DXCore: {}\nWersja systemu Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>Wersja programu MSBuild: {}\nZatwierdzenie: {}\nCzas kompilacji: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>Nie znaleziono niestandardowego jądra określonego w pliku {}: „{}”.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>Nie znaleziono wirtualnego dysku twardego modułów niestandardowego jądra w pliku {}: „{}”.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>Nie znaleziono niestandardowej dystrybucji systemu określonej w elemencie {} lub ma ona niepoprawny format.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Wszelkie prawa zastrzeżone.\nAby zapoznać się z informacjami o ochronie prywatności dotyczącymi tego produktu, odwiedź stronę https://aka.ms/privacy.\n\nSposób użycia: wslg.exe [Argument] [Options...] [CommandLine]\n\nArgumenty:\n    --cd &lt;Directory&gt;\n        Ustawia określony katalog jako bieżący katalog roboczy.\n        W przypadku użycia znaku ~ zostanie użyta ścieżka macierzysta użytkownika systemu Linux. Jeśli ścieżka rozpoczyna się\n        od znaku /, będzie interpretowana jako bezwzględna ścieżka systemu Linux.\n        W przeciwnym razie wartość musi być bezwzględną ścieżką systemu Windows.\n\n    --distribution, -d &lt;Distro&gt;\n        Uruchom określoną dystrybucję.\n\n    --user, -u &lt;UserName&gt;\n        Uruchom jako określony użytkownik.\n\n    --shell-type &lt;standard|login|none&gt;\n        Wykonaj określone polecenie z użyciem podanego typu powłoki.\n\n    --help\n        Wyświetl informacje o sposobie użycia.\n\n    --\n        Przekaż pozostałą część wiersza polecenia w niezmienionej postaci.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Aby kontynuować, naciśnij dowolny klawisz...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>W argumencie {} brakuje wymaganego parametru.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Eksportowanie w toku. Może to potrwać kilka minut.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Importowanie w toku. Może to potrwać kilka minut.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>Obsługa aplikacji graficznego interfejsu użytkownika jest wyłączona za pośrednictwem pliku {} lub /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Sprawdzanie dostępności aktualizacji.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Ta dystrybucja jest dostępna tylko w sklepie Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount w usłudze ARM64 wymaga systemu Windows w wersji 27653 lub nowszej.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Najnowsza wersja Podsystem Windows dla systemu Linux jest już zainstalowana.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Aktualizowanie Podsystem Windows dla systemu Linux do wersji: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Ta aplikacja wymaga składnika opcjonalnego Podsystem Windows dla systemu Linux.\nZainstaluj go, uruchamiając: wsl.exe --install --no-distribution\nMoże być konieczne ponowne uruchomienie systemu, aby zmiany zostały wprowadzone.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Określony plik musi mieć rozszerzenie pliku {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Określony plik musi mieć rozszerzenie pliku {} lub {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>Nie znaleziono przełącznika maszyny wirtualnej „{}”. Dostępne przełączniki: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Sieć pomostowa wymaga ustawienia przełącznika wsl2.vmSwitch.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Nie można pobrać listy dystrybucyjnej z „{}”. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Nie można dołączyć dysku „{}” do podsystemu WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Nie można zmienić rozmiaru dysku.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Nie znaleziono wartości.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Wersja systemu Windows {} nie obsługuje spakowanej wersji Podsystem Windows dla systemu Linux.\nZainstaluj wymaganą aktualizację za pośrednictwem usługi Windows Update lub za pośrednictwem: {}\nAby uzyskać informacje, odwiedź stronę https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Uruchomienie powłoki debugowania wymaga uruchomienia programu wsl.exe jako administrator.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Proces instalacji dystrybucji „{}” nie powiódł się. Kod zakończenia: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Nieprawidłowy format identyfikatora GUID: „{}”</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Nieprawidłowa nazwa sekcji w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Nieprawidłowa nazwa klucza w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Oczekiwano {} w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Nieprawidłowy znak ucieczki: „{}” w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Nieznany klucz „{}” w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Nieprawidłowa wartość całkowita „{}” dla klucza „{}” w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Nieprawidłowa wartość adresu IP „{}” dla klucza „{}” w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Nieprawidłowa wartość logiczna „{}” dla klucza „{}” w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Nieprawidłowy adres MAC „{}” dla klawisza „{}” w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Nie można otworzyć pliku konfiguracji {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Wystąpiły błędy podczas uruchamiania podsystemu WSL</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Nie można uruchomić systemowej sesji użytkownika dla \"{}\". Aby uzyskać więcej szczegółów, zobacz dziennik journalctl.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Otwórz program Podgląd zdarzeń</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Ta dystrybucja nie zawiera nazwy domyślnej. Użyj --name , aby wybrać nazwę dystrybucji.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Nie można jednocześnie określić argumentów {} i {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>Argument {} wymaga argumentu {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Nieprawidłowa nazwa dystrybucji: „{}”.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Przy użyciu starszej rejestracji dystrybucji. Rozważ użycie dystrybucji opartej na tar.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Starsze rejestracje dystrybucji nie obsługują argumentu --version .</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>Dystrybucja \"{}\" jest dostarczana przez manifest przesłonięcia.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>Skrót dystrybucji jest niezgodny. Oczekiwano: {}, rzeczywisty skrót: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Nieprawidłowy ciąg szesnastkowy: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>Element „{}” nie jest obsługiwany podczas instalowania starszych dystrybucji.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Dystrybucja została pomyślnie zainstalowana. Można ją uruchomić za pomocą polecenia \"wsl.exe -d {}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Nieprawidłowy ciąg pamięci „{}” dla wpisu .wslconfig „{}” w elemencie {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Nie można utworzyć dysku wymiany w „{}”: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Wirtualizacja zagnieżdżona nie jest obsługiwana na tej maszynie.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Nie można utworzyć sieciowego punktu końcowego z adresem: „{}”. Przypisano nowy adres: „{}”</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Nie można utworzyć sieci wirtualnej z zakresem adresów: „{}”. Utworzono nową sieć z zakresem: „{}”, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>TRYB AWARYJNY WŁĄCZONY — wiele funkcji zostanie wyłączonych</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Tryb dublowanych sieci nie jest obsługiwany: {}.\nPowrót do sieci NAT.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Wymagane jest jądro systemu Linux w wersji 5.10 lub nowszej</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Wersja systemu Windows {}. {} nie ma wymaganych funkcji</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Zapora funkcji Hyper-V nie jest obsługiwana</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>Tunelowanie DNS nie jest obsługiwane</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>Ustawienie wsl2.localhostForwarding nie ma wpływu w przypadku korzystania z trybu sieci lustrzanej</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Na hoście wykryto zmianę serwera proxy HTTP. Uruchom ponownie podsystem WSL, aby zastosować zmianę.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Wykryto konfigurację serwera proxy hosta lokalnego, ale nie została ona zdublowana w podsystemie WSL. Podsystem WSL w trybie NAT nie obsługuje serwerów proxy hosta lokalnego.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Wykryto konfigurację serwera proxy IPv6, ale nie została ona zdublowana w podsystemie WSL. Podsystem WSL w trybie NAT nie obsługuje protokołu IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Wykryto konfigurację serwera proxy IPv6 hosta lokalnego, ale nie została ona zdublowana w podsystemie WSL. Podsystem WSL nie obsługuje serwerów proxy IPv6 hosta lokalnego.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Wystąpił nieoczekiwany błąd podczas próby rozpoznania ustawień serwera proxy. Ustawienia serwera proxy nie zostały zdublowane w podsystemie WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Wystąpił błąd podczas uzyskiwania dostępu do rejestru. Ścieżka: \"{}\". Błąd: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Nie można uruchomić procesu przekazywania hosta lokalnego. Błąd: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Nie można uruchomić sieci wirtualnej — zainstaluj opcjonalny składnik platformy maszyn wirtualnych, uruchamiając: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Nie można skonfigurować sieci (networkingMode {}). Powrót do trybu networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Nie można włączyć składnika systemu Windows „{}” (kod zakończenia {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Wtyczka „{}” zwróciła błąd krytyczny</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Wtyczka „{}” zwróciła błąd krytyczny. Komunikat o błędzie: „{}”</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Wtyczka „{}” wymaga nowszej wersji podsystemu WSL. Uruchom polecenie: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>Ustawienie .wslconfig „{}” jest wyłączone przez zasady komputera.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>Powłoka debugowania została wyłączona przez zasady komputera.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount jest wyłączona przez zasady komputera.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>System WSL1 został wyłączony przez zasady komputera.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Uruchom polecenie „wsl.exe --set-version {} 2”, aby uaktualnić do podsystemu WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>Podsystem WSL kończy uaktualnianie...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Aktualizacja nie powiodła się (kod zakończenia: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Odinstalowanie nie powiodło się (kod zakończenia: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Plik dziennika: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Nie można usunąć pakietu MSIX (błąd: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Nie można zainstalować pakietu MSIX (błąd: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Opcjonalne składniki potrzebne do uruchomienia WSL nie są zainstalowane.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Zainstaluj brakujące składniki.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Zaimportowany plik nie jest prawidłową dystrybucją systemu Linux.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Naciśnij dowolny klawisz, aby wyjść...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Dostępna jest nowa wersja podsystemu Windows dla systemu Linux.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Zaktualizuj do wersji {} lub wyświetl jej informacje o wersji poniżej.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Aktualizuj</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Zobacz dokumenty</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Nie można ukończyć operacji, ponieważ dysk VHD jest obecnie używany. Aby wymusić zatrzymanie używania protokołu WSL:  wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} nie jest prawidłową wartością logiczną, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Rozrzedzony dysk VHD jest obsługiwany tylko w systemie WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>Uruchamianie podsystemu WSL jako systemu lokalnego nie jest obsługiwane.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Porada dotycząca wydajności:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Korzystanie z operacji intensywnie korzystających z we/wy, takich jak {} na dyskach z systemem Windows, będzie miało niską wydajność. Rozważ przeniesienie plików projektu do systemu plików Linux w celu uzyskania lepszej wydajności. Kliknij poniżej, aby dowiedzieć się więcej.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Wyświetl dokumenty</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Nie pokazuj ponownie</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>Maszyna wirtualna WSL2 uległa awarii.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>Ślad stosu został zapisany w: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Nie można uruchomić dystrybucji. Kod błędu: {}, krok niepowodzenia: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Nie można uruchomić dystrybucji, ponieważ jej dysk wirtualny jest uszkodzony.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Zduplikowany klucz konfiguracji „{}” w elemencie {}:{} (klucz powodujący konflikt: „{}” w {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Nieprawidłowa wartość „{}” klucza konfiguracji „{}” w elemencie {}:{} (prawidłowe wartości: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Oczekiwanie na ukończenie polecenia OOBE dla dystrybucji \"{}\"...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Nie można odczytać właściwości „{}” z dystrybucji {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Wygląda to na plik VHD. Użyj polecenia --vhd aby zaimportować plik VHD zamiast tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Nie można zainstalować {} z Microsoft Store: {}\nPróba pobrania z sieci Web...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Nie skonfigurowano dystrybucji domyślnej. Podaj dystrybucję do zainstalowania.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p jest wyłączony, przełączenie na 9p z transportem vsock.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>Instalacja podsystemu WSL jest prawdopodobnie uszkodzona (kod błędu: {}).\nNaciśnij dowolny, aby naprawić podsystem WSL, lub CTRL-C, aby anulować.\nTen monit przekroczy limit czasu w ciągu 60 sekund.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Nie można przeanalizować profilu terminalu podczas rejestrowania dystrybucji: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Nieprawidłowy rozmiar: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nKod błędu: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Nieprawidłowy dokument JSON. Błąd analizy: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>Liczba procesorów wsl2.processors nie może przekraczać liczby procesorów logicznych w systemie ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Nie można wykonać zapytania trybu sieciowego</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Niestandardowe moduły jądra zostały dostarczone bez określania niestandardowego jądra. Aby uzyskać więcej informacji, zobacz https://aka.ms/wslcustomkernel.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Z powodu bieżącego problemu ze zgodnością z klientem Globalnego Bezpiecznego Dostępu, tunelowanie DNS jest wyłączone.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Obsługa rozrzedzonych dysków VHD jest obecnie wyłączona z powodu potencjalnego uszkodzenia danych.\nAby wymusić używanie rozrzedzonego dysku VHD przez dystrybucję, uruchom polecenie:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Nie można zainstalować elementu {}. Aby uzyskać więcej szczegółów, zobacz dmesg.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Przetwarzanie pliku /etc/fstab za pomocą polecenia mount -a nie powiodło się.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Wystąpił błąd podczas instalowania dysku dystrybucji. Został on zainstalowany w trybie tylko do odczytu jako rezerwowy.\nZobacz instrukcje odzyskiwania: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Nie można przetłumaczyć „{}”</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Wystąpił problem. Spróbuj ponownie później.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Informacje</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Informacje o ustawieniach podsystemu Windows dla systemu Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Wszelkie prawa zastrzeżone.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Ustawienia podsystemu Windows dla systemu Linux</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Ustawienia podsystemu Windows dla systemu Linux umożliwiają deweloperom zarządzanie plikiem .wslconfig przy użyciu aplikacji opartej na graficznym interfejsie użytkownika.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Automatyczne odzyskiwanie pamięci</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Automatycznie zwalnia pamięć buforowaną po wykryciu bezczynnego użycia procesora. Ustaw od stopniowego do wolnego zwalniania i dropcache na potrzeby natychmiastowego zwolnienia pamięci podręcznej.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatyczne odzyskiwanie pamięci.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Automatycznie zwalnia pamięć buforowaną po wykryciu bezczynnego użycia procesora. Ustaw od stopniowego do wolnego zwalniania i dropcache na potrzeby natychmiastowego zwolnienia pamięci podręcznej.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Włączono automatyczny serwer proxy</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Włącza WSL w celu korzystania z informacji serwera proxy HTTP systemu Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Włączono automatyczny serwer proxy.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Włącza WSL w celu korzystania z informacji serwera proxy HTTP systemu Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Korzystanie z analizowania DNS w najlepszy sposób</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy właściwość wsl2.dnsTunneling jest ustawiona na wartość true. Po ustawieniu wartości true system Windows wyodrębni pytanie z żądania DNS i podejmie próbę jego rozwiązania, ignorując nieznane rekordy.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Korzystanie z analizowania DNS w najlepszy sposób.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy właściwość wsl2.dnsTunneling jest ustawiona na wartość true. Po ustawieniu wartości true system Windows wyodrębni pytanie z żądania DNS i podejmie próbę jego rozwiązania, ignorując nieznane rekordy.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Niestandardowe jądro</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Bezwzględna ścieżka systemu Windows do niestandardowego jądra systemu Linux.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Przeglądaj jądra</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Niestandardowe jądro</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Bezwzględna ścieżka systemu Windows do niestandardowego jądra systemu Linux.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Niestandardowe moduły jądra</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Bezwzględna ścieżka systemu Windows do niestandardowego dysku VHD modułów jądra systemu Linux.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Przeglądaj moduły jądra</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Niestandardowe moduły jądra</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Bezwzględna ścieżka systemu Windows do niestandardowego dysku VHD modułów jądra systemu Linux.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Niestandardowa dystrybucja systemu</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Przeglądaj dystrybucje</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Określ ścieżkę do dysku VHD, który będzie ładowany jako niestandardowa dystrybucja systemu, używana głównie do obsługi aplikacji graficznego interfejsu użytkownika w podsystemie WSL. [Dowiedz się więcej o dystrybucji systemu tutaj].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Niestandardowa dystrybucja systemu</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Określ ścieżkę do wirtualnego dysku twardego, który będzie ładowany jako niestandardowa dystrybucja systemu, używana głównie do obsługi aplikacji graficznego interfejsu użytkownika w podsystemie WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Włącz konsolę debugowania</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Wartość logiczna umożliwiająca włączenie okna konsoli wyjściowej, które wyświetla zawartość środowiska dmesg po uruchomieniu wystąpienia dystrybucji podsystemu WSL 2.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Włącz konsolę debugowania.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Wartość logiczna umożliwiająca włączenie okna konsoli wyjściowej, które wyświetla zawartość komunikatów diagnostycznych po uruchomieniu wystąpienia dystrybucji podsystemu WSL 2.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Domyślny rozmiar wirtualnego dysku twardego</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Domyślny maksymalny rozmiar rozszerzalnego wirtualnego dysku twardego (VHD) podsystemu WSL tylko dla nowo utworzonych dystrybucji.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetuj rozmiar</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Domyślny rozmiar wirtualnego dysku twardego</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Domyślny maksymalny rozmiar (w megabajtach) tylko dla rozwijalnego wirtualnego dysku twardego WSL (VHD) dla nowo utworzonych dystrybucji.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Deweloper</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentacja</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Zmień tryb DrvFS</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Zmienia implementację dostępu do plików między systemami operacyjnymi w podsystemie WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>Włączono serwer proxy DNS</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy właściwość wsl2.networkingMode jest ustawiona na wartość NAT. Wartość logiczna informująca podsystem WSL o skonfigurowaniu serwera DNS w systemie Linux do translatora adresów sieciowych na hoście. Ustawienie wartości false spowoduje zdublowanie serwerów DNS z systemu Windows do systemu Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Włączono serwer proxy DNS.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy właściwość wsl2.networkingMode jest ustawiona na wartość NAT. Wartość logiczna informująca podsystem WSL o skonfigurowaniu serwera DNS w systemie Linux do translatora adresów sieciowych na hoście. Ustawienie wartości false spowoduje zdublowanie serwerów DNS z systemu Windows do systemu Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>Tunelowanie DNS włączone</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Zmienia sposób proxy żądań DNS z podsystemu WSL do systemu Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tunelowanie DNS jest włączone.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Zmienia sposób proxy żądań DNS z podsystemu WSL do systemu Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>System plików</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Włączanie aplikacji graficznego interfejsu użytkownika</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Wartość logiczna umożliwiająca włączenie lub wyłączenie obsługi aplikacji graficznego interfejsu użytkownika ([WSLg]) w podsystemie WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Włączanie aplikacji graficznego interfejsu użytkownika.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Wartość logiczna umożliwiająca włączenie lub wyłączenie obsługi aplikacji graficznego interfejsu użytkownika (znanych jako WSL g) w podsystemie WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Włącz liczniki wydajności sprzętu</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Włącza liczniki wydajności sprzętu dla systemu Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Włącz liczniki wydajności sprzętu.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Włącza liczniki wydajności sprzętu dla systemu Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Włączono zaporę funkcji Hyper-V</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Włącza zaporę funkcji Hyper-V, która umożliwia regułom Zapory systemu Windows, a także regułom specyficznym dla ruchu funkcji Hyper-V, filtrowanie ruchu sieciowego WSL.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Włączono zaporę funkcji Hyper-V.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Włącza zaporę funkcji Hyper-V, która umożliwia regułom Zapory systemu Windows, a także regułom specyficznym dla ruchu funkcji Hyper-V, filtrowanie ruchu sieciowego WSL.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Sprzężenie zwrotne adresów hosta</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy wsl2.networkingMode jest ustawiony na dublowany. Po ustawieniu wartości True kontener będzie mógł łączyć się z hostem lub hostem w celu nawiązania połączenia z kontenerem za pomocą adresu IP przypisanego do hosta. Pamiętaj, że adres sprzężenia zwrotnego 127.0.0.1 może być zawsze używany — ta opcja umożliwia również użycie wszystkich dodatkowo przypisanych lokalnych adresów IP.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Sprzężenie zwrotne adresów hosta.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy właściwość wsl2.networkingMode jest ustawiona na wartość mirrored. Po ustawieniu wartości True kontener będzie mógł łączyć się z hostem lub hostem w celu nawiązania połączenia z kontenerem za pomocą adresu IP przypisanego do hosta. Pamiętaj, że adres sprzężenia zwrotnego 127.0.0.1 może być zawsze używany — ta opcja umożliwia również użycie wszystkich dodatkowo przypisanych lokalnych adresów IP.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Zignorowane porty</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy właściwość wsl2.networkingMode jest ustawiona na wartość Dublowana. Określa, z którymi portami mogą być powiązane aplikacje systemu Linux, które nie będą automatycznie przesyłane dalej ani uwzględniane w systemie Windows. Powinien być sformatowany na liście rozdzielanej przecinkami, np. 3000,9000,9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetuj porty</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Zignorowane porty</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy właściwość wsl2.networkingMode jest ustawiona na wartość Dublowana. Określa, z którymi portami mogą być powiązane aplikacje systemu Linux, które nie będą automatycznie przesyłane dalej ani uwzględniane w systemie Windows. Powinien być sformatowany na liście rozdzielanej przecinkami, na przykład 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Limit czasu początkowego automatycznego serwera proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy wsl2.autoProxy ma wartość true. Konfiguruje, jak długo (w milisekundach) protokół WSL będzie czekać na pobranie informacji o serwerze proxy HTTP podczas uruchamiania kontenera WSL. Jeśli ustawienia serwera proxy zostaną rozwiązane po tym czasie, wystąpienie WSL musi zostać uruchomione ponownie, aby można było użyć pobranych ustawień serwera proxy.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Limit czasu resetowania</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Limit czasu początkowego automatycznego serwera proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ma zastosowanie tylko wtedy, gdy właściwość wsl2.autoProxy ma wartość true. Określa, jak długo w milisekundach podsystem WSL będzie oczekiwać na pobranie informacji o serwerze proxy HTTP podczas uruchamiania kontenera WSL. Jeśli ustawienia serwera proxy zostaną rozpoznane po tym czasie, należy ponownie uruchomić wystąpienie podsystemu WSL, aby można było użyć pobranych ustawień serwera proxy.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Wiersz polecenia jądra</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Dodatkowe argumenty wiersza polecenia jądra.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Włącz przesyłanie dalej hosta lokalnego</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Wartość logiczna określająca, czy porty powiązane z symbolem wieloznacznym lub hostem lokalnym na maszynie wirtualnej WSL 2 powinny łączyć się z hosta za pośrednictwem hosta lokalnego localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Włącz przesyłanie dalej hosta lokalnego.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Wartość logiczna określająca, czy porty powiązane z symbolem wieloznacznym lub hostem lokalnym na maszynie wirtualnej podsystemu WSL 2 powinny łączyć się z hosta za pośrednictwem hosta lokalnego, dwukropek, port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Pamięć i procesor</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Rozmiar pamięci</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Ilość pamięci do przypisania do maszyny wirtualnej WSL 2.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetuj rozmiar</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Rozmiar pamięci</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ilość pamięci określonej w megabajtach do przypisania do maszyny wirtualnej WSL 2.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} ms</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Włącz wirtualizację zagnieżdżoną</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Wartość logiczna umożliwiająca włączenie lub wyłączenie wirtualizacji zagnieżdżonej, umożliwiając uruchamianie innych zagnieżdżonych maszyn wirtualnych wewnątrz protokołu WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Włącz wirtualizację zagnieżdżoną.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Wartość logiczna umożliwiająca włączanie lub wyłączanie wirtualizacji zagnieżdżonej, umożliwiając uruchamianie innych zagnieżdżonych maszyn wirtualnych wewnątrz podsystemu WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Tryb sieciowy</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Określa tryb sieci dla podsystemu WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tryb sieciowy.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Określa tryb sieci dla podsystemu WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Sieci</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Możesz pracować w różnych systemach operacyjnych ze wszystkimi plikami!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Dostęp do plików między systemami operacyjnymi</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Uzyskaj dostępu do plików systemu Windows z systemu Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Możesz uzyskać dostęp do plików systemu Windows z poziomu systemu Linux, przechodząc do lokalizacji „/mnt”, a następnie do litery dysku systemu Windows, jak w tym przykładzie dla dysku C:\n\n„cd /mnt/c”</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Uzyskaj dostępu do plików systemu Linux za pomocą Eksploratora plików</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Możesz wyświetlić pliki systemu Linux z Eksploratora plików, przechodząc do pliku „\\\\wsl.localhost\\” lub klikając ikonę „Linux”.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Uruchamiaj pliki i programy systemu Windows z poziomu podsystemu WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Nawet podczas korzystania z podsystemu WSL możesz uruchamiać pliki wykonywalne systemu Windows bezpośrednio z powłoki Bash. Spróbuj uruchomić\n„powershell.exe /c start .”, aby otworzyć Eksploratora plików w bieżącym folderze.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Uzyskiwanie dostępu do aplikacji sieciowych systemu Linux z systemu Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Jeśli tworzysz aplikację sieciową (na przykład aplikację działającą na serwerze NodeJS lub SQL) w dystrybucji systemu Linux, możesz uzyskać do niej dostęp z poziomu aplikacji systemu Windows (takiej jak przeglądarka internetowa Edge lub Chrome) przy użyciu hosta lokalnego (tak jak zwykle jest to możliwe). Oznacza to, że jeśli uruchomiono serwer z systemem Linux, który nasłuchuje portu 3000, możesz przejść do [http://localhost:3000] w przeglądarce Microsoft Edge w systemie Windows, aby uzyskać do niego dostęp.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Sieć w trybie dublowanym</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>Podsystem WSL obejmuje również nowy tryb sieci, nazywany trybem dublowanym, który dodaje zaawansowane funkcje, takie jak obsługa protokołu IPv6 i możliwość uzyskiwania dostępu do aplikacji sieciowych w sieci lokalnej.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o dostępie do plików między systemami operacyjnymi</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Podsystem Windows dla systemu Linux — Zapraszamy!</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>Podsystem WSL to doskonały sposób na wypróbowanie różnych dystrybucji systemu Linux.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Zarządzanie dystrybucją</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o podstawowych poleceniach podsystemu WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o importowaniu dowolnej dystrybucji systemu Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Polecenie Wyświetl listę dystrybucji do zainstalowania podsystemu WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>„wsl.exe -l -o”</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Polecenie Zainstaluj nazwaną dystrybucję podsystemu WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>„wsl.exe --install &lt;DistroName&gt;”</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Polecenie Wyświetl listę dostępnych dystrybucji podsystemu WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>„wsl.exe -l”</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Program Docker Desktop doskonale współpracuje z podsystemem WSL, aby ułatwić programowanie za pomocą kontenerów systemu Linux.\n\nNiektóre korzyści wynikające z używania programu Docker Desktop z podsystemem WSL to:\n\n• Polecenia platformy Docker można uruchamiać w podsystemie WSL lub w systemie Windows przy użyciu tego samego demona platformy Docker i obrazów.\n• Możesz bezproblemowo udostępniać pliki i foldery między systemami Windows i Linux przy użyciu automatycznego instalowania dysków z systemem Windows w podsystemie WSL.\n• Korzystając z preferowanych narzędzi i edytorów systemu Windows, możesz pracować nad kodem i plikami systemu Linux i na odwrót dzięki współdziałaniu podsystemu WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Integracja z programem Docker Desktop</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o używaniu podsystemu WSL z platformą Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Podsystem Windows dla systemu Linux (WSL) umożliwia uruchamianie ulubionych narzędzi, funkcji, aplikacji i przepływów pracy systemu Linux bezpośrednio w systemie Windows.\n\nPoświęć chwilę, aby zapoznać się z niektórymi ulubionymi funkcjami społeczności lub zapoznać się z naszą kompleksową dokumentacją.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>WSL — Zapraszamy!</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Najlepsze rozwiązania dotyczące konfiguracji</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Wprowadzenie do systemu Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentacja podsystemu Windows dla systemu Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Możesz używać graficznych aplikacji systemu Linux z natywnymi interakcjami systemu Windows, takimi jak alt-tab, uruchamianie menu Start, przypinanie paska zadań i obsługa wycinania i wklejania.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>Aplikacje graficznego interfejsu użytkownika</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>Podsystem WSL może korzystać z procesora GPU systemu Windows na potrzeby przepływów pracy uczenia maszynowego\n\nPliki binarne systemu Linux działające w podsystemie WSL mogą automatycznie korzystać z procesora GPU w systemie Windows na potrzeby przyspieszenia ich wydajności. Wystarczy zainstalować i uruchomić te przepływy pracy w taki sam sposób, jak na zwykłej maszynie z systemem Linux. Istnieje wiele różnych sposobów, aby rozpocząć pracę, począwszy od uruchamiania CUDA w kontenerze Docker, jeśli masz kartę graficzną NVIDIA, do uruchamiania PyTorch lub TensorFlow z DirectML na karcie graficznej AMD, Intel lub NVIDIA. Zobacz nasz przewodnik wprowadzający poniżej, aby dowiedzieć się więcej.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>Przyspieszanie procesora GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o przyspieszaniu procesora GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o aplikacjach graficznego interfejsu użytkownika podsystemu WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Oto lista aplikacji do wypróbowania (możesz zainstalować wszystkie te aplikacje w systemie Ubuntu, wpisując „sudo apt install &lt;The App Name&gt;”)\n\n    • gedit — podstawowy edytor tekstów\n    • audacity — nagrywanie i edytowanie plików dźwiękowych\n    • blender — tworzenie animacji i wizualizacji 3D\n    • gimp — edytowanie zdjęć\n    • nautilus — eksplorator plików systemu Linux\n    • vlc — odtwarzacz wideo</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Możesz łatwo uzyskiwać dostęp do aplikacji sieciowych w systemach operacyjnych Windows i Linux.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Integracja sieci</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o aplikacjach sieciowych</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o sieci w trybie dublowanym</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Podsystemu WSL można używać jako głównego środowiska deweloperskiego bezpośrednio z programu VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>Integracja z programem VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o używaniu podsystemu WSL z programem VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Jak zainstalować</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Po zainstalowaniu programu VS Code możesz zainstalować rozszerzenie Remote WSL z poziomu narzędzia Terminal Windows:\n\n„code –install-extension ms-vscode-remote.remote-wsl”</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Otwórz projektu podsystemu WSL w programie Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Aby otworzyć projekt w programie VS Code z poziomu dystrybucji podsystemu WSL, otwórz wiersz polecenia dystrybucji i uruchom polecenie „code .”, aby otworzyć plik projektu.\n\nMożesz również uzyskać dostęp do większej liczby opcji zdalnych programu VS Code za pomocą palety poleceń w samym programie VS Code. Naciśnij kombinację klawiszy „SHIFT+CTRL+P” na klawiaturze, aby otworzyć paletę poleceń, i wpisz „Remote-WSL” w celu wyświetlenia dostępnych opcji zdalnych programu VS Code, umożliwiając ponowne otwarcie folderu w sesji zdalnej, określenie dystrybucji, którą chcesz otworzyć i nie tylko.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Jak używać</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Funkcje opcjonalne</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Oświadczenie o ochronie prywatności</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Liczba procesorów</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Liczba procesorów logicznych do przypisania do maszyny wirtualnej WSL 2.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetuj liczbę</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Liczba procesorów</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Liczba procesorów logicznych do przypisania do maszyny wirtualnej WSL 2.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Pokrewne łącza:</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Informacje o wersji</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Włącz tryb awaryjny</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Uruchom podsystem WSL w „trybie awaryjnym”, który wyłącza wiele funkcji i jest przeznaczony do odzyskiwania dystrybucji, które są w złych stanach.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Włącz tryb awaryjny.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Uruchom podsystem WSL w „trybie awaryjnym”, który wyłącza wiele funkcji i jest przeznaczony do odzyskiwania dystrybucji, które są w złych stanach.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Informacje</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Deweloper</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Zarządzanie dystrybucją</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Integracja pulpitu platformy Docker</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>System plików</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Ogólne</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Przyspieszanie procesora GPU</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>Aplikacje graficznego interfejsu użytkownika</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Uruchom wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Pamięć i procesor</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Sieci</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Integracja sieci</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>WSL — Zapraszamy!</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Funkcje opcjonalne</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Ustawienia</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>Integracja z programem VS Code</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Co nowego</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Praca w różnych systemach plików</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problemy</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Domyślnie włącz rozrzedzony wirtualny dysk twardy</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Każdy nowo utworzony dysk VHD zostanie automatycznie rozrzedzony po włączeniu.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Domyślnie włącz rozrzedzony wirtualny dysk twardy.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Każdy nowo utworzony dysk VHD zostanie automatycznie rozrzedzony po włączeniu.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Zamień lokalizację pliku</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Bezwzględna ścieżka systemu Windows do wirtualnego dysku twardego wymiany.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Przeglądaj pliki wymiany</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Zamień lokalizację pliku</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Bezwzględna ścieżka systemu Windows do wirtualnego dysku twardego wymiany.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Rozmiar zamiany</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Ilość miejsca do dodania do maszyny wirtualnej WSL 2, 0 dla braku pliku wymiany. Magazyn wymiany to dyskowa pamięć RAM używana, gdy zapotrzebowanie na pamięć przekracza limit na urządzeniu sprzętowym.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Resetuj rozmiar</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Rozmiar zamiany</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ilość miejsca wymiany określonego w megabajtach do dodania do maszyny wirtualnej WSL 2. 0 dla pliku bez wymiany. Magazyn wymiany to dyskowa pamięć RAM używana, gdy zapotrzebowanie na pamięć przekracza limit na urządzeniu sprzętowym.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Włącz funkcję VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Użyj funkcji virtiofs zamiast planu 9, aby uzyskać dostęp do plików hosta, zwiększając szybkość.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Włącz funkcję Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Umożliwia instalowanie systemu plików 9P z hosta przy użyciu transportu virtio.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Limit czasu bezczynności maszyny wirtualnej</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Liczba milisekund bezczynności maszyny wirtualnej przed jej zamknięciem.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Limit czasu resetowania</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Limit czasu bezczynności maszyny wirtualnej</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Liczba milisekund bezczynności maszyny wirtualnej przed jej zamknięciem.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Kompiluj, uruchamiaj, debuguj i profiluj aplikacje działające w systemie WSL z poziomu programu Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Uruchamiaj i debuguj aplikacje w systemie WSL z poziomu programu Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o systemie WSL w programie Visual Studio dla deweloperów .NET</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Dowiedz się więcej o programie Visual Studio i systemie WSL dla deweloperów C++</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Możesz łatwo uruchamiać i debugować aplikacje .NET Core oraz wieloplatformowe aplikacje C++ w systemie Linux bez opuszczania programu Visual Studio, korzystając z Podsystemu Windows dla systemu Linux (WSL). Jeśli jesteś deweloperem międzyplatformowym, możesz użyć tej metody jako prostego sposobu na przetestowanie większej liczby środowisk docelowych.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Integracja z programem Visual Studio</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Integracja z programem Visual Studio</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/pt-BR/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Subsistema do Windows para Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>O Subsistema do Windows para Linux permite que os desenvolvedores executem um ambiente GNU/Linux -- incluindo a maioria das ferramentas, utilitários e aplicativos de linha de comando -- diretamente no Windows, sem modificações, sem a sobrecarga de uma máquina virtual tradicional ou configuração de inicialização dupla.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Falha ao desanexar o disco: {}. Para obter mais detalhes, execute 'dmesg' dentro do WSL2.\nPara forçar o WSL2 a parar e desanexar o disco, execute 'wsl.exe {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>O disco \"{}\" já está anexado.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Esse volume já está montado dentro do WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Um disco com esse nome já está montado; desmonte o disco ou escolha um novo nome e tente novamente.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>O nome de montagem especificado contém um caractere '/' inválido. Tente novamente sem o caractere inválido.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>O disco foi montado com êxito como '/mnt/wsl/{}'.\nObservação: o local será diferente se você tiver modificado a configuração automount.root em /etc/wsl.conf.\nPara desmontar e desanexar o disco, execute 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>O disco foi anexado, mas falhou ao montar: {}.\nPara obter mais detalhes, execute 'dmesg' dentro do WSL2.\nPara desanexar o disco, execute 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>A seguir está uma lista de distribuições válidas que podem ser instaladas.\nInstale usando 'wsl.exe {} &lt;Distro&gt;'.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>O nome de distribuição já foi definido.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>O local de instalação fornecido já está em uso.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Já existe uma distribuição com o nome fornecido. Use --name escolher um nome diferente.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Não há distribuição com o nome fornecido.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>É necessário acesso de administrador para montar um disco.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>A exportação da distribuição falhou.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Executando um upgrade único do sistema de arquivos do Subsistema do Windows para Linux para esta distribuição...\n</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Não é possível iniciar porque outra instância está sendo executada sem privilégios. Instâncias com e sem privilégios não têm permissão para serem executadas simultaneamente.\n</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>A importação da distribuição falhou.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Instalando o componente opcional do Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Baixando: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Instalando: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importando distribuição</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} foi baixado.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Subsistema do Windows para Linux está retomando uma instalação anterior...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Nome de distribuição inválido: '{}'.\nPara obter uma lista de distribuições válidas, use 'wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>A Subsistema do Windows para Linux instância foi finalizada.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Argumento de linha de comando inválido: {}\nUse '{} --help' para obter uma lista de argumentos com suporte.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>O argumento de linha de comando {} requer um valor.\nUse '{} --help' para obter uma lista de argumentos com suporte.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Configurações do console incompatíveis. Para usar esse recurso, o console herdado deve ser desabilitado.\n</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} não é um inteiro válido.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Uma instalação, desinstalação ou conversão está em andamento para esta distribuição.\n</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Iniciando {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Não é possível iniciar porque outra instância está sendo executada com privilégios. Instâncias com e sem privilégios não têm permissão para serem executadas simultaneamente.\n</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Subsistema do Windows para Linux não tem distribuições instaladas.\nVocê pode resolve isso instalando uma distribuição com as instruções abaixo:\n\nUse 'wsl.exe --list --online' para listar distribuições disponíveis\ne 'wsl.exe --install &lt;Distro&gt;' para instalar.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Não há distribuições em execução.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Padrão)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Distribuições do Subsistema do Windows para Linux:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Distribuição Padrão: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Versão Padrão: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Cancelando.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Usuário não encontrado.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Para informações sobre as principais diferenças com o WSL 2, visite https://aka.ms/wsl2\n</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Conversão em andamento. Isso pode levar alguns minutos.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>A distribuição já é a versão solicitada.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>A distribuição Herdada não dá suporte a WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>O WSL2 não pode ser iniciado porque a virtualização não está habilitada nesta máquina.\nVerifique se o componente opcional \"Plataforma da Máquina Virtual\" está habilitado e se a virtualização está ativada nas configurações de firmware do computador.\n\nHabilitar a \"Plataforma da Máquina Virtual\" executando: wsl.exe --install --no-distribution\n\nPara obter informações, visite https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Muitos discos rígidos virtuais ou discos físicos estão anexados.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD já montado via wsl.exe --mount, desmonte o disco antes de iniciar.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 não é compatível com a configuração atual da máquina. \n Habilite o componente opcional \"Windows Subsystem for Linux\" para usar WSL1.\n</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Esta operação só é compatível com WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Uso:\n    --networking-mode\n       Exibir modo de rede atual.\n\n    --msal-proxy-path\n        Exibir o caminho para o aplicativo proxy MSAL.\n\n    --vm-id\n        Exibir a ID da VM do WSL.\n\n    --version\n        Exibir a versão do pacote WSL.\n\n    -n\n        Não imprima uma nova linha.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Uso:\n    -a\n        Forçar o resultado ao formato de caminho absoluto.\n    -u\n        Traduzir de um caminho do Windows para um caminho WSL (padrão).\n    -w\n        Traduzir de um caminho WSL para um caminho do Windows.\n    -m\n        Traduzir de um caminho WSL para um caminho do Windows, com '/' em vez de '\\\\'\n\nExemplo: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Executa operações administrativas em Subsistema do Windows para Linux\n\nUso:\n    /l, /list [Option]\n        Lista as distribuições registradas.\n        /all - Lista todas as distribuições, incluindo distribuições que estão\n               sendo instaladas ou desinstaladas no momento.\n\n        /running - Lista apenas distribuições que estão em execução no momento.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Define a distribuição como o padrão.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Encerra a distribuição especificada.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Cancela o registro da distribuição e exclui o sistema de arquivos raiz.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Todos os direitos reservados.\nPara obter informações sobre a privacidade deste produto, visite https://aka.ms/privacy.\n\nUso: wsl.exe [Argumento] [Opções...] [Linha de comando]\n\nArgumentos para execução de binários do Linux:\n\n    Se nenhuma linha de comando for fornecida, o wsl.exe será iniciado no shell padrão.\n\n    --exec, -e &lt;CommandLine&gt;\n        Executa o comando especificado sem usar o shell padrão do Linux.\n\n    --shell-type &lt;standard|login|none&gt;\n        Executa o comando especificado com o tipo de shell fornecido.\n\n    --\n        Passa a linha de comando restante como está.\n\nOpções:\n    --cd &lt;Directory&gt;\n        Define o diretório especificado como o diretório de trabalho atual.\n        Se ~ for usado, o caminho da página inicial do usuário do Linux será usado. Se o caminho começar\n        com um caractere /, será interpretado como um caminho do Linux absoluto.\n        Caso contrário, o valor deverá ser um caminho do Windows absoluto.\n\n    --distribution, -d &lt;DistroName&gt;\n        Executa a distribuição especificada.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Executa a ID da distribuição especificada.\n\n    --user, -u &lt;UserName&gt;\n        Executa como o usuário especificado.\n\n    --system\n        Inicia um shell para a distribuição do sistema.\n\nArgumentos para gerenciar o Subsistema do Windows para Linux:\n\n    --help\n        Exibe as informações de uso.\n\n    --debug-shell\n        Abre um shell de depuração do WSL2 para fins de diagnóstico.\n\n    --install [Distribuição] [Opções...]\n        Instala uma distribuição do Subsistema do Windows para Linux.\n        Para uma lista de distribuições válidas, use ''wsl.exe --list --online''.\n\n        Opções:\n            --enable-wsl1\n                Habilita o suporte para o WSL1.\n\n            --fixed-vhd\n                Cria um disco de tamanho fixo para armazenar a distribuição.\n\n            --from-file &lt;Path&gt;\n                Instala uma distribuição de um arquivo local.\n\n            --legacy\n                Usa o manifesto de distribuição herdado.\n\n            --location &lt;Location&gt;\n                Define o caminho de instalação para a distribuição.\n\n            --name &lt;Name&gt;\n                Define o nome da distribuição.\n\n            --no-distribution\n                Instala apenas os componentes opcionais necessários, e não instala uma distribuição.\n\n            --no-launch, -n\n                Não inicia a distribuição após a instalação.\n\n            --version &lt;Version&gt;\n                Especifica a versão a ser usada para a nova distribuição.\n\n            --vhd-size &lt;MemoryString&gt;\n                Especifica o tamanho do disco para armazenar a distribuição.\n\n            --web-download\n                Baixa a distribuição da internet em vez da Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Altera as opções específicas da distribuição.\n\n        Opções:\n            --move &lt;Location&gt;\n                Move a distribuição para um novo local.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Defina o VHD de distribuição como esparso, permitindo que o espaço em disco seja recuperado automaticamente.\n\n            --set-default-user &lt;Username&gt;\n                Define o usuário padrão da distribuição.\n\n            --resize &lt;MemoryString&gt;\n                Redimensiona o disco da distribuição para o tamanho especificado.\n\n    --mount &lt;Disk&gt;\n        Anexa e monta um disco físico ou virtual em todas as distribuições do WSL 2.\n\n        Opções:\n            --vhd\n                Especifica que &lt;Disk&gt; refere-se a um disco rígido virtual.\n\n            --bare\n                Anexa o disco ao WSL2, mas não o monta.\n\n            --name &lt;Name&gt;\n                Monta o disco usando um nome personalizado para o ponto de montagem.\n\n            --type &lt;Type&gt;\n                O sistema de arquivos a ser usado ao montar um disco; se não especificado, o padrão é ext4.\n\n            --options &lt;Options&gt;\n                Opções de montagem adicionais.\n\n            --partition &lt;Index&gt;\n                O índice da partição a ser montada, se não especificado, por padrão, aplica-se a todo o disco.\n\n    --set-default-version &lt;Version&gt;\n        Altera a versão de instalação padrão para novas distribuições.\n\n    --shutdown\n        Encerra imediatamente todas as distribuições em execução e a\n        máquina virtual utilitária leve do WSL 2.\n\n        Opções:\n            --force\n                Encerra a máquina virtual WSL 2 mesmo que uma operação esteja em andamento. Pode causar perda de dados.\n\n    --status\n        Mostra o status do Subsistema do Windows para Linux.\n\n    --unmount [Disco]\n        Desmonta e desanexa um disco de todas as distribuições WSL2.\n        Desmonta e desanexa todos os discos se for chamado sem argumento.\n\n    --uninstall\n        Desinstala o pacote do Subsistema do Windows para Linux deste computador.\n\n    --update\n        Atualiza o pacote do Subsistema do Windows para Linux.\n\n        Opções:\n            --pre-release\n                Baixa uma versão de pré-lançamento, se disponível.\n\n    --version, -v\n        Exibe informações da versão.\n\nArgumentos para gerenciar as distribuições no Subsistema do Windows para Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Opções]\n        Exporta a distribuição para um arquivo tar.\n        O nome do arquivo pode ser - para stdout.\n\n        Opções:\n            --format &lt;Format&gt;\n                Especifica o formato de exportação. Valores com suporte: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Opções]\n        Importa o arquivo tar especificado como uma nova distribuição.\n        O nome do arquivo pode ser - para stdin.\n\n        Opções:\n            --version &lt;Version&gt;\n                Especifica a versão a ser usada para a nova distribuição.\n\n            --vhd\n                Especifica que o arquivo fornecido é um arquivo .vhd ou .vhdx, não um arquivo tar.\n                Esta operação faz uma cópia do arquivo VHD no local de instalação especificado.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Importa o arquivo VHD especificado como uma nova distribuição.\n        Este disco rígido virtual deve ser formatado com o tipo de sistema de arquivos ext4.\n\n    --list, -l [Opções]\n        Lista as distribuições.\n\n        Opções:\n            --all\n                Lista todas as distribuições, incluindo as distribuições que estão\n                atualmente sendo instaladas ou desinstaladas.\n\n            --running\n                Lista somente as distribuições que estão em execução no momento.\n\n            --quiet, -q\n                Mostra apenas os nomes de distribuição.\n\n            --verbose, -v\n                Mostra informações detalhadas sobre todas as distribuições.\n\n            --online, -o\n                Exibe uma lista de distribuições disponíveis para instalação com ''wsl.exe --install''.\n\n    --set-default, -s &lt;Distro&gt;\n        Define a distribuição como o padrão.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Altera a versão da distribuição especificada.\n\n    --terminate, -t &lt;Distro&gt;\n        Encerra a distribuição especificada.\n\n    --unregister &lt;Distro&gt;\n        Cancela o registro da distribuição e exclui o sistema de arquivos raiz.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>Versão do WSL: {}\nVersão do kernel: {}\nVersão do WSLg: {}\nVersão do MSRDC: {}\nVersão do Direct3D: {}\nVersão do DXCore: {}\nVersão do Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>Versão do MSBuild: {}\nConfirmação: {}\nTempo de compilação: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>O kernel personalizado especificado em {} não foi encontrado: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>O VHD dos módulos de kernel personalizados em {} não foi encontrado: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>A distribuição do sistema personalizado especificada em {} não foi encontrada ou não tem o formato correto.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Todos os direitos reservados.\nPara informações sobre privacidade deste produto, visite https://aka.ms/privacy.\n\nUso: wslg.exe [Argumento] [Opções...] [CommandLine]\n\nArgumentos:\n    --cd &lt;Directory&gt;\n        Define o diretório especificado como o diretório de trabalho atual.\n        Se ~ for usado, o caminho inicial do usuário do Linux será usado. Se o caminho começar\n        com um caractere /, ele será interpretado como um caminho absoluto do Linux.\n        Caso contrário, o valor deverá ser um caminho absoluto do Windows.\n\n    --distribution, -d &lt;Distro&gt;\n        Executa a distribuição especificada.\n\n    --user, -u &lt;UserName&gt;\n        Executa como o usuário especificado.\n\n    --shell-type &lt;standard|login|none&gt;\n        Executa o comando especificado com o tipo de shell fornecido.\n\n    --help\n        Exibe as informações de uso.\n\n    --\n        Passa a linha de comando restante como está.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Pressionar qualquer tecla para continuar...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>O argumento {} não tem um parâmetro necessário.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Exportação em andamento. Isso pode levar alguns minutos.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Importação em andamento, isso pode levar alguns minutos.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>O suporte ao aplicativo GUI está desabilitado via {} ou /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Verificando se há atualizações.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Esta distribuição só está disponível no Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount no ARM64 requer o Windows versão 27653 ou mais recente.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>A versão mais recente do Subsistema do Windows para Linux já está instalada.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Atualizando Subsistema do Windows para Linux para a versão: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Este aplicativo requer o Subsistema do Windows para Linux Opcional.\nInstale-o executando: wsl.exe --install --no-distribution\nTalvez seja necessário reiniciar o sistema para que as alterações entrem em vigor.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>O arquivo especificado deve ter a extensão de arquivo {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>O arquivo especificado deve ter a extensão de arquivo {} ou {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>O VmSwitch '{}' não foi encontrado. Switches disponíveis: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>A rede em ponte requer que wsl2.vmSwitch seja definido.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Falha ao buscar a lista de distribuição de '{}'. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Falha ao anexar o disco '{}' ao WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Falha ao redimensionar o disco.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Nenhum valor encontrado.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>A versão do Windows {} não dá suporte à versão empacotada do Subsistema do Windows para Linux.\nInstale a atualização necessária por meio do Windows Update ou por meio de: {}\nPara obter informações, visite https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>A execução do shell de depuração requer wsl.exe como Administrador.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>O processo de instalação da distribuição '{}' falhou com o código de saída: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Formato de GUID inválido: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Nome de seção inválido em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Nome de chave inválido em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>{} esperado em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Caractere de escape inválido: \"{}\" em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Chave desconhecida \"{}\" em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Valor inteiro inválido '{}' para a chave '{}' em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Valor de IP inválido '{}' para a chave '{}' em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Valor booliano inválido '{}' para a chave '{}' em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Endereço mac inválido '{}' para a chave '{}' em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Falha ao abrir o arquivo de configuração {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Ocorreram erros durante a inicialização do WSL</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Falha ao iniciar a sessão de usuário do sistema para '{}'. Consulte o journalctl para obter mais detalhes.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Abrir EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Esta distribuição não contém um nome padrão. Use --name para escolher o nome da distribuição.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Os argumentos {} e {} não podem ser especificados ao mesmo tempo.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>O argumento {} requer o argumento {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Nome de distribuição inválido: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Usando registro de distribuição herdado. Considere usar uma distribuição baseada em tar.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Registros de distribuição herdados não dão suporte ao argumento --version dados.</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>A distribuição \"{}\" é fornecida por um manifesto de substituição.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>O hash de distribuição não corresponde. Esperado: {}, hash real: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Cadeia de caracteres hexadecimal inválida: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>Não há suporte para '{}' ao instalar distribuições herdadas.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Distribuição instalada com êxito. Ele pode ser iniciado por meio de 'wsl.exe -d {}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Cadeia de caracteres de memória inválida '{}' .wslconfig entrada '{}' em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Falha ao criar o disco de permuta em '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Não há suporte para virtualização aninhada nesta máquina.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Falha ao criar o ponto de extremidade de rede com o endereço: '{}', novo endereço atribuído: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Falha ao criar rede virtual com intervalo de endereços: '{}', nova rede criada com intervalo: '{}', {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>MODO DE SEGURANÇA HABILITADO - muitos recursos serão desabilitados</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Não há suporte para o modo de rede espelhado: {}.\nVoltando à rede NAT.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>O Kernel do Linux versão 5.10 ou mais recente é necessário</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Versão do Windows {}. {} não tem os recursos necessários</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Não há suporte para o firewall do Hyper-V</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>Não há suporte para túnel DNS</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>A configuração wsl2.localhostForwarding não tem efeito ao usar o modo de rede espelhado</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Foi detectada uma alteração de Proxy Http no host. Reinicie o WSL para aplicar a alteração.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Uma configuração de proxy de host local foi detectada, mas não espelhada em WSL. O WSL no modo NAT não dá suporte a proxies de localhost.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Uma configuração de proxy IPv6 foi detectada, mas não espelhada em WSL. O WSL no modo NAT não dá suporte a IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Uma configuração de proxy IPv6 de localhost foi detectada, mas não espelhada em WSL. O WSL não dá suporte a proxies IPv6 de localhost.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Ocorreu um erro inesperado ao tentar resolve configurações de proxy, as configurações de proxy não foram espelhadas em WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Erro ao acessar o Registro. Caminho: '{}'. Erro: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Falha ao iniciar o processo de retransmissão localhost. Erro: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Falha ao iniciar a rede virtual - instale a Plataforma de Máquina Virtual do componente opcional executando: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Falha ao configurar a rede (networkingMode {}), voltando para networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Falha ao habilitar o componente do Windows '{}' (código de saída {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Um erro fatal foi retornado pelo plug-in '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Um erro fatal foi retornado pelo plug-in '{}'. Mensagem de erro: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>O plug-in '{}' requer uma versão mais recente do WSL. Execute: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>A configuração .wslconfig '{}' está desabilitada pela política do computador.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>O shell de depuração está desabilitado pela política do computador.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount está desabilitado pela política do computador.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>O WSL1 está desabilitado pela política do computador.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Execute 'wsl.exe --set-version {} 2' para atualizar para o WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>O WSL está concluindo uma atualização...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Falha na atualização (código de saída: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Falha ao desinstalar (código de saída: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Arquivo de log: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Falha ao remover o pacote MSIX (erro: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Falha ao instalar o pacote MSIX (erro: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Os componentes opcionais necessários para executar o WSL não estão instalados.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Instalar componentes ausentes.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>O arquivo importado não é uma distribuição válida do Linux.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Pressione qualquer tecla para sair...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Uma nova versão do Subsistema do Windows para Linux está disponível.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Atualize para a versão {} ou exiba suas notas de versão abaixo.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Atualizar</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Ver Documentos</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Não foi possível concluir a operação porque o VHD está em uso no momento. Para forçar o WSL a interromper o uso: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} não é um booliano válido, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>O VHD esparso é compatível apenas com o WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>A execução do WSL como sistema local não é suportada.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Dica de Desempenho:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>O uso de uma operação de uso intensivo de E/S, como {} em suas unidades do Windows, terá desempenho ruim. Considere mover seus arquivos de projeto para o sistema de arquivos do Linux para melhorar o desempenho. Clique abaixo para saber mais.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Exibir Documentos</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Não Mostrar Novamente</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>A Máquina Virtual WSL2 falhou.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>O rastreamento de pilha foi salvo em: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Falha ao iniciar a distribuição. Código de erro: {}, etapa de falha: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>A distribuição não pôde ser iniciada porque seu disco virtual está corrompido.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Chave de configuração duplicada '{}' em {}:{} (Chave em conflito: '{}' em {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Valor inválido '{}' para a chave de configuração '{}' em {}:{} (Valores válidos: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Aguardando a conclusão do comando OOBE para a distribuição \"{}\"...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Falha ao ler a propriedade '{}' da distribuição {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Parece um arquivo VHD. Use --vhd para importar um VHD em vez de um tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Falha ao instalar {} do Microsoft Store: {}\nTentando baixar a Web...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Nenhuma distribuição padrão foi configurada. Forneça uma distribuição para instalar.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p está desabilitado, voltando para 9p com transporte vsock.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>A instalação do WSL parece estar corrompida (Código de erro: {}).\nPressione qualquer tecla para reparar o WSL ou CTRL-C cancelar.\nEste prompt expirará em 60 segundos.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Falha ao analisar o perfil do terminal ao registrar a distribuição: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Tamanho inválido: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nCódigo de erro: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Documento JSON inválido. Erro de análise: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors não podem exceder o número de processadores lógicos no sistema ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Falha ao consultar o modo de rede</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Módulos de kernel personalizados foram fornecidos sem especificar um kernel personalizado. Consulte o https://aka.ms/wslcustomkernel para obter mais informações.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Devido a um problema de compatibilidade atual com o Cliente do Acesso Global Seguro, o Túnel DNS está desabilitado.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>O suporte a VHD esparso está atualmente desativado devido a possível corrupção de dados.\nPara forçar uma distribuição a usar um VHD esparso, execute:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Falha ao montar {}, consulte dmesg para obter mais detalhes.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>O processamento de /etc/fstab com o comando mount -a falhou.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Erro ao montar o disco de distribuição. Ele foi montado como somente leitura como fallback.\nConsulte as instruções de recuperação em: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Falha ao traduzir '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Algo não deu certo. Tente novamente mais tarde.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Sobre</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Sobre as Configurações do Subsistema do Windows para Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Todos os direitos reservados.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Configurações do Subsistema do Windows para Linux</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Subsistema do Windows para Linux configurações permitem que os desenvolvedores gerenciem o arquivo .wslconfig usando um aplicativo baseado em GUI.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Recuperação automática de memória</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Libera automaticamente a memória armazenada em cache após detectar o uso de CPU ocioso. Defina como gradual para liberação lenta e dropcache para liberação instantânea de memória armazenada em cache.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Recuperação automática de memória.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Libera automaticamente a memória armazenada em cache depois de detectar o uso ocioso de CPU. Defina como gradual para liberação lenta e dropcache para liberação instantânea de memória armazenada em cache.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy Automático habilitado</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Permite que o WSL utilize as informações de proxy HTTP do Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy Automático habilitado.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Permite que o WSL utilize as informações de proxy HTTP do Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Usar a melhor análise de DNS de esforço</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Aplicável somente quando wsl2.dnsTunneling é definido como verdadeiro. Quando definido como true, o Windows extrairá a pergunta da solicitação DNS e tentará resolve-la, ignorando os registros desconhecidos.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Usar a melhor análise de DNS de esforço.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.dnsTunneling está definido como verdadeiro. Quando definido como verdadeiro, o Windows extrairá a pergunta do pedido DNS e tentará resolve-la, ignorando os registos desconhecidos.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Kernel personalizado</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para um kernel do Linux personalizado.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procurar kernels</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Kernel personalizado</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para um kernel do Linux personalizado.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Personalizar módulos do kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para um VHD de módulos de kernel do Linux personalizados.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procurar módulos de kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Personalizar módulos do kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para um VHD de módulos de kernel do Linux personalizados.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Distribuição de sistema personalizada</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procurar distros</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Especifique um caminho para um VHD que será carregado como uma distribuição personalizada do sistema, usado principalmente para ativar aplicativos gui no WSL. [saiba mais sobre distros do sistema aqui].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Distribuição de sistema personalizada</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Especifique um caminho para um VHD que será carregado como uma distribuição personalizada do sistema, usado principalmente para ativar aplicativos gui no WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Habilitar console de depuração</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Booliano para ativar uma janela do console de saída que mostra o conteúdo do dmesg no início de uma instância de distribuição WSL 2.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar console de depuração.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleano para ativar uma janela da consola de saída que mostra o conteúdo das mensagens de diagnóstico no início de uma instância de distro WSL 2.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Tamanho Padrão do VHD</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>O tamanho máximo padrão para o VHD (disco rígido virtual) WSL expansível somente para distribuições recém-criadas.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Redefinir tamanho</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tamanho Padrão do VHD</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>O tamanho máximo padrão, especificado em megabytes, para o VHD (disco rígido virtual) WSL expansível somente para distribuições recém-criadas.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Desenvolvedor</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentação</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Alterar o modo DrvFS</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Altera a implementação de acesso a arquivos entre SO no WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy DNS habilitado</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Aplicável somente quando wsl2.networkingMode está definido como NAT. Booliano para informar o WSL para configurar o Servidor DNS no Linux para o NAT no host. A configuração como false espelho dns do Windows para o Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy DNS habilitado.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.networkingMode está definido como NAT. Booleano para informar o WSL para configurar o Servidor DNS no Linux para o NAT no anfitrião. Definir como falso irá espelho servidores DNS do Windows para Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>Túnel DNS habilitado</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Altera a maneira como as solicitações de DNS são proxy do WSL para o Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Túnel DNS habilitado.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Altera a maneira como as solicitações de DNS são proxy do WSL para o Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Sistema de Arquivos</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Habilitar aplicativos GUI</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Booliano para ativar ou desativar o suporte para aplicativos GUI ([WSLg]) no WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar aplicativos GUI.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleano para ativar ou desativar o suporte para aplicações GUI (conhecidas como WSL g) no WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Habilitar contadores de desempenho de hardware</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Habilita contadores de desempenho de hardware para Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar contadores de desempenho de hardware.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Habilita contadores de desempenho de hardware para Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Firewall do Hyper-V habilitado</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Habilita o firewall do Hyper-V, que permite que as regras do Firewall do Windows, bem como regras específicas do tráfego do Hyper-V, filtrem o tráfego de rede WSL.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Firewall do Hyper-V habilitado.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ativa a firewall do Hyper-V que permite que as regras da Firewall do Windows, bem como regras específicas do tráfego hyper-V, filtrem o tráfego de rede WSL.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Loopback de Endereço de Host</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Aplicável somente quando wsl2.networkingMode está definido como espelhado. Quando definido como True, permitirá que o Contêiner se conecte ao Host ou ao Host para se conectar ao Contêiner por um endereço IP atribuído ao Host. Observe que o endereço de loopback 127.0.0.1 sempre pode ser usado - essa opção permite que todos os endereços IP locais atribuídos corretamente também sejam usados.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Loopback de Endereço de Host.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.networkingMode está definido como espelhado. Quando definido como Verdadeiro, permitirá ao Contentor ligar ao Anfitrião ou ao Anfitrião ligar ao Contentor através de um endereço IP atribuído ao Anfitrião. Tenha em atenção que o endereço de loopback 127.0.0.1 pode sempre ser utilizado - esta opção também permite a utilização de todos os endereços IP locais atribuídos adicionalmente.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Portas ignoradas</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Aplicável somente quando wsl2.networkingMode está definido como espelhado. Especifica as portas às quais os aplicativos Linux podem se associar que não serão encaminhadas ou consideradas automaticamente no Windows. Deve ser formatado em uma lista separada por vírgulas, por exemplo: 3000.9000.9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Redefinir portas</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Portas ignoradas</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Aplicável somente quando wsl2.networkingMode está definido como espelhado. Especifica as portas às quais os aplicativos Linux podem se associar que não serão encaminhadas ou consideradas automaticamente no Windows. Deve ser formatado em uma lista separada por vírgulas, por exemplo, 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Tempo limite inicial do Proxy Automático</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Aplicável somente quando wsl2.autoProxy é definido como verdadeiro. Configura por quanto tempo (em milissegundos) o WSL aguardará pela recuperação de informações de proxy HTTP ao iniciar um contêiner WSL. Se as configurações de proxy forem resolvidas após esse período, a instância do WSL deverá ser reiniciada para usar as configurações de proxy recuperadas.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Redefinir tempo limite</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tempo limite inicial do Proxy Automático</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Aplicável somente quando wsl2.autoProxy está definido como true. Configura por quanto tempo, em milissegundos, o WSL aguardará pela recuperação de informações de proxy HTTP ao iniciar um contêiner WSL. Se as configurações de proxy forem resolvidas após esse período, a instância do WSL deverá ser reiniciada para usar as configurações de proxy recuperadas.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Linha de Comando do Kernel</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Argumentos adicionais de linha de comando do kernel.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Habilitar o encaminhamento de localhost</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Booliano que especifica se as portas associadas a curinga ou localhost na VM WSL 2 devem ser conectáveis do host via localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar o encaminhamento de localhost.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Especificação booleana se as portas vinculadas a caráter universal ou localhost na VM WSL 2 devem ser conectáveis a partir do anfitrião através de localhost, dois pontos, porta.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Memória e processador</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Tamanho da Memória</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>A quantidade de memória a ser atribuída à VM do WSL 2.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Redefinir tamanho</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tamanho da Memória</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>A quantidade de memória, especificada em megabytes, para atribuir à VM WSL 2.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} milissegundos</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Habilitar virtualização aninhada</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Booliano para ativar ou desativar a virtualização aninhada, permitindo que outras VMs aninhadas sejam executadas dentro do WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar virtualização aninhada.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleano para ativar ou desativar a virtualização aninhada, permitindo que outras VMs aninhadas sejam executadas dentro do WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Modo de rede</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Especifica o modo de rede para WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Modo de rede.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Especifica o modo de rede para WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Rede</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Você pode trabalhar nos dois sistemas operacionais com todos os seus arquivos!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Acesso a arquivos em dois sistemas operacionais</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Acessar seus arquivos do Windows a partir do Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Você pode acessar seus arquivos do Windows a partir do Linux navegando até ‘/mnt’ e, em seguida, até a letra da unidade do Windows, como neste exemplo para a unidade C:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Acessar seus arquivos do Linux com o Explorador de Arquivos</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Você pode visualizar seus arquivos Linux no Explorador de Arquivos navegando até '\\\\wsl.localhost\\' ou clicando no ícone 'Linux'.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Abrir arquivos e programas do Windows a partir do WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Mesmo enquanto estiver usando o WSL, você poderá executar seus executáveis do Windows diretamente a partir do bash. Experimente executar\n'powershell.exe /c start .' para abrir o Explorador de Arquivos na pasta atual.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Como acessar aplicativos de rede do Linux a partir do Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Se estiver criando um aplicativo de rede (por exemplo, um aplicativo em execução em um NodeJS ou SQL Server) na sua distribuição do Linux, você poderá acessá-lo a partir de um aplicativo do Windows (como o seu navegador da internet Edge ou Chrome) usando o localhost (exatamente como faria normalmente). Isso significa que, se você iniciou um servidor Linux que está escutando na porta 3000, você pode ir para [http://localhost:3000] no Edge no Windows para acessá-lo.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Rede no modo espelhado</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>O WSL também inclui um novo modo de rede chamado modo espelhado, que adiciona recursos avançados como o suporte a IPv6 e a capacidade de acessar seus aplicativos de rede na sua rede local.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre o acesso a arquivos em dois sistemas operacionais</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Bem-vindo ao Subsistema do Windows para Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>O WSL é uma maneira excelente de experimentar diferentes distribuições do Linux.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Gerenciamento de Distribuições</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre os comandos básicos do WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre como importar qualquer distribuição do Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Comando para listar distribuições do WSL instaláveis</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Comando para instalar uma distribuição WSL nomeada</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Comando para listar distribuições do WSL disponíveis</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>O Docker Desktop funciona muito bem com o WSL para ajudar você a desenvolver com contêineres do Linux.\n\nAlguns dos benefícios de usar o Docker Desktop com o WSL são os seguintes:\n\n• Você pode executar comandos do Docker no WSL ou no Windows usando o mesmo daemon e imagens do Docker.\n• Você pode compartilhar arquivos e pastas entre o Windows e o Linux de forma integrada, usando a montagem automática de unidades do Windows no WSL.\n• Você pode usar as ferramentas e editores do Windows de sua preferência para trabalhar em arquivos e código do Linux e vice-versa, graças à interoperabilidade do WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Integração do Docker Desktop</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre como usar o WSL com o Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>O Subsistema do Windows para Linux (WSL) permite que você execute suas ferramentas, utilitários, aplicativos e fluxos de trabalho favoritos do Linux diretamente no Windows.\n\nReserve um momento para visualizar alguns dos recursos favoritos da comunidade ou conferir nossa documentação abrangente.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Bem-vindo ao WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Boas práticas para a instalação</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Comece a usar o Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentação do Subsistema do Windows para Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Você pode usar aplicativos de base gráfica do Linux com interações nativas do Windows, como alt-tab, inicialização do menu iniciar, fixação da barra de tarefas e suporte para recortar e colar.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>Aplicativos de GUI</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>O WSL pode aproveitar sua GPU do Windows para fluxos de trabalho de Machine Learning\n\nBinários do Linux em execução no WSL podem usar automaticamente a GPU no Windows para acelerar seu desempenho. Você só precisa instalar e executar esses fluxos de trabalho da mesma forma que faria em um computador Linux normal. Existem várias maneiras de começar, desde executar o CUDA em um contêiner do Docker se você tiver uma placa gráfica NVIDIA até executar PyTorch ou TensorFlow com DirectML em sua placa gráfica AMD, Intel ou NVIDIA. Confira nosso painel de introdução abaixo para saber mais.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>Aceleração da GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre a aceleração de GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre os aplicativos de GUI do WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Aqui está uma lista de aplicativos para experimentar (Você pode instalar todos esses no Ubuntu digitando 'sudo apt install &lt;O Nome do Aplicativo&gt;') \n\n    • gedit: editor de texto básico\n    • audacity: gravar e editar arquivos de áudio\n    • blender: criar visualizações e animações 3D\n    • gimp: editar fotos\n    • nautilus: um explorador de arquivos do Linux\n    • vlc: player de vídeo</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Você pode acessar aplicativos de rede nos sistemas operacionais Windows e Linux com facilidade.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Integração de Rede</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre aplicativos de rede</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre o uso da rede no modo espelhado</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Você pode usar o WSL como seu ambiente de desenvolvimento em período integral diretamente do VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>Integração com o VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre como usar o WSL com o VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Como instalar</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Depois de instalar o VS Code, você pode instalar a extensão Remote WSL a partir do Terminal do Windows:\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Abrir um projeto do WSL no Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Para abrir um projeto no VS Code a partir da sua distribuição WSL, abra a linha de comando da distribuição e execute 'code .' para abrir um arquivo de projeto. \n\nVocê também pode acessar mais opções do VS Code Remote através da paleta de comandos dentro do próprio VS Code. Pressione \"SHIFT+CTRL+P\" no teclado para abrir a paleta de comandos e digite 'Remote-WSL' para ver uma lista das opções do VS Code Remote disponíveis, permitindo reabrir a pasta em uma sessão remota, especificar qual distribuição você deseja abrir e mais.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Como usar</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Recursos Opcionais</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Política de Privacidade</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Contagem do Processador</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Quantos processadores lógicos atribuir à VM do WSL 2.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Redefinir contagem</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Contagem do Processador</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Quantos processadores lógicos atribuir à VM do WSL 2.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Links relacionados</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Notas de versão</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Habilitar modo seguro</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Execute o WSL em \"Modo de Segurança\", que desabilita muitos recursos e destina-se a ser usado para recuperar distribuições que estão em estados inválidos.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar modo seguro.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Execute o WSL no \"Modo de Segurança\", o que desativa muitas funcionalidades e se destina a ser utilizado para recuperar distribuições que estejam em estados incorretos.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Sobre</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Desenvolvedor</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Gerenciamento de Distribuições</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Integração da Área de Trabalho do Docker</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Sistema de Arquivos</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Geral</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Aceleração da GPU</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>Aplicativos de GUI</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Iniciar wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Memória e processador</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Rede</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Integração de Rede</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Bem-vindo ao WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Recursos Opcionais</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Configurações</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>Integração com o VS Code</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Novidades</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Trabalhando em Sistemas de Arquivos</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Erros</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Habilitar VHD esparso por padrão</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Qualquer VHD recém-criado será definido para esparso automaticamente quando habilitado.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Habilitar VHD esparso por padrão.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Qualquer VHD recém-criado será definido para dispersão automaticamente quando ativado.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Local do Arquivo de Troca</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para o disco rígido virtual de troca.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procurar arquivos de permuta</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Local do Arquivo de Troca</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para o disco rígido virtual de permuta.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Tamanho da Troca</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Quanto espaço de troca será adicionado à VM do WSL 2, 0 para nenhum arquivo de troca. O armazenamento de permuta é a RAM baseada em disco usada quando a demanda de memória excede o limite no dispositivo de hardware.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Redefinir tamanho</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tamanho da Troca</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Quanto espaço de troca, especificado em megabytes, para adicionar à VM WSL 2. 0 para nenhum arquivo de troca. O armazenamento de permuta é a RAM baseada em disco usada quando a demanda de memória excede o limite no dispositivo de hardware.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Habilitar o VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Use virtiofs em vez do plano 9 para acessar arquivos de host, aumentando a velocidade.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Habilitar o Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Habilita a montagem do sistema de arquivos 9P do host usando o transporte virtio.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Tempo limite de Ociosidade da VM</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>O número de milissegundos em que uma VM está ociosa, antes de ser desligada.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Redefinir tempo limite</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tempo limite de Ociosidade da VM</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>O número de milissegundos em que uma VM está ociosa, antes de ser desligada.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Crie, execute, depure e crie o perfil de seus aplicativos em execução no WSL no Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Execute e depure seus aplicativos no WSL do Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre o WSL no Visual Studio para desenvolvedores do .NET</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre o Visual Studio e o WSL para desenvolvedores C++</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Você pode facilmente executar e depurar seus aplicativos .NET Core e C++ multiplataforma no Linux sem sair do Visual Studio usando o Subsistema do Windows para Linux (WSL). Se você for um desenvolvedor multiplataforma, poderá usar esse método como uma maneira simples de testar mais de seus ambientes de destino.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Integração com Visual Studio</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Integração com Visual Studio</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/pt-PT/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Subsistema Windows para Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>O Subsistema Windows para Linux permite aos programadores executar um ambiente GNU/Linux, incluindo a maioria das ferramentas de linha de comandos, utilitários e aplicações, diretamente no Windows, sem modificação, sem a sobrecarga de uma máquina virtual tradicional ou configuração de arranque duplo.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Falha ao desanexar o disco: {}. Para obter mais detalhes, execute \"dmesg\" no WSL2.\nPara forçar o WSL2 a parar e desanexar o disco, execute \"wsl.exe {}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>O disco \"{}\" já está ligado.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Esse volume já está montado no WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Já está montado um disco com esse nome; Volte a desmontar o disco ou escolha um novo nome e tente novamente.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>O nome de montagem especificado contém um caráter '/' inválido. Tente novamente sem o caráter inválido.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>O disco foi montado com êxito como '/mnt/wsl/{}'.\nNota: a localização será diferente se tiver modificado a definição automount.root em /etc/wsl.conf.\nPara desmontar e desanexar o disco, execute \"wsl.exe {} {}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>O disco foi anexado, mas não foi possível montar: {}.\nPara obter mais detalhes, execute \"dmesg\" no WSL2.\nPara desanexar o disco, execute \"wsl.exe {} {}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Segue-se uma lista de distribuições válidas que podem ser instaladas.\nInstale com \"wsl.exe {} &lt;Distro&gt;\".\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>O nome de distribuição já foi definido.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>A localização de instalação fornecida já está a ser utilizada.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Já existe uma distribuição com o nome fornecido. Utilize --name para escolher um nome diferente.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Não existe nenhuma distribuição com o nome fornecido.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>É necessário acesso de administrador para montar um disco.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Falha ao exportar a distribuição.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>A efetuar uma atualização única do Subsistema Windows para o sistema de ficheiros Linux para esta distribuição...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Não é possível iniciar porque existe outra instância em execução não elevada.  As instâncias elevadas e não elevadas não têm permissão para serem executadas em simultâneo.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Falha ao importar a distribuição.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>A instalar componente opcional do Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>A transferir: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>A instalar: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>A importar distribuição</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} foi transferido.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>O subsistema do Windows para Linux está a retomar uma instalação anterior...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Nome de distribuição inválido: \"{}\".\nPara obter uma lista de distribuições válidas, utilize \"wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>O subsistema do Windows para a instância de Linux terminou.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Argumento de linha de comandos inválido: {}\nUtilize \"{} --help' para obter uma lista de argumentos suportados.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>O argumento {} da linha de comandos requer um valor.\nUtilize \"{} --help' para obter uma lista de argumentos suportados.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Definições de consola não suportadas. Para poder utilizar esta funcionalidade, a consola legada tem de ser desativada.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} não é um número inteiro válido.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Está em curso uma instalação, desinstalação ou conversão para esta distribuição.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>A iniciar {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Não é possível iniciar porque existe outra instância em execução elevada.  As instâncias elevadas e não elevadas não têm permissão para serem executadas em simultâneo.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Subsistema Windows para Linux não tem distribuições instaladas.\nPode resolver este problema instalando uma distribuição com as instruções abaixo:\n\nUtilizar \"wsl.exe --list --online' para listar as distribuições disponíveis\ne \"wsl.exe --install &lt;Distro&gt;\" para instalar.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Não existem distribuições em execução.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Predefinição)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Subsistema Windows para Distribuições do Linux:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Distribuição Predefinida: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Versão predefinida: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>A anular o registo.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Utilizador não encontrado.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Para obter informações sobre as principais diferenças em relação ao WSL 2, visite https://aka.ms/wsl2</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Conversão em curso, esta operação poderá demorar alguns minutos.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>A distribuição já é a versão pedida.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>A distribuição legada não suporta o WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>O WSL2 não consegue iniciar porque a virtualização não está ativada nesta máquina.\nCertifique-se de que o componente opcional \"Plataforma de Máquina Virtual\" está ativado e a virtualização está ligada nas definições de firmware do seu computador.\n\nAtive a \"Plataforma de Máquina Virtual\" ao executar: wsl.exe --install --no-distribution\n\nPara obter informações, visite https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Existem demasiados discos rígidos virtuais ou discos físicos anexados.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>O VHD já está montado através de wsl.exe --mount, desmonte o disco antes de iniciar.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>O WSL1 não é suportado com a configuração atual do computador.\nAtive o componente opcional \"Subsistema Windows para Linux\" para utilizar o WSL1.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Esta operação só é suportada pelo WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Utilização:\n    --networking-mode\n       Exibir o modo de rede atual.\n\n    --msal-proxy-path\n        Apresentar o caminho para a aplicação proxy MSAL.\n\n    --vm-id\n        Apresentar o ID de VM WSL.\n\n    --version\n        Apresentar a versão do pacote WSL.\n\n    -n\n        Não imprimir uma nova linha.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Utilização:\n    -a\n        Forçar resultado para formato de caminho absoluto.\n    -u\n        Traduzir de um caminho do Windows para um caminho WSL (predefinição).\n    -w\n        Traduzir de um caminho WSL para um caminho do Windows.\n    -m\n        Traduzir de um caminho WSL para um caminho do Windows, com \"/\" em vez de \"\\\\\"\n\nExemplo: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Executa operações administrativas no Subsistema Windows para Linux\n\nUtilização:\n    /l, /list [Option]\n        Lista as distribuições registadas.\n        /all - Opcionalmente lista todas as distribuições, incluindo distribuições que\n               estão atualmente a ser instaladas ou desinstaladas.\n\n        /running - Lista apenas as distribuições que estão atualmente em execução.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Define a distribuição como predefinição.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Termina a distribuição especificada.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Anula o registo da distribuição e elimina o sistema de ficheiros raiz.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Todos os direitos reservados.\nPara informações de privacidade sobre este produto, visite https://aka.ms/privacy.\n\nUtilização: wsl.exe [Argument] [Options...] [CommandLine]\n\nArgumentos para executar binários do Linux:\n\n    Se nenhuma linha de comandos for fornecida, o wsl.exe inicia a shell predefinida.\n\n    --exec, -e &lt;CommandLine&gt;\n        Executa o comando especificado sem utilizar a shell predefinida do Linux.\n\n    --shell-type &lt;standard|login|none&gt;\n        Executa o comando especificado com o tipo de shell fornecido.\n\n    --\n        Passa a linha de comandos restante tal como está.\n\nOpções:\n    --cd &lt;Directory&gt;\n        Define o diretório especificado como o diretório de trabalho atual.\n        Se ~ for utilizado, será utilizado o caminho raiz do utilizador do Linux. Se o caminho começar\n        por um caráter /, será interpretado como caminho absoluto do Linux.\n        Caso contrário, o valor deve ser um caminho absoluto do Windows.\n\n    --distribution, -d &lt;DistroName&gt;\n        Executa a distribuição especificada.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Executa o ID da distribuição especificada.\n\n    --user, -u &lt;UserName&gt;\n        Executa como o utilizador especificado.\n\n    --system\n        Inicia uma shell para a distribuição do sistema.\n\nArgumentos para gerir o Subsistema Windows para Linux:\n\n    --help\n        Apresenta informações de utilização.\n\n    --debug-shell\n        Abre uma shell de depuração do WSL2 para fins de diagnóstico.\n\n    --install [Distro] [Options...]\n        Instala uma distribuição do Subsistema Windows para Linux.\n        Para obter uma lista de distribuições válidas, utilize 'wsl.exe --list --online'\n\n        Opções:\n            --enable-wsl1\n                Ativa o suporte WSL1.\n\n            --fixed-vhd\n                Cria um disco de tamanho fixo para armazenar a distribuição.\n\n            --from-file &lt;Path&gt;\n                Instala uma distribuição a partir de um ficheiro local.\n\n            --legacy\n                Utiliza o manifesto de distribuição legado.\n\n            --location &lt;Location&gt;\n                Define o caminho de instalação da distribuição.\n\n            --name &lt;Name&gt;\n                Define o nome da distribuição. \n\n            --no-distribution\n                Instala apenas os componentes opcionais necessários. Não instala uma distribuição.\n\n            --no-launch, -n\n                Não inicia a distribuição após a instalação.\n\n            --version &lt;Version&gt;\n                Especifica a versão a utilizar na nova distribuição.\n\n            --vhd-size &lt;MemoryString&gt;\n                Especifica o tamanho do disco para armazenar a distribuição.\n\n            --web-download\n                Transfere a distribuição da Internet, em vez de pela Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Altera as opções específicas da distribuição.\n\n        Opções:\n            --move &lt;Location&gt;\n                Move a distribuição para uma nova localização.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Define o VHD da distribuição para ser disperso, permitindo que o espaço em disco seja automaticamente recuperado.\n\n            --set-default-user &lt;Username&gt;\n                Define o utilizador predefinido da distribuição.\n\n            --resize &lt;MemoryString&gt;\n                Redimensiona o disco da distribuição ao tamanho especificado.\n\n    --mount &lt;Disk&gt;\n        Liga e monta um disco físico ou virtual em todas as distribuições do WSL 2.\n\n        Opções:\n            --vhd\n                Especifica que &lt;Disk&gt; se refere a um disco rígido virtual.\n\n            --bare\n                Liga o disco ao WSL2, mas sem o montar.\n\n            --name &lt;Name&gt;\n                Monta o disco com um nome personalizado para o ponto de montagem.\n\n            --type &lt;Type&gt;\n                O sistema de ficheiros a utilizar ao montar um disco. Se não for especificado, é predefinido como ext4.\n\n            --options &lt;Options&gt;\n                Opções de montagem adicionais.\n\n            --partition &lt;Index&gt;\n                Índice da partição a montar. Se não for especificado, é predefinido para todo o disco.\n\n    --set-default-version &lt;Version&gt;\n        Altera a versão de instalação predefinida para novas distribuições.\n\n    --shutdown\n        Termina imediatamente todas as distribuições em execução e o WSL 2\n        máquina virtual utilitária leve.\n\n        Opções:\n            --force\n                Termina a máquina virtual WSL 2 mesmo que esteja a decorrer uma operação. Pode causar perda de dados.\n\n    --status\n        Mostra o estado do Subsistema Windows para Linux.\n\n    --unmount [Disk]\n        Desmonta e desliga um disco de todas as distribuições do WSL 2.\n        Desmonta e desliga todos os discos se for chamado sem argumento.\n\n    --uninstall\n        Desinstala o pacote do Subsistema Windows para Linux a partir deste computador. \n\n    --update\n        Atualiza o pacote do Subsistema Windows para Linux.\n\n        Opções:\n            --pre-release\n                Transfere uma versão de pré-lançamento, se estiver disponível.\n\n    --version, -v\n        Apresenta informações da versão.\n\nArgumentos para gerir distribuições no Subsistema Windows para Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Exporta a distribuição para um ficheiro tar.\n        O nome de ficheiro pode ser - para stdout.\n\n        Opções:\n            --format &lt;Format&gt;\n                Especifica o formato de exportação. Valores suportados: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n        Importa o ficheiro tar especificado como nova distribuição.\n        O nome de ficheiro pode ser - para stdin.\n\n        Opções:\n            --version &lt;Version&gt;\n                Especifica a versão a utilizar para a nova distribuição.\n\n            --vhd\n                Especifica que o ficheiro fornecido é um ficheiro .vhd ou .vhdx, não um ficheiro tar.\n                Esta operação faz uma cópia do ficheiro VHD na localização de instalação especificada.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Importa o ficheiro VHD especificado como nova distribuição.\n        Este disco rígido virtual tem de ser formatado com o tipo de sistema de ficheiros ext4.\n\n    --list, -l [Options]\n        Lista as distribuições.\n\n        Opções:\n            --all\n                Lista todas as distribuições, incluindo as distribuições que estão\n                atualmente a ser instaladas ou desinstaladas.\n\n            --running\n                Lista apenas as distribuições que estão atualmente em execução.\n\n            --quiet, -q\n                Mostra apenas os nomes das distribuições.\n\n            --verbose, -v\n                Mostra informações detalhadas sobre todas as distribuições.\n\n            --online, -o\n                Mostra uma lista de distribuições disponíveis para instalação com 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Define a distribuição como predefinição.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Altera a versão da distribuição especificada.\n\n    --terminate, -t &lt;Distro&gt;\n        Termina a distribuição especificada.\n\n    --unregister &lt;Distro&gt;\n        Anula o registo da distribuição e elimina o sistema de ficheiros raiz.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>Versão WSL: {}\nVersão do kernel: {}\nVersão WSLg: {}\nVersão MSRDC: {}\nVersão direct3D: {}\nVersão DXCore: {}\nVersão do Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>Versão do MSBuild: {}\nConsolidar: {}\nHora da criação: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>O kernel personalizado especificado em {} não foi encontrado: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>O VHD dos módulos kernel personalizados em {} não foi encontrado: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>A distribuição personalizada do sistema especificada em {} não foi encontrada ou não tem o formato correto.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Todos os direitos reservados.\nPara obter informações de privacidade sobre este produto, visite https://aka.ms/privacy.\n\nUtilização: wslg.exe [Argumento] [Opções...] [Linha de Comandos]\n\nArgumentos:\n    --cd &lt;Diretório&gt;\n        Define o diretório especificado como diretório de trabalho atual.\n        Se ~ for utilizado, será utilizado o caminho raiz do utilizador do Linux. Se o caminho começar\n        com um caráter /, será interpretado como um caminho absoluto do Linux.\n        Caso contrário, o valor tem de ser um caminho absoluto do Windows.\n\n    --distribution, -d &lt;Distribuição&gt;\n        Executa a distribuição especificada.\n\n    --user, -u &lt;Nome de Utilizador&gt;\n        Executa como o utilizador especificado.\n\n    --shell-type &lt;standard|login|none&gt;\n        Executa o comando especificado com o tipo de shell fornecido.\n\n    --help\n        Apresenta informações de utilização.\n\n    --\n        Passa a linha de comandos restante conforme está.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Prima qualquer tecla para continuar...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Falta um parâmetro necessário no argumento {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Exportação em curso, esta operação poderá demorar alguns minutos.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Importação em curso, esta operação poderá demorar alguns minutos.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>O suporte da aplicação GUI está desativado através de {} ou /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>A procurar atualizações.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Esta distribuição apenas está disponível na Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount no ARM64 requer o Windows versão 27653 ou mais recente.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>A versão mais recente do subsistema do Windows para Linux já está instalada.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>A atualizar Subsistema Windows para Linux para a versão: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Esta aplicação requer o Componente Opcional Subsistema Windows para Linux.\nInstale-o executando: wsl.exe --install --no-distribution\nO sistema poderá ter de ser reiniciado para que as alterações possam ter efeito.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>O ficheiro especificado tem de ter a extensão de ficheiro {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>O ficheiro especificado tem de ter a extensão de ficheiro {} ou {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>O VmSwitch \"{}\" não foi encontrado. Interruptores disponíveis: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>A ponte de rede necessita que o wsl2.vmSwitch seja definido.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Falha ao obter a lista de distribuição de \"{}\". {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Falha ao anexar o disco \"{}\" ao WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Falha ao redimensionar o disco.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Não foi encontrado nenhum valor.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>A versão do Windows {} não suporta a versão em pacote do Subsistema Windows para Linux.\nInstalar a atualização necessária através da atualização do Windows ou através de: {}\nPara obter informações, visite https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>A execução da shell de depuração requer a execução de wsl.exe como Administrador.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>O processo de instalação da distribuição \"{}\" falhou com o código de saída: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Formato GUID inválido: \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Nome de secção inválido em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Nome de chave inválido em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>{} espera em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Caráter de escape inválido: \"{}\" em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Chave desconhecida \"{}\" em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Valor inteiro \"{}\" inválido para a chave \"{}\" em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Valor IP \"{}\" inválido para a chave \"{}\" em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Valor booleano '{}' inválido para a chave '{}' em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Endereço mac \"{}\" inválido para a chave \"{}\" em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Falha ao abrir o ficheiro de configuração {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Ocorreram erros durante o arranque do WSL</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Falha ao iniciar a sessão de utilizador do sistema para \"{}\". Consulte o journalctl para obter mais detalhes.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Abrir o EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Esta distribuição não contém um nome predefinido. Utilize --name para escolher o nome da distribuição.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Os argumentos {} e {} não podem ser especificados ao mesmo tempo.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>O argumento {} requer o argumento {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Nome de distribuição inválido: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>A utilizar o registo de distribuição legado. Em vez disso, considere utilizar uma distribuição baseada em tar.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Os registos de distribuição legados não suportam o argumento --version .</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>A distribuição \"{}\" é fornecida por um manifesto de substituição.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>O hash de distribuição não corresponde. Esperado: {}, hash real: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Cadeia hexadecimal inválida: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>'{}' não é suportado ao instalar distribuições legadas.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Distribuição instalada com êxito. Pode ser iniciado através de \"wsl.exe -d {}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Cadeia de memória '{}' inválida para .wslconfig entrada '{}' em {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Falha ao criar o disco de troca em '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>A virtualização aninhada não é suportada nesta máquina.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Falha ao criar o ponto final de rede com o endereço: \"{}\", novo endereço atribuído: \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Falha ao criar a rede virtual com o intervalo de endereços: \"{}\", criou uma nova rede com o intervalo: \"{}\", {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>MODO DE SEGURANÇA ATIVADO - muitas funcionalidades serão desativadas</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>O modo de rede espelhado não é suportado: {}.\nA reverter para a rede NAT.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>É necessária a versão 5.10 ou mais recente do Kernel do Linux</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Versão do Windows {}. {} não tem as funcionalidades necessárias</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>A firewall do Hyper-V não é suportada</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>O Túnel DNS não é suportado</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>A definição wsl2.localhostForwarding não tem qualquer efeito ao utilizar o modo de rede espelhado</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Foi detetada uma alteração de Proxy http no anfitrião. Reinicie o WSL para aplicar a alteração.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Foi detetada uma configuração de proxy de localhost, mas não foi espelhada no WSL. O WSL no modo NAT não suporta proxies de localhost.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Foi detetada uma configuração de proxy IPv6, mas não foi espelhada no WSL. O WSL no modo NAT não suporta IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Foi detetada uma configuração de proxy IPv6 de localhost, mas não foi espelhada no WSL. O WSL não suporta proxies IPv6 de localhost.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Ocorreu um erro inesperado ao tentar resolver as definições de proxy, as definições de proxy não foram espelhadas no WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Ocorreu um erro ao aceder ao registo. Caminho: '{}'. Erro: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Falha ao iniciar o processo de reencaminhamento localhost. Erro: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Falha ao iniciar a rede virtual - instale o componente opcional Virtual Machine Platform executando: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Falha ao configurar a rede (networkingMode {}), recuando para networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Falha ao ativar o componente do Windows \"{}\" (código de saída {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Foi devolvido um erro fatal pelo plug-in \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Foi devolvido um erro fatal pelo plug-in '{}'. Mensagem de erro: \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>O plug-in \"{}\" requer uma versão mais recente do WSL. Execute: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>A definição de .wslconfig \"{}\" está desativada pela política do computador.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>A shell de depuração está desativada pela política do computador.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount está desativado pela política do computador.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>O WSL1 está desativado pela política do computador.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Execute \"wsl.exe --set-version {} 2\" para atualizar para o WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>O WSL está a concluir uma atualização...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>A atualização falhou (código de saída: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Falha ao desinstalar (código de saída: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Ficheiro de registo: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Falha ao remover o pacote MSIX (erro: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Falha ao instalar o pacote MSIX (erro: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Os componentes opcionais necessários para executar o WSL não estão instalados.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Instalar componentes em falta.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>O ficheiro importado não é uma distribuição linux válida.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Prima qualquer tecla para sair...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Está disponível uma nova versão do Subsistema Windows para Linux.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Atualize para a versão {} ou veja as respetivas notas de versão abaixo.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Atualizar</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Ver Documentos</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Não foi possível concluir a operação porque o VHD está atualmente a ser utilizado. Para forçar o WSL a parar, utilize: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} não é um booleano válido, &lt;verdadeiro|falso&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>O VHD disperso é suportado apenas no WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>A execução do WSL como sistema local não é suportada.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Sugestão de Desempenho:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>A utilização de uma operação de E/S intensiva como {} nas suas unidades Windows terá um desempenho fraco. Considere mover os seus ficheiros de projeto para o sistema de ficheiros Linux para um melhor desempenho. Clique abaixo para saber mais.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Ver Documentos</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Não Voltar a Mostrar</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>A Máquina Virtual WSL2 falhou.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>O rastreio da pilha foi guardado em: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Falha ao iniciar a distribuição. Código de erro: {}, passo de falha: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Falha ao iniciar a distribuição porque o respetivo disco virtual está danificado.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Chave de configuração duplicada '{}' em {}:{} (Chave em conflito: '{}' em {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Valor \"{}\" inválido para a chave de configuração \"{}\" em {}:{} (Valores válidos: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>A aguardar que o comando OOBE seja concluído para distribuição \"{}\"...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Falha ao ler a propriedade \"{}\" a partir da distribuição {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Este parece ser um ficheiro VHD. Utilize --vhd para importar um ficheiro VHD em vez de um tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Falha ao instalar {} a partir da Microsoft Store: {}\nA tentar transferir a Web...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Não foi configurada nenhuma distribuição predefinida. Forneça uma distribuição para instalar.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p está desativado, a reverter para 9p com transporte vsock.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>A instalação WSL parece estar danificada (Código de erro: {}).\nPrima qualquer tecla para reparar o WSL ou CTRL-C para cancelar.\nEste pedido excederá o tempo limite dentro de 60 segundos.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Falha ao analisar o perfil de terminal ao registar a distribuição: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Tamanho inválido: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nCódigo de erro: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Documento JSON inválido. Erro de análise: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors não podem exceder o número de processadores lógicos no sistema ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Falha ao consultar o modo de rede</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Foram fornecidos módulos de kernel personalizados sem especificar um kernel personalizado. Consulte https://aka.ms/wslcustomkernel para obter mais informações.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Devido a um problema de compatibilidade atual com o Cliente de Acesso Seguro Global, o Túnel DNS está desativado.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>O suporte a VHD disperso está atualmente desativado devido a potenciais danos nos dados.\nPara forçar uma distribuição a utilizar um VHD disperso, execute:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Falha ao montar {}, consulte dmesg para obter mais detalhes.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>O processamento de /etc/fstab com mount -a falhou.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Ocorreu um erro ao montar o disco de distribuição; foi montado como só de leitura como contingência.\nVer instruções de recuperação em: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Falha ao traduzir '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Algo correu mal. Tente novamente mais tarde.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Acerca de</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Acerca das definições do Subsistema Windows para Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Todos os direitos reservados.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Definições do Subsistema Windows para Linux</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>As Definições do Subsistema Windows para Linux permitem aos programadores gerir o ficheiro .wslconfig através uma aplicação baseada em GUI.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Recuperação automática de memória</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Liberta automaticamente memória em cache após detetar a utilização inativa da CPU. Defina como \"gradual\" para libertação lenta e \"dropcache\" para libertação instantânea de memória em cache.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Recuperação automática de memória.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Liberta automaticamente memória em cache após detetar a utilização inativa da CPU. Defina como \"gradual\" para libertação lenta e \"dropcache\" para libertação instantânea de memória em cache.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy Automático ativado</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Permite que o WSL utilize as informações de proxy HTTP do Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy Automático ativado.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Permite que o WSL utilize as informações de proxy HTTP do Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Utilizar análise de DNS de melhor esforço</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.dnsTunneling está definido como verdadeiro. Quando definido como verdadeiro, o Windows extrairá a pergunta do pedido DNS e tentará resolvê-la, ignorando os registos desconhecidos.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Utilizar análise de DNS de melhor esforço.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.dnsTunneling está definido como verdadeiro. Quando definido como verdadeiro, o Windows extrairá a pergunta do pedido DNS e tentará resolvê-la, ignorando os registos desconhecidos.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Kernel personalizado</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para um kernel Linux personalizado.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procurar kernels</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Kernel personalizado</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para um kernel Linux personalizado.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Módulos de kernel personalizados</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para um VHD personalizado de módulos de kernel Linux.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Navegar por módulos de kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Módulos de kernel personalizados</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para um VHD personalizado de módulos de kernel Linux.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Distribuição do sistema personalizado</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procurar distros.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Especifique um caminho para um VHD que será carregado como distro de sistema personalizado, utilizado principalmente para alimentar aplicações GUI no WSL. [Saiba mais sobre as distros do sistema aqui].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Distribuição do sistema personalizado</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Especifique um caminho para um VHD que será carregado como distro de sistema personalizado, utilizado principalmente para alimentar aplicações GUI no WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Ativar consola de depuração</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Valores booleanos para ativar uma janela de consola de saída que mostra o conteúdo do dmesg no início de uma instância da distribuição WSL 2.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ativar consola de depuração.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleano para ativar uma janela de consola de saída que mostra o conteúdo das mensagens de diagnóstico no início de uma instância da distro WSL 2.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Tamanho de Disco Rígido Virtual Predefinido</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>O tamanho máximo predefinido para o disco rígido virtual WSL expansível (VHD) apenas para distribuições criadas recentemente.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Repor tamanho</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tamanho de Disco Rígido Virtual Predefinido</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>O tamanho máximo predefinido, especificado em megabytes, para o disco rígido virtual WSL expansível (VHD) apenas para distribuições criadas recentemente.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Programador</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentação</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Alterar o modo DrvFS</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Altera a implementação do acesso a ficheiros em todo o SO no WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>Proxy DNS ativado</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando o wsl2.networkingMode está definido como NAT. Booleano para informar o WSL para configurar o Servidor DNS no Linux para o NAT no anfitrião. Definir como falso irá espelhar os servidores DNS do Windows para Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Proxy DNS ativado.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.networkingMode está definido como NAT. Booleano para informar o WSL para configurar o Servidor DNS no Linux para o NAT no anfitrião. Definir como falso irá espelhar os servidores DNS do Windows para Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>Túnel DNS ativado</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Altera a forma como os pedidos DNS são transmitidos do WSL para o Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Túnel DNS ativado.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Altera a forma como os pedidos DNS são transmitidos do WSL para o Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Sistema de Ficheiros</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Ativar aplicações de GUI</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Booleano para ativar ou desativar o suporte para aplicações GUI ([WSLg]) no WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ativar aplicações de GUI</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleano para ativar ou desativar o suporte para aplicações GUI (conhecidas como WSL g) no WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Ativar contadores de desempenho de hardware</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Ativa os contadores de desempenho de hardware para Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ativar contadores de desempenho de hardware.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ativa os contadores de desempenho de hardware para Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Firewall de Hyper-V ativada</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Ativa a firewall do Hyper-V que permite que as regras da Firewall do Windows, bem como regras específicas do tráfego hyper-V, filtrem o tráfego de rede WSL.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Firewall de Hyper-V ativada.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ativa a firewall do Hyper-V que permite que as regras da Firewall do Windows, bem como regras específicas do tráfego hyper-V, filtrem o tráfego de rede WSL.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Loopback do Endereço de Anfitrião</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.networkingMode está definido como espelhado. Quando definido como Verdadeiro, permitirá ao Contentor ligar ao Anfitrião ou ao Anfitrião ligar ao Contentor através de um endereço IP atribuído ao Anfitrião. Tenha em atenção que o endereço de loopback 127.0.0.1 pode sempre ser utilizado - esta opção também permite a utilização de todos os endereços IP locais atribuídos adicionalmente.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Loopback do Endereço de Anfitrião</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.networkingMode está definido como espelhado. Quando definido como Verdadeiro, permitirá ao Contentor ligar ao Anfitrião ou ao Anfitrião ligar ao Contentor através de um endereço IP atribuído ao Anfitrião. Tenha em atenção que o endereço de loopback 127.0.0.1 pode sempre ser utilizado - esta opção também permite a utilização de todos os endereços IP locais atribuídos adicionalmente.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Portas ignoradas</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.networkingMode está definido como espelhado. Especifica as portas a que as aplicações Linux podem vincular que não serão reencaminhadas ou consideradas automaticamente no Windows. Deve ser formatado numa lista separada por vírgulas, por exemplo: 3000.9000.9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Repor portas</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Portas ignoradas</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.networkingMode está definido como espelhado. Especifica as portas a que as aplicações Linux podem vincular que não serão reencaminhadas ou consideradas automaticamente no Windows. Deve ser formatado numa lista separada por vírgulas, por exemplo, 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Tempo limite inicial do Proxy Automático</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.autoProxy está definido como verdadeiro. Configura durante quanto tempo (em milissegundos) o WSL aguardará pela obtenção de informações de proxy HTTP ao iniciar um contentor WSL. Se as definições de proxy forem resolvidas após este período, a instância WSL tem de ser reiniciada para utilizar as definições de proxy obtidas.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Repor tempo limite</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tempo limite inicial do Proxy Automático</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Apenas aplicável quando wsl2.autoProxy está definido como verdadeiro. Configura durante quanto tempo, em milissegundos, o WSL aguardará pela obtenção de informações de proxy HTTP ao iniciar um contentor WSL. Se as definições de proxy forem resolvidas após este período, a instância WSL tem de ser reiniciada para utilizar as definições de proxy obtidas.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Linha de Comandos de Kernel</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Argumentos adicionais da linha de comandos do kernel.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Ativar reencaminhamento localhost</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Valor booleano que especifica se as portas vinculadas a \"caráter universal\" ou \"localhost\" na VM WSL 2 devem ser conectáveis a partir do anfitrião através de localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ativar reencaminhamento do localhost.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Valor booleano que especifica se as portas vinculadas a \"caráter universal\" ou \"localhost\" na VM WSL 2 devem ser conectáveis a partir do anfitrião através de localhost, dois pontos, port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Memória e processador</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Tamanho de memória</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Quantidade de memória a atribuir à VM WSL 2.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Repor tamanho</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tamanho de memória</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Quantidade de memória, especificada em megabytes, para atribuir à VM WSL 2.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} milissegundos</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Ativar virtualização aninhada</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Booleano para ativar ou desativar a virtualização aninhada, permitindo que outras VMs aninhadas sejam executadas dentro do WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ativar virtualização aninhada.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleano para ativar ou desativar a virtualização aninhada, permitindo que outras VMs aninhadas sejam executadas dentro do WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Modo de rede</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Especifica o modo de rede para o WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Modo de rede.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Especifica o modo de rede para o WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Funcionamento em rede</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Pode trabalhar entre sistemas operativos com todos os seus ficheiros!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Acesso a Ficheiros Entre SO</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Aceder aos seus ficheiros do Windows no Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Pode aceder aos seus ficheiros do Windows no Linux ao navegar para “/mnt” e, em seguida, para a letra de unidade do Windows, como este exemplo para a unidade C:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Aceder aos seus ficheiros do Linux com o Explorador de Ficheiros</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Pode ver os seus ficheiros do Linux no Explorador de Ficheiros ao navegar para '\\\\wsl.localhost\\' ou ao clicar no ícone “Linux”.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Iniciar ficheiros e programas do Windows no WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Mesmo ao utilizar o WSL, pode executar os seus executáveis do Windows diretamente a partir do bash. Experimente executar\n'powershell.exe /c start .' para abrir o Explorador de Ficheiros na sua pasta atual.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Aceder a Aplicações de Rede do Linux no Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Se estiver a criar uma aplicação de rede (por exemplo, uma aplicação em execução num Servidor NodeJS ou SQL) na sua distribuição do Linux, pode aceder à mesma numa aplicação Windows (como o browser Edge ou Chrome) utilizando o localhost (tal como faria normalmente). Isto significa que, se tiver iniciado um servidor Linux que esteja a escutar a porta 3000, pode aceder a [http://localhost:3000] no Edge no Windows para aceder ao mesmo.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Rede de Modo Espelhado</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>O WSL também inclui um novo modo de rede, denominado modo espelhado, que adiciona capacidades avançadas como suporte de IPv6 e a capacidade de aceder às suas aplicações de rede na sua rede local.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Saiba Mais Sobre o Acesso a Ficheiros Entre SO</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Damos-lhe as boas-vindas ao Subsistema Windows para Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>O WSL é uma excelente forma de experimentar diferentes distribuições do Linux.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Gestão de Distribuições</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre comandos do WSL básicos</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais a importação de qualquer distribuição do Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Comando Listar Distribuições do WSL Instaláveis</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Comando Instalar uma Distribuição do WSL Nomeada</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Comando Listar Distribuições do WSL Disponíveis</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>O Docker Desktop funciona muito bem com o WSL para ajudar a desenvolver contentores do Linux.\n\nAlgumas das vantagens de utilizar o Docker Desktop com o WSL são:\n\n• pode executar comandos do Docker no WSL ou no Windows, com o mesmo daemon e as mesmas imagens do Docker;\n• pode partilhar ficheiros e pastas entre o Windows e o Linux sem problemas, com a montagem automática de unidades do Windows no WSL;\n• pode utilizar as suas ferramentas e os seus editores preferidos do Windows para trabalhar em código e ficheiros do Linux e vice-versa, graças à interoperabilidade do WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Integração do Docker Desktop</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Saiba Mais Sobre a Utilização do WSL com o Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>O Subsistema Windows para Linux (WSL) permite-lhe executar os seus utilitários, ferramentas aplicações e fluxos de trabalho do Linux favoritos diretamente no Windows.\n\nDedique um momento para pré-visualizar algumas das funcionalidades favoritas da comunidade ou veja a nossa documentação abrangente.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Damos-lhe as boas-vindas ao WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Melhores Práticas para a Configuração</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Começar com o Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Documentação do Subsistema Windows para Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Pode utilizar aplicações Linux com base gráfica com interações nativas do Windows, como alt-tab, início do menu iniciar, afixação na barra de tarefas e suporte para cortar e colar.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>Aplicações de GUI</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>O WSL pode tirar partido da sua GPU do Windows para fluxos de trabalho de Aprendizagem Automática\n\nOs binários do Linux em execução no WSL podem utilizar automaticamente a sua GPU no Windows para acelerar o desempenho. Só tem de instalar e executar esses fluxos de trabalho da mesma forma que faria num computador Linux normal. Há várias formas diferentes de começar, desde executar o CUDA num contentor docker, se tiver uma placa gráfica NVIDIA, a executar o PyTorch ou o TensorFlow com DirectML na sua placa gráfica AMD, Intel ou NVIDIA. Veja o nosso guia de introdução abaixo para saber mais.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>Aceleração por GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Saiba Mais Sobre Aceleração de GPU</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre as Aplicações de GUI do WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Eis uma lista de aplicações a experimentar (pode instalar todas no Ubuntu ao escrever 'sudo apt install &lt;The App Name&gt;')\n\n    • gedit – Editor de texto básico\n    • audacity – Gravar e editar ficheiros de áudio\n    • blender – Fazer animações e visualizações 3D\n    • gimp – Editar fotografias\n    • nautilus – Um explorador de ficheiros do Linux\n    • vlc – Leitor de vídeo</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Pode aceder facilmente a aplicações de rede em sistemas operativos Windows e Linux.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Integração de Rede</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Saiba Mais Sobre Aplicações de Rede</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Saiba Mais Sobre a Rede no Modo Espelhado</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Pode utilizar o WSL como o seu ambiente de desenvolvimento a tempo inteiro diretamente no VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>Integração do VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Saiba Mais Sobre a Utilização do WSL com o VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Como instalar</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>Depois de instalar o VS Code, pode instalar a extensão Remote WSL a partir do Terminal do Windows:\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Abrir um Projeto do WSL no Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Para abrir um projeto no VS Code a partir da sua distribuição do WSL, abra a linha de comandos da distribuição e execute 'code .' para abrir um ficheiro de projeto.\n\nTambém pode aceder a mais Opções remotas do VS Code através da paleta de comandos no próprio VS Code. Faça “SHIFT+CTRL+P” no teclado para abrir a paleta de comandos e escreva “Remote-WSL” para ver uma lista das Opções remotas do VS Code disponíveis, permitindo-lhe reabrir a pasta numa sessão remota, especificar que distribuição pretende abrir e muito mais.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Como utilizar</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Funcionalidades Opcionais</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Declaração de Privacidade</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Contagem de Processadores</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Quantos processadores lógicos atribuir à VM WSL 2.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Repor a contagem</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Contagem de Processadores</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Quantos processadores lógicos atribuir à VM WSL 2.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Ligações relacionadas</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Notas de versão</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Ativar modo de segurança</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Execute o WSL no \"Modo de Segurança\", o que desativa muitas funcionalidades e se destina a ser utilizado para recuperar distribuições que estejam em estados incorretos.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ativar modo de segurança.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Execute o WSL no \"Modo de Segurança\", o que desativa muitas funcionalidades e se destina a ser utilizado para recuperar distribuições que estejam em estados incorretos.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Acerca de</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Programador</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Gestão de Distribuições</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Integração do Docker Desktop</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Sistema de Ficheiros</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Geral</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Aceleração da GPU</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>Aplicações de GUI</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Iniciar wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Memória e processador</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Funcionamento em rede</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Integração de Rede</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Damos-lhe as boas-vindas ao WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Funcionalidades Opcionais</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Definições</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>Integração do VS Code</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Novidades</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Trabalhar em Sistemas de Ficheiros</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problemas</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Ativar disco rígido virtual disperso por predefinição</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Qualquer disco rígido virtual recém-criado será definido para dispersão automaticamente quando ativado.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ativar disco rígido virtual disperso por predefinição.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Qualquer disco rígido virtual recém-criado será definido para dispersão automaticamente quando ativado.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Localização do Ficheiro de Paginação</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para o disco rígido virtual de troca.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Procurar ficheiros de troca</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Localização do Ficheiro de Paginação</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Um caminho absoluto do Windows para o disco rígido virtual de troca.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Tamanho da troca</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Quantidade de espaço de troca a adicionar à VM WSL 2; 0 para nenhum ficheiro de paginação. O armazenamento de troca é uma RAM baseada em disco utilizada quando a procura de memória excede o limite no dispositivo de hardware.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Repor tamanho</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tamanho da troca</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Quanto espaço de troca, especificado em megabytes, deve ser adicionado à VM WSL 2. 0 para nenhum ficheiro swap. O armazenamento de troca é uma RAM baseada em disco utilizada quando a procura de memória excede o limite no dispositivo de hardware.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Ativar o VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Utilize \"virtiofs\" em vez de \"plan 9\" para aceder a ficheiros de anfitrião, aumentando a velocidade.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Ativar Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Ativa a montagem do sistema de ficheiros 9P a partir do anfitrião através do transporte virtio.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Tempo limite de Inatividade da VM</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>O número de milissegundos em que uma VM está inativa antes de ser encerrada.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Repor tempo limite</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tempo limite de Inatividade da VM</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>O número de milissegundos em que uma VM está inativa antes de ser encerrada.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Crie, execute, depure e defina perfis para as suas aplicações em execução no WSL a partir do Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Execute e depure as suas aplicações no WSL a partir do Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre o WSL no Visual Studio para programadores .NET</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Saiba mais sobre o Visual Studio e o WSL para programadores C++</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Pode executar e depurar facilmente as suas aplicações .NET Core e C++ entre plataformas no Linux sem sair do Visual Studio com o Subsistema Windows para Linux (WSL). Se for um programador entre plataformas, pode utilizar este método como uma forma simples de testar mais dos seus ambientes de destino.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Integração do Visual Studio</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Integração do Visual Studio</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/ru-RU/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Подсистема Windows для Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Подсистема Windows для Linux позволяет разработчикам запускать среду GNU/Linux (включая большинство программ командной строки, служебных программ и приложений) непосредственно в Windows без изменений, без накладных расходов традиционной виртуальной машины или настройки двойной загрузки.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Не удалось отключить диск: {}. Для получения дополнительных сведений запустите dmesg в WSL2.\nЧтобы принудительно остановить и отключить диск WSL2, выполните команду \"wsl.exe {}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Диск \"{}\" уже подключен.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Этот том уже подключен в WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Диск с таким именем уже подключен; отключите диск или выберите новое имя и повторите попытку.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Указанное имя подключения содержит недопустимый символ \"/\". Повторите попытку без недопустимого символа.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Диск успешно подключен как \"/mnt/wsl/{}\".\nПримечание. Расположение будет разным, если вы изменили параметр automount.root в /etc/wsl.conf.\nЧтобы отключить и отключить диск, выполните команду \"wsl.exe {} {}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Диск был подключен, но подключить его не удалось: {}.\nДля получения дополнительных сведений выполните команду \"dmesg\" в WSL2.\nЧтобы отключить диск, выполните команду \"wsl.exe {} {}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Ниже указан список допустимых дистрибутивов, которые можно установить.\nУстановить с помощью \"wsl.exe {} &lt;Distro&gt;\".\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Имя дистрибутива уже задано.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>Указанное расположение установки уже используется.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Распределение с указанным именем уже существует. Используйте --name , чтобы выбрать другое имя.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Отсутствует распределение с указанным именем.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Для подключения диска требуется доступ администратора.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Не удалось экспортировать дистрибутив.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Выполняется одноразовое обновление файловой системы подсистемы Windows для Linux для этого дистрибутива...\n\n</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Не удается запустить, так как сейчас выполняется другой экземпляр с пониженными привилегиями. Экземпляры с повышенными и пониженными привилегиями не разрешается запускать одновременно.\n</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Не удалось импортировать дистрибутив.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Установка дополнительного компонента Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Скачивание: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Установка: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Импорт распространения</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>Скачано: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>подсистема Windows для Linux возобновляет предыдущую установку...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Недопустимое имя распространения: \"{}\".\nЧтобы получить список допустимых дистрибутивов, используйте \"wsl.exe --list --online'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Экземпляр подсистема Windows для Linux завершен.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Недопустимый аргумент командной строки: {}\nИспользуйте \"{} --help', чтобы получить список поддерживаемых аргументов.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>Аргумент командной строки {} требует значения.\nИспользуйте \"{} --help', чтобы получить список поддерживаемых аргументов.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Неподдерживаемые параметры консоли. Чтобы использовать эту функцию, необходимо отключить консоль прежних версий.\n</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} не является допустимым целым числом.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Для этого распределения выполняется установка, удаление или преобразование.\n</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Запуск {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Не удается запустить, так как выполняется другой экземпляр с повышенными привилегиями. Экземпляры с повышенными и пониженными привилегиями не разрешается запускать одновременно.\n</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>подсистема Windows для Linux не имеет установленных дистрибутивов.\nЧтобы устранить эту проблему, установите дистрибутив с инструкциями ниже:\n\nИспользуйте \"wsl.exe --list --online' для перечисления доступных дистрибутивов\nи \"wsl.exe --install &lt;Distro&gt;\" для установки.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Нет выполняющихся дистрибутивов.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (по умолчанию)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Дистрибутивы подсистемы Windows для Linux:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Дистрибутив по умолчанию: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Версия по умолчанию: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Отмена регистрации.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Пользователь не найден.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Для получения сведений о ключевых различиях с WSL 2 перейдите на страницу https://aka.ms/wsl2\n</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Выполняется преобразование. Это может занять несколько минут.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>Запрашиваемая версия дистрибутива уже используется.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>Устаревшее распределение не поддерживает WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>Не удалось запустить WSL2, так как на этом компьютере не включена виртуализация.\nУбедитесь, что дополнительный компонент \"Платформа виртуальной машины\" включен и виртуализация включена в параметрах встроенного ПО компьютера.\n\nВключите \"Платформу виртуальной машины\", выстроив команду: wsl.exe --install --no-distribution\n\nДополнительные сведения см. в https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Подключено слишком много виртуальных жестких дисков или физических дисков.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD уже подключен с помощью команды wsl.exe --mount, отключите диск перед запуском.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>Текущая конфигурация компьютера не поддерживает WSL1.\nВключите необязательный компонент \"Подсистема Windows для Linux\", чтобы использовать WSL1.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Эта операция поддерживается только в WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Использование:\n    --networking-mode\n       Отображение текущего сетевого режима.\n\n    --msal-proxy-path\n        Отображение пути к прокси-приложению MSAL.\n\n    --vm-id\n        Отображение ИД виртуальной машины WSL.\n\n    --version\n        Отображение версии пакета WSL.\n\n    -n\n        Не печатать новую строку.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Использование:\n    -a\n        Принудительное форматирование результата в абсолютный путь.\n    -u\n        Преобразование из пути Windows в путь WSL (по умолчанию).\n    -w\n        Преобразование из пути WSL в путь Windows.\n    -m\n        Преобразование из пути WSL в путь Windows с \"/\" вместо \"\\\\\"\n\nПример: wslpath \"c:\\\\users\"</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Выполняет административные операции в подсистеме Windows для Linux\n\nИспользование:\n    /l, /list [Параметр]\n        Перечисляет зарегистрированные дистрибутивы.\n        /all - необязательно перечислить все дистрибутивы, включая дистрибутивы, которые\n               устанавливаются или удаляются в настоящий момент.\n\n        /running — перечислить только дистрибутивы, которые сейчас запущены.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Задает дистрибутив по умолчанию.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Прерывает работу дистрибутива.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Отменяет регистрацию дистрибутива и удаляет корневую файловую систему.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>(c) Корпорация Майкрософт (Microsoft Corporation). Все права защищены.\nСведения о конфиденциальности этого продукта см. на странице https://aka.ms/privacy.\n\nИспользование: wsl.exe [аргумент] [параметры...] [командная_строка]\n\nАргументы для запуска двоичных файлов Linux:\n\n    Если командная строка не указана, wsl.exe запускает оболочку по умолчанию.\n\n    --exec, -e &lt;CommandLine&gt;\n        Выполнить указанную команду без использования оболочки Linux по умолчанию.\n\n    --shell-type &lt;standard|login|none&gt;\n        Выполнить указанную команду с указанным типом оболочки.\n\n    --\n        Передать оставшуюся командную строку без изменений.\n\nПараметры:\n    --cd &lt;Directory&gt;\n        Задает указанный каталог в качестве текущего рабочего каталога.\n        Если указан символ \"~\", будет использоваться домашний путь пользователя Linux. Если путь начинается\n        с символа \"/\", он будет интерпретироваться как абсолютный путь Linux.\n        В противном случае значение должно быть абсолютным путем Windows.\n\n    --distribution, -d &lt;DistroName&gt;\n        Запуск указанного дистрибутива.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Запуск дистрибутива с указанным ИД.\n\n    --user, -u &lt;UserName&gt;\n        Запуск от имени указанного пользователя.\n\n    --system\n        Запускает оболочку для дистрибутива системы.\n\nАргументы для управления подсистемой Windows для Linux:\n\n    --help\n        Вывод сведений об использовании.\n\n    --debug-shell\n        Открыть оболочку отладки WSL2 для целей диагностики.\n\n    --install [дистрибутив] [параметры...]\n        Установить дистрибутив подсистемы Windows для Linux.\n        Для получения списка допустимых дистрибутивов используйте 'wsl.exe --list --online'.\n\n        Параметры:\n            --enable-wsl1\n                Включить поддержку WSL1.\n\n            --fixed-vhd\n                Создать диск фиксированного размера для хранения дистрибутива.\n\n            --from-file &lt;Path&gt;\n                Установить дистрибутив из локального файла.\n\n            --legacy\n                Использовать устаревший манифест дистрибутива.\n\n            --location &lt;Location&gt;\n                Задать путь установки для дистрибутива.\n\n            --name &lt;Name&gt;\n                Задать имя дистрибутива.\n\n            --no-distribution\n                Устанавливать только необходимые дополнительные компоненты, но не дистрибутив.\n\n            --no-launch, -n\n                Не запускать дистрибутив после установки.\n\n            --version &lt;Version&gt;\n                Указывает версию, используемую для нового дистрибутива.\n\n            --vhd-size &lt;MemoryString&gt;\n                Указывает размер диска для хранения дистрибутива.\n\n            --web-download\n                Скачать дистрибутив из Интернета вместо Microsoft Store.\n\n    --manage [дистрибутив] [параметры...]\n        Изменяет определенные параметры дистрибутива.\n\n        Параметры:\n            --move &lt;Location&gt;\n                Переместить дистрибутив в новое местоположение.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Задать для VHD дистрибутива значение \"разреженный\", чтобы обеспечить автоматическое освобождение дискового пространства.\n\n            --set-default-user &lt;Username&gt;\n                Задать пользователя по умолчанию для дистрибутива.\n\n            --resize &lt;MemoryString&gt;\n                Изменить размер диска дистрибутива до указанного размера.\n\n    --mount &lt;Disk&gt;\n        Присоединяет и подключает физический или виртуальный диск во всех дистрибутивах WSL 2.\n\n        Параметры:\n            --vhd\n                Указывает, что &lt;Disk&gt; относится к виртуальному жесткому диску.\n\n            --bare\n                Присоединить диск к WSL2, но не подключать его.\n\n            --name &lt;Name&gt;\n                Подключить диск, используя собственное имя для точки подключения.\n\n            --type &lt;Type&gt;\n                Файловая система, используемая при подключении диска. Если не указано, по умолчанию используется ext4.\n\n            --options &lt;Options&gt;\n                Дополнительные параметры подключения.\n\n            --partition &lt;Index&gt;\n                Индекс подключаемого раздела. Если не указано, по умолчанию используется весь диск.\n\n    --set-default-version &lt;Version&gt;\n        Изменяет версию установки по умолчанию для новых дистрибутивов.\n\n    --shutdown\n        Немедленно завершает все выполняющиеся дистрибутивы и\n        упрощенную виртуальную машину служебной программы WSL 2.\n\n        Параметры:\n            --force\n                Прервать работу виртуальной машины WSL 2, даже если операция выполняется. Может привести к потере данных.\n\n    --status\n        Показывает состояние подсистемы Windows для Linux.\n\n    --unmount [диск]\n        Отключает и отсоединяет диск от всех дистрибутивов WSL2.\n        При вызове без аргумента отключает и отсоединяет все диски.\n\n    --uninstall\n        Удаляет с этого компьютера пакет подсистемы Windows для Linux.\n\n    --update\n        Обновление пакета подсистемы Windows для Linux.\n\n        Параметры:\n            --pre-release\n                Скачивание предварительной версии, если она доступна.\n\n    --version, -v\n        Вывод сведений о версии.\n\nАргументы для управления дистрибутивами в подсистеме Windows для Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [параметры]\n        Экспорт дистрибутива в TAR-файл.\n        Имя файла может быть \"-\" для stdout.\n\n        Параметры:\n            --format &lt;Format&gt;\n                Указывает формат экспорта. Поддерживаемые значения: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [параметры]\n        Импортирует указанный TAR-файл в качестве нового дистрибутива.\n        Имя файла может быть \"-\" для stdin.\n\n        Параметры:\n            --version &lt;версия&gt;\n                Указывает версию, используемую для нового дистрибутива.\n\n            --vhd\n                Указывает, что предоставленный файл является файлом VHD или VHDX, а не TAR-файлом.\n                Эта операция создает копию VHD-файла в указанном расположении установки.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Импортирует указанный VHD-файл в качестве нового дистрибутива.\n        Этот виртуальный жесткий диск должен быть отформатирован с типом файловой системы ext4.\n\n    --list, -l [параметры]\n        Перечисление дистрибутивов.\n\n        Параметры:\n            --all\n                Перечислить все дистрибутивы, включая дистрибутивы,\n                которые устанавливаются или удаляются в настоящий момент.\n\n            --running\n                Перечислить только дистрибутивы, которые сейчас запущены.\n\n            --quiet, -q\n                Показывать только имена дистрибутивов.\n\n            --verbose, -v\n                Вывод подробных сведений обо всех дистрибутивах.\n\n            --online, -o\n                Вывод списка дистрибутивов, доступных для установки с помощью команды 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Задает дистрибутив по умолчанию.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Изменяет версию указанного дистрибутива.\n\n    --terminate, -t &lt;Distro&gt;\n        Прерывает работу указанного дистрибутива.\n\n    --unregister &lt;Distro&gt;\n        Отменяет регистрацию дистрибутива и удаляет корневую файловую систему.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>Версия WSL: {}\nВерсия ядра: {}\nВерсия WSLg: {}\nВерсия MSRDC: {}\nВерсия Direct3D: {}\nВерсия DXCore: {}\nВерсия Windows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>Версия MSBuild: {}\nФиксация: {}\nВремя сборки: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>Пользовательское ядро, указанное в {}, не найдено: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>Виртуальный жесткий диск пользовательских модулей ядра в {} не найден: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>Настраиваемое системное распределение, указанное в {}, не найдено или имеет неправильный формат.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>(c) Корпорация Майкрософт (Microsoft Corporation). Все права защищены.\nСведения о конфиденциальности этого продукта см. на странице https://aka.ms/privacy.\n\nИспользование: wsl.exe [Аргумент] [Параметры...] [Командная строка]\n\nАргументы:\n    --cd &lt;Directory&gt;\n        Задает указанный каталог в качестве текущего рабочего каталога.\n        Если указан символ \"~\", будет использоваться домашний путь пользователя Linux. Если путь начинается\n        с символа \"/\", он будет интерпретироваться как абсолютный путь Linux.\n        В противном случае значение должно быть абсолютным путем к Windows.\n\n    --distribution, -d &lt;Distro&gt;\n        Запуск указанного дистрибутива.\n\n    --user, -u &lt;UserName&gt;\n        Запуск от имени указанного пользователя.\n\n    --shell-type &lt;standard|login|none&gt;\n        Выполнить указанную команду с указанным типом оболочки.\n\n    --help\n        Вывод сведений об использовании.\n\n    --\n        Передать оставшуюся командную строку как есть.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Нажмите любую клавишу, чтобы продолжить…</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>В аргументе {} отсутствует обязательный параметр.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Выполняется экспорт. Это может занять несколько минут.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Выполняется импорт. Это может занять несколько минут.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>Поддержка приложения GUI отключена через {} или /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Проверяется наличие обновлений.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Распространение возможно только из Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount на ARM64 требует Windows версии 27653 или более поздней.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Последняя версия подсистема Windows для Linux уже установлена.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Обновление подсистема Windows для Linux до версии: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Для этого приложения требуется дополнительный компонент подсистемы Windows для Linux.\nУстановите его, выполнив команду wsl.exe --install --no-distribution\nЧтобы изменения вступили в силу может потребоваться перезапустить систему.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Указанный файл должен иметь расширение файла {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Указанный файл должен иметь расширение файла {} или {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>VmSwitch \"{}\" не найден. Доступные коммутаторы: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Для работы со связанными сетями необходимо установить wsl2.vmSwitch.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Не удалось получить список рассылки из \"{}\". {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Не удалось подключить диск \"{}\" к WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Не удалось изменить размер диска.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Значение не найдено.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Версия Windows {} не поддерживает упакованную версию подсистема Windows для Linux.\nУстановите необходимое обновление с помощью Центра обновления Windows или через: {}\nДополнительные сведения см. в https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Для запуска оболочки отладки требуется запуск wsl.exe от имени администратора.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Сбой процесса установки для распространения \"{}\" с кодом завершения: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Недопустимый формат GUID: \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Недопустимое имя раздела в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Недопустимое имя ключа в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>Предполагается наличие {} в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Недопустимый экранируемый символ: \"{}\" в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Неизвестный ключ \"{}\" в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Недопустимое целочисленное значение \"{}\" для ключа \"{}\" в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Недопустимое значение IP-адреса \"{}\" для ключа \"{}\" в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Недопустимое логическое значение \"{}\" для ключа \"{}\" в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Недопустимый mac-адрес \"{}\" для ключа \"{}\" в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Не удалось открыть файл конфигурации {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Произошли ошибки при запуске WSL</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Не удалось запустить сеанс системного пользователя для \"{}\". Дополнительные сведения см. в journalctl.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Открыть EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Это распределение не содержит имя по умолчанию. Используйте --name , чтобы выбрать имя распространения.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Аргументы {} и {} не могут быть указаны одновременно.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>Аргумент {} требует аргумента {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Недопустимое имя дистрибутива: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Использование устаревшей регистрации распространения. Вместо этого рассмотрите возможность использования дистрибутива на основе tar.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Устаревшие регистрации дистрибутивов не поддерживают аргумент --version .</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>Распределение \"{}\" предоставляется манифестом переопределения.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>Хэш распространения не совпадает. Ожидается: {}, фактический хэш: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Недопустимая шестнадцатеричная строка: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>\"{}\" не поддерживается при установке устаревших дистрибутивов.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Дистрибутив успешно установлен. Его можно запустить с помощью \"wsl.exe -d {}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Недопустимая строка памяти \"{}\" для .wslconfig \"{}\" в {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Не удалось создать диск переключения в \"{}\": {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Вложенная виртуализация не поддерживается на этом компьютере.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Не удалось создать конечную точку сети с адресом \"{}\", назначенный новый адрес: \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Не удалось создать виртуальную сеть с диапазоном адресов \"{}\", создана новая сеть с диапазоном: \"{}\", {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>БЕЗОПАСНЫЙ РЕЖИМ ВКЛЮЧЕН — многие функции будут отключены</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Режим зеркальной сети не поддерживается: {}.\nВозврат к сети NAT.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Требуется ядро Linux версии 5.10 или более поздней</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Версия Windows {}. У {} нет необходимых компонентов</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Брандмауэр Hyper-V не поддерживается</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>Туннелирование DNS не поддерживается</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>Параметр wsl2.localhostForwarding не действует при использовании зеркального сетевого режима</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>В узле обнаружено изменение HTTP-прокси. Перезапустите WSL, чтобы применить изменение.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Конфигурация прокси-сервера localhost обнаружена, но не отражена в WSL. WSL в режиме NAT не поддерживает прокси-серверы localhost.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Конфигурация прокси-сервера IPv6 обнаружена, но не отражена в WSL. WSL в режиме NAT не поддерживает IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Конфигурация прокси-сервера localhost IPv6 обнаружена, но не отражена в WSL. WSL не поддерживает прокси-серверы localhost IPv6.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>При попытке разрешить параметры прокси-сервера произошла непредвиденная ошибка. Параметры прокси-сервера не были зеркально отражены в WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Произошла ошибка при доступе к реестру. Путь: \"{}\". Ошибка: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Не удалось запустить процесс ретрансляции localhost. Ошибка: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Не удалось запустить виртуальную сеть. Установите дополнительный компонент \"Платформа виртуальной машины\", выполнив команду \"wsl.exe --install --no-distribution\"</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Не удалось настроить сеть (networkingMode {}). Выполняется возврат к networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Не удалось включить компонент Windows \"{}\" (код выхода {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Подключаемый модуль \"{}\" вернул неустранимую ошибку</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Подключаемый модуль \"{}\" вернул неустранимую ошибку. Сообщение об ошибке: \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Подключаемого модуля \"{}\" требуется более новая версия WSL. Выполните: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>Параметр .wslconfig \"{}\" отключен политикой компьютера.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>Оболочка отладки отключена политикой компьютера.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>Команда wsl.exe --mount отключена политикой компьютера.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>Подсистема WSL1 отключена политикой компьютера.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Выполните команду \"wsl.exe --set-version {} 2\", чтобы перейти на WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL завершает обновление...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Сбой обновления (код выхода: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Не удалось удалить (код выхода: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Файл журнала: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Не удалось удалить пакет MSIX (ошибка: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Не удалось установить пакет MSIX (ошибка: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Дополнительные компоненты, необходимые для запуска WSL, не установлены.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Установите отсутствующие компоненты.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Импортированный файл не является допустимым дистрибутивом Linux.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Нажмите любую клавишу для выхода...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Доступна новая версия подсистемы Windows для Linux.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Обновите версию {} или просмотрите ее заметки о выпуске ниже.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Обновить</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Просмотреть документы</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Не удалось завершить операцию, так как VHD сейчас используется. Чтобы принудительно остановить использование WSL, выполните команду wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} не является допустимым логическим значением, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Разреженный VHD поддерживается только в WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>Запуск WSL в виде локальной системы не поддерживается.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Совет по производительности:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Использование интенсивной операции ввода-вывода, например {} на дисках с Windows, приведет к низкой производительности. Рассмотрите возможность перемещения файлов проекта в файловую систему Linux для повышения производительности. Щелкните ниже, чтобы получить дополнительные сведения.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Просмотреть документы</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Больше не показывать</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>Виртуальная машина WSL2 завершила работу с ошибкой.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>Трассировка стека сохранена в: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Не удалось запустить распространение. Код ошибки: {}, шаг сбоя: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Не удалось запустить распределение, так как его виртуальный диск поврежден.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Повторяющийся ключ конфигурации \"{}\" в {}:{} (конфликтуемый ключ: \"{}\" в {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Недопустимое значение \"{}\" для ключа конфигурации \"{}\" в {}:{} (Допустимые значения: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Ожидание завершения команды OOBE для распространения \"{}\"...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Не удалось прочитать свойство \"{}\" из дистрибутива {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Похоже, это VHD-файл. Используйте --vhd для импорта VHD вместо tar.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Не удалось установить {} с Microsoft Store: {}\nПопытка скачивания веб-...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Нет настроенного дистрибутива по умолчанию. Укажите дистрибутив для установки.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p отключен, выполняется возврат к 9p с использованием транспорта vsock.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>Возможно, установка WSL повреждена (код ошибки: {}).\nНажмите любую клавишу, чтобы восстановить WSL, или нажмите CTRL-C, чтобы отменить.\nВремя ожидания этого запроса истекет через 60 секунд.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Не удалось проанализировать профиль терминала при регистрации распространения: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Недопустимый размер: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nКод ошибки: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Недопустимый документ JSON. Ошибка интерпретации параметров: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors не может превышать число логических процессоров в системе ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Не удалось запросить сетевой режим</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Модули настроенного ядра предоставлены без указания настроенного ядра. Дополнительные сведения см. на странице https://aka.ms/wslcustomkernel.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Из-за текущей проблемы совместимости с клиентом глобального безопасного доступа туннелирование DNS отключено.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Поддержка разреженного VHD в настоящее время отключена из-за возможного повреждения данных.\nЧтобы принудительно использовать разреженный VHD в дистрибутиве, выполните команду:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Не удалось подключить {}. Дополнительные сведения см. в dmesg.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Обработка /etc/fstab с помощью mount -a завершилась неудачей.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Произошла ошибка при подключении диска дистрибутива, он был подключен только для чтения в качестве резервного варианта.\nИнструкции по восстановлению см. на странице https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Не удалось перевести \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Произошла ошибка. Повторите попытку позже.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>О службе</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>О параметрах подсистемы Windows для Linux</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Майкрософт (Microsoft). Все права защищены.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Параметры подсистемы Windows для Linux</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Параметры подсистемы Windows для Linux позволяют разработчикам управлять файлом .wslconfig с помощью приложения с графическим интерфейсом.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Автоматическое освобождение памяти</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Автоматически освобождает кэшированную память после обнаружения использования процессора в режиме ожидания. Задайте постепенный режим для медленного выпуска и отбрасывание кэша для мгновенного освобождения кэшированной памяти.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Автоматическое освобождение памяти.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Автоматически освобождает кэшированную память после обнаружения использования процессора в режиме ожидания. Задайте постепенный режим для медленного выпуска и отбрасывание кэша для мгновенного освобождения кэшированной памяти.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Автоматический прокси включен</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Позволяет WSL использовать сведения HTTP-прокси Windows.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Автоматический прокси включен.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Позволяет WSL использовать сведения HTTP-прокси Windows.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Используйте лучший анализ DNS</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Применимо, тольк, если для параметра wsl2.dnsTunneling установлено значение ИСТИНА. Если установлено значение ИСТИНА, Windows извлечет вопрос из запроса DNS и попытается его решить, игнорируя неизвестные записи.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Используйте лучший анализ DNS.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Применимо, тольк, если для параметра wsl2.dnsTunneling установлено значение ИСТИНА. Если установлено значение ИСТИНА, Windows извлечет вопрос из запроса DNS и попытается его решить, игнорируя неизвестные записи.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Настраиваемое ядро</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Абсолютный путь Windows к собственному ядру Linux.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Обзор ядер</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Настраиваемое ядро</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Абсолютный путь Windows к собственному ядру Linux.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Модули настраиваемого ядра</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Абсолютный путь Windows к VHD модулей настраиваемого ядра Linux.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Обзор модулей ядра</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Модули настраиваемого ядра</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Абсолютный путь Windows к VHD модулей настраиваемого ядра Linux.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Пользовательский системный дистрибутив</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Обзор дистрибутивов</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Укажите путь к виртуальному жесткому диску, который будет загружаться как пользовательский системный дистрибутив, в основном используемый для работы приложений с графическим интерфейсом в WSL. [Подробнее о системных дистрибутивах можно узнать здесь].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Пользовательский системный дистрибутив</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Укажите путь к VHD, который будет загружаться как пользовательский системный дистрибутив, который в основном используется для работы приложений с графическим интерфейсом в WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Включить консоль отладки</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Логическое значение для включения окна вывода консоли, в котором отображается содержимое dmesg при запуске экземпляра дистрибутива WSL 2.е dmesg при запуске экземпляра дистрибутива WSL 2.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Включить консоль отладки.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Логическое значение для включения окна вывода консоли, в котором отображается содержимое диагностических сообщений при запуске экземпляра дистрибутива WSL 2.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Размер виртуального жесткого диска по умолчанию</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Максимальный размер по умолчанию для расширяемого виртуального жесткого диска WSL (VHD) только для вновь создаваемых дистрибутивов.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Восстановить размер</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Размер виртуального жесткого диска по умолчанию</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Максимальный размер по умолчанию, указанный в мегабайтах, для расширяемого виртуального жесткого диска WSL (VHD) только для вновь создаваемых дистрибутивов.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Разработчик</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Документация</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Изменить режим DrvFS</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Изменение реализации доступа к файлам между ОС в WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS-прокси включен</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Применимо, только если для параметра wsl2.networkingMode установлено значение NAT. Логическое значение, сообщающее WSL о необходимости настройки DNS-сервера в Linux для NAT на узле. Установка значения ЛОЖЬ приведет к зеркалированию DNS-серверов из Windows в Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-прокси включен.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Применимо, только если для параметра wsl2.networkingMode установлено значение NAT. Логическое значение, сообщающее WSL о необходимости настройки DNS-сервера в Linux для NAT на узле. Установка значения ЛОЖЬ приведет к зеркалированию DNS-серверов из Windows в Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>Нисходящая маршрутизация событий DNS включена</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Изменяет способ передачи DNS-запросов из WSL в Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Нисходящая маршрутизация событий DNS включена.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Изменяет способ передачи DNS-запросов из WSL в Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Файловая система</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Включить приложения с графическим интерфейсом</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Логическое значение для включения или отключения поддержки приложений с графическим интерфейсом ([WSLg]) в WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Включить приложения с графическим интерфейсом.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Логическое значение для включения или отключения поддержки приложений с графическим интерфейсом (известных как WSL g) в WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Включить счетчики производительности оборудования</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Включает аппаратные счетчики производительности для Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Включить счетчики производительности оборудования.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Включает аппаратные счетчики производительности для Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Брандмауэр Hyper-V включен</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Включает брандмауэр Hyper-V, позволяющий правилам брандмауэра Windows, а также правилам, специфичным для трафика Hyper-V, фильтровать сетевой трафик WSL.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Брандмауэр Hyper-V включен.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Включает брандмауэр Hyper-V, позволяющий правилам брандмауэра Windows, а также правилам, специфичным для трафика Hyper-V, фильтровать сетевой трафик WSL.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Адрес узла замыкания на себя</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Применимо только в том случае, если для wsl2.networkingMode установлено зеркальное отображение. Если установлено значение ИСТИНА, контейнеру будет разрешено подключаться к узлу или узлу подключаться к контейнеру по IP-адресу, назначенному узлу. Обратите внимание, что адрес замыкания на себя 127.0.0.1 можно использовать всегда — эта опция позволяет также использовать все дополнительно назначенные локальные IP-адреса.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Адрес узла замыкания на себя.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Применимо только в том случае, если для wsl2.networkingMode установлено зеркальное отображение. Если установлено значение ИСТИНА, контейнеру будет разрешено подключаться к узлу или узлу подключаться к контейнеру по IP-адресу, назначенному узлу. Обратите внимание, что адрес замыкания на себя 127.0.0.1 можно использовать всегда — эта опция позволяет также использовать все дополнительно назначенные локальные IP-адреса.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Пропущенные порты</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Применимо только в том случае, если для wsl2.networkingMode установлено зеркальное отображение. Указывает, к каким портам могут привязываться приложения Linux, которые не будут автоматически пересылаться или учитываться в Windows. Должен быть отформатирован в виде списка, разделенного запятыми, например: 3000,9000,9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Сброс портов</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Пропущенные порты</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Применимо только в том случае, если для wsl2.networkingMode установлено зеркальное отображение. Указывает, к каким портам могут привязываться приложения Linux, которые не будут автоматически пересылаться или учитываться в Windows. Должен быть отформатирован в виде списка, разделенного запятыми, например, 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Начальное время ожидания автоматического прокси-сервера</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Применимо, только если для wsl2.autoProxy установлено значение ИСТИНА. Определяет, как долго (в миллисекундах) WSL будет ждать получения информации о HTTP-прокси при запуске контейнера WSL. Если параметры прокси-сервера разрешены по истечении этого времени, экземпляр WSL необходимо перезапустить, чтобы использовать полученные параметры прокси-сервера.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Сброс времени ожидания</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Начальное время ожидания автоматического прокси-сервера</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Применимо, только если для wsl2.autoProxy установлено значение ИСТИНА. Определяет, как долго (в миллисекундах) WSL будет ждать получения информации о HTTP-прокси при запуске контейнера WSL. Если параметры прокси-сервера разрешены по истечении этого времени, экземпляр WSL необходимо перезапустить, чтобы использовать полученные параметры прокси-сервера.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Командная строка ядра</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Дополнительные аргументы командной строки ядра.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Включить переадресацию локального узла</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Логическое значение, указывающее, должны ли порты, привязанные к подстановочному знаку или локальному узлу в виртуальной машине WSL 2, быть доступны для подключения с узла через localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Включить переадресацию локального узла.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Логическое значение, указывающее, должны ли порты, привязанные к подстановочному знаку или локальному хосту в виртуальной машине WSL 2, быть доступны для подключения с хоста через localhost, двоеточие, порт.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} МБ</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Память и процессор</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Объем памяти</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Объем памяти, назначаемой виртуальной машине WSL 2.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Восстановить размер</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Объем памяти</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Объем памяти, указанный в мегабайтах для назначения виртуальной машине WSL 2.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} мс</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Включить встроенную виртуализацию</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Логическое значение для включения или отключения вложенной виртуализации, позволяющее другим вложенным виртуальным машинам работать внутри WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Включить встроенную виртуализацию.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Логическое значение для включения или отключения вложенной виртуализации, позволяющее другим вложенным виртуальным машинам работать внутри WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Сетевой режим</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Указывает сетевой режим для WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Сетевой режим.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Указывает сетевой режим для WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Сеть</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Вы можете работать со всеми своими файлами в любой операционной системе!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Доступ к файлам из одной ОС в другую</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Доступ к файлам Windows из Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Чтобы получить доступ к файлам Windows из Linux, перейдите в \"/mnt\", а затем на букву диска Windows, например, на диск C:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Доступ к файлам Linux с помощью проводника</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Файлы Linux можно просматривать из проводника, перейдя в \"\\\\wsl.localhost\\\" или щелкнув значок \"Linux\".</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Запуск файлов и программ Windows из WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Даже при использовании WSL вы можете запускать исполняемые файлы Windows непосредственно из Bash. Попробуйте ввести команду\n\"powershell.exe /c start .\" для открытия проводника в текущей папке.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Доступ к сетевым приложениям Linux из Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>При создании сетевого приложения (например, для работы на NodeJS или SQL Server) в дистрибутиве Linux, доступ к нему можно получить из приложения Windows (например, браузера Edge или Chrome) с помощью localhost (как обычно). Это означает, что если у вас запущен сервер Linux, прослушивающий порт 3000, для доступа к нему можно перейти на [http://localhost:3000] в Edge на Windows.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Зеркальный режим сети</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>В WSL также есть новый сетевой режим, именуемый зеркальным режимом. Он добавляет дополнительные возможности, такие как поддержка IPv6 и возможность доступа к сетевым приложениям в локальной сети.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее о доступе из одной ОС к файлам другой</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Добро пожаловать в подсистему Windows для Linux.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL — отличный способ опробовать различные дистрибутивы Linux.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Управление дистрибутивами</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее об основных командах WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее об импорте любого дистрибутива Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Команда вывода списка устанавливаемых дистрибутивов WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Установить именованную команду дистрибутива WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Список доступных команд дистрибутивов WSL</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop отлично сочетается с WSL для разработки с помощью контейнеров Linux.\n\nНекоторые преимущества использования Docker Desktop с WSL:\n\n• Команды Docker можно запускать в WSL или Windows с помощью одной и той же управляющей программы и образов Docker.\n• Удобно обмениваться файлами и папками между Windows и Linux с помощью автоматического подключения дисков Windows в WSL.\n• Можно использовать любимые инструменты и редакторы Windows для работы с кодом и файлами Linux, и наоборот, благодаря взаимодействию через WSL.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Интеграция Docker Desktop</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее об использовании WSL с Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Подсистема Windows для Linux (WSL) позволяет запускать ваши любимые средства, утилиты, приложения и процессы Linux непосредственно в Windows.\n\nОзнакомьтесь со списком функций, наиболее популярных среди разработчиков, или изучите полную документацию.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Добро пожаловать в WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Рекомендации по настройке</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Начало работы с Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Документация к подсистеме Windows для Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Графические приложения Linux можно использовать с собственным интерфейсом Windows, например клавишами ALT-TAB, запуском меню \"Пуск\", закреплением панели задач и поддержкой вырезания и вставки.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>Приложения графического интерфейса пользователя</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL может задействовать ваш графический процессор Windows для рабочих процессов Машинного обучения\n\nДвоичные файлы Linux, работающие в WSL, могут автоматически использовать графический процессор в Windows для ускорения производительности. Нужно только установить и запустить эти рабочие процессы точно так же, как и на обычном компьютере с Linux. Существует множество конфигураций, позволяющих начать работу: от запуска CUDA в контейнере Docker (если у вас есть графическая карта NVIDIA) до PyTorch или TensorFlow с DirectML на графической карте AMD, Intel или NVIDIA. Подробнее см. в руководстве по началу работы ниже.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>Ускорение графического процессора</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее об ускорении графического процессора</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее о приложениях графического интерфейса WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Вот список приложений, которые можно попробовать (все эти приложения можно установить в Ubuntu командой \"sudo apt install &lt;имя_приложения&gt;\")\n\n    • gedit — базовый текстовый редактор\n    • audacity — запись и редактирование звуковых файлов\n    • blender — создание трехмерных анимаций и визуализаций\n    • gimp — редактирование фотографий\n    • nautilus — проводник для файлов Linux\n    • vlc — видеопроигрыватель</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Удобный доступ к сетевым приложениям между операционными системами Windows и Linux.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Интеграция сети</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее о сетевых приложениях</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее о зеркальном режиме работы сети</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>WSL можно использовать в качестве постоянной среды разработки непосредственно из VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>Интеграция VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее об использовании WSL с VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Установка</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>После установки VS Code можно установить удаленное расширение WSL из терминала Windows:\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Открыть проект WSL в Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Чтобы открыть проект в VS Code из дистрибутива WSL, откройте командную строку дистрибутива и запустите команду \"code .\", чтобы открыть файл проекта.\n\nСуществуют и другие варианты удаленного доступа к VS Code, находящиеся в палитре команд в самом VS Code. Нажмите клавиши \"SHIFT+CTRL+P\" на клавиатуре, чтобы открыть палитру команд, и введите \"Remote-WSL\" для просмотра списка доступных вариантов удаленной работы с VS Code. Этот интерфейс позволяет повторно открыть папку в удаленном сеансе, указать дистрибутив, который нужно открыть, и многое другое.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Инструкции</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Дополнительные функции</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Заявление о конфиденциальности</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Число процессоров</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Число логических процессоров, назначаемое виртуальной машине WSL 2.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Сбросить счетчик</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Число процессоров</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Число логических процессоров, назначаемое виртуальной машине WSL 2.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Ссылки по теме</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Заметки о выпуске</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Включить безопасный режим</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Запускайте WSL в \"безопасном режиме\", который отключает многие функции и предназначен для восстановления дистрибутивов, находящихся в плохом состоянии.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Включить безопасный режим.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Запускайте WSL в \"безопасном режиме\", который отключает многие функции и предназначен для восстановления дистрибутивов, находящихся в плохом состоянии.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>О службе</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Разработчик</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Управление дистрибутивами</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Интеграция Docker Desktop</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Файловая система</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Общие</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>Ускорение GPU</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>Приложения графического интерфейса пользователя</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Запустить wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Память и процессор</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Сеть</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Интеграция сети</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Добро пожаловать в WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Дополнительные функции</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Настройки</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>Интеграция VS Code</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Что нового</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Работа в файловых системах</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Проблемы</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Включить разреженный виртуальный жесткий диск по умолчанию</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Все вновь созданные виртуальные жесткие диски будут автоматически настроены на разрежение при включении.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Включить разреженный виртуальный жесткий диск по умолчанию.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Все вновь созданные виртуальные жесткие диски будут автоматически настроены на разрежение при включении.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Поменять расположение файла</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Абсолютный путь Windows к обмену виртуальным жестким диском.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Просмотр файлов подкачки</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Поменять расположение файла</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Абсолютный путь Windows к обмену виртуальным жестким диском.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Размер буфера</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Объем места для обмена добавляемого в виртуальную машину WSL 2, 0 для файла переключения. Хранилище буфера — это дисковое ОЗУ, используемое при превышении ограничения на объем памяти на устройстве.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Восстановить размер</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Размер буфера</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Какой объем пространства подкачки (указан в мегабайтах) добавить к виртуальной машине WSL 2. 0 для отсутствия файла подкачки. Хранилище буфера — это дисковое ОЗУ, используемое при превышении ограничения на объем памяти на устройстве.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Включить VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Используйте virtiofs вместо плана 9 для доступа к файлам узла, увеличивая скорость.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Включить Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Позволяет монтировать файловую систему 9P с узла с помощью транспорта virtio.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Время ожидания простоя виртуальной машины</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Число миллисекунд бездействия виртуальной машины до ее завершения.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Сброс времени ожидания</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Время ожидания простоя виртуальной машины</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Число миллисекунд бездействия виртуальной машины до ее завершения.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Создавайте, запускайте, отлаживайте и профилируйте приложения на WSL из Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Запускайте и отлаживайте приложения в WSL из Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее о WSL в Visual Studio для разработчиков .NET</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Подробнее о Visual Studio и WSL для разработчиков C++</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Вы можете легко запускать и отлаживать приложения .NET Core и кроссплатформенные приложения C++ в Linux, не выходя из Visual Studio, с помощью подсистемы Windows для Linux (WSL). Если вы кроссплатформенный разработчик, этот метод помогает легко тестировать больше целевых сред.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Интеграция Visual Studio</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Интеграция Visual Studio</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/sv-SE/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Windows-undersystem för Linux</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Windows-undersystem för Linux gör att utvecklare kan köra en GNU/Linux-miljö, inklusive de flesta kommandoradsverktyg, verktyg och program, direkt i Windows, oförändrade, utan att behöva använda en traditionell virtuell dator eller installation med dubbel start.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Det gick inte att koppla från disken: {}. Mer information finns i 'dmesg' i WSL2.\nOm du vill tvinga WSL2 att stoppa och koppla från disken kör du 'wsl.exe {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>Disken {} är redan ansluten.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Volymen är redan monterad i WSL2.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>En disk med det namnet har redan monterats; demontera disken eller välj ett nytt namn och försök igen.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Det angivna monteringsnamnet innehåller ett ogiltigt /-tecken. Försök igen utan det ogiltiga tecknet.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Disken har monterats som '/mnt/wsl/{}'.\nObs! Platsen skiljer sig om du har ändrat inställningen automount.root i /etc/wsl.conf.\nOm du vill demontera och koppla från disken kör du 'wsl.exe {} {}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Disken anslöts men kunde inte monteras: {}.\nKör dmesg i WSL2 om du vill ha mer information.\nOm du vill koppla från disken kör du wsl.exe {} {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Följande är en lista över giltiga distributioner som kan installeras.\nInstallera med wsl.exe {} &lt;Distro&gt;.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Distributionsnamnet har redan angetts.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>Den angivna installationsplatsen används redan.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Det finns redan en distribution med det angivna namnet. Använd --name för att välja ett annat namn.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Det finns ingen distribution med det angivna namnet.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Administratörsåtkomst krävs för att montera en disk.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Det gick inte att exportera distributionen.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Utför en engångsuppgradering av Windows-undersystem för Linux-filsystemet för den här distributionen...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Det går inte att starta eftersom en annan instans körs med förhöjd behörighet.  Förhöjda och ohöjda instanser får inte köras samtidigt.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Det gick inte att importera distributionen.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Installerar valfri Windows-komponent: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>Laddar ned: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Installerar: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Importerar distributioner</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} har laddats ned.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Windows-undersystem för Linux återupptar en tidigare...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Ogiltigt distributionsnamn: {}.\nAnvänd wsl.exe --list --online' om du vill hämta en lista över giltiga distributioner.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Den Windows-undersystem för Linux-instansen har avslutats.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Ogiltigt kommandoradsargument: {}\nAnvänd {} --help' för att hämta en lista över argument som stöds.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>Kommandoradsargumentet {} kräver ett värde.\nAnvänd {} --help' för att hämta en lista över argument som stöds.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Konsolinställningarna stöds inte. Den äldre konsolen måste inaktiveras för att den här funktionen ska kunna användas.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} är inte ett giltigt heltal.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>En installation, avinstallation eller konvertering pågår för den här distributionen.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Startar {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Det går inte att starta eftersom en annan instans körs med förhöjd behörighet.  Förhöjda och ohöjda instanser får inte köras samtidigt.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Windows-undersystem för Linux har inga installerade distributioner.\nDu kan lösa detta genom att installera en distribution med anvisningarna nedan:\n\nAnvänd wsl.exe --list --online' för att lista tillgängliga distributioner\noch wsl.exe --install &lt;Distro&gt; att installera.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Det finns inga distributioner som körs.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Standardinställning)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Windows-undersystem för Linux-distributioner:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Standarddistribution: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Standardversion: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Avregistrering.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Användaren hittades inte.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>Information om nyckelskillnader med WSL 2 finns på https://aka.ms/wsl2</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Konvertering pågår. Det kan ta några minuter.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>Distributionen är redan den begärda versionen.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>Den äldre distributionen stöder inte WSL 2.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>Det går inte att starta WSL2 eftersom virtualisering inte är aktiverat på den här datorn.\nKontrollera att den valfria komponenten Virtual Machine Platform är aktiverad och att virtualisering är aktiverat i datorns inställningar för inbyggd programvara.\n\nAktivera Virtual Machine Platform genom att köra: wsl.exe --install --no-distribution\n\nMer information finns på https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>För många virtuella hårddiskar eller fysiska diskar är anslutna.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD har redan monterats via wsl.exe --mount, demontera disken innan du startar.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1 stöds inte med den aktuella datorkonfigurationen.\nAktivera den valfria komponenten \"Windows-undersystem för Linux\" för att använda WSL1.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Den här åtgärden stöds bara av WSL2.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Användning:\n    --networking-mode\n       Visa aktuellt nätverksläge.\n\n    --msal-proxy-path\n        Visa sökvägen till MSAL-proxyprogrammet.\n\n    --vm-id\n        Visa ID för virtuell WSL-dator.\n\n    --version\n        Visa WSL-paketets version.\n\n    -n\n        Skriv inte ut en ny rad.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Användning:\n    -a\n        Tvinga resultatet till absolut sökvägsformat.\n    -u\n        Översätt från en Windows-sökväg till en WSL-sökväg (standard).\n    -w\n        Översätt från en WLS-sökväg till en Windows-sökväg.\n    -m\n        Översätt från en WLS-sökväg till en Windows-sökväg med '/' istället för '\\\\'\n\nExempel: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Utför administrativa åtgärder på Windows-undersystem för Linux\n\nAnvändning:\n    /l, /list [Option]\n        Listar registrerade distributioner.\n        /all - Lista valfritt alla distributioner, inklusive distributioner som\n               håller på att installeras eller avinstalleras.\n\n        /running - Listar endast distributioner som körs för närvarande.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Anger fördelningen som standard.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Avslutar distributionen.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Avregistrerar distributionen och tar bort rotfilsystemet.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Med ensamrätt.\nSekretessinformation om den här produkten finns i https://aka.ms/privacy.\n\nUsage: wsl.exe [Argument] [Options...] [CommandLine]\n\nArgument för att köra Linux-binärfiler:\n\n    Om ingen kommandorad anges startar wsl.exe standardgränssnittet.\n\n    --exec, -e &lt;CommandLine&gt;\n        Kör det angivna kommandot utan.\n\n    --shell-type &lt;standard|login|none&gt;\n        Kör det angivna kommandot med den angivna gränssnittstypen.\n\n    --\n        Skicka den återstående kommandoraden som den är.\n\nAlternativ:\n    --cd &lt;Directory&gt;\n        Anger den angivna katalogen som aktuell arbetskatalog.\n        Om ~ används används Linux-användarens hemsökväg. Om sökvägen börjar\n        med ett/-tecken tolkas den som en absolut Linux-sökväg.\n        Annars måste värdet vara en absolut Windows-sökväg.\n\n    --distribution, -d &lt;DistroName&gt;\n        Kör den angivna distributionen.\n\n    --distribution-id &lt;DistroGuid&gt;\n         Kör det angivna distributions-ID:t.\n\n    --user, -u &lt;UserName&gt;\n        Kör som angiven användare.\n\n    --system\n        Startar ett gränssnitt för systemdistributionen.\n\nArgument för att hantera Windows-undersystem för Linux:\n\n    --help\n        Visa användningsinformation.\n\n    --debug-shell\n        Öppna ett WSL2-felsökningsgränssnitt i diagnostiksyfte.\n\n    --install [Distro] [Alternativ...]\n        Installera en Windows-undersystem för Linux distribution.\n         Använd 'wsl.exe --list --online' för en lista över giltiga distributioner'.\n\n        Alternativ:\n            --enable-wsl1\n                Aktivera WSL1-stöd.\n\n            --fixed-vhd\n                Skapa en disk med fast storlek för att lagra distributionen.\n\n            --from-file &lt;Path&gt;\n                Installera en distribution från en lokal fil.\n\n            --legacy\n                Använd det äldre distributionsmanifestet.\n\n            --location &lt;Location&gt;\n                Ange installationssökvägen för distributionen.\n\n            --name &lt;Name&gt;\n                 Ange namnet på distributionen.\n\n            --no-distribution\n                Installera endast nödvändiga valfria komponenter, installerar inte någon distribution.\n\n            --no-launch, -n\n                Starta inte distributionen efter installationen.\n\n            --version &lt;Version&gt;\n                Anger vilken version som ska användas för den nya distributionen.\n\n            --vhd-size &lt;MemoryString&gt;\n                Anger storleken på den disk som distributionen ska lagras på.\n\n            --web-download\n                Ladda ned distributionen från Internet i stället för Microsoft Store.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Distributionsalternativ för ändringar.\n\n        Alternativ:\n            --move &lt;Location&gt;\n                Flytta distributionen till en ny plats.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Ange att den virtuella hårddisken för distribution ska vara sparse, vilket gör att diskutrymmet kan frigöras automatiskt.\n\n            --set-default-user &lt;Username&gt;\n                Ange standardanvändaren för fördelningen.\n\n            --resize &lt;MemoryString&gt;\n                Ändra storlek på disken för fördelningen till den angivna storleken.\n\n    --mount &lt;Disk&gt;\n        Ansluter och monterar en fysisk eller virtuell disk i alla WSL 2-distributioner.\n\n        Alternativ:\n            --vhd\n                Anger att &lt;Disk&gt; refererar till en virtuell hårddisk.\n\n            --bare\n                Anslut disken till WSL2, men montera den inte.\n\n            --name &lt;Name&gt;\n                Montera disken med ett anpassat namn för monteringspunkten.\n\n            --type &lt;Type&gt;\n                Filsystem som ska användas vid montering av en disk,  om inget standardvärde anges till ext4.\n\n            --options &lt;Options&gt;\n                Ytterligare monteringsalternativ.\n\n            --partition &lt;Index&gt;\n                Index för partitionen som ska monteras, om det inte anges som standard för hela disken.\n\n    --set-default-version &lt;Version&gt;\n        Ändrar standardinstallationsversionen för nya distributioner.\n\n    --shutdown\n        Avslutar omedelbart alla distributioner som körs och den virtuella WSL 2-\n        lightweight utility virtual machine.\n\n        Alternativ:\n            --force\n                Avsluta den virtuella WSL 2-datorn även om en åtgärd pågår. Kan orsaka dataförlust.\n\n    --status\n        Visa status för Windows-undersystem för Linux.\n\n    --unmount [Disk]\n        Demonterar och kopplar bort en disk från alla WSL2-distributioner.\n        Demonterar och kopplar bort alla diskar om det anropas utan argument.\n\n    --uninstall\n        Avinstallerar Windows-undersystem för Linux paketet från den här datorn.\n\n    --update\n        Uppdatera Windows-undersystem för Linux-paketet.\n\n        Alternativ:\n            --pre-release\n                Ladda ned en förhandsversion om tillgänglig.\n\n    --version, -v\n        Visa versionsinformation.\n\nArguments for managing Argument för att hantera distributioner i Windows-undersystem för Linux:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\n        Exporterar distributionen till en tar-fil.\n        Filnamnet kan vara - för stdout.\n\n        Alternativ:\n            --format &lt;Format&gt;\n                S Anger exportformatet. Värden som stöds: tar, tar.gz, tar.xz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\n         Importerar den angivna tar-filen som en ny distribution.\n        Filnamnet kan vara - för stdin.\n\n        Alternativ:\n            --version &lt;Version&gt;\n                Anger vilken version som ska användas för den nya distributionen.\n\n            --vhd\n                Anger att den angivna filen är en VHD- eller VHDX-fil, inte en tar-fil.\n                ThDen här åtgärden gör en kopia av VHD-filen på den angivna installationsplatsen.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n         Importerar den angivna VHD-filen som en ny distribution.\n        Den här virtuella hårddisken måste formateras med filsystemtypen ext4.\n\n    --list, -l [Options]\n        Visar distributioner.\n\n        Alternativ:\n            --all\n                 Lista alla distributioner, inklusive distributioner som\n                 håller på att installeras eller avinstalleras.\n\n            --running\n                Lista endast distributioner som körs för närvarande.\n\n            --quiet, -q\n                Visa endast distributionsnamn.\n\n            --verbose, -v\n                Visa detaljerad information om alla distributioner.\n\n            --online, -o\n                Visar en lista över tillgängliga distributioner för installation med 'wsl.exe --install'.\n\n    --set-default, -s &lt;Distro&gt;\n        Anger fördelningen som standard\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Ändrar versionen av den angivna distributionen.\n\n    --terminate, -t &lt;Distro&gt;\n        Avslutar den angivna distributionen.\n\n    --unregister &lt;Distro&gt;\n        Avregistrerar distributionen och tar bort rotfilsystemet.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL-version: {}\nKernelversion: {}\nWSLg-version: {}\nMSRDC-version: {}\nDirect3D-version: {}\nDXCore-version: {}\nWindows-version: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild-version: {}\nBekräfta: {}\nBuild-tid: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>Den anpassade kerneln som anges i {} hittades inte: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>Det gick inte att hitta den anpassade kernelmodulens virtuella hårddisk i {}: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>Det gick inte att hitta den anpassade systemdistributionen som angavs i {} eller så har den inte rätt format.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Copyright (c) Microsoft Corporation. Med ensamrätt.\nSekretessinformation om den här produkten finns på https://aka.ms/privacy.\n\nAnvändning: wslg.exe [Argument] [Options...] [CommandLine]\n\nArgument:\n    --cd &lt;Directory&gt;\n        Anger den angivna katalogen som aktuell arbetskatalog.\n        Om ~ används kommer Linux-användarens hemsökväg att användas. Om sökvägen börjar\n        med ett /-tecken tolkas det som en absolut Linux-sökväg.\n        Annars måste värdet vara en absolut Windows-sökväg.\n\n    --distribution, -d &lt;Distro&gt;\n        Kör den angivna distributionen.\n\n    --user, -u &lt;UserName&gt;\n        Kör som den angivna användaren.\n\n    --shell-type &lt;standard|login|none&gt;\n        Kör det angivna kommandot med den angivna gränssnittstypen.\n\n    --help\n        Visa användningsinformation.\n\n    --\n        Skicka den återstående kommandoraden som den är.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Tryck ned valfri tangent för att fortsätta....</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>Argumentet {} saknar en obligatorisk parameter.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Export pågår. Det kan ta några minuter.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>Import pågår. Det kan ta några minuter.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>Stöd för GUI-program har inaktiverats via {} eller /etc/wsl.conf.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Letar efter uppdateringar.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Den här distributionen är endast tillgänglig från Microsoft Store.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>wsl.exe --mount på ARM64 kräver Windows version 27653 eller senare.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Den senaste versionen av Windows-undersystem för Linux är redan installerad.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Uppdaterar Windows-undersystem för Linux till version: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Det här programmet kräver den Windows-undersystem för Linux valfria komponenten.\nInstallera genom att köra: wsl.exe --install --no-distribution\nSystemet kan behöva startas om så att ändringarna kan börja gälla.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Den angivna filen måste ha filnamnstillägget {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Den angivna filen måste ha filnamnstillägget {} eller {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>Det gick inte att hitta VmSwitch {}. Tillgängliga växlar: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Bryggda nätverk kräver att wsl2.vmSwitch anges.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>Det gick inte att hämta distributionslistan från {}. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>Det gick inte att ansluta disken'{}' till WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Det gick inte att ändra storlek på disken.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Inget värde hittades.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Windows-versionen {} stöder inte den paketerade versionen av Windows-undersystem för Linux.\nInstallera den begärda uppdateringen via Windows Update eller via: {}\nMer information finns på https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Om du kör felsökningsgränssnittet måste du köra wsl.exe som administratör.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>Installationsprocessen för distributionen '{}' misslyckades med slutkoden: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Ogiltigt GUID-format: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>Ogiltigt namn på avsnitt i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>Ogiltigt nyckelnamn i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>{} förväntas i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>Ogiltigt undantagstecken: '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>Okänd tangent '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>Ogiltigt heltalsvärde '{}' för nyckeln '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>Ogiltigt IP-värde '{}' för nyckeln '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>Ogiltigt booleskt värde'{}' för nyckeln '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>Ogiltig mac-adress '{}' för nyckeln '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>Det gick inte att öppna konfigurationsfilen {}, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>Fel uppstod vid WSL-start</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>Det gick inte att starta systemanvändarsessionen för {}. Mer information finns i journalctl.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>Öppna EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Den här distributionen innehåller inget standardnamn. Använd --name för att välja distributionsnamnet.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>Det går inte att ange argumenten {} och {} samtidigt.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>Argumentet {} kräver argumentet {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Ogiltigt distributionsnamn: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Använder äldre distributionsregistrering. Överväg att använda en tar-baserad distribution i stället.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Äldre distributionsregistreringar stöder inte argumentet --version .</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>Fördelningen \"{}\" tillhandahålls av ett åsidosättningsmanifest.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>Distributionshash matchar inte. Förväntat: {}, faktisk hash: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Ogiltig hexadecimal sträng: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>'{}' stöds inte vid installation av äldre distributioner.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Distributionen har installerats. Det kan startas via \"wsl.exe -d {}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>Ogiltig minnessträng '{}' för .wslconfig-posten '{}' i {}:{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>Det gick inte att skapa växlingsdisken i '{}': {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>Kapslad virtualisering stöds inte på den här datorn.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Det gick inte att skapa nätverksslutpunkten med adressen: '{}', tilldelad ny adress: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Det gick inte att skapa ett virtuellt nätverk med adressintervallet '{}', ett nytt nätverk med intervallet '{}', {} skapades</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>FELSÄKERT LÄGE AKTIVERAT – många funktioner inaktiveras</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Speglat nätverksläge stöds inte: {}.\nÅtergår till NAT-nätverk.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Linux Kernel version 5.10 eller senare krävs</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows-version {}.{} har inte de nödvändiga funktionerna</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Hyper-V-brandväggen stöds inte</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>DNS-tunneltrafik stöds inte</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>Inställningen wsl2.localhostForwarding har ingen effekt när speglat nätverksläge används</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>En http-proxyändring har identifierats på värden. Starta om WSL för att tillämpa ändringen.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>En konfiguration för localhost-proxy upptäcktes men speglades inte i WSL. WSL i NAT-läge stöder inte localhost-proxys.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>En IPv6-proxykonfiguration identifierades men speglades inte till WSL. WSL i NAT-läge stöder inte IPv6.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>En konfiguration för localhost IPv6-proxy upptäcktes men speglades inte i WSL. WSL stöder inte localhost IPv6-proxys.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Ett oväntat fel uppstod när proxyinställningarna skulle matchas. Proxyinställningarna speglades inte i WSL.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Ett fel uppstod vid åtkomst till registret. Sökväg: {}. Fel: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>Det gick inte att starta localhost-reläprocessen. Fel: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Det gick inte att starta det virtuella nätverket. Installera den valfria komponenten Virtual Machine Platform genom att köra: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Det gick inte att konfigurera nätverket (networkingMode {}) och återgår till networkingMode {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>Det gick inte att aktivera Windows-komponenten '{}' (slutkod {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>Ett oåterkalleligt fel returnerades av plugin-programmet '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>Ett oåterkalleligt fel returnerades av plugin-programmet '{}'. Felmeddelande: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>Plugin-programmet {} kräver en nyare version av WSL. Kör: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>.wslconfig-inställningen '{}' har inaktiverats av datorprincipen.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>Felsökningsgränssnittet har inaktiverats av datorprincipen.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount har inaktiverats av datorprincipen.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>WSL1 har inaktiverats av datorprincipen.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>Kör wsl.exe --set-version {} 2 för att uppgradera till WSL2.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL slutför en uppgradering...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Uppdateringen misslyckades (slutkod: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Avinstallationen misslyckades (slutkod: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Loggfil: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>Det gick inte att ta bort MSIX-paketet (fel: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>Det gick inte att installera MSIX-paketet (fel: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Valfria komponenter som behövs för att köra WSL har inte installerats.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Installera komponenter som saknas.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>Den importerade filen är inte en giltig Linux-distribution.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Tryck på valfri tangent för att avsluta...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>En ny version av Windows-undersystem för Linux är tillgänglig.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>Uppdatera till version {} eller visa viktig information nedan.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Uppdatera</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Visa dokument</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>Det gick inte att slutföra åtgärden eftersom den virtuella hårddisken används för närvarande. Så här tvingar du WSL att sluta använda: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} är inte ett giltigt booleskt värde, &lt;tru|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Sparse VHD stöds endast på WSL2.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>Det finns inte stöd för att köra WSL som lokalt system.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Prestandatips:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Om du använder en I/O-intensiv åtgärd som {} på dina Windows-enheter får du dåliga prestanda. Överväg att flytta dina projektfiler till Linux-filsystemet för bättre prestanda. Klicka nedan om du vill veta mer.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Visa dokument</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Visa inte detta igen</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>Den virtuella WSL2-maskinen kraschade.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>Stackspårningen har sparats i: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Det gick inte att starta distributionen. Felkod: {}, felsteg: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Det gick inte att starta distributionen eftersom dess virtuella disk är skadad.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>Duplicerad konfigurationsnyckel '{}' i {}:{} (nyckel i konflikt: '{}' i {}:{})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>Ogiltigt värde '{}' för konfigurationsnyckeln '{}' i {}:{} (Giltiga värden: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>Väntar på att OOBE-kommandot ska slutföras för distributionen \"{}\"...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>Det gick inte att läsa egenskapen '{}' från fördelningen {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Det här ser ut som en VHD-fil. Använd --vhd för att importera en virtuell hårddisk i stället för en tjära.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Det gick inte att installera {} från Microsoft Store: {}\nFörsöker ladda ned...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Ingen standarddistribution har konfigurerats. Ange en distribution att installera.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p är inaktiverat och återgår till 9p med vsock-transport.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>WSL-installationen verkar vara skadad (felkod: {}).\nTryck på valfri tangent för att reparera WSL eller CTRL-C för att avbryta.\nTidsgränsen för den här frågan går ut om 60 sekunder.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Det gick inte att parsa terminalprofilen när distributionen registrerades: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Ogiltig storlek: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nFelkod: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Ogiltigt JSON-dokument. Syntaxfel: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors får inte överskrida antalet logiska processorer i systemet ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Det gick inte att fråga om nätverksläget</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Anpassade kernelmoduler angavs utan att en anpassad kernel angavs. Mer information finns i https://aka.ms/wslcustomkernel.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>DNS-tunneling är inaktiverat på grund av ett aktuellt kompatibilitetsproblem med klienten för Global säker åtkomst.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Stöd för dynamiskt expanderbar VHD är för närvarande inaktiverat på grund av potentiell datakorruption.\nOm du vill tvinga en distribution att använda en dynamiskt expanderbar VHD kör du:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>Det gick inte att montera {}, se dmesg för mer information.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>Det gick inte att bearbeta /etc/fstab med mount -a.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Ett fel uppstod vid montering av distributionsdisken, den monterades skrivskyddad som en reservåtgärd.\nSe återställningsinstruktioner på: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>Det gick inte att översätta \"{}\"</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Ett fel uppstod. Försök igen senare.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Om</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Om Windows-undersystem för Linux-inställningar</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Med ensamrätt.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Windows-undersystem för Linux inställningar</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Windows-undersystem för Linux Inställningar låter utvecklare hantera .wslconfig-filen med hjälp av ett GUI-baserat program.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Frigör automatiskt minne</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Frigör automatiskt cachelagrat minne efter identifiering av inaktiv CPU-användning. Ställ in på gradvis för långsam frisläppning och dropcache för omedelbar frisläppning av cachelagrat minne.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Frigör automatiskt minne.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Frigör automatiskt cachelagrat minne efter identifiering av inaktiv CPU-användning. Ställ in på gradvis för långsam frisläppning och dropcache för omedelbar frisläppning av cachelagrat minne.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Automatisk proxy har aktiverats</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>Gör att WSL kan använda Windows HTTP-proxyinformation.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Automatisk proxy har aktiverats.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gör att WSL kan använda Windows HTTP-proxyinformation.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>Använd bästa möjliga DNS-parsning</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.dnsTunneling har angetts till true. När värdet är true extraherar Windows frågan från DNS-begäran och försöker lösa den och ignorerar de okända posterna.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Använd bästa möjliga DNS-parsning.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.dnsTunneling har angetts till true. När värdet är true extraherar Windows frågan från DNS-begäran och försöker lösa den och ignorerar de okända posterna.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Anpassad kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>En absolut Windows-sökväg till en anpassad Linux-kernel.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bläddra bland kärnor</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Anpassad kernel</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En absolut Windows-sökväg till en anpassad Linux-kernel.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Anpassade kernelmoduler</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>En absolut Windows-sökväg till en anpassad virtuell hårddisk för Linux-kernelmoduler.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bläddra bland kernelmoduler</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Anpassade kernelmoduler</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En absolut Windows-sökväg till en anpassad virtuell hårddisk för Linux-kernelmoduler.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Distribution av anpassat system</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bläddra bland distributioner</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Ange en sökväg till en virtuell hårddisk som ska läsas in som en anpassad systemdistro, främst för att driva GUI-appar i WSL. [Läs mer om systemdistros här].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Distribution av anpassat system</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ange en sökväg till en virtuell hårddisk som ska läsas in som en anpassad systemdistro, främst för att driva GUI-appar i WSL.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Aktivera felsökningskonsol</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>Booleskt värde för att aktivera ett utdatakonsolfönster som visar innehållet i dmesg när en WSL 2-distributionsinstans startas.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivera felsökningskonsol.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleskt värde för att aktivera ett utdatakonsolfönster som visar innehållet i diagnostikmeddelanden när en WSL 2-distributionsinstans startas.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Standardstorlek för virtuell hårddisk</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Standardstorleken för den expanderbara virtuella WSL-hårddisken (VHD) för nyligen skapade distributioner.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Återställ storlek</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Standardstorlek för virtuell hårddisk</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Standardstorleken, angiven i megabyte, för den expanderbara virtuella WSL-hårddisken (VHD) endast för nyligen skapade distributioner.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Utvecklare</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Dokumentation</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>Ändra DrvFS-läge</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>Ändrar implementering av filåtkomst mellan operativsystem i WSL.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS-proxy har aktiverats</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.networkingMode har angetts till NAT. Booleskt värde för att informera WSL om att konfigurera DNS-servern i Linux till NAT på värden. Om du anger falskt speglas DNS-servrar från Windows till Linux.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS-proxy har aktiverats.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.networkingMode har angetts till NAT. Booleskt värde för att informera WSL om att konfigurera DNS-servern i Linux till NAT på värden. Om du anger falskt speglas DNS-servrar från Windows till Linux.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>Händelsedirigering nedåt för DNS har aktiverats</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>Ändrar hur DNS-begäranden proxieras från WSL till Windows.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Händelsedirigering nedåt för DNS har aktiverats.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Ändrar hur DNS-begäranden proxieras från WSL till Windows.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Filsystem</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>Aktivera GUI-program</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>Booleskt värde för att aktivera eller inaktivera stöd för GUI-program ([WSLg]) i WSL.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivera GUI-program.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleskt värde för att aktivera eller inaktivera stöd för GUI-program (kallas WSL g) i WSL.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Aktivera prestandaräknare för maskinvara</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Aktiverar maskinvaruprestandaräknare för Linux.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivera prestandaräknare för maskinvara.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Aktiverar maskinvaruprestandaräknare för Linux.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Hyper-V-brandväggen är aktiverad</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>Aktiverar Hyper-V-brandväggen som gör att Windows-brandväggsreglerna och regler som är specifika för Hyper-V-trafik kan filtrera WSL-nätverkstrafik.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hyper-V-brandväggen har aktiverats.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Aktiverar Hyper-V-brandväggen som gör att Windows-brandväggsreglerna och regler som är specifika för Hyper-V-trafik kan filtrera WSL-nätverkstrafik.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Loopback för värdadress</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.networkingMode är inställt på spegling. När värdet är True tillåter det att containern ansluter till värden, eller värden, för att ansluta till containern, via en IP-adress som tilldelats värden. Observera att loopback-adressen 127.0.0.1 alltid kan användas – med det här alternativet kan även alla ytterligare tilldelade lokala IP-adresser användas.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Loopback för värddatoradress.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.networkingMode är inställt på spegling. När värdet är True tillåter det att containern ansluter till värden, eller värden, för att ansluta till containern, via en IP-adress som tilldelats värden. Observera att loopback-adressen 127.0.0.1 alltid kan användas – med det här alternativet kan även alla ytterligare tilldelade lokala IP-adresser användas.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Ignorerade portar</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.networkingMode är inställt på spegling. Anger vilka portar Som Linux-program kan binda till som inte vidarebefordras automatiskt eller beaktas i Windows. Ska formateras i en kommaavgränsad lista, t.ex. 3000 9000 9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Återställ portar</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ignorerade portar</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.networkingMode är inställt på spegling. Anger vilka portar som Linux-program kan binda till som inte vidarebefordras automatiskt eller tas i beaktande i Windows. Ska formateras i en kommaavgränsad lista, t.ex. 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>Tidsgräns för inledande automatisk proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.autoProxy har angetts till true. Anger hur länge (i millisekunder) WSL ska vänta på att HTTP-proxyinformation hämtas när en WSL-behållare startas. Om proxyinställningarna matchas efter den här tidpunkten måste WSL-instansen startas om för att använda de hämtade proxyinställningarna.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Återställ tidsgräns</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Tidsgräns för inledande automatisk proxy</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Gäller endast när wsl2.autoProxy har angetts till true. Anger hur länge WSL i millisekunder ska vänta på att HTTP-proxyinformation hämtas när en WSL-container startas. Om proxyinställningarna matchas efter den här tidpunkten måste WSL-instansen startas om för att använda de hämtade proxyinställningarna.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Kernel-kommandorad</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Ytterligare kernel-kommandoradsargument.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Aktivera localhost-vidarebefordran</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>Booleskt värde som anger om portar som är bundna till jokertecken eller localhost i den virtuella WSL 2-datorn ska kunna anslutas från värden via localhost:port.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivera localhost-vidarebefordran.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleskt värde som anger om portar som är bundna till jokertecken eller localhost i den virtuella WSL 2-datorn ska kunna anslutas från värddatorn via localhost, kolon, port.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Minne och processor</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Minnesstorlek</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>Hur mycket minne som ska tilldelas den virtuella WSL 2-datorn.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Återställ storlek</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Minnesstorlek</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hur mycket minne som anges i megabyte för att tilldela den virtuella WSL 2-datorn.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} millisekunder</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>Aktivera kapslad virtualisering</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>Booleskt värde för att aktivera eller inaktivera kapslad virtualisering, vilket gör att andra kapslade virtuella datorer kan köras i WSL 2.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivera kapslad virtualisering.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Booleskt värde för att aktivera eller inaktivera kapslad virtualisering, vilket gör att andra kapslade virtuella datorer kan köras i WSL 2.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Nätverksläge</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>Anger nätverksläget för WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Nätverksläge.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Anger nätverksläget för WSL.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Nätverk</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Du kan arbeta i alla operativsystem med alla dina filer!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>Filåtkomst mellan operativsystem</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Få åtkomst till dina Windows-filer från Linux</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Du kan komma åt dina Windows-filer från Linux genom att navigera till /mnt och sedan till din Windows-enhetsbeteckning, t.ex. för C-enheten:\n\ncd /mnt/c</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Få åtkomst till dina Linux-filer med Utforskaren</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Du kan visa dina Linux-filer från Utforskaren genom att navigera till \\\\wsl.localhost\\ eller klicka på Linux-ikonen.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Starta Windows-filer och -program från WSL</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>Även om du använder WSL kan du köra dina körbara Windows-filer direkt från bash. Prova att köra\npowershell.exe /c start . för att öppna Utforskaren i den aktuella mappen.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Komma åt Linux-nätverksappar från Windows</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Om du skapar en nätverksapp (till exempel en app som körs på en NodeJS- eller SQL-server) i din Linux-distribution, kan du komma åt den från en Windows-app (som din Edge eller Chrome-webbläsare) med localhost (precis som vanligt). Det innebär att om du startade en Linux-server som lyssnar på port 3000 kan du gå till [http://localhost:3000] i Edge på Windows för att komma åt den.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Nätverk i speglat läge</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL innehåller också ett nytt nätverksläge som kallas speglat läge som lägger till avancerade funktioner som IPv6-stöd och möjlighet att komma åt dina nätverksprogram i ditt lokala nätverk.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om filåtkomst mellan operativsystem</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Välkommen till Windows-undersystem för Linux</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL är ett bra sätt att testa olika Linux-distributioner.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Distributionshantering</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om grundläggande WSL-kommandon</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om hur du importerar Linux-distributioner</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Visa lista över installerbara WSL-distributionskommandon</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe -l -o</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Installera ett namngivet WSL Distro-kommando</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe --install &lt;DistroName&gt;</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Visa lista över tillgängliga WSL-distributionskommandon</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>wsl.exe -l</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop fungerar utmärkt med WSL för att hjälpa dig att utveckla med Linux-containrar.\n\nNågra av fördelarna med att använda Docker Desktop med WSL är:\n\n• Du kan köra Docker-kommandon i WSL eller i Windows med samma Docker-daemon och avbildningar.\n• Du kan dela filer och mappar sömlöst mellan Windows och Linux med hjälp av den automatiska monteringen av Windows-enheter i WSL.\n• Du kan använda de Windows-verktyg och -redigerare du föredrar för att arbeta med Linux-kod och -filer, och vice versa, tack vare WSL:s samverkan.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker Desktop-integration</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om att använda WSL med Docker</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Med Windows-undersystem för Linux (WSL) kan du köra dina Linux-verktyg, hjälpmedel, program och arbetsflöden direkt i Windows.\n\nTa en stund för att förhandsgranska några av communityns favoritfunktioner eller visa vår omfattande dokumentation.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>Välkommen till WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Metodtips för konfiguration</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Komma igång med Linux</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>dokumentation om Windows-undersystem för Linux (WSL)</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Du kan använda grafiska Linux-program med inbyggda Windows-interaktioner som alt-tab, startmenystart, fästning i aktivitetsfältet och stöd för klipp ut och klistra in.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>GUI-appar</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL kan utnyttja din Windows GPU för Machine Learning-arbetsflöden \n\nLinux-binärfiler som körs i WSL kan automatiskt använda din GPU i Windows för att öka prestandan. Du behöver bara installera och köra dessa arbetsflöden på samma sätt som på en vanlig Linux-dator. Det finns många olika sätt att komma igång från att köra CUDA i en docker-container om du har ett NVIDIA-grafikkort, till att köra PyTorch eller TensorFlow med DirectML på ditt AMD-, Intel- eller NVIDIA-grafikkort. Se vår komma igång-guide nedan om du vill veta mer.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>GPU-acceleration</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om GPU-acceleration</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om WSL GUI-appar</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Här är en lista över appar att prova (du kan installera alla dessa i Ubuntu genom att skriva sudo apt install &lt;Appnamnet&gt;)\n\n    • gedit – Grundläggande textredigerare\n    • audacity – Spela in och redigera ljudfiler\n    • blender – Gör 3D-animeringar och visualiseringar\n    • gimp – Redigera foton\n    • nautilus – En Linux-filutforskare\n    •vlc – videospelare</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Du kan enkelt komma åt nätverksappar i Windows- och Linux-operativsystem.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Nätverksintegrering</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om nätverksprogram</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om nätverk i speglat läge</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>Du kan använda WSL som din utvecklingsmiljö på heltid direkt från VS Code.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS Code-integration</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om att använda WSL med VS Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Så här installerar du</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>När du har installerat VS Code kan du installera WSL-fjärrtillägget från Windows-terminal:\n\ncode –install-extension ms-vscode-remote.remote-wsl</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Öppna ett WSL-projekt i Visual Studio Code</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>Öppna ett projekt i VS Code från WSL-distributionen genom att öppna distributionens kommandorad och köra kod. För att öppna en projektfil.\n\nDu kan också komma åt fler VS Code-fjärralternativ via kommandopaletten inom själva VS Code. Tryck på SKIFT+CTRL+P på tangentbordet för att öppna kommandopaletten och skriv Remote-WSL om du vill se en lista över tillgängliga VS Code-fjärralternativ så att du kan öppna mappen igen i en fjärrsession, ange vilken distribution du vill öppna med mera.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Så här använder du</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>Valfria funktioner</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Sekretesspolicy</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>Antal processorer</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>Hur många logiska processorer som ska tilldelas den virtuella WSL 2-datorn.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Återställ antal</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Antal processorer</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hur många logiska processorer som ska tilldelas den virtuella WSL 2-datorn.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>Relaterade länkar</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Viktig information</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Aktivera felsäkert läge</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>Kör WSL i felsäkert läge, vilket inaktiverar många funktioner och är avsett att användas för att återställa distributioner i felaktiga tillstånd.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivera felsäkert läge.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Kör WSL i felsäkert läge, vilket inaktiverar många funktioner och är avsett att användas för att återställa distributioner i felaktiga tillstånd.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Om</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Utvecklare</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Distributionshantering</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Docker Desktop-integration</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Filsystem</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Allmänt</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>GPU-acceleration</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>GUI-appar</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>Starta wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Minne och processor</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Nätverk</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Nätverksintegrering</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>Välkommen till WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>Valfria funktioner</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Inställningar</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS Code-integration</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Nyheter</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Arbeta i alla filsystem</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Problem</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Aktivera virtuell hårddisk med sparse som standard</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Nyskapad VHD ställs in på sparse automatiskt när den är aktiverad.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Aktivera virtuell hårddisk med gles matris som standard.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Nyskapad VHD ställs in på sparse automatiskt när den är aktiverad.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Växla filplats</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>En absolut Windows-sökväg till den virtuella växlingshårddisken.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Bläddra bland växlingsfiler</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Växla filplats</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>En absolut Windows-sökväg till den virtuella växlingshårddisken.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Växlingsstorlek</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>Hur mycket växlingsutrymme som ska läggas till på den virtuella WSL 2-datorn, 0 utan växlingsfil. Växlingslagring är diskbaserat RAM-minne som används när minnesbehovet överskrider gränsen på maskinvaruenheten.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Återställ storlek</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Växlingsstorlek</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Hur mycket växlingsutrymme, som anges i megabyte, som ska läggas till i den virtuella WSL 2-datorn. 0 för ingen växlingsfil. Växlingslagring är diskbaserat RAM-minne som används när minnesbehovet överskrider gränsen på maskinvaruenheten.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>Aktivera VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Använd virtiofs i stället för plan 9 för att komma åt värdfiler, vilket ökar hastigheten.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Aktivera Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Aktiverar montering av 9P-filsystemet från värden med hjälp av virtio-transporten.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>Timeout för inaktivitet för virtuell dator</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Antalet millisekunder som en virtuell dator är inaktiv innan den stängs av.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Återställ tidsgräns</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Timeout för inaktivitet för virtuell dator</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Antalet millisekunder som en virtuell dator är inaktiv innan den stängs av.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Skapa, kör, felsök och profilera dina appar som körs på WSL direkt från Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Kör och felsök dina appar på WSL från Visual Studio</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om WSL i Visual Studio för .NET-utvecklare</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>Läs mer om Visual Studio och WSL för C++-utvecklare</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Du kan enkelt köra och felsöka dina .NET Core- och plattformsoberoende C++-appar i Linux utan att lämna Visual Studio med Windows-undersystem för Linux (WSL). Om du är en plattformsoberoende utvecklare kan du använda den här metoden som ett enkelt sätt att testa fler av dina målmiljöer.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studio-integrering</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studio integrering</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/tr-TR/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux, geliştiricilerin bir GNU/Linux ortamını -- çoğu komut satırı aracı, yardımcı program ve uygulama dahil -- doğrudan Windows üzerinde, değiştirilmeden, geleneksel bir sanal makine veya çift önyükleme kurulumunun ek yükü olmadan çalıştırmasını sağlar.</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>Disk ayrılamadı: {}. Daha fazla ayrıntı için lütfen WSL2 içinde 'dmesg' çalıştırın.\nWSL2'yi diski durdurmaya ve ayırmaya zorlamak için 'wsl.exe {}' çalıştırın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>'{}' diski zaten eklenmiş.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>Bu birim zaten WSL2 içine bağlı.</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Bu ada sahip bir disk zaten takılı; lütfen diskin bağlantısını kesin veya yeni bir ad seçin ve tekrar deneyin.</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>Belirtilen bağlama adı geçersiz bir '/' karakteri içeriyor. Lütfen geçersiz karakter olmadan yeniden deneyin.</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>Disk başarıyla '/mnt/wsl/{}' olarak bağlandı.\nNot: /etc/wsl.conf içinde automount.root ayarını değiştirdiyseniz konum farklı olacak.\nDiski çıkarıp ayırmak için '{} {}' wsl.exe çalıştırın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>Disk eklendi ancak bağlanamadı: {}.\nDaha fazla ayrıntı için WSL2 içinde 'dmesg' çalıştırın.\nDiski ayırmak için '{} {}' wsl.exe çalıştırın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>Aşağıdakiler, yüklenemedik geçerli dağıtımların listesidir.\n'wsl.exe {} &lt;Distro&gt;' kullanarak yükleyin.\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>Dağıtım adı zaten ayarlanmış.</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>Sağlanan yükleme konumu zaten kullanılıyor.</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>Sağlanan ada sahip bir dağıtım zaten mevcut. Farklı bir ad seçmek için --name komutunu kullanın.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>Sağlanan ada sahip dağıtım yok.</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>Bir diski bağlamak için yönetici erişimi gerekiyor.</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>Dağıtım dışarı aktarılamadı.</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>Bu dağıtım sistemi için Linux için Windows Alt Sistemi dosya sisteminin bir kez yükseltilemedi...</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>Başka bir örnek yükseltilmeyen olarak çalıştığından başlatılamıyor. Yükseltilmiş ve yükseltilmemiş örneklerin aynı anda çalışmasına izin verilmez.</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>Dağıtım içeri aktarılamadı.</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>Windows isteğe bağlı bileşeni yükleniyor: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>İndiriliyor: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>Yükleniyor: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>Dağıtım içeri aktarılıyor</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} indirildi.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi, önceki bir kurulumu devam ettiriyor...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>Geçersiz dağıtım adı: '{}'.\nGeçerli dağıtımların listesini almak için 'wsl.exe --list --online' komutunu kullanın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi örneği sonlandırıldı.</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>Geçersiz komut satırı bağımsız değişkeni: {}\nDesteklenen bağımsız değişkenlerin listesini almak için lütfen '{} --help' komutunu kullanın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>{} komut satırı bağımsız değişkeni bir değer gerektiriyor.\nDesteklenen bağımsız değişkenlerin listesini almak için lütfen '{} --help' komutunu kullanın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>Desteklenmeyen konsol ayarları. Bu özelliği kullanmak için eski konsolun devre dışı olması gerekir.</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} geçerli bir tamsayı değil.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>Bu dağıtım için bir yükleme, kaldırma veya dönüştürme işlemi devam ediyor.</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>Başlatılıyor {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>Başka bir örnek yükseltilmiş olarak çalıştığından başlatılamıyor.  Yükseltilmiş ve yükseltilmemiş örneklerin aynı anda çalışmasına izin verilmez.</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi yüklü dağıtımı yok.\nAşağıdaki yönergeleri içeren bir dağıtım yükleyerek bu sorunu çözebilirsiniz:\n\nKullanılabilir dağıtımları wsl.exe --list --online' için 'wsl.exe --list --online' 'i kullanın\nve 'wsl.exe --install &lt;Distro&gt;' kullanın.</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>Çalışan dağıtım yok.</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (Varsayılan)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi Dağıtımları:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>Varsayılan Dağıtım: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>Varsayılan Sürüm: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>Kayıt siliniyor.</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>Kullanıcı bulunamadı.</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>WSL 2 ile temel farklılıklar hakkında bilgi için lütfen https://aka.ms/wsl2 adresini ziyaret edin.</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>Dönüştürme devam ediyor, bu işlem birkaç dakika sürebilir.</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>Dağıtım zaten istenen sürüm.</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>Eski dağıtım WSL 2'yi desteklemez.</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>Sanallaştırma bu makinede etkinleştirilmediği için WSL2 başlatılamıyor.\nLütfen \"Sanal Makine Platformu\" isteğe bağlı bileşeninin etkinleştirildiğinden ve sanallaştırmanın bilgisayarınızın üretici yazılımı ayarlarında açık olduğundan emin olun.\n\n\"Sanal Makine Platformu\" seçeneğini şu komutu çalıştırarak etkinleştirin: wsl.exe --install --no-distribution\n\nBilgi için lütfen şu adresi ziyaret https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>Çok fazla sanal sabit disk veya fiziksel disk bağlı.</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>VHD zaten wsl.exe --mount, aracılığıyla bağlandı, lütfen başlatmadan önce diskin bağlantısını kesin.</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>WSL1, mevcut makine yapılandırmanızla desteklenmiyor.\nLütfen WSL1'i kullanmak için \"Windows Subsystem for Linux\" isteğe bağlı bileşenini etkinleştirin.</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>Bu işlem yalnızca WSL2 tarafından desteklenir.</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>Kullanım:\n    --networking-mode\n       Geçerli ağ modunu görüntüle.\n\n    --msal-proxy-path\n        MSAL ara sunucu uygulamasının yolunu görüntüle.\n\n    --vm-id\n        WSL VM kimliğini görüntüle.\n\n    --version\n        WSL paketinin sürümünü görüntüle.\n\n    -n\n        Yeni bir satır yazdırma.</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>Kullanım:\n    -a\n        Sonucu mutlak yol biçimine zorla.\n    -u\n        Windows yolundan WSL yoluna çevirin (varsayılan).\n    -w\n        WSL yolundan Windows yoluna çevirin.\n    -m\n        WSL yolundan '\\\\' yerine '/' içeren bir Windows yoluna çevirin\n\nÖrnek: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sisteminde yönetimsel işlemler gerçekleştirir\n\nKullanım:\n    /l, /list [Seçenek]\n        Kayıtlı dağıtımları listeler.\n        /all - Şu anda yüklenmekte veya kaldırılmakta olan\n               dağıtımlar da dahil olmak üzere tüm dağıtımları listeler.\n\n        /running - Yalnızca şu anda çalışmakta olan dağıtımları listeler.\n\n    /s, /setdefault &lt;DistributionName&gt;\n        Dağıtımı varsayılan olarak ayarlar.\n\n    /t, /terminate &lt;DistributionName&gt;\n        Dağıtımı sonlandırır.\n\n    /u, /unregister &lt;DistributionName&gt;\n        Dağıtımın kaydını siler ve kök dosya sistemini siler.</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>Telif Hakkı (c) Microsoft Corporation. Tüm hakları saklıdır.\nBu ürünle ilgili gizlilik bilgileri için lütfen https://aka.ms/privacy sayfasını ziyaret edin.\n\nKullanım: wsl.exe [Bağımsız Değişken] [Seçenekler...] [KomutSatırı]\n\nLinux ikili dosyalarını çalıştırmak için bağımsız değişkenler:\n\n    Komut satırı sağlanmamışsa wsl.exe varsayılan kabuğu başlatır.\n\n    --exec, -e &lt;CommandLine&gt;\n        Varsayılan Linux kabuğunu kullanmadan belirtilen komutu yürütür.\n\n    --shell-type &lt;standard|login|none&gt;\n        Belirtilen komutu sağlanan kabuk türüyle yürütür.\n\n    --\n        Kalan komut satırını olduğu gibi geçirir.\n\nSeçenekler:\n    --cd &lt;Directory&gt;\n        Belirtilen dizini geçerli çalışma dizini olarak ayarlar.\n        ~ kullanılıyorsa Linux kullanıcı giriş yolu kullanılır. Yol\n        / karakteriyle başlarsa mutlak bir Linux yolu olarak yorumlanır.\n        Aksi takdirde değerin mutlak bir Windows yolu olması gerekir.\n\n    --distribution, -d &lt;DistroName&gt;\n        Belirtilen dağıtımı çalıştırır.\n\n    --distribution-id &lt;DistroGuid&gt;\n        Belirtilen dağıtım kimliğini çalıştırır.\n\n    --user, -u &lt;UserName&gt;\n        Belirtilen kullanıcı olarak çalıştırır.\n\n    --system\n        Sistem dağıtımı için bir kabuk başlatır.\n\nLinux için Windows Alt Sistemini yönetmeyi sağlayan bağımsız değişkenler:\n\n    --help\n        Kullanım bilgilerini görüntüler.\n\n    --debug-shell\n        Tanılama amacıyla bir WSL2 hata ayıklama kabuğu açar.\n\n    --install [Dağıtım] [Seçenekler...]\n        Linux için Windows Alt Sistemi dağıtımı yükler.\n        Geçerli dağıtımların listesi için 'wsl.exe --list --online' kullanın.\n\n        Seçenekler:\n            --enable-wsl1\n                WSL1 desteğini etkinleştirir.\n\n            --fixed-vhd\n                Dağıtımı depolamak için sabit boyutlu bir disk oluşturun.\n\n            --from-file &lt;Path&gt;\n                Yerel dosyadan bir dağıtım yükler.\n\n            --legacy\n                Eski dağıtım bildirimini kullanır.\n\n            --location &lt;Location&gt;\n                Dağıtımın yükleme yolunu ayarlar.\n\n            --name &lt;Name&gt;\n                Dağıtımın adını ayarlar.\n\n            --no-distribution\n                Yalnızca gerekli isteğe bağlı bileşenleri yükler, dağıtımı yüklemez.\n\n            --no-launch, -n\n                Yüklemeden sonra dağıtımı başlatmaz.\n\n            --version &lt;Version&gt;\n                Yeni dağıtım için kullanılacak sürümü belirtir.\n\n            --vhd-size &lt;MemoryString&gt;\n                Dağıtımın depolanacağı diskin boyutunu belirtir.\n\n            --web-download\n                Dağıtımı Microsoft Store yerine internetten indirir.\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        Dağıtıma özgü seçenekleri değiştirir.\n\n        Seçenekler:\n            --move &lt;Location&gt;\n                Dağıtımı yeni bir konuma taşır.\n\n            --set-sparse, -s &lt;true|false&gt;\n                Dağıtım VHD'inin aralıklı olacak şekilde ayarlar ve böylece disk alanının otomatik olarak geri kazanılmasını sağlar.\n\n            --set-default-user &lt;Username&gt;\n                Dağıtımın varsayılan kullanıcısını ayarlar.\n\n            --resize &lt;MemoryString&gt;\n                Dağıtımın diskini belirtilen boyuta yeniden boyutlandırır.\n\n    --mount &lt;Disk&gt;\n        Tüm WSL 2 dağıtımlarında fiziksel veya sanal diski ekler ve bağlar.\n\n        Seçenekler:\n            --vhd\n                &lt;Disk&gt;'in sanal bir sabit diske başvurduğunu belirtir.\n\n            --bare\n                Diski WSL2'ye ekler, ancak bağlamaz.\n\n            --name &lt;Name&gt;\n                Bağlama noktası için özel bir ad kullanarak diski bağlar.\n\n            --type &lt;Type&gt;\n                Bir diski takarken dosya sistemi kullanılır, belirtilmemişse varsayılan ext4 olur.\n\n            --options &lt;Options&gt;\n                Ek bağlama seçenekleri.\n\n            --partition &lt;Index&gt;\n                Bağlanacak bölümün dizini, belirtilmemişse varsayılan olarak tüm diske ayarlanır.\n\n    --set-default-version &lt;Version&gt;\n        Yeni dağıtımlar için varsayılan yükleme sürümünü değiştirir.\n\n    --shutdown\n        Çalışan tüm dağıtımları ve WSL 2\n        basit yardımcı yazılım sanal makinesini anında sonlandırır.\n\n        Seçenekler:\n            --force\n                Devam eden bir işlem olsa bile WSL 2 sanal makinesini sonlandırın. Veri kaybına neden olabilir.\n\n    --status\n        Linux için Windows Alt Sistemi'nin durumunu gösterir.\n\n    --unmount [Disk]\n        Bir diskin tüm WSL2 dağıtımlarından bağlantısını keser ve ayırır.\n        Bağımsız değişken olmadan çağrılırsa tüm disklerin bağlantısını keser ve ayırır.\n\n    --uninstall\n        Bu makineden Linux için Windows Alt Sistemi paketini kaldırır.\n\n    --update\n        Linux için Windows Alt Sistemi'nin durumunu güncelleştirir.\n\n        Seçenekler:\n            --pre-release\n                Varsa bir yayın öncesi sürümü indirir.\n\n    --version, -v\n        Sürüm bilgilerini görüntüler.\n\nLinux için Windows Alt Sistemi'nde dağıtımı yönetmek için bağımsız değişkenler:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [Seçenekler]\n        Dağıtımı bir tar dosyasına dışarı aktarır.\n        Dosya adı - for stdout olabilir.\n\n        Seçenekler:\n            --format &lt;Format&gt;\n                Dışarı aktarma biçimini belirtir. Desteklenen değerler: tar, tar.gz, vhd.\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Seçenekler]\n        Belirtilen tar dosyasını yeni bir dağıtım olarak içeri aktarır.\n        Dosya adı - for stdin olabilir.\n\n        Seçenekler:\n            --version &lt;Version&gt;\n                Yeni dağıtım için kullanılacak sürümü belirtir.\n\n            --vhd\n                Sağlanan dosyanın tar dosyası değil, .vhd veya .vhdx dosyası olduğunu belirtir.\n                Bu işlem, belirtilen yükleme konumunda VHD dosyasının bir kopyasını oluşturur.\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        Belirtilen VHD dosyasını yeni bir dağıtım olarak içeri aktarır.\n        Bu sanal sabit diskin ext4 dosya sistemi türüyle biçimlendirilmesi gerekir.\n\n    --list, -l [Seçenekler]\n        Dağıtımları listeler.\n\n        Seçenekler:\n            --all\n                Şu anda yüklenmekte veya kaldırılmakta olan\n                dağıtımlar da dahil olmak üzere tüm dağıtımları listeler.\n\n            --running\n                Yalnızca şu anda çalışmakta olan dağıtımları listeler.\n\n            --quiet, -q\n                Yalnızca dağıtım adlarını gösterir.\n\n            --verbose, -v\n                Tüm dağıtımlar hakkında ayrıntılı bilgi gösterir.\n\n            --online, -o\n                'wsl.exe --install' ile yüklenebilecek kullanılabilir dağıtımların listesini görüntüler.\n\n    --set-default, -s &lt;Distro&gt;\n        Dağıtımı varsayılan olarak ayarlar.\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        Belirtilen dağıtımın sürümünü değiştirir.\n\n    --terminate, -t &lt;Distro&gt;\n        Belirtilen dağıtımı sonlandırır.\n\n    --unregister &lt;Distro&gt;\n        Dağıtımın kaydını siler ve kök dosya sistemini siler.</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL sürümü: {}\nÇekirdek sürümü: {}\nWSLg sürümü: {}\nMSRDC sürümü: {}\nDirect3D sürümü: {}\nDXCore sürümü: {}\nWindows sürümü: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild sürümü: {}\nİşleme: {}\nDerleme zamanı: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>{} içinde belirtilen özel çekirdek bulunamadı: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>{} içinde özel çekirdek modülleri VHD'si bulunamadı: '{}'.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>{} içinde belirtilen özel sistem dağıtımı bulunamadı veya doğru biçimde değil.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>Telif Hakkı (c) Microsoft Corporation. Tüm hakları saklıdır.\nBu ürünle ilgili gizlilik bilgileri için lütfen https://aka.ms/privacy sayfasını ziyaret edin.\n\nKullanım: wslg.exe [Argument] [Options...] [CommandLine]\n\nBağımsız değişkenler:\n    --cd &lt;Directory&gt;\n        Belirtilen dizini geçerli çalışma dizini olarak ayarlar.\n        ~ kullanılıyorsa, Linux kullanıcı giriş yolu kullanılır. Yol\n        / karakteriyle başlarsa, mutlak bir Linux yolu olarak yorumlanır.\n        Aksi takdirde değerin mutlak bir Windows yolu olması gerekir.\n\n    --distribution, -d &lt;Distro&gt;\n        Belirtilen dağıtımı çalıştırma.\n\n    --user, -u &lt;UserName&gt;\n        Belirtilen kullanıcı olarak çalıştırır.\n\n    --shell-type &lt;standard|login|none&gt;\n        Belirtilen komutu sağlanan kabuk türüyle yürütür.\n\n    --help\n        Kullanım bilgilerini görüntüler.\n\n    --\n        Kalan komut satırını olduğu gibi geçirir.</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>Devam etmek için herhangi bir tuşa basın...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>{} bağımsız değişkeninde gerekli bir parametre eksik.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>Dışarı aktarma devam ediyor, bu işlem birkaç dakika sürebilir.</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>İçeri aktarma devam ediyor, bu işlem birkaç dakika sürebilir.</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>GUI uygulama desteği {} veya /etc/wsl.conf aracılığıyla devre dışı bırakıldı.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>Güncelleştirmeler denetleniyor.</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>Bu dağıtım yalnızca Microsoft Store’dan kullanılabilir.</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>arm64 wsl.exe --mount , Windows sürüm 27653 veya daha yenisini gerektirir.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi'nin en son sürümü zaten yüklü.</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi şu sürüme güncelleniyor: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>Bu uygulama isteğe bağlı Linux için Windows Alt Sistemi gerektirir.\nŞu işlemi çalıştırarak yükleyin: wsl.exe --install --no-distribution\nDeğişikliklerin etkinleşmesi için sistemin yeniden başlatılması gerekebilir.</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>Belirtilen dosyada {} dosya uzantısı olmalıdır.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>Belirtilen dosyada {} veya {} dosya uzantısı olmalıdır.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>'{}' VmSwitch bulunamadı. Uygun anahtarlar: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>Bağlantılı ağ kuralları wsl2.vmSwitch anahtarının ayarlanmasını gerektirir.</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>'{}' konumundan dağıtım listesi getirilemedi. {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>'{}' diski WSL2'ye eklenemedi: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>Disk yeniden boyutlandırılamadı.</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>Değer bulunamadı.</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>{} Windows sürümü, paketlenmiş Linux için Windows Alt Sistemi sürümünü desteklemiyor.\nGerekli güncellemeyi Windows Update üzerinden veya şu adresten yükleyin: {}\nDaha fazla bilgi için lütfen https://aka.ms/wslinstall adresini ziyaret edin.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>Hata ayıklama kabuğu çalıştırmak için yönetici wsl.exe çalıştırmanız gerekir.</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>'{}' dağıtımı için yükleme işlemi şu çıkış koduyla başarısız oldu: {}.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>Geçersiz GUID biçimi: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>{}:{} içinde geçersiz bölüm adı</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>{}:{} içinde geçersiz anahtar adı</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>{}:{} içinde {} bekleniyor</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>{}:{} içinde geçersiz kaçış karakteri: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>{}:{} içinde bilinmeyen anahtar: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>{}:{} içinde '{}' anahtarı için '{}' tamsayı değeri geçersiz</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>{}:{} içinde '{}' anahtarı için geçersiz '{}' IP değeri</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>{}:{} içinde '{}' anahtarı için geçersiz '{}' boole değeri</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>{}:{} içinde '{}' anahtarı için '{}' mac adresi geçersiz</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>{} yapılandırma dosyası açılamadı, {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>WSL başlatılırken hatalar oluştu</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>'{}' için sistemli kullanıcı oturumu başlatılamadı. Daha fazla ayrıntı için journalctl'ye bakın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>EventViewer'ı aç</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>Bu dağıtım varsayılan bir ad içermiyor. Dağıtım adını seçmek için --name seçeneğini kullanın.</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>{} ve {} bağımsız değişkenleri aynı anda belirtilemez.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>{} bağımsız değişkeni {} bağımsız değişkenini gerektirir.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>Geçersiz dağıtım adı: \"{}\".</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>Eski dağıtım kaydı kullanılıyor. Bunun yerine tar tabanlı bir dağıtım kullanmayı düşünün.</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>Eski dağıtım kayıtları bağımsız değişken --version desteklemez.</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>\"{}\" dağıtımı bir geçersiz kılma bildirimi tarafından sağlandı.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>Dağıtım karması eşleşmiyor. Beklenen: {}, gerçek karma: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>Geçersiz onaltılık dize: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>Eski dağıtımlar yüklenirken '{}' desteklenmiyor.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>Dağıtım başarıyla yüklendi. 'wsl.exe -d {}' ile başlatılabilir</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>{}:{} içinde '{}' .wslconfig için geçersiz '{}' bellek dizesi</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>'{}' içinde takas diski oluşturulamadı: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>İç içe sanallaştırma bu makinede desteklenmiyor.</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>Ağ uç noktası şu adresle oluşturulamadı: '{}', atanan yeni adres: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>Adres aralığı '{}' olan sanal ağ oluşturulamadı, şu aralıkla yeni ağ oluşturuldu: '{}', {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>GÜVENLİ MOD ETKİN: birçok özellik devre dışı bırakılır</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>Yansıtılmış ağ modu desteklenmiyor: {}.\nNAT ağ bağlantısına geri dönülüyor.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>Linux Çekirdeği sürüm 5.10 veya daha yenisi gerekiyor</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows sürümü {}. {} gerekli özelliklere sahip değil</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Hyper-V güvenlik duvarı desteklenmiyor</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>DNS Tüneli desteklenmiyor</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>Yansıtılmış ağ modu kullanılırken wsl2.localhostForwarding ayarının etkisi yoktur</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>Konakta bir Http Proxy değişikliği algılandı. Değişikliği uygulamak için lütfen WSL'yi yeniden başlatın.</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>Bir localhost proxy yapılandırması algılandı ancak WSL'ye yansıtılmadı. NAT modundaki WSL, localhost proxy'lerini desteklemez.</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Bir IPv6 proxy yapılandırması algılandı ancak WSL'ye yansıtılmadı. NAT modunda WSL, IPv6'yı desteklemez.</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>Bir localhost IPv6 proxy yapılandırması algılandı ancak WSL'ye yansıtılmadı. WSL, localhost IPv6 proxy'lerini desteklemez.</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>Proxy ayarları çözümlanmaya çalışılırken beklenmeyen bir hata oluştu, proxy ayarları WSL'ye yansıtılamadı.</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>Kayıt defterine erişilirken bir hata oluştu. Yol: '{}'. Hata: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>localhost geçiş işlemi başlatılamadı. Hata: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>Sanal ağ başlatılamadı - lütfen şu komutu çalıştırarak isteğe bağlı Sanal Makine Platformu bileşenini yükleyin: wsl.exe --install --no-distribution</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>Ağ yapılandırılamadı (networkingMode {}), networkingMode {} durumuna geri dönüüyor.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>'{}' Windows bileşeni etkinleştirilemedi (çıkış kodu {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>'{}' eklentisi tarafından önemli bir hata döndürüldü</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>'{}' eklentisi tarafından önemli bir hata döndürüldü. Hata iletisi: '{}'</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>'{}' eklentisi daha yeni bir WSL sürümü gerektiriyor. Lütfen şu komutu çalıştırın: wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>'{}' .wslconfig ayarı bilgisayar ilkesi tarafından devre dışı bırakıldı.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>Hata ayıklama kabuğu, bilgisayar ilkesi tarafından devre dışı bırakıldı.</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>wsl.exe --mount bilgisayar ilkesi tarafından devre dışı bırakıldı.</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>WSL1, bilgisayar ilkesi tarafından devre dışı bırakıldı.</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>WSL2'ye yükseltmek için lütfen 'wsl.exe --set-version {} 2' çalıştırın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL bir yükseltme işlemini bitiriyor...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>Güncelleştirme başarısız (çıkış kodu: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>Kaldırma başarısız oldu (çıkış kodu: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>Günlük dosyası: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>MSIX paketi kaldırılamadı (hata: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>MSIX paketi yüklenemedi (hata: {}).</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>WSL'yi çalıştırmak için gereken isteğe bağlı bileşenler yüklü değil.</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>Eksik bileşenleri yükleyin.</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>İçeri aktarılan dosya geçerli bir Linux dağıtımı değil.</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>Çıkmak için herhangi bir tuşa basın...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>Yeni bir Linux için Windows Alt Sistemi sürümü var.</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>{} sürümüne güncelleştirin veya sürüm notlarını aşağıda görüntüleyin.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>Güncelleştir</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>Belgeleri Göster</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>İşlem tamamlanamadı çünkü VHD şu anda kullanımda. WSL'yi durmaya zorlamak için şu komutu kullanın: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} geçerli bir boole değil, &lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>Seyrek VHD yalnızca WSL2'de desteklenir.</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>WSL'nin yerel sistem olarak çalıştırılması desteklenmez.</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>Performans İpuçları:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>Windows sürücüleriniz üzerinde {} gibi yoğun G/Ç kullanımı düşük performansa sahip olur. Daha iyi performans için proje dosyalarını Linux dosya sistemine taşımayı düşünün. Daha fazla bilgi için aşağıya tıklayın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>Belgeleri Görüntüleyin</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>Tekrar Gösterme</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>WSL2 Sanal Makinesi çöktü.</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>Yığın izlemesi şu konuma kaydedildi: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>Dağıtım başlatılamadı. Hata kodu: {}, hata adımı: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>Dağıtım, sanal diski bozuk olduğundan başlatılamadı.</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>{}:{} içinde yinelenen '{}' yapılandırma anahtarı ({}:{} içinde çakışan anahtar: '{}')</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>{}:{} içinde '{}' yapılandırma anahtarı için geçersiz '{}' değeri (Geçerli değerler: {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>\"{}\" dağıtımı için OOBE komutunun tamamlanması ...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>{} dağıtımından '{}' özelliği okunamadı</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>Bu bir VHD dosyası gibi görünüyor. Tar yerine VHD dosyasını içeri aktarmak için --vhd kullanın.</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>Microsoft Store'dan {} yüklenemedi: {}\nWeb'den indirme deneniyor...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>Varsayılan dağıtım yapılandırılmadı. Lütfen yüklenecek bir dağıtım girin.</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>wsl2.virtio9p devre dışı bırakıldı, vsock aktarımıyla 9p'ye geri dönülüyor.</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>WSL yüklemesi bozulmuş gibi görünüyor (Hata kodu: {}).\nWSL'yi onarmak için herhangi bir tuşa basın veya CTRL-C tuşlarına basın.\nBu istem 60 saniye içinde zaman aşımına uğradı.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>Dağıtım kaydedilirken terminal profili ayrıştırılamadı: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>Geçersiz boyut: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\nHata kodu: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>Geçersiz JSON belgesi. Ayrıştırma hatası: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors, sistemdeki mantıksal işlemci sayısını ({} veya {}) &gt; aşamıyor</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>Ağ modu sorgulanamadı</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>Özelleştirilmiş çekirdek modülleri özelleştirilmiş bir çekirdek belirtilmeden sağlandı. Daha fazla bilgi için lütfen https://aka.ms/wslcustomkernel adresine bakın.</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>Küresel Güvenli Erişim İstemcisiyle ilgili mevcut bir uyumluluk sorunu nedeniyle DNS Tünelleme devre dışı bırakıldı.</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>Seyrek VHD desteği olası veri bozulması nedeniyle şu anda devre dışı bırakılmıştır.\nBir dağıtımı seyrek VHD kullanmaya zorlamak için lütfen şu komutu çalıştırın:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>{} bağlanamadı, daha fazla ayrıntı için dmesg komutuna bakın.</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>/etc/fstab with mount -a işlenemedi.</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>Dağıtım diski bağlanırken bir hata oluştu, yedek plan olarak salt okunur şekilde bağlandı.\nKurtarma yönergelerine bakın: https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>‘{}’ çevrilemedi</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>Bir sorun oluştu. Daha sonra yeniden deneyin.</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>Hakkında</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi Ayarları hakkında</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft. Tüm hakları saklıdır.</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi Ayarları</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi Ayarları, geliştiricilerin gui tabanlı .wslconfig kullanarak bu dosyayı yönetmesini sağlar.</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>Otomatik bellek geri kazanma</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>Boşta CPU kullanımı algılandıktan sonra önbelleğe alınan belleği otomatik olarak serbest bırakır. Yavaş serbest bırakma için kademeli olarak ve önbelleğe alınan belleğin anında serbest bırakılması için dropcache olarak ayarlayın.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Otomatik bellek geri kazanma.</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Boşta CPU kullanımı algılandıktan sonra önbelleğe alınan belleği otomatik olarak serbest bırakır. Yavaş serbest bırakma için kademeli olarak ve önbelleğe alınan belleğin anında serbest bırakılması için dropcache olarak ayarlayın.</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>Otomatik Ara Sunucu etkin</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>WSL'nin Windows'un HTTP ara sunucusu bilgilerini kullanmasına olanak verir.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Otomatik Ara Sunucu etkin.</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL'nin Windows'un HTTP ara sunucusu bilgilerini kullanmasına olanak verir.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>DNS ayrıştırma için en iyi çabayı kullan</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>Yalnızca wsl2.dnsTunneling true olarak ayarlandığında geçerlidir. True olarak ayarlandığında, Windows soruyu DNS isteğinden çıkarır ve bilinmeyen kayıtları yok sayarak çözmeye çalışır.</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS ayrıştırma için en iyi girişimi kullanın.</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Yalnızca wsl2.dnsTunneling true olarak ayarlandığında geçerlidir. True olarak ayarlandığında, Windows soruyu DNS isteğinden çıkarır ve bilinmeyen kayıtları yok sayarak çözmeye çalışır.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>Özel çekirdek</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>Özel bir Linux çekirdeğinin mutlak Windows yolu.</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Çekirdeklere gözat</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Özel çekirdek</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Özel bir Linux çekirdeğinin mutlak Windows yolu.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>Özel çekirdek modülleri</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>Özel bir Linux çekirdek modülü VHD'sine giden mutlak bir Windows yolu.</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Çekirdek modüllerine göz at</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Özel çekirdek modülleri</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Özel bir Linux çekirdek modülü VHD'sine giden mutlak bir Windows yolu.</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>Özel sistem dağıtımı</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Dağıtımlara gözat</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>Öncelikle WSL'deki GUI uygulamalarına güç sağlamak için kullanılan özel bir sistem dağıtımı olarak yüklenecek bir VHD'ye giden yolu belirtin. [Buradan sistem dağıtımları hakkında daha fazla bilgi edinin].</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Özel sistem dağıtımı</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Öncelikli olarak WSL'deki GUI uygulamalarına güç sağlamak için kullanılan özel bir sistem dağıtımı şeklinde yüklenecek VHD'ye giden yolu belirtin.</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>Hata ayıklama konsolunu etkinleştir</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>WSL 2 dağıtım örneği başlatıldığında dmesg içeriğini gösteren bir konsol penceresi çıktısını açan boole değeri.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hata ayıklama konsolunu etkinleştirin.</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 dağıtım örneği başlatıldığında tanılama iletilerinin içeriğini gösteren bir konsol penceresi çıktısını açan Boole değeri.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>Varsayılan VHD Boyutu</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>Yalnızca yeni oluşturulan dağıtımlar için genişletilebilir WSL sanal sabit diski (VHD) için varsayılan en büyük boyut.</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Boyutu sıfırla</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Varsayılan VHD Boyutu</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Yalnızca yeni oluşturulan dağıtımlar için genişletilebilir WSL sanal sabit diski (VHD) için megabayt olarak belirtilen varsayılan en büyük boyut.</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>Geliştirici</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Belgeler</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>DrvFS modunu değiştir</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>WSL'de işletim sistemleri arası dosya erişimi uygulamasını değiştirir.</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>DNS Ara Sunucusu etkin</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>Sadece wsl2.networkingMode NAT olarak ayarlandığında uygulanabilir. WSL'ye Linux'taki DNS Sunucusunu ana bilgisayardaki NAT'a göre yapılandırmasını bildiren boole değeri. False olarak ayarlandığında Windows'tan Linux'a DNS sunucuları yansıtılır.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS Ara Sunucusu etkin.</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Sadece wsl2.networkingMode NAT olarak ayarlandığında uygulanabilir. WSL'ye Linux'taki DNS Sunucusunu ana bilgisayardaki NAT'a göre yapılandırmasını bildiren boole değeri. False olarak ayarlandığında Windows'tan Linux'a DNS sunucuları yansıtılır.</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>DNS Tüneli etkin</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>DNS isteklerinin WSL'den Windows'a ara sunucudan aktarılma şeklini değiştirir.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>DNS Tüneli etkin.</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>DNS isteklerinin WSL'den Windows'a ara sunucudan aktarılma şeklini değiştirir.</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>Dosya Sistemi</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>GUI uygulamalarını etkinleştir</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>WSL'de GUI uygulamaları ([WSLg]) için desteği açmak veya kapatmak için Boole değeri.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>GUI uygulamalarını etkinleştirin.</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL'de GUI uygulamaları (WSL g olarak bilinir) için desteği açmak veya kapatmak için Boolean.</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>Donanım performans sayaçlarını etkinleştir</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>Linux için donanım performans sayaçlarını etkinleştirir.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Donanım performans sayaçlarını etkinleştirin.</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Linux için donanım performans sayaçlarını etkinleştirir.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>Hyper-V Güvenlik Duvarı etkin</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>WSL ağ trafiğini filtrelemek için Windows Güvenlik Duvarı kurallarının yanı sıra Hyper-V trafiğine özel kurallara izin veren Hyper-V güvenlik duvarını sağlar.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Hyper-V Güvenlik Duvarı etkin.</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL ağ trafiğini filtrelemek için Windows Güvenlik Duvarı kurallarının yanı sıra Hyper-V trafiğine özel kurallara izin veren Hyper-V güvenlik duvarını sağlar.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>Ana Bilgisayar Adresi Geri Döngüsü</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>Yalnızca wsl2.networkingMode olarak ayarlandığında uygulanabilir. True olarak ayarlandığında, Kapsayıcının Ana Bilgisayara veya Konak'a atanmış bir IP adresiyle Kapsayıcıya bağlanmasına izin ver. 127.0.0.1 geri döngü adresinin her zaman kullanabildiğine dikkat edin. Bu seçenek, ek olarak atanmış tüm yerel IP adreslerinin de kullanılabilir olduğunu unutmayın.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ana Bilgisayar Adresi Geri Döngüsü.</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Sadece wsl2.networkingMode yansıtılmış olarak ayarlandığında uygulanabilir. True olarak ayarlandığında, Ana Bilgisayar'a atanmış bir IP adresi aracılığıyla Kapsayıcı'nın Ana Bilgisayar'a veya Ana Bilgisayar'ın Kapsayıcı'ya bağlanmasına izin verir. 127.0.0.1 geri döngü adresinin her zaman kullanılabileceğini unutmayın. Bu seçenek, ek olarak atanan tüm yerel IP adreslerinin kullanılmasına da olanak verir.</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>Yoksayılan bağlantı noktaları</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>Yalnızca wsl2.networkingMode olarak ayarlandığında uygulanabilir. Linux uygulamalarının otomatik olarak iletilen veya Windows'da değerlendirilen bağlantı noktalarına bağlanacak bağlantı noktalarını belirtir. Virgülle ayrılmış bir listede biçimlendirilmelidir, örneğin: 3000.9000.9090.</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>Bağlantı noktalarını sıfırla</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Yoksayılan bağlantı noktaları</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Yalnızca wsl2.networkingMode yansıtılmış olarak ayarlandığında uygulanabilir. Linux uygulamalarının otomatik olarak iletilen veya Windows'ta değerlendirilen bağlantı noktalarına bağlanacak bağlantı noktalarını belirtir. Virgülle ayrılmış bir listede biçimlendirilmelidir, örneğin, 3000, 9000, 9090.</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>İlk Otomatik Ara Sunucu zaman aşımı</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>Yalnızca wsl2.autoProxy true olarak ayarlandığında geçerlidir. WSL kapsayıcısını başlatırken WSL'nin HTTP ara sunucusu bilgilerini almak için beklediği süreyi (milisaniye cinsinden) yapılandırır. Ara sunucu ayarları bu süreden sonra çözülürse, alınan ara sunucu ayarlarını kullanmak için WSL örneğinin yeniden başlatılması gerekir.</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Zaman aşımını sıfırla</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>İlk Otomatik Ara Sunucu zaman aşımı</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Yalnızca wsl2.autoProxy true olarak ayarlandığında geçerlidir. WSL kapsayıcısını başlatırken WSL'nin HTTP ara sunucusu bilgilerini almak için beklediği süreyi (milisaniye cinsinden) yapılandırır. Ara sunucu ayarları bu süreden sonra çözülürse, alınan ara sunucu ayarlarını kullanmak için WSL örneğinin yeniden başlatılması gerekir.</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>Çekirdek Komut Satırı</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>Ek çekirdek komut satırı bağımsız değişkenleri.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>Localhost iletmeyi etkinleştir</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM'de wildcard veya localhost'a bağlı bağlantı noktalarının localhost:port üzerinden ana bilgisayardan bağlanıp bağlanamayacağını belirten boole değeri.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>localhost iletmeyi etkinleştirin.</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM'de wildcard veya localhost'a bağlı bağlantı noktalarının localhost iki nokta üst üste port üzerinden ana bilgisayardan bağlanıp bağlanamayacağını belirten Boole değeri.</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>Bellek ve işlemci</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>Bellek Boyutu</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM'ye atanacak bellek miktarı.</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Boyutu sıfırla</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Bellek Boyutu</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM'sine atanacak, megabayt cinsinden bellek.</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} milisaniye</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>İç içe sanallaştırmayı etkinleştir</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>İç içe sanallaştırmayı açmak veya kapatmak için Boole değeri, diğer iç içe VM'lerin WSL 2 içinde çalışmasına olanak sağlar.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>İç içe sanallaştırmayı etkinleştirin.</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>İç içe sanallaştırmayı açıp kapatmaya yarayan Boole değeri, diğer iç içe VM'lerin WSL 2'nin içinde çalışmasına olanak verir.</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>Ağ modu</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>WSL için ağ modunu belirtir.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Ağ modu.</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL için ağ modunu belirtir.</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>Ağ İletişimi</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>Tüm dosyalarınızda işletim sistemleri arasında çalışabilirsiniz.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>İşletim Sistemleri Arası Dosya Erişimi</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>Windows dosyalarınıza Linux'tan erişme</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>Windows dosyalarınıza Linux içinden '/mnt' ve ardından bu örnekteki C sürücüsü gibi Windows sürücü harfinize giderek erişebilirsiniz:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>Linux dosyalarınıza Dosya Gezgini ile erişme</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>Linux dosyalarınızı Dosya Gezgini'nden '\\\\wsl.localhost\\' konumuna giderek veya 'Linux' simgesine tıklayarak görüntüleyebilirsiniz.</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>Windows dosyalarını ve programlarını WSL'den başlatma</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>WSL kullanırken bile Windows çalıştırılabilir dosyalarınızı doğrudan bash'ten çalıştırabilirsiniz. Dosya Gezgini’ni geçerli klasörünüzde açmak için\n'powershell.exe /c start .' komutunu çalıştırmayı deneyin.</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>Linux Ağ Uygulamalarına Windows'tan Erişme</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>Linux dağıtımınızda bir ağ uygulaması (örneğin, NodeJS veya SQL sunucusunda çalışan bir uygulama) oluşturuyorsanız, localhost'u (normalde yaptığınız gibi) kullanarak bu uygulamaya bir Windows uygulamasından (Edge veya Chrome internet tarayıcınız gibi) erişebilirsiniz. Bu, 3000 numaralı bağlantı noktasını dinleyen bir Linux sunucusu başlattıysanız, ona erişmek için Windows'ta Edge'de [http://localhost:3000] sayfasına gidebileceğiniz anlamına gelir.</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>Yansıtılmış Mod Ağı</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL ayrıca, IPv6 desteği ve yerel alan ağınızdaki ağ uygulamalarınıza erişme yeteneği gibi gelişmiş özellikler ekleyen, yansıtılmış mod adı verilen yeni bir ağ modunu da içerir.</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>İşletim Sistemleri Arası Dosya Erişimi Hakkında Daha Fazla Bilgi Edinin</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi'ne hoş geldiniz</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL, farklı Linux dağıtımlarını denemenin harika bir yoludur.</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>Dağıtım Yönetimi</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>Temel WSL komutları hakkında daha fazla bilgi edinin</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>Herhangi bir Linux dağıtımını içeri aktarma hakkında daha fazla bilgi edinin</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Yüklenebilir WSL Dağıtımlarını Listele Komutu</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Adlandırılmış bir WSL Dağıtım Komutu Yükleme</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>Kullanılabilir WSL Dağıtımlarını Listele Komutu</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Masaüstü, Linux kapsayıcılarıyla geliştirmenize yardımcı olmak için WSL ile harika çalışır.\n\nDocker Masaüstü'nü WSL ile kullanmanın avantajlarından bazıları şunlardır:\n\n• Docker komutlarını WSL'de veya Windows'da aynı Docker daemon'ını ve resimlerini kullanarak çalıştırabilirsiniz.\n• WSL'de Windows sürücülerinin otomatik olarak bağlanmasını kullanarak Windows ve Linux arasında dosya ve klasörleri sorunsuz bir şekilde paylaşabilirsiniz.\n• WSL'nin birlikte çalışabilirliği sayesinde, Linux kodu ve dosyaları üzerinde çalışmak için tercih ettiğiniz Windows araçlarını ve düzenleyicilerini kullanabilir veya tam tersini yapabilirsiniz.</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker Masaüstü Tümleştirmesi</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>WSL'yi Docker ile Kullanma Hakkında Daha Fazla Bilgi Edinin</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi (WSL), sık kullandığınız Linux araçlarınızı, yardımcı programlarınızı, uygulamalarınızı ve iş akışlarınızı doğrudan Windows üzerinde çalıştırmanıza olanak sağlar.\n\nTopluluğun sık kullanılan özelliklerinden bazılarını önizlemek veya kapsamlı belgelerimizi görüntülemek için birkaç dakikanızı ayırın.</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>WSL’ye Hoş Geldiniz</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>Kurulum için En İyi Yöntemler</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Linux'a Başlarken</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi (WSL) Belgeleri</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>Grafik tabanlı Linux uygulamalarını alt-sekme, başlat menüsünü başlatma, görev çubuğu sabitleme ve kes-yapıştır desteği gibi yerel Windows etkileşimleriyle kullanabilirsiniz.</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>GUI Uygulamaları</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL, Makine Öğrenimi iş akışları için Windows GPU'nuzdan yararlanabilir\n\nWSL'de çalışan Linux ikili dosyaları, performanslarını hızlandırmak için Windows'taki GPU'nuzu otomatik olarak kullanabilir. Bu iş akışlarını normal bir Linux makinesindekiyle aynı şekilde yüklemeniz ve çalıştırmanız yeterlidir. NVIDIA grafik kartınız varsa CUDA'yı docker kapsayıcısında çalıştırmaktan, AMD, Intel veya NVIDIA grafik kartınızda PyTorch veya TensorFlow'u DirectML ile çalıştırmaya kadar başlamanın birçok farklı yolu vardır. Daha fazla bilgi edinmek için aşağıda başlarken kılavuzumuza bakın.</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>GPU Hızlandırma</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>GPU Hızlandırma hakkında daha fazla bilgi edinin</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>WSL GUI Uygulamaları Hakkında Daha Fazla Bilgi Edinin</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>Denenecek uygulamaların listesini aşağıda bulabilirsiniz (Bunların hepsini Ubuntu'ya 'sudo apt install &lt;The App Name&gt;' yazarak yükleyebilirsiniz)\n\n    • gedit: Temel metin düzenleyici\n    • audacity: Ses dosyalarını kaydetme ve düzenleme\n    • blender: 3B animasyonlar ve görselleştirmeler yapma\n    • gimp: Fotoğrafları düzenleme\n    • nautilus: Linux dosya gezgini\n    • vlc: Video oynatıcı</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>Windows ve Linux işletim sistemlerindeki ağ uygulamalarına kolayca erişebilirsiniz.</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>Ağ Tümleştirmesi</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>Ağ Uygulamaları Hakkında Daha Fazla Bilgi Edinin</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>Yansıtılmış Mod Ağı Hakkında Daha Fazla Bilgi Edinin</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>WSL'yi tam zamanlı geliştirme ortamınız olarak doğrudan VS Code'dan kullanabilirsiniz.</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS Code Tümleştirmesi</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>WSL'yi VS Code ile Kullanma Hakkında Daha Fazla Bilgi Edinin</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>Yükleme</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>VS Code'u yükledikten sonra Remote WSL uzantısını Windows Terminal'dan yükleyebilirsiniz:\n\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>Visual Studio Code'da bir WSL Projesi Açma</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>WSL dağıtımınızdan VS Code'da bir proje açmak için dağıtımın komut satırını açın ve bir proje dosyası açmak için 'code .' komutunu çalıştırın.\n\nAyrıca VS Code'un içindeki komut paleti aracılığıyla daha fazla VS Code Remote seçeneğine de erişebilirsiniz. Komut paletini açmak için klavyenizdeki “SHIFT+CTRL+P” tuşlarına basın ve kullanılabilir VS Code Remote seçeneklerinin listesini görmek için ‘Remote-WSL’ yazın. Bu işlem, uzak bir oturumda klasörü yeniden açmanıza, hangi dağıtımı açmak istediğinizi belirtmenize ve daha fazlasına olanak sağlar.</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>Kullanma</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>İsteğe Bağlı Özellikler</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>Gizlilik Bildirimi</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>İşlemci Sayısı</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM'ye atanacak mantıksal işlemci sayısı.</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>Sayımı sıfırla</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>İşlemci Sayısı</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM'ye atanacak mantıksal işlemci sayısı.</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>İlgili bağlantılar</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>Sürüm notları</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>Güvenli modu etkinleştir</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>WSL'yi birçok özelliği devre dışı bırakan ve kötü durumdaki dağıtımları kurtarmak için kullanılması amaçlanan \"Güvenli Mod \"da çalıştırın.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Güvenli modu etkinleştirin.</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL'yi birçok özelliği devre dışı bırakan ve kötü durumdaki dağıtımları kurtarmak için kullanılması amaçlanan \"Güvenli Mod \"da çalıştırın.</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>Hakkında</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>Geliştirici</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>Dağıtım Yönetimi</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Docker Masaüstü Tümleştirmesi</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>Dosya Sistemi</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>Genel</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>GPU Hızlandırma</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>GUI Uygulamaları</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>wsl.exe başlat</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>Bellek ve işlemci</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>Ağ İletişimi</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>Ağ Tümleştirmesi</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>WSL’ye Hoş Geldiniz</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>İsteğe Bağlı Özellikler</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>Ayarlar</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS Code Tümleştirmesi</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>Yenilikler</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>Dosya Sistemlerinde Çalışma</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>Sorunlar</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>Seyrek VHD'yi varsayılan olarak etkinleştir</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>Yeni oluşturulan tüm sanal sabit diskler, etkinleştirildiğinde otomatik olarak seyrek olarak ayarlanır.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Seyrek VHD'yi varsayılan olarak etkinleştirin.</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Yeni oluşturulan tüm sanal sabit diskler, etkinleştirildiğinde otomatik olarak seyrek olarak ayarlanır.</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>Takas Dosya Konumu</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>Takas sanal sabit diskine giden mutlak bir Windows yolu.</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>Takas dosyalarına gözat</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Takas Dosya Konumu</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Takas sanal sabit diskine giden mutlak bir Windows yolu.</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>Takas Boyutu</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>WSL 2 VM'ye ekli takas alanı boyutu, takas dosyası yoksa 0. Takas depolama alanı, bellek ihtiyacı donanım aygıtındaki sınırı aştığında kullanılan disk tabanlı RAM'dir.</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>Boyutu sıfırla</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>Takas Boyutu</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>WSL 2 VM'sine eklenecek, megabayt cinsinden takas alanı. Takas dosyası yoksa 0. Takas depolama alanı, bellek ihtiyacı donanım cihazındaki sınırı aştığında kullanılan disk tabanlı RAM'dir.</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>VirtIO’yu etkinleştir</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>Konak dosyalarına erişmek için plan 9 yerine virtiofs kullanın ve hızı arttırın.</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>Virtio 9p'yi etkinleştir</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>Virtio aktarımını kullanarak ana bilgisayardan 9P dosya sisteminin bağlanmasını etkinleştirir.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>VM Boşta Kalma Zaman Aşımı</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>Bir VM'nin kapatılmadan önce boşta olduğu milisaniye sayısı.</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>Zaman aşımını sıfırla</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>VM Boşta Kalma Zaman Aşımı</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>Bir VM'nin kapatılmadan önce boşta olduğu milisaniye sayısı.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>Visual Studio'dan WSL üzerinde çalışan uygulamalarınızı derleyin, çalıştırın, hatalarını ayıklayın ve profillerini oluşturun</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>Visual Studio'dan WSL üzerinde uygulamalarınızı çalıştırın ve hatalarını ayıklayın</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>.NET geliştiricileri için Visual Studio'da WSL hakkında daha fazla bilgi edinin</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>C++ geliştiricileri için Visual Studio ve WSL hakkında daha fazla bilgi edinin</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>Linux için Windows Alt Sistemi'ni (WSL) kullanarak Visual Studio'dan ayrılmadan Linux'ta .Net Core ve platformlar arası C++ uygulamalarınızı kolayca çalıştırabilir ve hatalarını ayıklayabilirsiniz. Platformlar arası geliştirme yapıyorsanız, bu yöntemi hedef ortamlarınızı daha fazla test etmenin basit bir yolu olarak kullanabilirsiniz.</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studio Tümleştirmesi</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studio Tümleştirmesi</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/zh-CN/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AppName\" xml:space=\"preserve\">\n    <value>适用于 Linux 的 Windows 子系统</value>\n  </data>\n  <data name=\"AppShortName\" xml:space=\"preserve\">\n    <value>WSL</value>\n  </data>\n  <data name=\"AppDescription\" xml:space=\"preserve\">\n    <value>Windows Subsystem for Linux 使开发人员可以直接在 Windows 上运行未经修改的 GNU/Linux 环境 -- 包括大多数命令行工具、实用工具和应用程序 -- 而无需传统虚拟机或双引导设置的开销。</value>\n  </data>\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\n    <value>磁盘分离失败： {}。有关详细信息，请在 WSL2 内运行 “dmesg”。\n若要强制 WSL2 停止并分离磁盘，请运行“wsl.exe {}”。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\n    <value>磁盘“{}”已连接。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\n    <value>该卷已装载到 WSL2 内。</value>\n  </data>\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\n    <value>已装载具有该名称的磁盘;请卸载磁盘或选择新名称，然后重试。</value>\n  </data>\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\n    <value>指定的装载名称包含无效的 \"/\" 字符。请在没有无效字符的情况下重试。</value>\n  </data>\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\n    <value>已成功将磁盘装载为“/mnt/wsl/{}”。\n注意: 如果修改了 /etc/wsl.conf 中的 automount.root 设置，则位置会有所不同。\n若要卸载和分离磁盘，请运行“wsl.exe {} {}”。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\n    <value>已连接磁盘，但装载失败: {}。\n有关更多详细信息，请在 WSL2 中运行“dmesg”。\n若要分离磁盘，请运行“wsl.exe {} {}”。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\n    <value>以下是可安装的有效分发的列表。\n使用“wsl.exe {} &lt;Distro&gt;”安装。\n</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\n    <value>分发名称已设置。</value>\n  </data>\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\n    <value>提供的安装位置已在使用中。</value>\n  </data>\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\n    <value>已存在具有所提供名称的分发。使用 --name 选择其他名称。</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\n    <value>不存在具有所提供名称的分发。</value>\n  </data>\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\n    <value>需要管理员访问权限才能装载磁盘。</value>\n  </data>\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\n    <value>导出分发失败。</value>\n  </data>\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\n    <value>为此分发实施适用于 Linux 的 Windows 子系统文件系统的一次性升级...\n</value>\n  </data>\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\n    <value>无法启动，因为另一个实例正在权限未提升的状态下运行。不允许同时运行已提升权限的实例和未提升权限的实例。\n</value>\n  </data>\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\n    <value>导入分发失败。</value>\n  </data>\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\n    <value>正在安装 Windows 可选组件: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\n    <value>正在下载: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\n    <value>正在安装: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\n    <value>正在导入通讯组</value>\n  </data>\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\n    <value>{} 已下载。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\n    <value>适用于 Linux 的 Windows 子系统正在恢复以前的安装...</value>\n  </data>\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\n    <value>无效的分发名称：“{}”。\n若要获取有效分发列表，请使用“wsl.exe --list --online'”。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\n    <value>适用于 Linux 的 Windows 子系统实例已终止。</value>\n  </data>\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\n    <value>无效的命令行参数： {}\n请使用“{} --help' 获取受支持的参数列表。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\n    <value>命令行参数 {} 需要一个值。\n请使用“{} --help' 获取受支持的参数列表。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\n    <value>不受支持的控制台设置。若要使用此功能，必须禁用旧的控制台。\n</value>\n  </data>\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\n    <value>{} 不是有效整数。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\n    <value>正在进行此分发的安装、卸载或转换。\n</value>\n  </data>\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\n    <value>正在启动 {}...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\n    <value>无法启动，因为另一个实例正在权限已提升的状态下运行。已提升权限的实例和未提升权限的实例不允许同时运行。\n</value>\n  </data>\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\n    <value>适用于 Linux 的 Windows 子系统没有已安装的分发。\n可通过安装包含以下说明的分发来解决此问题：\n\n使用“wsl.exe --list --online' ”列出可用的分发\n和 “wsl.exe --install &lt;Distro&gt;” 进行安装。</value>\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\n    <value>没有正在运行的分发。</value>\n  </data>\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\n    <value>{} (默认值)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\n    <value>适用于 Linux 的 Windows 子系统分发:</value>\n  </data>\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\n    <value>默认分发: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\n    <value>默认版本: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\n    <value>正在注销。</value>\n  </data>\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\n    <value>找不到用户。</value>\n  </data>\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\n    <value>有关与 WSL 2 关键区别的信息，请访问 https://aka.ms/wsl2\n</value>\n  </data>\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\n    <value>正在进行转换，这可能需要几分钟时间。</value>\n  </data>\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\n    <value>该分发已是请求的版本。</value>\n  </data>\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\n    <value>旧分发不支持 WSL 2。</value>\n  </data>\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\n    <value>WSL2 无法启动，因为此计算机上未启用虚拟化。\n请确保计算机固件设置中“虚拟机平台”可选组件已启用，且虚拟化已开启。\n\n启用“虚拟机平台”通过运行: wsl.exe --install --no-distribution\n\n有关信息，请访问 https://aka.ms/enablevirtualization</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\n    <value>附加的虚拟硬盘或物理磁盘过多。</value>\n  </data>\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\n    <value>已通过 wsl.exe --mount, 装载 VHD，请在启动之前卸载磁盘。</value>\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\n    <value>当前计算机配置不支持 WSL1。\n若要使用 WSL1，请启用“Windows Subsystem for Linux”可选组件。</value>\n  </data>\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\n    <value>仅 WSL2 支持此操作。</value>\n  </data>\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\n    <value>使用量:\n    --networking-mode\n       显示当前网络模式。\n\n    --msal-proxy-path\n        显示 MSAL 代理应用程序的路径。\n\n    --vm-id\n        显示 WSL VM ID。\n\n    --version\n        显示 WSL 包的版本。\n\n    -n\n        不要打印换行符。</value>\n    <comment>{Locked=\"--networking-mode\n\"}{Locked=\"--msal-proxy-path\n\"}{Locked=\"--vm-id\n\"}{Locked=\"--version\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\n    <value>用法：\n    -a\n        强制结果为绝对路径格式。\n    -u\n        从 Windows 路径转换为 WSL 路径(默认)。\n    -w\n        从 WSL 路径转换为 Windows 路径。\n    -m\n        从 WSL 路径转换为 Windows 路径，使用“/”而不是“\\\\”\n\n示例: wslpath 'c:\\\\users'</value>\n    <comment>{Locked=\"-a\n\"}{Locked=\"-u\n\"}{Locked=\"-w\n\"}{Locked=\"-m\n\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\n    <value>对适用于 Linux 的 Windows 子系统执行管理操作\n\n用法:\n    /l, /list [选项]\n        列出已注册的分发版。\n        /all - (可选)列出所有分发版，包括\n               当前安装或未安装的分发版。\n\n        /running - 仅列出当前正在运行的分发版。\n\n    /s, /setdefault &lt;DistributionName&gt;\n        将分发版设置为默认值。\n\n    /t, /terminate &lt;DistributionName&gt;\n        终止分发版。\n\n    /u, /unregister &lt;DistributionName&gt;\n        取消注册分发版并删除根文件系统。</value>\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\n    <value>版权所有 (c) Microsoft Corporation。保留所有权利。\n有关此产品的隐私信息，请访问 https://aka.ms/privacy。\n\n用法: wsl.exe [Argument][Options...][CommandLine]\n\n运行 Linux 二进制文件的参数:\n\n    如果未提供命令行，wsl.exe 将启动默认 shell。\n\n    --exec, -e &lt;CommandLine&gt;\n        在不使用默认 Linux shell 的情况下执行指定的命令。\n\n    --shell-type &lt;standard|login|none&gt;\n        使用提供的 shell 类型执行指定的命令。\n\n    --\n        按原样传递剩余的命令行。\n\n选项:\n    --cd &lt;Directory&gt;\n        将指定目录设置为当前工作目录。\n        如果使用 ~，则将使用 Linux 用户的主路径。如果路径以\n        / 字符开始，它将被解释为绝对 Linux 路径。\n        否则，该值必须是绝对 Windows 路径。\n\n    --distribution, -d &lt;DistroName&gt;\n        运行指定的发行版。\n\n    --distribution-id &lt;DistroGuid&gt;\n        运行指定的发行版 ID。\n\n    --user, -u &lt;UserName&gt;\n        以指定用户身份运行。\n\n    --system\n        为系统发行版启动 shell。\n\n用于管理适用于 Linux 的 Windows 子系统的参数:\n\n    --help\n        显示使用情况信息。\n\n    --debug-shell\n        出于诊断目的打开 WSL2 调试 shell。\n\n    --install [Distro] [Options...]\n        安装适用于 Linux 的 Windows 子系统发行版。\n        有关有效发行版的列表，请使用 'wsl.exe --list --online'。\n\n        选项:\n            --enable-wsl1\n                启用 WSL1 支持。\n\n            --fixed-vhd\n                创建固定大小的磁盘来存储发行版。\n\n            --from-file &lt;Path&gt;\n                从本地文件安装发行版。\n\n            --legacy\n                使用旧发行版清单。\n\n            --location &lt;Location&gt;\n                设置发行版的安装路径。\n\n            --name &lt;Name&gt;\n                设置发行版的名称。\n\n            --no-distribution\n                仅安装所需的可选组件，不安装发行版。\n\n            --no-launch, -n\n                安装后不要启动发行版。\n\n            --version &lt;Version&gt;\n                指定要用于新分发的版本。\n\n            --vhd-size &lt;MemoryString&gt;\n                指定用于存储发行版的磁盘的大小。\n\n            --web-download\n                从 Internet 而不是 Microsoft Store 下载发行版。\n\n    --manage &lt;Distro&gt; &lt;Options...&gt;\n        更改发行版特定选项。\n\n        选项:\n            --move &lt;Location&gt;\n                将发行版移到新位置。\n\n            --set-sparse, -s &lt;true|false&gt;\n                将发行版的 VHD 设置为稀疏，从而允许自动回收磁盘空间。\n\n            --set-default-user &lt;Username&gt;\n                设置发行版的默认用户。\n\n            --resize &lt;MemoryString&gt;\n                将发行版的磁盘大小调整为指定大小。\n\n    --mount &lt;Disk&gt;\n        在所有 WSL 2 发行版中附加和装载物理磁盘或虚拟磁盘。\n\n        选项:\n            --vhd\n                指定 &lt;Disk&gt; 引用虚拟硬盘。\n\n            --bare\n                将磁盘附加到 WSL2，但不要装载它。\n\n            --name &lt;Name&gt;\n                使用装入点的自定义名称装载磁盘。\n\n            --type &lt;Type&gt;\n                装载磁盘时要使用的文件系统(如果未指定)默认为 ext4。\n\n            --options &lt;Options&gt;\n                其他装载选项。\n\n            --partition &lt;Index&gt;\n                要装载的分区的索引(如果未指定)默认为整个磁盘。\n\n    --set-default-version &lt;Version&gt;\n        更改新发行版的默认安装版本。\n\n    --shutdown\n        立即终止所有正在运行的发行版和 WSL 2\n        轻型实用工具虚拟机。\n\n        选项:\n            --force\n                即使正在执行操作，也终止 WSL 2 虚拟机。可能导致数据丢失。\n\n    --status\n        显示适用于 Linux 的 Windows 子系统状态。\n\n    --unmount [磁盘]\n        从所有 WSL2 发行版中卸载和分离磁盘。\n        如果在没有参数的情况下调用，则卸载和分离所有磁盘。\n\n    --uninstall\n        从此计算机卸载适用于 Linux 的 Windows 子系统包。\n\n    --update\n        更新适用于 Linux 的 Windows 子系统包。\n\n        选项:\n            --pre-release\n                下载预发行版本(如果可用)。\n\n    --version, -v\n        显示版本信息。\n\n用于在适用于 Linux 的 Windows 子系统中管理发行版的参数:\n\n    --export &lt;Distro&gt; &lt;FileName&gt; [选项]\n        将发行版导出到 tar 文件。\n        文件名可以是 - for stdout。\n\n        选项:\n            --format &lt;Format&gt;\n                指定导出格式。支持的值: tar、tar.gz、tar.xz、vhd。\n\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [选项]\n        将指定的 tar 文件作为新发行版导入。\n        文件名可以是 - for stdin。\n\n        选项:\n            --version &lt;Version&gt;\n                指定要用于新分发的版本。\n\n            --vhd\n                指定所提供的文件是 .vhd 还是 .vhdx 文件，而不是 tar 文件。\n                此操作在指定的安装位置创建 VHD 文件的副本。\n\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\n        将指定的 VHD 文件作为新发行版导入。\n        必须使用 ext4 文件系统类型设置此虚拟硬盘的格式。\n\n    --list, -l [选项]\n        列出发行版。\n\n        选项:\n            --all\n                列出所有发行版，包括当前\n                正在安装或卸载的发行版。\n\n            --running\n                仅列出当前正在运行的发行版。\n\n            --quiet, -q\n                仅显示发行版名称。\n\n            --verbose, -v\n                显示有关所有发行版的详细信息。\n\n            --online, -o\n                显示适合通过 'wsl --install' 安装的可用发行版列表。\n\n    --set-default, -s &lt;Distro&gt;\n        将分布版设置为默认值。\n\n    --set-version &lt;Distro&gt; &lt;Version&gt;\n        更改指定发行版的版本。\n\n    --terminate, -t &lt;Distro&gt;\n        终止指定的发行版。\n\n    --unregister &lt;Distro&gt;\n        取消注册发行版并删除根文件系统。</value>\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\n\"}{Locked=\"--help\n\"}{Locked=\"--debug-shell\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\n\"}{Locked=\"--fixed-vhd\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\n\"}{Locked=\"--bare\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\n\"}{Locked=\"--force\n\"}{Locked=\"--status\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\n\"}{Locked=\"--update\n\"}{Locked=\"--pre-release\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\n\"}{Locked=\"--running\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\n    <value>WSL 版本: {}\n内核版本: {}\nWSLg 版本: {}\nMSRDC 版本: {}\nDirect3D 版本: {}\nDXCore 版本: {}\nWindows: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\n    <value>MSBuild 版本: {}\n提交: {}\n生成时间: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\n    <value>找不到 {} 中指定的自定义内核:“{}”。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\n    <value>找不到 {} 中的自定义内核模块 vhd:“{}”。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\n    <value>在 {} 中指定的自定义系统分发找不到或格式不正确。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\n    <value>版权所有(c) Microsoft Corporation。保留所有权利。\n有关此产品的隐私信息，请访问 https://aka.ms/privacy。\n\n用法: wslg.exe [参数] [选项...][命令行]\n\n参数:\n   --cd &lt;Directory&gt;\n       将指定目录设置为当前工作目录。\n       如果使用 ~，则将使用 Linux 用户的主路径。如果路径开始于\n       对于 /字符，它将解释为绝对 Linux 路径。\n       否则，该值必须是绝对 Windows 路径。\n\n   --distribution, -d &lt;Distro&gt;\n       运行指定的分发。\n\n   --user, -u &lt;UserName&gt;\n       以指定用户身份运行。\n\n   --shell-type &lt;standard|login|none&gt;\n       使用提供的 shell 类型执行指定的命令。\n\n   --help\n       显示使用情况信息。\n\n   --\n       按时传递剩余的命令行。</value>\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\n    <value>按任意键继续...</value>\n  </data>\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\n    <value>自变量 {} 缺少必需的参数。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\n    <value>正在导出，这可能需要几分钟时间。</value>\n  </data>\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\n    <value>正在导入，这可能需要几分钟时间。</value>\n  </data>\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\n    <value>已通过 {} 或 /etc/wsl.conf 禁用 GUI 应用程序支持。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\n    <value>正在检查更新。</value>\n  </data>\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\n    <value>此分发版仅在Microsoft Store中可用。</value>\n  </data>\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\n    <value>在 ARM64 上 wsl.exe --mount 需要 Windows 版本 27653 或更高版本。</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\n    <value>已安装最新版本的适用于 Linux 的 Windows 子系统。</value>\n  </data>\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\n    <value>正在更新适用于 Linux 的 Windows 子系统: {}。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\n    <value>此应用程序需要适用于 Linux 的 Windows 子系统可选组件。\n通过运行 wsl.exe --install --no-distribution\n来安装它可能需要重新启动系统才能使更改生效。</value>\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\n\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\n    <value>指定的文件必须具有 {} 文件扩展名。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\n    <value>指定的文件必须具有 {} 或 {} 文件扩展名。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\n    <value>找不到 VmSwitch“{}”。可用开关: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\n    <value>桥接网络需要设置 wsl2.vmSwitch。</value>\n  </data>\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\n    <value>无法从 '{}' 提取通讯组列表。{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\n    <value>无法将磁盘“{}”附加到 WSL2: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\n    <value>未能重设磁盘大小。</value>\n  </data>\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\n    <value>找不到值。</value>\n  </data>\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\n    <value>Windows 版本 {} 不支持打包版本的 适用于 Linux 的 Windows 子系统。\n通过 Windows 更新或通过以下方式安装所需的更新： {}\n有关信息，请访问 https://aka.ms/wslinstall</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\n    <value>运行调试 shell 需要以管理员身份运行 wsl.exe。</value>\n  </data>\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\n    <value>分发“{}”的安装过程失败，退出代码: {}。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\n    <value>无效的 GUID 格式:“{}”</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\n    <value>{}:{} 中的节名称无效</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\n    <value>{}:{} 中的键名称无效</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\n    <value>{}:{} 中应为 {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\n    <value>{}:{} 中的转义字符“{}”无效</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\n    <value>{}:{} 中的键“{}”未知</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\n    <value>{}:{} 中键“{}”的整数值“{}”无效</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\n    <value>{}:{} 中键“{}”的 IP 值“{}”无效</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\n    <value>{}:{} 中的键“{}”的布尔值“{}”无效</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\n    <value>{}:{} 中键“{}”的 mac 地址“{}”无效</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\n    <value>未能打开配置文件 {}，{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\n    <value>WSL 启动期间出错</value>\n  </data>\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\n    <value>无法启动“{}”的系统用户会话。有关详细信息，请参阅 journalctl。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\n    <value>打开 EventViewer</value>\n  </data>\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\n    <value>此分发不包含默认名称。使用 --name 选择分发名称。</value>\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\n    <value>不能同时指定参数 {} 和 {}。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\n    <value>参数 {} 需要 {} 参数。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\n    <value>无效分发名称:“{}”。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\n    <value>正在使用旧分发注册。请考虑改用基于 tar 的分发。</value>\n  </data>\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\n    <value>旧分发注册不支持 --version 参数。</value>\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\n    <value>分发“{}”由替代清单提供。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\n    <value>分发哈希不匹配。预期: {}，实际哈希: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\n    <value>无效 HEX 字符串: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\n    <value>安装旧版分发时不支持“{}”。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\n    <value>已成功安装分发。可以通过 “wsl.exe -d {}” 启动它</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\n    <value>{}:{} 中的 .wslconfig 条目“{}”的内存字符串“{}”无效</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\n    <value>未能在“{}”中创建交换磁盘: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\n    <value>此计算机上不支持嵌套虚拟化。</value>\n  </data>\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\n    <value>无法创建地址为“{}”的网络端点，已分配新地址:“{}”</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\n    <value>无法创建地址范围为“{}”的虚拟网络，已创建具有以下范围的新网络:“{}”，{}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\n    <value>安全模式已启用 - 许多功能都将被禁用</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\n    <value>不支持镜像网络模式： {}。\n正在回退到 NAT 网络。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\n    <value>需要 Linux 内核版本 5.10 或更高版本</value>\n  </data>\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\n    <value>Windows 版本 {}。{} 没有所需的功能</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\n    <value>Hyper-V 防火墙不受支持</value>\n  </data>\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\n    <value>DNS 隧道不受支持</value>\n  </data>\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\n    <value>使用镜像网络模式时，wsl2.localhostForwarding 设置无效</value>\n  </data>\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\n    <value>在主机上检测到 Http 代理更改。请重启 WSL 以应用更改。</value>\n  </data>\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\n    <value>检测到 localhost 代理配置，但未镜像到 WSL。NAT 模式下的 WSL 不支持 localhost 代理。</value>\n  </data>\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\n    <value>检测到 IPv6 代理配置，但未镜像到 WSL。NAT 模式下的 WSL 不支持 IPv6。</value>\n  </data>\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\n    <value>检测到 localhost IPv6 代理配置，但未镜像到 WSL。WSL 不支持 localhost IPv6 代理。</value>\n  </data>\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\n    <value>尝试解析代理设置时发生意外错误，代理设置未镜像到 WSL。</value>\n  </data>\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\n    <value>访问注册表时出错。路径：“{}”。错误： {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\n    <value>未能启动 localhost 中继进程。错误： {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\n    <value>无法启动虚拟网络 - 请通过运行： wsl.exe --install --no-distribution 安装可选组件虚拟机平台</value>\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\n    <value>无法配置网络 (networkingMode {})，回退到 networkingMode {}。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\n    <value>无法启用 Windows 组件“{}”(退出代码 {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\n    <value>插件“{}”返回了一个错误</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\n    <value>插件“{}”返回了一个错误。错误消息:“{}”</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\n    <value>插件“{}”需要较新版本的 WSL。请运行： wsl.exe --update</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\n    <value>计算机策略已禁用 .wslconfig 设置“{}”。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\n    <value>计算机策略已禁用调试 shell。</value>\n  </data>\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\n    <value>计算机策略已禁用 wsl.exe --mount 。</value>\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\n    <value>计算机策略已禁用 WSL1。</value>\n  </data>\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\n    <value>请运行“wsl.exe --set-version {} 2”以升级到 WSL2。</value>\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\n    <value>WSL 正在完成升级...</value>\n  </data>\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\n    <value>更新失败(退出代码: {})。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\n    <value>卸载失败(退出代码: {})。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\n    <value>日志文件: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\n    <value>无法删除 MSIX 程序包 (错误： {})。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\n    <value>无法安装 MSIX 程序包 (错误： {})。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>未安装运行 WSL 所需的可选组件。</value>\n  </data>\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\n    <value>安装缺少的组件。</value>\n  </data>\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\n    <value>导入的文件不是有效的 Linux 分发。</value>\n  </data>\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\n    <value>按任意键即可退出...</value>\n  </data>\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\n    <value>有新版本的适用于 Linux 的 Windows 子系统可用。</value>\n  </data>\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\n    <value>更新到版本 {} 或查看下面的发行说明。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\n    <value>更新</value>\n  </data>\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\n    <value>请参阅文档</value>\n  </data>\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\n    <value>无法完成该操作，因为 VHD 当前正在使用中。强制 WSL 停止使用: wsl.exe --shutdown</value>\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\n    <value>{} 不是有效的布尔值，&lt;true|false&gt;</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\n    <value>仅 WSL2 支持稀疏 VHD。</value>\n  </data>\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\n    <value>不支持将 WSL 作为本地系统运行。</value>\n  </data>\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\n    <value>性能提示:</value>\n  </data>\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\n    <value>在 Windows 驱动器上使用像 {} 这样的 I/O 密集型操作将具有较差的性能。请考虑将项目文件移动到 Linux 文件系统以获得更好的性能。单击下方了解详细信息。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\n    <value>查看文档</value>\n  </data>\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\n    <value>不再显示</value>\n  </data>\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\n    <value>WSL2 虚拟机崩溃。</value>\n  </data>\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\n    <value>堆栈跟踪已保存在： {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\n    <value>无法启动分发。错误代码： {}，失败步骤： {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\n    <value>无法启动分发，因为其虚拟磁盘已损坏。</value>\n  </data>\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\n    <value>{}:{} 中的配置键“{}”重复({}:{}中的键“{}”冲突)</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\n    <value>{}:{} 中配置键“{}”的值“{}”无效（有效值: {}）</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\n    <value>正在等待 OOBE 命令完成分发“{}”...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\n    <value>未能从分发内容 {} 读取属性“{}”</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\n    <value>这看起来像一个 VHD 文件。使用 --vhd 导入 VHD，而不是导入 tar。</value>\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\n    <value>未能从 Microsoft Store 安装 {}：{}\n正在尝试 Web 下载...</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\n    <value>尚未配置默认分发。请提供要安装的分发。</value>\n  </data>\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\n    <value>已禁用 wsl2.virtio9p，使用 vsock 传输回退到 9p。</value>\n  </data>\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\n    <value>WSL 安装似乎已损坏 (错误代码： {})。\n按任意键修复 WSL，或按 CTRL-C 取消。\n此提示将在 60 秒后超时。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\n    <value>注册分发时无法分析终端配置文件： {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\n    <value>大小无效: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\n    <value>{}\n错误代码: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\n    <value>JSON 文档无效。分析错误: {}</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\n    <value>wsl2.processors 不能超过系统上的逻辑处理器数 ({} &gt; {})</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\n    <value>查询网络模式失败</value>\n  </data>\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\n    <value>提供的自定义内核模块未指定自定义内核。有关详细信息，请参阅 https://aka.ms/wslcustomkernel。</value>\n  </data>\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\n    <value>由于当前与全局安全访问客户端的兼容性问题，DNS 隧道被禁用。</value>\n  </data>\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\n    <value>由于潜在的数据损坏，目前已禁用稀疏 VHD 支持。\n要强制发行版使用稀疏 VHD，请运行:\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\n    <value>未能装载 {}，有关更多详细信息，请参阅 dmesg。</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\n    <value>使用 mount -a 处理 /etc/fstab 失败。</value>\n  </data>\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\n    <value>装载分发磁盘时出错，该磁盘作为回退以只读方式装载。\n请参阅以下位置的恢复说明： https://aka.ms/wsldiskmountrecovery</value>\n  </data>\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\n    <value>翻译“{}”失败</value>\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\n    <value>出现错误。请稍后再试。</value>\n  </data>\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\n    <value>关于</value>\n  </data>\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\n    <value>关于适用于 Linux 的 Windows 子系统设置</value>\n  </data>\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\n    <value>© Microsoft。保留所有权利。</value>\n  </data>\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\n    <value>适用于 Linux 设置的 Windows 子系统</value>\n  </data>\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\n    <value>适用于 Linux 的 Windows 子系统设置允许开发人员使用基于 GUI 的应用程序管理 .wslconfig 文件。</value>\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\n    <value>自动内存回收</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\n    <value>检测到空闲 CPU 使用情况后自动释放缓存内存。设为 gradual 缓慢释放；设为 dropcache 迅速释放。</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>自动内存回收。</value>\n  </data>\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>检测到空闲 CPU 使用情况后自动释放缓存内存。设为 gradual 缓慢释放；设为 dropcache 迅速释放。</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\n    <value>已启用自动代理</value>\n  </data>\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\n    <value>允许 WSL 使用 Windows 的 HTTP 代理信息。</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>已启用自动代理。</value>\n  </data>\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>允许 WSL 使用 Windows 的 HTTP 代理信息。</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\n    <value>尽力进行 DNS 解析</value>\n  </data>\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\n    <value>仅当 wsl2.dnsTunneling 设置为 true 时适用。设置为 true 时，Windows 将从 DNS 请求中提取问题并尝试解决该问题，忽略未知记录。</value>\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>尽力进行 DNS 分析。</value>\n  </data>\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>仅当 wsl2.dnsTunneling 设置为 true 时适用。设置为 true 时，Windows 将从 DNS 请求中提取问题并尝试解决该问题，忽略未知记录。</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\n    <value>自定义内核</value>\n  </data>\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\n    <value>自定义 Linux 内核的绝对 Windows 路径。</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>浏览内核</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>自定义内核</value>\n  </data>\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>自定义 Linux 内核的绝对 Windows 路径。</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\n    <value>自定义内核模块</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\n    <value>自定义 Linux 内核模块 VHD 的绝对 Windows 路径。</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>浏览内核模块</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>自定义内核模块</value>\n  </data>\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>自定义 Linux 内核模块 VHD 的绝对 Windows 路径。</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\n    <value>自定义系统发行版</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>浏览发行版</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\n    <value>指定一个 VHD 路径，该 VHD 将作为自定义系统发行版加载，主要用于在 WSL 中运行 GUI 应用。[在此处详细了解系统发行]。</value>\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgsystemdistro</value>\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>自定义系统发行版</value>\n  </data>\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>指定一个 VHD 路径，该 VHD 将作为自定义系统发行版加载，主要用于在 WSL 中运行 GUI 应用。</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\n    <value>启用调试控制台</value>\n  </data>\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\n    <value>用于打开输出控制台窗口的布尔值，该窗口在 WSL 2 发行版实例启动时显示 dmesg 的内容。</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>启用调试控制台。</value>\n  </data>\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>用于打开输出控制台窗口的布尔值，该窗口在 WSL 2 发行版实例启动时显示诊断消息的内容。</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\n    <value>默认 VHD 大小</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\n    <value>为可扩展的 WSL 虚拟硬盘指定的默认最大大小 (VHD)，仅用于新创建的分发。</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>重设大小</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>默认 VHD 大小</value>\n  </data>\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>为可扩展的 WSL 虚拟硬盘指定的默认最大大小 (VHD)，仅用于新创建的分发。</value>\n  </data>\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\n    <value>开发人员</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\n    <value>文档</value>\n  </data>\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\n  </data>\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\n    <value>更改 DrvFS 模式</value>\n  </data>\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\n    <value>更改 WSL 中的跨系统文件访问实现。</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\n    <value>已启用 DNS 代理</value>\n  </data>\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\n    <value>仅当 wsl2.networkingMode 设置为 NAT 时适用。用于通知 WSL 将 Linux 中的 DNS 服务器配置为主机上的 NAT 的布尔值。设置为 false 将把 Windows 中 DNS 服务器镜像到 Linux。</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>已启用 DNS 代理。</value>\n  </data>\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>仅当 wsl2.networkingMode 设置为 NAT 时适用。用于通知 WSL 将 Linux 中的 DNS 服务器配置为主机上的 NAT 的布尔值。设置为 false 将把 Windows 中 DNS 服务器镜像到 Linux。</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\n    <value>已启用 DNS 隧道</value>\n  </data>\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\n    <value>更改 DNS 请求从 WSL 代理到 Windows 的方式。</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>已启用 DNS 隧道。</value>\n  </data>\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>更改 DNS 请求从 WSL 代理到 Windows 的方式。</value>\n  </data>\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\n    <value>文件系统</value>\n  </data>\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\n    <value>启用 GUI 应用程序</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\n    <value>用于在 WSL 中打开或关闭对 GUI 应用程序 ([WSLg]) 的支持的布尔值。</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgproject</value>\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>启用 GUI 应用程序。</value>\n  </data>\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>布尔值，用于在 WSL 中启用或禁用对 GUI 应用程序(名为 WSL g)的支持。</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\n    <value>启用硬件性能计数器</value>\n  </data>\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\n    <value>为 Linux 启用硬件性能计数器。</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>启用硬件性能计数器。</value>\n  </data>\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>为 Linux 启用硬件性能计数器。</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\n    <value>已启用 Hyper-V 防火墙</value>\n  </data>\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\n    <value>启用 Hyper-V 防火墙，该防火墙允许 Windows 防火墙规则以及 Hyper-V 流量特定的规则来筛选 WSL 网络流量。</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>已启用 Hyper-V 防火墙。</value>\n  </data>\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>启用 Hyper-V 防火墙，该防火墙允许 Windows 防火墙规则以及 Hyper-V 流量特定的规则来筛选 WSL 网络流量。</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\n    <value>主机地址环回</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\n    <value>仅当 wsl2.networkingMode 设置为镜像(mirrored)时适用。设置为 True 时，将允许容器与主机通过分配给主机的 IP 地址互相连接。请注意，始终可以使用 127.0.0.1 环回地址 - 此选项还允许使用所有额外分配的本地 IP 地址。</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>主机地址环回。</value>\n  </data>\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>仅当 wsl2.networkingMode 设置为镜像(mirrored)时适用。设置为 True 时，将允许容器与主机通过分配给主机的 IP 地址互相连接。请注意，始终可以使用 127.0.0.1 环回地址 - 此选项还允许使用所有额外分配的本地 IP 地址。</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\n    <value>忽略的端口</value>\n  </data>\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\n    <value>仅当 wsl2.networkingMode 设置为镜像(mirrored)时适用。指定 Linux 应用程序可以绑定的端口，这些端口不会 Windows 自动转发或考虑。应采用逗号分隔的列表格式，例如：3000,9000,9090。</value>\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\n    <value>重置端口</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>忽略的端口</value>\n  </data>\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>仅当 wsl2.networkingMode 设置为镜像(mirrored)时适用。指定 Linux 应用程序可以绑定的端口，这些端口不会在 Windows 中自动转发或考虑。应采用逗号分隔的列表格式，例如 3000,9000,9090。</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\n    <value>初始自动代理超时</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\n    <value>仅当 wsl2.autoProxy 设置为 true 时适用。配置启动 WSL 容器时 WSL 等待检索 HTTP 代理信息的时间（以毫秒计）。如果在此时间后解析到代理设置，则必须重新启动 WSL 实例才能使用检索到的代理设置。</value>\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>重置超时</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>初始自动代理超时</value>\n  </data>\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>仅当 wsl2.autoProxy 设置为 true 时才适用。配置启动 WSL 容器时 WSL 等待检索 HTTP 代理信息的时间，以毫秒为单位。如果在此时间后解析到代理设置，则必须重新启动 WSL 实例才能使用检索到的代理设置。</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\n    <value>内核命令行</value>\n  </data>\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\n    <value>额外的内核命令行参数。</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\n    <value>启用 localhost 转发</value>\n  </data>\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\n    <value>布尔值，用于指定 WSL 2 VM 中绑定通配符或本地主机的端口是否应允许主机通过`localhost:端口号`进行连接。</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>启用 localhost 转发。</value>\n  </data>\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>布尔值，用于指定 WSL 2 VM 中绑定通配符或本地主机的端口是否应允许主机通过`localhost:端口号`进行连接。</value>\n  </data>\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\n    <value>{0} MB</value>\n  </data>\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\n    <value>内存和处理器</value>\n  </data>\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\n    <value>内存大小</value>\n  </data>\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\n    <value>要分配给 WSL 2 VM 的内存量。</value>\n  </data>\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\n    <value>重设大小</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>内存大小</value>\n  </data>\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>以 MB 为单位指定的要分配给 WSL 2 VM 的内存量。</value>\n  </data>\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\n    <value>{0} 毫秒</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\n    <value>启用嵌套虚拟化</value>\n  </data>\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\n    <value>用于打开或关闭嵌套虚拟化的布尔值，使其他嵌套虚拟机能够在 WSL 2 内运行。</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>启用嵌套虚拟化。</value>\n  </data>\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>用于打开或关闭嵌套虚拟化的布尔值，使其他嵌套虚拟机能够在 WSL 2 内运行。</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\n    <value>网络模式</value>\n  </data>\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\n    <value>指定 WSL 的网络模式。</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>网络模式。</value>\n  </data>\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>指定 WSL 的网络模式。</value>\n  </data>\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\n    <value>网络</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\n    <value>你可以跨操作系统处理所有文件!</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\n    <value>跨系统文件访问</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\n    <value>从 Linux 访问 Windows 文件</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\n    <value>通过导航到 '/mnt'，然后导航到你的 Windows 驱动器号(例如在本示例中为 C 驱动器)，可以从 Linux 中访问你的 Windows 文件:\n\n'cd /mnt/c'</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\n    <value>使用文件资源管理器访问 Linux 文件</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\n    <value>可以通过导航到 '\\\\wsl.localhost\\' 或单击 'Linux' 图标来查看文件资源管理器中的 Linux 文件。</value>\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\n    <value>从 WSL 启动 Windows 文件和程序</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\n    <value>即使使用 WSL 时，也可以直接从 bash 运行 Windows 可执行文件。尝试运行\n'powershell.exe /c start .'，在当前文件夹中打开文件资源管理器。</value>\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\n    <value>从 Windows 访问 Linux 网络应用</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\n    <value>如果要在 Linux 分发版中生成网络应用(例如在 NodeJS 或 SQL Server 上运行的应用)，则可以(像通常那样)使用 localhost 从 Windows 应用(如 Edge 或 Chrome Internet 浏览器)访问它。这意味着，如果启动了侦听端口 3000 的 Linux 服务器，则可以在 Windows 上的 Edge 中转到 [http://localhost:3000] 来访问它。</value>\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\n    <value>http://localhost:3000</value>\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\n    <value>镜像模式网络</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\n    <value>WSL 还包括一种称为镜像模式的新网络模式，该模式添加了 IPv6 支持等高级功能，并且能够访问局域网中的网络应用程序。</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\n    <value>了解有关跨系统文件访问的详细信息</value>\n  </data>\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslfilesystems</value>\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\n    <value>欢迎使用适用于 Linux 的 Windows 子系统</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\n   <value>WSL 是试用不同 Linux 发行版的好方法。</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\n    <value>发行版管理</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\n    <value>详细了解基本 WSL 命令</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcommands</value>\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\n    <value>详细了解如何导入任何 Linux 发行版</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslcustomdistro</value>\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>列出可安装的 WSL 发行版命令</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l -o'</value>\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>安装指定名称的 WSL 发行版命令</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\n    <value>列出可用的 WSL 发行版命令</value>\n  </data>\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\n    <value>'wsl.exe -l'</value>\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\n    <value>Docker Desktop 非常适合与 WSL 配合使用，可帮助你使用 Linux 容器进行开发。\n\n一些将 Docker Desktop 与 WSL 配合使用的优点:\n\n• 可以使用相同的 Docker 守护程序和映像在 WSL 或 Windows 中运行 Docker 命令。\n• 可以使用 WSL 中的 Windows 驱动器自动装载功能在 Windows 和 Linux 之间无缝共享文件和文件夹。\n• 由于 WSL 的互操作性，你可以使用偏好的 Windows 工具和编辑器处理 Linux 代码和文件，反之亦然。</value>\n </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\n    <value>Docker Desktop 集成</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>详细了解如何将 WSL 与 Docker 配合使用</value>\n  </data>\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocker</value>\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\n    <value>通过适用于 Linux 的 Windows 子系统(WSL)，可以直接在 Windows 上运行你最喜爱的 Linux 工具、实用工具、应用程序和工作流。\n\n花点时间预览社区的一些热门功能或查看我们的综合文档。</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\n    <value>欢迎使用 WSL</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\n    <value>安装的最佳做法</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslsetup</value>\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\n    <value>Linux 入门</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgettingstarted</value>\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\n    <value>适用于 Linux 的 Windows 子系统 (WSL) 文档</value>\n  </data>\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wsldocs</value>\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\n    <value>可以通过本机 Windows 交互操作(例如 alt-tab、开始菜单启动、任务栏固定以及剪切和粘贴支持)使用基于图形的 Linux 应用程序。</value>\n  </data>\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\n    <value>GUI 应用</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\n    <value>WSL 可以利用 Windows GPU 进行机器学习工作流\n\n在 WSL 中运行的 Linux 二进制文件可以自动使用 Windows 中的 GPU 来加快性能。只需像在常规 Linux 计算机上那样安装和运行这些工作流。开始入门有很多种不同的方法，如果使用 NVIDIA 显卡，可以在 docker 容器中运行 CUDA，还可以在 AMD、Intel 或 NVIDIA 显卡上使用 DirectML 运行 PyTorch 或 TensorFlow。请参阅下面的入门指南了解详细信息。</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\n    <value>GPU 加速</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\n    <value>详细了解 GPU 加速</value>\n  </data>\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslgpu</value>\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\n    <value>详细了解 WSL GUI 应用</value>\n  </data>\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslguiapps</value>\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\n    <value>下面是可尝试的应用列表(可以通过键入 'sudo apt install &lt;The App Name&gt;' 在 Ubuntu 中安装所有这些应用)\n\n    • gedit - 基本文本编辑器\n    • audacity - 录制和编辑音频文件\n    • blender - 制作 3D 动画和可视化效果\n    • gimp - 编辑照片\n    • nautilus - Linux 文件资源管理器\n    • vlc - 视频播放器</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\n    <value>可以跨 Windows 和 Linux 操作系统轻松访问网络应用。</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\n    <value>网络集成</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\n    <value>详细了解网络应用程序</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslnetworking</value>\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\n    <value>详细了解镜像模式网络</value>\n  </data>\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslmirroredmode</value>\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\n   <value>可以直接从 VS Code 使用 WSL 作为全时开发环境。</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\n    <value>VS Code 集成</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\n    <value>详细了解如何将 WSL 与 VS Code 配合使用</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslvscode</value>\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\n    <value>如何安装</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\n    <value>安装 VS Code 后，可以从 Windows 终端安装远程 WSL 扩展:\n\n\"code –install-extension ms-vscode-remote.remote-wsl\"</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\n    <value>在 Visual Studio Code 中打开 WSL 项目</value>\n  </data>\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\n    <value>若要在 WSL 发行版中使用 VS Code 打开项目，请打开该发行版的命令行并运行 'code .' 以打开项目文件。\n\n还可以通过 VS Code 自身的命令面板访问更多 VS Code 远程选项。在键盘上点击 \"Shift+Ctrl+P\" 打开命令面板并键入 \"Remote-WSL\" 以查看可用的 VS Code 远程选项列表，以便可以重新打开远程会话中的文件夹、指定要打开的发行版，等等。</value>\n  </data>\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\n    <value>使用方法</value>\n  </data>\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\n    <value>可选功能</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\n    <value>隐私声明</value>\n  </data>\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\n  </data>\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\n    <value>处理器计数</value>\n  </data>\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\n    <value>要分配给 WSL 2 VM 的逻辑处理器数量。</value>\n  </data>\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\n    <value>重置计数</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>处理器计数</value>\n  </data>\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>要分配给 WSL 2 VM 的逻辑处理器数量。</value>\n  </data>\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\n    <value>相关链接</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\n    <value>发行说明</value>\n  </data>\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslreleases</value>\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\n  </data>\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\n    <value>启用安全模式</value>\n  </data>\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\n    <value>在“安全模式”下运行 WSL，这将禁用许多功能，并用于恢复处于错误状态的分发。</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>启用安全模式。</value>\n  </data>\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>在“安全模式”下运行 WSL，这将禁用许多功能，并用于恢复处于错误状态的分发。</value>\n  </data>\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\n    <value>关于</value>\n  </data>\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\n    <value>开发人员</value>\n  </data>\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\n    <value>发行版管理</value>\n  </data>\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\n    <value>Docker Desktop 集成</value>\n  </data>\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\n    <value>文件系统</value>\n  </data>\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\n    <value>常规</value>\n  </data>\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\n    <value>GPU 加速</value>\n  </data>\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\n    <value>GUI 应用</value>\n  </data>\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\n    <value>启动 wsl.exe</value>\n  </data>\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\n    <value>内存和处理器</value>\n  </data>\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\n    <value>网络</value>\n  </data>\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\n    <value>网络集成</value>\n  </data>\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\n    <value>欢迎使用 WSL</value>\n  </data>\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\n    <value>可选功能</value>\n  </data>\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\n    <value>设置</value>\n  </data>\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\n    <value>VS Code 集成</value>\n  </data>\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\n    <value>新增功能</value>\n  </data>\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\n    <value>跨系统文件访问</value>\n  </data>\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\n    <value>问题</value>\n  </data>\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://aka.ms/wslproject</value>\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\n  </data>\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\n    <value>默认启用稀疏 VHD</value>\n  </data>\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\n    <value>启用后，任何新创建的 VHD 都将自动设置为稀疏。</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>默认启用稀疏 VHD。</value>\n  </data>\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>启用后，任何新创建的 VHD 都将自动设置为稀疏。</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\n    <value>交换文件位置</value>\n  </data>\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\n    <value>交换虚拟硬盘的绝对 Windows 路径。</value>\n  </data>\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\n    <value>浏览交换文件</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>交换文件位置</value>\n  </data>\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>交换虚拟硬盘的绝对 Windows 路径。</value>\n  </data>\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\n    <value>交换大小</value>\n  </data>\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\n    <value>要添加到 WSL 2 VM 的交换空间量，0 表示没有交换文件。当内存需求超出硬件设备的限制时，交换存储是基于磁盘的 RAM。</value>\n  </data>\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\n    <value>重设大小</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>交换大小</value>\n  </data>\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>要添加到 WSL 2 VM 的交换空间，以 MB 为单位指定。0 表示没有交换文件。当内存需求超出硬件设备的限制时，交换存储是基于磁盘的 RAM。</value>\n  </data>\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\n    <value>启用 VirtIO</value>\n  </data>\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\n    <value>使用 virtiofs 而不是 plan 9 来访问主机文件，加快速度。</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\n    <value>启用 Virtio 9p</value>\n  </data>\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\n    <value>使用 virtio 传输从主机启用 9P 文件系统装载。</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\n    <value>VM 空闲超时</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\n    <value>VM 在关闭之前处于空闲状态的毫秒数。</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\n    <value>重置超时</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\n    <value>VM 空闲超时</value>\n  </data>\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\n    <value>VM 在关闭之前处于空闲状态的毫秒数。</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\n    <value>通过 Visual Studio 构建、运行、调试和分析运行于 WSL 上的应用</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\n    <value>通过 Visual Studio 运行和调试 WSL 上的应用</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\n    <value>详细了解面向 .NET 开发人员的 Visual Studio 中的 WSL</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\n    <value>详细了解面向 C++ 开发人员的 Visual Studio 和 WSL</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\n    <value>你可以使用适用于 Linux 的 Windows 子系统(WSL)在不离开 Visual Studio 的情况下，在 Linux 中轻松运行和调试 .NET Core 和跨平台 C++ 应用。如果你是跨平台开发人员，可以通过这种方法简单地测试更多目标环境。</value>\n  </data>\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\n    <value>Visual Studio 集成</value>\n  </data>\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\n    <value>Visual Studio 集成</value>\n  </data>\n</root>"
  },
  {
    "path": "localization/strings/zh-TW/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<root>\r\n  <!--\r\n    Microsoft ResX Schema\r\n\r\n    Version 2.0\r\n\r\n    The primary goals of this format is to allow a simple XML format\r\n    that is mostly human readable. The generation and parsing of the\r\n    various data types are done through the TypeConverter classes\r\n    associated with the data types.\r\n\r\n    Example:\r\n\r\n    ... ado.net/XML headers & schema ...\r\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\r\n    <resheader name=\"version\">2.0</resheader>\r\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\r\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\r\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\r\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\r\n    </data>\r\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\r\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r\n        <comment>This is a comment</comment>\r\n    </data>\r\n\r\n    There are any number of \"resheader\" rows that contain simple\r\n    name/value pairs.\r\n\r\n    Each data row contains a name, and value. The row also contains a\r\n    type or mimetype. Type corresponds to a .NET class that support\r\n    text/value conversion through the TypeConverter architecture.\r\n    Classes that don't support this are serialized and stored with the\r\n    mimetype set.\r\n\r\n    The mimetype is used for serialized objects, and tells the\r\n    ResXResourceReader how to depersist the object. This is currently not\r\n    extensible. For a given mimetype the value must be set accordingly:\r\n\r\n    Note - application/x-microsoft.net.object.binary.base64 is the format\r\n    that the ResXResourceWriter will generate, however the reader can\r\n    read any of the formats listed below.\r\n\r\n    mimetype: application/x-microsoft.net.object.binary.base64\r\n    value   : The object must be serialized with\r\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.soap.base64\r\n    value   : The object must be serialized with\r\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.bytearray.base64\r\n    value   : The object must be serialized into a byte array\r\n            : using a System.ComponentModel.TypeConverter\r\n            : and then encoded with base64 encoding.\r\n    -->\r\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\r\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\r\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\r\n      <xsd:complexType>\r\n        <xsd:choice maxOccurs=\"unbounded\">\r\n          <xsd:element name=\"metadata\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"assembly\">\r\n            <xsd:complexType>\r\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"data\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"resheader\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n        </xsd:choice>\r\n      </xsd:complexType>\r\n    </xsd:element>\r\n  </xsd:schema>\r\n  <resheader name=\"resmimetype\">\r\n    <value>text/microsoft-resx</value>\r\n  </resheader>\r\n  <resheader name=\"version\">\r\n    <value>2.0</value>\r\n  </resheader>\r\n  <resheader name=\"reader\">\r\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <resheader name=\"writer\">\r\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <data name=\"AppName\" xml:space=\"preserve\">\r\n    <value>Windows 子系統 Linux 版</value>\r\n  </data>\r\n  <data name=\"AppShortName\" xml:space=\"preserve\">\r\n    <value>WSL</value>\r\n  </data>\r\n  <data name=\"AppDescription\" xml:space=\"preserve\">\r\n    <value>Windows 子系統 Linux 版可讓開發人員直接在 Windows 上執行 GNU/Linux 環境 (包括大部分的命令列工具、公用程式和應用程式)，而不需要傳統虛擬機器或雙重開機設定的額外負荷。</value>\r\n  </data>\r\n  <data name=\"MessageDetachFailed\" xml:space=\"preserve\">\r\n    <value>磁碟無法中斷連結： {}。如需詳細數據，請在 WSL2 內執行 'dmesg'。\r\n若要強制 WSL2 停止並中斷連結磁碟，請執行 『wsl.exe {}』。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskAlreadyAttached\" xml:space=\"preserve\">\r\n    <value>磁碟 '{}' 已連結。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskAlreadyMounted\" xml:space=\"preserve\">\r\n    <value>該磁碟區已掛接在 WSL2 內。</value>\r\n  </data>\r\n  <data name=\"MessageDiskMountNameAlreadyExists\" xml:space=\"preserve\">\r\n    <value>已掛接具有該名稱的磁碟; 請卸載磁碟或選擇新名稱，然後再試一次。</value>\r\n  </data>\r\n  <data name=\"MessageDiskMountNameInvalid\" xml:space=\"preserve\">\r\n    <value>指定的掛接名稱包含不正確 '/' 字元。請不要使用不正確字元再試一次。</value>\r\n  </data>\r\n  <data name=\"MessageDiskMounted\" xml:space=\"preserve\">\r\n    <value>磁碟已成功掛接為 『/mnt/wsl/{}』。\r\n注意： 如果您在 /etc/wsl.conf 中修改 automount.root 設定，位置會不一樣。\r\n若要卸除並卸離磁碟，請執行 'wsl.exe {} {}'。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskMountFailed\" xml:space=\"preserve\">\r\n    <value>磁碟已連結，但無法掛接： {}。\r\n如需詳細數據，請在 WSL2 內執行 'dmesg'。\r\n若要中斷連結磁碟，請執行 'wsl.exe {} {}'。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionListOnline\" xml:space=\"preserve\">\r\n    <value>下列是可安裝的有效發佈清單。\r\n使用 'wsl.exe {} &lt;Distro&gt;' 安裝.\r\n</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroAlreadySet\" xml:space=\"preserve\">\r\n    <value>已設定發佈名稱。</value>\r\n  </data>\r\n  <data name=\"MessageDistroInstallPathAlreadyExists\" xml:space=\"preserve\">\r\n    <value>提供的安裝位置已在使用中。</value>\r\n  </data>\r\n  <data name=\"MessageDistroNameAlreadyExists\" xml:space=\"preserve\">\r\n    <value>具有所提供名稱的發佈已經存在。使用 --name 選擇不同的名稱。</value>\r\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroNotFound\" xml:space=\"preserve\">\r\n    <value>沒有具有所提供名稱的發佈。</value>\r\n  </data>\r\n  <data name=\"MessageElevationNeededToMountDisk\" xml:space=\"preserve\">\r\n    <value>需要系統管理員存取權才能掛接磁碟。</value>\r\n  </data>\r\n  <data name=\"MessageExportFailed\" xml:space=\"preserve\">\r\n    <value>匯出發佈失敗。</value>\r\n  </data>\r\n  <data name=\"MessageFsUpgradeNeeded\" xml:space=\"preserve\">\r\n    <value>正在為此次發佈執行 Windows 子系統 Linux 版一次性升級...\r\n</value>\r\n  </data>\r\n  <data name=\"MessageHigherIntegrity\" xml:space=\"preserve\">\r\n    <value>無法啟動，因為有另一個執行個體正以未提高權限的方式執行。不允許同時執行提高權限和未提高權限的執行個體。\r\n</value>\r\n  </data>\r\n  <data name=\"MessageImportFailed\" xml:space=\"preserve\">\r\n    <value>匯入發布失敗。</value>\r\n  </data>\r\n  <data name=\"MessageInstallingWindowsComponent\" xml:space=\"preserve\">\r\n    <value>正在安裝 Windows 選用元件: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDownloading\" xml:space=\"preserve\">\r\n    <value>正在下載: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstalling\" xml:space=\"preserve\">\r\n    <value>正在安裝: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageImportingDistribution\" xml:space=\"preserve\">\r\n    <value>正在匯入發佈</value>\r\n  </data>\r\n  <data name=\"MessageDownloadComplete\" xml:space=\"preserve\">\r\n    <value>已下載 {}。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstallContinue\" xml:space=\"preserve\">\r\n    <value>Windows 子系統 Linux 版正在繼續先前的安裝...</value>\r\n  </data>\r\n  <data name=\"MessageInvalidDistributionName\" xml:space=\"preserve\">\r\n    <value>無效的發佈名稱： '{}'。\r\n若要取得有效發佈的清單，請使用 『wsl.exe --list --online'。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--list \"}{Locked=\"--online'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInstanceTerminated\" xml:space=\"preserve\">\r\n    <value>Windows 子系統 Linux 版執行個體已終止。</value>\r\n  </data>\r\n  <data name=\"MessageInvalidCommandLine\" xml:space=\"preserve\">\r\n    <value>無效的命令行自變數： {}\r\n請使用 '{} --help' 取得支援的自變數清單。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMissingArgument\" xml:space=\"preserve\">\r\n    <value>命令行自變數 {} 需要值。\r\n請使用 '{} --help' 取得支援的自變數清單。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--help'\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidConsole\" xml:space=\"preserve\">\r\n    <value>不支援的主控台設定。若要使用此功能，必須停用舊版主控台。\r\n</value>\r\n  </data>\r\n  <data name=\"MessageInvalidInteger\" xml:space=\"preserve\">\r\n    <value>{} 不是有效的整數。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidState\" xml:space=\"preserve\">\r\n    <value>此發佈正在進行安裝、解除安裝或轉換。\r\n</value>\r\n  </data>\r\n  <data name=\"MessageLaunchingDistro\" xml:space=\"preserve\">\r\n    <value>正在啟動 {}...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageLowerIntegrity\" xml:space=\"preserve\">\r\n    <value>無法啟動，因為有另一個執行個體正以提高權限的方式執行。不允許同時執行提高權限和未提高權限的執行個體。\r\n</value>\r\n  </data>\r\n  <data name=\"MessageNoDefaultDistro\" xml:space=\"preserve\">\r\n    <value>Windows 子系統 Linux 版 沒有已安裝的發佈。\r\n您可以使用下列指示安裝發佈以解決此問題：\r\n\r\n使用 『wsl.exe --list --online' 列出可用的發佈\r\n和 『wsl.exe --install &lt;Distro&gt;』 來安裝。</value>\r\n    <comment>{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNoRunningDistro\" xml:space=\"preserve\">\r\n    <value>沒有任何執行中的發佈。</value>\r\n  </data>\r\n  <data name=\"MessagePrintDistroDefault\" xml:space=\"preserve\">\r\n    <value>{} (預設)</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRegisteredDistrosHeader\" xml:space=\"preserve\">\r\n    <value>Windows 子系統 Linux 版發佈:</value>\r\n  </data>\r\n  <data name=\"MessageStatusDefaultDistro\" xml:space=\"preserve\">\r\n    <value>預設通訊群組: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageStatusDefaultVersion\" xml:space=\"preserve\">\r\n    <value>預設版本: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageStatusUnregistering\" xml:space=\"preserve\">\r\n    <value>取消註冊中。</value>\r\n  </data>\r\n  <data name=\"MessageUserNotFound\" xml:space=\"preserve\">\r\n    <value>找不到使用者。</value>\r\n  </data>\r\n  <data name=\"MessageVmModeConversionInfo\" xml:space=\"preserve\">\r\n    <value>有關 WSL 2 的主要差異詳細資訊，請瀏覽 https://aka.ms/wsl2\r\n</value>\r\n  </data>\r\n  <data name=\"MessageConversionStart\" xml:space=\"preserve\">\r\n    <value>轉換進行中，這可能需要幾分鐘的時間。</value>\r\n  </data>\r\n  <data name=\"MessageAlreadyRequestedVersion\" xml:space=\"preserve\">\r\n    <value>發佈已是要求的版本。</value>\r\n  </data>\r\n  <data name=\"MessageVmModeNotSupported\" xml:space=\"preserve\">\r\n    <value>舊版發佈不支援 WSL 2。</value>\r\n  </data>\r\n  <data name=\"MessageEnableVirtualization\" xml:space=\"preserve\">\r\n    <value>WSL2 無法啟動，因為此機未啟用虛擬化。\r\n請確保您的電腦韌體設定中啟用了「虛擬機平台」這個選用元件，並且虛擬化功能已開啟。\r\n\r\n啟用「虛擬機器平台」時，執行： wsl.exe --install --no-distribution\r\n\r\n詳情請造訪 https://aka.ms/enablevirtualization</value>\r\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageTooManyDisks\" xml:space=\"preserve\">\r\n    <value>連結太多虛擬硬碟或實體磁碟。</value>\r\n  </data>\r\n  <data name=\"MessageUserVhdAlreadyAttached\" xml:space=\"preserve\">\r\n    <value>VHD 已透過 wsl.exe --mount, 掛接，請先卸載磁碟再啟動。</value>\r\n    <comment>{Locked=\"--mount,\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWsl1NotSupported\" xml:space=\"preserve\">\r\n    <value>您目前的電腦設定不支援 WSL1。\r\n請啟用「Windows 子系統 Linux 版」選用元件以使用 WSL1。</value>\r\n  </data>\r\n  <data name=\"MessageWsl2Needed\" xml:space=\"preserve\">\r\n    <value>只有 WSL2 支援此作業。</value>\r\n  </data>\r\n  <data name=\"MessageWslInfoUsage\" xml:space=\"preserve\">\r\n    <value>使用方式: \r\n    --networking-mode\r\n       顯示當前的網路模式。\r\n\r\n    --msal-proxy-path\r\n        顯示 MSAL Proxy 應用程式的路徑。\r\n\r\n    --vm-id\r\n        顯示 WSL 虛擬機器識別碼。\r\n\r\n    --version\r\n        顯示 WSL 套件的版本。\r\n\r\n    -n\r\n        不要列印新行。</value>\r\n    <comment>{Locked=\"--networking-mode\r\n\"}{Locked=\"--msal-proxy-path\r\n\"}{Locked=\"--vm-id\r\n\"}{Locked=\"--version\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslPathUsage\" xml:space=\"preserve\">\r\n    <value>用法：\r\n    -a\r\n        強制結果使用絕對路徑格式。\r\n    -u\r\n        將 Windows 路徑轉換成 WSL 路徑 (預設)。\r\n    -w\r\n        將 WSL 路徑轉換成 Windows 路徑。\r\n    -m\r\n        在將 WSL 路徑轉換成 Windows 路徑時，使用 '/' 而非 '\\\\'\r\n\r\n範例： wslpath 'c：\\\\users'</value>\r\n    <comment>{Locked=\"-a\r\n\"}{Locked=\"-u\r\n\"}{Locked=\"-w\r\n\"}{Locked=\"-m\r\n\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslconfigUsage\" xml:space=\"preserve\">\r\n    <value>在 Windows 子系統 Linux 版上執行系統管理作業\r\n\r\n使用情況:\r\n    /l, /list [Option]\r\n        列出已註冊的發行版本。\r\n        /all - 選擇性地列出所有發行版本，包括\r\n               目前正在安裝或卸載的發行版本。\r\n\r\n        /running - 只列出目前正在執行的發行版本。\r\n\r\n    /s, /setdefault &lt;DistributionName&gt;\r\n        將發行版本設定為預設值。\r\n\r\n    /t, /terminate &lt;DistributionName&gt;\r\n        終止發行版本。\r\n\r\n    /u, /unregister &lt;DistributionName&gt;\r\n        取消註冊發行版本並刪除根檔案系統。</value>\r\n    <comment>{Locked=\"/l,\"}{Locked=\"/list \"}{Locked=\"/all \"}{Locked=\"/running \"}{Locked=\"/s,\"}{Locked=\"/setdefault \"}{Locked=\"/t,\"}{Locked=\"/terminate \"}{Locked=\"/u,\"}{Locked=\"/unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslUsage\" xml:space=\"preserve\">\r\n    <value>Copyright (c) Microsoft Corporation. 著作權所有，並保留一切權利。\r\n如需有關此產品的隱私權資訊，請造訪 https://aka.ms/privacy。\r\n\r\n使用方式: wsl.exe [Argument] [Options...] [CommandLine]\r\n\r\n用於執行 Linux 二進位檔的引數:\r\n\r\n    如果未提供任何命令列，wsl.exe 會啟動預設殼層。.\r\n\r\n    --exec, -e &lt;CommandLine&gt;\r\n        執行指定的命令，而不使用預設的 Linux 殼層。\r\n\r\n    --shell-type &lt;standard|login|none&gt;\r\n        使用提供的殼層類型執行指定的命令。.\r\n\r\n    --\r\n      依原樣傳遞其餘的命令列。\r\n\r\n選項:\r\n    --cd &lt;Directory&gt;\r\n        將指定的目錄設定為目前的工作目錄。\r\n        如果使用 ~，則會使用 Linux 使用者的常用路徑。如果路徑的開頭為\r\n        / 字元，它則會被解譯為絕對 Linux 路徑。\r\n        否則，該值必須是絕對 Windows 路徑。\r\n\r\n    --distribution, -d &lt;DistroName&gt;\r\n        執行指定的發佈。\r\n\r\n    --distribution-id &lt;DistroGuid&gt;\r\n        執行指定的發佈識別碼。\r\n\r\n    --user, -u &lt;UserName&gt;\r\n        以指定使用者身分執行。\r\n\r\n    --system\r\n        啟動系統發佈的殼層。\r\n\r\n用於管理 Linux 版 Windows 子系統的引數:\r\n\r\n    --help\r\n        顯示使用方式資訊。\r\n\r\n    --debug-shell\r\n        開啟用於診斷用途的 WSL2 偵錯殼層。\r\n\r\n    --install [Distro] [Options...]\r\n        安裝 Linux 版 Windows 子系統發行版本。\r\n        如需有效發行版本的清單，請使用 'wsl.exe --list --online'。\r\n\r\n       選項:\r\n            --enable-wsl1\r\n                啟用 WSL1 支援。\r \n\r\n            --fixed-vhd\r\n                建立大小固定的磁碟，以儲存發佈。\r \n\r\n            --from-file &lt;Path&gt;\r\n                從本機檔案安裝發佈。\r\n\r\n            --legacy\r\n                使用舊版發佈資訊清單。\r\n\r\n            --location &lt;Location&gt;\r\n                設定發佈的安裝路徑。\r\n\r\n            --name &lt;Name&gt;\r\n                設定發佈的名稱。\r\n\r\n            --no-distribution\r\n                僅安裝必須的選擇性元件，不安裝發行版本。\r\n\r\n            --no-launch, -n\r\n                安裝後請勿啟動發佈。\r\n\r\n            --version &lt;Version&gt;\r\n                指定要用於新發佈的版本。\r\n\r\n            --vhd-size &lt;MemoryString&gt;\r\n                指定磁碟大小，以儲存發佈。\r\n\r\n            --web-download\r\n                從網際網路下載發佈，而非從 Microsoft Store 下載。\r\n\r \n    --manage &lt;Distro&gt; &lt;Options...&gt;\r \n        變更 distro 指定選項。\r\n\r\n        選項:\r\n            --move &lt;Location&gt;\r\n                將發佈移動到新位置。\r\n\r\n            --set-sparse, -s &lt;true|false&gt;\r\n將 distro 的 VHD 設為疏鬆，允許磁碟空格自動回收。\r \n\r\n            --set-default-user &lt;Username&gt;\r\n                設定發佈的預設使用者。\r\n\r\n            --resize &lt;MemoryString&gt;\r\n                將發佈的磁碟大小調整為指定大小。\r\n\r\n    --mount &lt;Disk&gt;\r\n        在所有 WSL 2 發行版本中連結和掛接實體或虛擬磁碟。\r\n\r\n        選項:\r\n\t\t--vhd\r\n                指定 &lt;Disk&gt; 參考虛擬硬碟。\r\n\r\n            --bare\r\n                將磁碟連結到 WSL2，但不要掛接。\r\n\r\n            --name &lt;Name&gt;\r\n                使用掛接點的自訂名稱掛接磁碟。\r\n\r\n            --type &lt;Type&gt;\r\n                掛接磁碟時要使用的檔案系統，如果未指定，則預設為 ext4。\r\n\r\n            --options &lt;Options&gt;\r\n                其他掛接選項。\r\n\r\n            --partition &lt;Index&gt;\r\n                要掛接之磁碟分割的索引，如果未指定，則預設為整個磁碟。\r\n\r\n    --set-default-version &lt;Version&gt;\r\n        為新的發行版本變更預設安裝版本。\r\n\r\n    --shutdown\r\n        立即終止所有正在執行的發行版本和 WSL 2\r\n        輕量型公用程式虛擬機器。\r\n\r\n        選項:\r\n            --force\r\n                結束 WSL 2 虛擬機器，即使作業正在進行中。可能會造成資料遺失。\r\n\r \n    --status\r\n        顯示 Linux 版 Windows 子系統的狀態。\r\n\r\n    --unmount [Disk]\r\n        從所有 WSL2 發行版本卸載並中斷連結磁碟。\r\n        如果呼叫時沒有引數，則卸載並中斷連結所有磁碟。\r\n\r\n    --uninstall\r\n        從此電腦取消安裝 Linux 套件的 Windows 子系統。\r\n\r\n    --update\r\n        更新 Linux 版 Windows 子系統套件。\r\n\r\n        選項:\r\n            --pre-release\r\n                下載預先發行版本 (如果有提供)。\r\n\r\n    --version, -v\r\n       顯示版本資訊。\r\n\r\n用於在 Linux 版 Windows 子系統中管理發行版本的引數:\r\n\r\n    --export &lt;Distro&gt; &lt;FileName&gt; [Options]\r\n        將發行版本匯出至 tar 檔案。\r\n        stdout 的檔案名稱可以是 -。\r\n\r\n        選項:\r \n            --format &lt;Format&gt;\r\n                指定匯出格式。支援的值: tar、tar.gz、tar.xz、vhd。\r\n\r\n    --import &lt;Distro&gt; &lt;InstallLocation&gt; &lt;FileName&gt; [Options]\r\n        匯入指定的 tar 檔案以做為新發行版本。\r\n        標準輸出的檔案名稱可以是 -。\r\n\r\n        選項:\r\n            --version &lt;Version&gt;\r\n                指定要用於新發行版本的版本。\r\n\r\n            --vhd\r\n                指定提供的檔案是 .vhd 或 .vhdx 檔案，而不是 tar 檔案。\r\n                此作業會在指定的安裝位置複製 .vhdx 檔案。\r\n\r\n    --import-in-place &lt;Distro&gt; &lt;FileName&gt;\r\n        匯入指定的 VHD 檔案以做為新發行版本。\r\n        此虛擬硬碟必須格式化為 ext4 檔案系統類型。\r\n\r\n    --list, -l [Options]\r\n        列出發行版本。\r\n\r\n        Options:\r\n            --all\r\n                列出所有發行版本，包括\r\n                目前正在安裝或解除安裝的發行版本。\r\n\r\n            --running\r\n                僅列出目前正在執行的發行版本。\r\n\r\n            --quiet, -q\r\n                只顯示發行版本名稱。\r\n\r\n            --verbose, -v\r\n                顯示所有發行版本的詳細資訊\r\n\r\n            --online, -o\r\n                顯示可使用 'wsl.exe --install' 安裝的可用發行版本清單。\r\n\r\n    --set-default, -s &lt;Distro&gt;\r\n        將發行版本設定為預設值。\r\n\r\n    --set-version &lt;Distro&gt; &lt;Version&gt;\r\n        變更指定發行版本的版本。\r\n\r\n    --terminate, -t &lt;Distro&gt;\r\n        終止指定的發行版本。\r\n\r\n    --unregister &lt;Distro&gt;\r\n        取消註冊發行版本並刪除根檔案系統。</value>\r\n  <comment>{Locked=\"--exec,\"}{Locked=\"--shell-type \"}{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--distribution-id \"}{Locked=\"--user,\"}{Locked=\"--system\r\n\"}{Locked=\"--help\r\n\"}{Locked=\"--debug-shell\r\n\"}{Locked=\"--install \"}{Locked=\"--list \"}{Locked=\"--online'\"}{Locked=\"--enable-wsl1\r\n\"}{Locked=\"--fixed-vhd\r\n\"}{Locked=\"--from-file \"}{Locked=\"--legacy\r\n\"}{Locked=\"--location \"}{Locked=\"--name \"}{Locked=\"--no-distribution\r\n\"}{Locked=\"--no-launch,\"}{Locked=\"--version \"}{Locked=\"--vhd-size \"}{Locked=\"--web-download\r\n\"}{Locked=\"--manage \"}{Locked=\"--move \"}{Locked=\"--set-sparse,\"}{Locked=\"--set-default-user \"}{Locked=\"--resize \"}{Locked=\"--mount \"}{Locked=\"--vhd\r\n\"}{Locked=\"--bare\r\n\"}{Locked=\"--name \"}{Locked=\"--type \"}{Locked=\"--options \"}{Locked=\"--partition \"}{Locked=\"--set-default-version \"}{Locked=\"--shutdown\r\n\"}{Locked=\"--force\r\n\"}{Locked=\"--status\r\n\"}{Locked=\"--unmount \"}{Locked=\"--uninstall\r\n\"}{Locked=\"--update\r\n\"}{Locked=\"--pre-release\r\n\"}{Locked=\"--version,\"}{Locked=\"--export \"}{Locked=\"--format \"}{Locked=\"--import \"}{Locked=\"--version \"}{Locked=\"--vhd\r\n\"}{Locked=\"--import-in-place \"}{Locked=\"--list,\"}{Locked=\"--all\r\n\"}{Locked=\"--running\r\n\"}{Locked=\"--quiet,\"}{Locked=\"--verbose,\"}{Locked=\"--online,\"}{Locked=\"--install'\"}{Locked=\"--set-default,\"}{Locked=\"--set-version \"}{Locked=\"--terminate,\"}{Locked=\"--unregister \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePackageVersions\" xml:space=\"preserve\">\r\n    <value>WSL 版本： {}\r\n核心版本： {}\r\nWSLg 版本： {}\r\nMSRDC 版本： {}\r\nDirect3D 版本： {}\r\nDXCore 版本： {}\r\nWindows 版本： {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageBuildInfo\" xml:space=\"preserve\">\r\n    <value>MSBuild 版本： {}\r\n認可： {}\r\n建置時間： {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomKernelNotFound\" xml:space=\"preserve\">\r\n    <value>找不到 {} 中指定的自定義核心： '{}'。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomKernelModulesNotFound\" xml:space=\"preserve\">\r\n    <value>找不到 {} 中的自訂核心模組 VHD：'{}'。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCustomSystemDistroError\" xml:space=\"preserve\">\r\n    <value>找不到 {} 中指定的自定義系統發佈，或其格式不正確。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslgUsage\" xml:space=\"preserve\">\r\n    <value>Copyright (c) Microsoft Corporation。著作權所有，並保留一切權利。\r\n如需此產品的隱私權資訊，請瀏覽https://aka.ms/privacy。\r\n\r\n使用方式: wslg.exe [引數] [選項...][CommandLine]\r\n\r\n引數:\r\n   --cd &lt;Directory&gt;\r\n       將指定的目錄設定為目前的工作目錄。\r\n       如果使用 ~，將會使用 Linux 使用者首頁路徑。如果路徑開頭為\r\n       與 / 字元，它將被解譯為絕對 Linux 路徑。\r\n       否則，此值必須是絕對 Windows 路徑。\r\n\r\n   --distribution, -d &lt;Distro&gt;\r\n       執行指定的發佈。\r\n\r\n   --user, -u &lt;UserName&gt;\r\n       以指定的使用者執行。\r\n\r\n   --shell-type &lt;standard|login|none&gt;\r\n       使用提供的殼層類型執行指定的命令。\r\n\r\n   --help\r\n       顯示使用資訊。\r\n\r\n   --\r\n       將剩餘的命令列傳遞為目前。</value>\r\n    <comment>{Locked=\"--cd \"}{Locked=\"--distribution,\"}{Locked=\"--user,\"}{Locked=\"--shell-type \"}{Locked=\"--help\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePressAnyKeyToContinue\" xml:space=\"preserve\">\r\n    <value>請按任意鍵繼續...</value>\r\n  </data>\r\n  <data name=\"MessageRequiredParameterMissing\" xml:space=\"preserve\">\r\n    <value>引數 {} 遺失必要的參數。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageExportProgress\" xml:space=\"preserve\">\r\n    <value>匯出進行中，這可能需要幾分鐘的時間。</value>\r\n  </data>\r\n  <data name=\"MessageImportProgress\" xml:space=\"preserve\">\r\n    <value>正在匯入，這可能需要幾分鐘的時間。</value>\r\n  </data>\r\n  <data name=\"GuiApplicationsDisabled\" xml:space=\"preserve\">\r\n    <value>已透過 {} 或 /etc/wsl.conf 停用 GUI 應用程式支援。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCheckingForUpdates\" xml:space=\"preserve\">\r\n    <value>正在檢查更新。</value>\r\n  </data>\r\n  <data name=\"MessageDistroOnlyAvailableFromStore\" xml:space=\"preserve\">\r\n    <value>此發佈僅可從 Microsoft Store 取得。</value>\r\n  </data>\r\n  <data name=\"MessageWslMountNotSupportedOnArm\" xml:space=\"preserve\">\r\n    <value>ARM64 上的 wsl.exe --mount 需要 Windows 27653 版或更新版本。</value>\r\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUpdateNotNeeded\" xml:space=\"preserve\">\r\n    <value>已安裝最新版的Windows 子系統 Linux 版。</value>\r\n  </data>\r\n  <data name=\"MessageUpdatingToVersion\" xml:space=\"preserve\">\r\n    <value>正在將 Windows 子系統 Linux 版 更新為版本： {}。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWslOptionalComponentRequired\" xml:space=\"preserve\">\r\n    <value>此應用程式需要 Windows 子系統 Linux 版選用元件。\r\n執行下列項目，以進行安裝: wsl.exe --install --no-distribution\r\n系統可能需要重新啟動，變更才會生效。</value>\r\n     <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\r\n\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRequiresFileExtension\" xml:space=\"preserve\">\r\n    <value>指定的檔案必須具有 {} 副檔名。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageRequiresFileExtensions\" xml:space=\"preserve\">\r\n    <value>指定的檔案必須具有 {} 或 {} 副檔名。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVmSwitchNotFound\" xml:space=\"preserve\">\r\n    <value>找不到 VmSwitch '{}'。可用參數: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVmSwitchNotSet\" xml:space=\"preserve\">\r\n    <value>橋接器網路需要設定 wsl2.vmSwitch。</value>\r\n  </data>\r\n  <data name=\"MessageCouldFetchDistributionList\" xml:space=\"preserve\">\r\n    <value>未能從 '{}' 取得分配清單。{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToAttachDisk\" xml:space=\"preserve\">\r\n    <value>無法將磁碟 '{}' 連結到 WSL2： {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToResizeDisk\" xml:space=\"preserve\">\r\n    <value>無法調整磁碟。</value>\r\n  </data>\r\n  <data name=\"MessageNoValueFound\" xml:space=\"preserve\">\r\n    <value>找不到任何值。</value>\r\n  </data>\r\n  <data name=\"MessageOsNotSupported\" xml:space=\"preserve\">\r\n    <value>Windows 版本 {} 不支持封裝版本的 Windows 子系統 Linux 版。\r\n透過 Windows 更新或透過下列方式安裝必要的更新： {}\r\n如需詳細資訊，請造訪 https://aka.ms/wslinstall</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageAdministratorAccessRequiredForDebugShell\" xml:space=\"preserve\">\r\n    <value>執行偵錯殼層需要以系統管理員身分執行 wsl.exe。</value>\r\n  </data>\r\n  <data name=\"MessageInstallProcessFailed\" xml:space=\"preserve\">\r\n    <value>發佈 '{}' 的安裝程序失敗，結束代碼： {}。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidGuid\" xml:space=\"preserve\">\r\n    <value>無效的 GUID 格式: '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidSection\" xml:space=\"preserve\">\r\n    <value>{}:{} 中的章節名稱無效</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidKey\" xml:space=\"preserve\">\r\n    <value>{}:{} 中的金鑰名稱無效</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigExpected\" xml:space=\"preserve\">\r\n    <value>預期 {}:{} 中有 {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidEscape\" xml:space=\"preserve\">\r\n    <value>無效的逸出字元: {}:{} 中的 '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigUnknownKey\" xml:space=\"preserve\">\r\n    <value>{}:{} 中不明的按鍵 '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidInteger\" xml:space=\"preserve\">\r\n    <value>{}：{} 中索引鍵 '{}' 的整數值 '{}' 無效</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidIp\" xml:space=\"preserve\">\r\n    <value>{}：{} 中機碼 '{}' 的 IP 值 '{}' 無效</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidBoolean\" xml:space=\"preserve\">\r\n    <value>{}：{} 中索引鍵 '{}' 的布爾值 '{}' 無效</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigMacAddress\" xml:space=\"preserve\">\r\n    <value>{}：{} 中索引鍵 '{}' 的 mac 地址 '{}' 無效</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToOpenConfigFile\" xml:space=\"preserve\">\r\n    <value>無法開啟設定檔 {}，{}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWarningDuringStartup\" xml:space=\"preserve\">\r\n    <value>在 WSL 設定期間發生錯誤</value>\r\n  </data>\r\n  <data name=\"MessageSystemdUserSessionFailed\" xml:space=\"preserve\">\r\n    <value>無法啟動 『{}』 的系統用戶會話。如需詳細數據，請參閱 journalctl。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageOpenEventViewer\" xml:space=\"preserve\">\r\n    <value>開啟 EventViewer</value>\r\n  </data>\r\n  <data name=\"MessageDistributionNameNeeded\" xml:space=\"preserve\">\r\n    <value>此發佈不包含預設名稱。使用 --name 來選擇發佈名稱。</value>\r\n    <comment>{Locked=\"--name \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageArgumentsNotValidTogether\" xml:space=\"preserve\">\r\n    <value>無法同時指定 {} 和 {} 自變數。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageArgumentNotValidWithout\" xml:space=\"preserve\">\r\n    <value>引數 {} 需要 {} 引數。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidInstallDistributionName\" xml:space=\"preserve\">\r\n    <value>發佈名稱無效: \"{}\"。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUsingLegacyDistribution\" xml:space=\"preserve\">\r\n    <value>使用舊版散發註冊。請考慮改用 tar 型發佈。</value>\r\n  </data>\r\n  <data name=\"MessageLegacyDistributionVersionArgNotSupported\" xml:space=\"preserve\">\r\n    <value>舊版散發註冊不支援 --version 自變數。</value>\r\n    <comment>{Locked=\"--version \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionOverridden\" xml:space=\"preserve\">\r\n    <value>發佈 \"{}\" 是由覆寫資訊清單所提供。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageHashMismatch\" xml:space=\"preserve\">\r\n    <value>發佈雜湊不相符。預期: {}，實際: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidHexString\" xml:space=\"preserve\">\r\n    <value>不正確的十六進位字串: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNotSupportedOnLegacyDistros\" xml:space=\"preserve\">\r\n    <value>安裝舊版發佈時不支援 『{}』。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionInstalled\" xml:space=\"preserve\">\r\n    <value>已成功安裝發佈。可以透過 『wsl.exe -d {}' 啟動</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidNumberString\" xml:space=\"preserve\">\r\n    <value>{}：{} 中 .wslconfig 專案 '{}' 的記憶體字符串 '{}' 無效</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToCreateSwapVhd\" xml:space=\"preserve\">\r\n    <value>無法在 '{}'： {} 中建立交換磁碟</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNestedVirtualizationNotSupported\" xml:space=\"preserve\">\r\n    <value>此電腦不支援巢狀虛擬化。</value>\r\n  </data>\r\n  <data name=\"MessageFailedToCreateNetworkEndpoint\" xml:space=\"preserve\">\r\n    <value>無法建立位址為 '{}'、已指派新位址為 '{}' 的網络端點</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToCreateNetwork\" xml:space=\"preserve\">\r\n    <value>無法建立位址範圍為 『{}』 的虛擬網路，已建立範圍為 『{}』、{} 的新網路</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSafeModeEnabled\" xml:space=\"preserve\">\r\n    <value>安全模式已啟用 - 許多功能將會停用</value>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedReason\" xml:space=\"preserve\">\r\n    <value>不支援鏡像網路模式： {}。\r\n回到 NAT 網路。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedKernel\" xml:space=\"preserve\">\r\n    <value>需要 Linux 核心 5.10 版或更新版本</value>\r\n  </data>\r\n  <data name=\"MessageMirroredNetworkingNotSupportedWindowsVersion\" xml:space=\"preserve\">\r\n    <value>Windows 版本 {}。{} 沒有所需的功能</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageHyperVFirewallNotSupported\" xml:space=\"preserve\">\r\n    <value>不支援 Hyper-V 防火牆</value>\r\n  </data>\r\n  <data name=\"MessageDnsTunnelingNotSupported\" xml:space=\"preserve\">\r\n    <value>不支援 DNS 通道</value>\r\n  </data>\r\n  <data name=\"MessageLocalhostForwardingNotSupportedMirroredMode\" xml:space=\"preserve\">\r\n    <value>使用鏡像網路模式時，wsl2.localhostForwarding 設定沒有作用</value>\r\n  </data>\r\n  <data name=\"MessageHttpProxyChangeDetected\" xml:space=\"preserve\">\r\n    <value>在主機上偵測到 Http Proxy 變更。請重新開機 WSL 以套用變更。</value>\r\n  </data>\r\n  <data name=\"MessageProxyLocalhostSettingsDropped\" xml:space=\"preserve\">\r\n    <value>偵測到 localhost Proxy 設定，但未鏡像到 WSL。NAT 模式中的 WSL 不支援 localhost Proxy。</value>\r\n  </data>\r\n  <data name=\"MessageProxyV6SettingsDropped\" xml:space=\"preserve\">\r\n    <value>偵測到 IPv6 Proxy 設定，但未鏡像到 WSL。NAT 模式中的 WSL 不支援 IPv6。</value>\r\n  </data>\r\n  <data name=\"MessageProxyLoopbackV6SettingsDropped\" xml:space=\"preserve\">\r\n    <value>偵測到 localhost IPv6 Proxy 設定，但未鏡像到 WSL。WSL 不支援 localhost IPv6 Proxy。</value>\r\n  </data>\r\n  <data name=\"MessageProxyUnexpectedSettingsDropped\" xml:space=\"preserve\">\r\n    <value>嘗試解析 Proxy 設定時發生意外的錯誤，Proxy 設定未鏡像到 WSL。</value>\r\n  </data>\r\n  <data name=\"MessageRegistryError\" xml:space=\"preserve\">\r\n    <value>存取登錄時發生錯誤。路徑： '{}'。錯誤： {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageLocalhostRelayFailed\" xml:space=\"preserve\">\r\n    <value>無法啟動localhost轉送進程。錯誤： {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageVirtualMachinePlatformNotInstalled\" xml:space=\"preserve\">\r\n    <value>無法啟動虛擬網路 - 請執行 ： wsl.exe --install --no-distribution 以安裝選擇性元件 Virtual Machine Platform</value>\r\n    <comment>{Locked=\"--install \"}{Locked=\"--no-distribution\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNetworkInitializationFailedFallback2\" xml:space=\"preserve\">\r\n    <value>無法設定 networkingMode {} 的網络，回到 networkingMode {}。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageOptionalComponentInstallFailed\" xml:space=\"preserve\">\r\n    <value>無法啟用 Windows 元件 '{}' (結束代碼： {})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFatalPluginError\" xml:space=\"preserve\">\r\n    <value>外掛程式 '{}' 傳回嚴重錯誤</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFatalPluginErrorWithMessage\" xml:space=\"preserve\">\r\n    <value>外掛程式 '{}' 傳回嚴重錯誤。錯誤訊息： '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePluginRequiresUpdate\" xml:space=\"preserve\">\r\n    <value>外掛程式 '{}' 需要較新版本的 WSL。請執行： wsl.exe --update</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--update\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSettingOverriddenByPolicy\" xml:space=\"preserve\">\r\n    <value>計算機原則已停用 .wslconfig 設定 『{}』。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDebugShellDisabled\" xml:space=\"preserve\">\r\n    <value>電腦原則已停用偵錯殼層。</value>\r\n  </data>\r\n  <data name=\"MessageWSLMountDisabled\" xml:space=\"preserve\">\r\n    <value>wsl.exe --mount 已由電腦原則停用。</value>\r\n    <comment>{Locked=\"--mount \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWSL1Disabled\" xml:space=\"preserve\">\r\n    <value>WSL1 已由電腦原則停用。</value>\r\n  </data>\r\n  <data name=\"MessageUpgradeToWSL2\" xml:space=\"preserve\">\r\n    <value>請執行 'wsl.exe --set-version {} 2' 以升級至 WSL2。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}{Locked=\"--set-version \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFinishMsiInstallation\" xml:space=\"preserve\">\r\n    <value>WSL 正在完成升級...</value>\r\n  </data>\r\n  <data name=\"MessageUpdateFailed\" xml:space=\"preserve\">\r\n    <value>更新失敗 (結束代碼: {})。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUninstallFailed\" xml:space=\"preserve\">\r\n    <value>解除安裝失敗 (結束代碼: {})。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSeeLogFile\" xml:space=\"preserve\">\r\n    <value>記錄檔: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToRemoveMsix\" xml:space=\"preserve\">\r\n    <value>無法移除 MSIX 套件 (錯誤： {})。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagedFailedToInstallMsix\" xml:space=\"preserve\">\r\n    <value>無法安裝 MSIX 套件 (錯誤： {})。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageMissingOptionalComponents\" xml:space=\"preserve\">\r\n    <value>未安裝執行 WSL 所需的選用元件。</value>\r\n  </data>\r\n  <data name=\"MessageInstallMissingOptionalComponents\" xml:space=\"preserve\">\r\n    <value>安裝遺失的元件。</value>\r\n  </data>\r\n  <data name=\"MessageInvalidDistributionTar\" xml:space=\"preserve\">\r\n    <value>匯入的檔案不是有效的 Linux 發佈。</value>\r\n  </data>\r\n  <data name=\"MessagePressAnyKeyToExit\" xml:space=\"preserve\">\r\n    <value>按任意鍵離開...</value>\r\n  </data>\r\n  <data name=\"MessageNewWslVersionAvailable\" xml:space=\"preserve\">\r\n    <value>有新版的 Windows 子系統 Linux 版可供使用。</value>\r\n  </data>\r\n  <data name=\"MessageUpdateToVersion\" xml:space=\"preserve\">\r\n    <value>更新至版本 {} 或在下方檢視其版本資訊。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageUpdate\" xml:space=\"preserve\">\r\n    <value>更新</value>\r\n  </data>\r\n  <data name=\"MessageViewReleaseNotes\" xml:space=\"preserve\">\r\n    <value>查看文件</value>\r\n  </data>\r\n  <data name=\"MessageVhdInUse\" xml:space=\"preserve\">\r\n    <value>無法完成作業，因為 VHD 目前正在使用中。若要強制 WSL 停止使用： wsl.exe --shutdown</value>\r\n    <comment>{Locked=\"--shutdown\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidBoolean\" xml:space=\"preserve\">\r\n    <value>{} 不是有效的布林值，&lt;true|false&gt;</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageSparseVhdWsl2Only\" xml:space=\"preserve\">\r\n    <value>只有 WSL2 支援稀疏 VHD。</value>\r\n  </data>\r\n  <data name=\"MessageLocalSystemNotSupported\" xml:space=\"preserve\">\r\n    <value>系統不支援將 WSL 執行為本機系統。</value>\r\n  </data>\r\n  <data name=\"MessagePerformanceTip\" xml:space=\"preserve\">\r\n    <value>效能提示:</value>\r\n  </data>\r\n  <data name=\"MessageProblematicDrvFsUsage\" xml:space=\"preserve\">\r\n    <value>在您的 Windows 磁碟驅動器上使用如 {} 的 I/O 密集型操作，效能將會不佳。請考慮將項目檔移至 Linux 檔案系統，以獲得較佳的效能。按兩下下方以深入瞭解。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageViewDocs\" xml:space=\"preserve\">\r\n    <value>檢視文件</value>\r\n  </data>\r\n  <data name=\"MessageDontShowAgain\" xml:space=\"preserve\">\r\n    <value>不再顯示</value>\r\n  </data>\r\n  <data name=\"MessageWSL2Crashed\" xml:space=\"preserve\">\r\n    <value>WSL2 虛擬機器已損毀。</value>\r\n  </data>\r\n  <data name=\"MessageWSL2CrashedStackTrace\" xml:space=\"preserve\">\r\n    <value>堆棧追蹤已儲存於： {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistributionFailedToStart\" xml:space=\"preserve\">\r\n    <value>發佈無法啟動。錯誤碼： {}，失敗步驟： {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDiskCorrupted\" xml:space=\"preserve\">\r\n    <value>無法啟動分佈，因為其虛擬磁碟已損毀。</value>\r\n  </data>\r\n  <data name=\"MessageConfigKeyDuplicated\" xml:space=\"preserve\">\r\n    <value>{}：{} 中的重複設定索引鍵 '{}' (衝突密鑰： {}：{}) 中的 '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageConfigInvalidEnum\" xml:space=\"preserve\">\r\n    <value>{}：{} 中設定索引鍵 '{}' 的值 '{}' 無效 (有效值： {})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageWaitingForOobe\" xml:space=\"preserve\">\r\n    <value>正在等候 OOBE 命令完成，以進行散發「{}」...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageCorruptedDistroRegistration\" xml:space=\"preserve\">\r\n    <value>無法從散發 {} 讀取屬性 '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessagePassVhdFlag\" xml:space=\"preserve\">\r\n    <value>這看起來像一個 VHD 文件。使用 --vhd 匯入 VHD 而不是 tar。</value>\r\n    <comment>{Locked=\"--vhd \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDistroStoreInstallFailed\" xml:space=\"preserve\">\r\n    <value>無法從Microsoft Store安裝 {}： {}\r\n正在嘗試 Web 下載...</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageNoInstallDefault\" xml:space=\"preserve\">\r\n    <value>尚未設定預設發佈。請提供要安裝的發佈。</value>\r\n  </data>\r\n  <data name=\"MessageConfigVirtio9pDisabled\" xml:space=\"preserve\">\r\n    <value>已停用 wsl2.virtio9p，退回到使用 vsock 傳輸的 9p。</value>\r\n  </data>\r\n  <data name=\"MessageInstallationCorrupted\" xml:space=\"preserve\">\r\n    <value>WSL 安裝似乎已損毀，(錯誤碼： {})。\r\n按任意鍵以修復 WSL，或 CTRL-C 取消。\r\n此提示將在 60 秒內逾時。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToParseTerminalProfile\" xml:space=\"preserve\">\r\n    <value>註冊發佈時無法剖析終端機配置檔： {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidSize\" xml:space=\"preserve\">\r\n    <value>無效的大小: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageErrorCode\" xml:space=\"preserve\">\r\n    <value>{}\r\n錯誤碼: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageInvalidJson\" xml:space=\"preserve\">\r\n    <value>無效的 JSON 文件。剖析錯誤: {}</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageTooManyProcessors\" xml:space=\"preserve\">\r\n    <value>wsl2.processors 不能超過系統上的邏輯處理器數目 ({} &gt; {})</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFailedToQueryNetworkingMode\" xml:space=\"preserve\">\r\n    <value>無法查詢網路模式</value>\r\n  </data>\r\n  <data name=\"MessageMismatchedKernelModulesError\" xml:space=\"preserve\">\r\n    <value>已提供自定義核心模組，但未指定自定義核心。如需詳細資訊，請參閱 https://aka.ms/wslcustomkernel。</value>\r\n  </data>\r\n  <data name=\"MessageDnsTunnelingDisabled\" xml:space=\"preserve\">\r\n    <value>由於目前與全球安全存取用戶端的相容性問題，已停用 DNS 通道。</value>\r\n  </data>\r\n  <data name=\"MessageSparseVhdDisabled\" xml:space=\"preserve\">\r\n    <value>由於可能會導致資料損毀，目前已停用稀疏 VHD 支援。\r\n若要強制發行版本以使用稀疏 VHD，請執行:\r\nwsl.exe --manage &lt;DistributionName&gt; --set-sparse true --allow-unsafe</value>\r\n    <comment>{Locked=\"--manage \"}{Locked=\"--set-sparse \"}{Locked=\"--allow-unsafe\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageDrvfsMountFailed\" xml:space=\"preserve\">\r\n    <value>無法掛接 {}，如需詳細數據，請參閱 dmesg。</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"MessageFstabMountFailed\" xml:space=\"preserve\">\r\n    <value>處理掛載 -a 的 /etc/fstab 失敗。</value>\r\n  </data>\r\n  <data name=\"MessageReadOnlyDistro\" xml:space=\"preserve\">\r\n    <value>掛接散發磁碟時發生錯誤，它是以唯讀方式掛接為遞補。\r\n請參閱復原指示： https://aka.ms/wsldiskmountrecovery</value>\r\n  </data>\r\n  <data name=\"MessageFailedToTranslate\" xml:space=\"preserve\">\r\n    <value>無法翻譯 '{}'</value>\r\n    <comment>{FixedPlaceholder=\"{}\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_ErrorTryAgainLater.Text\" xml:space=\"preserve\">\r\n    <value>發生錯誤。請稍後再試一次。</value>\r\n  </data>\r\n  <data name=\"Settings_AboutPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>關於</value>\r\n  </data>\r\n  <data name=\"Settings_About.Header\" xml:space=\"preserve\">\r\n    <value>關於 Windows 子系統 Linux 版設定</value>\r\n  </data>\r\n  <data name=\"Settings_About.Description\" xml:space=\"preserve\">\r\n    <value>© Microsoft.著作權所有，並保留一切權利。</value>\r\n  </data>\r\n  <data name=\"Settings_AppDisplayName\" xml:space=\"preserve\">\r\n    <value>適用於 Linux 的 Windows 子系統設定</value>\r\n  </data>\r\n  <data name=\"Settings_AppDescription\" xml:space=\"preserve\">\r\n    <value>Windows 子系統 Linux 版 設定可讓開發人員使用 GUI 應用程式來管理 .wslconfig 檔案。</value>\r\n    <comment>{Locked=\".wslconfig\"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaim.Header\" xml:space=\"preserve\">\r\n    <value>自動記憶體回收</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaim.Description\" xml:space=\"preserve\">\r\n    <value>偵測到閑置 CPU 使用量後，自動釋放快取的記憶體。設定為緩時釋放的漸進式，以及快取記憶體的即時釋放投遞。</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>自動記憶體回收。</value>\r\n  </data>\r\n  <data name=\"Settings_AutoMemoryReclaimComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>偵測到閑置 CPU 使用量後，自動釋放快取的記憶體。設定為緩時釋放的漸進式，以及快取記憶體的即時釋放投遞。</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxy.Header\" xml:space=\"preserve\">\r\n    <value>已啟用自動 Proxy</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxy.Description\" xml:space=\"preserve\">\r\n    <value>讓 WSL 使用 Windows 的 HTTP Proxy 資訊。</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>已啟用自動 Proxy。</value>\r\n  </data>\r\n  <data name=\"Settings_AutoProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>讓 WSL 使用 Windows 的 HTTP Proxy 資訊。</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNS.Header\" xml:space=\"preserve\">\r\n    <value>使用最佳作業效果的 DNS 剖析</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNS.Description\" xml:space=\"preserve\">\r\n    <value>僅適用於 wsl2.dnsTunneling 設為 true 時。設定為 true 時，Windows 會從 DNS 要求擷取問題，並嘗試加以解決，並忽略未知的記錄。</value>\r\n    <comment>{Locked=\"wsl2.dnsTunneling\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>使用最佳作用的 DNS 剖析。</value>\r\n  </data>\r\n  <data name=\"Settings_BestEffortDNSToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>僅適用於 wsl2.dnsTunneling 設為 true 時。設定為 true 時，Windows 會從 DNS 要求擷取問題，並嘗試加以解決，並忽略未知的記錄。</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPath.Header\" xml:space=\"preserve\">\r\n    <value>自訂核心</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPath.Description\" xml:space=\"preserve\">\r\n    <value>自訂Linux核心的絕對 Windows 路徑。</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>瀏覽核心</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>自訂核心</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>自訂Linux核心的絕對 Windows 路徑。</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPath.Header\" xml:space=\"preserve\">\r\n    <value>自訂核心模組</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPath.Description\" xml:space=\"preserve\">\r\n    <value>自訂Linux核心模組 VHD 的絕對 Windows 路徑。</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>瀏覽核心模組</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>自訂核心模組</value>\r\n  </data>\r\n  <data name=\"Settings_CustomKernelModulesPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>自訂Linux核心模組 VHD 的絕對 Windows 路徑。</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPath.Header\" xml:space=\"preserve\">\r\n    <value>自訂系統發行版</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>瀏覽發行套件</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathDescription.Text\" xml:space=\"preserve\">\r\n    <value>指定要載入為自訂系統發行版的 VHD 路徑，主要用於在 WSL 中啟用 GUI 應用程式。[在此深入了解系統發行]。</value>\r\n    <comment>{Locked=\"[\"}{Locked=\"]\"}The text in between the delimiters [ ] is made into a hyperlink in code, and the delimiters are removed</comment>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathDescription.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgsystemdistro</value>\r\n    <comment>{Locked}Uri to the Microsoft WSLg architecture devblog</comment>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>自訂系統發行版</value>\r\n  </data>\r\n  <data name=\"Settings_CustomSystemDistroPathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>指定要載入為自訂系統發行版的 VHD 路徑，主要用於在 WSL 中啟用 GUI 應用程式。</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsole.Header\" xml:space=\"preserve\">\r\n    <value>啟用偵錯主控台</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsole.Description\" xml:space=\"preserve\">\r\n    <value>布林值，可開啟輸出控制台視窗，該視窗會在啟動 WSL 2 發行版執行個體時顯示 dmesg 的內容。</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>啟用偵錯主控台。</value>\r\n  </data>\r\n  <data name=\"Settings_DebugConsoleToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>布爾值，可開啟輸出控制台視窗，該視窗會在啟動 WSL 2 發行例項時顯示診斷訊息的內容。</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSize.Header\" xml:space=\"preserve\">\r\n    <value>預設 VHD 大小</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSize.Description\" xml:space=\"preserve\">\r\n    <value>僅新建立之發佈之可擴充 WSL 虛擬硬碟 (VHD) 的預設大小上限。</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>重設大小</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>預設 VHD 大小</value>\r\n  </data>\r\n  <data name=\"Settings_DefaultVHDSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>只針對新建的發佈指定可擴充 WSL 虛擬硬碟 (VHD) 的預設大小上限，以 MB 為單位。</value>\r\n  </data>\r\n  <data name=\"Settings_DeveloperPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>開發人員</value>\r\n  </data>\r\n  <data name=\"Settings_DocumentationLink.Content\" xml:space=\"preserve\">\r\n    <value>文件</value>\r\n  </data>\r\n  <data name=\"Settings_DocumentationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocs</value>\r\n    <comment>{Locked}Uri to Microsoft WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_DrvFSMode.Header\" xml:space=\"preserve\">\r\n    <value>變更 DrvFS 模式</value>\r\n  </data>\r\n  <data name=\"Settings_DrvFSMode.Description\" xml:space=\"preserve\">\r\n    <value>變更 WSL 中的跨作業系統檔案存取實作。</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxy.Header\" xml:space=\"preserve\">\r\n    <value>已啟用 DNS Proxy</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxy.Description\" xml:space=\"preserve\">\r\n    <value>只有在 wsl2.networkingMode 設為 NAT 時才適用。布爾值會通知 WSL 將 Linux 中的 DNS 伺服器設定為主機上的 NAT。設定為 false 會將 DNS 伺服器從 Windows 鏡像到 Linux。</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>已啟用 DNS Proxy。</value>\r\n  </data>\r\n  <data name=\"Settings_DNSProxyToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>只有在 wsl2.networkingMode 設定為 NAT 時才適用。布爾值會通知 WSL 將 Linux 中的 DNS 伺服器設定為主機上的 NAT。設定為 false 會將 DNS 伺服器從 Windows 鏡像到 Linux。</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunneling.Header\" xml:space=\"preserve\">\r\n    <value>已啟用 DNS 通道</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunneling.Description\" xml:space=\"preserve\">\r\n    <value>變更從 WSL 到 Windows 進行 DNS 要求的 Proxy 處理方式。</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>已啟用 DNS 通道。</value>\r\n  </data>\r\n  <data name=\"Settings_DNSTunnelingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>變更從 WSL 到 Windows 進行 DNS 要求的 Proxy 處理方式。</value>\r\n  </data>\r\n  <data name=\"Settings_FileSystemPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>檔案系統</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplications.Header\" xml:space=\"preserve\">\r\n    <value>啟用 GUI 應用程式</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsDescription.Text\" xml:space=\"preserve\">\r\n    <value>用於開啟或關閉 GUI 應用程式支援 ([WSLg]) WSL 的布爾值。</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsDescription.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgproject</value>\r\n    <comment>{Locked}Uri to the Microsoft GitHub WSLg project</comment>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>啟用 GUI 應用程式。</value>\r\n  </data>\r\n  <data name=\"Settings_GUIApplicationsToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>用於開啟或關閉 GUI 應用程式支援 (在 WSL 中稱為 WSL g) 的布林值。</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCounters.Header\" xml:space=\"preserve\">\r\n    <value>啟用硬體效能計數器</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCounters.Description\" xml:space=\"preserve\">\r\n    <value>啟用 Linux 的硬體效能計數器。</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>啟用硬體效能計數器。</value>\r\n  </data>\r\n  <data name=\"Settings_HWPerfCountersToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>啟用 Linux 的硬體效能計數器。</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewall.Header\" xml:space=\"preserve\">\r\n    <value>已啟用 Hyper-V 防火牆</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewall.Description\" xml:space=\"preserve\">\r\n    <value>啟用 Hyper-V 防火牆，允許 Windows 防火牆規則以及 Hyper-V 流量專用的規則，以篩選 WSL 網路流量。</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>已啟用 Hyper-V 防火牆。</value>\r\n  </data>\r\n  <data name=\"Settings_HyperVFirewallToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>啟用 Hyper-V 防火牆，允許 Windows 防火牆規則以及 Hyper-V 流量專用的規則，以篩選 WSL 網路流量。</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopback.Header\" xml:space=\"preserve\">\r\n    <value>主機位址回送</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopback.Description\" xml:space=\"preserve\">\r\n    <value>只有在 wsl2.networkingMode 設為鏡像時才適用。設定為 True 時，將允許容器透過指派給主機的 IP 位址連線到主機，或允許主機連線到容器。請注意，一律可以使用 127.0.0.1 回送位址 - 此選項也允許使用所有額外指派的本機 IP 位址。</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>主機位址回送。</value>\r\n  </data>\r\n  <data name=\"Settings_HostAddressLoopbackToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>僅適用於 wsl2.networkingMode 設為鏡像時。設定為 True 時，將允許容器透過指派給主機的 IP 位址連線到主機，或允許主機連線到容器。請注意，一律可以使用 127.0.0.1 回送位址 - 此選項也允許使用所有額外指派的本機 IP 位址。</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPorts.Header\" xml:space=\"preserve\">\r\n    <value>已忽略的連接埠</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPorts.Description\" xml:space=\"preserve\">\r\n    <value>只有在 wsl2.networkingMode 設為鏡像時才適用。指定 Linux 應用程式可以繫結到哪些埠，而這些埠不會自動轉送，也不會在 Windows 中納入考慮。格式應為逗號分隔清單，例如： 3000,9000,9090。</value>\r\n    <comment>{Locked=\"wsl2.networkingMode\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsResetButton.Content\" xml:space=\"preserve\">\r\n    <value>重設連接埠</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>已忽略的連接埠</value>\r\n  </data>\r\n  <data name=\"Settings_IgnoredPortsTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>僅適用於 wsl2.networkingMode 設為鏡像時。指定 Linux 應用程式可以繫結到哪些埠，而這些埠不會自動轉送，也不會在 Windows 中納入考慮。應以逗號分隔清單格式化，例如 3000、9000、9090。</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeout.Header\" xml:space=\"preserve\">\r\n    <value>初始自動 Proxy 逾時</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeout.Description\" xml:space=\"preserve\">\r\n    <value>僅適用於 wsl2.autoProxy 設為 true 時。設定 WSL 啟動 WSL 容器時，WSL 等候擷取 HTTP Proxy 資訊的時間) (毫秒。如果在此時間之後解析 Proxy 設定，則必須重新啟動 WSL 實例，才能使用擷取的 Proxy 設定。</value>\r\n    <comment>{Locked=\"wsl2.autoProxy\"}.wslconfig property key names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutResetButton.Content\" xml:space=\"preserve\">\r\n    <value>重設逾時</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>初始自動 Proxy 逾時</value>\r\n  </data>\r\n  <data name=\"Settings_InitialAutoProxyTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>僅適用於 wsl2.autoProxy 設為 true 時。設定啟動 WSL 容器時，WSL 將等候擷取 HTTP Proxy 資訊的時間，以毫秒為單位。如果在此時間之後解析 Proxy 設定，則必須重新啟動 WSL 實例，才能使用擷取的 Proxy 設定。</value>\r\n  </data>\r\n  <data name=\"Settings_KernelCommandLine.Header\" xml:space=\"preserve\">\r\n    <value>核心命令列</value>\r\n  </data>\r\n  <data name=\"Settings_KernelCommandLine.Description\" xml:space=\"preserve\">\r\n    <value>其他核心命令列引數。</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwarding.Header\" xml:space=\"preserve\">\r\n    <value>啟用 localhost 轉送</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwarding.Description\" xml:space=\"preserve\">\r\n    <value>布林值，指定繫結至 WSL 2 VM 中萬用字元或 localhost 的連接埠是否應可透過 localhost:port 從主機連結。</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>啟用 localhost 轉送。</value>\r\n  </data>\r\n  <data name=\"Settings_LocalhostForwardingToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>布林值，指定繫結至 WSL 2 VM 中萬用字元或 localhost 的連接埠是否應可透過 localhost、冒號、連接埠從主機連結。</value>\r\n  </data>\r\n  <data name=\"Settings_MegabyteStringFormat\" xml:space=\"preserve\">\r\n    <value>{0} MB</value>\r\n  </data>\r\n  <data name=\"Settings_MemAndProcPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>記憶體與處理器</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySize.Header\" xml:space=\"preserve\">\r\n    <value>記憶體大小</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySize.Description\" xml:space=\"preserve\">\r\n    <value>要指派給 WSL 2 VM 的記憶體。</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>重設大小</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>記憶體大小</value>\r\n  </data>\r\n  <data name=\"Settings_MemorySizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>要指派給 WSL 2 VM 的記憶體指定量，以 MB 為單位。</value>\r\n  </data>\r\n  <data name=\"Settings_MillisecondsStringFormat\" xml:space=\"preserve\">\r\n    <value>{0} 毫秒</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualization.Header\" xml:space=\"preserve\">\r\n    <value>啟用巢狀虛擬化</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualization.Description\" xml:space=\"preserve\">\r\n    <value>開啟或關閉巢狀虛擬化的布爾值，可讓其他巢狀 VM 在 WSL 2 內執行。</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>已啟用巢狀虛擬化。</value>\r\n  </data>\r\n  <data name=\"Settings_NestedVirtualizationToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>開啟或關閉巢狀虛擬化的布爾值，可讓其他巢狀 VM 在 WSL 2 內執行。</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingMode.Header\" xml:space=\"preserve\">\r\n    <value>網路模式</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingMode.Description\" xml:space=\"preserve\">\r\n    <value>指定 WSL 的網路模式。</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>網路模式。</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingModeComboBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>指定 WSL 的網路模式。</value>\r\n  </data>\r\n  <data name=\"Settings_NetworkingPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>網路功能</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess.Description\" xml:space=\"preserve\">\r\n    <value>您可以跨作業系統處理所有檔案!</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess.Title\" xml:space=\"preserve\">\r\n    <value>跨 OS 存取檔案</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle.Text\" xml:space=\"preserve\">\r\n    <value>從 Linux 存取 Windows 檔案</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample.Text\" xml:space=\"preserve\">\r\n    <value>您可以依序瀏覽至 ‘/mnt’ 與 Windows 磁碟機代號 (如範例所示的 C 磁碟機)，藉以在 Linux 中存取 Windows 檔案:\r\n\r\n'cd /mnt/c'</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle.Text\" xml:space=\"preserve\">\r\n    <value>使用檔案總管存取 Linux 檔案</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample.Text\" xml:space=\"preserve\">\r\n    <value>您可以瀏覽至 '\\\\wsl.localhost\\' 按一下 ‘Linux’ 圖示，從檔案總管檢視 Linux 檔案。</value>\r\n    <comment>{Locked=\"\\\\wsl.localhost\\\"}Url addresses should not be translated.</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle.Text\" xml:space=\"preserve\">\r\n    <value>從 WSL 啟動 Windows 檔案和程式</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample.Text\" xml:space=\"preserve\">\r\n    <value>即使在使用 WSL 時，您也可以直接從 bash 執行 Windows 可執行檔。請嘗試執行\r\n'powershell.exe /c start .'，以在目前的資料夾中開啟檔案總管。</value>\r\n    <comment>{Locked=\"/c start .\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle.Text\" xml:space=\"preserve\">\r\n    <value>從 Windows 存取 Linux 網路應用程式</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.Text\" xml:space=\"preserve\">\r\n    <value>如果您是在 Linux 發行版本中建置網路應用程式 (例如在 NodeJS 或 SQL 伺服器上執行的應用程式)，可以使用 localhost (方式與平常相同) 透過 Windows 應用程式 (例如 Edge 或 Chrome 網際網路瀏覽器) 加以存取。這代表如果您啟動接聽連接埠 3000 的 Linux 伺服器，即可透過 Windows 上的 Edge 前往 [http://localhost:3000] 加以存取。</value>\r\n    <comment>{Locked=\"http://localhost:3000\"}Url addresses should not be translated.</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample.NavigateUri\" xml:space=\"preserve\">\r\n    <value>http://localhost:3000</value>\r\n    <comment>{Locked}Uri to the localhost on port 3000</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle.Text\" xml:space=\"preserve\">\r\n    <value>鏡像模式網路功能</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample.Text\" xml:space=\"preserve\">\r\n    <value>WSL 也包含稱為「鏡像模式」的新網路模式，加入了 IPv6 支援等進階功能，並且能夠存取區域網路中的網路應用程式。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccessLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解如何跨 OS 存取檔案</value>\r\n  </data>\r\n  <data name=\"Settings_OOBECrossOSFileAccessLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslfilesystems</value>\r\n    <comment>{Locked}Uri to the Working across Windows and Linux file systems documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDisplayName\" xml:space=\"preserve\">\r\n    <value>歡迎使用 Windows 子系統 Linux 版</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement.Description\" xml:space=\"preserve\">\r\n   <value>WSL 非常適合用來嘗試不同的 Linux 發行版本。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement.Title\" xml:space=\"preserve\">\r\n    <value>發行版本管理</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解基本 WSL 命令</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementBasicWSLCommandsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslcommands</value>\r\n    <comment>{Locked}Uri to the basic WSL commands documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解如何匯入任何 Linux 發行版本</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagementImportCustomDistroLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslcustomdistro</value>\r\n    <comment>{Locked}Uri to the import any linux distribution to use with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>列出可安裝的 WSL 發行版本命令</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallableDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>'wsl.exe -l -o'</value>\r\n    <comment>{Locked=\"-l -o\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>安裝具名 WSL 發行版本命令</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>'wsl.exe --install &lt;DistroName&gt;'</value>\r\n    <comment>{Locked=\"--install \"}Command line arguments, file names and string inserts should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosTitle.Text\" xml:space=\"preserve\">\r\n    <value>列出可用的 WSL 發行版本命令</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDistroManagement_AvailableDistrosSample.Text\" xml:space=\"preserve\">\r\n    <value>'wsl.exe -l'</value>\r\n    <comment>{Locked=\"-l\"}Command line arguments and file names should not be translated</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Description\" xml:space=\"preserve\">\r\n    <value>Docker Desktop 非常適合與 WSL 搭配使用，可協助您利用 Linux 容器進行開發。\r\n\r\n搭配使用 Docker Desktop 與 WSL 的優點有:\r\n\r\n• 您可以使用相同的 Docker 精靈和映像，在 WSL 或 Windows 中執行 Docker 命令。\r\n• 您可以使用 WSL 中的 Windows 磁碟自動掛接，在 Windows 和 Linux 之間平順無礙地分享檔案和資料夾。\r\n• 您可以透過 WSL 的互通性，使用偏好的 Windows 工具和編輯器處理 Linux 程式碼和檔案，反之亦然。</value>\r\n </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegration.Title\" xml:space=\"preserve\">\r\n    <value>Docker Desktop 整合</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解如何搭配使用 WSL 與 Docker</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEDockerDesktopIntegrationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocker</value>\r\n    <comment>{Locked}Uri to the Docker remote containers on WSL 2 documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral.Description\" xml:space=\"preserve\">\r\n    <value>您可利用 Windows 子系統 Linux 版 (WSL)，直接在 Windows 中執行偏好的 Linux 工具，公用程式、應用程式和工作流程。\r\n\r\n請花一些時間預覽最受社群喜愛的一些功能，或查看我們提供的詳盡文件。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral.Title\" xml:space=\"preserve\">\r\n    <value>歡迎使用 WSL</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.Content\" xml:space=\"preserve\">\r\n    <value>設定最佳做法</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_BestPracticesSetupLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslsetup</value>\r\n    <comment>{Locked}Uri to the Best Practices for Setup Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.Content\" xml:space=\"preserve\">\r\n    <value>開始使用 Linux</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_GettingStartedLinuxLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgettingstarted</value>\r\n    <comment>{Locked}Uri to the Getting Started with Linux Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.Content\" xml:space=\"preserve\">\r\n    <value>Windows 子系統 Linux 版 (WSL) 文件</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGeneral_WSLDocumentationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wsldocs</value>\r\n    <comment>{Locked}Uri to the Microsoft WSL Documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps.Description\" xml:space=\"preserve\">\r\n    <value>您可以透過原生 Windows 互動使用圖形化 Linux 應用程式，例如 alt-tab、啟動 [開始] 功能表、工作列、釘選，以及剪下貼上功能。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps.Title\" xml:space=\"preserve\">\r\n    <value>GUI 應用程式</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAcceleration.Description\" xml:space=\"preserve\">\r\n    <value>WSL 可將 Windows GPU 用於機器學習工作流程\r\n\r\n在 WSL 中執行的 Linux 二進位檔案可自動使用 Windows GPU，以加快效能速度。您只需使用與一般 Linux 機器相同的操作方式，安裝並執行這些工作流程即可。您可以利用許多不同方式開始使用，包含在 Docker 容器中執行 CUDA (如有 NVIDIA 顯示卡)，一直到在 AMD、Intel 或 NVIDIA 顯示卡上使用 DirectML 執行 PyTorch 或 TensorFlow。請參閱下方的入門指南深入了解。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAcceleration.Title\" xml:space=\"preserve\">\r\n    <value>GPU 加速</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAccelerationLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解 GPU 加速</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGPUAccelerationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslgpu</value>\r\n    <comment>{Locked}Uri to the GPU acceleration for ML in WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIAppsLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解 WSL GUI 應用程式</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIAppsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslguiapps</value>\r\n    <comment>{Locked}Uri to the Run GUI apps on the WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEGUIApps_AppsListDescription.Text\" xml:space=\"preserve\">\r\n    <value>以下列出可嘗試的應用程式 (您可以鍵入 'sudo apt install &lt;應用程式名稱&gt;`，藉此在 Ubuntu 中安裝所有這些應用程式)\r\n\r\n    • gedit – 基本文字編輯器\r\n    • audacity – 錄製及編輯音訊檔案\r\n    • blender – 製作 3D 動畫和視覺效果\r\n    • gimp – 編輯相片\r\n    • nautilus – Linux 檔案總管\r\n    • vlc – 影片播放器</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegration.Description\" xml:space=\"preserve\">\r\n    <value>您可以跨 Windows 和 Linux 作業系統，輕鬆存取網路應用程式。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegration.Title\" xml:space=\"preserve\">\r\n    <value>網路整合</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解網路應用程式</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslnetworking</value>\r\n    <comment>{Locked}Uri to the Accessing network applications with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解鏡像模式網路功能</value>\r\n  </data>\r\n  <data name=\"Settings_OOBENetworkingIntegrationMirroredModeLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslmirroredmode</value>\r\n    <comment>{Locked}Uri to the WSL Mirrored Mode networking documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration.Description\" xml:space=\"preserve\">\r\n   <value>您可以直接從 VS Code 完全使用 WSL 作為開發環境。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration.Title\" xml:space=\"preserve\">\r\n    <value>VS Code 整合</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解如何搭配使用 WSL 與 VS Code</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegrationLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslvscode</value>\r\n    <comment>{Locked}Uri to the using Visual Studio Code with WSL documentation</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle.Text\" xml:space=\"preserve\">\r\n    <value>安裝方式</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample.Text\" xml:space=\"preserve\">\r\n    <value>安裝 VS Code 之後，即可從 Windows 終端機安裝遠端 WSL 延伸模組:\r\n\r\n'code –install-extension ms-vscode-remote.remote-wsl'</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle.Text\" xml:space=\"preserve\">\r\n    <value>使用 Visual Studio Code 開啟 WSL 專案</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample.Text\" xml:space=\"preserve\">\r\n    <value>如要透過 WSL 發行版本使用 VS Code 開啟專案，請開啟該發行版本的命令列，然後執行 'code .' 來開啟專案檔案。\r\n\r\n您也可以透過 VS Code 本身的命令選擇區，使用更多 VS Code 遠端選項。在鍵盤上按 “SHIFT+CTRL+P” 開啟命令選擇區，然後鍵入 ‘Remote-WSL’ 來查看可用的 VS Code 遠端選項清單。這樣做可讓您在遠端工作階段中重新開啟資料夾，並指定要開啟的發行版本等。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBE_HowToUse.Text\" xml:space=\"preserve\">\r\n    <value>如何使用</value>\r\n  </data>\r\n  <data name=\"Settings_OptionalFeaturesPageTitle.Text\" xml:space=\"preserve\">\r\n    <value>選用功能</value>\r\n  </data>\r\n  <data name=\"Settings_PrivacyPolicyLink.Content\" xml:space=\"preserve\">\r\n    <value>隱私權聲明</value>\r\n  </data>\r\n  <data name=\"Settings_PrivacyPolicyLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://go.microsoft.com/fwlink/?linkid=2234882</value>\r\n    <comment>{Locked}Uri to the Microsoft Privacy Policy</comment>\r\n  </data>\r\n  <data name=\"Settings_ProcCount.Header\" xml:space=\"preserve\">\r\n    <value>處理器計數</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCount.Description\" xml:space=\"preserve\">\r\n    <value>要指派給WSL 2 VM 的邏輯處理器數目。</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountResetButton.Content\" xml:space=\"preserve\">\r\n    <value>重設計數</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>處理器計數</value>\r\n  </data>\r\n  <data name=\"Settings_ProcCountTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>要指派給WSL 2 VM 的邏輯處理器數目。</value>\r\n  </data>\r\n  <data name=\"Settings_RelatedLinks.Text\" xml:space=\"preserve\">\r\n    <value>相關連結</value>\r\n  </data>\r\n  <data name=\"Settings_ReleaseNotesLink.Content\" xml:space=\"preserve\">\r\n    <value>版本資訊</value>\r\n  </data>\r\n  <data name=\"Settings_ReleaseNotesLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslreleases</value>\r\n    <comment>{Locked}Uri to Microsoft WSL release notes</comment>\r\n  </data>\r\n  <data name=\"Settings_SafeMode.Header\" xml:space=\"preserve\">\r\n    <value>啟用安全模式</value>\r\n  </data>\r\n  <data name=\"Settings_SafeMode.Description\" xml:space=\"preserve\">\r\n    <value>以「安全模式」執行 WSL，此模式會停用許多功能，並用於復原處於錯誤狀態的發佈。</value>\r\n  </data>\r\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>啟用安全模式。</value>\r\n  </data>\r\n  <data name=\"Settings_SafeModeToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>以「安全模式」執行 WSL，此模式會停用許多功能，並用於復原處於錯誤狀態的發佈。</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_About.Content\" xml:space=\"preserve\">\r\n    <value>關於</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Developer.Content\" xml:space=\"preserve\">\r\n    <value>開發人員</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_DistroManagement.Content\" xml:space=\"preserve\">\r\n    <value>Distro 管理</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_DockerDesktopIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Docker Desktop 整合</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_FileSystem.Content\" xml:space=\"preserve\">\r\n    <value>檔案系統</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_General.Content\" xml:space=\"preserve\">\r\n    <value>一般</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_GPUAcceleration.Content\" xml:space=\"preserve\">\r\n    <value>GPU 加速</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_GUIApps.Content\" xml:space=\"preserve\">\r\n    <value>GUI 應用程式</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_LaunchWSL.Content\" xml:space=\"preserve\">\r\n    <value>啟動 wsl.exe</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_MemAndProc.Content\" xml:space=\"preserve\">\r\n    <value>記憶體與處理器</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Networking.Content\" xml:space=\"preserve\">\r\n    <value>網路功能</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_NetworkingIntegration.Content\" xml:space=\"preserve\">\r\n    <value>網路整合</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_OOBE.Content\" xml:space=\"preserve\">\r\n    <value>歡迎使用 WSL</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_OptionalFeatures.Content\" xml:space=\"preserve\">\r\n    <value>選用功能</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_Settings.Content\" xml:space=\"preserve\">\r\n    <value>設定</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_VSCodeIntegration.Content\" xml:space=\"preserve\">\r\n    <value>VS Code 整合</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_WhatsNew.Content\" xml:space=\"preserve\">\r\n    <value>新增功能</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_WorkingAcrossFileSystems.Content\" xml:space=\"preserve\">\r\n    <value>跨檔案系統工作</value>\r\n  </data>\r\n  <data name=\"Settings_IssuesLink.Content\" xml:space=\"preserve\">\r\n    <value>問題</value>\r\n  </data>\r\n  <data name=\"Settings_IssuesLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://aka.ms/wslproject</value>\r\n    <comment>{Locked}Uri to Microsoft WSL issues</comment>\r\n  </data>\r\n  <data name=\"Settings_SparseVHD.Header\" xml:space=\"preserve\">\r\n    <value>預設啟用疏鬆 VHD</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHD.Description\" xml:space=\"preserve\">\r\n    <value>啟用時，任何新建立的 VHD 都會自動設定為疏鬆。</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>預設啟用疏鬆 VHD。</value>\r\n  </data>\r\n  <data name=\"Settings_SparseVHDToggleSwitch.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>啟用時，任何新建立的 VHD 都會自動設定為疏鬆。</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePath.Header\" xml:space=\"preserve\">\r\n    <value>交換檔案位置</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePath.Description\" xml:space=\"preserve\">\r\n    <value>交換虛擬硬碟的絕對 Windows 路徑。</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathBrowseButton.Content\" xml:space=\"preserve\">\r\n    <value>瀏覽交換檔案</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>交換檔案位置</value>\r\n  </data>\r\n  <data name=\"Settings_SwapFilePathTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>交換虛擬硬碟的絕對 Windows 路徑。</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSize.Header\" xml:space=\"preserve\">\r\n    <value>交換大小</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSize.Description\" xml:space=\"preserve\">\r\n    <value>要新增多少交換空間至 WSL 2 VM，0 表示沒有交換檔。當記憶體需求超過硬體裝置的限制時，交換記憶體是磁碟型 RAM 所使用。</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeResetButton.Content\" xml:space=\"preserve\">\r\n    <value>重設大小</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>交換大小</value>\r\n  </data>\r\n  <data name=\"Settings_SwapSizeTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>要新增至 WSL 2 VM 的交換空間，以 MB 為單位。0 表示沒有交換檔。當記憶體需求超過硬體裝置的限制時，交換記憶體是磁碟型 RAM 所使用。</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio.Header\" xml:space=\"preserve\">\r\n    <value>啟用 VirtIO</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio.Description\" xml:space=\"preserve\">\r\n    <value>使用 virtiofs 而非方案 9 存取主機檔案，加快速度。</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio9p.Header\" xml:space=\"preserve\">\r\n    <value>啟用 Virtio 9p</value>\r\n  </data>\r\n  <data name=\"Settings_Virtio9p.Description\" xml:space=\"preserve\">\r\n    <value>使用 virtio 傳輸，從主機啟用 9P 檔案系統掛接。</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeout.Header\" xml:space=\"preserve\">\r\n    <value>VM 閒置逾時</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeout.Description\" xml:space=\"preserve\">\r\n    <value>VM 在關機前閒置的毫秒數。</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutResetButton.Content\" xml:space=\"preserve\">\r\n    <value>重設逾時</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.Name\" xml:space=\"preserve\">\r\n    <value>VM 閒置逾時</value>\r\n  </data>\r\n  <data name=\"Settings_VMIdleTimeoutTextBox.AutomationProperties.HelpText\" xml:space=\"preserve\">\r\n    <value>VM 在關機前閒置的毫秒數。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration.Title\" xml:space=\"preserve\">\r\n    <value>從 Visual Studio 組建、執行、偵錯並分析在 WSL 上執行的應用程式</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration.Description\" xml:space=\"preserve\">\r\n    <value>從 Visual Studio 執行並偵錯您在 WSL 上的應用程式</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解 Visual Studio 中適用於 .NET 開發人員的 WSL</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForDotNetLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://learn.microsoft.com/visualstudio/debugger/debug-dotnet-core-in-wsl-2</value>\r\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.Content\" xml:space=\"preserve\">\r\n    <value>深入了解關於 Visual Studio 與適用於 C++ 開發人員的 WSL</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_VSForCppLink.NavigateUri\" xml:space=\"preserve\">\r\n    <value>https://learn.microsoft.com/cpp/linux/?view=msvc-170</value>\r\n    <comment>{locked}Uri should redirect to the correct locale for the browser</comment>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_Description.Text\" xml:space=\"preserve\">\r\n    <value>透過 Windows 子系統 Linux 版 (WSL)，您可以在 Linux 上輕鬆執行和偵錯 .NET Core 及跨平台 C++ 應用程式，而無需離開 Visual Studio。如果您是跨平台開發者，可以利用此方法簡單測試更多目標環境。</value>\r\n  </data>\r\n  <data name=\"Settings_OOBEVSIntegration_Title.Text\" xml:space=\"preserve\">\r\n    <value>Visual Studio 整合</value>\r\n  </data>\r\n  <data name=\"Settings_Shell_VSIntegration.Content\" xml:space=\"preserve\">\r\n    <value>Visual Studio 整合</value>\r\n  </data>\r\n</root>"
  },
  {
    "path": "msipackage/CMakeLists.txt",
    "content": "set(BIN ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE})\n\n# For pipeline builds, use PACKAGE_INPUT_DIR if specified (contains signed binaries)\n# For local builds, use bin directory\nif(DEFINED PACKAGE_INPUT_DIR)\n    message(STATUS \"Using signed binaries from ${PACKAGE_INPUT_DIR} for MSI packaging\")\nelse()\n    set(PACKAGE_INPUT_DIR ${BIN})\nendif()\n\nset(OUTPUT_PACKAGE ${BIN}/wsl.msi)\nset(PACKAGE_WIX_IN ${CMAKE_CURRENT_LIST_DIR}/package.wix.in)\nset(PACKAGE_WIX ${BIN}/package.wix)\nset(CAB_CACHE ${BIN}/cab)\nset(WINDOWS_BINARIES wsl.exe;wslg.exe;wslhost.exe;wslrelay.exe;wslservice.exe;wslserviceproxystub.dll;wslinstall.dll)\nif (WSL_BUILD_WSL_SETTINGS)\n    list(APPEND WINDOWS_BINARIES \"wslsettings/wslsettings.dll;wslsettings/wslsettings.exe;libwsl.dll\")\nendif()\n\nset(BINARIES_DEPENDENCIES)\nforeach(binary ${WINDOWS_BINARIES})\n    list(APPEND BINARIES_DEPENDENCIES \"${PACKAGE_INPUT_DIR}/${binary}\")\nendforeach()\n\nset(LINUX_BINARIES init;initrd.img)\nforeach(binary ${LINUX_BINARIES})\n    list(APPEND BINARIES_DEPENDENCIES \"${BIN}/${binary}\")\nendforeach()\n\nif (${WSL_BUILD_THIN_PACKAGE})\n    set(COMPRESS_PACKAGE \"no\")\nelse()\n    set(COMPRESS_PACKAGE \"yes\")\nendif()\n\nconfigure_file(${PACKAGE_WIX_IN} ${PACKAGE_WIX})\nfile(MAKE_DIRECTORY ${CAB_CACHE})\n\nif (NOT ${CMAKE_BUILD_TYPE} STREQUAL \"Debug\")\n    set(COMPRESSION \"high\")\nelse()\n    set(COMPRESSION \"none\")\nendif()\n\nadd_custom_command(\n    OUTPUT ${OUTPUT_PACKAGE}\n    COMMAND ${WIX_SOURCE_DIR}/wix.exe build ${PACKAGE_WIX} -o ${OUTPUT_PACKAGE} -arch ${TARGET_PLATFORM} -dcl ${COMPRESSION} -cc ${CAB_CACHE} -pdbtype none\n    COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/msipackage\"\n    VERBATIM\n    DEPENDS ${PACKAGE_WIX} ${BINARIES_DEPENDENCIES} # Make sure the package is rebuilt if any of the binaries or resources change\n)\n\nadd_custom_target(msipackage DEPENDS ${OUTPUT_PACKAGE})\nset_target_properties(msipackage PROPERTIES EXCLUDE_FROM_ALL FALSE SOURCES ${PACKAGE_WIX_IN})\nadd_dependencies(msipackage wsl wslg wslservice wslhost wslrelay wslserviceproxystub init initramfs wslinstall msixgluepackage)\n\nif (WSL_BUILD_WSL_SETTINGS)\n    add_dependencies(msipackage wslsettings libwsl)\nendif()\n\nset_source_files_properties(${OUTPUT_PACKAGE} PROPERTIES GENERATED TRUE)\n\nif (DEFINED WSL_POST_BUILD_COMMAND)\n    add_custom_command(\n        TARGET msipackage\n        POST_BUILD\n        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n        USES_TERMINAL\n        COMMAND ${WSL_POST_BUILD_COMMAND} -BuildType ${CMAKE_BUILD_TYPE})\nendif()"
  },
  {
    "path": "msipackage/package.wix.in",
    "content": "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">\n    <Package Name=\"Windows Subsystem for Linux\" Language=\"1033\" InstallerVersion=\"500\" Version=\"${PACKAGE_VERSION}\" Manufacturer=\"Microsoft Corporation\" UpgradeCode=\"6D5B792B-1EDC-4DE9-8EAD-201B820F8E82\" Scope=\"perMachine\" Compressed=\"${COMPRESS_PACKAGE}\">\n        <MajorUpgrade AllowDowngrades=\"yes\"  Disallow=\"no\"  />\n        <MediaTemplate EmbedCab=\"yes\" />\n\n        <StandardDirectory Id=\"ProgramFiles64Folder\">\n            <Directory Id=\"INSTALLDIR\" Name=\"WSL\">\n                <Directory Id=\"TOOLSFOLDER\" Name=\"tools\" />\n                <Directory Id=\"LIBFOLDER\" Name=\"lib\" />\n                <?if \"${WSL_BUILD_WSL_SETTINGS}\" = \"true\" ?>\n                    <Directory Id=\"WSLSETTINGS\" Name=\"wslsettings\"/>\n                <?endif?>\n            </Directory>\n        </StandardDirectory>\n\n        <Icon Id=\"wsl.ico\" SourceFile=\"${CMAKE_SOURCE_DIR}/images/wsl.ico\"/>\n        <Property Id=\"ARPPRODUCTICON\" Value=\"wsl.ico\"/>\n\n        <DirectoryRef Id=\"INSTALLDIR\">\n            <Component Id=\"wsl\" Guid=\"F0C8D6BA-1502-41E7-BF72-D93DFA134730\" UninstallWhenSuperseded=\"yes\" DisableRegistryReflection=\"yes\" Bitness=\"always64\">\n                <RemoveFile Id=\"CleanUpWSLShortCut\" Directory=\"ProgramMenuFolder\" Name=\"WSL\" On=\"uninstall\"/>\n                <File Id=\"wsl.exe\" Name=\"wsl.exe\" Source=\"${PACKAGE_INPUT_DIR}/wsl.exe\" KeyPath=\"yes\">\n                    <Shortcut Id=\"WSLShortcut\" Name=\"WSL\" Description=\"Windows Subsystem for Linux\" Arguments=\"--cd ~\" Advertise=\"yes\" Directory=\"ProgramMenuFolder\" Icon=\"wsl.ico\">\n                        <ShortcutProperty Key=\"System.AppUserModel.ID\" Value=\"Microsoft.WSL\"/>\n                        <ShortcutProperty Key=\"System.AppUserModel.ToastActivatorCLSID\" Value=\"{2B9C59C3-98F1-45C8-B87B-12AE3C7927E8}\"/>\n                    </Shortcut>\n                </File>\n\n                <File Id=\"wslg.exe\" Name=\"wslg.exe\" Source=\"${PACKAGE_INPUT_DIR}/wslg.exe\" />\n                <File Id=\"wslhost.exe\" Name=\"wslhost.exe\" Source=\"${PACKAGE_INPUT_DIR}/wslhost.exe\" />\n                <File Id=\"wslrelay.exe\" Name=\"wslrelay.exe\" Source=\"${PACKAGE_INPUT_DIR}/wslrelay.exe\" />\n                <File Id=\"wslserviceproxystub.dll\" Name=\"wslserviceproxystub.dll\" Source=\"${PACKAGE_INPUT_DIR}/wslserviceproxystub.dll\" />\n                <File Id=\"wsldeps.dll\" Name=\"wsldeps.dll\" Source=\"${PACKAGE_INPUT_DIR}/wsldeps.dll\" />\n\n                <?if \"${WSL_BUILD_WSL_SETTINGS}\" = \"true\" ?>\n                    <File Id=\"libwsl.dll\" Name=\"libwsl.dll\" Source=\"${PACKAGE_INPUT_DIR}/libwsl.dll\" />\n                <?endif?>\n\n                <?if \"${WSL_DEV_BINARY_PATH}\" = \"\" ?>\n                <File Id=\"system.vhd\" Source=\"${WSLG_SOURCE_DIR}/${TARGET_PLATFORM}/system.vhd\"/>\n                <?endif?>\n\n                <!-- Installation folder -->\n                <RegistryKey Root=\"HKLM\" Key=\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Lxss\\MSI\">\n                    <RegistryValue Name=\"InstallLocation\" Value=\"[INSTALLDIR]\" Type=\"string\" />\n                    <RegistryValue Name=\"ProductCode\" Value=\"[ProductCode]\" Type=\"string\" />\n                    <RegistryValue Name=\"Version\" Value=\"${PACKAGE_VERSION}\" Type=\"string\" />\n                </RegistryKey>\n            </Component>\n\n            <Component Id=\"explorerplan9shortcut\" Guid=\"{93CBFF23-A04C-4344-A332-238CE5B97AED}\" UninstallWhenSuperseded=\"yes\" DisableRegistryReflection=\"yes\" Bitness=\"always64\">\n                <!-- Explorer extensions -->\n              <RegistryKey Root=\"HKLM\" Key=\"SOFTWARE\\Classes\\CLSID\\{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}\">\n                <RegistryValue Value=\"Linux\" Type=\"string\"/>\n                <RegistryValue Name=\"SortOrderIndex\" Value=\"119\" Type=\"integer\"/>\n                <!--0x77-->\n                <RegistryValue Name=\"System.IsPinnedToNameSpaceTree\" Value=\"1\" Type=\"integer\"/>\n\n                <RegistryKey Key=\"DefaultIcon\">\n                    <RegistryValue Value=\"[System64Folder]wsl.exe,-1\" Type=\"string\"/>\n                </RegistryKey>\n\n                <RegistryKey Key=\"InProcServer32\">\n                    <RegistryValue Value=\"[System64Folder]windows.storage.dll\" Type=\"string\"/>\n                </RegistryKey>\n\n                <RegistryKey Key=\"ShellFolder\">\n                    <RegistryValue Name=\"Attributes\" Value=\"2692743245\" Type=\"integer\"/>\n                    <!--0xa080004d\"-->\n                    <RegistryValue Name=\"FolderValueFlags\" Value=\"40\" Type=\"integer\"/>\n                    <!--0x28-->\n                </RegistryKey>\n\n                <RegistryKey Key=\"Instance\">\n                    <RegistryValue Name=\"CLSID\" Value=\"{4FE04BFD-85B9-49DD-B914-F4C9556B9DA6}\" Type=\"string\"/>\n\n                    <RegistryKey Key=\"InitPropertyBag\">\n                        <RegistryValue Name=\"DisplayType\" Value=\"2\" Type=\"integer\"/>\n                        <RegistryValue Name=\"EnumObjectsTelemetryValue\" Value=\"WSL\" Type=\"string\"/>\n                        <RegistryValue Name=\"Provider\" Value=\"Plan 9 Network Provider\" Type=\"string\"/>\n                        <RegistryValue Name=\"ResName\" Value=\"\\\\wsl.localhost\" Type=\"string\"/>\n                    </RegistryKey>\n                </RegistryKey>\n              </RegistryKey>\n\n              <RegistryKey Root=\"HKLM\" Key=\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel\">\n                <RegistryValue Name=\"{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}\" Value=\"1\" Type=\"integer\"/>\n              </RegistryKey>\n\n              <RegistryKey Root=\"HKLM\" Key=\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}\">\n                <RegistryValue Value=\"Linux\" Type=\"string\"/>\n              </RegistryKey>\n\n              <RegistryKey Root=\"HKLM\" Key=\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IdListAliasTranslations\\WSL\">\n                <RegistryValue Name=\"Target\" Value=\"::{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}\" Type=\"string\"/>\n                <RegistryValue Name=\"Source\" Value=\"\\\\wsl.localhost\" Type=\"string\"/>\n              </RegistryKey>\n\n              <RegistryKey Root=\"HKLM\" Key=\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IdListAliasTranslations\\WSLLegacy\">\n                <RegistryValue Name=\"Target\" Value=\"::{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}\" Type=\"string\"/>\n                <RegistryValue Name=\"Source\" Value=\"\\\\wsl$\" Type=\"string\"/>\n              </RegistryKey>\n            </Component>\n\n            <Component Id=\"explorershell\" Guid=\"{93CBFF23-A04C-4344-A332-238CE5B97AEC}\" UninstallWhenSuperseded=\"yes\" DisableRegistryReflection=\"yes\" Bitness=\"always64\">\n                <?foreach PATH in SOFTWARE\\Classes\\Directory\\shell\\WSL;SOFTWARE\\Classes\\Directory\\Background\\shell\\WSL;SOFTWARE\\Classes\\Drive\\shell\\WSL?>\n                <RegistryKey Root=\"HKLM\" Key=\"$(var.PATH)\">\n                    <RegistryValue Value=\"@wsl.exe,-2\" Type=\"string\"/>\n                    <RegistryValue Name=\"Extended\" Value=\"\" Type=\"string\"/>\n                    <RegistryValue Name=\"NoWorkingDirectory\" Value=\"\" Type=\"string\"/>\n                    <RegistryKey Key=\"command\">\n                        <RegistryValue Value='wsl.exe --cd \"%V\"' Type=\"string\"/>\n                    </RegistryKey>\n                </RegistryKey>\n              <?endforeach?>\n\n              <ProgId Id=\"WSLDistributionTar\" Description=\"WSL tar distribution\" Icon=\"wsl.exe\">\n                  <Extension Id=\"wsl\">\n                      <Verb Id=\"open\" Command=\"open\" TargetFile=\"wsl.exe\" Argument=\"--install --prompt-before-exit --from-file &quot;%1&quot;\" />\n                  </Extension>\n              </ProgId>\n            </Component>\n\n            <Component Id=\"wslservice\" Guid=\"F0C8D6BA-1502-41E7-BF72-D93DFA134735\" UninstallWhenSuperseded=\"yes\" DisableRegistryReflection=\"yes\" Bitness=\"always64\">\n\n                <!-- WslServiceProxyStub -->\n                <RegistryKey Root=\"HKCR\" Key=\"Interface\\{38541BDC-F54F-4CEB-85D0-37F0F3D2617E}\">\n                    <RegistryValue Value=\"ILxssUserSession\" Type=\"string\" />\n                    <RegistryKey Key=\"ProxyStubClsid32\">\n                        <RegistryValue Value=\"{4EA0C6DD-E9FF-48E7-994E-13A31D10DC60}\" Type=\"string\" />\n                    </RegistryKey>\n                </RegistryKey>\n\n                <RegistryKey Root=\"HKCR\" Key=\"CLSID\\{4EA0C6DD-E9FF-48E7-994E-13A31D10DC60}\">\n                    <RegistryValue Value=\"PSFactoryBuffer\" Type=\"string\" />\n                </RegistryKey>\n                <RegistryKey Root=\"HKCR\" Key=\"CLSID\\{4EA0C6DD-E9FF-48E7-994E-13A31D10DC60}\\InProcServer32\">\n                    <RegistryValue Value=\"[INSTALLDIR]wslserviceproxystub.dll\" Type=\"string\" />\n                    <RegistryValue Name=\"ThreadingModel\" Value=\"Both\" Type=\"string\" />\n                </RegistryKey>\n\n                <!-- ILxssUserSession -->\n                <RegistryKey Root=\"HKCR\" Key=\"AppID\\{370121D2-AA7E-4608-A86D-0BBAB9DA1A60}\">\n\n                    <!-- O:BAG:BAD:(A;;CCDCSW;;;AU)(A;;CCDCSW;;;PS)(A;;CCDCSW;;;SY) -->\n                    <RegistryValue Name=\"AccessPermission\" Value=\"01000480580000006800000000000000140000000200440003000000000014000B00000001010000000000050B000000000014000B00000001010000000000050A000000000014000B0000000101000000000005120000000102000000000005200000002002000001020000000000052000000020020000\" Type=\"binary\" />\n                    <RegistryValue Name=\"LaunchPermission\" Value=\"01000480580000006800000000000000140000000200440003000000000014000B00000001010000000000050B000000000014000B00000001010000000000050A000000000014000B0000000101000000000005120000000102000000000005200000002002000001020000000000052000000020020000\" Type=\"binary\" />\n                    <RegistryValue Name=\"LocalService\" Value=\"WSLService\" Type=\"string\" />\n                </RegistryKey>\n\n                <!-- WslSupport + LxssUserSession -->\n                <RegistryKey Root=\"HKCR\" Key=\"CLSID\\{a9b7a1b9-0671-405c-95f1-e0612cb4ce7e}\">\n                    <RegistryValue Name=\"AppId\" Value=\"{370121D2-AA7E-4608-A86D-0BBAB9DA1A60}\" Type=\"string\" />\n                    <RegistryValue Value=\"LxssUserSession\" Type=\"string\" />\n                </RegistryKey>\n\n                <!-- Notification server -->\n                <RegistryKey Root=\"HKCR\" Key=\"CLSID\\{2B9C59C3-98F1-45C8-B87B-12AE3C7927E8}\\LocalServer32\">\n                    <RegistryValue Value='\"[INSTALLDIR]wslhost.exe\"' Type=\"string\" />\n                </RegistryKey>\n\n                <!-- wsldevicehost.dll -->\n                <RegistryKey Root=\"HKCR\" Key=\"AppID\\{17696EAC-9568-4CF5-BB8C-82515AAD6C09}\">\n                    <RegistryValue Name=\"DllSurrogate\" Value=\"\" Type=\"string\" />\n                    <RegistryValue Name=\"AppIDFlags\" Value=\"2048\" Type=\"integer\" />\n\n                    <!-- O:BAG:BAD:(A;;CCDCSW;;;AU)(A;;CCDCSW;;;PS)(A;;CCDCSW;;;SY) -->\n                    <RegistryValue Name=\"AccessPermission\" Value=\"01000480580000006800000000000000140000000200440003000000000014000B00000001010000000000050B000000000014000B00000001010000000000050A000000000014000B0000000101000000000005120000000102000000000005200000002002000001020000000000052000000020020000\" Type=\"binary\" />\n                    <RegistryValue Name=\"LaunchPermission\" Value=\"01000480580000006800000000000000140000000200440003000000000014000B00000001010000000000050B000000000014000B00000001010000000000050A000000000014000B0000000101000000000005120000000102000000000005200000002002000001020000000000052000000020020000\" Type=\"binary\" />\n                </RegistryKey>\n\n                <!-- WslDeviceHost_VirtioPmem -->\n                <RegistryKey Root=\"HKCR\" Key=\"CLSID\\{ABB755FC-1B86-4255-83E2-E5787ABCF6C2}\">\n                    <RegistryValue Value=\"WslDeviceHost_VirtioPmem\" Type=\"string\" />\n                    <RegistryValue Name=\"AppId\" Value=\"{17696EAC-9568-4CF5-BB8C-82515AAD6C09}\" Type=\"string\"/>\n\n                    <RegistryKey Key=\"InProcServer32\">\n                        <RegistryValue Value=\"[INSTALLDIR]wsldevicehost.dll\" Type=\"string\" />\n                        <RegistryValue Name=\"ThreadingModel\" Value=\"Both\" Type=\"string\" />\n                    </RegistryKey>\n                </RegistryKey>\n\n                <!-- WslDeviceHost_VirtioFs (admin) -->\n                <RegistryKey Root=\"HKCR\" Key=\"CLSID\\{7e6ad219-d1b3-42d5-b8ee-d96324e64ff6}\">\n                    <RegistryValue Value=\"WslDeviceHost_VirtioFs_Admin\" Type=\"string\"/>\n                    <RegistryValue Name=\"AppId\" Value=\"{17696EAC-9568-4CF5-BB8C-82515AAD6C09}\" Type=\"string\"/>\n\n                    <RegistryKey Key=\"InProcServer32\">\n                        <RegistryValue Value=\"[INSTALLDIR]wsldevicehost.dll\" Type=\"string\"/>\n                        <RegistryValue Name=\"ThreadingModel\" Value=\"Both\" Type=\"string\"/>\n                    </RegistryKey>\n                </RegistryKey>\n\n                <!-- WslDeviceHost_VirtioFs -->\n                <RegistryKey Root=\"HKCR\" Key=\"CLSID\\{60285AE6-AAF3-4456-B444-A6C2D0DEDA38}\">\n                    <RegistryValue Value=\"WslDeviceHost_VirtioFs\" Type=\"string\" />\n                    <RegistryValue Name=\"AppId\" Value=\"{17696EAC-9568-4CF5-BB8C-82515AAD6C09}\" Type=\"string\" />\n\n                    <RegistryKey Key=\"InProcServer32\">\n                        <RegistryValue Value=\"[INSTALLDIR]wsldevicehost.dll\" Type=\"string\" />\n                        <RegistryValue Name=\"ThreadingModel\" Value=\"Both\" Type=\"string\" />\n                    </RegistryKey>\n                </RegistryKey>\n\n                <!-- WslDeviceHost_VirtioNet -->\n                <RegistryKey Root=\"HKCR\" Key=\"CLSID\\{16479D2E-F0C3-4DBA-BF7A-04FFF0892B07}\">\n                    <RegistryValue Value=\"WslDeviceHost_Net\" Type=\"string\" />\n                    <RegistryValue Name=\"AppId\" Value=\"{17696EAC-9568-4CF5-BB8C-82515AAD6C09}\" Type=\"string\" />\n\n                    <RegistryKey Key=\"InProcServer32\">\n                        <RegistryValue Value=\"[INSTALLDIR]wsldevicehost.dll\" Type=\"string\" />\n                        <RegistryValue Name=\"ThreadingModel\" Value=\"Both\" Type=\"string\" />\n                    </RegistryKey>\n                </RegistryKey>\n\n                <!-- Session 0 service -->\n                <File Id=\"wslservice.exe\" Source=\"${PACKAGE_INPUT_DIR}/wslservice.exe\" KeyPath=\"yes\" />\n                <ServiceInstall Name=\"WSLService\" DisplayName=\"WSL Service\" Description=\"WSL Service\" Start=\"auto\" Type=\"ownProcess\" ErrorControl=\"normal\" Account=\"LocalSystem\" Vital=\"yes\" Interactive=\"no\" />\n\n                <!-- The service is stopped on uninstall and upgrade.\n                     N.B. It shouldn't be started on install because it will fail to start if the OC is missing, \n                     which would fail the install. -->\n                <ServiceControl Id=\"StopService\" Stop=\"both\" Remove=\"uninstall\" Name=\"WSLService\" Wait=\"yes\" />\n            </Component>\n\n            <Component Id=\"wslg\" Guid=\"F0C8D6BA-1502-41E7-BF72-D93DFA134731\" UninstallWhenSuperseded=\"yes\" DisableRegistryReflection=\"yes\" Bitness=\"always64\">\n                <?if \"${WSL_DEV_BINARY_PATH}\" = \"\" ?>\n                    <File Id=\"msrdc.exe\" Source=\"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/msrdc.exe\" />\n                    <File Id=\"wslg.rdp\" Source=\"${WSLG_SOURCE_DIR}/wslg.rdp\" />\n                    <File Id=\"wslg_desktop.rdp\" Source=\"${WSLG_SOURCE_DIR}/wslg_desktop.rdp\" />\n                    <File Id=\"rdclientax.dll\" Source=\"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/rdclientax.dll\" />\n                    <File Id=\"rdpnanoTransport.dll\" Source=\"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/rdpnanoTransport.dll\" />\n                    <File Id=\"RdpWinStlHelper.dll\" Source=\"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}/RdpWinStlHelper.dll\" />\n                <?endif?>\n                <File Id=\"wsldevicehost.dll\" Source=\"${PACKAGE_INPUT_DIR}/wsldevicehost.dll\" />\n                <File Id=\"${WSLG_TS_PLUGIN_DLL}\" Source=\"${PACKAGE_INPUT_DIR}/${WSLG_TS_PLUGIN_DLL}\" />\n\n                <!-- MSRDC Plugin registration -->\n                <RegistryKey Root=\"HKLM\" Key=\"SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\OptionalAddIns\\WSLDVC_PACKAGE\">\n                    <RegistryValue Name=\"Name\" Value=\"[INSTALLDIR]WSLDVCPlugin.dll\" Type=\"string\" />\n                </RegistryKey>\n            </Component>\n\n            <?if \"${WSL_DEV_BINARY_PATH}\" = \"\" ?>\n                <Component Id=\"msal\" Guid=\"F0C8D6BA-1502-41E7-BF72-D93DFA134734\" UninstallWhenSuperseded=\"yes\" DisableRegistryReflection=\"yes\" Bitness=\"always64\">\n                    <File Id=\"msal.wsl.proxy.exe\" Source=\"${MSAL_SOURCE_DIR}/${TARGET_PLATFORM}/msal.wsl.proxy.exe\" />\n                </Component>\n            <?endif?>\n        </DirectoryRef>\n\n        <?if \"${WSL_DEV_BINARY_PATH}\" = \"\" ?>\n            <DirectoryRef Id=\"LIBFOLDER\">\n                <Component Id=\"directx\" Guid=\"F0C8D6BA-1502-41E7-BF72-D93DFA134732\" Bitness=\"always64\" UninstallWhenSuperseded=\"yes\">\n                    <File Id=\"libd3d12.so\" Source=\"${DIRECT3D_SOURCE_DIR}/lib/${TARGET_PLATFORM}/libd3d12.so\" />\n                    <File Id=\"libd3d12core.so\" Source=\"${DIRECT3D_SOURCE_DIR}/lib/${TARGET_PLATFORM}/libd3d12core.so\" />\n                    <File Id=\"libdxcore.so\" Source=\"${DXCORE_SOURCE_DIR}/lib/libdxcore.so\" />\n                </Component>\n            </DirectoryRef>\n        <?endif?>\n\n        <DirectoryRef Id=\"TOOLSFOLDER\">\n            <Component Id=\"tools\" Guid=\"F0C8D6BA-1502-41E7-BF72-D93DFA134733\" Bitness=\"always64\" UninstallWhenSuperseded=\"yes\">\n                <File Id=\"init\" Source=\"${BIN}/init\" Checksum=\"yes\" />\n                <File Id=\"initrd.img\" Source=\"${BIN}/initrd.img\" Checksum=\"yes\" />\n                <File Id=\"bsdtar\" Source=\"${BSDTARD_SOURCE_DIR}/${TARGET_PLATFORM}/bsdtar\"/>\n\n                <?if \"${WSL_DEV_BINARY_PATH}\" = \"\" ?>\n                    <File Id=\"kernel\" Source=\"${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/kernel\"/>\n                    <File Id=\"modules.vhd\" Source=\"${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/modules.vhd\"/>\n                <?endif?>\n            </Component>\n        </DirectoryRef>\n\n        <?if \"${WSL_DEV_BINARY_PATH}\" = \"\" ?>\n            <ComponentGroup Id=\"msrdc_localization\" Directory=\"INSTALLDIR\" Source=\"${MSRDC_SOURCE_DIR}/${TARGET_PLATFORM}\">\n                <Component Id=\"msrdc_mui\" Guid=\"EDBB5FC5-058D-45D7-BB03-FB1337B3B577\" Bitness=\"always64\"  UninstallWhenSuperseded=\"yes\" />\n                <Files Include=\"**.mui\" />\n            </ComponentGroup>\n        <?endif?>\n\n\n        <?if \"${WSL_BUILD_WSL_SETTINGS}\" = \"true\" ?>\n            <ComponentGroup Id=\"wslsettings\" Directory=\"WSLSETTINGS\" Source=\"${BIN}/wslsettings\">\n                <Component Id=\"wslsettingsnonserver\" Guid=\"AB166073-8855-492B-95C8-C6E5939B66A5\" Bitness=\"always64\" DisableRegistryReflection=\"yes\" UninstallWhenSuperseded=\"yes\" Condition=\"MsiNTProductType = 1\">\n                    <RemoveFile Id=\"CleanUpWSLSettingsShortCutNonServer\" Directory=\"ProgramMenuFolder\" Name=\"WSLSettings\" On=\"uninstall\"/>\n                    <File Id=\"wslsettings.exe_nonserver\" Source=\"${PACKAGE_INPUT_DIR}/wslsettings/wslsettings.exe\" KeyPath=\"yes\" ShortName=\"kyk8fs6a.exe\">\n                        <Shortcut Id=\"WSLSettingsShortcutNonServer\" Name=\"WSL Settings\" Description=\"Windows Subsystem for Linux Settings\" Advertise=\"yes\" Directory=\"ProgramMenuFolder\" Icon=\"wsl.ico\">\n                            <ShortcutProperty Key=\"System.AppUserModel.IsSystemComponent\" Value=\"true\"/>\n                        </Shortcut>\n                    </File>\n                    <!-- Protocol registration -->\n                    <RegistryKey Root=\"HKLM\" Key=\"Software\">\n                        <RegistryKey Key=\"Classes\">\n                            <RegistryKey Key=\"wsl-settings\">\n                                <RegistryValue Value=\"URL:wsl-settings\" Type=\"string\" />\n                                <RegistryValue Name=\"URL Protocol\" Value=\"\" Type=\"string\" />\n                            </RegistryKey>\n                            <RegistryKey Key=\"[WSLSETTINGSPROGID]\">\n                                <RegistryKey Key=\"Application\">\n                                    <RegistryValue Name=\"ApplicationName\" Value=\"WSL Settings\" Type=\"string\" />\n                                </RegistryKey>\n                                <RegistryKey Key=\"shell\\open\\command\">\n                                    <RegistryValue Value='\"[WSLSETTINGS]wslsettings.exe\" \"----ms-protocol:%1\"' Type=\"string\" />\n                                </RegistryKey>\n                            </RegistryKey>\n                        </RegistryKey>\n                        <RegistryKey Key=\"Microsoft\\WindowsAppRuntimeApplications\\[WSLSETTINGSAPPID]\\Capabilities\\UrlAssociations\">\n                            <RegistryValue Name=\"wsl-settings\" Value=\"[WSLSETTINGSPROGID]\" Type=\"string\" />\n                        </RegistryKey>\n                        <RegistryKey Key=\"RegisteredApplications\">\n                            <RegistryValue Name=\"[WSLSETTINGSAPPID]\" Value=\"Software\\Microsoft\\WindowsAppRuntimeApplications\\[WSLSETTINGSAPPID]\\Capabilities\" Type=\"string\" />\n                        </RegistryKey>\n                    </RegistryKey>\n                </Component>\n                <Component Id=\"wslsettingsserver\" Guid=\"EE2D69A0-4F55-4EC5-9576-4FAD70BC798E\" Bitness=\"always64\" DisableRegistryReflection=\"yes\" UninstallWhenSuperseded=\"yes\" Condition=\"MsiNTProductType &gt; 1\">\n                    <RemoveFile Id=\"CleanUpWSLSettingsShortCutServer\" Directory=\"ProgramMenuFolder\" Name=\"WSLSettings\" On=\"uninstall\"/>\n                    <File Id=\"wslsettings.exe_server\" Source=\"${PACKAGE_INPUT_DIR}/wslsettings/wslsettings.exe\" KeyPath=\"yes\" ShortName=\"kyk8fs6b.exe\">\n                        <Shortcut Id=\"WSLSettingsShortcutServer\" Name=\"WSL Settings\" Description=\"Windows Subsystem for Linux Settings\" Advertise=\"yes\" Directory=\"ProgramMenuFolder\" Icon=\"wsl.ico\"/>\n                    </File>\n                    <!-- Protocol registration -->\n                    <RegistryKey Root=\"HKLM\" Key=\"Software\">\n                        <RegistryKey Key=\"Classes\">\n                            <RegistryKey Key=\"wsl-settings\">\n                                <RegistryValue Value=\"URL:wsl-settings\" Type=\"string\" />\n                                <RegistryValue Name=\"URL Protocol\" Value=\"\" Type=\"string\" />\n                            </RegistryKey>\n                            <RegistryKey Key=\"[WSLSETTINGSPROGID]\">\n                                <RegistryKey Key=\"Application\">\n                                    <RegistryValue Name=\"ApplicationName\" Value=\"WSL Settings\" Type=\"string\" />\n                                </RegistryKey>\n                                <RegistryKey Key=\"shell\\open\\command\">\n                                    <RegistryValue Value='\"[WSLSETTINGS]wslsettings.exe\" \"----ms-protocol:%1\"' Type=\"string\" />\n                                </RegistryKey>\n                            </RegistryKey>\n                        </RegistryKey>\n                        <RegistryKey Key=\"Microsoft\\WindowsAppRuntimeApplications\\[WSLSETTINGSAPPID]\\Capabilities\\UrlAssociations\">\n                            <RegistryValue Name=\"wsl-settings\" Value=\"[WSLSETTINGSPROGID]\" Type=\"string\" />\n                        </RegistryKey>\n                        <RegistryKey Key=\"RegisteredApplications\">\n                            <RegistryValue Name=\"[WSLSETTINGSAPPID]\" Value=\"Software\\Microsoft\\WindowsAppRuntimeApplications\\[WSLSETTINGSAPPID]\\Capabilities\" Type=\"string\" />\n                        </RegistryKey>\n                    </RegistryKey>\n                </Component>\n                <File Id=\"wslsettings.dll\" Source=\"${PACKAGE_INPUT_DIR}/wslsettings/wslsettings.dll\"/>\n                <Files Include=\"*.dll\">\n                    <Exclude Files=\"wslsettings.dll\" />\n                </Files>\n                <Files Include=\"*.exe\">\n                    <Exclude Files=\"wslsettings.exe\" />\n                </Files>\n                <Files Include=\"**/*.gif\"/>\n                <Files Include=\"**/*.ico\"/>\n                <Files Include=\"**/*.mui\"/>\n                <Files Include=\"**/*.png\"/>\n                <Files Include=\"*.json\"/>\n                <Files Include=\"*.pri\"/>\n                <Files Include=\"*.winmd\"/>\n                <Files Include=\"**/*.xbf\"/>\n            </ComponentGroup>\n        <?endif?>\n\n        <Feature Id=\"WSL\" Title=\"Windows Subsystem for Linux\" Level=\"1\">\n            <ComponentRef Id=\"wsl\" />\n            <ComponentRef Id=\"wslservice\" />\n            <ComponentRef Id=\"wslg\" />\n            <ComponentRef Id=\"tools\" />\n            <ComponentRef Id=\"explorershell\" />\n            <ComponentRef Id=\"explorerplan9shortcut\" />\n\n            <?if \"${WSL_DEV_BINARY_PATH}\" = \"\" ?>\n                <ComponentRef Id=\"msal\" />\n                <ComponentGroupRef Id=\"msrdc_localization\" />\n                <ComponentRef Id=\"directx\" />\n            <?endif?>\n\n            <?if \"${WSL_BUILD_WSL_SETTINGS}\" = \"true\" ?>\n                <ComponentGroupRef Id=\"wslsettings\" />\n            <?endif?>\n\n        </Feature>\n\n        <Binary Id=\"wslinstall.dll\" SourceFile=\"${PACKAGE_INPUT_DIR}/wslinstall.dll\" />\n        <Binary Id=\"msixpackage\" SourceFile=\"${PACKAGE_INPUT_DIR}/gluepackage.msix\"/>\n\n        <CustomAction Id=\"ValidateInstall\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"WslValidateInstallation\"\n            Return=\"check\"\n            Execute=\"deferred\"\n         />\n\n        <CustomAction Id=\"FinalizeInstall\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"WslFinalizeInstallation\"\n            Return=\"check\"\n            Execute=\"deferred\"\n         />\n\n        <CustomAction Id=\"DeprovisionMsix\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"DeprovisionMsix\"\n            Return=\"check\"\n            Execute=\"deferred\"\n        />\n\n        <CustomAction Id=\"RemoveMsixAsSystem\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"RemoveMsixAsSystem\"\n            Return=\"check\"\n            Execute=\"deferred\"\n         />\n\n        <CustomAction Id=\"RemoveMsixAsUser\"\n            Impersonate=\"yes\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"RemoveMsixAsUser\"\n            Return=\"check\"\n            Execute=\"deferred\"\n        />\n\n        <CustomAction Id=\"InstallMsix\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"InstallMsix\"\n            Return=\"check\"\n            Execute=\"deferred\"\n         />\n\n        <CustomAction Id=\"InstallMsixAsUser\"\n            Impersonate=\"yes\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"InstallMsixAsUser\"\n            Return=\"check\"\n            Execute=\"deferred\"\n        />\n\n        <CustomAction Id=\"CleanExplorerState\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"CleanExplorerState\"\n            Return=\"check\"\n            Execute=\"deferred\"\n        />\n\n        <CustomAction Id=\"CleanMsixState\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"CleanMsixState\"\n            Return=\"check\"\n            Execute=\"deferred\"\n         />\n\n        <CustomAction Id=\"RegisterLspCategories\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"RegisterLspCategories\"\n            Return=\"check\"\n            Execute=\"deferred\"\n         />\n\n\n         <CustomAction Id=\"RemoveRegistryKeyProtections\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"RemoveRegistryKeyProtections\"\n            Return=\"check\"\n            Execute=\"deferred\"\n         />\n\n         <CustomAction Id=\"UnregisterLspCategories\"\n            Impersonate=\"no\"\n            BinaryRef=\"wslinstall.dll\"\n            DllEntry=\"UnregisterLspCategories\"\n            Return=\"check\"\n            Execute=\"deferred\"\n         />\n\n        <CustomAction Id=\"InstallMsix.SetProperty\" Return=\"check\" Property=\"InstallMsix\" Value='[DATABASE]' Execute='immediate' />\n        <CustomAction Id=\"CalculateWslSettingsProtocolIds\" Impersonate=\"no\" BinaryRef=\"wslinstall.dll\" DllEntry=\"CalculateWslSettingsProtocolIds\" Return=\"check\" Execute='immediate' />\n\n        <!-- See https://learn.microsoft.com/en-us/windows/win32/msi/examples-of-conditional-statement-syntax -->\n        <InstallExecuteSequence>\n            <Custom Action=\"ValidateInstall\" After=\"InstallInitialize\" Condition=\"(not INSTALLED) and (not SKIPVALIDATION = 1)\" />\n\n            <!-- Must run before InstallServices or else we'll fail if wslservice already exists.\n                 Covers cases where:\n                 - The MSI package is installed for the first time and the old MSIX needs to be removed\n                 - The MSI package is uninstalled and the glue MSIX must be removed\n                 \n                 Must also run before CreateShortcuts since there might be an old MSIX shortcut that conflicts with ours.\n                 Also, this action must not run during upgrades \n                 since upgrades start by first uninstalling the package, this isn't run if UPGRADINGPRODUCTCODE is set in the 'removing' MSI process.\n                 In this path, only the installing MSI process should run this action.\n\n                 We use 'BindImage' since its just before 'CreateShortcuts' in the install sequence\n             -->\n            <Custom Action=\"DeprovisionMsix\" After=\"BindImage\" Condition='((not INSTALLED) or (REMOVE~=\"ALL\")) and (not UPGRADINGPRODUCTCODE) and (not SKIPMSIX = 1)'/>\n\n            <!-- We need to remove the MSIX both as user and as system to work around a race condition in MSIX -->\n            <Custom Action=\"RemoveMsixAsUser\" After=\"DeprovisionMsix\" Condition='((not INSTALLED) or (REMOVE~=\"ALL\")) and (not UPGRADINGPRODUCTCODE) and (not SKIPMSIX = 1)'/>\n            <Custom Action=\"RemoveMsixAsSystem\" After=\"RemoveMsixAsUser\" Condition='((not INSTALLED) or (REMOVE~=\"ALL\")) and (not UPGRADINGPRODUCTCODE) and (not SKIPMSIX = 1)'/>\n\n            <!-- This needs to run before the MSI logic tries to write the registry or create shortcuts-->\n            <Custom Action=\"CleanMsixState\" After=\"RemoveMsixAsSystem\" Condition='((not INSTALLED) or (not REMOVE~=\"ALL\")) and (not UPGRADINGPRODUCTCODE)' />\n\n            <!-- This is needed to remove the  SFGAO_NONENUMERATED flag from the explorer keys, which can prevent the WSL icon from being visible -->\n            <Custom Action=\"CleanExplorerState\" Before=\"WriteRegistryValues\" Condition='((not REMOVE~=\"ALL\") or WIX_UPGRADE_DETECTED)  and (not UPGRADINGPRODUCTCODE)' />\n\n            <!-- This needs to run before the registry is written since it removes registry protections that prevents MSI from writing to the registry key -->\n            <Custom Action=\"RemoveRegistryKeyProtections\" After=\"CleanExplorerState\" Condition='((not REMOVE~=\"ALL\") or WIX_UPGRADE_DETECTED)  and (not UPGRADINGPRODUCTCODE)' />\n\n            <!-- Runs during first install and upgrade\n                 This action must not run if UPGRADINGPRODUCTCODE is set because it should only run during the second sequence\n\n                 Note: versions < 2.0.2 pass REINSTALL=ALL which sets REMOVE=ALL.\n                 Use WIX_UPGRADE_DETECTED to cover this scenario since it's always set when a previous build is installed.\n            -->\n            <Custom Action=\"InstallMsix.SetProperty\" After=\"CleanMsixState\" Condition='((not REMOVE~=\"ALL\") or WIX_UPGRADE_DETECTED) and (not UPGRADINGPRODUCTCODE) and (not SKIPMSIX = 1)' />\n            <Custom Action=\"InstallMsix\" After=\"InstallMsix.SetProperty\" Condition='((not REMOVE~=\"ALL\") or WIX_UPGRADE_DETECTED) and (not UPGRADINGPRODUCTCODE) and (not SKIPMSIX = 1)' />\n            <Custom Action=\"InstallMsixAsUser\" After=\"InstallMsix\" Condition='((not REMOVE~=\"ALL\") or WIX_UPGRADE_DETECTED) and (not UPGRADINGPRODUCTCODE) and (not SKIPMSIX = 1)' />\n\n            <!-- This needs to run after the registry is written because this action needs to read the install path -->\n            <Custom Action=\"RegisterLspCategories\" After=\"WriteRegistryValues\" Condition='((not REMOVE~=\"ALL\") or WIX_UPGRADE_DETECTED)  and (not UPGRADINGPRODUCTCODE) and (not SKIPLSP = 1)' />\n\n            <!-- This needs to run before the registry is cleared because this action needs to read the install path.\n                 This actions runs on uninstallation and upgrade. -->\n            <Custom Action=\"UnregisterLspCategories\" Before=\"RemoveRegistryValues\" Condition='(REMOVE~=\"ALL\") and (not UPGRADINGPRODUCTCODE) and (not SKIPLSP = 1)' />\n\n            <?if \"${WSL_BUILD_WSL_SETTINGS}\" = \"true\" ?>\n                <!-- This needs to run before the registry is written because this action calculates registry key names and values -->\n                <Custom Action=\"CalculateWslSettingsProtocolIds\" Before=\"WriteRegistryValues\" Condition='(not REMOVE~=\"ALL\")' />\n            <?endif?>\n\n            <Custom Action=\"FinalizeInstall\" After=\"PublishFeatures\"/>\n\n        </InstallExecuteSequence>\n\n        <!-- Don't show a 'Modify' button in settings since there is nothing to modify -->\n        <Property Id=\"ARPNOMODIFY\" Value=\"yes\" Secure=\"yes\" />\n        <Property Id=\"ARPSYSTEMCOMPONENT\" Value=\"yes\" Secure=\"yes\" />\n\n        <!-- Setting REINSTALLMODE to AMUS will force all re-installed components to re-install all files, registry keys and shortcuts. \n        This is useful during downgrades since components with KeyPath set will not removed if their KeyPath points to valid file / registry key.\n        -->\n        <Property Id=\"REINSTALLMODE\" Value=\"AMUS\" Secure=\"yes\" />\n\n        <!-- Force all applications to exit during upgrade.\n             See: https://learn.microsoft.com/en-us/windows/win32/msi/msirmshutdown\n        -->\n        <Property Id=\"MSIRMSHUTDOWN\" Value=\"0\" Secure=\"yes\" />\n    </Package>\n</Wix>\n\n"
  },
  {
    "path": "msixgluepackage/AppxManifest.in",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Package xmlns=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10\"\n         xmlns:uap=\"http://schemas.microsoft.com/appx/manifest/uap/windows10\"\n         xmlns:uap3=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/3\"\n         xmlns:uap10=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/10\"\n         xmlns:rescap=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities\"\n         xmlns:desktop=\"http://schemas.microsoft.com/appx/manifest/desktop/windows10\"\n         xmlns:desktop6=\"http://schemas.microsoft.com/appx/manifest/desktop/windows10/6\"\n         IgnorableNamespaces=\"uap uap3 uap10 rescap desktop desktop6\">\n\n    <Identity Name=\"MicrosoftCorporationII.WindowsSubsystemForLinux\" Publisher=\"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US\" Version=\"${PACKAGE_VERSION}\" ProcessorArchitecture=\"${TARGET_PLATFORM}\"/>\n    <Properties>\n        <DisplayName>ms-resource:AppName</DisplayName>\n        <PublisherDisplayName>Microsoft Corp.</PublisherDisplayName>\n        <Logo>Images\\StoreLogo.png</Logo>\n        <desktop6:FileSystemWriteVirtualization>disabled</desktop6:FileSystemWriteVirtualization>\n        <desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>\n        <uap10:PackageIntegrity>\n            <uap10:Content Enforcement=\"on\"/>\n        </uap10:PackageIntegrity>\n    </Properties>\n    <Dependencies>\n        <TargetDeviceFamily Name=\"Windows.Desktop\" MinVersion=\"10.0.19041.0\" MaxVersionTested=\"10.0.26100.0\"/>\n    </Dependencies>\n    <Resources>\n        ${SUPPORTED_LANGS_MANIFEST_ENTRIES}\n        <Resource uap:Scale=\"200\"/>\n    </Resources>\n    <Applications>\n        <Application Id=\"wsl\"\n          Executable=\"wsl.exe\"\n          EntryPoint=\"Windows.FullTrustApplication\"\n          uap10:Parameters=\"--cd ~\">\n            <uap:VisualElements\n              DisplayName=\"ms-resource:AppName\"\n              Description=\"ms-resource:AppDescription\"\n              BackgroundColor=\"transparent\"\n              Square150x150Logo=\"Images\\Square150x150Logo.png\"\n              Square44x44Logo=\"Images\\Square44x44Logo.png\"\n              AppListEntry=\"none\">\n                <uap:DefaultTile Wide310x150Logo=\"Images\\Wide310x150Logo.png\" ShortName=\"ms-resource:AppShortName\" Square71x71Logo=\"Images\\SmallTile.png\" Square310x310Logo=\"Images\\LargeTile.png\"/>\n                <uap:SplashScreen Image=\"Images\\SplashScreen.png\"/>\n            </uap:VisualElements>\n            <Extensions>\n                <uap3:Extension Category=\"windows.appExecutionAlias\" Executable=\"wsl.exe\" EntryPoint=\"Windows.FullTrustApplication\">\n                    <uap3:AppExecutionAlias>\n                        <desktop:ExecutionAlias Alias=\"bash.exe\"/>\n                        <desktop:ExecutionAlias Alias=\"wsl.exe\"/>\n                        <desktop:ExecutionAlias Alias=\"wslconfig.exe\"/>\n                    </uap3:AppExecutionAlias>\n                </uap3:Extension>\n                <uap3:Extension Category=\"windows.appExtension\">\n                    <uap3:AppExtension Name=\"com.microsoft.windows.wsl\"\n                        Id=\"Wsl-EntryPoint\"\n                        DisplayName=\"WSL entry point\"\n                        Description=\"Entry point for the Windows Subsystem for Linux\"\n                        PublicFolder=\"Public\">\n                        <uap3:Properties>\n                            <Clsid>{a9b7a1b9-0671-405c-95f1-e0612cb4ce7e}</Clsid>\n                            <EntryPoint>wsl.exe</EntryPoint>\n                        </uap3:Properties>\n                    </uap3:AppExtension>\n                </uap3:Extension>\n            </Extensions>\n        </Application>\n    </Applications>\n    <Capabilities>\n        <Capability Name=\"internetClient\"/>\n        <rescap:Capability Name=\"runFullTrust\"/>\n        <rescap:Capability Name=\"unvirtualizedResources\"/>\n        <rescap:Capability Name=\"localSystemServices\"/>\n    </Capabilities>\n</Package>\n"
  },
  {
    "path": "msixgluepackage/CMakeLists.txt",
    "content": "add_appx_target(msixgluepackage\n                \"wsl.exe\"\n                \"${CMAKE_CURRENT_LIST_DIR}/AppxManifest.in\"\n                \"${BIN}/gluepackage.msix\"\n                \"wsl\")"
  },
  {
    "path": "msixinstaller/AppxManifest.in",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Package xmlns=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10\"\n         xmlns:uap=\"http://schemas.microsoft.com/appx/manifest/uap/windows10\"\n         xmlns:uap3=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/3\"\n         xmlns:uap10=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/10\"\n         xmlns:rescap=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities\"\n         xmlns:desktop=\"http://schemas.microsoft.com/appx/manifest/desktop/windows10\"\n         xmlns:desktop6=\"http://schemas.microsoft.com/appx/manifest/desktop/windows10/6\"\n         xmlns:desktop7=\"http://schemas.microsoft.com/appx/manifest/desktop/windows10/7\"\n         xmlns:com=\"http://schemas.microsoft.com/appx/manifest/com/windows10\"\n         xmlns:com2=\"http://schemas.microsoft.com/appx/manifest/com/windows10/2\"\n         xmlns:com3=\"http://schemas.microsoft.com/appx/manifest/com/windows10/3\"\n         IgnorableNamespaces=\"uap uap3 uap10 rescap desktop desktop6 com com2 com3\">\n\n    <Identity Name=\"MicrosoftCorporationII.WindowsSubsystemForLinux\" Publisher=\"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US\" Version=\"${PACKAGE_VERSION}\" ProcessorArchitecture=\"${TARGET_PLATFORM}\"/>\n    <Properties>\n        <DisplayName>ms-resource:AppName</DisplayName>\n        <PublisherDisplayName>Microsoft Corp.</PublisherDisplayName>\n        <Logo>Images\\StoreLogo.png</Logo>\n        <desktop6:FileSystemWriteVirtualization>disabled</desktop6:FileSystemWriteVirtualization>\n        <desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>\n        <uap10:PackageIntegrity>\n            <uap10:Content Enforcement=\"on\"/>\n        </uap10:PackageIntegrity>\n    </Properties>\n    <Dependencies>\n        <TargetDeviceFamily Name=\"Windows.Desktop\" MinVersion=\"10.0.19041.0\" MaxVersionTested=\"10.0.26100.0\"/>\n    </Dependencies>\n    <Resources>\n        ${SUPPORTED_LANGS_MANIFEST_ENTRIES}\n        <Resource uap:Scale=\"200\"/>\n    </Resources>\n    <Applications>\n        <Application Id=\"wsl\"\n          Executable=\"wsl.exe\"\n          EntryPoint=\"Windows.FullTrustApplication\"\n          uap10:Parameters=\"--cd ~\">\n            <uap:VisualElements\n              DisplayName=\"ms-resource:AppName\"\n              Description=\"ms-resource:AppDescription\"\n              BackgroundColor=\"transparent\"\n              Square150x150Logo=\"Images\\Square150x150Logo.png\"\n              Square44x44Logo=\"Images\\Square44x44Logo.png\"\n              AppListEntry=\"none\">\n                <uap:DefaultTile Wide310x150Logo=\"Images\\Wide310x150Logo.png\" ShortName=\"ms-resource:AppShortName\" Square71x71Logo=\"Images\\SmallTile.png\" Square310x310Logo=\"Images\\LargeTile.png\"/>\n                <uap:SplashScreen Image=\"Images\\SplashScreen.png\"/>\n            </uap:VisualElements>\n            <Extensions>\n                <desktop6:Extension Category=\"windows.service\" Executable=\"wslinstaller.exe\" EntryPoint=\"main\">\n                    <desktop6:Service Name=\"WslInstaller\" StartupType=\"auto\" StartAccount=\"localSystem\">\n                    </desktop6:Service>\n                </desktop6:Extension>\n                <uap3:Extension Category=\"windows.appExecutionAlias\" Executable=\"wsl.exe\" EntryPoint=\"Windows.FullTrustApplication\">\n                    <uap3:AppExecutionAlias>\n                        <desktop:ExecutionAlias Alias=\"bash.exe\"/>\n                        <desktop:ExecutionAlias Alias=\"wsl.exe\"/>\n                        <desktop:ExecutionAlias Alias=\"wslconfig.exe\"/>\n                    </uap3:AppExecutionAlias>\n                </uap3:Extension>\n                <uap3:Extension Category=\"windows.appExtension\">\n                    <uap3:AppExtension Name=\"com.microsoft.windows.wsl\"\n                        Id=\"Wsl-EntryPoint\"\n                        DisplayName=\"WSL entry point\"\n                        Description=\"Entry point for the Windows Subsystem for Linux\"\n                        PublicFolder=\"Public\">\n                        <uap3:Properties>\n                            <Clsid>{a9b7a1b9-0671-405c-95f1-e0612cb4ce7e}</Clsid>\n                            <EntryPoint>wsl.exe</EntryPoint>\n                        </uap3:Properties>\n                    </uap3:AppExtension>\n                </uap3:Extension>\n                <com2:Extension Category=\"windows.comServer\">\n                    <com2:ComServer>\n                        <com3:ServiceServer ServiceName=\"WslInstaller\"\n                            LaunchAndActivationPermission=\"O:BAG:BAD:(A;;11;;;AU)(A;;11;;;PS)(A;;11;;;SY)\">\n                            <com3:Class Id=\"B5AEB4C3-9541-492F-AD4D-505951F6ADA4\" DisplayName=\"WslInstaller\"/>\n                        </com3:ServiceServer>\n                    </com2:ComServer>\n                </com2:Extension>\n                <com2:Extension Category=\"windows.comInterface\">\n                    <com2:ComInterface>\n                        <com:Interface Id=\"1E912664-C599-474A-A552-DAEAF73B3164\" ProxyStubClsid=\"CE1044F6-36C5-4599-A5A8-3BBF27BA5495\"/>\n                        <com:ProxyStub Id=\"CE1044F6-36C5-4599-A5A8-3BBF27BA5495\" Path=\"WslInstallerProxyStub.dll\"></com:ProxyStub>\n                    </com2:ComInterface>\n                </com2:Extension>\n            </Extensions>\n        </Application>\n    </Applications>\n    <Capabilities>\n        <Capability Name=\"internetClient\"/>\n        <rescap:Capability Name=\"runFullTrust\"/>\n        <rescap:Capability Name=\"unvirtualizedResources\"/>\n        <rescap:Capability Name=\"packagedServices\"/>\n        <rescap:Capability Name=\"localSystemServices\"/>\n        <rescap:Capability Name=\"packageManagement\"/>\n    </Capabilities>\n</Package>\n"
  },
  {
    "path": "msixinstaller/CMakeLists.txt",
    "content": "set(DEPENDENCIES \"wsl.exe;wslinstaller.exe;wslinstallerproxystub.dll\")\n\n# Don't include the MSI if building a thin package.\nif (NOT \"${WSL_BUILD_THIN_PACKAGE}\")\n    list(APPEND DEPENDENCIES \"wsl.msi\")\nendif()\n\nadd_appx_target(msixinstallerpackage\n                \"${DEPENDENCIES}\"\n                \"${CMAKE_CURRENT_LIST_DIR}/AppxManifest.in\"\n                \"${BIN}/installer.msix\"\n                \"wsl;wslinstaller;msipackage;wslinstallerproxystub\")\n\nset(ARM64_BUNDLE_PATH ${CMAKE_SOURCE_DIR}/bin/arm64/${CMAKE_BUILD_TYPE}/installer.msix)\nset(X64_BUNDLE_PATH ${CMAKE_SOURCE_DIR}/bin/X64/${CMAKE_BUILD_TYPE}/installer.msix)\n\n# Only build the bundle if it exists\nif ((TARGET_PLATFORM STREQUAL \"arm64\" AND EXISTS ${X64_BUNDLE_PATH}) OR (TARGET_PLATFORM STREQUAL \"x64\" AND EXISTS ${ARM64_BUNDLE_PATH}))\n    set(PACKAGE_NAME \"Microsoft.WSL_${PACKAGE_VERSION}\")\n    set(BUNDLE_DIR ${CMAKE_SOURCE_DIR}/bundle/${CMAKE_BUILD_TYPE})\n    set(BUNDLE_MAPPINGS_FILE ${BUNDLE_DIR}/mappings.ini)\n\n    set(OUTPUT_BUNDLE ${BUNDLE_DIR}/${PACKAGE_NAME}_x64_ARM64.msixbundle)\n    file(MAKE_DIRECTORY ${BUNDLE_DIR})\n\n    file(WRITE ${BUNDLE_MAPPINGS_FILE} \"[Files]\\n\")\n    file(APPEND ${BUNDLE_MAPPINGS_FILE} \"\\\"${X64_BUNDLE_PATH}\\\"\" \" \\\"${PACKAGE_NAME}_x64.msix\\\"\\n\")\n    file(APPEND ${BUNDLE_MAPPINGS_FILE} \"\\\"${ARM64_BUNDLE_PATH}\\\"\" \" \\\"${PACKAGE_NAME}_ARM64.msix\\\"\\n\")\n\n    add_custom_command(\n        OUTPUT ${OUTPUT_BUNDLE}\n        COMMAND makeappx.exe bundle -f ${BUNDLE_MAPPINGS_FILE} -bv ${PACKAGE_VERSION} -o -p ${OUTPUT_BUNDLE}\n        COMMAND ${PACKAGE_SIGN_COMMAND} ${OUTPUT_BUNDLE}\n        COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/bundle\"\n        VERBATIM\n        DEPENDS ${CMAKE_SOURCE_DIR}/bin/X64/${CMAKE_BUILD_TYPE}/installer.msix ${CMAKE_SOURCE_DIR}/bin/arm64/${CMAKE_BUILD_TYPE}/installer.msix\n    )\n\n    set_source_files_properties(${OUTPUT_BUNDLE} PROPERTIES GENERATED TRUE)\n    add_custom_target(bundle DEPENDS ${OUTPUT_BUNDLE})\n    add_dependencies(bundle msixinstallerpackage)\n    set_target_properties(bundle PROPERTIES EXCLUDE_FROM_ALL FALSE)\nendif()"
  },
  {
    "path": "nuget/CMakeLists.txt",
    "content": "set(NUGET_PACKAGES Microsoft.WSL.PluginApi.nuspec)\n\n# generate vars with native paths since nuget won't accept unix path separators\ncmake_path(NATIVE_PATH CMAKE_SOURCE_DIR CMAKE_SOURCE_DIR_NATIVE)\ncmake_path(NATIVE_PATH CMAKE_BINARY_DIR CMAKE_BINARY_DIR_NATIVE)\n\nforeach(PACKAGE ${NUGET_PACKAGES})\n    configure_file(\"${CMAKE_CURRENT_LIST_DIR}/${PACKAGE}.in\" \"${CMAKE_BINARY_DIR}/${PACKAGE}\")\nendforeach()"
  },
  {
    "path": "nuget/Microsoft.WSL.PluginApi.nuspec.in",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<package xmlns=\"http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd\">\n  <metadata>\n    <id>Microsoft.WSL.PluginApi</id>\n    <version>${WSL_NUGET_PACKAGE_VERSION}</version>\n    <authors>Microsoft</authors>\n    <projectUrl>https://github.com/microsoft/WSL</projectUrl>\n    <description>WSL Plugin Interface</description>\n    <copyright>© Microsoft Corporation. All rights reserved.</copyright>\n    <icon>images\\icon.png</icon>\n    <readme>docs\\README.MD</readme>\n    <tags>WSL</tags>\n    <language>en-us</language>\n    <license type=\"expression\">MIT</license>\n  </metadata>\n  <files>\n    <file src=\"${CMAKE_SOURCE_DIR_NATIVE}\\src\\windows\\inc\\WslPluginApi.h\" target=\"build\\native\\include\"/>\n    <file src=\"${CMAKE_SOURCE_DIR_NATIVE}\\Images\\Square44x44Logo.altform-lightunplated_targetsize-256.png\" target=\"images\\icon.png\"/>\n    <file src=\"${CMAKE_SOURCE_DIR_NATIVE}\\nuget\\README.WslPluginApi.MD\" target=\"docs\\README.MD\"/>\n  </files>\n</package>\n"
  },
  {
    "path": "nuget/README.WslPluginApi.MD",
    "content": "# WSL plugin api\n\nThis package contains the `WslPluginApi.h` header which defines the WSL plugin interface.\n\nFor more details, see: https://learn.microsoft.com/windows/wsl/ ."
  },
  {
    "path": "nuget.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<configuration>\r\n    <config>\r\n        <add key=\"defaultPushSource\" value=\"https://pkgs.dev.azure.com/shine-oss/wsl/_packaging/WslDependencies/nuget/v3/index.json\" />\r\n    </config>\r\n    <solution>\r\n        <add key=\"disableSourceControlIntegration\" value=\"true\" />\r\n    </solution>\r\n    <activePackageSource>\r\n        <add key=\"All\" value=\"(Aggregate source)\" />\r\n    </activePackageSource>\r\n    <packageRestore>\r\n        <!-- Allow NuGet to download missing packages -->\r\n        <add key=\"enabled\" value=\"True\" />\r\n\r\n        <!-- Automatically check for missing packages during build in Visual Studio -->\r\n        <add key=\"automatic\" value=\"True\" />\r\n    </packageRestore>\r\n    <packageSources>\r\n        <clear />\r\n        <add key=\"WSL\" value=\"https://pkgs.dev.azure.com/shine-oss/wsl/_packaging/WslDependencies/nuget/v3/index.json\" />\r\n    </packageSources>\r\n    <disabledPackageSources>\r\n        <!-- Override any User and Computer NuGet package settings to guarantee\r\n        Project only (above sources) are enabled and the only ones present. -->\r\n        <clear />\r\n    </disabledPackageSources>\r\n</configuration>\r\n\r\n"
  },
  {
    "path": "packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"CommunityToolkit.Mvvm\" version=\"8.4.0\" />\n  <package id=\"CommunityToolkit.WinUI.Animations\" version=\"8.2.250402\" />\n  <package id=\"CommunityToolkit.WinUI.Controls.SettingsControls\" version=\"8.2.250402\" />\n  <package id=\"Microsoft.Direct3D.Linux\" version=\"1.611.1-81528511\" targetFramework=\"native\" />\n  <package id=\"Microsoft.DXCore.Linux.amd64fre\" version=\"10.0.26100.1-240331-1435.ge-release\" targetFramework=\"native\" />\n  <package id=\"Microsoft.DXCore.Linux.arm64fre\" version=\"10.0.26100.1-240331-1435.ge-release\" targetFramework=\"native\" />\n  <package id=\"Microsoft.Extensions.Hosting\" version=\"10.0.0\" />\n  <package id=\"Microsoft.Identity.MSAL.WSL.Proxy\" version=\"0.1.1\" />\n  <package id=\"Microsoft.NETCore.App.Runtime.win-arm64\" version=\"10.0.4\" />\n  <package id=\"Microsoft.NETCore.App.Runtime.win-x64\" version=\"10.0.4\" />\n  <package id=\"Microsoft.RemoteDesktop.Client.MSRDC.SessionHost\" version=\"1.2.6676\" />\n  <package id=\"Microsoft.Taef\" version=\"10.100.251104001\" targetFramework=\"native\" />\n  <package id=\"Microsoft.Windows.ImplementationLibrary\" version=\"1.0.251108.1\" targetFramework=\"native\" />\n  <package id=\"Microsoft.Windows.SDK.NET.Ref\" version=\"10.0.26100.81\" />\n  <package id=\"Microsoft.WindowsAppSDK\" version=\"1.8.251106002\" />\n  <package id=\"Microsoft.WSL.bsdtar\" version=\"0.0.2-2\" />\n  <package id=\"Microsoft.WSL.Dependencies.amd64fre\" version=\"10.0.27820.1000-250318-1700.rs-base2-hyp\" targetFramework=\"native\" />\n  <package id=\"Microsoft.WSL.Dependencies.arm64fre\" version=\"10.0.27820.1000-250318-1700.rs-base2-hyp\" targetFramework=\"native\" />\n  <package id=\"Microsoft.WSL.DeviceHost\" version=\"1.1.39-0\" />\n  <package id=\"Microsoft.WSL.Kernel\" version=\"6.6.114.1-1\" targetFramework=\"native\" />\n  <package id=\"Microsoft.WSL.LinuxSdk\" version=\"1.20.0\" targetFramework=\"native\" />\n  <package id=\"Microsoft.WSL.TestDistro\" version=\"2.5.7-47\" />\n  <package id=\"Microsoft.WSLg\" version=\"1.0.73\" />\n  <package id=\"Microsoft.Xaml.Behaviors.WinUI.Managed\" version=\"3.0.0\" />\n  <package id=\"vswhere\" version=\"3.1.7\" />\n  <package id=\"WinUIEx\" version=\"2.9.0\" />\n  <package id=\"Wix\" version=\"5.0.2\" />\n</packages>\n"
  },
  {
    "path": "src/linux/inc/lxdef.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n// Lxcore type definitions.\ntypedef int LX_INT;\ntypedef unsigned long LX_UID_T;\ntypedef unsigned long LX_GID_T;\ntypedef unsigned long LX_MODE_T;\n\n// Windows-like type definitions\ntypedef std::uint8_t UINT8;\ntypedef std::int8_t INT8;\ntypedef std::uint16_t UINT16;\ntypedef std::int16_t INT16;\ntypedef std::uint32_t UINT32;\ntypedef std::int32_t INT32;\ntypedef std::uint64_t UINT64;\ntypedef std::int64_t INT64;\ntypedef std::uintptr_t ULONG_PTR;\ntypedef unsigned char UCHAR;\ntypedef const char* PCSTR;\n\ntypedef struct _GUID\n{\n    std::uint32_t Data1;\n    std::uint16_t Data2;\n    std::uint16_t Data3;\n    std::uint8_t Data4[8];\n\n    bool operator==(const _GUID& other) const\n    {\n        return memcmp(this, &other, sizeof(*this)) == 0;\n    }\n\n    bool operator!=(const _GUID& other) const\n    {\n        return !(*this == other);\n    }\n} GUID;\n\nstatic_assert(sizeof(GUID) == 16);\n\n// Lxcore versions of error values.\n// N.B. These are negative.\n#define LX_EPERM (-EPERM)\n#define LX_ENOENT (-ENOENT)\n#define LX_ESRCH (-ESRCH)\n#define LX_EINTR (-EINTR)\n#define LX_EIO (-EIO)\n#define LX_ENXIO (-ENXIO)\n#define LX_ENOEXEC (-ENOEXEC)\n#define LX_E2BIG (-E2BIG)\n#define LX_EBADF (-EBADF)\n#define LX_ECHILD (-ECHILD)\n#define LX_EAGAIN (-EAGAIN)\n#define LX_EWOULDBLOCK (-EWOULDBLOCK)\n#define LX_ENOMEM (-ENOMEM)\n#define LX_EACCES (-EACCES)\n#define LX_EFAULT (-EFAULT)\n#define LX_EBUSY (-EBUSY)\n#define LX_EEXIST (-EEXIST)\n#define LX_EXDEV (-EXDEV)\n#define LX_ENODEV (-ENODEV)\n#define LX_ENOTDIR (-ENOTDIR)\n#define LX_EISDIR (-EISDIR)\n#define LX_EINVAL (-EINVAL)\n#define LX_ENFILE (-ENFILE)\n#define LX_EMFILE (-EMFILE)\n#define LX_ENOTTY (-ENOTTY)\n#define LX_EFBIG (-EFBIG)\n#define LX_ENOSPC (-ENOSPC)\n#define LX_ESPIPE (-ESPIPE)\n#define LX_EROFS (-EROFS)\n#define LX_EMLINK (-EMLINK)\n#define LX_EPIPE (-EPIPE)\n#define LX_ERANGE (-ERANGE)\n#define LX_EDEADLK (-EDEADLK)\n#define LX_ENAMETOOLONG (-ENAMETOOLONG)\n#define LX_ENOLCK (-ENOLCK)\n#define LX_ENOSYS (-ENOSYS)\n#define LX_ENOTEMPTY (-ENOTEMPTY)\n#define LX_ELOOP (-ELOOP)\n#define LX_EIDRM (-EIDRM)\n#define LX_ENODATA (-ENODATA)\n#define LX_ENOATTR (-ENOATTR)\n#define LX_EPROTO (-EPROTO)\n#define LX_EOVERFLOW (-EOVERFLOW)\n#define LX_EUSERS (-EUSERS)\n#define LX_ENOTSOCK (-ENOTSOCK)\n#define LX_EDESTADDRREQ (-EDESTADDRREQ)\n#define LX_EMSGSIZE (-EMSGSIZE)\n#define LX_EPROTOTYPE (-EPROTOTYPE)\n#define LX_ENOPROTOOPT (-ENOPROTOOPT)\n#define LX_EPROTONOSUPPORT (-EPROTONOSUPPORT)\n#define LX_ESOCKTNOSUPPORT (-ESOCKTNOSUPPORT)\n#define LX_EOPNOTSUPP (-EOPNOTSUPP)\n#define LX_ENOTSUP (-ENOTSUP)\n#define LX_EAFNOSUPPORT (-EAFNOSUPPORT)\n#define LX_EADDRINUSE (-EADDRINUSE)\n#define LX_EADDRNOTAVAIL (-EADDRNOTAVAIL)\n#define LX_ENETUNREACH (-ENETUNREACH)\n#define LX_ECONNABORTED (-ECONNABORTED)\n#define LX_ECONNRESET (-ECONNRESET)\n#define LX_ENOBUFS (-ENOBUFS)\n#define LX_EISCONN (-EISCONN)\n#define LX_ENOTCONN (-ENOTCONN)\n#define LX_ETIMEDOUT (-ETIMEDOUT)\n#define LX_ECONNREFUSED (-ECONNREFUSED)\n#define LX_EHOSTDOWN (-EHOSTDOWN)\n#define LX_EHOSTUNREACH (-EHOSTUNREACH)\n#define LX_EALREADY (-EALREADY)\n#define LX_EINPROGRESS (-EINPROGRESS)\n#define LX_ENOMEDIUM (-ENOMEDIUM)\n#define LX_EMEDIUMTYPE (-EMEDIUMTYPE)\n#define LX_ECANCELED (-ECANCELED)\n#define LX_ENOKEY (-ENOKEY)\n\n// Lxcore version of constants\n#define LX_PATH_MAX (PATH_MAX)\n#define LX_DT_DIR (DT_DIR)\n#define LX_DT_LNK (DT_LNK)\n\n// Other definitions available on NT.\nextern \"C++\" {\n\ntemplate <size_t S>\nstruct _ENUM_FLAG_INTEGER_FOR_SIZE;\n\ntemplate <>\nstruct _ENUM_FLAG_INTEGER_FOR_SIZE<1>\n{\n    typedef INT8 type;\n};\n\ntemplate <>\nstruct _ENUM_FLAG_INTEGER_FOR_SIZE<2>\n{\n    typedef INT16 type;\n};\n\ntemplate <>\nstruct _ENUM_FLAG_INTEGER_FOR_SIZE<4>\n{\n    typedef INT32 type;\n};\n\ntemplate <>\nstruct _ENUM_FLAG_INTEGER_FOR_SIZE<8>\n{\n    typedef INT64 type;\n};\n\n// used as an approximation of std::underlying_type<T>\ntemplate <class T>\nstruct _ENUM_FLAG_SIZED_INTEGER\n{\n    typedef typename _ENUM_FLAG_INTEGER_FOR_SIZE<sizeof(T)>::type type;\n};\n}\n\n#define _ENUM_FLAG_CONSTEXPR constexpr\n#define DEFINE_ENUM_FLAG_OPERATORS(ENUMTYPE) \\\n    extern \"C++\" { \\\n    inline _ENUM_FLAG_CONSTEXPR ENUMTYPE operator|(ENUMTYPE a, ENUMTYPE b) throw() \\\n    { \\\n        return ENUMTYPE(((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)a) | ((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)b)); \\\n    } \\\n    inline ENUMTYPE& operator|=(ENUMTYPE& a, ENUMTYPE b) throw() \\\n    { \\\n        return (ENUMTYPE&)(((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type&)a) |= ((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)b)); \\\n    } \\\n    inline _ENUM_FLAG_CONSTEXPR ENUMTYPE operator&(ENUMTYPE a, ENUMTYPE b) throw() \\\n    { \\\n        return ENUMTYPE(((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)a) & ((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)b)); \\\n    } \\\n    inline ENUMTYPE& operator&=(ENUMTYPE& a, ENUMTYPE b) throw() \\\n    { \\\n        return (ENUMTYPE&)(((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type&)a) &= ((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)b)); \\\n    } \\\n    inline _ENUM_FLAG_CONSTEXPR ENUMTYPE operator~(ENUMTYPE a) throw() \\\n    { \\\n        return ENUMTYPE(~((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)a)); \\\n    } \\\n    inline _ENUM_FLAG_CONSTEXPR ENUMTYPE operator^(ENUMTYPE a, ENUMTYPE b) throw() \\\n    { \\\n        return ENUMTYPE(((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)a) ^ ((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)b)); \\\n    } \\\n    inline ENUMTYPE& operator^=(ENUMTYPE& a, ENUMTYPE b) throw() \\\n    { \\\n        return (ENUMTYPE&)(((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type&)a) ^= ((_ENUM_FLAG_SIZED_INTEGER<ENUMTYPE>::type)b)); \\\n    } \\\n    }\n\ntypedef struct _LIST_ENTRY\n{\n    struct _LIST_ENTRY* Flink;\n    struct _LIST_ENTRY* Blink;\n} LIST_ENTRY, *PLIST_ENTRY;\n\ninline void InitializeListHead(PLIST_ENTRY listHead)\n{\n    listHead->Flink = listHead->Blink = listHead;\n}\n\n[[nodiscard]] inline bool IsListEmpty(const LIST_ENTRY* listHead)\n{\n    return listHead->Flink == listHead;\n}\n\ninline void InsertTailList(PLIST_ENTRY listHead, PLIST_ENTRY entry)\n{\n    PLIST_ENTRY blink = listHead->Blink;\n    entry->Flink = listHead;\n    entry->Blink = blink;\n    blink->Flink = entry;\n    listHead->Blink = entry;\n}\n\ninline bool RemoveEntryList(PLIST_ENTRY entry)\n{\n    PLIST_ENTRY flink = entry->Flink;\n    PLIST_ENTRY blink = entry->Blink;\n    blink->Flink = flink;\n    flink->Blink = blink;\n    return flink == blink;\n}\n\n#define CONTAINING_RECORD(address, type, field) ((type*)((char*)(address) - (ULONG_PTR)(&((type*)0)->field)))\n"
  },
  {
    "path": "src/linux/inc/lxwil.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <unistd.h>\n#include <dirent.h>\n#include <sys/types.h>\n#include <sstream>\n#include <optional>\n#include <assert.h>\n\nnamespace wil {\n\n#define STRING_TO_WSTRING_IMPL(Str) L##Str\n#define STRING_TO_WSTRING(Str) STRING_TO_WSTRING_IMPL(Str)\n#define TEXT(X) X\n#define FAIL_FAST() raise(SIGABRT);\n#define FAIL_FAST_CAUGHT_EXCEPTION() FAIL_FAST()\n#define FAIL_FAST_IF(condition) \\\n    if ((condition)) \\\n    { \\\n        FAIL_FAST() \\\n    }\n\ntypedef void LogFunction(const char* message, const char* exceptionDescription) noexcept;\n__declspec(selectany) LogFunction* g_LogExceptionCallback{};\n\nnamespace details {\n    struct FailureInfo\n    {\n        const char* File;\n        int Line;\n        const char* Function;\n    };\n} // namespace details\n\nclass ResultException : public std::exception\n{\npublic:\n    ResultException(int result, details::FailureInfo info) noexcept : m_Result{result}, m_Info{info}\n    {\n    }\n\n    ~ResultException() noexcept\n    {\n        delete[] m_What;\n    }\n\n    const char* what() const noexcept override\n    {\n        constexpr size_t bufferSize = 4096;\n        if (m_What == nullptr)\n        {\n            m_What = new (std::nothrow) char[bufferSize]{};\n            if (m_What == nullptr)\n            {\n                return strerror(m_Result);\n            }\n\n            snprintf(m_What, bufferSize, \"%s @%s:%d (%s)\\n\", strerror(m_Result), m_Info.File, m_Info.Line, m_Info.Function);\n        }\n\n        return m_What;\n    }\n\n    int GetErrorCode() const noexcept\n    {\n        return m_Result;\n    }\n\nprivate:\n    mutable char* m_What{};\n    int m_Result;\n    details::FailureInfo m_Info;\n};\n\nclass ExceptionWithUserMessage : public std::exception\n{\npublic:\n    ExceptionWithUserMessage(std::string&& message) : m_message(std::move(message))\n    {\n    }\n\n    const char* what() const noexcept override\n    {\n        return m_message.c_str();\n    }\n\nprivate:\n    std::string m_message;\n};\n\nnamespace details {\n    template <typename TLambda>\n    class lambda_call\n    {\n    public:\n        lambda_call(const lambda_call&) = delete;\n        lambda_call& operator=(const lambda_call&) = delete;\n        lambda_call& operator=(lambda_call&& other) = delete;\n\n        explicit lambda_call(TLambda&& lambda) noexcept : m_lambda(std::move(lambda))\n        {\n            static_assert(std::is_same<decltype(lambda()), void>::value, \"scope_exit lambdas must not have a return value\");\n            static_assert(\n                !std::is_lvalue_reference<TLambda>::value && !std::is_rvalue_reference<TLambda>::value,\n                \"scope_exit should only be directly used with a lambda\");\n        }\n\n        lambda_call(lambda_call&& other) noexcept : m_lambda(std::move(other.m_lambda)), m_call(other.m_call)\n        {\n            other.m_call = false;\n        }\n\n        ~lambda_call() noexcept\n        {\n            reset();\n        }\n\n        // Ensures the scope_exit lambda will not be called\n        void release() noexcept\n        {\n            m_call = false;\n        }\n\n        // Executes the scope_exit lambda immediately if not yet run; ensures it will not run again\n        void reset() noexcept\n        {\n            if (m_call)\n            {\n                m_call = false;\n                m_lambda();\n            }\n        }\n\n        // Returns true if the scope_exit lambda is still going to be executed\n        explicit operator bool() const noexcept\n        {\n            return m_call;\n        }\n\n    protected:\n        TLambda m_lambda;\n        bool m_call = true;\n    };\n\n    inline void ThrowErrorIf(bool condition, int error, FailureInfo info)\n    {\n        if (condition)\n        {\n            throw ::wil::ResultException(error, info);\n        }\n    }\n\n    inline void LogFailure(const char* message, const char* exceptionDescription) noexcept\n    {\n        auto callback = g_LogExceptionCallback;\n        if (callback != nullptr)\n        {\n            callback(message, exceptionDescription);\n        }\n        else\n        {\n            if (message != nullptr)\n            {\n                fputs(message, stderr);\n                fputs(\"\\n\", stderr);\n            }\n\n            if (exceptionDescription != nullptr)\n            {\n                fputs(\"Exception: \", stderr);\n                fputs(exceptionDescription, stderr);\n                fputs(\"\\n\", stderr);\n            }\n        }\n    }\n\n    inline void LogCaughtException(const char* message)\n    {\n        try\n        {\n            throw;\n        }\n        catch (const std::exception& ex)\n        {\n            LogFailure(message, ex.what());\n        }\n        catch (...)\n        {\n            LogFailure(message, nullptr);\n        }\n    }\n} // namespace details\n\ninline int ResultFromCaughtException()\n{\n    try\n    {\n        throw;\n    }\n    catch (wil::ResultException& ex)\n    {\n        return ex.GetErrorCode();\n    }\n    catch (std::bad_alloc&)\n    {\n        return ENOMEM;\n    }\n    catch (...)\n    {\n    }\n\n    // Unknown exception type.\n    return EINVAL;\n}\n\n#define __WIL_ERROR_INFO {__FILE__, __LINE__, __FUNCTION__}\n\n#define THROW_ERRNO(Error) throw ::wil::ResultException(Error, __WIL_ERROR_INFO)\n#define THROW_USER_ERROR(Message) throw ::wil::ExceptionWithUserMessage((Message))\n#define THROW_ERRNO_IF(Error, Condition) ::wil::details::ThrowErrorIf((Condition), (Error), __WIL_ERROR_INFO)\n#define THROW_LAST_ERROR_IF(Condition) THROW_ERRNO_IF(errno, (Condition));\n#define THROW_LAST_ERROR() THROW_ERRNO(errno);\n\n#define THROW_INVALID() THROW_ERRNO(EINVAL)\n#define THROW_UNEXCEPTED() THROW_ERRNO(EINVAL)\n#define THROW_INVALID_IF(Condition) THROW_ERRNO_IF(EINVAL, (Condition))\n#define THROW_UNEXPECTED_IF(Condition) THROW_ERRNO_IF(EINVAL, (Condition))\n\n#define LOG_CAUGHT_EXCEPTION() ::wil::details::LogCaughtException(nullptr);\n#define LOG_CAUGHT_EXCEPTION_MSG(msg) ::wil::details::LogCaughtException(msg);\n#define RETURN_CAUGHT_EXCEPTION() return -::wil::ResultFromCaughtException()\n#define CATCH_RETURN() \\\n    catch (...) \\\n    { \\\n        RETURN_CAUGHT_EXCEPTION(); \\\n    }\n#define CATCH_RETURN_ERRNO() \\\n    catch (...) \\\n    { \\\n        LOG_CAUGHT_EXCEPTION(); \\\n        errno = ::wil::ResultFromCaughtException(); \\\n        return -1; \\\n    }\n\n#define CATCH_LOG() \\\n    catch (...) \\\n    { \\\n        LOG_CAUGHT_EXCEPTION(); \\\n    }\n#define CATCH_LOG_MSG(msg) \\\n    catch (...) \\\n    { \\\n        LOG_CAUGHT_EXCEPTION_MSG(msg); \\\n    }\n\nclass unique_dir\n{\npublic:\n    static constexpr DIR* invalid_dir = nullptr;\n\n    unique_dir(DIR* dir = invalid_dir) noexcept : m_Dir{dir}\n    {\n    }\n\n    ~unique_dir() noexcept\n    {\n        reset();\n    }\n\n    unique_dir(const unique_dir&) = delete;\n    unique_dir& operator=(const unique_dir&) = delete;\n\n    unique_dir(unique_dir&& other) noexcept : m_Dir{other.m_Dir}\n    {\n        other.m_Dir = invalid_dir;\n    }\n\n    unique_dir& operator=(unique_dir&& other) noexcept\n    {\n        std::swap(m_Dir, other.m_Dir);\n        return *this;\n    }\n\n    explicit operator bool() const noexcept\n    {\n        return m_Dir != invalid_dir;\n    }\n\n    DIR* get() const noexcept\n    {\n        return m_Dir;\n    }\n\n    void reset(DIR* dir = invalid_dir) noexcept\n    {\n        if (m_Dir != invalid_dir)\n        {\n            closedir(m_Dir);\n        }\n\n        m_Dir = dir;\n    }\n\n    DIR* release() noexcept\n    {\n        DIR* dir = m_Dir;\n        m_Dir = invalid_dir;\n        return dir;\n    }\n\n    friend void swap(unique_dir& dir1, unique_dir& dir2)\n    {\n        std::swap(dir1.m_Dir, dir2.m_Dir);\n    }\n\nprivate:\n    DIR* m_Dir;\n};\n\nclass unique_fd\n{\npublic:\n    static constexpr int invalid_fd = -1;\n\n    unique_fd(int fd = invalid_fd) noexcept : m_Fd{fd}\n    {\n    }\n\n    ~unique_fd() noexcept\n    {\n        reset();\n    }\n\n    unique_fd(const unique_fd&) = delete;\n    unique_fd& operator=(const unique_fd&) = delete;\n\n    unique_fd(unique_fd&& other) noexcept : m_Fd{other.m_Fd}\n    {\n        other.m_Fd = invalid_fd;\n    }\n\n    unique_fd& operator=(unique_fd&& other) noexcept\n    {\n        std::swap(m_Fd, other.m_Fd);\n        return *this;\n    }\n\n    explicit operator bool() const noexcept\n    {\n        return m_Fd >= 0;\n    }\n\n    int get() const noexcept\n    {\n        return m_Fd;\n    }\n\n    void reset(int fd = invalid_fd) noexcept\n    {\n        if (m_Fd >= 0)\n        {\n            close(m_Fd);\n        }\n\n        m_Fd = fd;\n    }\n\n    int release() noexcept\n    {\n        int fd = m_Fd;\n        m_Fd = invalid_fd;\n        return fd;\n    }\n\n    friend void swap(unique_fd& fd1, unique_fd& fd2)\n    {\n        std::swap(fd1.m_Fd, fd2.m_Fd);\n    }\n\nprivate:\n    int m_Fd;\n};\n\nclass unique_pipe\n{\npublic:\n    unique_pipe() = default;\n\n    unique_pipe(unique_fd&& readFd, unique_fd&& writeFd) noexcept : m_Read(std::move(readFd)), m_Write(std::move(writeFd))\n    {\n    }\n\n    unique_pipe(const unique_pipe&) = delete;\n    unique_pipe& operator=(const unique_pipe&) = delete;\n\n    unique_pipe(unique_pipe&& other) noexcept\n    {\n        m_Read = std::move(other.m_Read);\n        m_Write = std::move(other.m_Write);\n    }\n\n    unique_pipe& operator=(unique_pipe&& other) noexcept\n    {\n        m_Read = std::move(other.m_Read);\n        m_Write = std::move(other.m_Write);\n        return *this;\n    }\n\n    explicit operator bool() const noexcept\n    {\n        return m_Read || m_Write;\n    }\n\n    unique_fd& read()\n    {\n        return m_Read;\n    }\n\n    unique_fd& write()\n    {\n        return m_Write;\n    }\n\n    std::pair<unique_fd, unique_fd> release() noexcept\n    {\n        auto fds = std::make_pair(std::move(m_Read), std::move(m_Write));\n        return fds;\n    }\n\n    friend void swap(unique_pipe& left, unique_pipe& right)\n    {\n        std::swap(left.m_Read, right.m_Read);\n        std::swap(left.m_Write, right.m_Write);\n    }\n\n    static unique_pipe create(int flags)\n    {\n        int pipe[2] = {-1, -1};\n        if (pipe2(pipe, flags) < -1)\n        {\n            THROW_ERRNO(errno);\n        }\n\n        return unique_pipe(unique_fd(pipe[0]), unique_fd(pipe[1]));\n    }\n\nprivate:\n    unique_fd m_Read;\n    unique_fd m_Write;\n};\n\nclass unique_file\n{\npublic:\n    static constexpr FILE* invalid_file = nullptr;\n\n    unique_file(FILE* file = invalid_file) noexcept : m_File{file}\n    {\n    }\n\n    ~unique_file() noexcept\n    {\n        reset();\n    }\n\n    unique_file(const unique_file&) = delete;\n    unique_file& operator=(const unique_file&) = delete;\n\n    unique_file(unique_file&& other) noexcept : m_File{other.m_File}\n    {\n        other.m_File = invalid_file;\n    }\n\n    unique_file& operator=(unique_file&& other) noexcept\n    {\n        std::swap(m_File, other.m_File);\n        return *this;\n    }\n\n    explicit operator bool() const noexcept\n    {\n        return m_File != invalid_file;\n    }\n\n    FILE* get() const noexcept\n    {\n        return m_File;\n    }\n\n    void reset(FILE* file = invalid_file) noexcept\n    {\n        if (m_File != invalid_file)\n        {\n            fclose(m_File);\n        }\n\n        m_File = file;\n    }\n\n    FILE* release() noexcept\n    {\n        FILE* file = m_File;\n        m_File = invalid_file;\n        return file;\n    }\n\n    friend void swap(unique_file& file1, unique_file& file2)\n    {\n        std::swap(file1.m_File, file2.m_File);\n    }\n\nprivate:\n    FILE* m_File;\n};\n\n/** Returns an object that executes the given lambda when destroyed.\nCapture the object with 'auto'; use reset() to execute the lambda early or release() to avoid\nexecution.  Exceptions thrown in the lambda will fail-fast; use scope_exit_log to avoid. */\ntemplate <typename TLambda>\n[[nodiscard]] inline auto scope_exit(TLambda&& lambda) noexcept\n{\n    return details::lambda_call<TLambda>(std::forward<TLambda>(lambda));\n}\n\nnamespace details {\n    template <unsigned long long flag>\n    struct verify_single_flag_helper\n    {\n        static_assert((flag != 0) && ((flag & (flag - 1)) == 0), \"Single flag expected, zero or multiple flags found\");\n        static const unsigned long long value = flag;\n    };\n\n// Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value\n#define __WI_MAKE_UNSIGNED(val) \\\n    (sizeof(val) == 1   ? static_cast<unsigned char>(val) \\\n     : sizeof(val) == 2 ? static_cast<unsigned short>(val) \\\n     : sizeof(val) == 4 ? static_cast<unsigned long>(val) \\\n                        : static_cast<unsigned long long>(val))\n#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1)))\n#define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val))\n\n    template <typename TVal, typename TFlags>\n    inline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags)\n    {\n        return ((val & flags) == static_cast<decltype(val & flags)>(flags));\n    }\n\n    template <typename TVal>\n    inline constexpr bool IsSingleFlagSetHelper(TVal val)\n    {\n        return __WI_IS_SINGLE_FLAG_SET(val);\n    }\n\n    template <typename TVal>\n    inline constexpr bool IsClearOrSingleFlagSetHelper(TVal val)\n    {\n        return ((val == static_cast<std::remove_reference_t<TVal>>(0)) || IsSingleFlagSetHelper(val));\n    }\n\n    template <typename TVal, typename TMask, typename TFlags>\n    inline constexpr void UpdateFlagsInMaskHelper(TVal& val, TMask mask, TFlags flags)\n    {\n        val = static_cast<std::remove_reference_t<TVal>>((val & ~mask) | (flags & mask));\n    }\n\n    template <long>\n    struct variable_size;\n\n    template <>\n    struct variable_size<1>\n    {\n        typedef unsigned char type;\n    };\n\n    template <>\n    struct variable_size<2>\n    {\n        typedef unsigned short type;\n    };\n\n    template <>\n    struct variable_size<4>\n    {\n        typedef unsigned long type;\n    };\n\n    template <>\n    struct variable_size<8>\n    {\n        typedef unsigned long long type;\n    };\n\n    template <typename T>\n    struct variable_size_mapping\n    {\n        typedef typename variable_size<sizeof(T)>::type type;\n    };\n} // namespace details\n\n/** Defines the unsigned type of the same width (1, 2, 4, or 8 bytes) as the given type.\nThis allows code to generically convert any enum class to it's corresponding underlying type. */\ntemplate <typename T>\nusing integral_from_enum = typename details::variable_size_mapping<T>::type;\n\n#define WI_StaticAssertSingleBitSet(flag) \\\n    static_cast<decltype(flag)>(::wil::details::verify_single_flag_helper<static_cast<unsigned long long>(WI_EnumValue(flag))>::value)\n#define WI_IsAnyFlagSet(val, flags) \\\n    (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast<decltype((val) & (flags))>(0))\n#define WI_IsFlagSet(val, flag) WI_IsAnyFlagSet(val, WI_StaticAssertSingleBitSet(flag))\n#define WI_AreAllFlagsClear(val, flags) \\\n    (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast<decltype((val) & (flags))>(0))\n#define WI_IsAnyFlagClear(val, flags) (!wil::details::AreAllFlagsSetHelper(val, flags))\n#define WI_IsFlagClear(val, flag) WI_AreAllFlagsClear(val, WI_StaticAssertSingleBitSet(flag))\n#define WI_EnumValue(val) static_cast<::wil::integral_from_enum<decltype(val)>>(val)\n//! Evaluates as true if every bitflag specified in `flags` is set within `val`.\n#define WI_AreAllFlagsSet(val, flags) wil::details::AreAllFlagsSetHelper(val, flags)\n//! Set zero or more bitflags specified by `flags` in the variable `var`.\n#define WI_SetAllFlags(var, flags) ((var) |= (flags))\n//! Set a single compile-time constant `flag` in the variable `var`.\n#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag))\n//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true.\n#define WI_SetFlagIf(var, flag, condition) \\\n    do \\\n    { \\\n        if (condition) \\\n        { \\\n            WI_SetFlag(var, flag); \\\n        } \\\n    } while ((void)0, 0)\n//! Clear zero or more bitflags specified by `flags` from the variable `var`.\n#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags))\n//! Clear a single compile-time constant `flag` from the variable `var`.\n#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag))\n\n#define WI_ASSERT(condition) assert(condition)\n\n#define EMIT_USER_WARNING(Warning) \\\n    if (::wil::ScopedWarningsCollector::CanCollectWarning()) \\\n    { \\\n        ::wil::ScopedWarningsCollector::CollectWarning(Warning); \\\n    }\n\nclass ScopedWarningsCollector\n{\npublic:\n    ScopedWarningsCollector()\n    {\n        assert(!g_collectedWarnings.has_value());\n\n        g_collectedWarnings.emplace();\n    }\n\n    ~ScopedWarningsCollector()\n    {\n        assert(g_collectedWarnings.has_value());\n\n        g_collectedWarnings = {};\n    }\n\n    ScopedWarningsCollector(const ScopedWarningsCollector&) = delete;\n    ScopedWarningsCollector(ScopedWarningsCollector&&) = delete;\n    ScopedWarningsCollector& operator=(const ScopedWarningsCollector&) = delete;\n    ScopedWarningsCollector& operator=(ScopedWarningsCollector&&) = delete;\n\n    static bool CanCollectWarning()\n    {\n        return g_collectedWarnings.has_value();\n    }\n\n    static void CollectWarning(std::string&& warning)\n    {\n        assert(g_collectedWarnings.has_value());\n\n        (*g_collectedWarnings) << std::move(warning) << \"\\n\";\n    }\n\n    static std::string ConsumeWarnings()\n    {\n        if (!g_collectedWarnings.has_value())\n        {\n            return {};\n        }\n\n        auto warnings = g_collectedWarnings->str();\n\n        g_collectedWarnings = std::stringstream{};\n\n        return warnings;\n    }\n\nprivate:\n    static thread_local std::optional<std::stringstream> g_collectedWarnings;\n};\n\n} // namespace wil\n"
  },
  {
    "path": "src/linux/inc/seccomp_defs.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n// This file defines all the seccomp structures and constants that aren't available in the sdk yet\n\n#pragma once\n\n#include <unistd.h>\n\n// From include\\asm\\unist_32.h\n// Read socketcall.2, there is no socketcall systemcall on x86_64 systems.\n// You will not get this value with the standard defines.\n#define I386_NR_socketcall (102)\n#define ARMV7_NR_bind (282)\n"
  },
  {
    "path": "src/linux/init/CMakeLists.txt",
    "content": "set(SOURCES\n    main.cpp\n    binfmt.cpp\n    config.cpp\n    DnsServer.cpp\n    DnsTunnelingChannel.cpp\n    DnsTunnelingManager.cpp\n    drvfs.cpp\n    escape.cpp\n    GnsEngine.cpp\n    GnsPortTracker.cpp\n    init.cpp\n    localhost.cpp\n    Localization.cpp\n    NetworkManager.cpp\n    plan9.cpp\n    telemetry.cpp\n    timezone.cpp\n    SecCompDispatcher.cpp\n    util.cpp\n    WslDistributionConfig.cpp\n    wslinfo.cpp\n    wslpath.cpp)\n\nset(HEADERS\n    ../inc/lxwil.h\n    binfmt.h\n    common.h\n    config.h\n    DnsServer.h\n    DnsTunnelingChannel.h\n    DnsTunnelingManager.h\n    drvfs.h\n    escape.h\n    GnsEngine.h\n    GnsPortTracker.h\n    localhost.h\n    NetworkManager.h\n    plan9.h\n    telemetry.h\n    timezone.h\n    SecCompDispatcher.h\n    util.h\n    WslDistributionConfig.h\n    wslinfo.h\n    wslpath.h)\n\nset(LINUX_CXXFLAGS ${LINUX_CXXFLAGS} -I \"${CMAKE_CURRENT_LIST_DIR}/../netlinkutil\")\nset(INIT_LIBRARIES ${COMMON_LINUX_LINK_LIBRARIES} netlinkutil plan9 mountutil configfile)\nadd_linux_executable(init \"${SOURCES}\" \"${HEADERS};${COMMON_LINUX_HEADERS}\" \"${INIT_LIBRARIES}\")\nadd_dependencies(init localization)\n\nset_target_properties(init PROPERTIES FOLDER linux)\n\nset(INITRAMFS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/initrd.img)\nset(INIT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/init)\nadd_custom_command(\n    OUTPUT ${INITRAMFS} \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/initramfs\"\n    DEPENDS init ${INIT}\n    COMMAND powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File \"${CMAKE_SOURCE_DIR}/tools/create-initrd.ps1\" \"${INIT}\" \"${INITRAMFS}\"\n    COMMAND ${CMAKE_COMMAND} -E touch \"${CMAKE_CURRENT_BINARY_DIR}/CmakeFiles/initramfs\"\n    VERBATIM)\nadd_custom_target(initramfs DEPENDS ${INITRAMFS})\nset_target_properties(initramfs PROPERTIES FOLDER linux)\n"
  },
  {
    "path": "src/linux/init/DnsServer.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include <arpa/inet.h>\r\n#include <sys/epoll.h>\r\n#include <netinet/in.h>\r\n#include <sys/socket.h>\r\n#include \"DnsServer.h\"\r\n#include \"RuntimeErrorWithSourceLocation.h\"\r\n#include \"Syscall.h\"\r\n#include \"util.h\"\r\n\r\n// Port used by DNS server\r\nconstexpr int c_dnsServerPort = 53;\r\n// Max number of events to be returned by epoll_wait()\r\nconstexpr int c_epollWaitMaxEvents = 100;\r\n// Maximum size of DNS over UDP requests is 4096 bytes (max size is reached for EDNS UDP requests)\r\nconstexpr int c_maxUdpDnsBufferSize = 4096;\r\n// Max number of pending connections in the TCP listen queue\r\nconstexpr int c_maxListenBacklog = 1000;\r\n\r\nDnsServer::DnsServer(DnsTunnelingCallback&& tunnelDnsRequest) : m_tunnelDnsRequest(std::move(tunnelDnsRequest))\r\n{\r\n}\r\n\r\nDnsServer::~DnsServer() noexcept\r\n{\r\n    Stop();\r\n}\r\n\r\nvoid DnsServer::Start(const std::string& ipAddress) noexcept\r\ntry\r\n{\r\n    // Create epoll handler fd. 0 represents default flags\r\n    m_epollFd = Syscall(epoll_create1, 0);\r\n\r\n    StartUdpDnsServer(ipAddress);\r\n    StartTcpDnsServer(ipAddress);\r\n\r\n    // Create and register the shutdown pipe with epoll\r\n    m_shutdownServerLoopPipe = wil::unique_pipe::create(0);\r\n\r\n    epoll_event event{};\r\n    event.events = EPOLLIN;\r\n    event.data.fd = m_shutdownServerLoopPipe.read().get();\r\n    Syscall(epoll_ctl, m_epollFd.get(), EPOLL_CTL_ADD, m_shutdownServerLoopPipe.read().get(), &event);\r\n\r\n    // Start server loop\r\n    m_serverThread = std::thread([this]() { ServerLoop(); });\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsServer::StartUdpDnsServer(const std::string& ipAddress) noexcept\r\ntry\r\n{\r\n    sockaddr_in serverAddr{};\r\n\r\n    serverAddr.sin_family = AF_INET;\r\n    Syscall(inet_pton, AF_INET, ipAddress.c_str(), &serverAddr.sin_addr);\r\n    serverAddr.sin_port = htons(c_dnsServerPort);\r\n\r\n    // Create IPv4 UDP socket\r\n    m_udpSocket = Syscall(socket, AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);\r\n\r\n    // Bind socket\r\n    Syscall(bind, m_udpSocket.get(), reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr));\r\n\r\n    // Configure epoll to track the UDP socket. EPOLLIN is used to get epoll notifications\r\n    // whenever there is data available to be read from the socket\r\n    epoll_event event{};\r\n    event.events = EPOLLIN;\r\n    event.data.fd = m_udpSocket.get();\r\n    Syscall(epoll_ctl, m_epollFd.get(), EPOLL_CTL_ADD, m_udpSocket.get(), &event);\r\n\r\n    GNS_LOG_INFO(\"Successfully started UDP server on IP {}\", ipAddress.c_str());\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsServer::StartTcpDnsServer(const std::string& ipAddress) noexcept\r\ntry\r\n{\r\n    sockaddr_in serverAddr{};\r\n\r\n    serverAddr.sin_family = AF_INET;\r\n    Syscall(inet_pton, AF_INET, ipAddress.c_str(), &serverAddr.sin_addr);\r\n    serverAddr.sin_port = htons(c_dnsServerPort);\r\n\r\n    // Create IPv4 TCP socket\r\n    m_tcpListenSocket = Syscall(socket, AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);\r\n\r\n    // Bind socket\r\n    Syscall(bind, m_tcpListenSocket.get(), reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr));\r\n\r\n    // Listen for incoming connections\r\n    Syscall(listen, m_tcpListenSocket.get(), c_maxListenBacklog);\r\n\r\n    // Configure epoll to track the TCP listening socket. EPOLLIN is used to get epoll notifications\r\n    // whenever there is a new incoming TCP connection.\r\n    epoll_event event{};\r\n    event.events = EPOLLIN;\r\n    event.data.fd = m_tcpListenSocket.get();\r\n    Syscall(epoll_ctl, m_epollFd.get(), EPOLL_CTL_ADD, m_tcpListenSocket.get(), &event);\r\n\r\n    GNS_LOG_INFO(\"Successfully started TCP server on IP {}\", ipAddress.c_str());\r\n}\r\nCATCH_LOG();\r\n\r\nvoid DnsServer::HandleUdpDnsResponse(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept\r\ntry\r\n{\r\n    GNS_LOG_INFO(\"New UDP DNS response DNS buffer size: {}, UDP request id: {}\", dnsBuffer.size(), dnsClientIdentifier.DnsClientId);\r\n\r\n    std::scoped_lock<std::mutex> lock{m_udpLock};\r\n\r\n    auto it = m_udpRequests.find(dnsClientIdentifier.DnsClientId);\r\n    if (it == m_udpRequests.end())\r\n    {\r\n        GNS_LOG_ERROR(\"Received a response for a UDP request that is not tracked, UDP request id: {}\", dnsClientIdentifier.DnsClientId);\r\n        return;\r\n    }\r\n\r\n    // Stop tracking the request, irrespective of the DNS response being successfully sent\r\n    const auto removeDnsRequest = wil::scope_exit([&] { m_udpRequests.erase(dnsClientIdentifier.DnsClientId); });\r\n\r\n    sockaddr_in& remoteAddr = it->second;\r\n\r\n    // Send DNS response buffer back to the Linux DNS client\r\n    int bufferSize = dnsBuffer.size();\r\n    int totalBytesSent = 0;\r\n\r\n    while (totalBytesSent < bufferSize)\r\n    {\r\n        int bytesSent = Syscall(\r\n            sendto, m_udpSocket.get(), dnsBuffer.data() + totalBytesSent, bufferSize - totalBytesSent, 0, reinterpret_cast<sockaddr*>(&remoteAddr), sizeof(remoteAddr));\r\n        totalBytesSent += bytesSent;\r\n    }\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsServer::HandleTcpDnsResponse(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept\r\ntry\r\n{\r\n    GNS_LOG_INFO(\r\n        \"New TCP DNS response \"\r\n        \"DNS buffer size: {}, TCP connection id: {}\",\r\n        dnsBuffer.size(),\r\n        dnsClientIdentifier.DnsClientId);\r\n\r\n    std::scoped_lock<std::mutex> lock{m_tcpLock};\r\n\r\n    auto it = m_tcpConnectionContexts.find(dnsClientIdentifier.DnsClientId);\r\n    if (it == m_tcpConnectionContexts.end())\r\n    {\r\n        GNS_LOG_ERROR(\"Received a response for an untracked TCP connection id: {}\", dnsClientIdentifier.DnsClientId);\r\n        return;\r\n    }\r\n\r\n    auto tcpConnection = it->second->m_tcpConnection.get();\r\n\r\n    // Send DNS response buffer back to the Linux DNS client.\r\n    //\r\n    // Note: there might be more DNS requests sent on the same TCP connection. The DNS protocol allows sending the responses in a\r\n    // different order than the order of the corresponding DNS requests.\r\n    int bufferSize = dnsBuffer.size();\r\n    int totalBytesSent = 0;\r\n\r\n    while (totalBytesSent < bufferSize)\r\n    {\r\n        int bytesSent = Syscall(write, tcpConnection, dnsBuffer.data() + totalBytesSent, bufferSize - totalBytesSent);\r\n        totalBytesSent += bytesSent;\r\n    }\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsServer::HandleNewTcpConnection() noexcept\r\ntry\r\n{\r\n    std::scoped_lock<std::mutex> lock{m_tcpLock};\r\n\r\n    // Accept new connection. Mark connection socket as non-blocking\r\n    wil::unique_fd connectionFd = Syscall(accept4, m_tcpListenSocket.get(), nullptr, nullptr, SOCK_NONBLOCK);\r\n\r\n    // Get next connection id. If value reaches UINT_MAX + 1 it will be automatically reset to 0\r\n    const auto connectionId = m_currentTcpConnectionId++;\r\n\r\n    // Track the new connection\r\n    auto [it, _] = m_tcpConnectionContexts.emplace(\r\n        connectionId, std::make_unique<DnsServer::TcpConnectionContext>(connectionId, std::move(connectionFd)));\r\n    auto& localContext = it->second;\r\n\r\n    auto removeContextOnError = wil::scope_exit([&] { m_tcpConnectionContexts.erase(connectionId); });\r\n\r\n    // Register the new connection with epoll. EPOLLIN is used to get epoll notifications\r\n    // whenever there is new data on the TCP connection.\r\n    epoll_event event{};\r\n    event.events = EPOLLIN;\r\n    event.data.fd = localContext->m_tcpConnection.get();\r\n    event.data.ptr = localContext.get();\r\n    Syscall(epoll_ctl, m_epollFd.get(), EPOLL_CTL_ADD, localContext->m_tcpConnection.get(), &event);\r\n\r\n    removeContextOnError.release();\r\n}\r\nCATCH_LOG();\r\n\r\nvoid DnsServer::HandleNewTcpData(TcpConnectionContext* context) noexcept\r\ntry\r\n{\r\n    std::vector<gsl::byte> dnsRequest;\r\n    uint32_t tcpConnectionId{};\r\n\r\n    // Scoped m_tcpLock\r\n    {\r\n        std::scoped_lock<std::mutex> lock{m_tcpLock};\r\n\r\n        // In case of any failure reading data, close the connection and stop tracking it.\r\n        // Note: Closing the connection automatically unregisters it from epoll.\r\n        auto removeConnectionOnError = wil::scope_exit([&] { m_tcpConnectionContexts.erase(context->m_connectionId); });\r\n\r\n        // Read the remaining bytes of the current DNS request\r\n        int bytesReceived = Syscall(\r\n            recv,\r\n            context->m_tcpConnection.get(),\r\n            context->m_currentDnsRequest.data() + context->m_currentRequestOffset,\r\n            context->m_currentDnsRequest.size() - context->m_currentRequestOffset,\r\n            0);\r\n\r\n        // 0 bytes received indicates connection was closed by the TCP client\r\n        if (bytesReceived == 0)\r\n        {\r\n            return;\r\n        }\r\n\r\n        context->m_currentRequestOffset += bytesReceived;\r\n\r\n        if (context->m_currentRequestOffset == context->m_currentDnsRequest.size())\r\n        {\r\n            // We read the 2 bytes that represent the DNS request length\r\n            // Resize buffer to fit the entire DNS request (2 bytes storing the request length + the actual DNS request)\r\n            if (context->m_currentDnsRequest.size() == c_byteCountTcpRequestLength)\r\n            {\r\n                uint16_t dnsRequestLength = 0;\r\n                memcpy(&dnsRequestLength, context->m_currentDnsRequest.data(), c_byteCountTcpRequestLength);\r\n                // The request length is stored in network byte order\r\n                dnsRequestLength = ntohs(dnsRequestLength);\r\n\r\n                context->m_currentDnsRequest.resize(c_byteCountTcpRequestLength + dnsRequestLength);\r\n            }\r\n            // We read a full DNS request\r\n            else\r\n            {\r\n                // Move request to a local variable\r\n                dnsRequest = std::move(context->m_currentDnsRequest);\r\n                tcpConnectionId = context->m_connectionId;\r\n\r\n                // Reset state to prepare for the next DNS request on the connection (if any)\r\n                context->m_currentRequestOffset = 0;\r\n                context->m_currentDnsRequest.resize(c_byteCountTcpRequestLength);\r\n            }\r\n        }\r\n\r\n        removeConnectionOnError.release();\r\n    }\r\n\r\n    if (!dnsRequest.empty())\r\n    {\r\n        // Tunnel request to Windows\r\n        LX_GNS_DNS_CLIENT_IDENTIFIER dnsClientIdentifier{};\r\n        dnsClientIdentifier.DnsClientId = tcpConnectionId;\r\n        dnsClientIdentifier.Protocol = IPPROTO_TCP;\r\n\r\n        GNS_LOG_INFO(\"New TCP DNS request DNS buffer size: {}, TCP connection id: {}\", dnsRequest.size(), dnsClientIdentifier.DnsClientId);\r\n\r\n        m_tunnelDnsRequest(gsl::make_span(dnsRequest), dnsClientIdentifier);\r\n    }\r\n}\r\nCATCH_LOG();\r\n\r\nvoid DnsServer::HandleDnsResponse(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept\r\ntry\r\n{\r\n    switch (dnsClientIdentifier.Protocol)\r\n    {\r\n    case IPPROTO_UDP:\r\n    {\r\n        HandleUdpDnsResponse(dnsBuffer, dnsClientIdentifier);\r\n        break;\r\n    }\r\n    case IPPROTO_TCP:\r\n    {\r\n        HandleTcpDnsResponse(dnsBuffer, dnsClientIdentifier);\r\n        break;\r\n    }\r\n\r\n    default:\r\n    {\r\n        GNS_LOG_ERROR(\"Unexpected DNS protocol {}\", dnsClientIdentifier.Protocol);\r\n        break;\r\n    }\r\n    }\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsServer::ServerLoop() noexcept\r\n{\r\n    UtilSetThreadName(\"DnsServer\");\r\n\r\n    epoll_event events[c_epollWaitMaxEvents];\r\n    memset(events, 0, sizeof(events));\r\n\r\n    for (;;)\r\n    {\r\n        try\r\n        {\r\n            // A fixed number of events is requested from epoll_wait (c_epollWaitMaxEvents). In case the number of ready events is\r\n            // greater than c_epollWaitMaxEvents, epoll will round-robin through the ready events until we get a notification for all of them.\r\n            size_t numReadyEvents = Syscall(epoll_wait, m_epollFd.get(), events, c_epollWaitMaxEvents, -1);\r\n\r\n            // No event\r\n            if (numReadyEvents == 0)\r\n            {\r\n                continue;\r\n            }\r\n\r\n            for (size_t index = 0; index < numReadyEvents; index++)\r\n            {\r\n                // Notification for the shutdown pipe == the server needs to exit\r\n                if (events[index].data.fd == m_shutdownServerLoopPipe.read().get())\r\n                {\r\n                    return;\r\n                }\r\n                // Notification for the listen socket == a new incoming TCP connection\r\n                else if (events[index].data.fd == m_tcpListenSocket.get())\r\n                {\r\n                    HandleNewTcpConnection();\r\n                }\r\n                // Notification for the UDP socket == There is data to be read from the UDP socket, indicating a new DNS request was received\r\n                else if (events[index].data.fd == m_udpSocket.get())\r\n                {\r\n                    HandleUdpDnsRequest();\r\n                }\r\n                // Other notifications == new data was received on one of the active TCP connections\r\n                else\r\n                {\r\n                    HandleNewTcpData(static_cast<TcpConnectionContext*>(events[index].data.ptr));\r\n                }\r\n            }\r\n        }\r\n        CATCH_LOG()\r\n    }\r\n}\r\n\r\nvoid DnsServer::HandleUdpDnsRequest() noexcept\r\ntry\r\n{\r\n    static std::array<gsl::byte, c_maxUdpDnsBufferSize> s_dnsBuffer;\r\n\r\n    gsl::span<gsl::byte> dnsRequest;\r\n    uint32_t udpRequestId{};\r\n\r\n    // Scoped m_udpLock\r\n    {\r\n        std::scoped_lock<std::mutex> lock{m_udpLock};\r\n\r\n        // Since we only configure an IPv4 DNS server in Linux, we expect all Linux DNS clients to use IPv4 addresses\r\n        sockaddr_in remoteAddr{};\r\n        socklen_t remoteAddrLen = sizeof(remoteAddr);\r\n\r\n        // Read the DNS request\r\n        int bytesReceived = Syscall(\r\n            recvfrom, m_udpSocket.get(), s_dnsBuffer.data(), c_maxUdpDnsBufferSize, 0, reinterpret_cast<sockaddr*>(&remoteAddr), &remoteAddrLen);\r\n\r\n        if (bytesReceived == 0)\r\n        {\r\n            GNS_LOG_ERROR(\"recvfrom returned 0 bytes\");\r\n            return;\r\n        }\r\n\r\n        // Get next request id. If value reaches UINT_MAX + 1 it will be automatically reset to 0\r\n        const auto requestId = m_currentUdpRequestId++;\r\n\r\n        GNS_LOG_INFO(\r\n            \"New UDP DNS request DNS client IP: {}, DNS client port {}, DNS buffer size: {}, UDP request id: {}\",\r\n            Address::FromBinary(AF_INET, 0, &remoteAddr.sin_addr).Addr().c_str(),\r\n            ntohs(remoteAddr.sin_port),\r\n            bytesReceived,\r\n            requestId);\r\n\r\n        // Move request to a local variable\r\n        dnsRequest = std::move(gsl::make_span(s_dnsBuffer).subspan(0, bytesReceived));\r\n        udpRequestId = requestId;\r\n\r\n        // Track the request\r\n        m_udpRequests.emplace(requestId, remoteAddr);\r\n    }\r\n\r\n    if (!dnsRequest.empty())\r\n    {\r\n        auto removeRequestOnError = wil::scope_exit([&] {\r\n            std::scoped_lock<std::mutex> lock{m_udpLock};\r\n            m_udpRequests.erase(udpRequestId);\r\n        });\r\n\r\n        // Tunnel request to Windows\r\n        LX_GNS_DNS_CLIENT_IDENTIFIER dnsClientIdentifier{};\r\n        dnsClientIdentifier.Protocol = IPPROTO_UDP;\r\n        dnsClientIdentifier.DnsClientId = udpRequestId;\r\n\r\n        m_tunnelDnsRequest(dnsRequest, dnsClientIdentifier);\r\n\r\n        removeRequestOnError.release();\r\n    }\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsServer::Stop() noexcept\r\ntry\r\n{\r\n    GNS_LOG_INFO(\"stopping DNS server\");\r\n\r\n    // Signal the server loop to stop by closing the write fd of the pipe\r\n    m_shutdownServerLoopPipe.write().reset();\r\n\r\n    if (m_serverThread.joinable())\r\n    {\r\n        m_serverThread.join();\r\n    }\r\n}\r\nCATCH_LOG()"
  },
  {
    "path": "src/linux/init/DnsServer.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include <map>\r\n#include \"common.h\"\r\n#include \"lxinitshared.h\"\r\n\r\nusing DnsTunnelingCallback = std::function<void(const gsl::span<gsl::byte>, const LX_GNS_DNS_CLIENT_IDENTIFIER&)>;\r\n\r\n// Number of bytes used to store the length of DNS over TCP requests\r\nconstexpr int c_byteCountTcpRequestLength = 2;\r\n\r\nclass DnsServer\r\n{\r\npublic:\r\n    DnsServer(DnsTunnelingCallback&& tunnelDnsRequest);\r\n    ~DnsServer() noexcept;\r\n\r\n    DnsServer(const DnsServer&) = delete;\r\n    DnsServer(DnsServer&&) = delete;\r\n    DnsServer& operator=(const DnsServer&) = delete;\r\n    DnsServer& operator=(DnsServer&&) = delete;\r\n\r\n    // Start DNS server.\r\n    //\r\n    // Arguments:\r\n    //    ipAddress - IP address to start server on.\r\n    void Start(const std::string& ipAddress) noexcept;\r\n\r\n    // Process DNS response received from Windows.\r\n    //\r\n    // Arguments:\r\n    //    dnsBuffer - buffer containing DNS response.\r\n    //    dnsClientIdentifier - struct containing protocol (TCP/UDP) and unique id of the Linux DNS client making the request.\r\n    void HandleDnsResponse(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept;\r\n\r\n    void Stop() noexcept;\r\n\r\nprivate:\r\n    struct TcpConnectionContext\r\n    {\r\n        // Connection fd\r\n        wil::unique_fd m_tcpConnection;\r\n\r\n        // Offset in m_currentDnsRequest indicating how much of the current DNS request on\r\n        // the TCP connection has been read. Using 2 bytes to represent the offset as the request length is represented using 2 bytes.\r\n        uint16_t m_currentRequestOffset = 0;\r\n\r\n        // Buffer containing the current DNS request received on the TCP connection.\r\n        std::vector<gsl::byte> m_currentDnsRequest;\r\n\r\n        // Unique connection id. The connection fd would be a candidate for this, but the fd might be reused, so we need a different id.\r\n        uint32_t m_connectionId{};\r\n\r\n        TcpConnectionContext(uint32_t id, wil::unique_fd&& tcpConnection) :\r\n            m_tcpConnection(std::move(tcpConnection)), m_connectionId(id)\r\n        {\r\n            // Resize to fit the bytes that represent the request length\r\n            m_currentDnsRequest.resize(c_byteCountTcpRequestLength);\r\n        }\r\n\r\n        ~TcpConnectionContext() noexcept = default;\r\n\r\n        TcpConnectionContext(const TcpConnectionContext&) = delete;\r\n        TcpConnectionContext& operator=(const TcpConnectionContext&) = delete;\r\n        TcpConnectionContext(TcpConnectionContext&&) = delete;\r\n        TcpConnectionContext& operator=(TcpConnectionContext&&) = delete;\r\n    };\r\n\r\n    void StartUdpDnsServer(const std::string& ipAddress) noexcept;\r\n\r\n    void StartTcpDnsServer(const std::string& ipAddress) noexcept;\r\n\r\n    // Main server loop, processing epoll notifications.\r\n    void ServerLoop() noexcept;\r\n\r\n    // Accept new incoming TCP connection.\r\n    void HandleNewTcpConnection() noexcept;\r\n\r\n    // Handle new data received on an existing TCP connection.\r\n    void HandleNewTcpData(TcpConnectionContext* context) noexcept;\r\n\r\n    // Read the next DNS request from the UDP socket.\r\n    void HandleUdpDnsRequest() noexcept;\r\n\r\n    void HandleUdpDnsResponse(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept;\r\n\r\n    void HandleTcpDnsResponse(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept;\r\n\r\n    // File descriptor used to interact with epoll. Declared before the UDP and TCP sockets and the shutdown pipe so it will be closed after them.\r\n    // Note: Closing a socket fd automatically leads to unregistering it from epoll - EPOLL_CTL_DEL is not necessary for that fd.\r\n    wil::unique_fd m_epollFd;\r\n\r\n    std::mutex m_udpLock;\r\n\r\n    // _Guarded_by_(m_udpLock)\r\n    wil::unique_fd m_udpSocket;\r\n\r\n    // Unique id that is incremented for each DNS request over UDP. In case the value reaches MAX_UINT and is reset to 0,\r\n    // it's assumed previous requests with id's 0, 1, ... finished in the meantime and the id can be reused.\r\n    // _Guarded_by_(m_udpLock)\r\n    uint32_t m_currentUdpRequestId = 0;\r\n\r\n    // Mapping id of an UDP DNS request to the sockaddr_in struct storing the IP and port used by the Linux DNS client that made\r\n    // the DNS request. Note: Since we only configure an IPv4 DNS server in Linux, we expect all Linux DNS clients to use IPv4\r\n    // addresses. _Guarded_by_(m_udpLock)\r\n    std::map<uint32_t, sockaddr_in> m_udpRequests;\r\n\r\n    wil::unique_fd m_tcpListenSocket;\r\n\r\n    std::mutex m_tcpLock;\r\n\r\n    // Unique id that is incremented for each TCP connection. In case the value reaches MAX_UINT and is reset to 0,\r\n    // it's assumed previous connections with id's 0, 1, ... were closed in the meantime and the id can be reused.\r\n    // _Guarded_by_(m_udpLock)\r\n    uint32_t m_currentTcpConnectionId = 0;\r\n\r\n    // Mapping TCP connection unique id to connection context\r\n    // _Guarded_by_(m_udpLock)\r\n    std::map<uint32_t, std::unique_ptr<TcpConnectionContext>> m_tcpConnectionContexts;\r\n\r\n    // Pipe used to stop m_serverThread.\r\n    wil::unique_pipe m_shutdownServerLoopPipe;\r\n\r\n    // Thread running the server loop.\r\n    std::thread m_serverThread;\r\n\r\n    // Callback used for tunneling a DNS request to Windows to be resolved.\r\n    DnsTunnelingCallback m_tunnelDnsRequest;\r\n};\r\n"
  },
  {
    "path": "src/linux/init/DnsTunnelingChannel.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include <netinet/in.h>\r\n#include <arpa/inet.h>\r\n#include \"DnsTunnelingChannel.h\"\r\n#include \"util.h\"\r\n#include \"RuntimeErrorWithSourceLocation.h\"\r\n#include \"Syscall.h\"\r\n#include \"message.h\"\r\n\r\nDnsTunnelingChannel::DnsTunnelingChannel(int channelFd, DnsTunnelingCallback&& reportDnsResponse) :\r\n    m_channel(wil::unique_fd{channelFd}, \"DnsTunneling\"), m_reportDnsResponse(std::move(reportDnsResponse))\r\n{\r\n    // Create a pipe to be used for signalling the receive loop to stop\r\n    m_shutdownReceiveWorkerPipe = wil::unique_pipe::create(0);\r\n\r\n    // Start loop waiting for incoming messages from Windows side\r\n    m_receiveWorkerThread = std::thread([this]() { ReceiveLoop(); });\r\n}\r\n\r\nDnsTunnelingChannel::~DnsTunnelingChannel()\r\n{\r\n    Stop();\r\n}\r\n\r\nvoid DnsTunnelingChannel::SendDnsMessage(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept\r\ntry\r\n{\r\n    wsl::shared::MessageWriter<LX_GNS_DNS_TUNNELING_MESSAGE> message(LxGnsMessageDnsTunneling);\r\n    message->DnsClientIdentifier = dnsClientIdentifier;\r\n    message.WriteSpan(dnsBuffer);\r\n\r\n    m_channel.SendMessage<LX_GNS_DNS_TUNNELING_MESSAGE>(message.Span());\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsTunnelingChannel::ReceiveLoop() noexcept\r\n{\r\n    UtilSetThreadName(\"DnsTunneling\");\r\n\r\n    // Returns false if the write pipe was closed, signaling that loop should exit\r\n    // Returns true if there is data to be received on the channel fd\r\n    auto wait_for_channel_fd = [this]() -> bool {\r\n        struct pollfd poll_fds[2];\r\n        poll_fds[0] = {.fd = m_channel.Socket(), .events = POLLIN, .revents = 0};\r\n        poll_fds[1] = {.fd = m_shutdownReceiveWorkerPipe.read().get(), .events = POLLIN, .revents = 0};\r\n\r\n        unsigned int retryCount = 0;\r\n        const unsigned int maxRetryCount = 3;\r\n\r\n        for (;;)\r\n        {\r\n            int return_value = SyscallInterruptable(poll, poll_fds, ARRAY_SIZE(poll_fds), -1);\r\n            if (return_value < 0)\r\n            {\r\n                GNS_LOG_ERROR(\"poll failed\");\r\n                retryCount++;\r\n\r\n                if (retryCount < maxRetryCount)\r\n                {\r\n                    continue;\r\n                }\r\n                else\r\n                {\r\n                    return false;\r\n                }\r\n            }\r\n            else if (return_value == 0)\r\n            {\r\n                GNS_LOG_ERROR(\"poll returned 0 (timeout)\");\r\n                return false;\r\n            }\r\n            else if (poll_fds[1].revents)\r\n            {\r\n                return false;\r\n            }\r\n            else if (poll_fds[0].revents & POLLIN)\r\n            {\r\n                return true;\r\n            }\r\n        }\r\n    };\r\n\r\n    std::vector<gsl::byte> receiveBuffer;\r\n\r\n    for (;;)\r\n    {\r\n        try\r\n        {\r\n            if (!wait_for_channel_fd())\r\n            {\r\n                break;\r\n            }\r\n\r\n            GNS_LOG_INFO(\"processing next message from Windows\");\r\n\r\n            // Read next message. wsl::shared::socket::RecvMessage() first reads the message header, then uses it to determine the\r\n            // total size of the message and read the rest of the message, resizing the buffer if needed.\r\n            auto [message, span] = m_channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\r\n            if (message == nullptr)\r\n            {\r\n                GNS_LOG_ERROR(\"failed to read message\");\r\n                return;\r\n            }\r\n\r\n            // Get the message type from the message header\r\n            switch (message->MessageType)\r\n            {\r\n            case LxGnsMessageDnsTunneling:\r\n            {\r\n                // Cast message to a LX_GNS_DNS_TUNNELING_MESSAGE struct\r\n                auto* dnsMessage = gslhelpers::try_get_struct<LX_GNS_DNS_TUNNELING_MESSAGE>(span);\r\n                if (!dnsMessage)\r\n                {\r\n                    GNS_LOG_ERROR(\"failed to convert message to LX_GNS_DNS_TUNNELING_MESSAGE\");\r\n                    return;\r\n                }\r\n\r\n                // Extract DNS buffer from message\r\n                auto dnsBuffer = span.subspan(offsetof(LX_GNS_DNS_TUNNELING_MESSAGE, Buffer));\r\n\r\n                GNS_LOG_INFO(\r\n                    \"received DNS message DNS buffer size: {}, Protocol {}, DNS client id: {}\",\r\n                    dnsBuffer.size(),\r\n                    dnsMessage->DnsClientIdentifier.Protocol == IPPROTO_UDP ? \"UDP\" : \"TCP\",\r\n                    dnsMessage->DnsClientIdentifier.DnsClientId);\r\n\r\n                // Invoke callback to notify about the new DNS response\r\n                m_reportDnsResponse(dnsBuffer, dnsMessage->DnsClientIdentifier);\r\n\r\n                break;\r\n            }\r\n\r\n            default:\r\n            {\r\n                throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected LX_MESSAGE_TYPE : {}\", static_cast<int>(message->MessageType)));\r\n            }\r\n            }\r\n        }\r\n        CATCH_LOG()\r\n    }\r\n}\r\n\r\nvoid DnsTunnelingChannel::Stop() noexcept\r\ntry\r\n{\r\n    GNS_LOG_INFO(\"stopping DNS server\");\r\n\r\n    // Stop receive loop by closing the write fd of the pipe\r\n    m_shutdownReceiveWorkerPipe.write().reset();\r\n\r\n    if (m_receiveWorkerThread.joinable())\r\n    {\r\n        m_receiveWorkerThread.join();\r\n    }\r\n}\r\nCATCH_LOG()\r\n"
  },
  {
    "path": "src/linux/init/DnsTunnelingChannel.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include \"common.h\"\r\n#include \"lxinitshared.h\"\r\n#include \"SocketChannel.h\"\r\n\r\nusing DnsTunnelingCallback = std::function<void(const gsl::span<gsl::byte>, const LX_GNS_DNS_CLIENT_IDENTIFIER&)>;\r\n\r\nclass DnsTunnelingChannel\r\n{\r\npublic:\r\n    DnsTunnelingChannel(int channelFd, DnsTunnelingCallback&& reportDnsResponse);\r\n    ~DnsTunnelingChannel();\r\n\r\n    DnsTunnelingChannel(const DnsTunnelingChannel&) = delete;\r\n    DnsTunnelingChannel(DnsTunnelingChannel&&) = delete;\r\n    DnsTunnelingChannel& operator=(const DnsTunnelingChannel&) = delete;\r\n    DnsTunnelingChannel& operator=(DnsTunnelingChannel&&) = delete;\r\n\r\n    // Construct and send a LX_GNS_DNS_TUNNELING_MESSAGE message on the channel.\r\n    // Note: Callers are responsible for sequencing calls to this method.\r\n    //\r\n    // Arguments:\r\n    // dnsBuffer - buffer containing DNS request.\r\n    // dnsClientIdentifier - struct containing protocol (TCP/UDP) and unique id of the Linux DNS client making the request.\r\n    void SendDnsMessage(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept;\r\n\r\n    // Stop the channel.\r\n    void Stop() noexcept;\r\n\r\nprivate:\r\n    // Wait for messages on the channel from Windows side.\r\n    void ReceiveLoop() noexcept;\r\n\r\n    wsl::shared::SocketChannel m_channel;\r\n\r\n    // Thread running the receive loop.\r\n    std::thread m_receiveWorkerThread;\r\n\r\n    // Pipe used to stop m_receiveWorkerThread.\r\n    wil::unique_pipe m_shutdownReceiveWorkerPipe;\r\n\r\n    // Callback used to notify when there is a new DNS response message on the channel.\r\n    const DnsTunnelingCallback m_reportDnsResponse;\r\n};\r\n"
  },
  {
    "path": "src/linux/init/DnsTunnelingManager.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include <iostream>\r\n#include <filesystem>\r\n#include <fstream>\r\n#include <netinet/in.h>\r\n#include \"common.h\"\r\n#include \"DnsTunnelingManager.h\"\r\n\r\nDnsTunnelingManager::DnsTunnelingManager(int hvsocketFd, const std::string& dnsTunnelingIpAddress) :\r\n    m_dnsChannel(\r\n        hvsocketFd,\r\n        [this](const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) {\r\n            m_dnsServer.HandleDnsResponse(dnsBuffer, dnsClientIdentifier);\r\n        }),\r\n    m_dnsServer([this](const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) {\r\n        if (m_stopped)\r\n        {\r\n            return;\r\n        }\r\n\r\n        m_dnsChannel.SendDnsMessage(dnsBuffer, dnsClientIdentifier);\r\n    })\r\n{\r\n    GNS_LOG_INFO(\"Using DNS server IP {}\", dnsTunnelingIpAddress.c_str());\r\n\r\n    // Start DNS server used for tunneling. Server has both TCP and UDP support.\r\n    //\r\n    // Note: because DnsTunnelingManager runs as part of GNS daemon, which is started before GnsPortTracker, binding the DNS\r\n    // server will not be intercepted by the bind seccomp hook. This is ok because in FSE mode there is no need for host<->guest\r\n    // loopback communication to/from the DNS server (all traffic to/from DNS server will stay in the container).\r\n    m_dnsServer.Start(dnsTunnelingIpAddress);\r\n}\r\n\r\nDnsTunnelingManager::~DnsTunnelingManager()\r\n{\r\n    // Scoped m_dnsLock\r\n    {\r\n        // Set flag to signal object is stopping\r\n        m_stopped = true;\r\n    }\r\n\r\n    // Stop channel first as it can call into the DNS server object\r\n    m_dnsChannel.Stop();\r\n\r\n    // Stop DNS server\r\n    m_dnsServer.Stop();\r\n}\r\n"
  },
  {
    "path": "src/linux/init/DnsTunnelingManager.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include \"DnsTunnelingChannel.h\"\r\n#include \"DnsServer.h\"\r\n\r\nclass DnsTunnelingManager\r\n{\r\npublic:\r\n    DnsTunnelingManager(int hvsocketFd, const std::string& dnsTunnelingIpAddress);\r\n    ~DnsTunnelingManager();\r\n\r\n    DnsTunnelingManager(const DnsTunnelingManager&) = delete;\r\n    DnsTunnelingManager(DnsTunnelingManager&&) = delete;\r\n    DnsTunnelingManager& operator=(const DnsTunnelingManager&) = delete;\r\n    DnsTunnelingManager& operator=(DnsTunnelingManager&&) = delete;\r\n\r\nprivate:\r\n    // Hvsocket channel used to communicate with the host.\r\n    DnsTunnelingChannel m_dnsChannel;\r\n\r\n    // DNS server used for tunneling, supporting both UDP and TCP.\r\n    DnsServer m_dnsServer;\r\n\r\n    std::atomic<bool> m_stopped = false;\r\n};\r\n"
  },
  {
    "path": "src/linux/init/GnsEngine.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include <iostream>\n#include <locale>\n#include <regex>\n#include <filesystem>\n#include <format>\n#include <fstream>\n#include \"address.h\"\n#include \"common.h\"\n#include \"GnsEngine.h\"\n#include \"util.h\"\n#include \"Utils.h\"\n#include \"lxinitshared.h\"\n#include \"stringshared.h\"\n\nusing wsl::shared::hns::GuestEndpointResourceType;\nusing wsl::shared::hns::ModifyGuestEndpointSettingRequest;\nusing wsl::shared::hns::ModifyRequestType;\n\nconstexpr auto c_interfaceLookupTimeout = std::chrono::seconds(30);\nconstexpr auto c_interfaceLookupRetryPeriod = std::chrono::milliseconds(100);\nconstexpr auto c_ipStrings = {\"ip\", \"ip6\"};\n\nconst char* c_loopbackInterfaceName = \"lo\";\n\nGnsEngine::GnsEngine(\n    const NotificationRoutine& notificationRoutine,\n    const StatusRoutine& statusRoutine,\n    NetworkManager& manager,\n    std::optional<int> dnsTunnelingFd,\n    const std::string& dnsTunnelingIpAddress) :\n    notificationRoutine(notificationRoutine), statusRoutine(statusRoutine), manager(manager)\n{\n    if (dnsTunnelingFd.has_value())\n    {\n        // Add the IP address to the loopback interface, to be used by the DNS tunneling listener.\n        // Note: Linux allows IPv4 addresses that are not in the range 127.0.0.0/8 to be added to the loopback interface.\n        auto loInterface = Interface::Open(c_loopbackInterfaceName);\n        Address address{AF_INET, 32, dnsTunnelingIpAddress};\n        manager.ModifyAddress(loInterface, address, Operation::Create);\n\n        dnsTunnelingManager.emplace(dnsTunnelingFd.value(), dnsTunnelingIpAddress);\n    }\n}\n\nInterface GnsEngine::OpenAdapterImpl(const GUID& id)\n{\n    std::string interfaceName;\n    for (const auto& e : std::filesystem::directory_iterator(\"/sys/class/net/\"))\n    {\n        auto adapterId = GetAdapterId(e.path());\n        if (adapterId.has_value() && adapterId.value() == id)\n        {\n            interfaceName = e.path().filename().string();\n            // Special case _wlanxx interfaces: look for the wlanxx version instead.\n            if (interfaceName.compare(0, 5, \"_wlan\") == 0)\n            {\n                continue;\n            }\n\n            break;\n        }\n    }\n\n    if (!interfaceName.empty())\n    {\n        GNS_LOG_INFO(\n            \"Found an interface matching the GUID {}, with name {}\",\n            wsl::shared::string::GuidToString<char>(id).c_str(),\n            interfaceName.c_str());\n        return Interface::Open(interfaceName);\n    }\n\n    throw RuntimeErrorWithSourceLocation(std::format(\"Couldn't find an adapter for id: {}\", wsl::shared::string::GuidToString<char>(id)));\n}\n\nInterface GnsEngine::OpenAdapter(const GUID& id)\n{\n    return wsl::shared::retry::RetryWithTimeout<Interface>([&]() { return OpenAdapterImpl(id); }, c_interfaceLookupRetryPeriod, c_interfaceLookupTimeout);\n}\n\nInterface GnsEngine::OpenInterfaceImpl(const std::string& deviceName)\n{\n    try\n    {\n        return Interface::Open(deviceName);\n    }\n    catch (const std::exception& e)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Failed to open interface with device name: {}\", deviceName), e);\n    }\n}\n\nInterface GnsEngine::OpenInterface(const std::string& deviceName)\n{\n    return wsl::shared::retry::RetryWithTimeout<Interface>(\n        [&]() { return OpenInterfaceImpl(deviceName); }, c_interfaceLookupRetryPeriod, c_interfaceLookupTimeout);\n}\n\nstd::optional<GUID> GnsEngine::GetAdapterId(const std::string& path)\n{\n    // Sample symlink:\n    // /sys/class/net/eth0/device -> ../../devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0004:00/VMBUS:00/ebfda100-7464-4629-9da5-12de5470cb4f\n\n    try\n    {\n        auto symlink = std::filesystem::read_symlink(path);\n        const std::string adapterName = symlink.filename();\n        if (adapterName.size() > 3 && adapterName.compare(0, 4, \"wlan\") == 0)\n        {\n            symlink = symlink.parent_path().parent_path();\n        }\n        auto device = symlink.parent_path().parent_path();\n        std::string deviceGuid = device.filename();\n        if (deviceGuid.size() > 6 && deviceGuid.compare(0, 6, \"virtio\") == 0)\n        {\n            deviceGuid = device.parent_path().parent_path().parent_path().filename();\n        }\n\n        return wsl::shared::string::ToGuid(deviceGuid);\n    }\n    catch (...)\n    {\n        return {};\n    }\n}\n\nInterface GnsEngine::OpenInterfaceOrAdapter(const std::wstring& nameOrId)\n{\n    if (!nameOrId.empty() && nameOrId[0] == L'{')\n    {\n        auto id = wsl::shared::string::ToGuid(nameOrId);\n        if (!id.has_value())\n        {\n            THROW_ERRNO(EINVAL);\n        }\n\n        return OpenAdapter(id.value());\n    }\n    else\n    {\n        return OpenInterface(wsl::shared::string::WideToMultiByte(nameOrId));\n    }\n}\n\nvoid GnsEngine::ProcessNotification(const nlohmann::json& payload, Interface& interface)\n{\n    using namespace std::placeholders;\n\n    if (!payload.contains(\"ResourceType\"))\n    {\n        throw RuntimeErrorWithSourceLocation(\"Json is missing ResourceType\");\n    }\n\n    switch (payload[\"ResourceType\"].get<GuestEndpointResourceType>())\n    {\n    case GuestEndpointResourceType::Route:\n        GNS_LOG_INFO(\"GuestEndpointResourceType::Route for interfaceName {}\", interface.Name().c_str());\n        ProcessNotificationImpl(interface, payload, &GnsEngine::ProcessRouteChange);\n        break;\n\n    case GuestEndpointResourceType::IPAddress:\n        GNS_LOG_INFO(\"GuestEndpointResourceType::IPAddress for interfaceName {}\", interface.Name().c_str());\n        ProcessNotificationImpl(interface, payload, &GnsEngine::ProcessIpAddressChange);\n        break;\n\n    case GuestEndpointResourceType::MacAddress:\n        GNS_LOG_INFO(\"GuestEndpointResourceType::MacAddress for interfaceName {}\", interface.Name().c_str());\n        ProcessNotificationImpl(interface, payload, &GnsEngine::ProcessMacAddressChange);\n        break;\n\n    case GuestEndpointResourceType::DNS:\n        GNS_LOG_INFO(\"GuestEndpointResourceType::DNS for interfaceName {}\", interface.Name().c_str());\n        ProcessNotificationImpl(interface, payload, &GnsEngine::ProcessDNSChange);\n        break;\n\n    case GuestEndpointResourceType::Interface:\n        GNS_LOG_INFO(\"GuestEndpointResourceType::Interface for interfaceName {}\", interface.Name().c_str());\n        ProcessNotificationImpl(interface, payload, &GnsEngine::ProcessLinkChange);\n        break;\n\n    default:\n        throw RuntimeErrorWithSourceLocation(std::format(\n            \"Unexpected LxGnsMessageNotification for interfaceName {}: {}\", interface.Name(), payload[\"ResourceType\"].get<std::string>()));\n        break;\n    }\n}\n\ntemplate <typename T>\nvoid GnsEngine::ProcessNotificationImpl(\n    Interface& interface, const nlohmann::json& payload, void (GnsEngine::*routine)(Interface&, const T&, wsl::shared::hns::ModifyRequestType))\n{\n    T settings{};\n    nlohmann::from_json(payload.at(\"Settings\"), settings);\n    (this->*routine)(interface, settings, payload[\"RequestType\"].get<wsl::shared::hns::ModifyRequestType>());\n}\n\nvoid GnsEngine::ProcessIpAddressChange(Interface& interface, const wsl::shared::hns::IPAddress& payload, wsl::shared::hns::ModifyRequestType action)\n{\n    uint16_t addrFamily = UtilWinAfToLinuxAf(payload.Family);\n    if (addrFamily != AF_INET && addrFamily != AF_INET6)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected family: {}\", payload.Family));\n    }\n\n    Address address{\n        addrFamily,\n        payload.OnLinkPrefixLength,\n        wsl::shared::string::WideToMultiByte(payload.Address),\n        static_cast<IpPrefixOrigin>(payload.PrefixOrigin),\n        static_cast<IpSuffixOrigin>(payload.SuffixOrigin),\n        payload.PreferredLifetime};\n\n    // For addresses plumbed through this path, the corresponding prefix route will be plumbed separately,\n    // so do not let Linux autogenerate the prefix route.\n    address.SetIsPrefixRouteAutogenerationDisabled(true);\n\n    const auto addressString = utils::Stringify(address);\n\n    if (action == ModifyRequestType::Remove)\n    {\n        GNS_LOG_INFO(\"Remove address {} on interfaceName {}\", addressString.c_str(), interface.Name().c_str());\n        manager.ModifyAddress(interface, address, Operation::Remove);\n    }\n    else if (action == ModifyRequestType::Add)\n    {\n        GNS_LOG_INFO(\"Add address {} on interfaceName {}\", addressString.c_str(), interface.Name().c_str());\n        manager.ModifyAddress(interface, address, Operation::Create);\n    }\n    else if (action == ModifyRequestType::Update)\n    {\n        GNS_LOG_INFO(\"Update address {} on interfaceName {}\", addressString.c_str(), interface.Name().c_str());\n        manager.ModifyAddress(interface, address, Operation::Update);\n    }\n    else\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected ip address action: {}\", static_cast<int>(action)));\n    }\n}\n\nvoid GnsEngine::ProcessRouteChange(Interface& interface, const wsl::shared::hns::Route& route, wsl::shared::hns::ModifyRequestType action)\n{\n    int addrFamily = UtilWinAfToLinuxAf(route.Family);\n    if (addrFamily != AF_INET && addrFamily != AF_INET6)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected family: {}\", route.Family));\n    }\n\n    if (action == ModifyRequestType::Reset)\n    {\n        GNS_LOG_INFO(\"Reset routes on interfaceName {}\", interface.Name().c_str());\n        manager.ResetRoutingTable(addrFamily, interface);\n        return;\n    }\n\n    bool defaultRoute = (addrFamily == AF_INET && route.DestinationPrefix == LX_INIT_DEFAULT_ROUTE_PREFIX) ||\n                        (addrFamily == AF_INET6 && route.DestinationPrefix == LX_INIT_DEFAULT_ROUTE_V6_PREFIX);\n    std::optional<Address> to;\n    if (!defaultRoute)\n    {\n        to = Address::FromPrefixString(addrFamily, wsl::shared::string::WideToMultiByte(route.DestinationPrefix));\n    }\n\n    // Note: for the next hop parameter to the Route constructor, the prefix length can be any valid prefix length -\n    // it's just used to create an address object.  We currently use the SitePrefixLength field for convenience.\n    const auto nextHopValue = wsl::shared::string::WideToMultiByte(route.NextHop);\n    auto interfaceRoute =\n        Route{addrFamily, {{addrFamily, route.SitePrefixLength, nextHopValue}}, interface.Index(), defaultRoute, to, route.Metric};\n\n    auto routeString = utils::Stringify(interfaceRoute);\n\n    if (action == ModifyRequestType::Add)\n    {\n        GNS_LOG_INFO(\"Add route {} on interfaceName {}\", routeString.c_str(), interface.Name().c_str());\n        manager.ModifyRoute(interfaceRoute, Operation::Create);\n    }\n    else if (action == ModifyRequestType::Remove)\n    {\n        GNS_LOG_INFO(\"Remove route {} on interfaceName {}\", routeString.c_str(), interface.Name().c_str());\n        manager.ModifyRoute(interfaceRoute, Operation::Remove);\n    }\n    else if (action == ModifyRequestType::Update)\n    {\n        GNS_LOG_INFO(\"Update route {} on interfaceName {}\", routeString.c_str(), interface.Name().c_str());\n        manager.ModifyRoute(interfaceRoute, Operation::Update);\n    }\n    else\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected route action: {}\", static_cast<int>(action)));\n    }\n}\n\nvoid GnsEngine::ProcessDNSChange(Interface& interface, const wsl::shared::hns::DNS& payload, wsl::shared::hns::ModifyRequestType action)\n{\n    if (action == ModifyRequestType::Remove)\n    {\n        GNS_LOG_INFO(\"Ignoring Remove on interfaceName {}\", interface.Name().c_str());\n        return; // Will be overwritten when the next 'add' / 'update' comes\n    }\n\n    if (action != ModifyRequestType::Update && action != ModifyRequestType::Add)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected DNS Change action: {}\", static_cast<int>(action)));\n    }\n\n    std::wstringstream content;\n    if (!payload.Options.empty())\n    {\n        content << payload.Options; // The Options field is used to pass the file header\n    }\n\n    for (const auto& server : wsl::shared::string::Split(payload.ServerList, L','))\n    {\n        content << L\"nameserver \" << server << L\"\\n\";\n    }\n\n    // Use 'search' for DNS suffixes.\n    // Per resolv.conf(5): \"The domain directive is an obsolete name for the search directive\n    // that handles one search list entry only.\"\n    // See: https://man7.org/linux/man-pages/man5/resolv.conf.5.html\n    if (!payload.Search.empty())\n    {\n        content << L\"search \" << wsl::shared::string::Join(wsl::shared::string::Split(payload.Search, L','), L' ') << L\"\\n\";\n    }\n\n    GNS_LOG_INFO(\n        \"Setting DNS search to {}: {} on interfaceName {} \", payload.Search.c_str(), content.str().c_str(), interface.Name().c_str());\n\n    std::wofstream resolvConf;\n    resolvConf.exceptions(std::ofstream::badbit | std::ofstream::failbit);\n    resolvConf.open(\"/etc/resolv.conf\", std::ofstream::trunc);\n    resolvConf << content.str();\n}\n\nvoid GnsEngine::ProcessMacAddressChange(Interface& interface, const wsl::shared::hns::MacAddress& address, wsl::shared::hns::ModifyRequestType type)\n{\n    GNS_LOG_INFO(\n        \"Setting to MAC address to {} (will toggle the interface state) on interfaceName {} \",\n        address.PhysicalAddress.c_str(),\n        interface.Name().c_str());\n    manager.SetAdapterMacAddress(interface, wsl::shared::string::ParseMacAddress(address.PhysicalAddress, '-'));\n}\n\nvoid GnsEngine::ProcessLinkChange(Interface& interface, const wsl::shared::hns::NetworkInterface& link, wsl::shared::hns::ModifyRequestType type)\n{\n    GNS_LOG_INFO(\n        \"Setting link state to {} on interfaceName {}\",\n        link.Connected ? \"InterfaceState::Up\" : \"InterfaceState::Down\",\n        interface.Name().c_str());\n    manager.SetInterfaceState(interface, link.Connected ? NetworkManager::InterfaceState::Up : NetworkManager::InterfaceState::Down);\n\n    if (link.Connected && link.NlMtu != 0)\n    {\n        GNS_LOG_INFO(\"Setting MTU to {} on interfaceName {} \", link.NlMtu, interface.Name().c_str());\n        interface.SetMtu(link.NlMtu);\n    }\n\n    if (link.Connected && link.Metric != 0)\n    {\n        GNS_LOG_INFO(\"Setting Metric to {} on interfaceName {} \", link.Metric, interface.Name().c_str());\n        interface.SetMetric(link.Metric);\n    }\n}\n\nstd::tuple<bool, int> GnsEngine::ProcessNextMessage()\n{\n    int return_value = 0;\n\n    auto payload = notificationRoutine();\n    if (!payload.has_value())\n    {\n        GNS_LOG_ERROR(\"Received empty message, exiting\");\n        return std::make_tuple(false, -1);\n    }\n\n    switch (payload->MessageType)\n    {\n    case LxGnsMessageNoOp:\n    {\n        break;\n    }\n    case LxGnsMessageNotification:\n    {\n        auto interface = OpenAdapter(payload->AdapterId.value());\n\n        ProcessNotification(nlohmann::json::parse(payload->Json), interface);\n        break;\n    }\n    case LxGnsMessageInterfaceConfiguration:\n    {\n        const auto endpoint = wsl::shared::FromJson<wsl::shared::hns::HNSEndpoint>(payload->Json.c_str());\n        const auto endpointString = wsl::shared::string::GuidToString<char>(endpoint.ID);\n        auto interface = OpenAdapter(endpoint.ID);\n\n        // Give the interface a new name if requested.\n        if (endpoint.PortFriendlyName.size() > 0)\n        {\n            auto assignedName = wsl::shared::string::WideToMultiByte(endpoint.PortFriendlyName);\n            if (assignedName.compare(interface.Name()) != 0)\n            {\n                // Special case for wlanxx adapters: create a virtual wifi interface.\n                if (assignedName.size() > 3 && assignedName.compare(0, 4, \"wlan\") == 0)\n                {\n                    auto backingName = std::string(\"_\") + assignedName;\n                    GNS_LOG_INFO(\n                        \"LxGnsMessageInterfaceConfiguration: endpointID ({}) setting interfaceName to {}\",\n                        endpointString.c_str(),\n                        backingName.c_str());\n                    manager.SetAdapterName(interface, backingName);\n\n                    GNS_LOG_INFO(\n                        \"LxGnsMessageInterfaceConfiguration: endpointID ({}) creating virtual Wi-Fi named {}\",\n                        endpointString.c_str(),\n                        assignedName.c_str());\n                    interface = manager.CreateVirtualWifiAdapter(interface, assignedName);\n\n                    auto backingInterface = Interface::Open(backingName);\n                    GNS_LOG_INFO(\n                        \"LxGnsMessageInterfaceConfiguration: endpointID ({}) setting interface ({}) state up on the newly \"\n                        \"created interfaceName {}\",\n                        endpointString.c_str(),\n                        backingName.c_str(),\n                        backingInterface.Name().c_str());\n                    manager.SetInterfaceState(backingInterface, NetworkManager::InterfaceState::Up);\n                }\n                else\n                {\n                    GNS_LOG_INFO(\n                        \"LxGnsMessageInterfaceConfiguration: endpointID ({}) setting interfaceName from {} to {}\",\n                        endpointString.c_str(),\n                        interface.Name().c_str(),\n                        assignedName.c_str());\n                    manager.SetAdapterName(interface, assignedName);\n                    interface = Interface::Open(assignedName);\n                }\n            }\n            else\n            {\n                GNS_LOG_INFO(\n                    \"LxGnsMessageInterfaceConfiguration: no-op - the endpoint ID {} PortFriendlyName ({}) is already matching \"\n                    \"the interfaceName {}\",\n                    endpointString.c_str(),\n                    assignedName.c_str(),\n                    interface.Name().c_str());\n            }\n        }\n        else\n        {\n            GNS_LOG_INFO(\n                \"LxGnsMessageInterfaceConfiguration: no-op - the endpoint ID {} PortFriendlyName is blank\", endpointString.c_str());\n        }\n\n        // The IP address can be empty if flow steering is enabled (we'll get it from a notification)\n        if (!endpoint.IPAddress.empty())\n        {\n            manager.SetAdapterConfiguration(interface, endpoint);\n        }\n\n        manager.SetInterfaceState(interface, NetworkManager::InterfaceState::Up);\n        break;\n    }\n    case LxGnsMessageVmNicCreatedNotification:\n    {\n        auto vmNic = wsl::shared::FromJson<wsl::shared::hns::VmNicCreatedNotification>(payload->Json.c_str());\n        auto interface = OpenAdapter(vmNic.adapterId);\n\n        GNS_LOG_INFO(\n            \"LxGnsMessageVmNicCreatedNotification: EnableLoopbackRouting on adapterId {}, interfaceName {}\",\n            wsl::shared::string::GuidToString<char>(vmNic.adapterId).c_str(),\n            interface.Name().c_str());\n        manager.EnableLoopbackRouting(interface);\n        break;\n    }\n    case LxGnsMessageCreateDeviceRequest:\n    {\n        auto createDeviceRequest = wsl::shared::FromJson<wsl::shared::hns::CreateDeviceRequest>(payload->Json.c_str());\n        switch (createDeviceRequest.type)\n        {\n        case wsl::shared::hns::DeviceType::Loopback:\n        {\n            const GUID emptyGuid{};\n            assert(createDeviceRequest.lowerEdgeAdapterId.has_value());\n            auto gelnic = OpenAdapter(createDeviceRequest.lowerEdgeAdapterId.value());\n            GNS_LOG_INFO(\n                \"LxGnsMessageCreateDeviceRequest [Loopback]: InitializeLoopbackConfiguration deviceName {}, interfaceName {}\",\n                wsl::shared::string::GuidToString<char>(createDeviceRequest.lowerEdgeAdapterId.value_or(emptyGuid)).c_str(),\n                gelnic.Name().c_str());\n            manager.InitializeLoopbackConfiguration(gelnic);\n            break;\n        }\n        default:\n            throw RuntimeErrorWithSourceLocation(\n                std::format(\"Unexpected Wslcore::Networking::DeviceType : {}\", static_cast<int>(createDeviceRequest.type)));\n            break;\n        }\n        break;\n    }\n    case LxGnsMessageModifyGuestDeviceSettingRequest:\n    {\n        auto modifyRequest = wsl::shared::FromJson<wsl::shared::hns::ModifyGuestEndpointSettingRequest<wsl::shared::hns::NetworkInterface>>(\n            payload->Json.c_str());\n        if (modifyRequest.ResourceType != GuestEndpointResourceType::Interface)\n        {\n            GNS_LOG_INFO(\n                \"ModifyGuestEndpointSettingRequest - ignoring request that's not for type Interface (type {}) device \"\n                \"{}\",\n                static_cast<uint32_t>(modifyRequest.ResourceType),\n                modifyRequest.targetDeviceName.value_or(L\"<empty>\").c_str());\n            break;\n        }\n\n        if (!modifyRequest.targetDeviceName.has_value())\n        {\n            GNS_LOG_INFO(\"ModifyGuestEndpointSettingRequest targetDeviceName is empty\");\n            break;\n        }\n\n        auto interface = OpenInterfaceOrAdapter(modifyRequest.targetDeviceName.value());\n        GNS_LOG_INFO(\n            \"ModifyGuestEndpointSettingRequest [Interface]: setting link state for deviceName {} interfaceName {}\",\n            modifyRequest.targetDeviceName->c_str(),\n            interface.Name().c_str());\n\n        ProcessLinkChange(interface, modifyRequest.Settings, modifyRequest.RequestType);\n        break;\n    }\n    case LxGnsMessageLoopbackRoutesRequest:\n    {\n        auto request = wsl::shared::FromJson<wsl::shared::hns::LoopbackRoutesRequest>(payload->Json.c_str());\n        if (request.operation != wsl::shared::hns::OperationType::Create && request.operation != wsl::shared::hns::OperationType::Remove)\n        {\n            GNS_LOG_INFO(\n                \"LxGnsMessageLoopbackRoutesRequest - ignoring request that has the wrong operation type {} for interface \"\n                \"{}\",\n                static_cast<int>(request.operation),\n                request.targetDeviceName.c_str());\n            break;\n        }\n\n        int addrFamily = UtilWinAfToLinuxAf(request.family);\n        if (addrFamily != AF_INET && addrFamily != AF_INET6)\n        {\n            throw RuntimeErrorWithSourceLocation(std::format(\"LxGnsMessageLoopbackRoutesRequest: unexpected family: {}\", request.family));\n        }\n\n        assert(request.operation == wsl::shared::hns::OperationType::Create || request.operation == wsl::shared::hns::OperationType::Remove);\n        auto operation = (request.operation == wsl::shared::hns::OperationType::Create) ? Operation::Create : Operation::Remove;\n        auto interface = OpenInterfaceOrAdapter(request.targetDeviceName);\n        auto ipAddress = wsl::shared::string::WideToMultiByte(request.ipAddress);\n        int prefixLen = MAX_PREFIX_LEN(addrFamily);\n        Address address(addrFamily, prefixLen, ipAddress);\n        manager.UpdateLoopbackRoute(interface, address, operation);\n        break;\n    }\n    case LxGnsMessageDeviceSettingRequest:\n    {\n        auto json = nlohmann::json::parse(payload->Json);\n        auto interface = OpenInterfaceOrAdapter(json.at(\"targetDeviceName\").get<std::wstring>());\n        ProcessNotification(json, interface);\n        break;\n    }\n    case LxGnsMessageInitialIpConfigurationNotification:\n    {\n        auto notification = wsl::shared::FromJson<wsl::shared::hns::InitialIpConfigurationNotification>(payload->Json.c_str());\n        auto interface = OpenInterfaceOrAdapter(notification.targetDeviceName);\n\n        if (WI_IsFlagClear(notification.flags, wsl::shared::hns::InitialIpConfigurationNotificationFlags::SkipPrimaryRoutingTableUpdate))\n        {\n            auto table = manager.FindRoutingTableIdForInterface(interface);\n            if (!table.has_value())\n            {\n                throw RuntimeErrorWithSourceLocation(std::format(\n                    \"LxGnsMessageInitialIpConfigurationNotification: failed to find routing table with name {}\", interface.Name()));\n            }\n\n            GNS_LOG_INFO(\n                \"LxGnsMessageInitialIpConfigurationNotification: Changing primary routing table to {} with id {}\",\n                interface.Name().c_str(),\n                table.value());\n            manager.ChangePrimaryRoutingTable(table.value());\n        }\n\n        GNS_LOG_INFO(\"LxGnsMessageInitialIpConfigurationNotification: Resetting IPv6 state for interface {}\", interface.Name().c_str());\n        interface.ResetIpv6State();\n\n        if (WI_IsFlagClear(notification.flags, wsl::shared::hns::InitialIpConfigurationNotificationFlags::SkipLoopbackRouteReset))\n        {\n            GNS_LOG_INFO(\"LxGnsMessageInitialIpConfigurationNotification: Wiping loopback routes\");\n            manager.ResetLoopbackRoutes();\n        }\n\n        // EnableIpv4ArpFilter does not need to be called per interface as each interface gets mirrored\n        // It could be global if we had a single global Init message\n        // If there are more global init requirements in the future, we should consider a new global message\n        GNS_LOG_INFO(\"LxGnsMessageInitialIpConfigurationNotification: Enabling IPv4 arp_filter\");\n        manager.EnableIpv4ArpFilter();\n        break;\n    }\n    case LxGnsMessageSetupIpv6:\n    {\n        manager.DisableDAD();\n        manager.DisableRouterDiscovery();\n        manager.DisableIpv6AddressGeneration();\n        break;\n    }\n    case LxGnsMessageConnectTestRequest:\n    {\n        // the payload is where to send the request, not in a JSON format\n        wsl::shared::conncheck::ConnCheckResult result = manager.SendConnectRequest(payload->Json.c_str());\n        // convert the 2 enums into a single integer value\n        // Ipv4 status will be the lower 16 bits\n        return_value = static_cast<uint32_t>(result.Ipv4Status);\n        // Ipv6 status will be the upper 16 bits\n        return_value |= (static_cast<uint32_t>(result.Ipv6Status) << 16);\n        GNS_LOG_INFO(\"LxGnsMessageConnectTestRequest (destination: {}) returning: {:#x}\", payload->Json.c_str(), return_value);\n        break;\n    }\n    case LxGnsMessageGlobalNetFilter:\n    {\n        // the global network filters exist to 'mark' traffic that is originating from root\n        // vs. traffic that is originating from another Linux container (namespace)\n        // it also adds a NAT to the chain for the traffic that is not marked\n        // see the 'nft add rule' command in the below LxGnsMessageInterfaceNetFilter section\n        auto runCommand = [](const std::string& command) { THROW_LAST_ERROR_IF(UtilExecCommandLine(command.c_str()) < 0); };\n        for (const auto& ip : c_ipStrings)\n        {\n            runCommand(std::format(\"nft add table {} filter\", ip));\n            runCommand(std::format(\"nft \\\"add chain {} filter WSLOUTPUT {{ type filter hook output priority filter; }}\\\"\", ip));\n            runCommand(std::format(\"nft add rule {} filter WSLOUTPUT counter mark set 0x1\", ip));\n            runCommand(std::format(\"nft add table {} nat\", ip));\n            runCommand(std::format(\"nft \\\"add chain {} nat WSLPOSTROUTING {{ type nat hook postrouting priority srcnat - 1; }}\\\"\", ip));\n        }\n\n        break;\n    }\n    case LxGnsMessageInterfaceNetFilter:\n    {\n        auto interfaceNetFilterRequest = wsl::shared::FromJson<wsl::shared::hns::InterfaceNetFilterRequest>(payload->Json.c_str());\n        auto interface = OpenInterfaceOrAdapter(interfaceNetFilterRequest.targetDeviceName);\n\n        GNS_LOG_INFO(\n            \"LxGnsMessageInterfaceNetFilter for interface {} {{operation={}, startPort={}, endPort={}}}\",\n            interface.Name().c_str(),\n            static_cast<int>(interfaceNetFilterRequest.operation),\n            interfaceNetFilterRequest.ephemeralPortRangeStart,\n            interfaceNetFilterRequest.ephemeralPortRangeEnd);\n\n        switch (interfaceNetFilterRequest.operation)\n        {\n        case wsl::shared::hns::OperationType::Create:\n        {\n            // Create SNAT rules on the interface.\n            for (const auto& ip : c_ipStrings)\n            {\n                for (const auto& protocol : {\"udp\", \"tcp\"})\n                {\n                    const auto commandLine = std::format(\n                        \"nft add rule {} nat WSLPOSTROUTING oif {} {} sport 1-65535 mark != 0x1 counter masquerade to :{}-{}\",\n                        ip,\n                        interface.Name().c_str(),\n                        protocol,\n                        interfaceNetFilterRequest.ephemeralPortRangeStart,\n                        interfaceNetFilterRequest.ephemeralPortRangeEnd);\n\n                    GNS_LOG_INFO(\"LxGnsMessageInterfaceNetFilter (Create): {}\", commandLine.c_str());\n                    THROW_LAST_ERROR_IF(UtilExecCommandLine(commandLine.c_str()) < 0);\n                }\n            }\n\n            manager.UpdateMirroredLoopbackRulesForInterface(interface.Name(), Operation::Create);\n            break;\n        }\n        case wsl::shared::hns::OperationType::Remove:\n        {\n            // Remove SNAT rules on the interface (one in ipv4 and one in ipv6).\n            // Rules can only be removed via handle number, so find the handle numbers first.\n            for (const auto& ip : c_ipStrings)\n            {\n                const auto listChainCommand = std::format(\"nft -a list chain {} nat WSLPOSTROUTING\", ip);\n                std::string listOutputString;\n                THROW_LAST_ERROR_IF(UtilExecCommandLine(listChainCommand.c_str(), &listOutputString) < 0);\n\n                std::regex pattern(\"oif\\\\s+\\\"\" + interface.Name() + \"\\\"\\\\s+.*handle\\\\s+(\\\\d+)\");\n                std::smatch matches;\n                std::vector<int> handleNumbers;\n                auto iter = listOutputString.cbegin();\n                while (std::regex_search(iter, listOutputString.cend(), matches, pattern))\n                {\n                    handleNumbers.push_back(std::stoi(matches.str(1)));\n                    iter = matches.suffix().first;\n                }\n\n                for (const auto& handle : handleNumbers)\n                {\n                    auto commandLine = std::format(\"nft delete rule {} nat WSLPOSTROUTING handle {}\", ip, handle);\n                    GNS_LOG_INFO(\"LxGnsMessageInterfaceNetFilter (Remove): {}\", commandLine.c_str());\n                    THROW_LAST_ERROR_IF(UtilExecCommandLine(commandLine.c_str()) < 0);\n                }\n            }\n\n            manager.UpdateMirroredLoopbackRulesForInterface(interface.Name(), Operation::Remove);\n            break;\n        }\n        default:\n            throw RuntimeErrorWithSourceLocation(std::format(\n                \"Unexpected Wslcore::Networking::OperationType : {}\", static_cast<int>(interfaceNetFilterRequest.operation)));\n            break;\n        }\n        break;\n    }\n\n    default:\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected LX_MESSAGE_TYPE : {}\", static_cast<int>(payload->MessageType)));\n    }\n\n    return std::make_tuple(true, return_value);\n}\n\nvoid GnsEngine::run()\n{\n    UtilSetThreadName(\"GnsEngine\");\n\n    while (true)\n    {\n        try\n        {\n            GNS_LOG_INFO(\"Processing Next Message\");\n            auto [should_continue, return_value] = ProcessNextMessage();\n            if (!should_continue)\n            {\n                break;\n            }\n\n            GNS_LOG_INFO(\"Processing Next Message Successful ({:#x})\", return_value);\n            statusRoutine(return_value, \"\");\n        }\n        catch (const std::exception& e)\n        {\n            GNS_LOG_ERROR(\"Error while processing message: {}\", e.what());\n            statusRoutine(-1, e.what());\n        }\n    }\n\n    // ensure our exit path is in the error stream\n    GNS_LOG_ERROR(\"exiting\");\n}\n"
  },
  {
    "path": "src/linux/init/GnsEngine.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include <functional>\n#include <optional>\n#include <tuple>\n#include \"util.h\"\n#include \"lxinitshared.h\"\n#include \"NetworkManager.h\"\n#include \"DnsTunnelingManager.h\"\n#include \"hns_schema.h\"\n\nclass GnsEngine\n{\npublic:\n    struct Message\n    {\n        LX_MESSAGE_TYPE MessageType;\n        std::string Json;\n        std::optional<GUID> AdapterId;\n    };\n\n    using NotificationRoutine = std::function<std::optional<Message>()>;\n    using StatusRoutine = std::function<void(int, const std::string&)>;\n\n    GnsEngine(\n        const NotificationRoutine& notificationRoutine,\n        const StatusRoutine& statusRoutine,\n        NetworkManager& manager,\n        std::optional<int> dnsTunnelingFd,\n        const std::string& dnsTunnelingIpAddress);\n\n    void setup();\n\n    void run();\n\nprivate:\n    std::tuple<bool, int> ProcessNextMessage();\n\n    void ProcessNotification(const nlohmann::json& payload, Interface& interface);\n\n    void ProcessRouteChange(Interface& interface, const wsl::shared::hns::Route& route, wsl::shared::hns::ModifyRequestType type);\n\n    void ProcessIpAddressChange(Interface& interface, const wsl::shared::hns::IPAddress& route, wsl::shared::hns::ModifyRequestType type);\n\n    void ProcessMacAddressChange(Interface& interface, const wsl::shared::hns::MacAddress& mac, wsl::shared::hns::ModifyRequestType type);\n\n    void ProcessDNSChange(Interface& interface, const wsl::shared::hns::DNS& dns, wsl::shared::hns::ModifyRequestType type);\n\n    void ProcessLinkChange(Interface& interface, const wsl::shared::hns::NetworkInterface& link, wsl::shared::hns::ModifyRequestType type);\n\n    static Interface OpenAdapter(const GUID& id);\n\n    static Interface OpenAdapterImpl(const GUID& id);\n\n    static Interface OpenInterface(const std::string& deviceName);\n\n    static Interface OpenInterfaceImpl(const std::string& deviceName);\n\n    static Interface OpenInterfaceOrAdapter(const std::wstring& nameOrId);\n\n    static std::optional<GUID> GetAdapterId(const std::string& name);\n\n    template <typename T>\n    static T Deserialize(const std::string& json);\n\n    template <typename T>\n    void ProcessNotificationImpl(\n        Interface& interface, const nlohmann::json& payload, void (GnsEngine::*routine)(Interface&, const T&, wsl::shared::hns::ModifyRequestType));\n\n    const NotificationRoutine& notificationRoutine;\n    const StatusRoutine& statusRoutine;\n    NetworkManager& manager;\n\n    std::optional<DnsTunnelingManager> dnsTunnelingManager;\n};\n"
  },
  {
    "path": "src/linux/init/GnsPortTracker.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include <filesystem>\n#include <optional>\n#include <iostream>\n#include <linux/audit.h> /* Definition of AUDIT_* constants */\n#include <linux/sock_diag.h>\n#include <linux/inet_diag.h>\n#include <linux/net.h>\n#include <sys/syscall.h>\n#include <sys/xattr.h>\n#include \"common.h\" // Needs to be included before sal.h before of __reserved macro\n#include \"NetlinkTransactionError.h\"\n#include \"GnsPortTracker.h\"\n#include \"lxinitshared.h\"\n\nconstexpr size_t c_bind_timeout_seconds = 60;\nconstexpr auto c_sock_diag_refresh_delay = std::chrono::milliseconds(500);\nconstexpr auto c_sock_diag_poll_timeout = std::chrono::milliseconds(10);\nconstexpr auto c_bpf_poll_timeout = std::chrono::milliseconds(500);\n\nGnsPortTracker::GnsPortTracker(\n    std::shared_ptr<wsl::shared::SocketChannel> hvSocketChannel, NetlinkChannel&& netlinkChannel, std::shared_ptr<SecCompDispatcher> seccompDispatcher) :\n    m_hvSocketChannel(std::move(hvSocketChannel)), m_channel(std::move(netlinkChannel)), m_seccompDispatcher(seccompDispatcher)\n{\n    m_networkNamespace = std::filesystem::read_symlink(\"/proc/self/ns/net\").string();\n}\n\nvoid GnsPortTracker::RunPortRefresh()\n{\n    UtilSetThreadName(\"GnsPortTracker\");\n\n    // The polling of bound sockets is done in a separate thread because\n    // sock_diag sometimes fails with EBUSY when a bind() is in progress.\n    // Doing this in a separate thread allows the main thread not to be delayed\n    // because of transient sock_diag failures\n\n    for (;;)\n    {\n        // Netlink will sometimes return EBUSY. Don't fail for that\n        try\n        {\n            std::promise<void> resume;\n            auto result = PortRefreshResult{ListAllocatedPorts(), time(nullptr), std::bind(&std::promise<void>::set_value, &resume)};\n            m_allocatedPortsRefresh.set_value(result);\n\n            resume.get_future().wait();\n        }\n        catch (const NetlinkTransactionError& e)\n        {\n            if (e.Error().value_or(0) != -EBUSY)\n            {\n                std::cerr << \"Failed to refresh allocated ports, \" << e.what() << std::endl;\n            }\n        }\n\n        std::this_thread::sleep_for(c_sock_diag_refresh_delay);\n    }\n}\n\nint GnsPortTracker::ProcessSecCompNotification(seccomp_notif* notification)\n{\n    seccomp_notif notificationCopy = *notification;\n    m_request.post(notificationCopy);\n    return m_reply.get();\n}\n\nvoid GnsPortTracker::Run()\n{\n    // This method consumes seccomp notifications and allows / disallows port allocations\n    // depending on wsl core's response.\n    // After dealing with a notification it also looks at the bound ports list to check\n    // for port deallocation\n\n    std::thread{std::bind(&GnsPortTracker::RunPortRefresh, this)}.detach();\n    std::thread{std::bind(&GnsPortTracker::RunDeferredResolve, this)}.detach();\n\n    auto future = std::make_optional(m_allocatedPortsRefresh.get_future());\n    std::optional<PortRefreshResult> refreshResult;\n\n    for (;;)\n    {\n        std::optional<BindCall> bindCall;\n        try\n        {\n            bindCall = ReadNextRequest();\n        }\n        catch (const std::exception& e)\n        {\n            GNS_LOG_ERROR(\"Failed to read bind request, {}\", e.what());\n        }\n\n        if (bindCall.has_value())\n        {\n            int result = 0;\n            if (bindCall->Request.has_value())\n            {\n                PortAllocation& allocationRequest = bindCall->Request.value();\n                result = HandleRequest(allocationRequest);\n                if (result == 0)\n                {\n                    TrackPort(allocationRequest);\n                    GNS_LOG_INFO(\n                        \"Tracking bind call: family ({}) port ({}) protocol ({})\",\n                        allocationRequest.Family,\n                        allocationRequest.Port,\n                        allocationRequest.Protocol);\n                }\n            }\n\n            try\n            {\n                CompleteRequest(bindCall->CallId, result);\n            }\n            catch (const std::exception& e)\n            {\n                GNS_LOG_ERROR(\"Failed to complete bind request, {}\", e.what());\n            }\n\n            if (bindCall->PortZeroBind.has_value())\n            {\n                try\n                {\n                    std::lock_guard lock(m_deferredMutex);\n                    m_deferredQueue.push_back(std::move(bindCall->PortZeroBind.value()));\n                    m_deferredCv.notify_one();\n                }\n                catch (const std::exception& e)\n                {\n                    GNS_LOG_ERROR(\"Failed to queue port-0 bind for deferred resolution, {}\", e.what());\n                }\n            }\n        }\n\n        // If bindCall is empty, then the read() timed out. Look for any closed port\n        if (future.has_value() && future->wait_for(c_sock_diag_poll_timeout) == std::future_status::ready)\n        {\n            refreshResult.emplace(future->get());\n            future.reset();\n            m_allocatedPortsRefresh = {};\n\n            // If this loop's iteration had a bind call, it's possible that RefreshAllocatedPort\n            // was called before the bind called was processed. Make sure that the port list\n            // is up to date (If this is called, the next block will schedule another refresh)\n            if (!bindCall.has_value())\n            {\n                OnRefreshAllocatedPorts(refreshResult->Ports, refreshResult->Timestamp);\n            }\n        }\n\n        // Process any port-0 binds that the background thread has resolved.\n        std::deque<PortAllocation> resolved;\n        {\n            std::lock_guard lock(m_resolvedMutex);\n            resolved.swap(m_resolvedQueue);\n        }\n        for (auto& allocation : resolved)\n        {\n            const auto result = HandleRequest(allocation);\n            if (result == 0)\n            {\n                TrackPort(std::move(allocation));\n            }\n            else\n            {\n                GNS_LOG_ERROR(\n                    \"Failed to register resolved port-0 bind: family ({}) port ({}) protocol ({}), error {}\",\n                    allocation.Family,\n                    allocation.Port,\n                    allocation.Protocol,\n                    result);\n            }\n        }\n\n        // Only look at bound ports if there's something to deallocate to avoid wasting cycles\n        if (refreshResult.has_value())\n        {\n            if (!m_allocatedPorts.empty())\n            {\n                future = m_allocatedPortsRefresh.get_future();\n                refreshResult->Resume(); // This will resume the sock_diag thread\n                refreshResult.reset();\n            }\n        }\n    }\n}\n\nstd::set<GnsPortTracker::PortAllocation> GnsPortTracker::ListAllocatedPorts()\n{\n    std::set<PortAllocation> ports;\n\n    inet_diag_req_v2 message{};\n    message.sdiag_family = AF_INET;\n    message.sdiag_protocol = IPPROTO_TCP;\n    message.idiag_states = ~0;\n\n    auto onMessage = [&](const NetlinkResponse& response) {\n        for (const auto& e : response.Messages<inet_diag_msg>(SOCK_DIAG_BY_FAMILY))\n        {\n            const auto* payload = e.Payload();\n            in6_addr address = {};\n\n            if (payload->idiag_family == AF_INET6)\n            {\n                static_assert(sizeof(address.s6_addr32) == 16);\n                static_assert(sizeof(address.s6_addr32) == sizeof(payload->id.idiag_src));\n                memcpy(address.s6_addr32, payload->id.idiag_src, sizeof(address.s6_addr32));\n            }\n            else\n            {\n                address.s6_addr32[0] = payload->id.idiag_src[0];\n            }\n\n            ports.emplace(ntohs(payload->id.idiag_sport), static_cast<int>(payload->idiag_family), static_cast<int>(message.sdiag_protocol), address);\n        }\n    };\n\n    {\n        auto transaction = m_channel.CreateTransaction(message, SOCK_DIAG_BY_FAMILY, NLM_F_DUMP);\n        transaction.Execute(onMessage);\n    }\n\n    message.sdiag_family = AF_INET6;\n    {\n        auto transaction = m_channel.CreateTransaction(message, SOCK_DIAG_BY_FAMILY, NLM_F_DUMP);\n        transaction.Execute(onMessage);\n    }\n\n    message.sdiag_protocol = IPPROTO_UDP;\n    message.sdiag_family = AF_INET;\n    {\n        auto transaction = m_channel.CreateTransaction(message, SOCK_DIAG_BY_FAMILY, NLM_F_DUMP);\n        transaction.Execute(onMessage);\n    }\n\n    message.sdiag_family = AF_INET6;\n    {\n        auto transaction = m_channel.CreateTransaction(message, SOCK_DIAG_BY_FAMILY, NLM_F_DUMP);\n        transaction.Execute(onMessage);\n    }\n\n    return ports;\n}\n\nvoid GnsPortTracker::OnRefreshAllocatedPorts(const std::set<PortAllocation>& Ports, time_t Timestamp)\n{\n    // Because there's no way to get notified when the bind() call actually completes, it' possible\n    // that this method is called before the bind() completion and so the port allocation may not be visible yet.\n    // To avoid deallocating ports that simply haven't been done allocating yet, m_allocatedPorts stores a timeout\n    // that prevents deallocating the port unless:\n    //\n    // - The port has been seen to be allocated (if so, then the timeout is empty)\n    // - The timeout has expired\n\n    for (auto it = m_allocatedPorts.begin(); it != m_allocatedPorts.end();)\n    {\n        if (Ports.find(it->first) == Ports.end())\n        {\n            if (!it->second.has_value() || it->second.value() < Timestamp)\n            {\n                auto result = RequestPort(it->first, false);\n                if (result != 0)\n                {\n                    std::cerr << \"GnsPortTracker: Failed to deallocate port \" << it->first << \", \" << result << std::endl;\n                }\n\n                GNS_LOG_INFO(\n                    \"No longer tracking bind call: family ({}) port ({}) protocol ({})\",\n                    it->first.Family,\n                    it->first.Port,\n                    it->first.Protocol);\n\n                it = m_allocatedPorts.erase(it);\n                continue;\n            }\n        }\n        else\n        {\n            it->second.reset(); // The port is known to be allocated, remove the timeout\n        }\n\n        it++;\n    }\n}\n\nint GnsPortTracker::RequestPort(const PortAllocation& Port, bool allocate)\n{\n    LX_GNS_PORT_ALLOCATION_REQUEST request{};\n    request.Header.MessageType = LxGnsMessagePortMappingRequest;\n    request.Header.MessageSize = sizeof(request);\n    request.Af = Port.Family;\n    request.Protocol = Port.Protocol;\n    request.Port = Port.Port;\n    request.Allocate = allocate;\n    static_assert(sizeof(request.Address32) == 16);\n    static_assert(sizeof(request.Address32) == sizeof(Port.Address.s6_addr32));\n    memcpy(request.Address32, Port.Address.s6_addr32, sizeof(request.Address32));\n\n    const auto& response = m_hvSocketChannel->Transaction(request);\n\n    return response.Result;\n}\n\nint GnsPortTracker::HandleRequest(const PortAllocation& Port)\n{\n    // If the port is already allocated, let the call go through and the kernel will\n    // decide if bind() should succeed or not\n    // Note: Returning 0 will also cause the port's timeout to be updated\n\n    if (m_allocatedPorts.contains(Port))\n    {\n        GNS_LOG_INFO(\"Request for a port that's already reserved (family {}, port {}, protocol {})\", Port.Family, Port.Port, Port.Protocol);\n        return 0;\n    }\n\n    // Ask the host for this port otherwise\n    const auto error = RequestPort(Port, true);\n    GNS_LOG_INFO(\n        \"Requested the host for port allocation on port (family {}, port {}, protocol {}) - returned {}\", Port.Family, Port.Port, Port.Protocol, error);\n    return error;\n}\n\nstd::optional<GnsPortTracker::BindCall> GnsPortTracker::ReadNextRequest()\n{\n    // Read the call information\n    auto request_value = m_request.try_get(c_bpf_poll_timeout);\n    if (!request_value.has_value())\n    {\n        return {};\n    }\n\n    auto callInfo = request_value.value();\n\n    // This logic needs to be defensive because the calling process is blocked until\n    // CompleteRequest() is called, so if the call information can't be processed because\n    // the caller has done something wrong (bad pointer, fd, or protocol), just let it go through\n    // and the kernel will fail it\n\n    try\n    {\n        return GetCallInfo(callInfo.id, callInfo.pid, callInfo.data.arch, callInfo.data.nr, gsl::make_span(callInfo.data.args));\n    }\n    catch (const std::exception& e)\n    {\n        GNS_LOG_ERROR(\"Failed to read bind() call info with ID {} for pid {}, {}\", callInfo.id, callInfo.pid, e.what());\n        return {{{}, {}, callInfo.id}};\n    }\n}\n\nstd::optional<GnsPortTracker::BindCall> GnsPortTracker::GetCallInfo(\n    uint64_t CallId, pid_t Pid, int Arch, int SysCallNumber, const gsl::span<unsigned long long>& Arguments)\n{\n    auto ParseSocket = [&](int Socket, size_t AddressPtr, size_t AddressLength) -> std::optional<BindCall> {\n        if (AddressLength < sizeof(sockaddr))\n        {\n            return {{{}, {}, CallId}}; // Invalid sockaddr. Let it go through.\n        }\n\n        auto networkNamespace = std::filesystem::read_symlink(std::format(\"/proc/{}/ns/net\", Pid)).string();\n        if (networkNamespace != m_networkNamespace)\n        {\n            GNS_LOG_INFO(\"Skipping bind() call for pid {} in network namespace {}\", Pid, networkNamespace.c_str());\n            return {{{}, {}, CallId}}; // Different network namespace. Let it go through.\n        }\n\n        auto processMemory = m_seccompDispatcher->ReadProcessMemory(CallId, Pid, AddressPtr, AddressLength);\n        if (!processMemory.has_value())\n        {\n            throw RuntimeErrorWithSourceLocation(\"Failed to read process memory\");\n        }\n\n        sockaddr& address = *reinterpret_cast<sockaddr*>(processMemory->data());\n\n        if ((address.sa_family != AF_INET && address.sa_family != AF_INET6) ||\n            (address.sa_family == AF_INET6 && AddressLength < sizeof(sockaddr_in6)))\n        {\n            return {{{}, {}, CallId}}; // This is a non IP call, or invalid sockaddr_in6. Let it go through\n        }\n\n        // Read the port.  The port *happens* to be in the same spot in memory for both sockaddr_in\n        // and sockaddr_in6. To avoid a second memory read, we take advantage of this fact to fetch\n        // the port from the currently read memory, regardless of the address family.\n        static_assert(sizeof(sockaddr_in) <= sizeof(sockaddr));\n\n        const auto* inAddr = reinterpret_cast<sockaddr_in*>(&address);\n        in_port_t port = ntohs(inAddr->sin_port);\n        if (port == 0)\n        {\n            // Port 0 means the kernel will assign an ephemeral port. We can't know\n            // the port until after the bind() completes, so duplicate the socket fd\n            // now (while the process is still stopped by seccomp) and defer the\n            // getsockname() lookup to after CompleteRequest() unblocks it.\n            try\n            {\n                const int protocol = GetSocketProtocol(Pid, Socket);\n                auto dupFd = DuplicateSocketFd(Pid, Socket);\n                if (!dupFd)\n                {\n                    return {{{}, {}, CallId}};\n                }\n                if (!m_seccompDispatcher->ValidateCookie(CallId))\n                {\n                    return {{{}, {}, CallId}};\n                }\n                return {{{}, DeferredPortLookup{Pid, std::move(dupFd), protocol}, CallId}};\n            }\n            catch (const std::exception&)\n            {\n                return {{{}, {}, CallId}}; // Can't determine protocol, just let it through\n            }\n        }\n\n        in6_addr storedAddress = {};\n\n        if (address.sa_family == AF_INET)\n        {\n            storedAddress.s6_addr32[0] = inAddr->sin_addr.s_addr;\n        }\n        else\n        {\n            const auto* inAddr6 = reinterpret_cast<sockaddr_in6*>(&address);\n            memcpy(storedAddress.s6_addr32, inAddr6->sin6_addr.s6_addr32, sizeof(storedAddress.s6_addr32));\n        }\n\n        // It's possible that the calling process lied and passed a sockaddr that\n        // doesn't match the underlying socket family or a bad fd. If that's the case,\n        // then GetSocketProtocol() will throw\n        const int protocol = GetSocketProtocol(Pid, Socket);\n\n        // As GetSocketProtocol interacts with /proc/<pid>, to avoid TOCTOU races we need to\n        // verify that call is still valid (call id is the same thing as cookie)\n        if (!m_seccompDispatcher->ValidateCookie(CallId))\n        {\n            throw RuntimeErrorWithSourceLocation(std::format(\"Invalid call id {}\", CallId));\n        }\n\n        return {{{PortAllocation(port, address.sa_family, protocol, storedAddress)}, {}, CallId}};\n    };\n#ifdef __x86_64__\n    if (Arch & __AUDIT_ARCH_64BIT)\n    {\n        return ParseSocket(Arguments[0], Arguments[1], Arguments[2]);\n    }\n    // Note: 32bit on x86_64 uses the __NR_socketcall with the first argument\n    // set to SYS_BIND to make bind system call and the second argument is\n    // a pointer to a block of memory containing the original arguments.\n    else\n    {\n        if (Arguments[0] != SYS_BIND)\n        {\n            return {{{}, {}, CallId}}; // Not a bind call, just let the call go through\n        }\n        // Grab the first 3 parameters\n        auto processMemory = m_seccompDispatcher->ReadProcessMemory(CallId, Pid, Arguments[1], sizeof(uint32_t) * 3);\n        if (!processMemory.has_value())\n        {\n            throw RuntimeErrorWithSourceLocation(\"Failed to read process memory\");\n        }\n\n        uint32_t* CopiedArguments = reinterpret_cast<uint32_t*>(processMemory->data());\n        return ParseSocket(CopiedArguments[0], CopiedArguments[1], CopiedArguments[2]);\n    }\n#else\n    return ParseSocket(Arguments[0], Arguments[1], Arguments[2]);\n#endif\n}\n\nvoid GnsPortTracker::CompleteRequest(uint64_t id, int result)\n{\n    m_reply.post(result);\n}\n\nint GnsPortTracker::GetSocketProtocol(int pid, int fd)\n{\n    const auto path = std::format(\"/proc/{}/fd/{}\", pid, fd);\n\n    // Because there's a race between the time where the buffer size is determined\n    // and the actual getxattr() call, retry until the buffer size is big enough\n    std::string protocol;\n    int result = -1;\n    do\n    {\n        int bufferSize = Syscall(getxattr, path.c_str(), \"system.sockprotoname\", nullptr, 0);\n        protocol.resize(std::max(0, bufferSize - 1));\n\n        result = getxattr(path.c_str(), \"system.sockprotoname\", protocol.data(), bufferSize);\n    } while (result < 0 && errno == ERANGE);\n\n    if (result < 0)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Failed to read protocol for socket: {}, {}\", path, errno));\n    }\n\n    // In case the size of the attribute shrunk between the two getxattr calls\n    protocol.resize(std::max(0, result - 1));\n\n    if (protocol == \"TCP\" || protocol == \"TCPv6\")\n    {\n        return IPPROTO_TCP;\n    }\n    else if (protocol == \"UDP\" || protocol == \"UDPv6\")\n    {\n        return IPPROTO_UDP;\n    }\n\n    throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected IP socket protocol: {}\", protocol));\n}\n\nwil::unique_fd GnsPortTracker::DuplicateSocketFd(pid_t Pid, int SocketFd)\n{\n    // Duplicate the socket fd from the target process into our address space.\n    // We cannot use open(\"/proc/pid/fd/N\") for sockets because the symlink target\n    // (socket:[inode]) is not a valid filesystem path. Use pidfd_getfd() instead.\n    wil::unique_fd pidFd(static_cast<int>(syscall(SYS_pidfd_open, Pid, 0u)));\n    if (!pidFd)\n    {\n        GNS_LOG_INFO(\"Port-0 bind: pidfd_open failed for pid {} (errno {})\", Pid, errno);\n        return {};\n    }\n\n    wil::unique_fd dupFd(static_cast<int>(syscall(SYS_pidfd_getfd, pidFd.get(), SocketFd, 0u)));\n    if (!dupFd)\n    {\n        GNS_LOG_INFO(\"Port-0 bind: pidfd_getfd failed for pid {} fd {} (errno {})\", Pid, SocketFd, errno);\n    }\n\n    return dupFd;\n}\n\nvoid GnsPortTracker::TrackPort(PortAllocation allocation)\ntry\n{\n    // Use insert_or_assign so the deallocation timeout is refreshed if the same\n    // port key is already present (emplace would silently keep the old entry).\n    m_allocatedPorts.insert_or_assign(std::move(allocation), std::make_optional(time(nullptr) + c_bind_timeout_seconds));\n}\ncatch (const std::exception& e)\n{\n    GNS_LOG_ERROR(\"Failed to track port allocation, {}\", e.what());\n}\n\nvoid GnsPortTracker::RunDeferredResolve()\n{\n    UtilSetThreadName(\"GnsPortZero\");\n\n    for (;;)\n    {\n        DeferredPortLookup lookup{0, {}, 0};\n        {\n            std::unique_lock lock(m_deferredMutex);\n            m_deferredCv.wait(lock, [&] { return !m_deferredQueue.empty(); });\n            lookup = std::move(m_deferredQueue.front());\n            m_deferredQueue.pop_front();\n        }\n\n        const auto pid = lookup.Pid;\n        try\n        {\n            ResolvePortZeroBind(std::move(lookup));\n        }\n        catch (const std::exception& e)\n        {\n            GNS_LOG_ERROR(\"Failed to resolve port-0 bind for pid {}, {}\", pid, e.what());\n        }\n    }\n}\n\nvoid GnsPortTracker::ResolvePortZeroBind(DeferredPortLookup lookup)\n{\n    // The socket fd was already duplicated (via pidfd_getfd) while the target process\n    // was stopped by seccomp, so it remains valid even if the process has closed or\n    // reused the original fd number.\n\n    // The bind() syscall is being completed asynchronously on the seccomp dispatcher\n    // thread after CompleteRequest() unblocks it. Poll getsockname() briefly until\n    // the kernel assigns a port.\n    constexpr int maxRetries = 25;\n    constexpr auto retryDelay = std::chrono::milliseconds(100);\n\n    in_port_t port = 0;\n    in6_addr address = {};\n    int resolvedFamily = 0;\n\n    for (int attempt = 0; attempt < maxRetries; ++attempt)\n    {\n        if (attempt > 0)\n        {\n            std::this_thread::sleep_for(retryDelay);\n        }\n\n        sockaddr_storage storage{};\n        socklen_t addrLen = sizeof(storage);\n        if (getsockname(lookup.DuplicatedSocketFd.get(), reinterpret_cast<sockaddr*>(&storage), &addrLen) != 0)\n        {\n            GNS_LOG_ERROR(\"Port-0 bind: getsockname failed for pid {} (errno {})\", lookup.Pid, errno);\n            return;\n        }\n\n        resolvedFamily = static_cast<int>(storage.ss_family);\n\n        if (storage.ss_family == AF_INET)\n        {\n            const auto* sin = reinterpret_cast<const sockaddr_in*>(&storage);\n            port = ntohs(sin->sin_port);\n            address.s6_addr32[0] = sin->sin_addr.s_addr;\n        }\n        else if (storage.ss_family == AF_INET6)\n        {\n            const auto* sin6 = reinterpret_cast<const sockaddr_in6*>(&storage);\n            port = ntohs(sin6->sin6_port);\n            memcpy(address.s6_addr32, sin6->sin6_addr.s6_addr32, sizeof(address.s6_addr32));\n        }\n        else\n        {\n            GNS_LOG_ERROR(\"Port-0 bind: unexpected address family ({}) for pid {}\", resolvedFamily, lookup.Pid);\n            return;\n        }\n\n        if (port != 0)\n        {\n            break;\n        }\n    }\n\n    if (port == 0)\n    {\n        GNS_LOG_ERROR(\"Port-0 bind: kernel did not assign a port for pid {} after retries\", lookup.Pid);\n        return;\n    }\n\n    PortAllocation allocation(port, resolvedFamily, lookup.Protocol, address);\n    GNS_LOG_INFO(\n        \"Port-0 bind resolved: family ({}) port ({}) protocol ({}) for pid {}\", resolvedFamily, port, lookup.Protocol, lookup.Pid);\n    {\n        std::lock_guard lock(m_resolvedMutex);\n        m_resolvedQueue.push_back(std::move(allocation));\n    }\n}\n\nstd::ostream& operator<<(std::ostream& out, const GnsPortTracker::PortAllocation& entry)\n{\n    return out << \"Port=\" << entry.Port << \", Family=\" << entry.Family << \", Protocol=\" << entry.Protocol;\n}\n"
  },
  {
    "path": "src/linux/init/GnsPortTracker.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n#include <deque>\r\n#include <map>\r\n#include <mutex>\r\n#include <set>\r\n#include <utility>\r\n#include <optional>\r\n#include <NetlinkChannel.h>\r\n#include <future>\r\n#include <functional>\r\n#include <memory>\r\n#include <time.h>\r\n#include \"util.h\"\r\n#include <linux/seccomp.h>\r\n#include \"waitablevalue.h\"\r\n#include \"SecCompDispatcher.h\"\r\n#include \"SocketChannel.h\"\r\n\r\nclass GnsPortTracker\r\n{\r\npublic:\r\n    GnsPortTracker(std::shared_ptr<wsl::shared::SocketChannel> hvSocketChannel, NetlinkChannel&& netlinkChannel, std::shared_ptr<SecCompDispatcher> seccompDispatcher);\r\n\r\n    GnsPortTracker(const GnsPortTracker&) = delete;\r\n    GnsPortTracker(GnsPortTracker&&) = delete;\r\n    GnsPortTracker& operator=(const GnsPortTracker&) = delete;\r\n    GnsPortTracker& operator=(GnsPortTracker&&) = delete;\r\n\r\n    void Run();\r\n\r\n    int ProcessSecCompNotification(seccomp_notif* notification);\r\n\r\n    struct PortAllocation\r\n    {\r\n        in6_addr Address = {};\r\n        std::uint16_t Port = {};\r\n        int Family = {};\r\n        int Protocol = {};\r\n\r\n        PortAllocation(PortAllocation&&) = default;\r\n        PortAllocation(const PortAllocation&) = default;\r\n\r\n        PortAllocation& operator=(PortAllocation&&) = default;\r\n        PortAllocation& operator=(const PortAllocation&) = default;\r\n\r\n        PortAllocation(std::uint16_t Port, int Family, int Protocol, in6_addr& Address) :\r\n            Port(Port), Family(Family), Protocol(Protocol)\r\n        {\r\n            memcpy(this->Address.s6_addr32, Address.s6_addr32, sizeof(this->Address.s6_addr32));\r\n        }\r\n\r\n        bool operator<(const PortAllocation& other) const\r\n        {\r\n            if (Port < other.Port)\r\n            {\r\n                return true;\r\n            }\r\n            else if (Port > other.Port)\r\n            {\r\n                return false;\r\n            }\r\n\r\n            if (Family < other.Family)\r\n            {\r\n                return true;\r\n            }\r\n            else if (Family > other.Family)\r\n            {\r\n                return false;\r\n            }\r\n\r\n            if (Protocol < other.Protocol)\r\n            {\r\n                return true;\r\n            }\r\n            else if (Protocol > other.Protocol)\r\n            {\r\n                return false;\r\n            }\r\n\r\n            static_assert(sizeof(Address.s6_addr32) == 16);\r\n            if (int res = memcmp(Address.s6_addr32, other.Address.s6_addr32, sizeof(Address.s6_addr32)); res < 0)\r\n            {\r\n                return true;\r\n            }\r\n            else if (res > 0)\r\n            {\r\n                return false;\r\n            }\r\n\r\n            return false;\r\n        }\r\n    };\r\n\r\n    struct DeferredPortLookup\r\n    {\r\n        pid_t Pid;\r\n        wil::unique_fd DuplicatedSocketFd; // Duplicated via pidfd_getfd while process was stopped\r\n        int Protocol;\r\n\r\n        DeferredPortLookup(pid_t Pid, wil::unique_fd DuplicatedSocketFd, int Protocol) :\r\n            Pid(Pid), DuplicatedSocketFd(std::move(DuplicatedSocketFd)), Protocol(Protocol)\r\n        {\r\n        }\r\n\r\n        DeferredPortLookup(DeferredPortLookup&&) = default;\r\n        DeferredPortLookup& operator=(DeferredPortLookup&&) = default;\r\n        DeferredPortLookup(const DeferredPortLookup&) = delete;\r\n        DeferredPortLookup& operator=(const DeferredPortLookup&) = delete;\r\n    };\r\n\r\n    struct BindCall\r\n    {\r\n        std::optional<PortAllocation> Request;\r\n        std::optional<DeferredPortLookup> PortZeroBind;\r\n        std::uint64_t CallId;\r\n    };\r\n\r\n    struct PortRefreshResult\r\n    {\r\n        std::set<PortAllocation> Ports;\r\n        time_t Timestamp;\r\n        std::function<void()> Resume;\r\n    };\r\n\r\nprivate:\r\n    void OnRefreshAllocatedPorts(const std::set<PortAllocation>& Ports, time_t Timestamp);\r\n\r\n    void RunPortRefresh();\r\n\r\n    std::set<PortAllocation> ListAllocatedPorts();\r\n\r\n    std::optional<BindCall> ReadNextRequest();\r\n\r\n    std::optional<BindCall> GetCallInfo(uint64_t CallId, pid_t Pid, int Arch, int SysCallNumber, const gsl::span<unsigned long long>& Arguments);\r\n\r\n    int RequestPort(const PortAllocation& Port, bool Allocate);\r\n\r\n    int HandleRequest(const PortAllocation& Request);\r\n\r\n    void CompleteRequest(uint64_t Id, int Result);\r\n\r\n    static int GetSocketProtocol(int Pid, int Fd);\r\n\r\n    static wil::unique_fd DuplicateSocketFd(pid_t Pid, int SocketFd);\r\n\r\n    void ResolvePortZeroBind(DeferredPortLookup lookup);\r\n\r\n    void RunDeferredResolve();\r\n\r\n    void TrackPort(PortAllocation allocation);\r\n\r\n    std::map<PortAllocation, std::optional<time_t>> m_allocatedPorts;\r\n    std::shared_ptr<wsl::shared::SocketChannel> m_hvSocketChannel;\r\n    NetlinkChannel m_channel;\r\n    std::promise<PortRefreshResult> m_allocatedPortsRefresh;\r\n\r\n    WaitableValue<seccomp_notif> m_request;\r\n    WaitableValue<int> m_reply;\r\n\r\n    std::shared_ptr<SecCompDispatcher> m_seccompDispatcher;\r\n\r\n    std::string m_networkNamespace;\r\n\r\n    std::mutex m_deferredMutex;\r\n    std::condition_variable m_deferredCv;\r\n    std::deque<DeferredPortLookup> m_deferredQueue;\r\n\r\n    // Resolved port-0 allocations posted by the background RunDeferredResolve thread\r\n    // for the main Run() loop to process (keeps SocketChannel access single-threaded).\r\n    std::mutex m_resolvedMutex;\r\n    std::deque<PortAllocation> m_resolvedQueue;\r\n};\r\n\r\nstd::ostream& operator<<(std::ostream& out, const GnsPortTracker::PortAllocation& portAllocation);\r\n"
  },
  {
    "path": "src/linux/init/Localization.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Localization.cpp\n\nAbstract:\n\n    This file contains the Linux localization logic.\n\n--*/\n\n#include \"Localization.h\"\n\nstd::string FormatLanguage(const char* Input)\n{\n    // Expected format: en_US.UTF-8\n\n    std::string output(Input);\n    auto dot = output.find(\".\");\n\n    if (dot != std::string::npos && dot != 0)\n    {\n        output = output.substr(0, dot);\n    }\n\n    std::replace(output.begin(), output.end(), '_', '-');\n\n    return output;\n}\n\nstd::optional<std::string> GetUserLanguage()\n{\n    for (const auto* e : {\"LANGUAGE\", \"LANG\", \"LC_ALL\"})\n    {\n        if (const auto* lang = getenv(e))\n        {\n            return lang;\n        }\n    }\n\n    return {};\n}\n\nconst char* wsl::shared::Localization::LookupString(const std::vector<std::pair<std::string, const char*>>& strings, Options options)\n{\n    try\n    {\n        static const auto language = GetUserLanguage();\n        if (language.has_value())\n        {\n            for (const auto& e : strings)\n            {\n                if (wsl::shared::string::IsEqual(e.first, language.value(), true))\n                {\n                    return e.second;\n                }\n            }\n        }\n    }\n    catch (...)\n    {\n        LOG_CAUGHT_EXCEPTION();\n    }\n\n    // Default to English is string is not found (English is always the first entry)\n    return strings[0].second;\n}\n"
  },
  {
    "path": "src/linux/init/NetworkManager.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include <iostream>\n#include <filesystem>\n#include <lxwil.h>\n#include \"lxinitshared.h\"\n#include \"common.h\"\n#include \"NetworkManager.h\"\n#include \"util.h\"\n#include \"address.h\"\n#include \"conncheckshared.h\"\n#include \"RuntimeErrorWithSourceLocation.h\"\n#include \"stringshared.h\"\n\n// Custom table used for storing routes for loopback IPs.\nconstexpr int c_loopbackRoutingTableId = 127;\n// Custom table used for storing routes for local IPs. A separate table is used for local IPs so that\n// when the IP addresses are changing, all routes from the table can be deleted before adding routes for the new set of IPs.\n// The routes for the loopback IPs will always be the same, thus keeping them in a separate table\nconstexpr int c_localRoutingTableId = 128;\n\n// See comments in AddMirroredLoopbackRoutingRules for explanation about those priorities.\nconstexpr int c_WindowsToLinuxRulePriority = 0;\nconstexpr int c_LinuxToWindowsRulePriority = 1;\nconstexpr int c_LocalRulePriority = 2;\n\n// Creates routing tables per interface, identified by the interface index plus an offset.\nconstexpr int c_routeTableOffsetFromIndex = 1000;\n\n// All loopback/local packets will be sent out of the guest via those gateways. There\n// will be static ARP entries matching those gateways to the MAC address below, such that\n// every packet will have that as destination MAC.\n//\n// Note: Eventually a mechanism will be needed to replace the gateway addresses in case they\n// conflict with other addresses in the network.\nconst Address c_ipv4LoopbackGateway = {AF_INET, Ipv4MaxPrefixLen, LX_INIT_IPV4_LOOPBACK_GATEWAY_ADDRESS};\nconst Address c_ipv6LoopbackGateway = {AF_INET6, Ipv6MaxPrefixLen, LX_INIT_IPV6_LOOPBACK_GATEWAY_ADDRESS};\n\n// v4 and v6 loopback address range used in Mirrored mode: 127.0.0.1/32 and ::1/128.\n//\n// Note: Although the v4 loopback address range is 127.0.0.0/8, only traffic to 127.0.0.1 can be used to communicate host<->guest\n// in Mirrored mode. Traffic to other v4 loopback addresses will stay in the guest. This can be changed if other loopback\n// addresses are needed by host<->guest loopback scenarios.\nconst Address c_loopbackV4AddressRange = {AF_INET, Ipv4MaxPrefixLen, \"127.0.0.1\"};\nconst Address c_loopbackV6AddressRange = {AF_INET6, Ipv6MaxPrefixLen, \"::1\"};\n\n// 00:11:22:33:44:55 represents the MAC address that all loopback/local packets will have as destination\n// MAC when they are sent out of the guest. This will help Windows to identify which\n// packets are loopback/local.\nconst MacAddress c_gatewayMacAddress = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};\n\nconstexpr const char* c_acceptLocalSetting = \"accept_local\";\nconstexpr const char* c_routeLocalnetSetting = \"route_localnet\";\nconstexpr char c_disableSetting[] = \"0\\n\";\nconstexpr char c_enableSetting[] = \"1\\n\";\n\nNetworkManager::NetworkManager(RoutingTable& routingTable) :\n    routingTable(routingTable), loopbackRoutingTable(c_loopbackRoutingTableId), localRoutingTable(c_localRoutingTableId)\n{\n}\n\nstd::optional<int> NetworkManager::FindRoutingTableIdForInterface(const Interface& interface) const\n{\n    return c_routeTableOffsetFromIndex + interface.Index();\n}\n\nvoid NetworkManager::ChangePrimaryRoutingTable(int newTableId)\n{\n    routingTable.ChangeTableId(newTableId);\n}\n\nstd::vector<Route> NetworkManager::ListRoutes(int family) const\n{\n    return routingTable.ListRoutes(family);\n}\n\nInterface NetworkManager::CreateVirtualWifiAdapter(Interface& baseAdapter, const std::string& wifiName)\n{\n    GNS_LOG_INFO(\"Creating virtual wifi adapter with name {}\", wifiName.c_str());\n    baseAdapter.CreateVirtualWifiAdapter(wifiName);\n    auto virtualWifi = Interface::Open(wifiName);\n\n    GNS_LOG_INFO(\"Enabling Ipv4 loopback routing on virtual wifi adapter with name {}\", wifiName.c_str());\n    EnableLoopbackRouting(virtualWifi);\n    return virtualWifi;\n}\n\nInterface NetworkManager::CreateProxyWifiAdapter(Interface& baseAdapter, const std::string& wifiName)\n{\n    baseAdapter.CreateProxyWifiAdapter(wifiName);\n    auto proxyWifi = Interface::Open(wifiName);\n\n    GNS_LOG_INFO(\"Enabling Ipv4 loopback routing on proxy wifi adapter with name {}\", wifiName.c_str());\n    EnableLoopbackRouting(proxyWifi);\n    return proxyWifi;\n}\n\n// SUPPORTS IPV4 ONLY, and only supports 1 IP address per adapter.\n// Not used for mirroring.\nvoid NetworkManager::SetAdapterConfiguration(Interface& interface, const wsl::shared::hns::HNSEndpoint& configuration)\n{\n    InterfaceConfiguration config;\n    config.Addresses.emplace_back(Address{AF_INET, configuration.PrefixLength, wsl::shared::string::WideToMultiByte(configuration.IPAddress)});\n\n    config.LocalAddresses = config.Addresses;\n    config.BroadcastAddress = utils::ComputeBroadcastAddress(config.Addresses[0]);\n\n    std::stringstream addressInfo;\n    addressInfo << config.Addresses[0];\n    GNS_LOG_INFO(\n        \"Setting the IPv4 address on endpointID ({}) to {} on interfaceName {}\",\n        wsl::shared::string::GuidToString<char>(configuration.ID).c_str(),\n        addressInfo.str().c_str(),\n        interface.Name().c_str());\n    interface.SetIpv4Configuration(config);\n}\n\nvoid NetworkManager::SetInterfaceState(Interface& adapter, InterfaceState state)\n{\n    GNS_LOG_INFO(\"Setting interface state to {} on interfaceName {}\", state == InterfaceState::Up ? \"Up\" : \"Down\", adapter.Name().c_str());\n    if (state == InterfaceState::Up)\n    {\n        adapter.SetUp();\n    }\n    else\n    {\n        adapter.SetDown();\n    }\n}\n\nvoid NetworkManager::SetAdapterName(Interface& adapter, const std::string& name)\n{\n    adapter.SetName(name);\n}\n\nvoid NetworkManager::SetAdapterNamespace(Interface& adapter, int namespaceFd)\n{\n    adapter.SetNamespace(namespaceFd);\n}\n\nvoid NetworkManager::SetWiphyNamespace(Interface& adapter, int namespaceFd)\n{\n    adapter.SetWiphyNamespace(namespaceFd);\n}\n\nvoid NetworkManager::ModifyRoute(const Route& route, Operation operation)\n{\n    routingTable.ModifyRoute(route, operation);\n}\n\nvoid NetworkManager::ResetRoutingTable(int addressFamily, const Interface& interface)\n{\n    auto routes = routingTable.ListRoutes(addressFamily);\n    auto pred = [&](const Route& route) {\n        // Routes without gateways are link level routes (scope link)\n        return !route.via.has_value() || route.dev != interface.Index();\n    };\n\n    std::erase_if(routes, pred);\n\n    for (const auto& e : routes)\n    {\n        const auto routeString = utils::Stringify(e);\n        try\n        {\n            GNS_LOG_INFO(\"Removing route {} from interfaceName {}\", routeString, interface.Name());\n            routingTable.ModifyRoute(e, Operation::Remove);\n        }\n        catch (const std::exception& ex)\n        {\n            throw RuntimeErrorWithSourceLocation(std::format(\"Failed to remove route '{}', {}\", routeString, ex.what()));\n        }\n    }\n}\n\nvoid NetworkManager::ModifyAddress(Interface& adapter, const Address& address, Operation operation)\n{\n    std::vector<Route> routes;\n\n    // If the ip address is changing, the routing table needs to be saved & restored\n    // because netlink doesn't allow ip addresses to be changed, but only deleted and added,\n    // which causes the routing rules attached to the interface to be dropped\n    if (operation == Operation::Update)\n    {\n        routes = routingTable.ListRoutes(address.Family());\n    }\n\n    adapter.ModifyIpAddress(address, operation);\n\n    // Restore the routes for this interface.\n    // Note: If a route fails to be restored, it's probably because the new address's subnet is different,\n    // and so the route would have been unusable with the new address anyway\n    for (const auto& savedRoute : routes)\n    {\n        if (savedRoute.dev == adapter.Index() && savedRoute.via.has_value())\n        {\n            const auto savedRouteString = utils::Stringify(savedRoute);\n\n            try\n            {\n                GNS_LOG_INFO(\n                    \"Restoring route {} after address change, on interfaceName {}\", savedRouteString.c_str(), adapter.Name().c_str());\n                routingTable.ModifyRoute(savedRoute, Operation::Create);\n            }\n            catch (const std::exception& ex)\n            {\n                GNS_LOG_ERROR(\n                    \"Failed to restore route {} after address change, on interfaceName {}, caught exception \"\n                    \"{}\",\n                    savedRouteString.c_str(),\n                    adapter.Name().c_str(),\n                    ex.what());\n            }\n        }\n    }\n}\n\nvoid NetworkManager::SetAdapterMacAddress(Interface& interface, const MacAddress& address)\n{\n    SetInterfaceState(interface, InterfaceState::Down);\n    interface.SetMacAddress(address);\n    SetInterfaceState(interface, InterfaceState::Up);\n}\n\nvoid NetworkManager::DisassociateAdapterFromBond(const std::string& bondInterfaceName, Interface& interface)\n{\n    auto bondInterface = Interface::Open(bondInterfaceName);\n    GNS_LOG_INFO(\n        \"Trying to disassociate from bond - bondDeviceName {}, interfaceName {}\", bondInterfaceName.c_str(), interface.Name().c_str());\n    bondInterface.RemoveFromBond(interface);\n    GNS_LOG_INFO(\n        \"Successfully disassociated from bond - bondDeviceName {}, interfaceName {}\", bondInterfaceName.c_str(), interface.Name().c_str());\n}\n\nvoid NetworkManager::AssociateAdapterWithBond(const std::string& bondInterfaceName, Interface& interface)\n{\n    auto bondInterface = Interface::Open(bondInterfaceName);\n    // must set the interface down before associating it to bond\n    SetInterfaceState(interface, InterfaceState::Down);\n    bondInterface.AddToBond(interface);\n    GNS_LOG_INFO(\n        \"Successfully associated to bond - bondDeviceName {}, interfaceName {}\", bondInterfaceName.c_str(), interface.Name().c_str());\n    SetInterfaceState(interface, InterfaceState::Up);\n}\n\nvoid NetworkManager::ActivateAdapterWithBond(const std::string& bondInterfaceName, const Interface& interface)\n{\n    auto bondInterface = Interface::Open(bondInterfaceName);\n    bondInterface.SetActiveChild(interface);\n}\n\nInterface NetworkManager::CreateBondAdapter(const std::string& name)\n{\n    Interface::CreateBondAdapter(name);\n    auto bondInterface = Interface::Open(name);\n\n    // Enable routing of IPv4 loopback on the bond interface.\n    GNS_LOG_INFO(\"Enabling IPv4 loopback routing on bond adapter with name {}\", name.c_str());\n    EnableLoopbackRouting(bondInterface);\n    return bondInterface;\n}\n\n/*\n    Enable the accept_local and route_localnet settings required to send/receive loopback and local\n    packets on an interface.\n\n    Note: The function supports only IPv4 settings at the moment. There are no equivalent IPv6 settings\n    for accept_local and route_localnet.\n*/\nvoid NetworkManager::EnableLoopbackRouting(Interface& interface)\n{\n    GNS_LOG_INFO(\"Enabling sysctl accept_local setting on adapter with name {}\", interface.Name().c_str());\n    interface.EnableNetworkSetting(c_acceptLocalSetting, AF_INET);\n\n    GNS_LOG_INFO(\"Enabling sysctl route_localnet setting on adapter with name {}\", interface.Name().c_str());\n    interface.EnableNetworkSetting(c_routeLocalnetSetting, AF_INET);\n}\n\n/*\n    Note: GELNIC stands for Guest-Exclusive Loopback NIC. It represents the mirrored interface of the host\n    loopback interface. Every packet that arrives in the guest having a loopback destination address will\n    arrive on the GELNIC.\n*/\nvoid NetworkManager::InitializeLoopbackConfiguration(Interface& gelnic)\n{\n    // Enable routing of IPv4 loopback on the GELNIC.\n    GNS_LOG_INFO(\"Enabling IPv4 loopback routing on GELNIC adapter {}\", gelnic.Name().c_str());\n    EnableLoopbackRouting(gelnic);\n\n    // Disable IPv4 reverse path filtering on the GELNIC.\n    // The effective rp_filter setting for interface \"name\" is the more restrictive between \"name\" and \"all\", so both must be set.\n    GNS_LOG_INFO(\"Disabling sysctl rp_filter setting on loopback adapter\");\n    gelnic.DisableNetworkSetting(\"rp_filter\", AF_INET);\n    ModifyNetSetting(AF_INET, \"rp_filter\", \"all\", c_disableSetting, strlen(c_disableSetting));\n\n    InitializeLoopbackConfigurationImpl(gelnic, AF_INET);\n    // InitializeLoopbackConfigurationImpl(gelnic, AF_INET6);\n}\n\n/*\n    In mirrored networking mode, Linux ip rules (policy based routing) are configured such that\n    loopback traffic or local traffic (local = traffic with destination an IP assigned to Linux/Windows)\n    can flow between Windows and Linux. Note: This applies only to TCP and UDP traffic.\n\n    Below are outputs of \"ip rule show\" in both NAT and mirrored mode, along with explanations about how\n    the rules work.\n\n    The leftmost part of each rule represents the priority of the rule (priority 0 is the highest priority)\n\n    The \"lookup\" keyword is followed by the name or id of a routing table\n    The local table is used by Linux to know when to deliver a packet locally (to a Linux process). This table is used\n    for both traffic with destination 127.0.0.1 and traffic with destination an IP assigned to Linux.\n    Table 127 contains routes used to send traffic with destination 127.0.0.1 out of Linux, to be processed by Windows.\n    Traffic will be sent out of Linux via the \"GELNIC\" interface (which will be called loopback0).\n    Table 128 contains routes used to send traffic with local destination out of Linux, to be processed by Windows.\n    Traffic will be sent out of Linux via the mirrored interface that has the destination IP assigned to it.\n    Tables main and default contain routes that are not related to loopback traffic\n\n    Priority 0 rules will deliver to Linux loopback/local traffic coming from Windows (this can be traffic\n    with origin in Windows or traffic with origin in Linux that was sent to Windows and Windows sent it back to Linux).\n    \"iif\" represents input interface (the interface on which Linux received the traffic).\n    \"loopback0\" is the interface used to send 127.0.0.1 traffic between Linux and Windows. \"eth0\" refers to an\n    interface that was mirrored in Linux. Each time an interface is mirrored, a rule like this needs to be added\n    for that interface. And each time the interface is deleted, the rule needs to be deleted.\n\n    Priority 1 rules are used to send traffic that originates in Linux out of Linux to Windows, so that Windows can decide\n    if that traffic must be sent to Windows or back to Linux. Those rules apply to traffic originating in the Linux\n    root network namespace, but also to traffic that root network namespace receives from other network namespaces, such\n    as Docker containers.\n\n    The priority 2 rule is needed for loopback/local traffic that is not TCP or UDP, such as ICMP. This traffic will\n    stay inside Linux, it cannot be sent between Linux and Windows. The rule is also needed for receiving inbound traffic\n    from an external machine.\n\n    NAT mode ip rule show:\n    0:      from all lookup local\n    32766:  from all lookup main\n    32767:  from all lookup default\n\n    mirrored mode ip rule show:\n    0:      from all iif loopback0 ipproto tcp lookup local\n    0:      from all iif loopback0 ipproto udp lookup local\n    0:      from all iif eth0 ipproto tcp lookup local\n    0:      from all iif eth0 ipproto udp lookup local\n    1:      from all ipproto tcp lookup 127\n    1:      from all ipproto udp lookup 127\n    1:      from all ipproto tcp lookup 128\n    1:      from all ipproto udp lookup 128\n    2:      from all lookup local\n    32766:  from all lookup main\n    32767:  from all lookup default\n*/\nvoid NetworkManager::AddMirroredLoopbackRoutingRules(Interface& gelnic, int addressFamily)\n{\n    GNS_LOG_INFO(\"gelnic name {}, addressFamily {}\", gelnic.Name().c_str(), addressFamily);\n\n    // Delete rule with priority 0 for local table (from all prio 0 lookup local).\n    Rule rule = Rule(addressFamily, RT_TABLE_LOCAL, 0);\n    ruleManager.ModifyRoutingTablePriority(rule, Operation::Remove);\n\n    // Adding priority 0 rules for the GELNIC interface\n    // Similar priority 0 rules will also be added or deleted when an interface is mirrored in Linux, or deleted\n    UpdateMirroredLoopbackRulesForInterface(gelnic.Name(), Operation::Create);\n\n    auto AddPriority1Rule = [&](const Protocol protocol, const int routingTableId) {\n        Rule rule = Rule(addressFamily, routingTableId, c_LinuxToWindowsRulePriority, protocol);\n        ruleManager.ModifyRoutingTablePriorityWithProtocol(rule, Operation::Create);\n    };\n\n    // Adding priority 1 rules\n    AddPriority1Rule(Protocol::Tcp, c_loopbackRoutingTableId);\n    AddPriority1Rule(Protocol::Udp, c_loopbackRoutingTableId);\n    AddPriority1Rule(Protocol::Tcp, c_localRoutingTableId);\n    AddPriority1Rule(Protocol::Udp, c_localRoutingTableId);\n\n    // Add a rule referencing the local table, with priority 2\n    rule = Rule(addressFamily, RT_TABLE_LOCAL, c_LocalRulePriority);\n    ruleManager.ModifyRoutingTablePriority(rule, Operation::Create);\n}\n\nvoid NetworkManager::UpdateMirroredLoopbackRulesForInterface(const std::string& interfaceName, Operation operation)\n{\n    assert(operation == Operation::Create || operation == Operation::Remove);\n\n    // Add or remove priority 0 rules needed by mirrored loopback traffic. See the comments in AddMirroredLoopbackRoutingRules for\n    // more details. Currently only IPv4 guest<->host loopback is supported in mirrored mode - adding only IPv4 rules.\n    GNS_LOG_INFO(\n        \"{} priority 0 rule for interfaceName {} for TCP\", operation == Operation::Create ? \"Add\" : \"Remove\", interfaceName.c_str());\n    Rule rule = Rule(AF_INET, RT_TABLE_LOCAL, c_WindowsToLinuxRulePriority, Protocol::Tcp);\n    rule.iif = interfaceName;\n    ruleManager.ModifyLoopbackRule(rule, operation);\n\n    GNS_LOG_INFO(\n        \"{} priority 0 rule for interfaceName {} for UDP\", operation == Operation::Create ? \"Add\" : \"Remove\", interfaceName.c_str());\n    rule = Rule(AF_INET, RT_TABLE_LOCAL, c_WindowsToLinuxRulePriority, Protocol::Udp);\n    rule.iif = interfaceName;\n    ruleManager.ModifyLoopbackRule(rule, operation);\n}\n\n/*\n    Adds the policy rules required for loopback. Also adds routes for the loopback address range\n    127.0.0.1/32 or ::1/128.\n*/\nvoid NetworkManager::InitializeLoopbackConfigurationImpl(Interface& gelnic, int addressFamily)\n{\n    // Set to GELNIC to status up before adding the configurations\n    gelnic.SetUp();\n\n    AddMirroredLoopbackRoutingRules(gelnic, addressFamily);\n\n    auto gateway = addressFamily == AF_INET ? c_ipv4LoopbackGateway : c_ipv6LoopbackGateway;\n    auto addressRange = addressFamily == AF_INET ? c_loopbackV4AddressRange : c_loopbackV6AddressRange;\n\n    // Add a static ARP entry for the loopback gateway. The purpose of the static entries is\n    // to guarantee that each loopback packet that leaves the guest has the same destination MAC.\n    GNS_LOG_INFO(\"Adding static ARP entry for the loopback gateway  {}\", gateway.Addr().c_str());\n    Neighbor neighbor = Neighbor(gateway, c_gatewayMacAddress, gelnic.Index());\n    neighborManager.ModifyNeighborEntry(neighbor, Operation::Create);\n\n    // Add routes for 127.0.0.1/32 or ::1/128\n    Route route = Route(addressFamily, gateway, gelnic.Index(), false, addressRange, 0);\n    route.isLoopbackRoute = true;\n\n    const auto routeString = utils::Stringify(route);\n    GNS_LOG_INFO(\"Add route {} on GELNIC adapter {}\", routeString.c_str(), gelnic.Name().c_str());\n    loopbackRoutingTable.ModifyRoute(route, Operation::Create);\n}\n\n/*\n    Add or remove loopback routes for the set of IP addresses that were added/deleted on an interface. All routes are via the\n    same gateway address. The function can be used for both IPv4 and IPv6 addresses.\n*/\nvoid NetworkManager::UpdateLoopbackRoute(Interface& interface, const Address& address, Operation operation)\n{\n    assert(operation == Operation::Create || operation == Operation::Remove);\n\n    // For the moment don't process IPv6 addresses, since inbound IPv6 loopback is not supported yet (dropped by\n    // default by the Linux stack). Once that is addressed, this check will be removed.\n    if (address.Family() == AF_INET6)\n    {\n        GNS_LOG_INFO(\"Ignoring IPv6 address {}\", utils::Stringify(address).c_str());\n        return;\n    }\n\n    auto gateway = address.Family() == AF_INET ? c_ipv4LoopbackGateway : c_ipv6LoopbackGateway;\n\n    if (operation == Operation::Create)\n    {\n        // When adding routes, always add the static neighbor entry for the loopback gateway. The purpose of the static entries is\n        // to guarantee that each loopback packet that leaves the guest has the same destination MAC.\n        //\n        // Note: The entries are added each time we add routes in order to avoid keeping track of whether they are added or not\n        // (as the entries will be lost when an interface changes state to down).\n        GNS_LOG_INFO(\"Adding static neighbor entry for the loopback gateway {}\", gateway.Addr().c_str());\n        Neighbor neighbor = Neighbor(gateway, c_gatewayMacAddress, interface.Index());\n        neighborManager.ModifyNeighborEntry(neighbor, Operation::Create);\n    }\n\n    Route route = Route(address.Family(), gateway, interface.Index(), false, address, 0);\n    route.isLoopbackRoute = true;\n\n    const auto routeString = utils::Stringify(route);\n    GNS_LOG_INFO(\n        \"{} loopback route {} on interfaceName {}\",\n        operation == Operation::Create ? \"Add\" : \"Remove\",\n        routeString.c_str(),\n        interface.Name().c_str());\n    localRoutingTable.ModifyRoute(route, operation);\n}\n\nvoid NetworkManager::ResetLoopbackRoutes()\n{\n    localRoutingTable.RemoveAll(AF_UNSPEC);\n}\n\nvoid NetworkManager::CreateTunAdapter(const std::string& name)\n{\n    Interface::CreateTunAdapter(name);\n\n    // Enable routing of IPv4 loopback on the tunnel interface.\n    GNS_LOG_INFO(\"Enabling IPv4 loopback routing on tunnel adapter with name {}\", name.c_str());\n    Interface tunInterface = {-1, name};\n    EnableLoopbackRouting(tunInterface);\n}\n\nvoid NetworkManager::ModifyNetSetting(int addressFamily, const char* settingName, const char* scope, const char* settingValue, size_t settingValueLen)\n{\n    const std::filesystem::path settingFilePath =\n        std::format(\"/proc/sys/net/{}/conf/{}/{}\", ((addressFamily == AF_INET) ? \"ipv4\" : \"ipv6\"), scope, settingName);\n    wil::unique_fd fd(Syscall(open, settingFilePath.c_str(), (O_WRONLY | O_CLOEXEC)));\n    Syscall(write, fd.get(), settingValue, settingValueLen);\n}\n\nvoid NetworkManager::DisableRouterDiscovery()\n{\n    ModifyNetSetting(AF_INET6, \"accept_ra\", \"all\", c_disableSetting, strlen(c_disableSetting));\n    ModifyNetSetting(AF_INET6, \"accept_ra\", \"default\", c_disableSetting, strlen(c_disableSetting));\n}\n\nvoid NetworkManager::DisableDAD()\n{\n    // DAD is not enabled for IPv4 by default on Linux-based systems, so only disable for IPv6.\n    ModifyNetSetting(AF_INET6, \"dad_transmits\", \"all\", c_disableSetting, strlen(c_disableSetting));\n    ModifyNetSetting(AF_INET6, \"dad_transmits\", \"default\", c_disableSetting, strlen(c_disableSetting));\n}\n\nvoid NetworkManager::DisableIpv6AddressGeneration()\n{\n    // Disable autoconfiguration.\n    ModifyNetSetting(AF_INET6, \"autoconf\", \"all\", c_disableSetting, strlen(c_disableSetting));\n    ModifyNetSetting(AF_INET6, \"autoconf\", \"default\", c_disableSetting, strlen(c_disableSetting));\n\n    // Disable link local address generation.\n    constexpr char c_genModeNone[] = \"1\\n\";\n    ModifyNetSetting(AF_INET6, \"addr_gen_mode\", \"all\", c_genModeNone, strlen(c_genModeNone));\n    ModifyNetSetting(AF_INET6, \"addr_gen_mode\", \"default\", c_genModeNone, strlen(c_genModeNone));\n\n    // Disable privacy extensions, i.e. temporary address generation.\n    ModifyNetSetting(AF_INET6, \"use_tempaddr\", \"all\", c_disableSetting, strlen(c_disableSetting));\n    ModifyNetSetting(AF_INET6, \"use_tempaddr\", \"default\", c_disableSetting, strlen(c_disableSetting));\n}\n\nvoid NetworkManager::EnableIpv4ArpFilter()\n{\n    // sets /proc/sys/net/ipv4/conf/all/arp_filter to a value of 1\n    // this is to stop Linux from attempting to ARP a configured IP address across all connected interfaces\n    // setting this to 1 instructs Linux to only ARP for that address over the interface that the address was assigned\n    // This setting is required to avoid breaking mirroring where multiple interfaces are mirrored on the same network (they have\n    // addresses on the same prefix) which can cause the Host to interpret an ARP from an interface without the address to be a\n    // duplicate which causes the host fail DAD, and DHCP immediately requests a new address (this will just continue in a loop)\n    ModifyNetSetting(AF_INET, \"arp_filter\", \"all\", c_enableSetting, strlen(c_enableSetting));\n    ModifyNetSetting(AF_INET, \"arp_filter\", \"default\", c_enableSetting, strlen(c_enableSetting));\n}\n\nwsl::shared::conncheck::ConnCheckResult NetworkManager::SendConnectRequest(const char* remoteAddress)\n{\n    return wsl::shared::conncheck::CheckConnection(remoteAddress, nullptr, \"80\");\n}"
  },
  {
    "path": "src/linux/init/NetworkManager.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n#include <Interface.h>\r\n#include <RoutingTable.h>\r\n#include <IpRuleManager.h>\r\n#include <IpNeighborManager.h>\r\n#include <conncheckshared.h>\r\n#include \"hns_schema.h\"\r\n\r\nclass NetworkManager\r\n{\r\npublic:\r\n    NetworkManager(RoutingTable& routingTable);\r\n\r\n    Interface CreateVirtualWifiAdapter(Interface& baseAdapter, const std::string& wifiName);\r\n\r\n    Interface CreateProxyWifiAdapter(Interface& baseAdapter, const std::string& wifiName);\r\n\r\n    void SetAdapterConfiguration(Interface& adapter, const wsl::shared::hns::HNSEndpoint& configuration);\r\n\r\n    enum class InterfaceState\r\n    {\r\n        Up,\r\n        Down\r\n    };\r\n\r\n    void SetInterfaceState(Interface& adapter, InterfaceState state);\r\n\r\n    void SetAdapterName(Interface& adapter, const std::string& name);\r\n\r\n    void SetAdapterNamespace(Interface& adapter, int namespaceFd);\r\n\r\n    void SetWiphyNamespace(Interface& adapter, int namespaceFd);\r\n\r\n    std::optional<int> FindRoutingTableIdForInterface(const Interface& interface) const;\r\n\r\n    void ChangePrimaryRoutingTable(int newTableId);\r\n\r\n    std::vector<Route> ListRoutes(int family) const;\r\n\r\n    void ModifyRoute(const Route& route, Operation operation);\r\n\r\n    void ModifyAddress(Interface& adapter, const Address& address, Operation operation);\r\n\r\n    void ResetRoutingTable(int addressFamily, const Interface& interface);\r\n\r\n    void SetAdapterMacAddress(Interface& interface, const MacAddress& address);\r\n\r\n    void DisassociateAdapterFromBond(const std::string& bondInterfaceName, Interface& interface);\r\n\r\n    void AssociateAdapterWithBond(const std::string& bondInterfaceName, Interface& interface);\r\n\r\n    void ActivateAdapterWithBond(const std::string& bondInterfaceName, const Interface& interface);\r\n\r\n    Interface CreateBondAdapter(const std::string& name);\r\n\r\n    void EnableLoopbackRouting(Interface& interface);\r\n\r\n    void InitializeLoopbackConfiguration(Interface& gelnic);\r\n\r\n    void AddMirroredLoopbackRoutingRules(Interface& gelnic, int addressFamily);\r\n\r\n    void UpdateMirroredLoopbackRulesForInterface(const std::string& interfaceName, Operation operation);\r\n\r\n    void UpdateLoopbackRoute(Interface& interface, const Address& address, Operation operation);\r\n\r\n    void ResetLoopbackRoutes();\r\n\r\n    void CreateTunAdapter(const std::string& name);\r\n\r\n    void DisableRouterDiscovery();\r\n\r\n    void DisableDAD();\r\n\r\n    void DisableIpv6AddressGeneration();\r\n\r\n    void EnableIpv4ArpFilter();\r\n\r\n    wsl::shared::conncheck::ConnCheckResult SendConnectRequest(const char* remoteAddress);\r\n\r\nprivate:\r\n    void InitializeLoopbackConfigurationImpl(Interface& gelnic, int addressFamily);\r\n\r\n    RoutingTable& routingTable;\r\n    // Custom routing tables used for loopback mirroring. Not to be confused with the Linux \"local\" table\r\n    RoutingTable loopbackRoutingTable;\r\n    RoutingTable localRoutingTable;\r\n\r\n    IpRuleManager ruleManager;\r\n    IpNeighborManager neighborManager;\r\n\r\n    void ModifyNetSetting(int addressFamily, const char* settingName, const char* scope, const char* settingValue, size_t settingValueLen);\r\n};\r\n"
  },
  {
    "path": "src/linux/init/SecCompDispatcher.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <linux/unistd.h>\n\n#include \"SecCompDispatcher.h\"\n#include \"common.h\"\n#include \"Syscall.h\"\n#include \"SyscallError.h\"\n\nstatic int seccomp(unsigned int operation, unsigned int flags, void* args, const std::source_location& source = std::source_location::current())\n{\n    int result = syscall(__NR_seccomp, operation, flags, args);\n    if (result < 0)\n    {\n        auto error = errno;\n        std::stringstream argString;\n        detail::PrettyPrintArguments(argString, operation, flags, args);\n        throw SyscallError(\"seccomp\", argString.str(), error, source);\n    }\n\n    return result;\n}\n\nSecCompDispatcher::SecCompDispatcher(int m_NotifyFd) : m_notifyFd(m_NotifyFd)\n{\n    seccomp(SECCOMP_GET_NOTIF_SIZES, 0, &m_notificationSizes);\n\n    m_worker = std::thread([this]() { Run(); });\n}\n\nSecCompDispatcher::~SecCompDispatcher()\n{\n    m_shutdown.reset();\n    m_worker.join();\n}\n\n/**\n * @brief Poll for notifications from seccomp and dispatch them a handler.\n *\n */\nvoid SecCompDispatcher::Run()\n{\n    UtilSetThreadName(\"SecCompDispatcher\");\n\n    // Create a pipe to signal the thread to stop.\n    int pipes[2];\n    Syscall(pipe2, pipes, 0);\n    m_shutdown = pipes[1];\n    wil::unique_fd terminate = pipes[0];\n    auto wait_for_fd = [&terminate](int fd, int event) -> bool {\n        struct pollfd poll_fds[2];\n        poll_fds[0] = {.fd = fd, .events = event, .revents = 0};\n        poll_fds[1] = {.fd = terminate.get(), .events = POLLIN, .revents = 0};\n        for (;;)\n        {\n            int return_value = SyscallInterruptable(poll, poll_fds, ARRAY_SIZE(poll_fds), -1);\n            if (return_value < 0)\n            {\n                continue;\n            }\n            else if (return_value == 0)\n            {\n                continue;\n            }\n            else if (poll_fds[1].revents)\n            {\n                return false;\n            }\n            else if (poll_fds[0].revents & event)\n            {\n                return true;\n            }\n        }\n    };\n    std::vector<uint8_t> notification_buffer(m_notificationSizes.seccomp_notif);\n    std::vector<std::uint8_t> response_buffer(m_notificationSizes.seccomp_notif_resp);\n    assert(m_notificationSizes.seccomp_notif_resp >= sizeof(seccomp_notif_resp));\n    for (;;)\n    {\n        if (!wait_for_fd(m_notifyFd.get(), POLLIN))\n        {\n            break;\n        }\n\n        // Clear the buffers to make the 5.15 kernel happy.\n        notification_buffer.clear();\n        notification_buffer.resize(m_notificationSizes.seccomp_notif);\n\n        auto* callInfo = reinterpret_cast<seccomp_notif*>(notification_buffer.data());\n        try\n        {\n            Syscall(ioctl, m_notifyFd.get(), SECCOMP_IOCTL_NOTIF_RECV, callInfo);\n        }\n        catch (const SyscallError& e)\n        {\n            if (e.GetErrno() == ENOENT)\n            {\n                // The target thread was killed by a signal as the notification information was being generated,\n                // or the target's (blocked) system call was interrupted by a signal handler.\n                GNS_LOG_INFO(\"SECCOMP_IOCTL_NOTIF_RECV failed with ENOENT\");\n                continue;\n            }\n\n            throw;\n        }\n        int result = 0;\n        GNS_LOG_INFO(\n            \"Notified for arch {:X} syscall {} with id {}lu for pid {} with args ({}lX, {}lX, {}lX, {}lX, {}lX, \"\n            \"{}lX)\",\n            callInfo->data.arch,\n            callInfo->data.nr,\n            callInfo->id,\n            callInfo->pid,\n            callInfo->data.args[0],\n            callInfo->data.args[1],\n            callInfo->data.args[2],\n            callInfo->data.args[3],\n            callInfo->data.args[4],\n            callInfo->data.args[5]);\n\n        auto handler = m_handlers.find(callInfo->data.nr);\n\n        try\n        {\n            if (handler != m_handlers.end())\n            {\n                result = handler->second(callInfo);\n            }\n        }\n        catch (std::exception& e)\n        {\n            GNS_LOG_ERROR(\"Dispatch of call failed, {}\", e.what());\n        }\n\n        response_buffer.clear();\n        response_buffer.resize(m_notificationSizes.seccomp_notif_resp);\n\n        auto* resultInfo = reinterpret_cast<seccomp_notif_resp*>(response_buffer.data());\n        resultInfo->id = callInfo->id;\n        resultInfo->error = -result;\n        resultInfo->val = 0;\n        resultInfo->flags = result == 0 ? SECCOMP_USER_NOTIF_FLAG_CONTINUE : 0;\n\n        GNS_LOG_INFO(\"Responding to notification with id {}lu for pid {}, result {}\", callInfo->id, callInfo->pid, result);\n        try\n        {\n            Syscall(ioctl, m_notifyFd.get(), SECCOMP_IOCTL_NOTIF_SEND, resultInfo);\n        }\n        catch (std::exception& e)\n        {\n            GNS_LOG_ERROR(\"Failed to respond to notification with id {}lu for pid {}, {}\", callInfo->id, callInfo->pid, e.what());\n        }\n    }\n}\n\nbool SecCompDispatcher::ValidateCookie(uint64_t id) noexcept\n{\n    try\n    {\n        // If the cookie is not valid, the ioctl will return < 0 and the call below will throw\n        Syscall(ioctl, m_notifyFd.get(), SECCOMP_IOCTL_NOTIF_ID_VALID, &id);\n        return true;\n    }\n    catch (std::exception& e)\n    {\n        return false;\n    }\n}\n\nvoid SecCompDispatcher::RegisterHandler(int SysCallNr, const std::function<int(seccomp_notif*)>& Handler)\n{\n    m_handlers[SysCallNr] = Handler;\n}\n\nvoid SecCompDispatcher::UnregisterHandler(int SysCallNr)\n{\n    m_handlers.erase(SysCallNr);\n}\n\nstd::optional<std::vector<gsl::byte>> SecCompDispatcher::ReadProcessMemory(uint64_t Cookie, pid_t Pid, size_t Address, size_t Length) noexcept\n{\n    try\n    {\n        std::vector<gsl::byte> targetMemory(Length);\n        const std::string path = std::format(\"/proc/{}/mem\", Pid);\n        wil::unique_fd mem(Syscall(open, path.c_str(), O_RDWR));\n\n        // PID reuse can cause a TOCTOU race here, so validate that the notification is still\n        // valid to make sure that the above fd points to the right process\n        if (!ValidateCookie(Cookie))\n        {\n            throw RuntimeErrorWithSourceLocation(std::format(\"Invalid cookie {}\", Cookie));\n        }\n\n        Syscall(lseek64, mem.get(), Address, SEEK_SET);\n        if (Syscall(read, mem.get(), targetMemory.data(), targetMemory.size()) != targetMemory.size())\n        {\n            throw RuntimeErrorWithSourceLocation(std::format(\"Couldn't read the whole call address with error {}\", errno));\n        }\n\n        // Based on https://man7.org/linux/man-pages/man2/seccomp_unotify.2.html (example in function getTargetPathname),\n        // it's possible that right before reading the process memory, the intercepted system call was interrupted by a signal and\n        // the memory we read may no longer be associated with that system call\n        //\n        // We need to validate the cookie again to make sure the seccomp notification is still valid after we read the process memory\n        if (!ValidateCookie(Cookie))\n        {\n            throw RuntimeErrorWithSourceLocation(std::format(\"Invalid cookie {}\", Cookie));\n        }\n\n        return targetMemory;\n    }\n    catch (std::exception& e)\n    {\n        GNS_LOG_ERROR(\"Failed to read process memory for pid {}, cookie {}u, {}\", Pid, Cookie, e.what());\n        return std::nullopt;\n    }\n}\n"
  },
  {
    "path": "src/linux/init/SecCompDispatcher.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include <functional>\n#include <map>\n#include <functional>\n#include <stdio.h>\n#include <asm/types.h>\n#include <errno.h>\n#include \"common.h\"\n#include \"util.h\"\n#include <linux/seccomp.h>\n\nclass SecCompDispatcher\n{\npublic:\n    SecCompDispatcher(int SecCompFd);\n    ~SecCompDispatcher();\n\n    SecCompDispatcher(const SecCompDispatcher&) = delete;\n    SecCompDispatcher(SecCompDispatcher&&) = delete;\n    SecCompDispatcher& operator=(const SecCompDispatcher&) = delete;\n    SecCompDispatcher& operator=(SecCompDispatcher&&) = delete;\n\n    void RegisterHandler(int SysCallNr, const std::function<int(seccomp_notif*)>& Handler);\n    void UnregisterHandler(int SysCallNr);\n\n    bool ValidateCookie(uint64_t id) noexcept;\n\n    std::optional<std::vector<gsl::byte>> ReadProcessMemory(uint64_t cookie, pid_t pid, size_t address, size_t length) noexcept;\n\nprivate:\n    void Run();\n\n    seccomp_notif_sizes m_notificationSizes;\n    std::map<int, std::function<int(seccomp_notif*)>> m_handlers;\n    wil::unique_fd m_notifyFd;\n    wil::unique_fd m_shutdown;\n    std::thread m_worker;\n};\n"
  },
  {
    "path": "src/linux/init/WslDistributionConfig.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslDistributionConfig.h\n\nAbstract:\n\n    This file contains the WslDistributionConfig class implementation.\n\n--*/\n\n#include \"WslDistributionConfig.h\"\n#include \"configfile.h\"\n#include \"util.h\"\n\nusing wsl::linux::WslDistributionConfig;\nusing namespace wsl::linux;\n\nWslDistributionConfig::WslDistributionConfig(const char* configFilePath)\n{\n\n    std::vector<ConfigKey> keys = {\n        ConfigKey(c_ConfigAutoMountOption, AutoMount),\n        ConfigKey(c_ConfigAutoMountRoot, DrvFsPrefix),\n        ConfigKey(\"automount.options\", DrvFsOptions),\n        ConfigKey(c_ConfigMountFsTabOption, MountFsTab),\n        ConfigKey(c_ConfigLinkOsLibsOption, LinkOsLibs),\n        ConfigKey(\"automount.cgroups\", {{\"v1\", CGroupVersion::v1}, {\"v2\", CGroupVersion::v2}}, CGroup, nullptr),\n\n        ConfigKey(\"filesystem.umask\", Umask),\n\n        ConfigKey(c_ConfigInteropAppendWindowsPathOption, InteropAppendWindowsPath),\n        ConfigKey(c_ConfigInteropEnabledOption, InteropEnabled),\n\n        ConfigKey(c_ConfigGenerateHostsOption, GenerateHosts),\n        ConfigKey(c_ConfigGenerateResolvConfOption, GenerateResolvConf),\n        ConfigKey(\"network.hostname\", HostName),\n\n        ConfigKey(c_ConfigAutoUpdateTimezoneOption, AutoUpdateTimezone),\n\n        ConfigKey(c_ConfigPlan9EnabledOption, Plan9Enabled),\n        ConfigKey(\"fileServer.logFile\", Plan9LogFile),\n        ConfigKey(\"fileServer.logLevel\", Plan9LogLevel),\n        ConfigKey(\"fileServer.logTruncate\", Plan9LogTruncate),\n\n        ConfigKey(c_ConfigGpuEnabledOption, GpuEnabled),\n        ConfigKey(c_ConfigAppendGpuLibPathOption, AppendGpuLibPath),\n\n        ConfigKey(\"user.default\", DefaultUser),\n\n        ConfigKey(c_ConfigBootCommandOption, BootCommand),\n        ConfigKey(c_ConfigBootSystemdOption, BootInit),\n        ConfigKey(\"boot.initTimeout\", BootInitTimeout),\n        ConfigKey(c_ConfigBootProtectBinfmtOption, BootProtectBinfmt),\n\n        ConfigKey(c_ConfigEnableGuiAppsOption, GuiAppsEnabled),\n    };\n\n    //\n    // If the config file does not exist, then ParseConfigFile sets all the configuration\n    // values to their defaults.\n    //\n\n    wil::unique_file File{fopen(configFilePath, \"r\")};\n    ParseConfigFile(keys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));\n\n    //\n    // Ensure the DrvFs prefix is well-formed (not empty and ends with a path separator).\n    //\n\n    std::string Prefix;\n    if (!DrvFsPrefix.empty())\n    {\n        Prefix = DrvFsPrefix;\n    }\n\n    if (Prefix.empty() || Prefix.back() != '/')\n    {\n        Prefix += '/';\n        DrvFsPrefix = Prefix;\n    }\n\n    //\n    // Using boot.systemd is only supported on WSL2.\n    //\n\n    BootInit &= UtilIsUtilityVm();\n    if (BootInit && (access(INIT_PATH, X_OK) < 0))\n    {\n        LOG_WARNING(\"access({}) failed {} - {} disabled\", INIT_PATH, errno, c_ConfigBootSystemdOption);\n        BootInit = false;\n    }\n\n    //\n    // Apply safe mode overrides.\n    //\n\n    const char* Value = getenv(LX_WSL2_SAFE_MODE);\n    if (Value && (strcmp(Value, \"1\") == 0))\n    {\n        auto DisableBoolOption = [&](const char* Option, bool& ConfigValue) {\n            if (ConfigValue)\n            {\n                LOG_WARNING(\"{} - {} disabled\", WSL_SAFE_MODE_WARNING, Option);\n                ConfigValue = false;\n            }\n        };\n\n        bool BootCommandEnabled = (BootCommand.has_value());\n        for (const auto& ConfigOption : std::vector<std::pair<const char*, bool&>>{\n                 {c_ConfigAutoMountOption, AutoMount},\n                 {c_ConfigLinkOsLibsOption, LinkOsLibs},\n                 {c_ConfigMountFsTabOption, MountFsTab},\n                 {c_ConfigBootCommandOption, BootCommandEnabled},\n                 {c_ConfigBootSystemdOption, BootInit},\n                 {c_ConfigGenerateHostsOption, GenerateHosts},\n                 {c_ConfigGenerateResolvConfOption, GenerateResolvConf},\n                 {c_ConfigPlan9EnabledOption, Plan9Enabled},\n                 {c_ConfigAppendGpuLibPathOption, AppendGpuLibPath},\n                 {c_ConfigGpuEnabledOption, GpuEnabled},\n                 {c_ConfigInteropAppendWindowsPathOption, InteropAppendWindowsPath},\n                 {c_ConfigInteropEnabledOption, InteropEnabled},\n                 {c_ConfigAutoUpdateTimezoneOption, AutoUpdateTimezone}})\n        {\n            DisableBoolOption(ConfigOption.first, ConfigOption.second);\n        }\n\n        BootCommand = {};\n    }\n}"
  },
  {
    "path": "src/linux/init/WslDistributionConfig.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslDistributionConfig.h\n\nAbstract:\n\n    This file contains the WslDistributionConfig class definition.\n\n--*/\n\n#pragma once\n#include <optional>\n#include \"p9tracelogging.h\"\n#include \"lxinitshared.h\"\n#include \"common.h\"\n#include \"SocketChannel.h\"\n\nnamespace wsl::linux {\n\nconstexpr auto c_ConfigAutoMountOption = \"automount.enabled\";\nconstexpr auto c_ConfigAutoUpdateTimezoneOption = \"time.useWindowsTimezone\";\nconstexpr auto c_ConfigBootCommandOption = \"boot.command\";\nconstexpr auto c_ConfigBootProtectBinfmtOption = \"boot.protectBinfmt\";\nconstexpr auto c_ConfigBootSystemdOption = \"boot.systemd\";\nconstexpr auto c_ConfigInteropAppendWindowsPathOption = \"interop.appendWindowsPath\";\nconstexpr auto c_ConfigInteropEnabledOption = \"interop.enabled\";\nconstexpr auto c_ConfigMountFsTabOption = \"automount.mountFsTab\";\nconstexpr auto c_ConfigGenerateHostsOption = \"network.generateHosts\";\nconstexpr auto c_ConfigGenerateResolvConfOption = \"network.generateResolvConf\";\nconstexpr auto c_ConfigEnableGuiAppsOption = \"general.guiApplications\";\nconstexpr auto c_ConfigPlan9EnabledOption = \"fileServer.enabled\";\nconstexpr auto c_ConfigAppendGpuLibPathOption = \"gpu.appendLibPath\";\nconstexpr auto c_ConfigGpuEnabledOption = \"gpu.enabled\";\nconstexpr auto c_ConfigLinkOsLibsOption = \"automount.ldconfig\";\nconstexpr auto c_ConfigAutoMountRoot = \"automount.root\";\n\nstruct WslDistributionConfig\n{\n    WslDistributionConfig(const char* configFilePath);\n\n    WslDistributionConfig(const WslDistributionConfig&) = delete;\n    WslDistributionConfig& operator=(const WslDistributionConfig&) = delete;\n\n    WslDistributionConfig(WslDistributionConfig&&) = default;\n    WslDistributionConfig& operator=(WslDistributionConfig&&) = default;\n\n    enum class CGroupVersion\n    {\n        v1 = 0,\n        v2 = 1\n    };\n\n    bool AutoMount = true;\n    bool AutoUpdateTimezone = true;\n    std::optional<std::string> BootCommand;\n    bool BootInit = false;\n    int BootInitTimeout = 10 * 1000;\n    bool BootProtectBinfmt = true;\n    std::optional<std::string> DefaultUser;\n    std::string DrvFsPrefix = \"/mnt\";\n    std::optional<std::string> DrvFsOptions;\n    bool InteropAppendWindowsPath = true;\n    bool InteropEnabled = true;\n    bool MountFsTab = true;\n    bool GenerateHosts = true;\n    bool GenerateResolvConf = true;\n    std::optional<std::string> HostName;\n    bool Plan9Enabled = true;\n    std::optional<std::string> Plan9LogFile;\n    int Plan9LogLevel = TRACE_LEVEL_INFORMATION;\n    bool Plan9LogTruncate = true;\n    int Umask = 0022;\n    bool AppendGpuLibPath = true;\n    bool GpuEnabled = true;\n    bool LinkOsLibs = true;\n    CGroupVersion CGroup = CGroupVersion::v2;\n\n    //\n    // Values not set by /etc/wsl.conf.\n    //\n\n    bool GuiAppsEnabled = false;\n    std::optional<int> FeatureFlags;\n    std::optional<LX_MINI_INIT_NETWORKING_MODE> NetworkingMode;\n    std::optional<std::string> VmId;\n\n    //\n    // Global state for boot state. The socket is used to delay-start the distro init process\n    // to when the first session leader is created.\n    //\n\n    wil::unique_fd BootStartWriteSocket;\n    wsl::shared::SocketChannel Plan9ControlChannel;\n    std::optional<pid_t> InitPid;\n};\n\n} // namespace wsl::linux"
  },
  {
    "path": "src/linux/init/binfmt.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    binfmt.c\n\nAbstract:\n\n    This file contains definitions for the NT interop binfmt interpreter.\n\n--*/\n\n#include <lxbusapi.h>\n#include <sys/signalfd.h>\n#include <pty.h>\n#include <locale.h>\n#include <signal.h>\n#include \"common.h\"\n#include \"binfmt.h\"\n#include \"wslpath.h\"\n#include <libgen.h>\n#include \"util.h\"\n#include \"SocketChannel.h\"\n\n#define ACCEPT_TIMEOUT (10 * 1000)\n\n#define LOG_STDERR(_str, ...) \\\n    { \\\n        fprintf(stderr, _str \": %s\\n\", ##__VA_ARGS__, (g_Locale ? strerror_l(errno, g_Locale) : strerror(errno))); \\\n    }\n\nint g_ConsoleFd = -1;\nstruct termios g_ConsoleInfoBackup;\nlocale_t g_Locale;\n\nvoid CreateNtProcessConfigureConsole(PLX_INIT_CREATE_NT_PROCESS_COMMON Common);\n\nstd::vector<gsl::byte> CreateNtProcessMessage(LX_MESSAGE_TYPE MessageType, int Argc, char* Argv[]);\n\nint CreateNtProcessUtilityVm(int Argc, char* Argv[]);\n\nint CreateNtProcessWsl(int Argc, char* Argv[]);\n\nbool HasOpenFileDescriptors(struct pollfd* PollDescriptors, int PollDescriptorSize);\n\nvoid RestoreConsoleState(void);\n\nvoid WindowSizeChanged(int SignalChannelFd);\n\nint CreateNtProcess(int Argc, char* Argv[])\n\n/*++\n\nRoutine Description:\n\n    This routine issues a create NT process request.\n\nArguments:\n\n    Argc - Supplies the argument count.\n\n    Argv - Supplies the command line arguments.\n\nReturn Value:\n\n    The exit code of the launched process on success, 1 on failure.\n\n--*/\n\n{\n    //\n    // The first argument will be the path of the binfmt interpreter, the second\n    // argument will be the full filename of the Windows binary.\n    //\n    // N.B. The binfmt interpreter is registered with the 'P' flag which preserves\n    //      Argv[0] by adding it to the command line after the path of the Windows binary.\n    //      https://en.wikipedia.org/wiki/Binfmt_misc\n    //\n\n    int ExitCode = 1;\n    if (Argc <= 1)\n    {\n        return ExitCode;\n    }\n\n    //\n    // Initialize a locale for localized error messages.\n    //\n    // N.B. Failure to initialize the locale is non-fatal.\n    //\n\n    g_Locale = newlocale(LC_ALL_MASK, \"\", NULL);\n\n    //\n    // Check if the binary is being run on WSL or in a Utility VM.\n    //\n\n    if (!UtilIsUtilityVm())\n    {\n        ExitCode = CreateNtProcessWsl(Argc, Argv);\n    }\n    else\n    {\n        ExitCode = CreateNtProcessUtilityVm(Argc, Argv);\n    }\n\n    RestoreConsoleState();\n    return ExitCode;\n}\n\nint CreateNtProcessUtilityVm(int Argc, char* Argv[])\n\n/*++\n\nRoutine Description:\n\n    This routine issues a create NT process request for VM Mode.\n\nArguments:\n\n    Argc - Supplies the argument count.\n\n    Argv - Supplies the command line arguments.\n\nReturn Value:\n\n    The exit code of the launched process on success, 1 on failure.\n\n--*/\ntry\n{\n    int ExitCode = 1;\n\n    //\n    // Create the interop message.\n    //\n\n    auto Buffer = CreateNtProcessMessage(LxInitMessageCreateProcessUtilityVm, Argc, Argv);\n    if (Buffer.empty())\n    {\n        return ExitCode;\n    }\n\n    //\n    // Create a listening socket to accept connections for stdin, stdout,\n    // stderr, and the control channel.\n    //\n\n    sockaddr_vm SocketAddress;\n    wil::unique_fd Sockets[LX_INIT_CREATE_NT_PROCESS_SOCKETS];\n    wil::unique_fd ListenSocket{UtilListenVsockAnyPort(&SocketAddress, COUNT_OF(Sockets))};\n    if (!ListenSocket)\n    {\n        return ExitCode;\n    }\n\n    auto Span = gsl::make_span(Buffer);\n    auto* Message = gslhelpers::get_struct<LX_INIT_CREATE_NT_PROCESS_UTILITY_VM>(Span);\n    Message->Port = SocketAddress.svm_port;\n\n    //\n    // Establish a connection to the interop server.\n    //\n\n    wsl::shared::SocketChannel channel{UtilConnectToInteropServer(), \"Interop\"};\n    if (channel.Socket() < 0)\n    {\n        return ExitCode;\n    }\n\n    //\n    // Send the create process message to the interop server.\n    //\n\n    channel.SendMessage<LX_INIT_CREATE_NT_PROCESS_UTILITY_VM>(Span);\n\n    //\n    // Accept connections from the interop server.\n    //\n\n    for (int Index = 0; Index < COUNT_OF(Sockets); Index += 1)\n    {\n        Sockets[Index] = UtilAcceptVsock(ListenSocket.get(), SocketAddress, ACCEPT_TIMEOUT);\n        if (!Sockets[Index])\n        {\n            return ExitCode;\n        }\n    }\n\n    //\n    // Close the listening socket.\n    //\n\n    ListenSocket.reset();\n\n    //\n    // Create a signalfd to detect window size changes.\n    //\n\n    sigset_t SignalMask;\n    sigemptyset(&SignalMask);\n    sigaddset(&SignalMask, SIGWINCH);\n    sigaddset(&SignalMask, SIGINT);\n    int Result = sigprocmask(SIG_BLOCK, &SignalMask, NULL);\n    if (Result < 0)\n    {\n        LOG_STDERR(\"sigprocmask failed %d\", errno);\n        return ExitCode;\n    }\n\n    wil::unique_fd SignalFd{signalfd(-1, &SignalMask, 0)};\n    if (!SignalFd)\n    {\n        LOG_STDERR(\"signalfd failed %d\", errno);\n        return ExitCode;\n    }\n\n    //\n    // Fill output and poll file descriptors.\n    //\n\n    int OutputFd[] = {Sockets[0].get(), 1, 2};\n    pollfd PollDescriptors[] = {\n        {0, POLLIN}, {Sockets[1].get(), POLLIN}, {Sockets[2].get(), POLLIN}, {Sockets[3].get(), POLLIN}, {SignalFd.get(), POLLIN}};\n\n    //\n    // Begin relaying from stdin to the stdin socket, and from the stdout and\n    // stderr sockets to stdout and stderr.\n    //\n\n    while (HasOpenFileDescriptors(PollDescriptors, COUNT_OF(PollDescriptors)))\n    {\n        Result = poll(PollDescriptors, COUNT_OF(PollDescriptors), -1);\n        if (Result <= 0)\n        {\n            break;\n        }\n\n        for (int Index = 0; Index < COUNT_OF(OutputFd); Index += 1)\n        {\n            if (PollDescriptors[Index].revents & (POLLIN | POLLHUP | POLLERR))\n            {\n                auto BytesRead = UtilReadBuffer(PollDescriptors[Index].fd, Buffer);\n                if (BytesRead == 0)\n                {\n                    PollDescriptors[Index].fd = -1;\n                    if (Index == 0)\n                    {\n                        if (shutdown(OutputFd[0], SHUT_WR) < 0)\n                        {\n                            LOG_STDERR(\"shutdown failed %d\", errno);\n                        }\n                    }\n                }\n                else if (BytesRead < 0)\n                {\n                    LOG_STDERR(\"read failed %d\", errno);\n                    PollDescriptors[Index].fd = -1;\n                }\n                else\n                {\n                    auto BytesWritten = UtilWriteBuffer(OutputFd[Index], Buffer.data(), BytesRead);\n                    if (BytesWritten < 0)\n                    {\n                        LOG_STDERR(\"write failed %d\", errno);\n                    }\n                }\n            }\n        }\n\n        //\n        // Read the create process response or exit status message from the\n        // control channel.\n        //\n\n        if (PollDescriptors[3].revents & POLLIN)\n        {\n            auto PollMessage = wsl::shared::socket::RecvMessage(PollDescriptors[3].fd, Buffer);\n            if (PollMessage.empty())\n            {\n                PollDescriptors[3].fd = -1;\n                continue;\n            }\n\n            auto* Header = gslhelpers::get_struct<MESSAGE_HEADER>(PollMessage);\n            if (Header->MessageType == LxInitMessageCreateProcessResponse)\n            {\n                //\n                // Verify the process launch request was successful.\n                //\n\n                auto* Response = gslhelpers::try_get_struct<LX_INIT_CREATE_PROCESS_RESPONSE>(PollMessage);\n                if (!Response)\n                {\n                    LOG_STDERR(\"Invalid message size %zd\", PollMessage.size());\n                    break;\n                }\n\n                if (Response->Result != 0)\n                {\n                    errno = Response->Result;\n                    LOG_STDERR(\"%s\", Argv[0]);\n                    break;\n                }\n\n                //\n                // If the application was a GUI application and stdin is a console, restore\n                // the terminal mode. This allows ctrl-c and ctrl-z to function for\n                // graphical apps.\n                //\n\n                if ((Response->Flags & LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION) != 0)\n                {\n                    RestoreConsoleState();\n                }\n            }\n            else if (Header->MessageType == LxInitMessageExitStatus)\n            {\n                //\n                // Set the process exit code and go through the relay loop until\n                // all data has been flushed.\n                //\n\n                auto* ExitStatus = gslhelpers::try_get_struct<LX_INIT_PROCESS_EXIT_STATUS>(PollMessage);\n                if (!ExitStatus)\n                {\n                    LOG_STDERR(\"Invalid message size %zd\", PollMessage.size());\n                    break;\n                }\n\n                ExitCode = ExitStatus->ExitCode;\n                PollDescriptors[3].fd = -1;\n            }\n            else\n            {\n                LOG_STDERR(\"Unexpected message %d\", Header->MessageType);\n                break;\n            }\n        }\n\n        //\n        // Forward window resize events via the relay pipe and handle sigint.\n        //\n\n        if (PollDescriptors[4].revents & POLLIN)\n        {\n            signalfd_siginfo SignalInfo;\n            auto BytesRead = TEMP_FAILURE_RETRY(read(PollDescriptors[4].fd, &SignalInfo, sizeof(SignalInfo)));\n            if (BytesRead != sizeof(SignalInfo))\n            {\n                LOG_STDERR(\"read failed %zd %d\", BytesRead, errno);\n                break;\n            }\n\n            if (SignalInfo.ssi_signo == SIGWINCH)\n            {\n                WindowSizeChanged(Sockets[3].get());\n            }\n            else if (SignalInfo.ssi_signo == SIGINT)\n            {\n                if (shutdown(OutputFd[0], SHUT_WR) < 0)\n                {\n                    LOG_STDERR(\"shutdown failed %d\", errno);\n                }\n\n                break;\n            }\n            else\n            {\n                LOG_STDERR(\"Unexpected signal %u\", SignalInfo.ssi_signo);\n                break;\n            }\n        }\n\n        //\n        // Control channel is closed. This means that the windows process has\n        // exited. Close stdin channel to unblock the relay thread and disable\n        // polling on the stdin and signalfd channels. However, there might be\n        // some unread data on the stdout and stderr channels so continue\n        // polling/reading on them until EOF is received.\n        //\n\n        if (PollDescriptors[3].fd == -1)\n        {\n            if (shutdown(OutputFd[0], SHUT_WR) < 0)\n            {\n                LOG_STDERR(\"shutdown failed %d\", errno);\n            }\n\n            PollDescriptors[0].fd = -1;\n            PollDescriptors[4].fd = -1;\n        }\n    }\n\n    return ExitCode;\n}\nCATCH_RETURN_ERRNO()\n\nint CreateNtProcessWsl(int Argc, char* Argv[])\n\n/*++\n\nRoutine Description:\n\n    This routine issues a create NT process request for lxcore-based instances.\n\nArguments:\n\n    Argc - Supplies the argument count.\n\n    Argv - Supplies the command line arguments.\n\nReturn Value:\n\n    The exit code of the launched process on success, 1 on failure.\n\n--*/\n\n{\n    int ExitCode = 1;\n\n    //\n    // Connect to the Windows server that handles create process requests.\n    //\n\n    wil::unique_fd LxBusFd{TEMP_FAILURE_RETRY(open(LXBUS_DEVICE_NAME, O_RDWR))};\n    if (!LxBusFd)\n    {\n        return ExitCode;\n    }\n\n    LXBUS_CONNECT_SERVER_PARAMETERS ConnectParams{};\n    ConnectParams.Input.Flags = LXBUS_IPC_CONNECT_FLAG_UNNAMED_SERVER;\n    ConnectParams.Input.TimeoutMs = LXBUS_IPC_INFINITE_TIMEOUT;\n    int Result = TEMP_FAILURE_RETRY(ioctl(LxBusFd.get(), LXBUS_IOCTL_CONNECT_SERVER, &ConnectParams));\n    if (Result < 0)\n    {\n        return ExitCode;\n    }\n\n    wil::unique_fd CreateProcessFd{ConnectParams.Output.MessagePort};\n    std::vector<gsl::byte> Buffer = CreateNtProcessMessage(LxInitMessageCreateProcess, Argc, Argv);\n    if (Buffer.empty())\n    {\n        return ExitCode;\n    }\n\n    //\n    // Marshal the standard handles.\n    //\n\n    auto Span = gsl::make_span(Buffer);\n    auto* Message = gslhelpers::get_struct<LX_INIT_CREATE_NT_PROCESS>(Span);\n    for (int Index = 0; Index < LX_INIT_STD_FD_COUNT; ++Index)\n    {\n        LXBUS_IPC_MESSAGE_MARSHAL_VFS_FILE_PARAMETERS MarshalFile{};\n        MarshalFile.Input.Fd = Index;\n        Result = TEMP_FAILURE_RETRY(ioctl(CreateProcessFd.get(), LXBUS_IPC_MESSAGE_IOCTL_MARSHAL_VFS_FILE, &MarshalFile));\n        if (Result < 0)\n        {\n            return ExitCode;\n        }\n\n        Message->StdFdIds[Index] = MarshalFile.Output.VfsFileId;\n    }\n\n    //\n    // Send the create NT process message to the server.\n    //\n\n    auto Bytes = UtilWriteBuffer(CreateProcessFd.get(), Span);\n    if (Bytes != static_cast<ssize_t>(Span.size()))\n    {\n        return ExitCode;\n    }\n\n    //\n    // Close the file descriptors representing stdin, stdout, and stderr.\n    //\n\n    for (int Index = 0; Index < LX_INIT_STD_FD_COUNT; ++Index)\n    {\n        CLOSE(Index);\n    }\n\n    //\n    // Create a signalfd to detect window size changes.\n    //\n\n    sigset_t SignalMask;\n    sigemptyset(&SignalMask);\n    sigaddset(&SignalMask, SIGWINCH);\n    sigaddset(&SignalMask, SIGINT);\n    Result = sigprocmask(SIG_BLOCK, &SignalMask, NULL);\n    if (Result < 0)\n    {\n        LOG_STDERR(\"sigprocmask failed %d\", errno);\n        return ExitCode;\n    }\n\n    wil::unique_fd SignalFd{signalfd(-1, &SignalMask, 0)};\n    if (!SignalFd)\n    {\n        LOG_STDERR(\"signalfd failed %d\", errno);\n        return ExitCode;\n    }\n\n    //\n    // Initialize poll state.\n    //\n\n    pollfd PollDescriptors[2];\n    PollDescriptors[0].fd = CreateProcessFd.get();\n    PollDescriptors[0].events = POLLIN;\n    PollDescriptors[1].fd = SignalFd.get();\n    PollDescriptors[1].events = POLLIN;\n\n    //\n    // Begin worker loop.\n    //\n\n    wil::unique_fd SignalChannelFd{};\n    for (;;)\n    {\n        Result = poll(PollDescriptors, COUNT_OF(PollDescriptors), -1);\n        if (Result < 0)\n        {\n            LOG_STDERR(\"poll failed %d\", errno);\n            break;\n        }\n\n        //\n        // Read the create process response or exit status message from the\n        // control channel.\n        //\n\n        if (PollDescriptors[0].revents & POLLIN)\n        {\n            union\n            {\n                MESSAGE_HEADER Header;\n                LX_INIT_PROCESS_EXIT_STATUS ExitStatus;\n                LX_INIT_CREATE_PROCESS_RESPONSE Response;\n            } Reply;\n\n            Bytes = TEMP_FAILURE_RETRY(read(PollDescriptors[0].fd, &Reply, sizeof(Reply)));\n            if (Bytes < 0)\n            {\n                LOG_STDERR(\"read failed %d\", errno);\n                return ExitCode;\n            }\n\n            if (Reply.Header.MessageType == LxInitMessageCreateProcessResponse)\n            {\n                //\n                // Verify the process launch request was successful.\n                //\n\n                if (Reply.Response.Result != 0)\n                {\n                    errno = Reply.Response.Result;\n                    LOG_STDERR(\"%s\", Argv[0]);\n                    return ExitCode;\n                }\n\n                //\n                // Unmarshal the signal channel if one was created.\n                //\n\n                if (Reply.Response.SignalPipeId != 0)\n                {\n                    LXBUS_IPC_MESSAGE_UNMARSHAL_HANDLE_PARAMETERS UnmarshalHandle{};\n                    UnmarshalHandle.Input.HandleId = Reply.Response.SignalPipeId;\n                    Result = TEMP_FAILURE_RETRY(ioctl(CreateProcessFd.get(), LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_HANDLE, &UnmarshalHandle));\n                    if (Result < 0)\n                    {\n                        return ExitCode;\n                    }\n\n                    SignalChannelFd.reset(UnmarshalHandle.Output.FileDescriptor);\n                }\n\n                //\n                // If the application was a GUI application and stdin is a console, restore\n                // the terminal mode. This allows ctrl-c and ctrl-z to function for\n                // graphical apps.\n                //\n\n                if ((Reply.Response.Flags & LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION) != 0)\n                {\n                    RestoreConsoleState();\n                }\n            }\n            else if (Reply.Header.MessageType == LxInitMessageExitStatus)\n            {\n                ExitCode = Reply.ExitStatus.ExitCode;\n                UtilWriteBuffer(PollDescriptors[0].fd, &Reply, Bytes);\n                break;\n            }\n            else\n            {\n                LOG_STDERR(\"Unexpected message\");\n                break;\n            }\n        }\n\n        //\n        // Forward window resize events via the relay pipe and handle sigint.\n        //\n\n        if (PollDescriptors[1].revents & POLLIN)\n        {\n            signalfd_siginfo SignalInfo;\n            Bytes = TEMP_FAILURE_RETRY(read(PollDescriptors[1].fd, &SignalInfo, sizeof(SignalInfo)));\n            if (Bytes != sizeof(SignalInfo))\n            {\n                LOG_STDERR(\"read failed %zd %d\", Bytes, errno);\n                break;\n            }\n\n            if (SignalInfo.ssi_signo == SIGWINCH)\n            {\n                WindowSizeChanged(SignalChannelFd.get());\n            }\n            else if (SignalInfo.ssi_signo == SIGINT)\n            {\n                break;\n            }\n            else\n            {\n                LOG_STDERR(\"Unexpected signal %u\", SignalInfo.ssi_signo);\n                break;\n            }\n        }\n    }\n\n    return ExitCode;\n}\n\nvoid CreateNtProcessConfigureConsole(PLX_INIT_CREATE_NT_PROCESS_COMMON Common)\n\n/*++\n\nRoutine Description:\n\n    This routine queries stdin, stdout, and stderr and determines if a\n    Windows pseudoconsole should be created. It also performs additional logic\n    around setting and restoring the terminal mode if stdin is a console.\n\nArguments:\n\n    Common - Supplies a pointer to the common create process information. This\n        buffer will be modified if the console state is inconsistent.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    struct winsize WindowSize;\n\n    //\n    // Ensure that stdin, stdout, and stderr are terminals.\n    //\n\n    termios ConsoleInfo;\n    for (int Index = 0; Index < LX_INIT_STD_FD_COUNT; Index += 1)\n    {\n        if (tcgetattr(Index, &ConsoleInfo) < 0)\n        {\n            return;\n        }\n    }\n\n    //\n    // Ensure that stdin represents the foreground process group.\n    // N.B. It's possible that standard file descriptors point to tty while the process\n    // has no controlling terminal (in case its parent called setsid() without opening a new terminal for instance).\n    // See https://github.com/microsoft/WSL/issues/13173.\n    //\n\n    auto processGroup = tcgetpgrp(0);\n    if (processGroup < 0)\n    {\n        if (errno != ENOTTY)\n        {\n            LOG_STDERR(\"tcgetpgrp failed\");\n        }\n\n        return;\n    }\n\n    if (processGroup != getpgrp())\n    {\n        return;\n    }\n\n    //\n    // Ensure stdin, stdout, and stderr represent the same terminal.\n    //\n\n    struct stat StdIn;\n    if (fstat(0, &StdIn) < 0)\n    {\n        LOG_STDERR(\"fstat(0) failed\");\n        return;\n    }\n\n    struct stat StatBuffer;\n    for (int Index = 1; Index < LX_INIT_STD_FD_COUNT; Index += 1)\n    {\n        if (fstat(Index, &StatBuffer) < 0)\n        {\n            LOG_STDERR(\"fstat(%d) failed\", Index);\n            return;\n        }\n\n        if (StatBuffer.st_dev != StdIn.st_dev)\n        {\n            return;\n        }\n    }\n\n    //\n    // Query the window size.\n    //\n\n    if (ioctl(0, TIOCGWINSZ, &WindowSize) < 0)\n    {\n        LOG_STDERR(\"ioctl(TIOCGWINSZ) failed\");\n        return;\n    }\n\n    //\n    // Don't create a pseudoconsole if either the row or column size is zero.\n    //\n\n    if ((WindowSize.ws_row == 0) || (WindowSize.ws_col == 0))\n    {\n        return;\n    }\n\n    Common->Rows = WindowSize.ws_row;\n    Common->Columns = WindowSize.ws_col;\n\n    //\n    // Set the terminal to raw mode.\n    //\n\n    memcpy(&g_ConsoleInfoBackup, &ConsoleInfo, sizeof(ConsoleInfo));\n    cfmakeraw(&ConsoleInfo);\n    if (TEMP_FAILURE_RETRY(tcsetattr(0, TCSANOW, &ConsoleInfo)) < 0)\n    {\n        LOG_STDERR(\"tcsetattr failed\");\n        return;\n    }\n\n    //\n    // Duplicate stdin to query window size changes.\n    //\n\n    g_ConsoleFd = dup(0);\n    if (g_ConsoleFd < 0)\n    {\n        LOG_STDERR(\"dup failed\");\n        return;\n    }\n\n    Common->CreatePseudoconsole = true;\n}\n\nstd::vector<gsl::byte> CreateNtProcessMessage(LX_MESSAGE_TYPE MessageType, int Argc, char* Argv[])\n\n/*++\n\nRoutine Description:\n\n    This routine allocates and initializes a create NT process message.\n\nArguments:\n\n    MessageType - Supplies the message type.\n\n    Argc - Supplies the command line argument count.\n\n    Argv - Supplies the command line arguments.\n\nReturn Value:\n\n    The initialized message buffer.\n\n--*/\n\ntry\n{\n    //\n    // Calculate the size of the create process message.\n    //\n\n    size_t Size;\n    switch (MessageType)\n    {\n    case LxInitMessageCreateProcess:\n        Size = offsetof(LX_INIT_CREATE_NT_PROCESS, Common.Buffer);\n        break;\n\n    case LxInitMessageCreateProcessUtilityVm:\n        Size = offsetof(LX_INIT_CREATE_NT_PROCESS_UTILITY_VM, Common.Buffer);\n        break;\n\n    default:\n        return {};\n    }\n\n    //\n    // Translate the Linux filename into a Windows path.\n    //\n\n    auto Filename = WslPathTranslate(Argv[0], (TRANSLATE_FLAG_ABSOLUTE | TRANSLATE_FLAG_RESOLVE_SYMLINKS), TRANSLATE_MODE_WINDOWS);\n    if (Filename.empty())\n    {\n        return {};\n    }\n\n    if (UtilSizeTAdd(Filename.length(), Size, &Size) == false)\n    {\n        return {};\n    }\n\n    if (UtilSizeTAdd(1, Size, &Size) == false)\n    {\n        return {};\n    }\n\n    //\n    // Attempt to translate the current working directory, if translation fails\n    // use an empty current working directory.\n    //\n\n    auto Cwd = std::filesystem::current_path().string();\n    auto CurrentWorkingDirectory = WslPathTranslate(Cwd.data(), TRANSLATE_FLAG_ABSOLUTE, TRANSLATE_MODE_WINDOWS);\n    if (UtilSizeTAdd(CurrentWorkingDirectory.length(), Size, &Size) == false)\n    {\n        return {};\n    }\n\n    if (UtilSizeTAdd(1, Size, &Size) == false)\n    {\n        return {};\n    }\n\n    //\n    // Initialize the environment.\n    //\n\n    auto Environment = UtilParseWslEnv(nullptr);\n    if (UtilSizeTAdd(Environment.size(), Size, &Size) == false)\n    {\n        return {};\n    }\n\n    if (UtilSizeTAdd(1, Size, &Size) == false)\n    {\n        return {};\n    }\n\n    //\n    // If Argv[0] and Argv[1] match, use the basename for Argv[1].\n    // This is useful for Windows binaries that inspect the first argument.\n    //\n    // N.B. Arg[1] cannot be passed as-is because some windows binaries (like cmd.exe)\n    //      do not handle the Linux-style path.\n    //\n\n    if ((Argc > 1) && (strcmp(Argv[0], Argv[1])) == 0)\n    {\n        Argv[1] = basename(Argv[1]);\n    }\n\n    //\n    // Calculate the size of the command line.\n    //\n\n    for (int Index = 1; Index < Argc; Index += 1)\n    {\n        if (UtilSizeTAdd(strlen(Argv[Index]), Size, &Size) == false)\n        {\n            return {};\n        }\n\n        if (UtilSizeTAdd(1, Size, &Size) == false)\n        {\n            return {};\n        }\n    }\n\n    if (Size > ULONG_MAX)\n    {\n        return {};\n    }\n\n    //\n    // Initialize the message.\n    //\n\n    std::vector<gsl::byte> Buffer(Size);\n    auto Message = gsl::make_span(Buffer);\n    auto* Header = gslhelpers::get_struct<MESSAGE_HEADER>(Message);\n    Header->MessageType = MessageType;\n    Header->MessageSize = static_cast<unsigned>(Size);\n    Message = Message.subspan(\n        (MessageType == LxInitMessageCreateProcess) ? offsetof(LX_INIT_CREATE_NT_PROCESS, Common)\n                                                    : offsetof(LX_INIT_CREATE_NT_PROCESS_UTILITY_VM, Common));\n\n    auto* Common = gslhelpers::get_struct<LX_INIT_CREATE_NT_PROCESS_COMMON>(Message);\n    size_t Offset = offsetof(LX_INIT_CREATE_NT_PROCESS_COMMON, Buffer);\n\n    //\n    // Copy filename, cwd, environment into the message buffer.\n    //\n\n    Common->FilenameOffset = wsl::shared::string::CopyToSpan(Filename, Message, Offset);\n    Common->CurrentWorkingDirectoryOffset = wsl::shared::string::CopyToSpan(CurrentWorkingDirectory, Message, Offset);\n    Common->EnvironmentOffset = wsl::shared::string::CopyToSpan(std::string_view{Environment.data(), Environment.size()}, Message, Offset);\n\n    //\n    // Copy the command line arguments.\n    //\n\n    Common->CommandLineOffset = gsl::narrow_cast<unsigned int>(Offset);\n    Common->CommandLineCount = gsl::narrow_cast<unsigned short>(Argc - 1);\n    for (int Index = 1; Index < Argc; Index += 1)\n    {\n        wsl::shared::string::CopyToSpan(Argv[Index], Message, Offset);\n    }\n\n    //\n    // Initialize the console state.\n    //\n\n    CreateNtProcessConfigureConsole(Common);\n\n    //\n    // Return the message to the caller.\n    //\n\n    return Buffer;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\nbool HasOpenFileDescriptors(struct pollfd* PollDescriptors, int PollDescriptorSize)\n/*++\n\nRoutine Description:\n\n    This routine checks if the given array of PollDescriptors has any\n    PollDescriptor that is still open. In other words, it checks if there\n    is at least one descriptor that has a fd >= 0.\n\nArguments:\n\n    PollDescriptors - The array of poll descriptors.\n    PollDescriptorSize - The count of number of elements in the\n                    PollDescriptors array.\n\nReturn Value:\n\n    True if there is at least one open poll descriptor, False otherwise.\n\n--*/\n{\n    for (int i = 0; i < PollDescriptorSize; i++)\n    {\n        if (PollDescriptors[i].fd >= 0)\n        {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid RestoreConsoleState(void)\n\n/*++\n\nRoutine Description:\n\n    This routine restores the original console state.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    if (g_ConsoleFd != -1)\n    {\n        tcsetattr(g_ConsoleFd, TCSANOW, &g_ConsoleInfoBackup);\n        CLOSE(g_ConsoleFd);\n        g_ConsoleFd = -1;\n    }\n}\n\nvoid WindowSizeChanged(int SignalChannelFd)\n\n/*++\n\nRoutine Description:\n\n    This routine is the signal handler interop window size changes.\n\nArguments:\n\n    SignalChannelFd - Supplies a file descriptor to write the window size\n        message.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    if ((SignalChannelFd == -1) || (g_ConsoleFd == -1))\n    {\n        return;\n    }\n\n    //\n    // Query the new window size and send the updated size via the signal\n    // channel.\n    //\n\n    winsize WindowSize;\n    int Result = ioctl(g_ConsoleFd, TIOCGWINSZ, &WindowSize);\n    if (Result < 0)\n    {\n        LOG_STDERR(\"ioctl(TIOCGWINSZ) failed\");\n        return;\n    }\n\n    LX_INIT_WINDOW_SIZE_CHANGED ResizeMessage;\n    ResizeMessage.Header.MessageType = LxInitMessageWindowSizeChanged;\n    ResizeMessage.Header.MessageSize = sizeof(ResizeMessage);\n    ResizeMessage.Columns = WindowSize.ws_col;\n    ResizeMessage.Rows = WindowSize.ws_row;\n    Result = UtilWriteBuffer(SignalChannelFd, gslhelpers::struct_as_bytes(ResizeMessage));\n    if (Result < 0)\n    {\n        LOG_STDERR(\"sending resize message failed\");\n    }\n}\n"
  },
  {
    "path": "src/linux/init/binfmt.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    binfmt.h\n\nAbstract:\n\n    This file contains declarations for the Windows interop binfmt interpreter.\n\n--*/\n\n#pragma once\n\n//\n// Name of the WSL binfmt_misc interpreter.\n//\n\n#define LX_INIT_BINFMT_NAME \"WSLInterop\"\n#define BINFMT_MISC_MOUNT_TARGET \"/proc/sys/fs/binfmt_misc\"\n#define BINFMT_MISC_REGISTER_FILE BINFMT_MISC_MOUNT_TARGET \"/register\"\n#define BINFMT_INTEROP_REGISTRATION_STRING(Name) \":\" Name \":M::MZ::\" LX_INIT_PATH \":P\"\n\nint CreateNtProcess(int Argc, char* Argv[]);\n"
  },
  {
    "path": "src/linux/init/common.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    common.h\n\nAbstract:\n\n    This file contains common/shared information.\n\n--*/\n\n#pragma once\n\n#ifndef TEMP_FAILURE_RETRY\n#define TEMP_FAILURE_RETRY(expression) \\\n    (__extension__({ \\\n        long int __result; \\\n        do \\\n            __result = (long int)(expression); \\\n        while (__result == -1L && errno == EINTR); \\\n        __result; \\\n    }))\n#endif\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <stddef.h>\n#include <unistd.h>\n#include <string.h>\n#include <errno.h>\n#include <sched.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/syscall.h>\n#include <sys/reboot.h>\n#include <sys/un.h>\n#include <dirent.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <signal.h>\n#include <poll.h>\n#include <utility>\n#include <format>\n#include <new>\n#include <thread>\n#include <vector>\n#include <gsl/gsl>\n#include <gslhelpers.h>\n#include <lxwil.h>\n#include <cstdarg>\n#include \"lxinitshared.h\"\n#include \"defs.h\"\n\n#define ETC_FOLDER \"/etc/\"\n#define NAME_ENV \"NAME\"\n#define INIT_PATH \"/sbin/init\"\n#define INTEROP_TIMEOUT_MS (INTEROP_TIMEOUT_SEC * 1000)\n#define SESSION_LEADER_ACCEPT_TIMEOUT_MS (30 * 1000)\n#define INTEROP_TIMEOUT_SEC (10)\n#define RUN_FOLDER \"/run\"\n#define WSL_SAFE_MODE_WARNING \"SAFE MODE ENABLED\"\n#define CONFIG_FILE ETC_FOLDER \"wsl.conf\"\n\nextern thread_local std::string g_threadName;\nextern int g_LogFd;\nextern int g_TelemetryFd;\nextern struct sigaction g_SavedSignalActions[_NSIG];\n\n//\n// N.B. The clone syscall is used directly instead of the libc wrapper, because\n//      it allows execution to continue at the point of the call with a\n//      copy-on-write stack.\n//\n// The clone syscall argument are architecture-specific:\n//     x86, arm, arm64: clone(flags, stack, ptid, tls, ctid)\n//     amd64:           clone(flags, stack, ptid, ctid, tls)\n//\n\n#if defined(__i386__) || defined(__arm__) || defined(__aarch64__)\n#define CLONE(_flags) syscall(SYS_clone, (_flags), NULL, NULL, NULL, NULL);\n#elif defined(__x86_64__)\n#define CLONE(_flags) syscall(SYS_clone, (_flags), NULL, NULL, NULL, NULL);\n#else\n#error CLONE function signature is architecture-specific.\n#endif\n\n#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))\n\n#define CLOSE(_fd) \\\n    { \\\n        if (_fd != -1 && close(_fd) < 0) \\\n        { \\\n            FATAL_ERROR(\"close({}) {}\", _fd, errno); \\\n        } \\\n    }\n\ntemplate <typename... Args>\nauto LogImpl(int fd, const std::format_string<Args...>& format, Args&&... args)\n{\n    auto logline = std::format(format, std::forward<Args>(args)...);\n    if (logline.empty())\n    {\n        return;\n    }\n\n    if (logline.back() != '\\n')\n    {\n        logline.push_back('\\n');\n    }\n\n    write(fd, logline.c_str(), logline.size());\n}\n\n#define LOG_ERROR(str, ...) \\\n    { \\\n        LogImpl(g_LogFd, \"<3>WSL ({} - {}) ERROR: {}:{}: \" str \"\\n\", getpid(), g_threadName.c_str(), __FUNCTION__, __LINE__, ##__VA_ARGS__); \\\n    }\n\n#define LOG_INFO(str, ...) \\\n    { \\\n        LogImpl(g_LogFd, \"<6>WSL ({} - {}): \" str \"\\n\", getpid(), g_threadName.c_str(), ##__VA_ARGS__); \\\n    }\n\n#define LOG_WARNING(str, ...) \\\n    { \\\n        LogImpl(g_LogFd, \"<4>WSL ({} - {}) WARNING: \" str \"\\n\", getpid(), g_threadName.c_str(), ##__VA_ARGS__); \\\n    }\n\n#define FATAL_ERROR_EX(status, str, ...) \\\n    { \\\n        LOG_ERROR(str, ##__VA_ARGS__); \\\n        _exit(status); \\\n    }\n\n#define GNS_LOG_INFO(str, ...) \\\n    { \\\n        LogImpl(g_TelemetryFd, \"{}: {} - \" str \"\\n\", g_threadName.c_str(), __FUNCTION__, ##__VA_ARGS__); \\\n    }\n\n#define GNS_LOG_ERROR(str, ...) \\\n    { \\\n        LogImpl(g_TelemetryFd, \"{}: {} - ERROR: \" str \"\\n\", g_threadName.c_str(), __FUNCTION__, ##__VA_ARGS__); \\\n    }\n\n#define FATAL_ERROR(str, ...) FATAL_ERROR_EX(1, str, ##__VA_ARGS__)\n\n// Some of these files need the LOG_* macros.\n#include \"retryshared.h\"\n#include \"socketshared.h\"\n#include \"stringshared.h\"\n\nint InitializeLogging(bool SetStderr, wil::LogFunction* ExceptionCallback = nullptr) noexcept;\n\nvoid LogException(const char* Message, const char* Description) noexcept;\n"
  },
  {
    "path": "src/linux/init/config.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    config.c\n\nAbstract:\n\n    This file contains methods for configuring a running instance.\n\n--*/\n\n#include <bitset>\n#include <sys/mount.h>\n#include <sys/utsname.h>\n#include <sys/socket.h>\n#include <sys/sysmacros.h>\n#include <pwd.h>\n#include <future>\n#include <signal.h>\n#include <pty.h>\n#include <lxbusapi.h>\n#include \"common.h\"\n#include \"mountutilcpp.h\"\n#include \"config.h\"\n#include \"util.h\"\n#include \"configfile.h\"\n#include \"binfmt.h\"\n#include \"wslpath.h\"\n#include \"wslinfo.h\"\n#include \"drvfs.h\"\n#include \"timezone.h\"\n#include \"message.h\"\n#include \"WslDistributionConfig.h\"\n#include \"lxfsshares.h\"\n#include \"plan9.h\"\n\n#define AUTO_MOUNT_PARENT_MODE 0755\n#define CGROUP_DEVICE \"cgroup\"\n#define CGROUPS_FILE \"/proc/cgroups\"\n#define CGROUPS_NO_V1 \"cgroup_no_v1=\"\n#define DEFAULT_CWD \"/\"\n#define DRVFS_MOUNT_OPTIONS (MS_NOATIME)\n#define DRVFS_SOURCE \" :\\\\\"\n#define DRVFS_TARGET_MODE 0777\n#define DRVFS_OPTIONS_BUFFER_LENGTH 38\n#define ETC_DEFAULT_FOLDER ETC_FOLDER \"default/\"\n#define HOSTNAME_FILE_PATH ETC_FOLDER \"hostname\"\n#define HOSTNAME_FILE_MODE 0644\n#define HOSTS_FILE_MODE 0644\n#define HOSTS_FILE_PATH ETC_FOLDER \"hosts\"\n#define LANG_ENV \"LANG\"\n#define LOCALE_FILE_PATH ETC_DEFAULT_FOLDER \"locale\"\n#define PATH_ENV \"PATH\"\n#define RESOLV_CONF_DIRECTORY_MODE 0755\n#define RESOLV_CONF_FILE_MODE 0644\n#define RESOLV_CONF_FILE_NAME \"resolv.conf\"\n#define RESOLV_CONF_FILE_PATH ETC_FOLDER RESOLV_CONF_FILE_NAME\n#define RESOLV_CONF_FOLDER RUN_FOLDER \"/resolvconf\"\n#define RESOLV_CONF_SYMLINK_TARGET \"..\" RESOLV_CONF_FOLDER \"/\" RESOLV_CONF_FILE_NAME\n#define RESOLV_CONF_SYMLINK_WSL_MOUNT_SUFFIX SHARED_MOUNT_FOLDER \"/\" RESOLV_CONF_FILE_NAME\n#define RUN_FOLDER \"/run\"\n#define SHARED_MOUNT_FOLDER \"wsl\"\n#define USER_MOUNT_FOLDER \"user\"\n#define WINDOWS_LD_CONF_FILE \"/etc/ld.so.conf.d/ld.wsl.conf\"\n#define WINDOWS_LD_CONF_FILE_MODE 0644\n\n#define MOUNTS_FILE \"/proc/self/mounts\"\n#define MOUNTS_FIELD_SEPARATOR ' '\n#define MOUNTS_LINE_SEPARATOR '\\n'\n#define MOUNTS_DEVICE_FIELD 0\n#define MOUNTS_FSTYPE_FIELD 2\n\nusing wsl::linux::WslDistributionConfig;\n\nstatic void ConfigApplyWindowsLibPath(const wsl::linux::WslDistributionConfig& Config);\n\nstatic bool CreateLoginSession(const wsl::linux::WslDistributionConfig& Config, const char* Username, uid_t Uid);\n\nclass RemoveMountAndEnvironmentOnScopeExit\n{\npublic:\n    RemoveMountAndEnvironmentOnScopeExit() = default;\n\n    RemoveMountAndEnvironmentOnScopeExit(const char* EnvironmentName) : m_environmentName(EnvironmentName)\n    {\n        m_mountPath = getenv(m_environmentName);\n    }\n\n    RemoveMountAndEnvironmentOnScopeExit& operator=(const RemoveMountAndEnvironmentOnScopeExit&) = delete;\n    RemoveMountAndEnvironmentOnScopeExit(const RemoveMountAndEnvironmentOnScopeExit&) = delete;\n\n    RemoveMountAndEnvironmentOnScopeExit(RemoveMountAndEnvironmentOnScopeExit&& Other)\n    {\n        *this = std::move(Other);\n    }\n\n    RemoveMountAndEnvironmentOnScopeExit& operator=(RemoveMountAndEnvironmentOnScopeExit&& Other)\n    {\n        m_environmentName = Other.m_environmentName;\n        Other.m_environmentName = nullptr;\n\n        m_mountPath = Other.m_mountPath;\n        Other.m_mountPath = nullptr;\n\n        return *this;\n    }\n\n    ~RemoveMountAndEnvironmentOnScopeExit()\n    {\n        if (m_environmentName != nullptr)\n        {\n            if (unsetenv(m_environmentName) < 0)\n            {\n                LOG_ERROR(\"unsetenv({}) failed {}\", m_environmentName, errno);\n            }\n        }\n\n        if (m_mountPath != nullptr)\n        {\n            if (umount2(m_mountPath, MNT_DETACH) < 0)\n            {\n                LOG_ERROR(\"umount2({}, MNT_DETACH) failed {}\", m_mountPath, errno);\n                return;\n            }\n\n            if (rmdir(m_mountPath) < 0)\n            {\n                LOG_ERROR(\"rmdir({}) failed {}\", m_mountPath, errno);\n            }\n        }\n    }\n\n    operator bool() const\n    {\n        return m_mountPath;\n    }\n\n    const char* MountPath() const\n    {\n        return m_mountPath;\n    }\n\n    bool MoveMount(const char* Target)\n    {\n        if (m_mountPath == nullptr)\n        {\n            return false;\n        }\n\n        if (UtilMount(m_mountPath, Target, nullptr, (MS_MOVE | MS_REC), nullptr) < 0)\n        {\n            return false;\n        }\n\n        if (rmdir(m_mountPath) < 0)\n        {\n            LOG_ERROR(\"rmdir({}) failed {}\", m_mountPath, errno);\n        }\n\n        m_mountPath = nullptr;\n        return true;\n    }\n\nprivate:\n    const char* m_environmentName = nullptr;\n    const char* m_mountPath = nullptr;\n};\n\nconstexpr auto HostsFileFormatString = LX_INIT_AUTO_GENERATED_FILE_HEADER\n    \"# [network]\\n\"\n    \"# generateHosts = false\\n\"\n    \"127.0.0.1\\tlocalhost\\n\"\n    \"127.0.1.1\\t{}.{}\\t{}\\n\"\n    \"{}\\n\"\n    \"# The following lines are desirable for IPv6 capable hosts\\n\"\n    \"::1     ip6-localhost ip6-loopback\\n\"\n    \"fe00::0 ip6-localnet\\n\"\n    \"ff00::0 ip6-mcastprefix\\n\"\n    \"ff02::1 ip6-allnodes\\n\"\n    \"ff02::2 ip6-allrouters\\n\";\n\nconstexpr auto WindowsLibSearchFileHeaderString = LX_INIT_AUTO_GENERATED_FILE_HEADER\n    \"# [automount]\\n\"\n    \"# ldconfig = false\\n\";\n\nconst INIT_STARTUP_ANY LxssStartupCommon[] = {\n    INIT_ANY_DIRECTORY(\"/sys\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT_DEVICE(\"/sys\", \"sysfs\", \"sysfs\", (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME | MS_SHARED)),\n    INIT_ANY_DIRECTORY(\"/proc\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT_DEVICE(\"/proc\", \"proc\", \"proc\", (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME | MS_SHARED)),\n    INIT_ANY_DIRECTORY(\"/dev/block\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_SYMLINK(\"/dev/fd\", \"/proc/self/fd\"),\n    INIT_ANY_SYMLINK(\"/dev/stdin\", \"/proc/self/fd/0\"),\n    INIT_ANY_SYMLINK(\"/dev/stdout\", \"/proc/self/fd/1\"),\n    INIT_ANY_SYMLINK(\"/dev/stderr\", \"/proc/self/fd/2\"),\n    INIT_ANY_DIRECTORY(\"/dev/pts\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT_DEVICE_OPTION(\"/dev/pts\", \"devpts\", \"devpts\", \"gid=5,mode=620\", MS_NOATIME | MS_NOSUID | MS_NOEXEC),\n    INIT_ANY_DIRECTORY(\"/run\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT_OPTION(\"/run\", \"tmpfs\", \"mode=755\", (MS_NODEV | MS_STRICTATIME | MS_NOSUID | MS_SHARED)),\n    INIT_ANY_DIRECTORY(\"/run/lock\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT(\"/run/lock\", \"tmpfs\", MS_NOATIME | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_SHARED),\n    INIT_ANY_DIRECTORY(\"/run/shm\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT(\"/run/shm\", \"tmpfs\", MS_NOATIME | MS_NOSUID | MS_NODEV | MS_SHARED),\n    INIT_ANY_DIRECTORY(\"/dev/shm\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT_DEVICE(\"/dev/shm\", nullptr, \"/run/shm\", MS_BIND),\n    INIT_ANY_DIRECTORY(\"/run/user\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT_OPTION(\"/run/user\", \"tmpfs\", \"mode=755\", MS_NOATIME | MS_NOSUID | MS_NOEXEC | MS_NODEV),\n    INIT_ANY_DIRECTORY(\"/bin\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_SYMLINK(\"/bin/\" WSLINFO_NAME, \"/init\"),\n    INIT_ANY_SYMLINK(\"/bin/\" WSLPATH_NAME, \"/init\"),\n    INIT_ANY_DIRECTORY(\"/sbin\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_SYMLINK(\"/sbin/\" MOUNT_DRVFS_NAME, \"/init\"),\n    INIT_ANY_MOUNT_DEVICE(BINFMT_MISC_MOUNT_TARGET, \"binfmt_misc\", \"binfmt_misc\", MS_RELATIME),\n    INIT_ANY_DIRECTORY(\"/tmp\", ROOT_UID, ROOT_GID, S_IFDIR | S_ISVTX | 0777)};\n\nconst INIT_STARTUP_ANY LxssStartupLoggingVmMode[] = {\n    INIT_ANY_DIRECTORY(\"/dev\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT_OPTION(\"/dev\", \"devtmpfs\", \"mode=755\", (MS_NOSUID | MS_RELATIME | MS_SHARED))};\n\nconst INIT_STARTUP_ANY LxssStartupLoggingWsl[] = {\n    INIT_ANY_DIRECTORY(\"/dev\", ROOT_UID, ROOT_GID, S_IFDIR | 0755),\n    INIT_ANY_MOUNT_OPTION(\"/dev\", \"tmpfs\", \"mode=755\", MS_NOATIME | MS_SHARED),\n    INIT_ANY_NODE(\"/dev/kmsg\", ROOT_UID, ROOT_GID, S_IFCHR | 0644, INIT_DEV_LOG_KMSG_MAJOR_NUMBER, INIT_DEV_LOG_KMSG_MINOR_NUMBER)};\n\nconst INIT_STARTUP_ANY LxssStartupWsl[] = {\n    INIT_ANY_NODE(\"/dev/ptmx\", ROOT_UID, TTY_GID, S_IFCHR | 0666, INIT_DEV_PTM_MAJOR_NUMBER, INIT_DEV_PTM_MINOR_NUMBER),\n    INIT_ANY_NODE(\"/dev/random\", ROOT_UID, ROOT_GID, S_IFCHR | 0666, INIT_DEV_RANDOM_MAJOR_NUMBER, INIT_DEV_RANDOM_MINOR_NUMBER),\n    INIT_ANY_NODE(\"/dev/urandom\", ROOT_UID, ROOT_GID, S_IFCHR | 0666, INIT_DEV_URANDOM_MAJOR_NUMBER, INIT_DEV_URANDOM_MINOR_NUMBER),\n    INIT_ANY_NODE(\"/dev/null\", ROOT_UID, ROOT_GID, S_IFCHR | 0666, INIT_DEV_NULL_MAJOR_NUMBER, INIT_DEV_NULL_MINOR_NUMBER),\n    INIT_ANY_NODE(\"/dev/tty\", ROOT_UID, TTY_GID, S_IFCHR | 0666, INIT_DEV_TTYCT_MAJOR_NUMBER, INIT_DEV_TTYCT_MINOR_NUMBER),\n    INIT_ANY_NODE(\"/dev/tty0\", ROOT_UID, TTY_GID, S_IFCHR | 0620, INIT_DEV_TTY_MAJOR_NUMBER, INIT_DEV_TTY0_MINOR_NUMBER),\n    INIT_ANY_NODE(\"/dev/zero\", ROOT_UID, ROOT_GID, S_IFCHR | 0666, INIT_DEV_ZERO_MAJOR_NUMBER, INIT_DEV_ZERO_MINOR_NUMBER),\n    INIT_ANY_NODE(LXBUS_DEVICE_NAME, ROOT_UID, ROOT_GID, S_IFCHR | 0666, INIT_DEV_LXBUS_MAJOR_NUMBER, INIT_DEV_LXBUS_MINOR_NUMBER)};\n\n//\n// Mount namespace file descriptors for VM mode.\n//\n\nint g_ElevatedMountNamespace = -1;\nint g_NonElevatedMountNamespace = -1;\n\n//\n// Boot state bookkeeping.\n//\n\nextern wsl::shared::SocketChannel g_plan9ControlChannel;\n\nvoid ConfigAppendNtPath(EnvironmentBlock& Environment, char* NtPath)\n\n/*++\n\nRoutine Description:\n\n    This routine updates the $PATH variable of the provided environment block.\n\nArguments:\n\n    Environment - Supplies the environment block to update.\n\n    NtPath - Supplies a semicolon-separated list of NT paths to translate and\n        append to the $PATH variable. If no $PATH variable exists, one is\n        created.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    auto TranslatedPath = UtilTranslatePathList(NtPath, true);\n    if (!TranslatedPath.has_value())\n    {\n        return;\n    }\n\n    ConfigAppendToPath(Environment, TranslatedPath.value());\n    return;\n}\nCATCH_LOG()\n\nvoid ConfigAppendToPath(EnvironmentBlock& Environment, std::string_view PathElement)\n\n/*++\n\nRoutine Description:\n\n    This routine adds the specified path element to the $PATH variable of the\n    supplied environment block.\n\nArguments:\n\n    Environment - Supplies the environment block to update.\n\n    PathElement - Supplies a path element to add to the $PATH variable. If no\n        $PATH variable exists, one is created.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    //\n    // If no PATH variable is present, create a new variable. If a PATH is\n    // present, add the path element onto the end of the existing value.\n    //\n\n    auto Path = Environment.GetVariable(PATH_ENV);\n    if (Path.empty())\n    {\n        Environment.AddVariable(PATH_ENV, PathElement);\n    }\n    else\n    {\n        std::string NewPath{Path};\n        if (NewPath.back() != ':')\n        {\n            NewPath += ':';\n        }\n\n        NewPath += PathElement;\n        Environment.AddVariable(PATH_ENV, NewPath);\n    }\n\n    return;\n}\nCATCH_LOG()\n\nvoid ConfigHandleInteropMessage(\n    wsl::shared::SocketChannel& ResponseChannel,\n    wsl::shared::SocketChannel& InteropChannel,\n    bool Elevated,\n    gsl::span<gsl::byte> Message,\n    const MESSAGE_HEADER* Header,\n    const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine handles a message received from a Linux client using init's\n    interop socket.\n\nArguments:\n\n    ResponseChannel - Supplies channel used to send responses.\n\n    InteropChannel - Supplies a channel to the host to be used for create\n        process requests.\n\n    Elevated - Supplies a boolean specifying if the elevated DrvFs share should be used.\n\n    Message - Supplies the message buffer.\n\n    Header- Supplies the message Header.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    switch (Header->MessageType)\n    {\n    case LxInitMessageCreateProcessUtilityVm:\n        if (InteropChannel.Socket() > 0)\n        {\n            InteropChannel.SendMessage<LX_INIT_CREATE_NT_PROCESS_UTILITY_VM>(Message);\n        }\n\n        break;\n\n    case LxInitMessageQueryDrvfsElevated:\n    {\n        ResponseChannel.SendResultMessage<bool>(Elevated);\n        break;\n    }\n\n    case LxInitMessageQueryEnvironmentVariable:\n    {\n        auto* Query = gslhelpers::try_get_struct<LX_INIT_QUERY_ENVIRONMENT_VARIABLE>(Message);\n        if (!Query)\n        {\n            LOG_ERROR(\"Unexpected MessageSize {}\", Message.size());\n            return;\n        }\n\n        auto Value = UtilGetEnvironmentVariable(Query->Buffer);\n        wsl::shared::MessageWriter<LX_INIT_QUERY_ENVIRONMENT_VARIABLE> Response(LxInitMessageQueryEnvironmentVariable);\n        Response.WriteString(Value);\n        ResponseChannel.SendMessage<LX_INIT_QUERY_ENVIRONMENT_VARIABLE>(Response.Span());\n    }\n\n    break;\n\n    case LxInitMessageQueryFeatureFlags:\n    {\n        assert(Config.FeatureFlags.has_value());\n        ResponseChannel.SendResultMessage<int32_t>(Config.FeatureFlags.value());\n        break;\n    }\n\n    case LxInitMessageCreateLoginSession:\n    {\n        auto* CreateSession = gslhelpers::try_get_struct<LX_INIT_CREATE_LOGIN_SESSION>(Message);\n        if (!CreateSession)\n        {\n            LOG_ERROR(\"Unexpected MessageSize {}\", Message.size());\n            return;\n        }\n\n        bool success = false;\n        auto sendResponse = wil::scope_exit([&]() { ResponseChannel.SendResultMessage<bool>(success); });\n\n        if (!Config.BootInit || Config.InitPid.value_or(0) != getpid())\n        {\n            LOG_ERROR(\"Unexpected LxInitMessageCreateLoginSession message\");\n        }\n        else\n        {\n            success = CreateLoginSession(Config, CreateSession->Buffer, CreateSession->Uid);\n        }\n\n        break;\n    }\n\n    case LxInitMessageQueryNetworkingMode:\n        assert(Config.NetworkingMode.has_value());\n        ResponseChannel.SendResultMessage<uint8_t>(static_cast<uint8_t>(Config.NetworkingMode.value()));\n        break;\n\n    case LxInitMessageQueryVmId:\n    {\n        wsl::shared::MessageWriter<LX_INIT_QUERY_VM_ID> Response(LxInitMessageQueryVmId);\n        if (Config.VmId.has_value())\n        {\n            Response.WriteString(Config.VmId.value());\n        }\n\n        ResponseChannel.SendMessage<LX_INIT_QUERY_VM_ID>(Response.Span());\n        break;\n    }\n\n    default:\n        LOG_ERROR(\"unexpected message {}\", Header->MessageType);\n        break;\n    }\n}\nCATCH_LOG()\n\nwsl::linux::WslDistributionConfig ConfigInitializeCommon(struct sigaction* SavedSignalActions)\n\n/*++\n\nRoutine Description:\n\n    This routine sets up common devices and mounts.\n\nArguments:\n\n    SavedSignalActions - Supplies an array to save default signal actions.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    wil::unique_fd DevNullFd;\n    unsigned int Index;\n\n    //\n    // Set the umask to 0 to ensure that devices and files that init creates\n    // have the correct mode.\n    //\n\n    umask(0);\n\n    //\n    // Perform initialization required for logging to kmsg.\n    //\n\n    if (!UtilIsUtilityVm())\n    {\n        for (Index = 0; Index < COUNT_OF(LxssStartupLoggingWsl); Index += 1)\n        {\n            THROW_LAST_ERROR_IF(ConfigInitializeEntry(&LxssStartupLoggingWsl[Index]) < 0);\n        }\n    }\n    else\n    {\n        for (Index = 0; Index < COUNT_OF(LxssStartupLoggingVmMode); Index += 1)\n        {\n            THROW_LAST_ERROR_IF(ConfigInitializeEntry(&LxssStartupLoggingVmMode[Index]) < 0);\n        }\n    }\n\n    //\n    // Open /dev/kmsg for logging.\n    //\n\n    THROW_LAST_ERROR_IF(InitializeLogging(true) < 0);\n\n    //\n    // Ignore all signals except SIGHUP and signals that cannot be ignored.\n    //\n    // N.B. Ignoring SIGCHLD automatically reaps zombie processes.\n    //\n    // N.B. Child processes reset signals to default before calling execv.\n    //\n\n    THROW_LAST_ERROR_IF(UtilSaveSignalHandlers(SavedSignalActions) < 0);\n\n    THROW_LAST_ERROR_IF(UtilSetSignalHandlers(SavedSignalActions, true) < 0);\n\n    //\n    // Load the configuration file.\n    //\n\n    wsl::linux::WslDistributionConfig Config{CONFIG_FILE};\n\n    if (getenv(LX_WSL2_SYSTEM_DISTRO_SHARE_ENV) != nullptr)\n    {\n        Config.GuiAppsEnabled = true;\n    }\n\n    //\n    // Initialize the static entries.\n    //\n\n    for (Index = 0; Index < COUNT_OF(LxssStartupCommon); Index += 1)\n    {\n        THROW_LAST_ERROR_IF(ConfigInitializeEntry(&LxssStartupCommon[Index]) < 0);\n    }\n\n    //\n    // Initialize WSL1 and WSL2 specific environment.\n    //\n\n    if (!UtilIsUtilityVm())\n    {\n        THROW_LAST_ERROR_IF(ConfigInitializeWsl() < 0);\n    }\n\n    //\n    // Open /dev/null for the stdin and stdout in case libraries try to use\n    // them (but keep stderr open for kmsg logging).\n    //\n\n    DevNullFd = TEMP_FAILURE_RETRY(open(\"/dev/null\", O_RDWR));\n    THROW_LAST_ERROR_IF(!DevNullFd);\n\n    for (const auto& Fd : {STDIN_FILENO, STDOUT_FILENO})\n    {\n        THROW_LAST_ERROR_IF(dup2(DevNullFd.get(), Fd) < 0);\n    }\n\n    //\n    // Initialize cgroups based on what the kernel supports.\n    //\n\n    ConfigInitializeCgroups(Config);\n\n    //\n    // Attempt to register the NT interop binfmt extension.\n    //\n    // N.B. Registration for VM mode is done by mini_init.\n    //\n\n    if ((!UtilIsUtilityVm()) && (Config.InteropEnabled))\n    {\n        ConfigRegisterBinfmtInterpreter();\n    }\n\n    //\n    // Ensure the target for automounts exists.\n    //\n\n    if ((Config.AutoMount) || ((UtilIsUtilityVm())))\n    {\n        UtilMkdirPath(Config.DrvFsPrefix.c_str(), AUTO_MOUNT_PARENT_MODE, false);\n    }\n\n    //\n    // Initialization successful.\n    //\n\n    return Config;\n}\n\nvoid ConfigInitializeX11(const wsl::linux::WslDistributionConfig& Config)\ntry\n{\n    auto socketPath = \"/tmp/\" X11_SOCKET_NAME;\n    THROW_LAST_ERROR_IF(UtilMkdir(socketPath, 0775) < 0);\n\n    std::string source{Config.DrvFsPrefix};\n    source += WSLG_SHARED_FOLDER;\n    source += \"/\" X11_SOCKET_NAME;\n    THROW_LAST_ERROR_IF(mount(source.c_str(), socketPath, NULL, (MS_BIND | MS_REC), NULL) < 0);\n\n    // The .X11-unix folder is mounted read-only so the socket file can't be removed.\n    // It's left writable in the system distro since wslg is supposed to write to that folder to create it.\n    if (WI_IsFlagClear(Config.FeatureFlags.value(), LxInitFeatureSystemDistro))\n    {\n        THROW_LAST_ERROR_IF(mount(\"none\", socketPath, NULL, (MS_RDONLY | MS_REMOUNT | MS_BIND), NULL) < 0);\n    }\n}\nCATCH_LOG()\n\nint ConfigInitializeInstance(wsl::shared::SocketChannel& Channel, gsl::span<gsl::byte> Buffer, wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine initializes the instance's externally controlled state, which\n    is received from the service.\n\n    N.B. When setting these values errors are treated as non-fatal to account\n         for unexpected distro state.\n\nArguments:\n\n    MessageFd - Supplies a file descriptor to send the response message.\n\n    Buffer - Supplies the message buffer.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    //\n    // Validate input parameters.\n    //\n\n    const auto* Message = gslhelpers::try_get_struct<const LX_INIT_CONFIGURATION_INFORMATION>(Buffer);\n    if (!Message)\n    {\n        FATAL_ERROR(\"Unexpected configuration size {}\", Buffer.size());\n    }\n\n    //\n    // Set the host name and domain name buffers.\n    //\n\n    std::string Hostname = wsl::shared::string::FromSpan(Buffer, Message->HostnameOffset);\n    auto* Domainname = wsl::shared::string::FromSpan(Buffer, Message->DomainnameOffset);\n    auto* WindowsHosts = wsl::shared::string::FromSpan(Buffer, Message->WindowsHostsOffset);\n    auto* DistributionName = wsl::shared::string::FromSpan(Buffer, Message->DistributionNameOffset);\n    auto* Plan9SocketPath = wsl::shared::string::FromSpan(Buffer, Message->Plan9SocketOffset);\n    auto* Timezone = wsl::shared::string::FromSpan(Buffer, Message->TimezoneOffset);\n    bool Elevated = Message->DrvfsMount == LxInitDrvfsMountElevated;\n\n    const std::string ThreadName = std::format(\"{}({})\", (Config.BootInit ? \"init-systemd\" : \"init\"), DistributionName);\n    UtilSetThreadName(ThreadName.c_str());\n\n    //\n    // Store feature flags for future use.\n    //\n    // N.B. This is also stored in an environment variable so that mount.drvfs, when launched\n    //      through fstab mounting below, can use that. This is needed because mount.drvfs won't\n    //      be able to connect to init during this call. This environment variable is not present\n    //      for user-launched processes.\n    //\n\n    Config.FeatureFlags = Message->FeatureFlags;\n    UtilSetFeatureFlags(Config.FeatureFlags.value());\n\n    //\n    // Determine the default UID which can be specified in /etc/wsl.conf.\n    //\n\n    uid_t DefaultUid = Message->DrvFsDefaultOwner;\n    if (Config.DefaultUser.has_value())\n    {\n        passwd* PasswordEntry = getpwnam(Config.DefaultUser->c_str());\n        if (PasswordEntry == nullptr)\n        {\n            LOG_ERROR(\"getpwnam({}) failed {}\", Config.DefaultUser->c_str(), errno);\n        }\n        else\n        {\n            DefaultUid = PasswordEntry->pw_uid;\n        }\n    }\n\n    //\n    // Process the /etc/fstab file.\n    //\n    // N.B. This must happen before mounting DrvFs volumes because the user may\n    //      have specified DrvFs mounts in /etc/fstab and they should overwrite defaults.\n    //\n\n    if (Config.MountFsTab)\n    {\n        ConfigMountFsTab(Elevated);\n    }\n\n    //\n    // Perform additional WSL2-specific mounts.\n    //\n\n    if (UtilIsUtilityVm())\n    {\n        if (ConfigInitializeVmMode(Elevated, Config) < 0)\n        {\n            FATAL_ERROR(\"ConfigInitializeVmMode\");\n        }\n    }\n\n    if (Config.AutoMount && (Message->DrvfsMount != LxInitDrvfsMountNone))\n    {\n        ConfigMountDrvFsVolumes(Message->DrvFsVolumesBitmap, DefaultUid, Elevated, Config);\n    }\n\n    //\n    // If a hostname was specified in /etc/wsl.conf, use it.\n    //\n\n    if (Config.HostName.has_value())\n    {\n        Hostname = Config.HostName.value();\n        LOG_WARNING(\"hostname set to {} in {}\", Hostname.c_str(), CONFIG_FILE);\n    }\n\n    //\n    // Sanitize the hostname.\n    //\n    // N.B. If systemd is enabled, systemd-hostnamed will cleanup the\n    // hostname, which can lead to a disconnect if that doesn't match\n    // what we write in /etc/hostname & /etc/hosts, so to hostname needs\n    // to be cleaned up before being passed to systemd.\n    //\n    // N.B. While the Windows UI doesn't let the user set an invalid hostname\n    // (from systemd-hostnamed's perspective), it's possible to override that\n    // via Rename-Computer.\n\n    Hostname = wsl::shared::string::CleanHostname(Hostname);\n\n    //\n    // Update the host and domain name.\n    //\n\n    if (sethostname(Hostname.c_str(), Hostname.size()) < 0)\n    {\n        LOG_ERROR(\"sethostname({}) failed {}\", Hostname.c_str(), errno);\n        Hostname = wsl::shared::string::c_defaultHostName;\n        if (sethostname(Hostname.c_str(), Hostname.size()) < 0)\n        {\n            LOG_ERROR(\"sethostname({}) failed {}\", Hostname.c_str(), errno);\n        }\n    }\n\n    if (setenv(NAME_ENV, Hostname.c_str(), 1) < 0)\n    {\n        LOG_ERROR(\"setenv({}, {}) failed {}\", NAME_ENV, Hostname.c_str(), errno);\n    }\n\n    //\n    // Update the domain name.\n    //\n\n    if (setdomainname(Domainname, strlen(Domainname)) < 0)\n    {\n        LOG_ERROR(\"setdomainname({}) failed {}\", Domainname, errno);\n    }\n\n    //\n    // Generate and write /etc/hostname.\n    //\n\n    wil::unique_fd HostnameFd{TEMP_FAILURE_RETRY(creat(HOSTNAME_FILE_PATH, HOSTNAME_FILE_MODE))};\n    if (!HostnameFd)\n    {\n        LOG_ERROR(\"creat {} failed: {}\", HOSTNAME_FILE_PATH, errno);\n    }\n    else\n    {\n        try\n        {\n            auto FileContents = std::format(\"{}\\n\", Hostname);\n            if (UtilWriteStringView(HostnameFd.get(), FileContents) < 0)\n            {\n                LOG_ERROR(\"write failed {}\", errno);\n            }\n        }\n        CATCH_LOG()\n    }\n\n    HostnameFd.reset();\n\n    //\n    // Generate and write /etc/hosts.\n    //\n\n    if (Config.GenerateHosts)\n    {\n        wil::unique_fd HostsFd{TEMP_FAILURE_RETRY(creat(HOSTS_FILE_PATH, HOSTS_FILE_MODE))};\n        if (!HostsFd)\n        {\n            LOG_ERROR(\"creat {} failed {}\", HOSTS_FILE_PATH, errno);\n        }\n        else\n        {\n            try\n            {\n                auto FileContents = std::format(HostsFileFormatString, Hostname.c_str(), Domainname, Hostname.c_str(), WindowsHosts);\n                if (UtilWriteStringView(HostsFd.get(), FileContents) < 0)\n                {\n                    LOG_ERROR(\"write failed {}\", errno);\n                }\n            }\n            CATCH_LOG()\n        }\n    }\n    else\n    {\n        LOG_WARNING(\"{} updating disabled in {}\", HOSTS_FILE_PATH, CONFIG_FILE);\n    }\n\n    //\n    // Store the distribution name.\n    //\n\n    if (setenv(WSL_DISTRO_NAME_ENV, DistributionName, 1) < 0)\n    {\n        LOG_ERROR(\"setenv({}, {}, 1) failed {}\", WSL_DISTRO_NAME_ENV, DistributionName, errno);\n    }\n\n    //\n    // Run the Plan 9 server. This requires a DrvFs mount for the socket file,\n    // so either fstab or automount must be enabled to have a chance for the\n    // mount to be available.\n    //\n    // N.B. Failure to start the server is non-fatal.\n    //\n\n    unsigned int Plan9Port = LX_INIT_UTILITY_VM_INVALID_PORT;\n    if ((WI_IsFlagClear(Config.FeatureFlags.value(), LxInitFeatureDisable9pServer)) && (Config.Plan9Enabled) &&\n        (Config.AutoMount || Config.MountFsTab))\n    {\n        std::tie(Plan9Port, Config.Plan9ControlChannel) = StartPlan9Server(Plan9SocketPath, Config);\n    }\n\n    //\n    // If the root filesystem is compressed, log a warning.\n    //\n\n    if (WI_IsFlagSet(Config.FeatureFlags.value(), LxInitFeatureRootfsCompressed))\n    {\n        LOG_WARNING(\"{} root file system is compressed, performance may be severely impacted.\", DistributionName);\n    }\n\n    //\n    // Update the timezone.\n    //\n\n    UpdateTimezone(Timezone, Config);\n\n    if (Config.BootInit)\n    {\n        try\n        {\n            // Create the /run/user bind mount.\n            // This mount is required because systemd will mount a tmpfs on each /run/user/<uid> folder\n            // so /run/user need to be in the global mount namespace so both elevated and non elevated processes see it.\n            const auto UserMountTarget = Config.DrvFsPrefix + WSLG_SHARED_FOLDER \"/run/user\";\n            THROW_LAST_ERROR_IF(UtilMkdirPath(UserMountTarget.c_str(), 0755) < 0);\n            THROW_LAST_ERROR_IF(UtilMount(UserMountTarget.c_str(), RUN_FOLDER \"/\" USER_MOUNT_FOLDER, nullptr, MS_BIND, nullptr) < 0)\n        }\n        CATCH_LOG();\n    }\n\n    //\n    // Create a listening hvsocket for interop if the feature is enabled.\n    //\n\n    wil::unique_fd ListenSocket{};\n    sockaddr_vm SocketAddress{};\n    if (UtilIsUtilityVm() && Config.InteropEnabled)\n    {\n        ListenSocket = UtilListenVsockAnyPort(&SocketAddress, 1);\n    }\n\n    //\n    // Send the config response to the service.\n    //\n\n    wsl::shared::MessageWriter<LX_INIT_CONFIGURATION_INFORMATION_RESPONSE> Response(LxInitMessageInitializeResponse);\n    Response->Plan9Port = Plan9Port;\n    Response->DefaultUid = DefaultUid;\n    Response->InteropPort = ListenSocket ? SocketAddress.svm_port : LX_INIT_UTILITY_VM_INVALID_PORT;\n    Response->SystemdEnabled = Config.BootInit;\n\n    struct stat PidNamespaceInfo = {};\n    THROW_LAST_ERROR_IF(stat(\"/proc/self/ns/pid\", &PidNamespaceInfo));\n    Response->PidNamespace = PidNamespaceInfo.st_ino;\n    static_assert(sizeof(Response->PidNamespace) == sizeof(PidNamespaceInfo.st_ino));\n\n    auto [Flavor, Version] = UtilReadFlavorAndVersion(\"/etc/os-release\");\n    if (Flavor.has_value())\n    {\n        Response.WriteString(Response->FlavorIndex, Flavor->c_str());\n    }\n\n    if (Version.has_value())\n    {\n        Response.WriteString(Response->VersionIndex, Version->c_str());\n    }\n\n    Channel.SendMessage<LX_INIT_CONFIGURATION_INFORMATION_RESPONSE>(Response.Span());\n\n    //\n    // Accept the interop connection.\n    //\n\n    wsl::shared::SocketChannel InteropChannel;\n    if (ListenSocket)\n    {\n        InteropChannel = {UtilAcceptVsock(ListenSocket.get(), SocketAddress, INTEROP_TIMEOUT_MS), \"Interop\"};\n    }\n\n    //\n    // Create a thread to handle interop requests.\n    //\n\n    InteropServer InteropServer;\n    if (InteropServer.Create() < 0)\n    {\n        FATAL_ERROR(\"Could not create init interop server\");\n    }\n\n    //\n    // If init is not running as pid 1, create a symlink to the interop server that was created.\n    //\n\n    if (Config.InitPid.has_value())\n    {\n        try\n        {\n            std::string LinkPath = std::format(WSL_INTEROP_SOCKET_FORMAT, WSL_TEMP_FOLDER, 1, WSL_INTEROP_SOCKET);\n            if (symlink(InteropServer.Path(), LinkPath.c_str()) < 0)\n            {\n                LOG_ERROR(\"symlink({}, {}) failed {}\", InteropServer.Path(), LinkPath.c_str(), errno);\n            }\n        }\n        CATCH_LOG()\n    }\n\n    UtilCreateWorkerThread(\n        \"Interop\", [InteropChannel = std::move(InteropChannel), InteropServer = std::move(InteropServer), Elevated, &Config]() mutable {\n            std::vector<gsl::byte> Buffer;\n            for (;;)\n            {\n                wsl::shared::SocketChannel ClientChannel{InteropServer.Accept(), \"InteropServer\"};\n                if (ClientChannel.Socket() < 0)\n                {\n                    continue;\n                }\n\n                auto [Message, Span] = ClientChannel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\n                if (Message == nullptr)\n                {\n                    continue;\n                }\n\n                ConfigHandleInteropMessage(ClientChannel, InteropChannel, Elevated, Span, Message, Config);\n            }\n        });\n\n    //\n    // If there was a command specified in /etc/wsl.conf, run it in a child process.\n    //\n\n    if (Config.BootCommand.has_value())\n    {\n        UtilCreateChildProcess(\"BootCommand\", [Command = Config.BootCommand.value(), SavedSignals = g_SavedSignalActions]() {\n            //\n            // Restore default signal dispositions for the child process.\n            //\n\n            THROW_LAST_ERROR_IF(UtilSetSignalHandlers(SavedSignals, false) < 0);\n            THROW_LAST_ERROR_IF(UtilRestoreBlockedSignals() < 0);\n\n            execl(\"/bin/sh\", \"sh\", \"-c\", Command.c_str(), nullptr);\n            LOG_ERROR(\"execl() failed, {}\", errno);\n        });\n    }\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nint ConfigInitializeVmMode(bool Elevated, wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine sets up VM Mode specific devices and mounts.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    //\n    // Move temporary mounts created by mini_init to their final locations.\n    //\n    // N.B. Failure to mount these is not fatal.\n    //\n\n    for (auto& share : g_gpuShares)\n    {\n        try\n        {\n            auto variable = LX_WSL2_GPU_SHARE_ENV + std::string{share.Name};\n            auto tempMount = RemoveMountAndEnvironmentOnScopeExit(variable.c_str());\n            if (tempMount && Config.GpuEnabled)\n            {\n                tempMount.MoveMount(share.MountPoint);\n            }\n        }\n        CATCH_LOG()\n    }\n\n    if (Config.GpuEnabled)\n    {\n        ConfigApplyWindowsLibPath(Config);\n    }\n\n    try\n    {\n        auto tempMount = RemoveMountAndEnvironmentOnScopeExit(LX_WSL2_CROSS_DISTRO_ENV);\n        if (tempMount)\n        {\n            const auto target = Config.DrvFsPrefix + SHARED_MOUNT_FOLDER;\n            if (tempMount.MoveMount(target.c_str()))\n            {\n                ConfigCreateResolvConfSymlink(Config);\n            }\n        }\n    }\n    CATCH_LOG()\n\n    try\n    {\n        auto tempMount = RemoveMountAndEnvironmentOnScopeExit(LX_WSL2_SYSTEM_DISTRO_SHARE_ENV);\n        if (tempMount)\n        {\n            const auto target = Config.DrvFsPrefix + WSLG_SHARED_FOLDER;\n            if (!tempMount.MoveMount(target.c_str()))\n            {\n                Config.GuiAppsEnabled = false;\n            }\n            else\n            {\n                Config.GuiAppsEnabled = true;\n\n                //\n                // Create a bind mount of the shared WSLg path at the expected location for x11 clients.\n                //\n                // N.B. If using distro init, this is done after waiting for the distro init to finish booting\n                // since that will typically clear the /tmp directory.\n                //\n\n                ConfigInitializeX11(Config);\n\n                //\n                // Add environment variables to support GUI applications.\n                //\n\n                for (const auto& var : ConfigGetWslgEnvironmentVariables(Config))\n                {\n                    if (setenv(var.first.c_str(), var.second.c_str(), 1) < 0)\n                    {\n                        LOG_ERROR(\"setenv({}, {}) failed {}\", var.first.c_str(), var.second.c_str(), errno);\n                    }\n                }\n            }\n        }\n    }\n    CATCH_LOG()\n\n    try\n    {\n        auto tempMount = RemoveMountAndEnvironmentOnScopeExit(LX_WSL2_KERNEL_MODULES_MOUNT_ENV);\n        if (tempMount)\n        {\n            auto target = getenv(LX_WSL2_KERNEL_MODULES_PATH_ENV);\n            if (target)\n            {\n                unsetenv(LX_WSL2_KERNEL_MODULES_PATH_ENV);\n                tempMount.MoveMount(target);\n            }\n        }\n    }\n    CATCH_LOG()\n\n    //\n    // Change the permission of some devtmpfs devices to be more permissive.\n    //\n    // N.B. These devices may not be present with a custom kernel config.\n    //\n\n    for (const auto* Device : {\"/dev/fuse\", \"/dev/net/tun\"})\n    {\n        if ((chmod(Device, 0666) < 0) && (errno != ENOENT))\n        {\n            LOG_ERROR(\"chmod({}, 0666) failed {}\", Device, errno);\n            return -1;\n        }\n    }\n\n    //\n    // Open a file descriptor to the current mount namespace.\n    //\n\n    wil::unique_fd Namespace{UtilOpenMountNamespace()};\n    if (!Namespace)\n    {\n        return -1;\n    }\n\n    if (Elevated)\n    {\n        g_ElevatedMountNamespace = Namespace.release();\n    }\n    else\n    {\n        g_NonElevatedMountNamespace = Namespace.release();\n    }\n\n    return 0;\n}\n\nint ConfigInitializeWsl(void)\n\n/*++\n\nRoutine Description:\n\n    This routine sets up WSL-specific devices and mounts.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    unsigned int Index;\n    int Result;\n\n    for (Index = 0; Index < COUNT_OF(LxssStartupWsl); Index += 1)\n    {\n        Result = ConfigInitializeEntry(&LxssStartupWsl[Index]);\n        if (Result < 0)\n        {\n            goto InitializeWslExit;\n        }\n    }\n\n    //\n    // Initialize the serial device entries.\n    //\n\n    for (Index = INIT_DEV_TTY_MINOR_NUMBER_FIRST_SERIAL; Index < INIT_DEV_TTY_MINOR_NUMBER_MAX_SERIAL; Index += 1)\n    {\n        auto TtySPath = std::format(INIT_DEV_TTY_SERIAL_FORMAT, (Index - INIT_DEV_TTY_MINOR_NUMBER_FIRST_SERIAL));\n\n        Result = mknod(TtySPath.c_str(), INIT_DEV_TTY_SERIAL_MODE, makedev(INIT_DEV_TTY_MAJOR_NUMBER, Index));\n        if (Result < 0)\n        {\n            FATAL_ERROR(\"mknod({}) failed {}\", TtySPath, errno);\n        }\n\n        Result = chown(TtySPath.c_str(), INIT_DEV_TTY_SERIAL_UID, INIT_DEV_TTY_SERIAL_GID);\n        if (Result < 0)\n        {\n            FATAL_ERROR(\"chown({}) failed {}\", TtySPath, errno);\n        }\n    }\n\n    Result = 0;\n\nInitializeWslExit:\n    return Result;\n}\n\nint ConfigInitializeEntry(PCINIT_STARTUP_ANY AnyEntry)\n\n/*++\n\nRoutine Description:\n\n    This routine creates an init startup entry.\n\nArguments:\n\n    AnyEntry - Supplies the startup entry to create.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    PCINIT_STARTUP_DIRECTORY Directory;\n    PCINIT_STARTUP_FILE File;\n    PCINIT_STARTUP_MOUNT Mount;\n    PCINIT_STARTUP_NODE Node;\n    int Result;\n    PCINIT_STARTUP_SYMBOLIC_LINK Symlink;\n\n    switch (AnyEntry->Type)\n    {\n    case InitStartupTypeDirectory:\n        Directory = &AnyEntry->u.Directory;\n        Result = UtilMkdir(Directory->Path, Directory->Security.Mode);\n        if (Result < 0)\n        {\n            FATAL_ERROR(\"Failed to create {} {}\", Directory->Path, errno);\n        }\n\n        if (errno != EEXIST)\n        {\n            Result = chown(Directory->Path, Directory->Security.Uid, Directory->Security.Gid);\n            if (Result < 0)\n            {\n                FATAL_ERROR(\"Failed to chown {} {}\", Directory->Path, errno);\n            }\n        }\n\n        break;\n\n    case InitStartupTypeMount:\n        Mount = &AnyEntry->u.Mount;\n        Result = mount(Mount->DeviceName, Mount->MountLocation, Mount->FileSystemType, Mount->Flags & ~MS_SHARED, Mount->MountOptions);\n        if (Result < 0 && !Mount->IgnoreFailure)\n        {\n            FATAL_ERROR(\"Failed to mount {} at {} as {} {}\", Mount->DeviceName, Mount->MountLocation, Mount->FileSystemType, errno);\n        }\n\n        // N.B. The shared flag must be done in a followup mount() call\n        if (WI_IsFlagSet(Mount->Flags, MS_SHARED))\n        {\n            Result = mount(nullptr, Mount->MountLocation, nullptr, MS_SHARED, nullptr);\n            if (Result < 0 && !Mount->IgnoreFailure)\n            {\n                FATAL_ERROR(\"Failed to make shared mount {} {}\", Mount->MountLocation, errno);\n            }\n        }\n\n        break;\n\n    case InitStartupTypeNode:\n        Node = &AnyEntry->u.Node;\n        Result = mknod(Node->Path, Node->Security.Mode, makedev(Node->MajorNumber, Node->MinorNumber));\n        if (Result < 0)\n        {\n            FATAL_ERROR(\"Failed to create {} {}\", Node->Path, errno);\n        }\n\n        Result = chown(Node->Path, Node->Security.Uid, Node->Security.Gid);\n        if (Result < 0)\n        {\n            FATAL_ERROR(\"Failed to chown {} {}\", Node->Path, errno);\n        }\n\n        break;\n\n    case InitStartupTypeSymlink:\n        Symlink = &AnyEntry->u.Symlink;\n        Result = symlink(Symlink->Target, Symlink->Source);\n        if ((Result < 0) && (errno != EEXIST))\n        {\n            FATAL_ERROR(\"Failed to create {} -> {} {}\", Symlink->Source, Symlink->Target, errno);\n        }\n\n        break;\n\n    case InitStartupTypeFile:\n        File = &AnyEntry->u.File;\n        Result = TEMP_FAILURE_RETRY(creat(File->FileName, File->Mode));\n        if ((Result < 0) && (errno != EEXIST))\n        {\n            FATAL_ERROR(\"Failed to create {} {}\", File->FileName, errno);\n            goto InitializeEntryExit;\n        }\n\n        break;\n\n    default:\n        FATAL_ERROR(\"Unsupported Type {}\", AnyEntry->Type);\n    }\n\n    Result = 0;\n\nInitializeEntryExit:\n    return Result;\n}\n\nvoid ConfigCreateResolvConfSymlink(const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine ensures the /etc/resolv.conf symlink exists for WSL2.\n\nArguments:\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    if (!UtilIsUtilityVm())\n    {\n        return;\n    }\n\n    if (!Config.GenerateResolvConf)\n    {\n        LOG_WARNING(\"{} updating disabled in {}\", RESOLV_CONF_FILE_PATH, CONFIG_FILE);\n\n        //\n        // Ensure that the symlink between /etc/resolv.conf -> /mnt/wsl/resolv.conf is removed\n        //\n\n        ConfigReconfigureResolvConfSymlink(Config);\n\n        return;\n    }\n\n    //\n    // Create a /etc/resolv.conf symlink to the file that is automatically\n    // generated by WSL Core.\n    //\n\n    try\n    {\n        std::string Target = std::format(\"{}{}/{}\", Config.DrvFsPrefix, SHARED_MOUNT_FOLDER, RESOLV_CONF_FILE_NAME);\n\n        remove(RESOLV_CONF_FILE_PATH);\n        if (symlink(Target.c_str(), RESOLV_CONF_FILE_PATH) < 0)\n        {\n            LOG_ERROR(\"symlink({}, {}) failed {}\", Target, RESOLV_CONF_FILE_PATH, errno);\n        }\n    }\n    CATCH_LOG()\n}\n\nint ConfigCreateResolvConfSymlinkTarget(void)\n\n/*++\n\nRoutine Description:\n\n    If the /etc/resolv.conf file is a symlink, this routine will recursively\n    create the directory structure and target of the symlink.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    bool RestoreCwd = false;\n    int Result;\n    char SymlinkBuffer[PATH_MAX + 1];\n    int SymlinkFd = -1;\n\n    //\n    // If /etc/resolv.conf is a symlink, recursively create the directory\n    // structure.  If the file is not a symlink, return success. If the symlink\n    // does not exist, recreate it.\n    // TODO: move to std::filesystem\n    //\n\n    Result = readlink(RESOLV_CONF_FILE_PATH, SymlinkBuffer, (sizeof(SymlinkBuffer) - 1));\n\n    if (Result < 0)\n    {\n        if (errno == EINVAL)\n        {\n            Result = 0;\n            goto CreateResolvConfSymlinkTargetExit;\n        }\n        else if (errno == ENOENT)\n        {\n            Result = symlink(RESOLV_CONF_SYMLINK_TARGET, RESOLV_CONF_FILE_PATH);\n            if (Result < 0)\n            {\n                LOG_ERROR(\"symlink({}, {}) failed {}\", RESOLV_CONF_SYMLINK_TARGET, RESOLV_CONF_FILE_PATH, errno);\n\n                goto CreateResolvConfSymlinkTargetExit;\n            }\n\n            Result = readlink(RESOLV_CONF_FILE_PATH, SymlinkBuffer, (sizeof(SymlinkBuffer) - 1));\n        }\n\n        if (Result < 0)\n        {\n            LOG_ERROR(\"readlink({}) failed {}\", RESOLV_CONF_FILE_PATH, errno);\n            goto CreateResolvConfSymlinkTargetExit;\n        }\n    }\n\n    //\n    // Null-terminate the symlink buffer string.\n    //\n\n    SymlinkBuffer[Result] = '\\0';\n\n    //\n    // Set current working directory to the folder that contains the resolv.conf\n    // symlink.\n    //\n    // N.B. This is so creating the target of the symlinks will work since they\n    //      may use relative symlinks.\n    //\n\n    Result = chdir(ETC_FOLDER);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"chdir {} failed {}\", ETC_FOLDER, errno);\n        goto CreateResolvConfSymlinkTargetExit;\n    }\n\n    RestoreCwd = true;\n\n    //\n    // Check if the symlink target exists, if the file exists return success. If\n    // it does not exist, recursively create the directory structure.\n    //\n\n    Result = access(SymlinkBuffer, W_OK);\n    if (Result == 0)\n    {\n        goto CreateResolvConfSymlinkTargetExit;\n    }\n    else if (errno != ENOENT)\n    {\n        Result = -1;\n        LOG_ERROR(\"access {} W_OK failed {}\", SymlinkBuffer, errno);\n        goto CreateResolvConfSymlinkTargetExit;\n    }\n\n    //\n    // Recursively create the directory structure.\n    //\n\n    Result = UtilMkdirPath(SymlinkBuffer, RESOLV_CONF_DIRECTORY_MODE, true);\n\n    //\n    // The symlink target itself does not need to be created here, as it will\n    // be created when the symlink is opened later.\n    //\n\n    Result = 0;\n\nCreateResolvConfSymlinkTargetExit:\n    if (RestoreCwd != false)\n    {\n        if (chdir(DEFAULT_CWD) < 0)\n        {\n            LOG_ERROR(\"chdir({}) failed {}\", DEFAULT_CWD, errno);\n        }\n    }\n\n    if (SymlinkFd != -1)\n    {\n        CLOSE(SymlinkFd);\n    }\n\n    return Result;\n}\n\nint ConfigReconfigureResolvConfSymlink(const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    Checks the value of the Config.GenerateResolvConf and removes the symlink (if one has been set previously)\n\nArguments:\n\n    Config - Supplies the Distribution Configuration.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    int Result;\n    char SymLinkBuffer[PATH_MAX + 1];\n\n    // check if /etc/resolv.conf is symlink\n    // TODO: move to std::filesystem.\n    Result = readlink(RESOLV_CONF_FILE_PATH, SymLinkBuffer, (sizeof(SymLinkBuffer) - 1));\n    if (Result < 0)\n    {\n        if (errno == EINVAL || errno == ENOENT)\n        {\n            Result = 0;\n        }\n        else\n        {\n            LOG_ERROR(\"readlink({}) failed {}\", RESOLV_CONF_FILE_PATH, errno);\n        }\n\n        return Result;\n    }\n\n    // null-terminate the symlink buffer string\n    SymLinkBuffer[Result] = '\\0';\n\n    // recreate the location of [automount root]/wsl/resolv.conf\n    auto target = Config.DrvFsPrefix + std::string{RESOLV_CONF_SYMLINK_WSL_MOUNT_SUFFIX};\n\n    // check if the symlink is pointing to /mnt/wsl/resolv.conf created by wslcore\n    // do not interfere with symlinks set by other networking management processes (ie. resolvconf, NetworkManager, etc.)\n    if (std::string_view{SymLinkBuffer} == target)\n    {\n        // generateResolveConf setting has changed, remove symlink and restore if specified\n        Result = remove(RESOLV_CONF_FILE_PATH);\n        if (Result < 0)\n        {\n            LOG_ERROR(\"remove({}) failed {}\", RESOLV_CONF_FILE_PATH, errno);\n        }\n    }\n\n    return Result;\n}\n\nEnvironmentBlock ConfigCreateEnvironmentBlock(const PLX_INIT_CREATE_PROCESS_COMMON Common, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine creates the environment block to be used when launching a new\n    process.\n\nArguments:\n\n    Common - Supplies a pointer to the common create process message data.\n\nReturn Value:\n\n    An environment block.\n\n--*/\n\n{\n    //\n    // Initialize the environment block.\n    //\n\n    auto Buffer = (char*)Common + Common->EnvironmentOffset;\n    EnvironmentBlock Environment(Buffer, Common->EnvironmentCount);\n\n    //\n    // Add environment variables to support GUI applications.\n    //\n    // N.B. This must be done before processing WSLENV so the user can override\n    //      these values if desired.\n    //\n\n    if (Config.GuiAppsEnabled)\n    {\n        for (const auto& Var : ConfigGetWslgEnvironmentVariables(Config))\n        {\n            Environment.AddVariable(Var.first, Var.second);\n        }\n    }\n\n    //\n    // Add each Windows environment variable from WSLENV to the environment block.\n    //\n    // N.B. Failure to parse WSLENV is non-fatal.\n    //\n\n    Buffer = (char*)Common + Common->NtEnvironmentOffset;\n    auto NtEnvironment = UtilParseWslEnv(Buffer);\n    if (!NtEnvironment.empty())\n    {\n        for (size_t Index = 0;;)\n        {\n            Buffer = NtEnvironment.data() + Index;\n            auto Length = strnlen(Buffer, NtEnvironment.size() - Index);\n            if (Length == 0)\n            {\n                break;\n            }\n\n            auto Value = strchr(Buffer, '=');\n            if (Value != NULL)\n            {\n                *Value = '\\0';\n                Value += 1;\n                Environment.AddVariable(Buffer, Value);\n            }\n\n            Index += Length + 1;\n        }\n    }\n\n    //\n    // Add the GPU library to the $PATH variable. This is done because some GPU\n    // vendors ship small utilities along with their usermode drivers.\n    //\n\n    if (UtilIsUtilityVm())\n    {\n        if (Config.AppendGpuLibPath && Config.GpuEnabled)\n        {\n            ConfigAppendToPath(Environment, LXSS_LIB_PATH);\n        }\n    }\n\n    //\n    // Translate the NT path into a list of Linux paths and add it to the PATH\n    // environment variable. Individual path elements that fail to translate\n    // are skipped.\n    //\n    // N.B. Failure to append the NT path is non-fatal.\n    //\n\n    Buffer = reinterpret_cast<char*>(Common) + Common->NtPathOffset;\n    if ((Config.InteropAppendWindowsPath) && (*Buffer != '\\0'))\n    {\n        ConfigAppendNtPath(Environment, Buffer);\n    }\n\n    return Environment;\n}\n\nstd::set<std::pair<unsigned int, std::string>> ConfigGetMountedDrvFsVolumes(void)\n\n/*++\n\nRoutine Description:\n\n    This routine returns a bitmap indicating which Windows drive letters are\n    already mounted.\n\n    N.B. If this function fails, it just returns 0, since failure is not\n         considered fatal here.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    The bitmap of mounted drives.\n\n--*/\n\n{\n    std::set<std::pair<unsigned int, std::string>> MountPoints;\n    mountutil::MountEnum MountEnum;\n    while (MountEnum.Next())\n    {\n        //\n        // Do not consider bind mounts.\n        //\n\n        if (strcmp(MountEnum.Current().Root, \"/\") != 0)\n        {\n            continue;\n        }\n\n        //\n        // Extract the correct mount source depending on whether this is 9p\n        // (WSL2) or DrvFs (WSL1). For virtio-9p, the entry's mount source\n        // will just be \"drvfs\" or \"drvfsa\", so it must be extracted from the\n        // aname (this works for hvsocket-9p too).\n        // N.B. UtilParsePlan9MountSource always returns a canonicalized path.\n        //\n\n        std::string MountSource;\n        if (strcmp(MountEnum.Current().FileSystemType, PLAN9_FS_TYPE) == 0)\n        {\n            MountSource = UtilParsePlan9MountSource(MountEnum.Current().SuperOptions);\n        }\n        else if (strcmp(MountEnum.Current().FileSystemType, DRVFS_FS_TYPE) == 0)\n        {\n            MountSource = MountEnum.Current().Source;\n            UtilCanonicalisePathSeparator(MountSource, PATH_SEP_NT);\n        }\n        else if (strcmp(MountEnum.Current().FileSystemType, VIRTIO_FS_TYPE) == 0)\n        {\n            MountSource = QueryVirtiofsMountSource(MountEnum.Current().Source);\n        }\n        else\n        {\n            continue;\n        }\n\n        if (MountSource.empty())\n        {\n            continue;\n        }\n\n        auto letter = ConfigGetDriveLetter(MountSource);\n        if (letter.has_value())\n        {\n            MountPoints.emplace(letter.value(), MountEnum.Current().MountPoint);\n        }\n    }\n\n    return MountPoints;\n}\n\nstd::vector<std::pair<std::string, std::string>> ConfigGetWslgEnvironmentVariables(const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine returns the environment variables needed by WSLg.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    A list of environment variables.\n\n--*/\n\n{\n    std::string WaylandPath = std::format(\"{}{}/{}\", Config.DrvFsPrefix, WSLG_SHARED_FOLDER, WAYLAND_RUNTIME_DIR);\n    std::string PulsePath = std::format(\"unix:{}{}/{}\", Config.DrvFsPrefix, WSLG_SHARED_FOLDER, PULSE_SERVER_NAME);\n    return std::vector<std::pair<std::string, std::string>>{\n        {XDG_RUNTIME_DIR_ENV, std::move(WaylandPath)},\n        {X11_DISPLAY_ENV, X11_DISPLAY_VALUE},\n        {WAYLAND_DISPLAY_ENV, WAYLAND_DISPLAY_VALUE},\n        {PULSE_SERVER_ENV, std::move(PulsePath)},\n        {LX_WSL2_GUI_APP_SUPPORT_ENV, \"1\"}};\n}\n\nvoid ConfigInitializeCgroups(wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This parses the /proc/cgroups file and mounts enabled cgroups.\n\n    N.B. This routine was modeled after the cgroupfs-mount script.\n\nArguments:\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    std::vector<std::string> DisabledControllers;\n\n    if (UtilIsUtilityVm())\n    {\n        if (Config.CGroup == WslDistributionConfig::CGroupVersion::v1)\n        {\n            auto commandLine = UtilReadFileContent(\"/proc/cmdline\");\n            auto position = commandLine.find(CGROUPS_NO_V1);\n            if (position != std::string::npos)\n            {\n                auto list = commandLine.substr(position + sizeof(CGROUPS_NO_V1) - 1);\n                auto end = list.find_first_of(\" \\n\");\n                if (end != std::string::npos)\n                {\n                    list = list.substr(0, end);\n                }\n\n                if (list == \"all\")\n                {\n                    LOG_WARNING(\"Distribution has cgroupv1 enabled, but kernel command line has {}all. Falling back to cgroupv2\", CGROUPS_NO_V1);\n                    Config.CGroup = WslDistributionConfig::CGroupVersion::v2;\n                }\n                else\n                {\n                    DisabledControllers = wsl::shared::string::Split(list, ',');\n                }\n            }\n        }\n\n        if (Config.CGroup == WslDistributionConfig::CGroupVersion::v1)\n        {\n            THROW_LAST_ERROR_IF(mount(\"tmpfs\", CGROUP_MOUNTPOINT, \"tmpfs\", (MS_NOSUID | MS_NODEV | MS_NOEXEC), \"mode=755\") < 0);\n        }\n\n        const auto Target = Config.CGroup == WslDistributionConfig::CGroupVersion::v1 ? CGROUP_MOUNTPOINT \"/unified\" : CGROUP_MOUNTPOINT;\n        THROW_LAST_ERROR_IF(\n            UtilMount(CGROUP2_DEVICE, Target, CGROUP2_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), \"nsdelegate\") < 0);\n\n        if (Config.CGroup == WslDistributionConfig::CGroupVersion::v2)\n        {\n            return;\n        }\n    }\n    else\n    {\n        THROW_LAST_ERROR_IF(mount(\"tmpfs\", CGROUP_MOUNTPOINT, \"tmpfs\", (MS_NOSUID | MS_NODEV | MS_NOEXEC), \"mode=755\") < 0);\n    }\n\n    //\n    // Mount cgroup v1 when running in WSL1 mode or when a WSL2 distro has automount.cgroups=v1 specified.\n    //\n    // Open the /proc/cgroups file and parse each line, ignoring malformed\n    // lines and disabled controllers.\n    //\n\n    wil::unique_file Cgroups{fopen(CGROUPS_FILE, \"r\")};\n    THROW_LAST_ERROR_IF(!Cgroups);\n\n    ssize_t BytesRead;\n    char* Line = nullptr;\n    auto LineCleanup = wil::scope_exit([&]() { free(Line); });\n    size_t LineLength = 0;\n    while ((BytesRead = getline(&Line, &LineLength, Cgroups.get())) != -1)\n    {\n        char* Subsystem = nullptr;\n        bool Enabled = false;\n        if ((UtilParseCgroupsLine(Line, &Subsystem, &Enabled) < 0) || (Enabled == false) ||\n            std::find(DisabledControllers.begin(), DisabledControllers.end(), Subsystem) != DisabledControllers.end())\n\n        {\n            continue;\n        }\n\n        auto Target = std::format(\"{}/{}\", CGROUP_MOUNTPOINT, Subsystem);\n        THROW_LAST_ERROR_IF(\n            UtilMount(CGROUP_DEVICE, Target.c_str(), CGROUP_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), Subsystem) < 0);\n    }\n}\nCATCH_LOG()\n\nstd::optional<unsigned int> ConfigGetDriveLetter(std::string_view MountSource)\n\n/*++\n\nRoutine Description:\n\n    This routine extracts the drive letter from a mount source.\n\n    N.B. Recognized formats are \"X:\", \"X:\\134\" (the escape sequence for a\n         backslash used in /proc/self/mounts) and \"X:/\", where X may be\n         lowercase or uppercase.\n\nArguments:\n\n    MountSource - Supplies the mount source.\n\nReturn Value:\n\n    The drive letter index as an integer (0 is 'A', 1 is 'B' and so on).\n\n--*/\n\n{\n    //\n    // The length must be 2 or 3 and the second character must always be ':'.\n    //\n\n    if ((MountSource.length() < 2) || (MountSource.length() > 3) || (MountSource[1] != ':'))\n    {\n        return {};\n    }\n\n    //\n    // If there are three characters, the third one must be a path separator.\n    //\n\n    if (MountSource.length() == 3 && MountSource[2] != '/' && MountSource[2] != '\\\\')\n    {\n        return {};\n    }\n\n    //\n    // Extract the drive letter from the first character.\n    //\n\n    if ((MountSource[0] >= 'a') && (MountSource[0] <= 'z'))\n    {\n        return MountSource[0] - 'a';\n    }\n    else if ((MountSource[0] >= 'A') && (MountSource[0] <= 'Z'))\n    {\n        return MountSource[0] - 'A';\n    }\n\n    return {};\n}\n\nvoid ConfigMountDrvFsVolumes(unsigned int DrvFsVolumes, uid_t OwnerUid, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine mounts the specified DrvFs volumes.\n\nArguments:\n\n    DrvFsVolumes - Supplies a bitmap that contains the indices of DrvFs volumes\n        to mount.\n\n    OwnerUid - Supplies the owner uid to use.\n\n    Admin - Supplies an optional boolean to specify if the admin or non-admin\n        server should be used.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    if (DrvFsVolumes == 0)\n    {\n        return;\n    }\n\n    //\n    // If fstab was processed, exclude already mounted volumes.\n    //\n\n    std::set<std::pair<unsigned int, std::string>> MountedVolumes;\n    if (Config.MountFsTab)\n    {\n        MountedVolumes = ConfigGetMountedDrvFsVolumes();\n    }\n\n    //\n    // Attempt to determine the owner gid to use.\n    //\n    // N.B. If no entry is found, root is used as the owner gid.\n    //\n\n    auto OwnerGid = ROOT_GID;\n    auto Password = getpwuid(OwnerUid);\n    if (Password != nullptr)\n    {\n        OwnerGid = Password->pw_gid;\n    }\n\n    //\n    // Initialize the mount options.\n    //\n    // N.B. If the options weren't specified, ConfigDrvFsOptions will be an\n    //      empty string. Since DrvFs ignores empty mount options, the extra\n    //      comma on the end in that case is not a problem.\n    //\n\n    std::string Options =\n        std::format(\"noatime,uid={},gid={},{}\", OwnerUid, OwnerGid, Config.DrvFsOptions.has_value() ? Config.DrvFsOptions->c_str() : \"\");\n\n    //\n    // Iterate over the bitmap and attempt to create a DrvFs mount for each\n    // drive letter.\n    //\n    // N.B. __builtin_ffsll returns a one-based index.\n    //\n\n    char Source[] = DRVFS_SOURCE;\n    for (int Index = __builtin_ffsll(DrvFsVolumes); Index != 0; Index = __builtin_ffsll(DrvFsVolumes))\n    {\n        //\n        // Mask out the current Index.\n        //\n\n        Index -= 1;\n        DrvFsVolumes ^= (1 << Index);\n\n        //\n        // If this drive is already mounted on the same mountpoint, skip.\n        //\n\n        auto Target = std::format(\"{}{:c}\", Config.DrvFsPrefix, 'a' + Index);\n        if (MountedVolumes.contains(std::make_pair(Index, Target.c_str())))\n        {\n            LOG_WARNING(\"{} already mounted, skipping...\", Target);\n            continue;\n        }\n\n        //\n        // Create the target directory and attempt the mount.\n        //\n\n        if (UtilMkdir(Target.c_str(), DRVFS_TARGET_MODE) < 0)\n        {\n            continue;\n        }\n\n        Source[0] = 'A' + Index;\n        if (MountDrvfs(Source, Target.c_str(), Options.c_str(), Admin, Config) < 0)\n        {\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageDrvfsMountFailed(Source));\n        }\n    }\n}\nCATCH_LOG()\n\nstatic void ConfigApplyWindowsLibPath(const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine creates a file for the GNU loader to include in its\n    library search paths, located under /etc/ld.so.conf.d/.\n\n    After writing this file, /sbin/ldconfig will be invoked to update the cache.\n\n    N.B. Failures during this function are not fatal to instance start.\n\nArguments:\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    const char* const LdConfigArgv[] = {LDCONFIG_COMMAND, nullptr};\n\n    if (!Config.LinkOsLibs)\n    {\n        return;\n    }\n\n    wil::unique_fd Fd{TEMP_FAILURE_RETRY(open(WINDOWS_LD_CONF_FILE, (O_CREAT | O_RDWR | O_TRUNC), WINDOWS_LD_CONF_FILE_MODE))};\n    if (!Fd)\n    {\n        LOG_ERROR(\"open {} failed {}\", WINDOWS_LD_CONF_FILE, errno);\n        return;\n    }\n\n    std::string_view Buffer{WindowsLibSearchFileHeaderString};\n    if (UtilWriteStringView(Fd.get(), Buffer) < 0)\n    {\n        LOG_ERROR(\"write failed {}\", errno);\n        return;\n    }\n\n    Buffer = LXSS_LIB_PATH;\n    if (UtilWriteStringView(Fd.get(), Buffer) < 0)\n    {\n        LOG_ERROR(\"write failed {}\", errno);\n        return;\n    }\n\n    if (UtilCreateProcessAndWait(LdConfigArgv[0], LdConfigArgv) < 0)\n    {\n        LOG_ERROR(\"Processing ldconfig failed\");\n    }\n}\n\nvoid ConfigMountFsTab(bool Elevated)\n\n/*++\n\nRoutine Description:\n\n    This routine runs mount -a to process the /etc/fstab file.\n\n    N.B. Failures during this function are not fatal to instance start.\n\nArguments:\n\n    Elevated - True if the plan9 drvfs entries should use the elevated plan9 server.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    //\n    // Note: The WSL_DRVFS_ELEVATED_ENV variable is used because the interop server isn't running yet.\n    //\n\n    const char* const Argv[] = {MOUNT_COMMAND, MOUNT_FSTAB_ARG, nullptr};\n    if (UtilCreateProcessAndWait(Argv[0], Argv, nullptr, {{WSL_DRVFS_ELEVATED_ENV, Elevated ? \"1\" : \"0\"}}) < 0)\n    {\n        auto message = wsl::shared::Localization::MessageFstabMountFailed();\n        LOG_ERROR(\"{}\", message.c_str());\n\n        EMIT_USER_WARNING(std::move(message));\n    }\n}\n\nint ConfigRegisterBinfmtInterpreter(void)\n\n/*++\n\nRoutine Description:\n\n    This routine registers the binfmt extension for interop.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    //\n    // Register the interop binfmt extension.\n    //\n\n    wil::unique_fd Fd{TEMP_FAILURE_RETRY(open(BINFMT_MISC_REGISTER_FILE, O_WRONLY))};\n    if (!Fd)\n    {\n        LOG_ERROR(\"open \" BINFMT_MISC_REGISTER_FILE \" failed {}\", errno);\n        return -1;\n    }\n\n    std::string_view Buffer{BINFMT_INTEROP_REGISTRATION_STRING(LX_INIT_BINFMT_NAME) \"\\n\"};\n    int Result = UtilWriteStringView(Fd.get(), Buffer);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"binfmt registration failed {}\", errno);\n    }\n\n    return Result;\n}\n\nint ConfigRemountDrvFs(gsl::span<gsl::byte> Buffer, wsl::shared::SocketChannel& Channel, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This remounts DrvFs volumes in the appropriate mount namespace\n    and the result on the channel.\n\nArguments:\n\n    Buffer - Supplies a buffer to the LX_INIT_MOUNT_DRVFS message.\n\n    ResultChannel - Supplies the file descriptor to write the result to.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n{\n    Channel.SendResultMessage<int32_t>(ConfigRemountDrvFsImpl(Buffer, Config));\n\n    return 0;\n}\n\nint ConfigRemountDrvFsImpl(gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This remounts DrvFs volumes in the appropriate mount namespace.\n\nArguments:\n\n    Buffer - Supplies a buffer to the LX_INIT_MOUNT_DRVFS message.\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    //\n    // This method is only valid for VM mode.\n    //\n\n    if (!UtilIsUtilityVm())\n    {\n        return -1;\n    }\n\n    const auto* Message = gslhelpers::try_get_struct<LX_INIT_MOUNT_DRVFS>(Buffer);\n    if (!Message)\n    {\n        LOG_ERROR(\"Unexpected sizeof for LX_INIT_MOUNT_DRVFS: {}u\", Buffer.size());\n        return -1;\n    }\n\n    if (Message->Admin ? (g_ElevatedMountNamespace != -1) : (g_NonElevatedMountNamespace != -1))\n    {\n        LOG_ERROR(\"{} namespace already initialized\", Message->Admin ? \"Admin\" : \"Non-Admin\");\n        return -1;\n    }\n\n    //\n    // Read the mountinfo file for the namespace that is already configured.\n    // This contains all mounts from /etc/fstab as well as the initial drives\n    // that were mounted when the instance was created.\n    //\n\n    wil::unique_file MountInfo{fopen(MOUNT_INFO_FILE, \"r\")};\n    if (!MountInfo)\n    {\n        LOG_ERROR(\"fopen failed {}\", errno);\n        return -1;\n    }\n\n    std::string FileContents = UtilReadFile(MountInfo.get());\n\n    wil::unique_fd OriginalNamespace{UtilOpenMountNamespace()};\n    if (!OriginalNamespace)\n    {\n        return -1;\n    }\n\n    auto RestoreNamespace = wil::scope_exit([&]() {\n        if (setns(OriginalNamespace.get(), CLONE_NEWNS) < 0)\n        {\n            LOG_ERROR(\"restoring mount namespace failed {}\", errno);\n        }\n    });\n\n    //\n    // Configure the new mount namespace.\n    //\n\n    if (unshare(CLONE_NEWNS) < 0)\n    {\n        LOG_ERROR(\"unshare failed {}\", errno);\n        return -1;\n    }\n\n    wil::unique_fd NewNamespace{UtilOpenMountNamespace()};\n    if (!NewNamespace)\n    {\n        return -1;\n    }\n\n    if (Message->Admin)\n    {\n        g_ElevatedMountNamespace = NewNamespace.release();\n    }\n    else\n    {\n        g_NonElevatedMountNamespace = NewNamespace.release();\n    }\n\n    //\n    // Parse the mountinfo file get a list of all the drvfs mounts.\n    //\n\n    std::vector<MOUNT_ENTRY> DrvfsMounts;\n    for (char *Sp1, *Info = strtok_r(FileContents.data(), \"\\n\", &Sp1); Info != nullptr; Info = strtok_r(NULL, \"\\n\", &Sp1))\n    {\n        MOUNT_ENTRY MountEntry;\n        if (MountParseMountInfoLine(Info, &MountEntry) < 0)\n        {\n            return -1;\n        }\n\n        //\n        // Bind mounts which have a root other than / are currently not supported.\n        //\n        // TODO_LX: Support bind mounts.\n        //\n\n        if (strcmp(MountEntry.Root, \"/\") != 0)\n        {\n            continue;\n        }\n\n        if (strcmp(MountEntry.FileSystemType, PLAN9_FS_TYPE) == 0)\n        {\n\n            //\n            // Ensure that only drvfs mounts are re-mounted. This avoids unmounting sharefs mounts (used for mounting gpu libs and drivers).\n            //\n\n            auto Plan9Source = UtilParsePlan9MountSource(MountEntry.SuperOptions);\n            if (Plan9Source.empty() || !ConfigGetDriveLetter(Plan9Source).has_value())\n            {\n                continue;\n            }\n        }\n        else if (strcmp(MountEntry.FileSystemType, VIRTIO_FS_TYPE) != 0)\n        {\n            continue;\n        }\n\n        DrvfsMounts.emplace_back(std::move(MountEntry));\n    }\n\n    //\n    // Unmount the existing drvfs mounts in reverse order, then remount the new version.\n    //\n\n    for (auto ReverseIterator = DrvfsMounts.rbegin(); ReverseIterator != DrvfsMounts.rend(); ReverseIterator++)\n    {\n        const auto* MountPoint = (*ReverseIterator).MountPoint;\n        if (umount2(MountPoint, MNT_DETACH) < 0)\n        {\n            LOG_ERROR(\"umount2({}) failed {}\", MountPoint, errno);\n        }\n    }\n\n    std::bitset<32> volumesToMount(Message->VolumesToMount);\n    std::bitset<32> unreadableVolumes(Message->UnreadableVolumes);\n\n    std::string NewMountOptions;\n    for (const auto& MountEntry : DrvfsMounts)\n    {\n        if (strcmp(MountEntry.FileSystemType, PLAN9_FS_TYPE) == 0)\n        {\n            const char* NewSource = MountEntry.Source;\n            auto Plan9Source = UtilParsePlan9MountSource(MountEntry.SuperOptions);\n            if (Plan9Source.empty())\n            {\n                continue;\n            }\n\n            auto driveIndex = ConfigGetDriveLetter(Plan9Source);\n            if (driveIndex.has_value())\n            {\n                //\n                // This is drive mount. Remount only if the drive is actually readable.\n                //\n\n                if (unreadableVolumes[driveIndex.value()])\n                {\n                    //\n                    // This drive is not readable, don't try to mount it.\n                    //\n\n                    LOG_WARNING(\"Drvfs mount '{}' is not readable, skipping mount\", Plan9Source);\n                    continue;\n                }\n\n                volumesToMount[driveIndex.value()] = false;\n            }\n\n            //\n            // Construct new Plan9 mount options based on the existing mount.\n            //\n\n            NewMountOptions = MountEntry.MountOptions;\n            NewMountOptions += ',';\n            if (WSL_USE_VIRTIO_9P())\n            {\n                //\n                // Check if the existing mount is a drvfs mount that needs to be remounted.\n                //\n\n                auto Tag = Message->Admin ? LX_INIT_DRVFS_VIRTIO_TAG : LX_INIT_DRVFS_ADMIN_VIRTIO_TAG;\n                if (strcmp(MountEntry.Source, Tag) != 0)\n                {\n                    continue;\n                }\n\n                NewSource = Message->Admin ? LX_INIT_DRVFS_ADMIN_VIRTIO_TAG : LX_INIT_DRVFS_VIRTIO_TAG;\n            }\n\n            //\n            // Remove the transport-related mount options.\n            //\n\n            std::string_view SuperOptions = MountEntry.SuperOptions;\n            while (!SuperOptions.empty())\n            {\n                auto Option = UtilStringNextToken(SuperOptions, \",\");\n                if (wsl::shared::string::StartsWith(Option, \"trans=\") || wsl::shared::string::StartsWith(Option, \"rfd=\") ||\n                    wsl::shared::string::StartsWith(Option, \"wfd=\") || wsl::shared::string::StartsWith(Option, \"msize=\"))\n                {\n                    continue;\n                }\n\n                NewMountOptions += Option;\n                NewMountOptions += ',';\n            }\n\n            MountPlan9Share(NewSource, MountEntry.MountPoint, NewMountOptions.c_str(), Message->Admin);\n        }\n        else if (strcmp(MountEntry.FileSystemType, VIRTIO_FS_TYPE) == 0)\n        {\n            RemountVirtioFs(MountEntry.Source, MountEntry.MountPoint, MountEntry.MountOptions, Message->Admin);\n        }\n        else\n        {\n            LOG_ERROR(\"Unexpected fstype {}\", MountEntry.FileSystemType);\n        }\n    }\n\n    // It's possible that some drives are only visible to one namespace and not the other\n    // (for instance if only an elevated token has read access to a drive).\n    // If that's the case, those drives might not have been mounted previously, so\n    // mount any drive that hasn't been found in MountInfo.\n    if (Config.AutoMount)\n    {\n        ConfigMountDrvFsVolumes(volumesToMount.to_ulong(), Message->DefaultOwnerUid, Message->Admin, Config);\n    }\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nint ConfigSetMountNamespace(bool Elevated)\n\n/*++\n\nRoutine Description:\n\n    This routine sets the mount namespace of the caller.\n\nArguments:\n\n    Elevated - Supplies true if the client represents an elevated Windows process, false otherwise.\n\nReturn Value:\n\n    The file descriptor representing the mount namespace on success, -1 on failure.\n\n--*/\n\n{\n    if (!UtilIsUtilityVm())\n    {\n        return -1;\n    }\n\n    auto Namespace = Elevated ? g_ElevatedMountNamespace : g_NonElevatedMountNamespace;\n    if (Namespace == -1)\n    {\n        LOG_ERROR(\"{} namespace has not been initialized\", Elevated ? \"Admin\" : \"Non-Admin\");\n        return -1;\n    }\n\n    if (setns(Namespace, CLONE_NEWNS) < 0)\n    {\n        LOG_ERROR(\"setns failed {}\", errno);\n        return -1;\n    }\n\n    return Namespace;\n}\n\nvoid ConfigUpdateLanguage(EnvironmentBlock& Environment)\n\n/*++\n\nRoutine Description:\n\n    This routine queries the contents of the /etc/default/locale text file and\n    if present updates the $LANG environment variable in the environment block.\n\nArguments:\n\n    Environment - Supplies the environment block pointer to update.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    //\n    // Attempt to open the /etc/default/locale file. If the file does not exist\n    // then the $LANG environment variable will not be updated.\n    //\n    // N.B. This file is being opened by root. The only user-visible content\n    //      will be the contents of the last line of the file that contains\n    //      \"LANG=\".\n    //\n\n    wil::unique_file LocaleFile{fopen(LOCALE_FILE_PATH, \"r\")};\n    if (!LocaleFile)\n    {\n        if (errno != ENOENT)\n        {\n            LOG_ERROR(\"fopen({}) failed {}\", LOCALE_FILE_PATH, errno);\n        }\n\n        return;\n    }\n\n    // TODO: Move to std::regex\n\n    //\n    // Parse the file line-by-line looking for the \"LANG=\" string.\n    //\n\n    char* Line = nullptr;\n    auto freeLine = wil::scope_exit([&Line]() { free(Line); });\n\n    size_t LineLength = 0;\n    while (getline(&Line, &LineLength, LocaleFile.get()) != -1)\n    {\n        //\n        // Handle comments by replacing the first comment character with a null\n        // terminator, thus ending the string.\n        //\n\n        auto SpecialCharacter = strchr(Line, '#');\n        if (SpecialCharacter != nullptr)\n        {\n            *SpecialCharacter = '\\0';\n        }\n\n        //\n        // If the current line contains the \"LANG=\" string, update the\n        // environment block with the remainder of the line.\n        //\n        // N.B. No validation is done on the contents of the string. If the\n        //      file contains multiple lines containing \"LANG=\" the last will\n        //      be used.\n        //\n\n        auto Content = strstr(Line, LANG_ENV \"=\");\n        if (Content != nullptr)\n        {\n            Content += sizeof(LANG_ENV);\n\n            //\n            // Replace newline character with a null terminator.\n            //\n\n            SpecialCharacter = strchr(Content, '\\n');\n            if (SpecialCharacter != nullptr)\n            {\n                *SpecialCharacter = '\\0';\n            }\n\n            Environment.AddVariable(LANG_ENV, Content);\n        }\n    }\n\n    return;\n}\nCATCH_LOG()\n\nvoid ConfigUpdateNetworkInformation(gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine updates the instance's network information by writing to\n    /etc/resolv.conf.\n\nArguments:\n\n    Buffer - Supplies the message buffer.\n\n    Config - Supplies the WSL distribution configuration.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    if (!Config.GenerateResolvConf)\n    {\n        LOG_WARNING(\"{} updating disabled in {}\", RESOLV_CONF_FILE_PATH, CONFIG_FILE);\n        return;\n    }\n\n    //\n    // Validate input parameters.\n    //\n\n    auto* Message = gslhelpers::try_get_struct<LX_INIT_NETWORK_INFORMATION>(Buffer);\n    if (!Message)\n    {\n        LOG_ERROR(\"Unexpected network information size {}\", Buffer.size());\n        return;\n    }\n\n    //\n    // Write the contents to /etc/resolv.conf.\n    //\n\n    if (ConfigCreateResolvConfSymlinkTarget() < 0)\n    {\n        return;\n    }\n\n    THROW_LAST_ERROR_IF(UtilMkdir(RESOLV_CONF_FOLDER, RESOLV_CONF_DIRECTORY_MODE) < 0);\n\n    wil::unique_fd Fd{TEMP_FAILURE_RETRY(open(RESOLV_CONF_FILE_PATH, (O_CREAT | O_RDWR | O_TRUNC), RESOLV_CONF_FILE_MODE))};\n    THROW_LAST_ERROR_IF(!Fd);\n\n    const char* Header = wsl::shared::string::FromSpan(Buffer, Message->FileHeaderIndex);\n    if (Header)\n    {\n        THROW_LAST_ERROR_IF(UtilWriteStringView(Fd.get(), Header) < 0);\n    }\n\n    const char* Content = wsl::shared::string::FromSpan(Buffer, Message->FileContentsIndex);\n    if (Content)\n    {\n        THROW_LAST_ERROR_IF(UtilWriteStringView(Fd.get(), Content) < 0);\n    }\n    else\n    {\n        LOG_ERROR(\"/etc/resolv.conf unexpectedly empty\");\n    }\n}\nCATCH_LOG()\n\nbool CreateLoginSession(const wsl::linux::WslDistributionConfig& Config, const char* Username, uid_t Uid)\n/*++\n\nRoutine Description:\n\n    Create a systemd login session for the given user.\n\nArguments:\n\n    Config - Supplies the WSL distribution configuration.\n\n    Username - Supplies session username.\n\n    Uid - Supplies the session UID.\n\nReturn Value:\n\n    true on success, false on failure.\n\n--*/\ntry\n{\n    static std::mutex LoginSessionsLock;\n    static std::map<uid_t, int> LoginSessions;\n\n    // Keep track of login sessions that have been created.\n    LoginSessionsLock.lock();\n    auto Unlock = wil::scope_exit([&]() { LoginSessionsLock.unlock(); });\n    if (LoginSessions.contains(Uid))\n    {\n        return true;\n    }\n\n    int LoginLeader;\n    const int Result = forkpty(&LoginLeader, nullptr, nullptr, nullptr);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"forkpty failed {}\", errno);\n        return false;\n    }\n    else if (Result == 0)\n    {\n        Unlock.reset();\n        _exit(execl(\"/bin/login\", \"/bin/login\", \"-f\", Username, nullptr));\n    }\n\n    LoginSessions.emplace(Uid, LoginLeader);\n\n    //\n    // N.B. Init needs to not ignore SIGCHLD so it can wait for the child process.\n    //\n    signal(SIGCHLD, SIG_DFL);\n    auto restoreDisposition = wil::scope_exit([]() { signal(SIGCHLD, SIG_IGN); });\n\n    if (Config.BootInitTimeout > 0)\n    {\n        auto cmd = std::format(\"systemctl is-active user@{}.service\", Uid);\n        try\n        {\n            return wsl::shared::retry::RetryWithTimeout<bool>(\n                [&]() {\n                    std::string Output;\n                    auto exitCode = UtilExecCommandLine(cmd.c_str(), &Output, 0, false);\n                    if (exitCode == 0) // is-active returns 0 if the unit is active.\n                    {\n                        return true;\n                    }\n                    else if (Output == \"failed\\n\")\n                    {\n                        LOG_ERROR(\"{} returned: {}\", cmd, Output);\n                        return false;\n                    }\n\n                    THROW_ERRNO(EAGAIN);\n                },\n                std::chrono::milliseconds{250},\n                std::chrono::milliseconds{Config.BootInitTimeout});\n        }\n        catch (...)\n        {\n            LOG_ERROR(\"Timed out waiting for user session for uid={}\", Uid);\n            return false;\n        }\n    }\n\n    return true;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return false;\n}\n"
  },
  {
    "path": "src/linux/init/config.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    config.c\n\nAbstract:\n\n    This file contains information related to configuring a running instance.\n\n--*/\n\n#pragma once\n\n#include <unistd.h>\n#include <pwd.h>\n#include <optional>\n#include <set>\n#include <string_view>\n#include <optional>\n#include \"SocketChannel.h\"\n#include \"WslDistributionConfig.h\"\n\n#define WSL_USE_VIRTIO_9P() (WI_IsFlagSet(UtilGetFeatureFlags(), LxInitFeatureVirtIo9p))\n#define WSL_USE_VIRTIO_FS() (WI_IsFlagSet(UtilGetFeatureFlags(), LxInitFeatureVirtIoFs))\n#define WSLG_SHARED_FOLDER \"wslg\"\n\n#define INIT_MAKE_SECURITY(_uid, _gid, _mode) {_uid, _gid, _mode}\n#define INIT_ANY_SYMLINK(_source, _target) \\\n    { \\\n        InitStartupTypeSymlink, \\\n        { \\\n            .Symlink = INIT_MAKE_SYMLINK(_source, _target) \\\n        } \\\n    }\n#define INIT_ANY_DIRECTORY(_path, _uid, _gid, _mode) \\\n    { \\\n        InitStartupTypeDirectory, \\\n        { \\\n            .Directory = INIT_MAKE_DIRECTORY(_path, _uid, _gid, _mode) \\\n        } \\\n    }\n#define INIT_ANY_MOUNT(_mount, _filesystem, _flags) \\\n    { \\\n        InitStartupTypeMount, \\\n        { \\\n            .Mount = INIT_MAKE_MOUNT(_mount, _filesystem, NULL, NULL, INIT_MAKE_SECURITY(0, 0, 0), _flags, false) \\\n        } \\\n    }\n\n#define INIT_ANY_MOUNT_OPTION(_mount, _filesystem, _option, _flags) \\\n    { \\\n        InitStartupTypeMount, \\\n        { \\\n            .Mount = INIT_MAKE_MOUNT(_mount, _filesystem, NULL, (_option), INIT_MAKE_SECURITY(0, 0, 0), _flags, false) \\\n        } \\\n    }\n\n#define INIT_ANY_MOUNT_DEVICE(_mount, _filesystem, _device, _flags) \\\n    { \\\n        InitStartupTypeMount, \\\n        { \\\n            .Mount = INIT_MAKE_MOUNT(_mount, _filesystem, (_device), NULL, INIT_MAKE_SECURITY(0, 0, 0), _flags, false) \\\n        } \\\n    }\n\n#define INIT_ANY_MOUNT_DEVICE_OPTION(_mount, _filesystem, _device, _option, _flags) \\\n    { \\\n        InitStartupTypeMount, \\\n        { \\\n            .Mount = INIT_MAKE_MOUNT(_mount, _filesystem, (_device), (_option), INIT_MAKE_SECURITY(0, 0, 0), _flags, false) \\\n        } \\\n    }\n\n#define INIT_ANY_MOUNT_DEVICE_OPTION_IGNORE_FAILURE(_mount, _filesystem, _device, _option, _flags) \\\n    { \\\n        InitStartupTypeMount, \\\n        { \\\n            .Mount = INIT_MAKE_MOUNT(_mount, _filesystem, (_device), (_option), INIT_MAKE_SECURITY(0, 0, 0), _flags, true) \\\n        } \\\n    }\n\n#define INIT_ANY_NODE(_path, _uid, _gid, _mode, _major, _minor) \\\n    { \\\n        InitStartupTypeNode, \\\n        { \\\n            .Node = INIT_MAKE_NODE(_path, _uid, _gid, _mode, _major, _minor) \\\n        } \\\n    }\n#define INIT_ANY_FILE(_filename, _mode) \\\n    { \\\n        InitStartupTypeFile, \\\n        { \\\n            .File = INIT_MAKE_FILE(_filename, _mode) \\\n        } \\\n    }\n#define INIT_MAKE_MOUNT(_mount, _filesystem, _devicename, _options, _directorysecurity, _flags, _ignorefailure) \\\n    {(_mount), (_filesystem), _devicename, _options, _directorysecurity, _flags, _ignorefailure}\n#define INIT_MAKE_SYMLINK(_source, _target) {(_source), (_target)}\n#define INIT_MAKE_DIRECTORY(_path, _uid, _gid, _mode) \\\n    { \\\n        (_path), \\\n        { \\\n            _uid, _gid, _mode \\\n        } \\\n    }\n#define INIT_MAKE_FILE(_filename, _mode) {(_filename), _mode}\n#define INIT_MAKE_NODE(_path, _uid, _gid, _mode, _major, _minor) {(_path), {_uid, _gid, _mode}, _major, _minor}\n\n//\n// Major devices.\n//\n\n#define INIT_DEV_MEM_MAJOR_NUMBER (1)\n#define INIT_DEV_TTY_MAJOR_NUMBER (4)\n#define INIT_DEV_ALT_TTY_MAJOR_NUMBER (5)\n#define INIT_DEV_MISC_MAJOR_NUMBER (10)\n\n//\n// LxBus Device.\n//\n// TODO_LX: The minor number is in the dynamic allocation range for misc\n//          devices. Once dynamic allocation is supported it should probably\n//          be actually dynamically allocated.\n//\n\n#define INIT_DEV_LXBUS_MINOR_NUMBER (50)\n#define INIT_DEV_LXBUS_MAJOR_NUMBER INIT_DEV_MISC_MAJOR_NUMBER\n\n//\n// Full Device.\n//\n\n#define INIT_DEV_FULL_MINOR_NUMBER (7)\n#define INIT_DEV_FULL_MAJOR_NUMBER INIT_DEV_MEM_MAJOR_NUMBER\n\n//\n// Log devices.\n//\n\n#define INIT_DEV_LOG_KMSG_MINOR_NUMBER (11)\n#define INIT_DEV_LOG_KMSG_MAJOR_NUMBER INIT_DEV_MEM_MAJOR_NUMBER\n\n//\n// Null Device.\n//\n\n#define INIT_DEV_NULL_MINOR_NUMBER (3)\n#define INIT_DEV_NULL_MAJOR_NUMBER INIT_DEV_MEM_MAJOR_NUMBER\n\n//\n// Zero Device.\n//\n\n#define INIT_DEV_ZERO_MINOR_NUMBER (5)\n#define INIT_DEV_ZERO_MAJOR_NUMBER INIT_DEV_MEM_MAJOR_NUMBER\n\n//\n// PTMX Device.\n//\n\n#define INIT_DEV_PTM_MINOR_NUMBER (2)\n#define INIT_DEV_PTM_MAJOR_NUMBER INIT_DEV_ALT_TTY_MAJOR_NUMBER\n\n//\n// PTS Devices.\n//\n\n#define INIT_DEV_PTS_MAJOR_NUMBER (136)\n\n//\n// Random device.\n//\n\n#define INIT_DEV_RANDOM_MINOR_NUMBER (8)\n#define INIT_DEV_RANDOM_MAJOR_NUMBER INIT_DEV_MEM_MAJOR_NUMBER\n\n//\n// TTY device.\n//\n\n#define INIT_DEV_TTY0_MINOR_NUMBER (0)\n#define INIT_DEV_TTY_MINOR_NUMBER_FIRST_VIRTUAL (1)\n#define INIT_DEV_TTY_MINOR_NUMBER_MAX_VIRTUAL (64)\n#define INIT_DEV_TTY_MINOR_NUMBER_FIRST_SERIAL (64)\n#define INIT_DEV_TTY_MINOR_NUMBER_MAX_SERIAL (256)\n\n#define INIT_DEV_TTY_SERIAL_MODE (S_IFCHR | 0660)\n#define INIT_DEV_TTY_SERIAL_GID DIALOUT_GID\n#define INIT_DEV_TTY_SERIAL_UID ROOT_UID\n#define INIT_DEV_TTY_SERIAL_FORMAT \"/dev/ttyS{}\"\n\n#define INIT_DEV_TTYCT_MINOR_NUMBER (0)\n#define INIT_DEV_TTYCT_MAJOR_NUMBER INIT_DEV_ALT_TTY_MAJOR_NUMBER\n\n//\n// URandom Device.\n//\n\n#define INIT_DEV_URANDOM_MINOR_NUMBER (9)\n#define INIT_DEV_URANDOM_MAJOR_NUMBER INIT_DEV_MEM_MAJOR_NUMBER\n\n//\n// UID and GID values.\n//\n\n#define DIALOUT_GID (20)\n#define ROOT_GID (0)\n#define ROOT_UID (0)\n#define TTY_GID (5)\n#define TTY_MODE (0660)\n\n//\n// Initialization helpers.\n//\n\ntypedef struct _INIT_SECURITY\n{\n    uid_t Uid;\n    gid_t Gid;\n    mode_t Mode;\n} INIT_SECURITY, *PINIT_SECURITY;\n\nusing PCINIT_SECURITY = const INIT_SECURITY*;\n\ntypedef struct _INIT_STARTUP_MOUNT\n{\n    const char* MountLocation;\n    const char* FileSystemType;\n    const char* DeviceName;\n    const char* MountOptions;\n    INIT_SECURITY DirectorySecurity;\n    unsigned long Flags;\n    bool IgnoreFailure;\n} INIT_STARTUP_MOUNT, *PINIT_STARTUP_MOUNT;\n\nusing PCINIT_STARTUP_MOUNT = const INIT_STARTUP_MOUNT*;\n\ntypedef struct _INIT_STARTUP_SYMBOLIC_LINK\n{\n    const char* Source;\n    const char* Target;\n} INIT_STARTUP_SYMBOLIC_LINK, *PINIT_STARTUP_SYMBOLIC_LINK;\n\nusing PCINIT_STARTUP_SYMBOLIC_LINK = const INIT_STARTUP_SYMBOLIC_LINK*;\n\ntypedef struct _INIT_STARTUP_DIRECTORY\n{\n    const char* Path;\n    INIT_SECURITY Security;\n} INIT_STARTUP_DIRECTORY, *PINIT_STARTUP_DIRECTORY;\n\nusing PCINIT_STARTUP_DIRECTORY = const INIT_STARTUP_DIRECTORY*;\n\ntypedef struct _INIT_STARTUP_FILE\n{\n    const char* FileName;\n    mode_t Mode;\n} INIT_STARTUP_FILE, *PINIT_STARTUP_FILE;\n\nusing PCINIT_STARTUP_FILE = const INIT_STARTUP_FILE*;\n\ntypedef struct _INIT_STARTUP_NODE\n{\n    const char* Path;\n    INIT_SECURITY Security;\n    unsigned int MajorNumber;\n    unsigned int MinorNumber;\n} INIT_STARTUP_NODE, *PINIT_STARTUP_NODE;\n\nusing PCINIT_STARTUP_NODE = const INIT_STARTUP_NODE*;\n\ntypedef enum _INIT_STARTUP_TYPE\n{\n    InitStartupTypeDirectory,\n    InitStartupTypeMount,\n    InitStartupTypeNode,\n    InitStartupTypeSymlink,\n    InitStartupTypeFile\n} INIT_STARTUP_TYPE,\n    *PINIT_STARTUP_TYPE;\n\ntypedef struct _INIT_STARTUP_ANY\n{\n    INIT_STARTUP_TYPE Type;\n\n    union\n    {\n        INIT_STARTUP_DIRECTORY Directory;\n        INIT_STARTUP_MOUNT Mount;\n        INIT_STARTUP_NODE Node;\n        INIT_STARTUP_SYMBOLIC_LINK Symlink;\n        INIT_STARTUP_FILE File;\n    } u;\n} INIT_STARTUP_ANY, *PINIT_STARTUP_ANY;\n\nusing PCINIT_STARTUP_ANY = const INIT_STARTUP_ANY*;\n\nclass EnvironmentBlock\n{\npublic:\n    EnvironmentBlock() = default;\n\n    EnvironmentBlock(const char* Buffer, unsigned short Count)\n    {\n        m_variables.reserve(Count);\n        for (unsigned short Index = 0; Index < Count; Index += 1)\n        {\n            std::string entry{Buffer};\n            const auto size = entry.size();\n            m_variables.push_back(std::move(entry));\n            Buffer += size + 1;\n        }\n    }\n\n    void AddVariable(std::string_view Name, std::string_view Value)\n    {\n        std::string entry{Name};\n        entry += \"=\";\n        auto found = std::find_if(\n            m_variables.begin(), m_variables.end(), [&entry](const auto& variable) { return (variable.find(entry) == 0); });\n\n        entry += Value;\n        if (found != m_variables.end())\n        {\n            *found = std::move(entry);\n        }\n        else\n        {\n            m_variables.push_back(std::move(entry));\n        }\n    }\n\n    int AddVariableNoThrow(std::string_view Name, std::string_view Value)\n    try\n    {\n        AddVariable(Name, Value);\n        return 0;\n    }\n\n    CATCH_RETURN_ERRNO()\n\n    std::string_view GetVariable(std::string_view Name)\n    {\n        std::string prefix{Name};\n        prefix += \"=\";\n        auto found = std::find_if(\n            m_variables.begin(), m_variables.end(), [&prefix](const auto& variable) { return (variable.find(prefix) == 0); });\n\n        std::string_view value{};\n        if (found != m_variables.end())\n        {\n            value = *found;\n            value = value.substr(prefix.size());\n        }\n\n        return value;\n    }\n\n    std::vector<const char*> Variables()\n    {\n        std::vector<const char*> variables;\n        variables.reserve(m_variables.size() + 1);\n        std::for_each(\n            m_variables.begin(), m_variables.end(), [&variables](const auto& variable) { variables.push_back(variable.c_str()); });\n\n        variables.push_back(nullptr);\n        return variables;\n    }\n\nprivate:\n    std::vector<std::string> m_variables;\n};\n\nvoid ConfigAppendToPath(EnvironmentBlock& Environment, std::string_view PathElement);\n\nwsl::linux::WslDistributionConfig ConfigInitializeCommon(struct sigaction* SavedSignalActions);\n\nint ConfigInitializeEntry(PCINIT_STARTUP_ANY AnyEntry);\n\nint ConfigInitializeVmMode(bool Elevated, wsl::linux::WslDistributionConfig& Config);\n\nint ConfigInitializeWsl(void);\n\nvoid ConfigInitializeX11(const wsl::linux::WslDistributionConfig& Config);\n\nvoid ConfigCreateResolvConfSymlink(const wsl::linux::WslDistributionConfig& Config);\n\nint ConfigCreateResolvConfSymlinkTarget(void);\n\nEnvironmentBlock ConfigCreateEnvironmentBlock(PLX_INIT_CREATE_PROCESS_COMMON Common, const wsl::linux::WslDistributionConfig& Config);\n\nstd::optional<unsigned int> ConfigGetDriveLetter(std::string_view MountSource);\n\nstd::set<std::pair<unsigned int, std::string>> ConfigGetMountedDrvFsVolumes(void);\n\nstd::vector<std::pair<std::string, std::string>> ConfigGetWslgEnvironmentVariables(const wsl::linux::WslDistributionConfig& Config);\n\nvoid ConfigHandleInteropMessage(\n    wsl::shared::SocketChannel& ResponseChannel,\n    wsl::shared::SocketChannel& InteropChannel,\n    bool Elevated,\n    gsl::span<gsl::byte> Message,\n    const MESSAGE_HEADER* Header,\n    const wsl::linux::WslDistributionConfig& Config);\n\nvoid ConfigInitializeCgroups(wsl::linux::WslDistributionConfig& Config);\n\nint ConfigInitializeInstance(wsl::shared::SocketChannel& Channel, gsl::span<gsl::byte> Buffer, wsl::linux::WslDistributionConfig& Config);\n\nvoid ConfigMountDrvFsVolumes(unsigned int DrvFsVolumes, uid_t OwnerUid, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config);\n\nvoid ConfigMountFsTab(bool Elevated);\n\nint ConfigReconfigureResolvConfSymlink(const wsl::linux::WslDistributionConfig& Config);\n\nint ConfigRegisterBinfmtInterpreter(void);\n\nint ConfigSetMountNamespace(bool Elevated);\n\nint ConfigRemountDrvFs(gsl::span<gsl::byte> Buffer, wsl::shared::SocketChannel& Channel, const wsl::linux::WslDistributionConfig& Config);\n\nint ConfigRemountDrvFsImpl(gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config);\n\nvoid ConfigUpdateLanguage(EnvironmentBlock& Environment);\n\nvoid ConfigUpdateNetworkInformation(gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config);\n\ntemplate <>\nstruct std::formatter<INIT_STARTUP_TYPE, char>\n{\n    template <typename TCtx>\n    constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(INIT_STARTUP_TYPE str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", static_cast<int>(str));\n    }\n};"
  },
  {
    "path": "src/linux/init/drvfs.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    drvfs.c\n\nAbstract:\n\n    This file contains DrvFs function definitions.\n\n--*/\n\n#include \"common.h\"\n#include <sys/mount.h>\n#include <stdarg.h>\n#include <mountutilcpp.h>\n#include \"util.h\"\n#include \"drvfs.h\"\n#include \"config.h\"\n#include \"message.h\"\n#include <cassert>\n#include <filesystem>\n#include <optional>\n\nusing namespace std::chrono_literals;\n\n#define PLAN9_CASE_OPTION \"case=\"\n#define PLAN9_CASE_OPTION_DIR PLAN9_CASE_OPTION \"dir\"\n#define PLAN9_CASE_OPTION_FORCE PLAN9_CASE_OPTION \"force\"\n#define PLAN9_CASE_OPTION_OFF PLAN9_CASE_OPTION \"off\"\n#define PLAN9_SYMLINK_ROOT_OPTION \"symlinkroot=\"\n#define PLAN9_UNC_PREFIX_LENGTH (2)\n\n#define VIRTIOFS_TAG_DIR \"/run/wsl/virtiofs\"\n\n#define LOG_STDERR(_errno) fprintf(stderr, \"mount: %s\\n\", strerror(_errno))\n\nconstexpr int c_exitCodeInvalidUsage = 1;\nconstexpr int c_exitCodeMountFail = 32;\n\nint MountFilesystem(const char* FsType, const char* Source, const char* Target, const char* Options, int* ExitCode = nullptr);\n\nint MountWithRetry(const char* Source, const char* Target, const char* FsType, const char* Options, int* ExitCode = nullptr);\n\nvoid SaveVirtiofsTagMapping(const char* Tag, const char* Source)\n\n/*++\n\nRoutine Description:\n\n    This routine creates a symlink in VIRTIOFS_TAG_DIR that maps a virtiofs tag\n    to its Windows mount source path. This allows QueryVirtiofsMountSource to\n    resolve tags without talking to the service.\n\nArguments:\n\n    Tag - Supplies the virtiofs tag.\n\n    Source - Supplies the Windows path the tag refers to.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    //\n    // Validate the tag is a GUID to prevent path traversal.\n    //\n\n    const auto Guid = wsl::shared::string::ToGuid(Tag);\n    if (!Guid)\n    {\n        LOG_WARNING(\"Invalid virtiofs tag {}\", Tag);\n        return;\n    }\n\n    //\n    // Canonicalize path separators to backslashes before persisting.\n    //\n\n    std::string CanonicalSource{Source};\n    UtilCanonicalisePathSeparator(CanonicalSource, PATH_SEP_NT);\n\n    UtilMkdirPath(VIRTIOFS_TAG_DIR, 0755);\n\n    auto LinkPath = std::format(\"{}/{}\", VIRTIOFS_TAG_DIR, Tag);\n\n    //\n    // Remove any existing symlink for this tag before creating a new one.\n    //\n\n    unlink(LinkPath.c_str());\n    if (symlink(CanonicalSource.c_str(), LinkPath.c_str()) < 0)\n    {\n        LOG_WARNING(\"Failed to create virtiofs tag symlink {} -> {}: {}\", LinkPath, CanonicalSource, errno);\n    }\n}\n\nstd::pair<std::string, std::string> ConvertDrvfsMountOptionsToPlan9(std::string_view Options, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine converts each applicable DrvFs mount option into a 9p mount option and splits\n    non-DrvFs options to a different list.\n\nArguments:\n\n    Options - Supplies the DrvFs mount options.\n\nReturn Value:\n\n    A pair representing the 9p mount options, and standard mount options.\n\n--*/\n\n{\n    using wsl::shared::string::StartsWith;\n\n    std::string Plan9Options{};\n    std::string StandardOptions{};\n    while (!Options.empty())\n    {\n        auto Option = UtilStringNextToken(Options, \",\");\n        if ((Option == \"metadata\") || (StartsWith(Option, PLAN9_CASE_OPTION)) || (StartsWith(Option, \"uid=\")) ||\n            (StartsWith(Option, \"gid=\")) || (StartsWith(Option, \"umask=\")) || (StartsWith(Option, \"dmask=\")) ||\n            (StartsWith(Option, \"fmask=\")) || (StartsWith(Option, PLAN9_SYMLINK_ROOT_OPTION)))\n        {\n            if (Option == PLAN9_CASE_OPTION_FORCE)\n            {\n                LOG_WARNING(\"{} not supported, using {}\", PLAN9_CASE_OPTION_FORCE, PLAN9_CASE_OPTION_DIR);\n                Option = PLAN9_CASE_OPTION_DIR;\n            }\n\n            Plan9Options += ';';\n            Plan9Options += Option;\n        }\n        else if (StartsWith(Option, \"fallback=\"))\n        {\n            LOG_WARNING(\"{} not supported, ignoring...\", Option);\n        }\n        else\n        {\n            StandardOptions += Option;\n            StandardOptions += ',';\n        }\n    }\n\n    Plan9Options += \";\" PLAN9_SYMLINK_ROOT_OPTION;\n    Plan9Options += Config.DrvFsPrefix;\n    return {std::move(Plan9Options), std::move(StandardOptions)};\n}\n\nbool IsDrvfsElevated(void)\n\n/*++\n\nRoutine Description:\n\n    This routine determines whether drvfs mounts should use the elevated server.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    True if this process should use the elevated server; otherwise, false.\n\n--*/\n\n{\n    const char* envFlag = getenv(WSL_DRVFS_ELEVATED_ENV);\n    if (envFlag != nullptr)\n    {\n        if (strcmp(envFlag, \"0\") == 0)\n        {\n            return false;\n        }\n        else if (strcmp(envFlag, \"1\") == 0)\n        {\n            return true;\n        }\n\n        LOG_ERROR(\"Unexpected value for {}: '{}'\", WSL_DRVFS_ELEVATED_ENV, envFlag);\n    }\n\n    //\n    // Establish a connection to the interop server. If the connection cannot\n    // be established, use the non-admin DrvFs port.\n    //\n\n    wsl::shared::SocketChannel channel{UtilConnectToInteropServer(), \"InteropClientDrvfs\"};\n    if (channel.Socket() < 0)\n    {\n        return false;\n    }\n\n    //\n    // Query the interop server for which port to use.\n    //\n\n    MESSAGE_HEADER QueryPortMessage;\n    QueryPortMessage.MessageType = LxInitMessageQueryDrvfsElevated;\n    QueryPortMessage.MessageSize = sizeof(QueryPortMessage);\n\n    channel.SendMessage(QueryPortMessage);\n    return channel.ReceiveMessage<RESULT_MESSAGE<bool>>().Result;\n}\n\nint MountFilesystem(const char* FsType, const char* Source, const char* Target, const char* Options, int* ExitCode)\n\n/*++\n\nRoutine Description:\n\n    This routine will perform a mount using the /bin/mount binary.\n\nArguments:\n\n    FsType - Supplies the file system type.\n\n    Source - Supplies the mount source.\n\n    Target - Supplies the mount target.\n\n    Options - Supplies the mount options.\n\n    ExitCode - Supplies an optional pointer that receives the exit code.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    const char* const Argv[] = {\n        MOUNT_COMMAND, MOUNT_INTERNAL_ONLY_ARG, MOUNT_TYPES_ARG, FsType, Source, Target, MOUNT_OPTIONS_ARG, Options, nullptr};\n\n    int Status = 0;\n    const int Result = UtilCreateProcessAndWait(Argv[0], Argv, &Status);\n\n    //\n    // If the mount process failed, make sure its exit code is propagated. If it terminated\n    // abnormally or could not be launched, just return failure.\n    //\n\n    if (ExitCode != nullptr)\n    {\n        if (Result < 0)\n        {\n            if (WIFEXITED(Status) && Status != 0)\n            {\n                *ExitCode = WEXITSTATUS(Status);\n            }\n            else\n            {\n                *ExitCode = c_exitCodeMountFail;\n            }\n        }\n        else\n        {\n            *ExitCode = 0;\n        }\n    }\n\n    return Result;\n}\n\nint MountWithRetry(const char* Source, const char* Target, const char* FsType, const char* Options, int* ExitCode)\n\n/*++\n\nRoutine Description:\n\n    This routine performs a mount with retry logic for DrvFs filesystems.\n\nArguments:\n\n    Source - Supplies the mount source.\n\n    Target - Supplies the mount target.\n\n    FsType - Supplies the filesystem type.\n\n    Options - Supplies the mount options.\n\n    ExitCode - Supplies an optional pointer that receives the exit code.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    //\n    // Verify the target directory exists before mounting.\n    //\n\n    int Result = access(Target, F_OK);\n    if (Result < 0)\n    {\n        LOG_STDERR(errno);\n    }\n    else\n    {\n        auto Parsed = mountutil::MountParseFlags(Options);\n        Result = UtilMount(Source, Target, FsType, Parsed.MountFlags, Parsed.StringOptions.c_str(), std::chrono::seconds{2});\n    }\n\n    if (ExitCode)\n    {\n        *ExitCode = Result < 0 ? c_exitCodeMountFail : 0;\n    }\n\n    return Result;\n}\nCATCH_RETURN_ERRNO()\n\nint MountDrvfs(const char* Source, const char* Target, const char* Options, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config, int* ExitCode)\n\n/*++\n\nRoutine Description:\n\n    This routine will perform a DrvFs mount.\n\nArguments:\n\n    Source - Supplies the mount source.\n\n    Target - Supplies the mount target.\n\n    Options - Supplies the mount options.\n\n    Admin - Supplies an optional boolean to specify if the admin or non-admin share should be used.\n\n    ExitCode - Supplies an optional pointer that receives the exit code.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    if (!UtilIsUtilityVm())\n    {\n        return MountFilesystem(DRVFS_FS_TYPE, Source, Target, Options, ExitCode);\n    }\n    else if (WSL_USE_VIRTIO_FS())\n    {\n        return MountVirtioFs(Source, Target, Options, Admin, Config, ExitCode);\n    }\n\n    return MountPlan9(Source, Target, Options, Admin, Config, ExitCode);\n}\nCATCH_RETURN_ERRNO()\n\nint MountDrvfsEntry(int Argc, char* Argv[])\n\n/*++\n\nRoutine Description:\n\n    This routine is the entrypoint for mount.drvfs.\n\nArguments:\n\n    Argc - Supplies the argument count.\n\n    Argv - Supplies the command line arguments.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    if (Argc < 3)\n    {\n        LOG_STDERR(EINVAL);\n        return c_exitCodeInvalidUsage;\n    }\n\n    //\n    // Handle mount options if provided.\n    //\n\n    auto* Options = \"\";\n    if (Argc > 4)\n    {\n        Options = Argv[4];\n    }\n\n    int ExitCode = c_exitCodeMountFail;\n    MountDrvfs(Argv[1], Argv[2], Options, {}, wsl::linux::WslDistributionConfig{CONFIG_FILE}, &ExitCode);\n    return ExitCode;\n}\n\nint MountPlan9Share(const char* Source, const char* Target, const char* Options, bool Admin, int* ExitCode)\n\n/*++\n\nRoutine Description:\n\n    This routine will perform a plan 9 mount using the /bin/mount binary.\n\nArguments:\n\n    Source - Supplies the mount source.\n\n    Target - Supplies the mount target.\n\n    Options - Supplies the mount options.\n\n    Admin - Supplies a boolean specifying if the admin share should be used.\n\n    ExitCode - Supplies an optional pointer that receives the exit code.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    std::string MountOptions;\n    if (WSL_USE_VIRTIO_9P())\n    {\n        Source = Admin ? LX_INIT_DRVFS_ADMIN_VIRTIO_TAG : LX_INIT_DRVFS_VIRTIO_TAG;\n        MountOptions = std::format(\"msize=262144,trans=virtio,{}\", Options);\n        return MountWithRetry(Source, Target, PLAN9_FS_TYPE, MountOptions.c_str(), ExitCode);\n    }\n    else\n    {\n        auto Port = Admin ? LX_INIT_UTILITY_VM_PLAN9_DRVFS_ADMIN_PORT : LX_INIT_UTILITY_VM_PLAN9_DRVFS_PORT;\n        wil::unique_fd Fd{UtilConnectVsock(Port, false, LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE)};\n        if (!Fd)\n        {\n            return -1;\n        }\n\n        MountOptions =\n            std::format(\"msize={},trans=fd,rfdno={},wfdno={},{}\", LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE, Fd.get(), Fd.get(), Options);\n\n        return MountFilesystem(PLAN9_FS_TYPE, Source, Target, MountOptions.c_str(), ExitCode);\n    }\n}\n\nint MountPlan9(const char* Source, const char* Target, const char* Options, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config, int* ExitCode)\n\n/*++\n\nRoutine Description:\n\n    This routine will perform a DrvFs mount using Plan9.\n\nArguments:\n\n    Source - Supplies the mount source.\n\n    Target - Supplies the mount target.\n\n    Options - Supplies the mount options.\n\n    Admin - Supplies an optional boolean to specify if the admin or non-admin share should be used.\n\n    Config - Supplies the distribution configuration.\n\n    ExitCode - Supplies an optional pointer that receives the exit code.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    //\n    // Check if the path is a UNC path.\n    //\n\n    const char* Plan9Source;\n    std::string UncSource;\n    if ((strlen(Source) >= PLAN9_UNC_PREFIX_LENGTH) && ((Source[0] == '/') || (Source[0] == '\\\\')) &&\n        ((Source[1] == '/') || (Source[1] == '\\\\')))\n    {\n        UncSource = PLAN9_UNC_TRANSLATED_PREFIX;\n        UncSource += &Source[PLAN9_UNC_PREFIX_LENGTH];\n        Plan9Source = UncSource.c_str();\n    }\n    else\n    {\n        Plan9Source = Source;\n    }\n\n    //\n    // Check whether to use the elevated or regular 9p server.\n    //\n\n    bool Elevated = Admin.has_value() ? Admin.value() : IsDrvfsElevated();\n\n    //\n    // Initialize mount options.\n    //\n\n    auto Plan9Options = std::format(\"{};path={}\", PLAN9_ANAME_DRVFS, Plan9Source);\n\n    //\n    // N.B. The cache option is added to the start of this so if the user\n    //      specifies one explicitly, it will override the default.\n    //\n\n    std::string MountOptions = \"cache=mmap,\";\n    auto ParsedOptions = ConvertDrvfsMountOptionsToPlan9(Options ? Options : \"\", Config);\n    Plan9Options += ParsedOptions.first;\n    MountOptions += ParsedOptions.second;\n\n    //\n    // Append the 9p mount options to the end of the other mount options and perform the mount operation.\n    //\n\n    MountOptions += Plan9Options;\n    if (MountPlan9Share(Source, Target, MountOptions.c_str(), Elevated, ExitCode) < 0)\n    {\n        return -1;\n    }\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nint MountVirtioFs(const char* Source, const char* Target, const char* Options, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config, int* ExitCode)\n\n/*++\n\nRoutine Description:\n\n    This routine mounts a virtiofs share. The DrvFs mount options are converted into 9p mount options\n    which are used to determine behavior when the device is added to the host.\n\nArguments:\n\n    Source - Supplies the mount source.\n\n    Target - Supplies the mount target.\n\n    Options - Supplies DrvFs mount options to translate into Plan9 mount\n        options.\n\n    Admin - Supplies an optional boolean to specify if the admin or non-admin server should be used.\n\n    ExitCode - Supplies an optional pointer that receives the exit code.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    assert(WSL_USE_VIRTIO_FS());\n\n    //\n    // Check whether to use the elevated or non-elevated virtiofs server.\n    //\n\n    if (!Admin.has_value())\n    {\n        Admin = IsDrvfsElevated();\n    }\n\n    //\n    // Convert the DrvFs mount options.\n    //\n    // N.B. Since virtiofs does not allow passing mount options, the 9p mount options are used to specify share\n    //      behavior when creating virtiofs shares on the host.\n    //\n\n    auto [Plan9Options, MountOptions] = ConvertDrvfsMountOptionsToPlan9(Options ? Options : \"\", Config);\n\n    //\n    // Construct a request to add a virtiofs share.\n    //\n\n    wsl::shared::MessageWriter<LX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE> AddShare(LxInitMessageAddVirtioFsDevice);\n    AddShare->Admin = Admin.value();\n    AddShare.WriteString(AddShare->PathOffset, Source);\n    AddShare.WriteString(AddShare->OptionsOffset, Plan9Options);\n\n    //\n    // Connect to the wsl service to add the virtiofs share. If adding the share fails, fallback to mounting using Plan9.\n    //\n\n    wsl::shared::SocketChannel Channel{UtilConnectVsock(LX_INIT_UTILITY_VM_VIRTIOFS_PORT, true), \"VirtoFs\"};\n    if (Channel.Socket() < 0)\n    {\n        return -1;\n    }\n\n    gsl::span<gsl::byte> ResponseSpan;\n    const auto& Response = Channel.Transaction<LX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE>(AddShare.Span(), &ResponseSpan);\n    if (Response.Result != 0)\n    {\n        LOG_WARNING(\"Add virtiofs share for {} failed {}, falling back to Plan9\", Source, Response.Result);\n        return MountPlan9(Source, Target, Options, Admin, Config, ExitCode);\n    }\n\n    //\n    // Perform the mount operation.\n    //\n\n    auto* Tag = wsl::shared::string::FromSpan(ResponseSpan, Response.TagOffset);\n    auto* ResponseSource = wsl::shared::string::FromSpan(ResponseSpan, Response.SourceOffset);\n    THROW_LAST_ERROR_IF(MountWithRetry(Tag, Target, VIRTIO_FS_TYPE, MountOptions.c_str(), ExitCode) < 0);\n\n    //\n    // Save the tag mapping.\n    //\n    // N.B. Use the source path from the response since the service canonicalizes it.\n    //\n\n    SaveVirtiofsTagMapping(Tag, ResponseSource);\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nint RemountVirtioFs(const char* Tag, const char* Target, const char* Options, bool Admin)\n\n/*++\n\nRoutine Description:\n\n    This routine translates DrvFs mount options into Plan9 mount options and\n    mounts the share.\n\nArguments:\n\n    Tag - Supplies the virtiofs tag to remount.\n\n    Target - Supplies the mount target.\n\n    Options - Supplies mount options.\n\n    Admin - Supplies a boolean to specify if the admin or non-admin server should be used.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    assert(WSL_USE_VIRTIO_FS());\n\n    wsl::shared::MessageWriter<LX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE> RemountShare(LxInitMessageRemountVirtioFsDevice);\n    RemountShare->Admin = Admin;\n    RemountShare.WriteString(RemountShare->TagOffset, Tag);\n\n    //\n    // Connect to the host and send the remount request.\n    //\n\n    wsl::shared::SocketChannel Channel{UtilConnectVsock(LX_INIT_UTILITY_VM_VIRTIOFS_PORT, true), \"RemountVirtioFs\"};\n    if (Channel.Socket() < 0)\n    {\n        return -1;\n    }\n\n    gsl::span<gsl::byte> ResponseSpan;\n    const auto& Response = Channel.Transaction<LX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE>(RemountShare.Span(), &ResponseSpan);\n    if (Response.Result != 0)\n    {\n        LOG_ERROR(\"Remount virtiofs share for {} failed {}\", Tag, Response.Result);\n        return -1;\n    }\n\n    auto* NewTag = wsl::shared::string::FromSpan(ResponseSpan, Response.TagOffset);\n    auto* Source = wsl::shared::string::FromSpan(ResponseSpan, Response.SourceOffset);\n    THROW_LAST_ERROR_IF(MountWithRetry(NewTag, Target, VIRTIO_FS_TYPE, Options) < 0);\n\n    SaveVirtiofsTagMapping(NewTag, Source);\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nstd::string QueryVirtiofsMountSource(const char* Tag)\n\n/*++\n\nRoutine Description:\n\n    This routine takes a virtiofs tag and determines the Windows path it refers to\n    by reading the symlink created during mount.\n\nArguments:\n\n    Tag - Supplies the virtiofs tag to query.\n\nReturn Value:\n\n    The mount source, an empty string on failure.\n\n--*/\n\ntry\n{\n    if (!WSL_USE_VIRTIO_FS())\n    {\n        return {};\n    }\n\n    //\n    // Validate the tag is a GUID.\n    //\n\n    const auto Guid = wsl::shared::string::ToGuid(Tag);\n    if (!Guid)\n    {\n        return {};\n    }\n\n    //\n    // Read the symlink that maps this tag to its Windows source path.\n    //\n\n    auto LinkPath = std::format(\"{}/{}\", VIRTIOFS_TAG_DIR, Tag);\n    return std::filesystem::read_symlink(LinkPath).string();\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n"
  },
  {
    "path": "src/linux/init/drvfs.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    drvfs.h\n\nAbstract:\n\n    This file contains DrvFs function declarations.\n\n--*/\n\n#pragma once\n#include <optional>\n#include \"WslDistributionConfig.h\"\n\n#define DRVFS_FS_TYPE \"drvfs\"\n#define MOUNT_DRVFS_NAME \"mount.drvfs\"\n\nint MountDrvfs(const char* Source, const char* Target, const char* Options, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config, int* ExitCode = nullptr);\n\nint MountDrvfsEntry(int Argc, char* Argv[]);\n\nint MountPlan9Share(const char* Source, const char* Target, const char* Options, bool Admin, int* ExitCode = nullptr);\n\nint MountPlan9(const char* Source, const char* Target, const char* Options, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config, int* ExitCode);\n\nint MountVirtioFs(const char* Source, const char* Target, const char* Options, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config, int* ExitCode = nullptr);\n\nint RemountVirtioFs(const char* Tag, const char* Target, const char* Options, bool Admin);\n\nstd::string QueryVirtiofsMountSource(const char* Tag);\n"
  },
  {
    "path": "src/linux/init/escape.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    escape.c\n\nAbstract:\n\n    This file contains support for escaping Linux paths for us on NTFS using\n    the DrvFs escape conventions.\n\n--*/\n\n#include \"common.h\"\n#include \"escape.h\"\n#include \"util.h\"\n\n//\n// List indicating which characters are legal in NTFS.\n// This differs from the Windows logic in two ways:\n// 1. Slashes are allowed (because escaping is done on full Linux paths).\n// 2. Colons are disallowed (because they indicate alternate data streams).\n//\n\nstatic constexpr bool EscapeNtfsLegalAnsiCharacterArray[128] = {\n    false, // 0x00 ^@\n    false, // 0x01 ^A\n    false, // 0x02 ^B\n    false, // 0x03 ^C\n    false, // 0x04 ^D\n    false, // 0x05 ^E\n    false, // 0x06 ^F\n    false, // 0x07 ^G\n    false, // 0x08 ^H\n    false, // 0x09 ^I\n    false, // 0x0A ^J\n    false, // 0x0B ^K\n    false, // 0x0C ^L\n    false, // 0x0D ^M\n    false, // 0x0E ^N\n    false, // 0x0F ^O\n    false, // 0x10 ^P\n    false, // 0x11 ^Q\n    false, // 0x12 ^R\n    false, // 0x13 ^S\n    false, // 0x14 ^T\n    false, // 0x15 ^U\n    false, // 0x16 ^V\n    false, // 0x17 ^W\n    false, // 0x18 ^X\n    false, // 0x19 ^Y\n    false, // 0x1A ^Z\n    false, // 0x1B ESC\n    false, // 0x1C FS\n    false, // 0x1D GS\n    false, // 0x1E RS\n    false, // 0x1F US\n    true,  // 0x20 space\n    true,  // 0x21 !\n    false, // 0x22 \"\n    true,  // 0x23 #\n    true,  // 0x24 $\n    true,  // 0x25 %\n    true,  // 0x26 &\n    true,  // 0x27 '\n    true,  // 0x28 (\n    true,  // 0x29 )\n    false, // 0x2A *\n    true,  // 0x2B +\n    true,  // 0x2C,\n    true,  // 0x2D -\n    true,  // 0x2E .\n    true,  // 0x2F /   *** Normally \"false\"\n    true,  // 0x30 0\n    true,  // 0x31 1\n    true,  // 0x32 2\n    true,  // 0x33 3\n    true,  // 0x34 4\n    true,  // 0x35 5\n    true,  // 0x36 6\n    true,  // 0x37 7\n    true,  // 0x38 8\n    true,  // 0x39 9\n    false, // 0x3A :   *** Normally \"true\"\n    true,  // 0x3B ;\n    false, // 0x3C <\n    true,  // 0x3D =\n    false, // 0x3E >\n    false, // 0x3F ?\n    true,  // 0x40 @\n    true,  // 0x41 A\n    true,  // 0x42 B\n    true,  // 0x43 C\n    true,  // 0x44 D\n    true,  // 0x45 E\n    true,  // 0x46 F\n    true,  // 0x47 G\n    true,  // 0x48 H\n    true,  // 0x49 I\n    true,  // 0x4A J\n    true,  // 0x4B K\n    true,  // 0x4C L\n    true,  // 0x4D M\n    true,  // 0x4E N\n    true,  // 0x4F O\n    true,  // 0x50 P\n    true,  // 0x51 Q\n    true,  // 0x52 R\n    true,  // 0x53 S\n    true,  // 0x54 T\n    true,  // 0x55 U\n    true,  // 0x56 V\n    true,  // 0x57 W\n    true,  // 0x58 X\n    true,  // 0x59 Y\n    true,  // 0x5A Z\n    true,  // 0x5B [\n    false, // 0x5C backslash\n    true,  // 0x5D ]\n    true,  // 0x5E ^\n    true,  // 0x5F _\n    true,  // 0x60 `\n    true,  // 0x61 a\n    true,  // 0x62 b\n    true,  // 0x63 c\n    true,  // 0x64 d\n    true,  // 0x65 e\n    true,  // 0x66 f\n    true,  // 0x67 g\n    true,  // 0x68 h\n    true,  // 0x69 i\n    true,  // 0x6A j\n    true,  // 0x6B k\n    true,  // 0x6C l\n    true,  // 0x6D m\n    true,  // 0x6E n\n    true,  // 0x6F o\n    true,  // 0x70 p\n    true,  // 0x71 q\n    true,  // 0x72 r\n    true,  // 0x73 s\n    true,  // 0x74 t\n    true,  // 0x75 u\n    true,  // 0x76 v\n    true,  // 0x77 w\n    true,  // 0x78 x\n    true,  // 0x79 y\n    true,  // 0x7A z\n    true,  // 0x7B {\n    false, // 0x7C |\n    true,  // 0x7D }\n    true,  // 0x7E ~\n    true   // 0x7F \n};\n\n//\n// This is the utf-8 sequence for character 0xf000, the first character in the\n// range used to escape unsupported characters.\n//\n\nstatic const char UtilEscapeCharBase[] = {0xef, 0x80, 0x80};\n\nbool EscapeCharNeedsEscape(char Character)\n\n/*++\n\nDescription:\n\n    This routine checks whether a character needs to be escaped to be used in\n    a path.\n\n    N.B. Slashes are allowed because this function is used on complete Linux\n         paths. The caller should translate those to backslashes after\n         calling this function.\n\nParameters:\n\n    Character - Supplies the character.\n\nReturn:\n\n    True if the character needs to be escaped; otherwise, false.\n\n--*/\n\n{\n    //\n    // Check if the character needs to be escaped.\n    //\n\n    return (static_cast<unsigned char>(Character) <= SCHAR_MAX) && (EscapeNtfsLegalAnsiCharacterArray[static_cast<int>(Character)] == false);\n}\n\nvoid EscapePathForNt(const char* Path, char* EscapedPath)\n\n/*++\n\nDescription:\n\n    This routine escapes a Linux path for use with NT.\n\n    N.B. The path is assumed to use Linux separators (forward slash), so those\n         are not escaped, but rather replaced with backslashes.\n\nParameters:\n\n    Path - Supplies the path to escape.\n\n    EscapedPath - Supplies a buffer that receives the escaped path. This\n        buffer is assumed to be the right length.\n\nReturn:\n\n    None.\n\n--*/\n\n{\n    const char* Current;\n    size_t InsertionIndex;\n\n    InsertionIndex = 0;\n    for (Current = Path; *Current != '\\0'; Current += 1)\n    {\n        if (*Current == PATH_SEP)\n        {\n            EscapedPath[InsertionIndex] = PATH_SEP_NT;\n            InsertionIndex += 1;\n        }\n        else if (EscapeCharNeedsEscape(*Current) == false)\n        {\n            EscapedPath[InsertionIndex] = *Current;\n            InsertionIndex += 1;\n        }\n        else\n        {\n            //\n            // Insert the utf-8 sequence for the escaped character.\n            //\n            // N.B. The last byte of the sequence can hold only 6 bits of data\n            //      due to utf-8 encoding, so the 2 most significant bits of\n            //      the character are encoded in the second byte.\n            //\n\n            EscapedPath[InsertionIndex] = UtilEscapeCharBase[0];\n            EscapedPath[InsertionIndex + 1] = UtilEscapeCharBase[1] | (*Current >> 6);\n            EscapedPath[InsertionIndex + 2] = UtilEscapeCharBase[2] | (*Current & 0x3f);\n            InsertionIndex += sizeof(UtilEscapeCharBase);\n        }\n    }\n}\n\nsize_t EscapePathForNtLength(const char* Path)\n\n/*++\n\nDescription:\n\n    This routine determines the length needed to escape a Linux path for use\n    in NT.\n\n    N.B. The path is assumed to use Linux separators (forward slash), so those\n         are not escaped.\n\nParameters:\n\n    Path - Supplies the path to escape.\n\nReturn:\n\n    The length in bytes. If this equals the length of the original string,\n    there are no characters that need to e escaped.\n\n--*/\n\n{\n    const char* Current;\n    size_t Length;\n\n    Length = 0;\n    for (Current = Path; *Current != '\\0'; Current += 1)\n    {\n        if (EscapeCharNeedsEscape(*Current) == false)\n        {\n            Length += 1;\n        }\n        else\n        {\n            Length += sizeof(UtilEscapeCharBase);\n        }\n    }\n\n    return Length;\n}\n\nvoid UnescapePathInplace(char* Path)\n\n/*++\n\nDescription:\n\n    This routine unescapes the supplied string inplace.\n\nParameters:\n\n    Path - Supplies the path to be unescaped.\n\nReturn:\n\n    None.\n\n--*/\n\n{\n    char* Current;\n    char* Remaining;\n    char Unescaped;\n\n    for (Current = Path; *Current != '\\0'; Current += 1)\n    {\n        //\n        // If the current character is a utf-8 sequence that can be unescaped,\n        // replace the character and shift down the remainder of the string.\n        //\n\n        if ((Current[0] == UtilEscapeCharBase[0]) && ((Current[1] & UtilEscapeCharBase[1]) != 0) &&\n            ((Current[2] & UtilEscapeCharBase[2]) != 0))\n        {\n            Unescaped = (Current[1] << 6) | (Current[2] & 0x3f);\n            if (EscapeCharNeedsEscape(Unescaped) != false)\n            {\n                *Current = Unescaped;\n                Remaining = &Current[3];\n                memmove(Current + 1, Remaining, strlen(Remaining) + 1);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/linux/init/escape.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    escape.h\n\nAbstract:\n\n    This file contains declarations for escaping Linux paths for us on NTFS using\n    the DrvFs escape conventions.\n\n--*/\n\nvoid EscapePathForNt(const char* Path, char* EscapedPath);\n\nsize_t EscapePathForNtLength(const char* Path);\n\nvoid UnescapePathInplace(char* Path);\n"
  },
  {
    "path": "src/linux/init/init.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    init.c\n\nAbstract:\n\n    This file contains the lx init implementation.\n\n--*/\n\n#include <cassert>\n#include <sys/eventfd.h>\n#include <sys/mount.h>\n#include <sys/prctl.h>\n#include <sys/signalfd.h>\n#include <sys/wait.h>\n#include <sys/epoll.h>\n#include <sys/syscall.h>\n#include <linux/filter.h>\n#include <pty.h>\n#include <utmp.h>\n#include <libgen.h>\n#include <grp.h>\n#include <sysexits.h>\n#include <iostream>\n#include <cstddef>\n#include <lxbusapi.h>\n#include \"p9tracelogging.h\"\n#include \"common.h\"\n#include \"config.h\"\n#include \"util.h\"\n#include \"timezone.h\"\n#include \"binfmt.h\"\n#include \"wslpath.h\"\n#include \"wslinfo.h\"\n#include \"drvfs.h\"\n#include \"plan9.h\"\n#include \"localhost.h\"\n#include \"telemetry.h\"\n#include \"GnsEngine.h\"\n#include \"lxinitshared.h\"\n#include \"message.h\"\n#include \"configfile.h\"\n#include \"CommandLine.h\"\n\nstatic_assert(EX_NOUSER == LX_INIT_USER_NOT_FOUND);\nstatic_assert(EUSERS == LX_INIT_TTY_LIMIT);\n\n#define DEFAULT_SHELL \"/bin/sh\"\n#define DEFAULT_SHELL_ARGS 4\n#define HOME_ENV \"HOME\"\n#define LOGNAME_ENV \"LOGNAME\"\n#define SHELL_ENV \"SHELL\"\n#define SHELL_PATH \"/bin/sh\"\n#define USER_ENV \"USER\"\n\nusing namespace wsl::shared;\n\ntypedef struct _CREATE_PROCESS_PARSED_COMMON\n{\n    const char* Filename;\n    std::string CurrentWorkingDirectory;\n    std::vector<const char*> CommandLine;\n    EnvironmentBlock Environment;\n    uid_t Uid;\n    CREATE_PROCESS_SHELL_OPTIONS ShellOptions;\n    bool AllowOOBE;\n} CREATE_PROCESS_PARSED_COMMON, *PCREATE_PROCESS_PARSED_COMMON;\n\ntypedef struct _CREATE_PROCESS_PARSED\n{\n    CREATE_PROCESS_PARSED_COMMON Common;\n    wil::unique_fd EventFd;\n    wil::unique_fd StdFd[LX_INIT_STD_FD_COUNT];\n    wil::unique_fd ServiceFd;\n} CREATE_PROCESS_PARSED, *PCREATE_PROCESS_PARSED;\n\nstruct sigaction g_SavedSignalActions[_NSIG];\n\n//\n// Best effort to put all processes launched within a session into the same\n// process group. This is how a shell like /bin/bash would typically launch\n// grouped commands (e.g. 'find' and 'less' from: find . -iname \"*.txt\" | less)\n//\n\nvolatile pid_t g_SessionGroup = -1;\n\n//\n// Fallback passwd struct to use in case the /etc/passwd file is missing or\n// corrupt.\n//\n\nconstexpr passwd c_defaultPasswordEntry = {\n    const_cast<char*>(\"root\"), NULL, ROOT_UID, ROOT_GID, NULL, const_cast<char*>(\"/\"), const_cast<char*>(DEFAULT_SHELL)};\n\nint CaptureCrash(int Argc, char** Argv);\n\nvoid CreateProcess(PCREATE_PROCESS_PARSED Parsed, int TtyFd, const wsl::linux::WslDistributionConfig& Config);\n\nvoid CreateProcessCommon(PCREATE_PROCESS_PARSED_COMMON Common, int TtyFd, int ServiceSocketFd, const wsl::linux::WslDistributionConfig&);\n\nCREATE_PROCESS_PARSED CreateProcessParse(gsl::span<gsl::byte> Buffer, int MessageFd, const wsl::linux::WslDistributionConfig& Config);\n\nint CreateProcessParseCommon(PCREATE_PROCESS_PARSED_COMMON Parsed, gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config);\n\nint CreateProcessReplyToServer(PCREATE_PROCESS_PARSED Parsed, pid_t CreateProcessPid, int MessageFd);\n\nvoid CreateWslSystemdUnits(const wsl::linux::WslDistributionConfig& Config);\n\nint InitConnectToServer(int LxBusFd, bool WaitForServer);\n\nint InitCreateProcessUtilityVm(\n    gsl::span<gsl::byte> Message,\n    const LX_INIT_CREATE_PROCESS_UTILITY_VM& Header,\n    wsl::shared::SocketChannel& MessageFd,\n    const wsl::linux::WslDistributionConfig& Config);\n\nint InitCreateSessionLeader(gsl::span<gsl::byte> Buffer, wsl::shared::SocketChannel& Channel, int LxBusFd, wsl::linux::WslDistributionConfig& Config);\n\nvoid InitEntry(int Argc, char* Argv[]);\n\nvoid InitEntryWsl(wsl::linux::WslDistributionConfig& Config);\n\nvoid InitEntryUtilityVm(wsl::linux::WslDistributionConfig& Config);\n\nvoid InitTerminateInstance(gsl::span<gsl::byte> Buffer, wsl::shared::SocketChannel& Channel, wsl::linux::WslDistributionConfig& Config);\n\nvoid InitTerminateInstanceInternal(const wsl::linux::WslDistributionConfig& Config);\n\nvoid InstallSystemdUnit(const char* Path, const std::string& Name, const char* Content);\n\nint GenerateSystemdUnits(int Argc, char** Argv);\n\nint GenerateUserSystemdUnits(int Argc, char** Argv);\n\nvoid HardenMirroredNetworkingSettingsAgainstSystemd();\n\nvoid PostProcessImportedDistribution(wsl::shared::MessageWriter<LX_MINI_INIT_IMPORT_RESULT>& Message, const char* ExtractedPath);\n\nvoid SessionLeaderCreateProcess(gsl::span<gsl::byte> Buffer, int MessageFd, int TtyFd);\n\nvoid SessionLeaderEntry(int MessageFd, int TtyFd, const wsl::linux::WslDistributionConfig& Config);\n\nvoid SessionLeaderEntryUtilityVm(wsl::shared::SocketChannel& Channel, const wsl::linux::WslDistributionConfig& Config);\n\nunsigned int StartPlan9(int Argc, char** Argv);\n\nunsigned int StartGns(int Argc, char** Argv);\n\nvoid WaitForBootProcess(wsl::linux::WslDistributionConfig& Config);\n\nwil::unique_fd UnmarshalConsoleFromServer(int MessageFd, LXBUS_IPC_CONSOLE_ID ConsoleId);\n\nint WslEntryPoint(int Argc, char* Argv[])\n{\n    //\n    // Determine if the binary is being launched in init daemon mode by\n    // checking the pid and Argc.\n    //\n    // N.B. Using the pid is not enough because this process might be running\n    // in a docker container. See: https://github.com/microsoft/WSL/issues/10883.\n    //\n    // If not in init daemon mode, differentiate between various functionality by checking Argv[0].\n    //\n\n    char* BaseName = basename(Argv[0]);\n    int ExitCode = -1;\n    pid_t Pid = getpid();\n\n    if (Pid == 1 && strcmp(BaseName, \"init\") == 0 && Argc <= 1)\n    {\n        InitEntry(Argc, Argv);\n    }\n    else\n    {\n        if (strcmp(BaseName, WSLPATH_NAME) == 0)\n        {\n            ExitCode = WslPathEntry(Argc, Argv);\n        }\n        else if (strcmp(BaseName, MOUNT_DRVFS_NAME) == 0)\n        {\n            ExitCode = MountDrvfsEntry(Argc, Argv);\n        }\n        else if (strcmp(BaseName, LX_INIT_LOCALHOST_RELAY) == 0)\n        {\n            ExitCode = RunPortTracker(Argc, Argv);\n        }\n        else if (strcmp(BaseName, LX_INIT_TELEMETRY_AGENT) == 0)\n        {\n            ExitCode = StartTelemetryAgent();\n        }\n        else if (strcmp(BaseName, LX_INIT_GNS) == 0)\n        {\n            ExitCode = StartGns(Argc, Argv);\n        }\n        else if (strcmp(BaseName, LX_INIT_PLAN9) == 0)\n        {\n            ExitCode = StartPlan9(Argc, Argv);\n        }\n        else if (strcmp(BaseName, WSLINFO_NAME) == 0)\n        {\n            ExitCode = WslInfoEntry(Argc, Argv);\n        }\n        else if (strcmp(BaseName, LX_INIT_WSL_CAPTURE_CRASH) == 0)\n        {\n            ExitCode = CaptureCrash(Argc, Argv);\n        }\n        else if (strcmp(BaseName, LX_INIT_WSL_GENERATOR) == 0)\n        {\n            ExitCode = GenerateSystemdUnits(Argc, Argv);\n        }\n        else if (strcmp(BaseName, LX_INIT_WSL_USER_GENERATOR) == 0)\n        {\n            ExitCode = GenerateUserSystemdUnits(Argc, Argv);\n        }\n        else\n        {\n            // Handle the special case for import result messages, everything else is sent to the binfmt interpreter.\n            if (Pid == 1 && strcmp(BaseName, \"init\") == 0 && Argc == 3 && strcmp(Argv[1], LX_INIT_IMPORT_MESSAGE_ARG) == 0)\n            {\n                try\n                {\n                    wsl::shared::MessageWriter<LX_MINI_INIT_IMPORT_RESULT> message;\n                    PostProcessImportedDistribution(message, Argv[2]);\n                    UtilWriteBuffer(STDOUT_FILENO, message.Span());\n                    char buffer[1];\n                    read(STDIN_FILENO, buffer, sizeof(buffer));\n                    exit(0);\n                }\n                CATCH_RETURN_ERRNO()\n            }\n\n            ExitCode = CreateNtProcess(Argc - 1, &Argv[1]);\n        }\n    }\n\n    return ExitCode;\n}\n\nint GenerateUserSystemdUnits(int Argc, char** Argv)\n{\n    if (Argc < 2)\n    {\n        LOG_ERROR(\"Unit folder missing\");\n        return 1;\n    }\n\n    const auto* installPath = Argv[1];\n\n    try\n    {\n        std::string automountRoot = \"/mnt\";\n        wil::unique_file File{fopen(\"/etc/wsl.conf\", \"r\")};\n        if (File)\n        {\n            std::vector<ConfigKey> ConfigKeys = {\n                ConfigKey(wsl::linux::c_ConfigAutoMountRoot, automountRoot),\n\n            };\n            ParseConfigFile(ConfigKeys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));\n            File.reset();\n        }\n\n        // TODO: handle quotes in path\n\n        auto unitContent = std::format(\n            R\"(# Note: This file is generated by WSL to configure wslg.\n\n[Unit]\nDescription=WSLg user service\nDefaultDependencies=no\n\n[Service]\nType=oneshot\nEnvironment=WSLG_RUNTIME_DIR={}/{}/{}\nExecStart=/bin/sh -c 'mkdir -p -m 00755 \"$XDG_RUNTIME_DIR/pulse\"'\nExecStart=/bin/sh -c 'ln -sf \"$WSLG_RUNTIME_DIR/wayland-0\" \"$XDG_RUNTIME_DIR/wayland-0\"'\nExecStart=/bin/sh -c 'ln -sf \"$WSLG_RUNTIME_DIR/wayland-0.lock\" \"$XDG_RUNTIME_DIR/wayland-0.lock\"'\nExecStart=/bin/sh -c 'ln -sf \"$WSLG_RUNTIME_DIR/pulse/native\" \"$XDG_RUNTIME_DIR/pulse/native\"'\nExecStart=/bin/sh -c 'ln -sf \"$WSLG_RUNTIME_DIR/pulse/pid\" \"$XDG_RUNTIME_DIR/pulse/pid\"'\n  )\",\n            automountRoot,\n            WSLG_SHARED_FOLDER,\n            WAYLAND_RUNTIME_DIR);\n\n        InstallSystemdUnit(installPath, \"wslg-session\", unitContent.c_str());\n\n        return 0;\n    }\n    CATCH_LOG()\n\n    return 1;\n}\n\nint GenerateSystemdUnits(int Argc, char** Argv)\n{\n    if (Argc < 2)\n    {\n        LOG_ERROR(\"Unit folder missing\");\n        return 1;\n    }\n\n    try\n    {\n        const auto* installPath = Argv[1];\n\n        LOG_INFO(\"Generating WSL systemd units in {}\", installPath);\n\n        bool enableGuiApps = true;\n        bool protectBinfmt = true;\n        bool interopEnabled = true;\n        std::string automountRoot = \"/mnt\";\n\n        wil::unique_file File{fopen(\"/etc/wsl.conf\", \"r\")};\n        if (File)\n        {\n            std::vector<ConfigKey> ConfigKeys = {\n                ConfigKey(wsl::linux::c_ConfigEnableGuiAppsOption, enableGuiApps),\n                ConfigKey(wsl::linux::c_ConfigBootProtectBinfmtOption, protectBinfmt),\n                ConfigKey(wsl::linux::c_ConfigInteropEnabledOption, interopEnabled),\n                ConfigKey(wsl::linux::c_ConfigAutoMountRoot, automountRoot),\n\n            };\n            ParseConfigFile(ConfigKeys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));\n            File.reset();\n        }\n\n        // Mask systemd-networkd-wait-online.service since WSL always ensures that networking is configured during boot.\n        // That unit can cause systemd boot timeouts since WSL's network interface is unmanaged by systemd.\n        THROW_LAST_ERROR_IF(symlink(\"/dev/null\", std::format(\"{}/systemd-networkd-wait-online.service\", installPath).c_str()) < 0);\n\n        // Mask NetworkManager-wait-online.service for the same reason, as it causes timeouts on distros using NetworkManager.\n        THROW_LAST_ERROR_IF(symlink(\"/dev/null\", std::format(\"{}/NetworkManager-wait-online.service\", installPath).c_str()) < 0);\n\n        // Only create the wslg unit if both enabled in wsl.conf, and if the wslg folder actually exists.\n        if (enableGuiApps && access(\"/mnt/wslg/runtime-dir\", F_OK) == 0)\n        {\n            THROW_LAST_ERROR_IF(UtilMkdirPath(\"/run/tmpfiles.d\", 0755) < 0);\n            const std::string tmpFilesConfig =\n                \"# Note: This file is generated by WSL to prevent systemd-tmpfiles from removing /tmp/.X11-unix during boot.\\n\";\n\n            THROW_LAST_ERROR_IF(WriteToFile(\"/run/tmpfiles.d/x11.conf\", tmpFilesConfig.c_str()) < 0);\n\n            // Note: It's not possible to use a mount unit because systemd will not mount /tmp/.X11-unix\n            // if /proc/mount says it's already mounted.\n\n            constexpr auto* x11UnitContent = R\"(# Note: This file is generated by WSL to prevent tmp.mount from hiding /tmp/.X11-unix\n\n[Unit]\nDescription=WSLg Remount Service\nDefaultDependencies=no\nAfter=systemd-tmpfiles-setup.service tmp.mount\nConditionPathExists=/mnt/wslg/.X11-unix\nConditionPathExists=!/tmp/.X11-unix/X0\n\n[Service]\nType=oneshot\nExecStart=/bin/mount -o bind,ro,X-mount.mkdir -t none /mnt/wslg/.X11-unix /tmp/.X11-unix)\";\n            InstallSystemdUnit(installPath, \"wslg\", x11UnitContent);\n        }\n\n        if (interopEnabled && protectBinfmt)\n        {\n            // N.B. ExecStop is required to prevent distributions from removing the WSL binfmt entry on shutdown.\n            auto systemdBinfmtContent = std::format(\n                R\"(# Note: This file is generated by WSL to prevent binfmt.d from overriding WSL's binfmt interpreter.\n# To disable this unit, add the following to /etc/wsl.conf:\n# [boot]\n# protectBinfmt=false\n\n[Service]\nExecStop=\nExecStart=/bin/sh -c '(echo -1 > {}/{}) ; (echo \"{}\" > {})' )\",\n                BINFMT_MISC_MOUNT_TARGET,\n                LX_INIT_BINFMT_NAME,\n                BINFMT_INTEROP_REGISTRATION_STRING(LX_INIT_BINFMT_NAME),\n                BINFMT_MISC_REGISTER_FILE);\n\n            // Install the override for systemd-binfmt.service.\n            {\n                auto overrideFolder = std::format(\"{}/systemd-binfmt.service.d\", installPath);\n                THROW_LAST_ERROR_IF(UtilMkdirPath(overrideFolder.c_str(), 0755) < 0);\n                THROW_LAST_ERROR_IF(WriteToFile(std::format(\"{}/override.conf\", overrideFolder).c_str(), systemdBinfmtContent.c_str()) < 0);\n            }\n\n            // Install the override for binfmt-support.service.\n            {\n                auto overrideFolder = std::format(\"{}/binfmt-support.service.d\", installPath);\n                THROW_LAST_ERROR_IF(UtilMkdirPath(overrideFolder.c_str(), 0755) < 0);\n                THROW_LAST_ERROR_IF(WriteToFile(std::format(\"{}/override.conf\", overrideFolder).c_str(), systemdBinfmtContent.c_str()) < 0);\n            }\n        }\n\n        return 0;\n    }\n    CATCH_LOG()\n\n    return 1;\n}\n\nint CaptureCrash(int Argc, char** Argv)\ntry\n{\n    UtilSetThreadName(\"CaptureCrash\");\n\n    if (Argc < 5)\n    {\n        std::cerr << \"Usage: \" << Argv[0] << \"<time> <executable> <pid> <signal>\" << std::endl;\n        return 1;\n    }\n\n    InitializeLogging(false);\n\n    LOG_INFO(\"Capturing crash for pid: {}, executable: {}, signal: {}, port: {}\", Argv[3], Argv[2], Argv[4], LX_INIT_UTILITY_VM_CRASH_DUMP_PORT);\n\n    wsl::shared::SocketChannel channel(UtilConnectVsock(LX_INIT_UTILITY_VM_CRASH_DUMP_PORT, true), \"crash-dump\");\n\n    wsl::shared::MessageWriter<LX_PROCESS_CRASH> message(LxProcessCrash);\n    message.WriteString(Argv[2]);\n    message->Timestamp = std::strtoull(Argv[1], nullptr, 10);\n    message->Signal = std::strtoul(Argv[4], nullptr, 10);\n    message->Pid = std::strtoull(Argv[3], nullptr, 10);\n\n    auto result = channel.Transaction<LX_PROCESS_CRASH>(message.Span()).Result;\n    if (result != 0)\n    {\n        LOG_ERROR(\"Received error while trying to capture crash dump: {}\", result);\n    }\n\n    std::vector<char> buffer(LX_RELAY_BUFFER_SIZE);\n\n    int bytes = -1;\n    while ((bytes = TEMP_FAILURE_RETRY(read(STDIN_FILENO, buffer.data(), buffer.size()))) > 0)\n    {\n        if (UtilWriteBuffer(channel.Socket(), buffer.data(), bytes) < 0)\n        {\n            LOG_ERROR(\"Error while trying read write dump, {}\", errno);\n            return 1;\n        }\n    }\n\n    if (bytes != 0)\n    {\n        LOG_ERROR(\"Error while trying read crash dump from stdin, {}\", errno);\n        return 1;\n    }\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nvoid CreateProcess(PCREATE_PROCESS_PARSED Parsed, int TtyFd, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine is the entry point for the create process.\n\nArguments:\n\n    Parsed - Supplies a pointer to a create process parsed structure.\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    ssize_t BytesRead;\n    uint64_t EventFdData;\n    int StdFdIndex;\n\n    //\n    // Initialize the new process and wait until the session leader signals\n    // to execvpe.\n    //\n\n    for (StdFdIndex = 0; StdFdIndex < LX_INIT_STD_FD_COUNT; StdFdIndex += 1)\n    {\n        //\n        // If a standard file descriptor is not set, use the TTY file descriptor.\n        //\n\n        if (dup2(Parsed->StdFd[StdFdIndex] ? Parsed->StdFd[StdFdIndex].get() : TtyFd, StdFdIndex) < 0)\n        {\n            FATAL_ERROR(\"dup2 failed {}\", errno);\n        }\n\n        Parsed->StdFd[StdFdIndex].reset();\n    }\n\n    //\n    // Read the eventfd data from the wsl service.\n    //\n\n    BytesRead = TEMP_FAILURE_RETRY(read(Parsed->EventFd.get(), &EventFdData, sizeof(EventFdData)));\n    if (BytesRead != sizeof(EventFdData))\n    {\n        FATAL_ERROR(\"Failed to read (size {}) EventFd {}\", BytesRead, errno);\n    }\n\n    //\n    // Launch the process.\n    //\n\n    CreateProcessCommon(&Parsed->Common, TtyFd, Parsed->ServiceFd.get(), Config);\n    return;\n}\n\nvoid CreateProcessCommon(PCREATE_PROCESS_PARSED_COMMON Common, int TtyFd, int ServiceSocket, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine is entry point for the common create process functionality.\n\nArguments:\n\n    Common - Supplies a pointer to the common create process parsed data.\n\n    TtyFd - Supplies the file descriptor representing the process's terminal.\n        This method takes ownership of this file descriptor.\n\n    ServiceSocket - Supplies the file descriptor to a socket connected to the Service.\n\n    Config - Supplies the distribution configuration\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    //\n    // Print any errors that occurred.\n    //\n\n    for (const auto& e : wsl::shared::string::Split<char>(wil::ScopedWarningsCollector::ConsumeWarnings(), '\\n'))\n    {\n        if (!e.empty())\n        {\n            fprintf(stderr, \"wsl: %s\\n\", e.c_str());\n        }\n    }\n\n    //\n    // Restore default signal dispositions and clear the signal mask for the child process.\n    //\n\n    THROW_LAST_ERROR_IF(UtilSetSignalHandlers(g_SavedSignalActions, false) < 0);\n\n    sigset_t SignalMask;\n    sigemptyset(&SignalMask);\n    THROW_LAST_ERROR_IF(sigprocmask(SIG_SETMASK, &SignalMask, NULL) < 0);\n\n    auto AddEnvironmentVariable = [&](const char* Name) {\n        const auto Value = UtilGetEnvironmentVariable(Name);\n        if (!Value.empty())\n        {\n            Common->Environment.AddVariable(Name, Value.c_str());\n        }\n    };\n\n    AddEnvironmentVariable(NAME_ENV);\n    AddEnvironmentVariable(WSL_DISTRO_NAME_ENV);\n\n    //\n    // Get the password entry for the user. (root if the distribution is being installed)\n    //\n\n    const passwd* PasswordEntry{};\n\n    auto ConfigureUid = [&](uint32_t Uid) {\n        PasswordEntry = getpwuid(Uid);\n        if (PasswordEntry == nullptr)\n        {\n            LOG_ERROR(\"getpwuid({}) failed {}\", Uid, errno);\n            PasswordEntry = const_cast<passwd*>(&c_defaultPasswordEntry);\n        }\n\n        //\n        // Add environment variables to the environment block.\n        //\n\n        Common->Environment.AddVariable(HOME_ENV, PasswordEntry->pw_dir);\n        Common->Environment.AddVariable(USER_ENV, PasswordEntry->pw_name);\n        Common->Environment.AddVariable(LOGNAME_ENV, PasswordEntry->pw_name);\n        Common->Environment.AddVariable(SHELL_ENV, PasswordEntry->pw_shell);\n    };\n\n    //\n    // Set the $LANG environment variable.\n    //\n    // N.B. Failure to update $LANG environment variable is non-fatal.\n    //\n\n    ConfigUpdateLanguage(Common->Environment);\n\n    //\n    // Launch the OOBE command, if any\n    //\n\n    if (Common->AllowOOBE)\n    {\n        assert(ServiceSocket != -1);\n\n        wsl::shared::SocketChannel channel(wil::unique_fd{ServiceSocket}, \"OOBE\");\n\n        std::string OobeCommand{};\n        int defaultUid = 0;\n        ConfigKeyPresence defaultUidPresent{};\n        std::vector<ConfigKey> keys = {ConfigKey(\"oobe.command\", OobeCommand), ConfigKey(\"oobe.defaultUid\", defaultUid, &defaultUidPresent)};\n\n        {\n            wil::unique_file File{fopen(WSL_DISTRIBUTION_CONF, \"r\")};\n            ParseConfigFile(keys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));\n        }\n\n        int32_t OobeResult = 0;\n        if (!OobeCommand.empty())\n        {\n            auto Pid = UtilCreateChildProcess(\"OOBE\", [&OobeCommand, &Common, &ConfigureUid]() {\n                ConfigureUid(0);\n                execle(\"/bin/sh\", \"sh\", \"-c\", OobeCommand.c_str(), nullptr, const_cast<char**>(Common->Environment.Variables().data()));\n                LOG_ERROR(\"execle() failed, {}\", errno);\n            });\n\n            int Status = -1;\n            if (TEMP_FAILURE_RETRY(waitpid(Pid, &Status, 0)) < 0)\n            {\n                LOG_ERROR(\"Waiting for child '{}' failed, waitpid failed {}\", OobeCommand.c_str(), errno);\n                _exit(1);\n            }\n\n            if (UtilProcessChildExitCode(Status, OobeCommand.c_str(), 0, false) < 0)\n            {\n                OobeResult = -1;\n                fprintf(stderr, \"OOBE command \\\"%s\\\" failed, exiting\\n\", OobeCommand.c_str());\n            }\n        }\n\n        LX_INIT_OOBE_RESULT result{};\n        result.Header.MessageType = LxInitOobeResult;\n        result.Header.MessageSize = sizeof(result);\n        result.Result = OobeResult;\n        result.DefaultUid = defaultUidPresent == ConfigKeyPresence::Present ? defaultUid : -1;\n\n        channel.SendMessage(result);\n\n        if (OobeResult != 0)\n        {\n            _exit(1);\n        }\n\n        ConfigureUid(defaultUidPresent == ConfigKeyPresence::Present ? defaultUid : Common->Uid);\n    }\n    else\n    {\n        ConfigureUid(Common->Uid);\n    }\n\n    //\n    // Ensure that a login session has been created for the user and set expected\n    // environment variables.\n    //\n\n    if (Config.InitPid.has_value())\n    {\n        wsl::shared::SocketChannel InteropChannel{UtilConnectToInteropServer(Config.InitPid.value()), \"InteropClient\"};\n        THROW_LAST_ERROR_IF(InteropChannel.Socket() < 0);\n\n        wsl::shared::MessageWriter<LX_INIT_CREATE_LOGIN_SESSION> CreateSession(LxInitMessageCreateLoginSession);\n        CreateSession->Uid = PasswordEntry->pw_uid;\n        CreateSession->Gid = PasswordEntry->pw_gid;\n        CreateSession.WriteString(PasswordEntry->pw_name);\n\n        auto result = InteropChannel.Transaction<LX_INIT_CREATE_LOGIN_SESSION>(CreateSession.Span());\n\n        if (!result.Result)\n        {\n            fprintf(stderr, \"wsl: %s\\n\", wsl::shared::Localization::MessageSystemdUserSessionFailed(PasswordEntry->pw_name).c_str());\n        }\n\n        Common->Environment.AddVariable(\"DBUS_SESSION_BUS_ADDRESS\", std::format(\"unix:path=/run/user/{}/bus\", PasswordEntry->pw_uid));\n        Common->Environment.AddVariable(XDG_RUNTIME_DIR_ENV, std::format(\"/run/user/{}\", PasswordEntry->pw_uid));\n    }\n\n    //\n    // If a filename was provided, use the filename and command line as-is.\n    // Otherwise, use the user's default shell. If the user's default shell is\n    // is empty, fall back to using /bin/sh.\n    //\n\n    std::string Argv0;\n    std::vector<const char*> CommandLine = Common->CommandLine;\n    char* Filename = const_cast<char*>(Common->Filename);\n    if (strlen(Filename) == 0)\n    {\n        Filename = const_cast<char*>(SHELL_PATH);\n        auto Size = sizeof(SHELL_PATH) - 1;\n        if (PasswordEntry->pw_shell != NULL)\n        {\n            Size = strlen(PasswordEntry->pw_shell);\n            if (Size != 0)\n            {\n                Filename = PasswordEntry->pw_shell;\n            }\n        }\n\n        if ((Common->ShellOptions & ShellOptionsLogin) != 0)\n        {\n            //\n            // Construct the name of the shell as the last path element\n            // prepended with a '-' and use this as Argv[0].\n            //\n            // N.B. This is the same behavior as the login binary.\n            //\n\n            auto Shell = strrchr(Filename, '/');\n            if (Shell != nullptr)\n            {\n                Shell = Shell + 1;\n            }\n            else\n            {\n                Shell = Filename;\n            }\n\n            Argv0 = \"-\";\n            Argv0 += Shell;\n        }\n        else\n        {\n            Argv0 = Filename;\n        }\n\n        CommandLine.insert(CommandLine.begin(), Argv0.c_str());\n    }\n\n    //\n    // Set the owner of the tty device.\n    //\n\n    if (TtyFd != -1)\n    {\n        if (fchown(TtyFd, PasswordEntry->pw_uid, TTY_GID) < 0)\n        {\n            LOG_ERROR(\"fchown failed {}\", errno);\n        }\n\n        CLOSE(TtyFd);\n        TtyFd = -1;\n    }\n\n    //\n    // Set the supplemental groups, gid, uid, and current working directory.\n    //\n\n    UtilInitGroups(PasswordEntry->pw_name, PasswordEntry->pw_gid);\n    THROW_LAST_ERROR_IF(setgid(PasswordEntry->pw_gid) < 0);\n    THROW_LAST_ERROR_IF(setuid(PasswordEntry->pw_uid) < 0);\n\n    //\n    // If the provided current working directory is empty, use the user's home\n    // path as the current working directory.\n    //\n    // N.B. Failures to set the current working directory are non-fatal.\n    //\n\n    std::string Directory;\n    if (Common->CurrentWorkingDirectory.empty())\n    {\n        Directory = PasswordEntry->pw_dir;\n    }\n    else\n    {\n        Directory = Common->CurrentWorkingDirectory;\n        if (Directory[0] == '~')\n        {\n            Directory = PasswordEntry->pw_dir;\n            if (Common->CurrentWorkingDirectory.size() > 1)\n            {\n                Directory += &Common->CurrentWorkingDirectory[1];\n            }\n        }\n    }\n\n    if (chdir(Directory.c_str()) < 0)\n    {\n        LOG_ERROR(\"chdir({}) failed {}\", Directory, errno);\n    }\n\n    //\n    // Launch the process.\n    //\n\n    execvpe(Filename, const_cast<char**>(CommandLine.data()), const_cast<char**>(Common->Environment.Variables().data()));\n    FATAL_ERROR(\"execvpe({}) failed: {}\", Filename, strerror(errno));\n\n    return;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    FATAL_ERROR(\"Create process failed\");\n}\n\nCREATE_PROCESS_PARSED CreateProcessParse(gsl::span<gsl::byte> Buffer, int MessageFd, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine parses a create process message.\n\nArguments:\n\n    Buffer - Supplies the create process message.\n\n    MessageFd - Supplies a message port file descriptor.\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    The create process parameters.\n\n--*/\n\n{\n    //\n    // Validate the message size.\n    //\n\n    auto* Message = gslhelpers::try_get_struct<LX_INIT_CREATE_PROCESS>(Buffer);\n    THROW_ERRNO_IF(EINVAL, !Message);\n\n    //\n    // Parse common create process information.\n    //\n\n    CREATE_PROCESS_PARSED Parsed{};\n    int Result = CreateProcessParseCommon(&Parsed.Common, Buffer.subspan(offsetof(LX_INIT_CREATE_PROCESS, Common)), Config);\n    THROW_ERRNO_IF(EINVAL, Result < 0);\n\n    //\n    // Create the eventfd.\n    //\n\n    Parsed.EventFd = eventfd(0, EFD_CLOEXEC);\n    THROW_LAST_ERROR_IF(!Parsed.EventFd);\n\n    //\n    // Set up the standard handles for the process.\n    //\n    //\n\n    for (unsigned short Index = 0; Index < LX_INIT_STD_FD_COUNT; Index += 1)\n    {\n        if (Message->StdFdIds[Index] != LX_INIT_CREATE_PROCESS_USE_CONSOLE)\n        {\n            LXBUS_IPC_MESSAGE_UNMARSHAL_HANDLE_PARAMETERS UnmarshalHandle{};\n            UnmarshalHandle.Input.HandleId = Message->StdFdIds[Index];\n            Result = TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_HANDLE, &UnmarshalHandle));\n            THROW_LAST_ERROR_IF(Result < 0);\n\n            Parsed.StdFd[Index] = UnmarshalHandle.Output.FileDescriptor;\n        }\n    }\n\n    //\n    // Unmarshal the fork token.\n    //\n\n    LXBUS_IPC_MESSAGE_UNMARSHAL_FORK_TOKEN_PARAMETERS UnmarshalForkToken{};\n    UnmarshalForkToken.Input.ForkTokenId = Message->ForkTokenId;\n    Result = TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_FORK_TOKEN, &UnmarshalForkToken));\n    THROW_LAST_ERROR_IF(Result < 0);\n\n    //\n    // Unmarshal the ipc server.\n    //\n\n    if (Message->IpcServerId != LXBUS_IPC_SERVER_ID_INVALID)\n    {\n        LXBUS_IPC_MESSAGE_UNMARSHAL_SERVER_PARAMETERS UnmarshalServer{};\n        UnmarshalServer.Input.ServerId = Message->IpcServerId;\n        Result = TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_SERVER, &UnmarshalServer));\n        THROW_LAST_ERROR_IF(Result < 0);\n\n        if (Parsed.Common.AllowOOBE)\n        {\n            wil::unique_fd LxBusFd{TEMP_FAILURE_RETRY(open(LXBUS_DEVICE_NAME, O_RDWR))};\n            THROW_LAST_ERROR_IF(!LxBusFd);\n\n            LXBUS_CONNECT_SERVER_PARAMETERS ConnectParams{};\n            ConnectParams.Input.Flags = LXBUS_IPC_CONNECT_FLAG_UNNAMED_SERVER;\n            ConnectParams.Input.TimeoutMs = LXBUS_IPC_INFINITE_TIMEOUT;\n            Result = TEMP_FAILURE_RETRY(ioctl(LxBusFd.get(), LXBUS_IOCTL_CONNECT_SERVER, &ConnectParams));\n            THROW_LAST_ERROR_IF(Result < 0);\n\n            Parsed.ServiceFd = ConnectParams.Output.MessagePort;\n        }\n    }\n\n    return Parsed;\n}\n\nint CreateProcessParseCommon(PCREATE_PROCESS_PARSED_COMMON Parsed, gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine parses a create process message.\n\nArguments:\n\n    Parsed - Supplies a buffer to store the common create process parameters.\n\n    Buffer - Supplies the common create process message data.\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    auto* Common = gslhelpers::try_get_struct<LX_INIT_CREATE_PROCESS_COMMON>(Buffer);\n    if (!Common)\n    {\n        LOG_ERROR(\"Invalid message size {}\", Buffer.size());\n        return -1;\n    }\n\n    //\n    // Populate the current working directory. If the path does not begin with a\n    // UNIX path separator or `~`, it is translated.\n    //\n    // N.B. Failure to translate the current working directory is non-fatal.\n    //\n\n    auto* Path = wsl::shared::string::FromSpan(Buffer, Common->CurrentWorkingDirectoryOffset);\n    if ((*Path == '/') || (*Path == '~'))\n    {\n        Parsed->CurrentWorkingDirectory = Path;\n    }\n    else if (*Path != '\\0')\n    {\n        Parsed->CurrentWorkingDirectory = WslPathTranslate(const_cast<char*>(Path), TRANSLATE_FLAG_ABSOLUTE, TRANSLATE_MODE_UNIX);\n        if (Parsed->CurrentWorkingDirectory.empty() && Config.AutoMount)\n        {\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageFailedToTranslate(Path));\n        }\n    }\n\n    //\n    // Initialize the command line will a null-terminator.\n    //\n\n    auto CommandLine = Buffer.subspan(Common->CommandLineOffset);\n    for (unsigned short Index = 0; Index < Common->CommandLineCount; Index += 1)\n    {\n        std::string_view Argument{wsl::shared::string::FromSpan(CommandLine)};\n        Parsed->CommandLine.emplace_back(Argument.data());\n        CommandLine = CommandLine.subspan(Argument.size() + 1);\n    }\n\n    //\n    // If a username was provided, get the password entry for the specified username.\n    // If no username was provided use the one specified in /etc/wsl.conf.\n    // Otherwise, use the default UID from the registry.\n    //\n\n    struct passwd* PasswordEntry = nullptr;\n    auto Username = wsl::shared::string::FromSpan(Buffer, Common->UsernameOffset);\n    if (strlen(Username) != 0)\n    {\n        PasswordEntry = getpwnam(Username);\n        if (PasswordEntry == nullptr)\n        {\n            FATAL_ERROR_EX(EX_NOUSER, \"getpwnam({}) failed {}\", Username, errno);\n        }\n    }\n    else if (Config.DefaultUser.has_value())\n    {\n        PasswordEntry = getpwnam(Config.DefaultUser->c_str());\n        if (PasswordEntry == nullptr)\n        {\n            LOG_ERROR(\"getpwnam({}) failed {}\", Config.DefaultUser->c_str(), errno);\n        }\n    }\n\n    if (PasswordEntry == nullptr)\n    {\n        PasswordEntry = getpwuid(Common->DefaultUid);\n        if (PasswordEntry == nullptr)\n        {\n            LOG_ERROR(\"getpwuid({}) failed {}\", Common->DefaultUid, errno);\n        }\n    }\n\n    Parsed->CommandLine.emplace_back(nullptr);\n    Parsed->Environment = ConfigCreateEnvironmentBlock(Common, Config);\n    Parsed->Filename = wsl::shared::string::FromSpan(Buffer, Common->FilenameOffset);\n    Parsed->ShellOptions = static_cast<CREATE_PROCESS_SHELL_OPTIONS>(Common->ShellOptions);\n    Parsed->Uid = PasswordEntry ? PasswordEntry->pw_uid : ROOT_UID; // If the default user was not found, fall back to root.\n    Parsed->AllowOOBE = WI_IsFlagSet(Common->Flags, LxInitCreateProcessFlagAllowOOBE);\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nint CreateProcessReplyToServer(PCREATE_PROCESS_PARSED Parsed, pid_t CreateProcessPid, int MessageFd)\n\n/*++\n\nRoutine Description:\n\n    This routine replies to the server for a create process message.\n\nArguments:\n\n    Parsed - Supplies a pointer to a create process parsed structure.\n\n    CreateProcessPid - Supplies the pid of a newly created child process.\n\n    MessageFd - Supplies a message port file descriptor.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n    N.B. On failure, this routine will terminate the child process.\n\n--*/\n\n{\n    auto terminateChild = wil::scope_exit([CreateProcessPid]() {\n        if (kill(CreateProcessPid, SIGKILL) < 0)\n        {\n            FATAL_ERROR(\"Failed to kill child process {}\", errno);\n        }\n    });\n\n    //\n    // Marshal the pid of the new child process and send a message\n    // indicating that the child was created.\n    //\n\n    LXBUS_IPC_MESSAGE_MARSHAL_PROCESS_PARAMETERS MarshalProcess{};\n    MarshalProcess.Input.Process = CreateProcessPid;\n    if (TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_MARSHAL_PROCESS, &MarshalProcess)) < 0)\n    {\n        LOG_ERROR(\"Failed to marshal pid {}\", errno);\n        return -1;\n    }\n\n    auto Bytes = UtilWriteBuffer(MessageFd, &MarshalProcess.Output.ProcessId, sizeof(MarshalProcess.Output.ProcessId));\n    if (Bytes < 0)\n    {\n        LOG_ERROR(\"Failed to write ProcessId {}\", errno);\n        return -1;\n    }\n\n    //\n    // Wait for the server to indicate that the process can be continued or\n    // it needs to be terminated.\n    //\n\n    Bytes = TEMP_FAILURE_RETRY(read(MessageFd, &MarshalProcess.Output.ProcessId, sizeof(MarshalProcess.Output.ProcessId)));\n    if (Bytes != sizeof(MarshalProcess.Output.ProcessId))\n    {\n        LOG_ERROR(\"Failed to read (size {}) ProcessId {}\", Bytes, errno);\n        return -1;\n    }\n\n    if (MarshalProcess.Output.ProcessId == 0)\n    {\n        LOG_ERROR(\"Server replied with failure\");\n        return -1;\n    }\n\n    uint64_t EventFdData = 1;\n    Bytes = UtilWriteBuffer(Parsed->EventFd.get(), &EventFdData, sizeof(EventFdData));\n    if (Bytes < 0)\n    {\n        LOG_ERROR(\"Failed to write EventFd {}\", errno);\n        return -1;\n    }\n\n    terminateChild.release();\n    return 0;\n}\n\nint InitCreateSessionLeader(gsl::span<gsl::byte> Buffer, wsl::shared::SocketChannel& Channel, int LxBusFd, wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine creates a session leader from the init process.\n\nArguments:\n\n    Buffer - Supplies the message buffer.\n\n    Channel - Supplies a message channel\n\n    LxBusFd - Supplies an LxBus file descriptor (WSL1 only).\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\ntry\n{\n    int Result = -1;\n    pid_t SessionLeader = -1;\n    wil::unique_fd SessionLeaderFd;\n    struct sockaddr_vm SocketAddress;\n\n    //\n    // N.B. FATAL_ERROR will exit the init process, which will also terminate\n    //      any previously created sessions that are still running. On failure,\n    //      the calling function may choose to continue, in order to preserve\n    //      these previous sessions. The FATAL_ERROR macro should be used with\n    //      care here.\n    //\n\n    //\n    // Validate input parameters.\n    //\n\n    auto* CreateSession = gslhelpers::try_get_struct<LX_INIT_CREATE_SESSION>(Buffer);\n    if (!CreateSession)\n    {\n        FATAL_ERROR(\"Unexpected create session size {}\", Buffer.size());\n    }\n\n    //\n    // Connect to the Windows server for the new session leader and create the\n    // new session leader process.\n    //\n\n    if (LxBusFd >= 0)\n    {\n        //\n        // Unmarshal the console for the session leader.\n        //\n\n        if (CreateSession->ConsoleId == LX_INIT_NO_CONSOLE)\n        {\n            FATAL_ERROR(\"Console required for session leader\");\n        }\n\n        auto TtyFd = UnmarshalConsoleFromServer(Channel.Socket(), CreateSession->ConsoleId);\n        if (!TtyFd)\n        {\n            Result = -1;\n            LOG_ERROR(\"UnmarshalConsoleFromServer failed\");\n            goto InitCreateSessionLeaderExit;\n        }\n\n        SessionLeaderFd = InitConnectToServer(LxBusFd, false);\n        if (!SessionLeaderFd)\n        {\n            Result = -1;\n            goto InitCreateSessionLeaderExit;\n        }\n\n        SessionLeader = UtilCreateChildProcess(\n            \"SessionLeader\", [SessionLeaderFd = std::move(SessionLeaderFd), TtyFd = std::move(TtyFd), &Channel, &Config]() mutable {\n                umask(Config.Umask);\n                Channel.Close();\n\n                THROW_LAST_ERROR_IF(UtilRestoreBlockedSignals() < 0);\n\n                SessionLeaderEntry(SessionLeaderFd.get(), TtyFd.get(), Config);\n            });\n    }\n    else\n    {\n        WaitForBootProcess(Config);\n\n        //\n        // Ensure the /etc/resolv.conf symlink is present.\n        //\n\n        ConfigCreateResolvConfSymlink(Config);\n\n        //\n        // Create a listening socket for the service to connect to and tell the\n        // service which port to use.\n        //\n        // N.B. If creating the socket fails, a message with invalid port number\n        //      should be sent to unblock the wsl service.\n        //\n\n        wil::unique_fd ListenSocket{UtilListenVsockAnyPort(&SocketAddress, 1)};\n        if (!ListenSocket)\n        {\n            SocketAddress.svm_port = -1;\n        }\n\n        LX_INIT_CREATE_SESSION_RESPONSE Response;\n        Response.Header.MessageType = LxInitMessageCreateSessionResponse;\n        Response.Header.MessageSize = sizeof(Response);\n        Response.Port = SocketAddress.svm_port;\n        Channel.SendMessage(Response);\n\n        if (!ListenSocket)\n        {\n            Result = -1;\n            goto InitCreateSessionLeaderExit;\n        }\n\n        // Note: The call to accept() must be done in the child because if accept() takes a long time, it can block the creation\n        // of other session leaders. See https://github.com/microsoft/WSL/issues/9114.\n\n        SessionLeader = UtilCreateChildProcess(\n            \"SessionLeader\", [ListenSocket = std::move(ListenSocket), &Channel, &Config, Mask = Config.Umask, SocketAddress]() {\n                umask(Mask);\n                Channel.Close();\n\n                THROW_LAST_ERROR_IF(UtilRestoreBlockedSignals() < 0);\n\n                wsl::shared::SocketChannel channel{\n                    {UtilAcceptVsock(ListenSocket.get(), SocketAddress, SESSION_LEADER_ACCEPT_TIMEOUT_MS)}, \"SessionLeader\"};\n                if (channel.Socket() < 0)\n                {\n                    LOG_ERROR(\"UtilAcceptVsock() failed for session leader {}\", errno);\n                    _exit(1);\n                }\n\n                SessionLeaderEntryUtilityVm(channel, Config);\n            });\n    }\n\n    if (SessionLeader < 0)\n    {\n        Result = -1;\n        goto InitCreateSessionLeaderExit;\n    }\n\n    Result = 0;\n\nInitCreateSessionLeaderExit:\n    return Result;\n}\nCATCH_RETURN_ERRNO();\n\nint InitConnectToServer(int LxBusFd, bool WaitForServer)\n\n/*++\n\nRoutine Description:\n\n    This routine connects the init process to the lxbus server.\n\nArguments:\n\n    LxBusFd - Supplies an LxBus file descriptor.\n\n    WaitForServer - Supplies true to wait for the server, false otherwise.\n\nReturn Value:\n\n    A message port file descriptor on success, -1 on failure.\n\n--*/\n\n{\n    LXBUS_CONNECT_SERVER_PARAMETERS Connection;\n    int MessageFd;\n    int Result;\n\n    MessageFd = -1;\n    memset(&Connection, 0, sizeof(Connection));\n\n    //\n    // Connect to the server and set the CLOEXEC flag.\n    //\n\n    Connection.Input.ServerName = LX_INIT_SERVER_NAME;\n    Connection.Input.TimeoutMs = LXBUS_IPC_INFINITE_TIMEOUT;\n    if (WaitForServer != false)\n    {\n        Connection.Input.Flags = LXBUS_IPC_CONNECT_FLAG_WAIT_FOR_SERVER_REGISTRATION;\n    }\n\n    Result = TEMP_FAILURE_RETRY(ioctl(LxBusFd, LXBUS_IOCTL_CONNECT_SERVER, &Connection));\n    if (Result < 0)\n    {\n        FATAL_ERROR(\"Failed to connect to server {}\", errno);\n    }\n\n    MessageFd = Connection.Output.MessagePort;\n    Result = fcntl(MessageFd, F_SETFD, FD_CLOEXEC);\n    if (Result < 0)\n    {\n        FATAL_ERROR(\"fcntl failed {}\", errno);\n    }\n\n    return MessageFd;\n}\n\nint InitCreateProcessUtilityVm(\n    gsl::span<gsl::byte> Span,\n    const LX_INIT_CREATE_PROCESS_UTILITY_VM& CreateProcess,\n    wsl::shared::SocketChannel& Channel,\n    const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine creates a process from init.\n\nArguments:\n\n    Span - Supplies the message buffer.\n\n    CreateProcess - Supplies the message\n\n    Channel - Supplies the channel.\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    std::vector<gsl::byte> Buffer;\n    ssize_t BytesRead;\n    ssize_t BytesWritten;\n    pid_t ChildPid;\n    wsl::shared::SocketChannel ControlChannel;\n\n    LX_INIT_PROCESS_EXIT_STATUS ExitStatus;\n    unsigned int Index;\n    bool InteropEnabled;\n    InteropServer InteropServer;\n    int ListenSocket = -1;\n    int Master = -1;\n    CREATE_PROCESS_PARSED_COMMON Parsed = {nullptr};\n    std::vector<gsl::byte> PendingStdin;\n    struct pollfd PollDescriptors[7];\n    pid_t RelayPid = -1;\n    int Result;\n    int SignalFd = -1;\n    struct signalfd_siginfo SignalInfo;\n    sigset_t SignalMask;\n    struct sockaddr_vm SocketAddress;\n    std::vector<wil::unique_fd> Sockets(LX_INIT_UTILITY_VM_CREATE_PROCESS_SOCKET_COUNT);\n    int Status;\n    wil::unique_pipe StdErrPipe;\n    int StdIn = -1;\n    wil::unique_pipe StdInPipe;\n    wil::unique_pipe StdOutPipe;\n    wsl::shared::SocketChannel TerminalControlChannel;\n\n    int TtyFd = -1;\n    struct winsize WindowSize;\n\n    //\n    // Connect an extra socket for OOBE, if requested.\n    //\n\n    if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagAllowOOBE))\n    {\n        Sockets.push_back(wil::unique_fd{});\n    }\n\n    //\n    // Create a listening socket to accept connections for stdin, stdout,\n    // stderr, and the control channel.\n    //\n    // N.B. If creating the socket fails, a message with invalid port number\n    //      should be sent to unblock the wsl service.\n    //\n\n    ListenSocket = UtilListenVsockAnyPort(&SocketAddress, Sockets.size());\n    if (ListenSocket < 0)\n    {\n        SocketAddress.svm_port = -1;\n    }\n\n    //\n    // Tell the service which sockets ports to connect to.\n    //\n\n    Channel.SendResultMessage<uint32_t>(SocketAddress.svm_port);\n\n    //\n    // Exit if creating the listening socket failed.\n    //\n\n    if (ListenSocket < 0)\n    {\n        Result = -1;\n        goto CreateProcessUtilityVmEnd;\n    }\n\n    //\n    // Create a process to relay input and output via sockets. The parent\n    // returns to continue processing messages.\n    //\n\n    RelayPid = fork();\n    if (RelayPid < 0)\n    {\n        FATAL_ERROR(\"fork failed for child process {}\", errno);\n    }\n\n    if (RelayPid > 0)\n    {\n        Result = 0;\n        goto CreateProcessUtilityVmEnd;\n    }\n\n    UtilSetThreadName(\"Relay\");\n\n    //\n    // Move to the correct mount namespace to create the child in.\n    //\n\n    if (ConfigSetMountNamespace(WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsElevated)) < 0)\n    {\n        Result = -1;\n        goto CreateProcessUtilityVmEnd;\n    }\n\n    //\n    // Accept connections from the wsl service.\n    //\n\n    for (auto& Socket : Sockets)\n    {\n        Socket.reset(UtilAcceptVsock(ListenSocket, SocketAddress));\n        if (Socket.get() < 0)\n        {\n            Result = -1;\n            goto CreateProcessUtilityVmEnd;\n        }\n    }\n\n    //\n    // Close the listening socket.\n    //\n\n    CLOSE(ListenSocket);\n    ListenSocket = -1;\n\n    //\n    // Initialize interop.\n    //\n\n    InteropEnabled = WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsInteropEnabled) && Config.InteropEnabled;\n    if (InteropEnabled)\n    {\n        Result = InteropServer.Create();\n        if (Result < 0)\n        {\n            goto CreateProcessUtilityVmEnd;\n        }\n    }\n\n    //\n    // For any of the standard handles that are not consoles, create pipes.\n    //\n\n    if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdInConsole))\n    {\n        try\n        {\n            StdInPipe = wil::unique_pipe::create(O_CLOEXEC);\n        }\n        catch (...)\n        {\n            LOG_CAUGHT_EXCEPTION_MSG(\"pipe failed\");\n            Result = -1;\n            goto CreateProcessUtilityVmEnd;\n        }\n    }\n\n    if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdOutConsole))\n    {\n        try\n        {\n            StdOutPipe = wil::unique_pipe::create(O_CLOEXEC);\n        }\n        catch (...)\n        {\n            LOG_CAUGHT_EXCEPTION_MSG(\"pipe failed\");\n            Result = -1;\n            goto CreateProcessUtilityVmEnd;\n        }\n    }\n\n    if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdErrConsole))\n    {\n        try\n        {\n            StdErrPipe = wil::unique_pipe::create(O_CLOEXEC);\n        }\n        catch (...)\n        {\n            LOG_CAUGHT_EXCEPTION_MSG(\"pipe failed\");\n            Result = -1;\n            goto CreateProcessUtilityVmEnd;\n        }\n    }\n\n    //\n    // Mark the relay process as a subreaper.\n    //\n\n    Result = prctl(PR_SET_CHILD_SUBREAPER, 1);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"prctl failed {}\", errno);\n        goto CreateProcessUtilityVmEnd;\n    }\n\n    //\n    // Block SIGCHLD.\n    // N.B. This needs to be done before forking so SIGCHILD isn't missed\n    // in case the child exits before the relay masks the signal.\n    //\n\n    sigemptyset(&SignalMask);\n    sigaddset(&SignalMask, SIGCHLD);\n    Result = sigprocmask(SIG_BLOCK, &SignalMask, nullptr);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"sigprocmask failed {}\", errno);\n        goto CreateProcessUtilityVmEnd;\n    }\n\n    //\n    // Create a pseudoterminal and child process.\n    //\n\n    memset(&WindowSize, 0, sizeof(WindowSize));\n    WindowSize.ws_col = CreateProcess.Columns;\n    WindowSize.ws_row = CreateProcess.Rows;\n    Result = forkpty(&Master, NULL, NULL, &WindowSize);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"forkpty failed {}\", errno);\n        goto CreateProcessUtilityVmEnd;\n    }\n\n    if (Result == 0)\n    {\n        //\n        // Reset the signal masks.\n        //\n\n        sigemptyset(&SignalMask);\n        Result = sigprocmask(SIG_SETMASK, &SignalMask, nullptr);\n        if (Result < 0)\n        {\n            LOG_ERROR(\"sigprocmask failed {}\", errno);\n            goto CreateProcessUtilityVmEnd;\n        }\n\n        //\n        // Duplicate stdin to get a file descriptor representing the controlling\n        // terminal.\n        //\n\n        TtyFd = dup(STDIN_FILENO);\n        if (TtyFd < 0)\n        {\n            LOG_ERROR(\"dup failed {}\", errno);\n            goto CreateProcessUtilityVmEnd;\n        }\n\n        //\n        // Replace any standard file descriptor that is not a console with a\n        // pipe.\n        //\n\n        if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdInConsole))\n        {\n            Result = dup2(StdInPipe.read().get(), STDIN_FILENO);\n            if (Result < 0)\n            {\n                LOG_ERROR(\"dup2 failed {}\", errno);\n                goto CreateProcessUtilityVmEnd;\n            }\n        }\n\n        if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdOutConsole))\n        {\n            Result = dup2(StdOutPipe.write().get(), STDOUT_FILENO);\n            if (Result < 0)\n            {\n                LOG_ERROR(\"dup2 failed {}\", errno);\n                goto CreateProcessUtilityVmEnd;\n            }\n        }\n\n        if (WI_IsFlagClear(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdErrConsole))\n        {\n            Result = dup2(StdErrPipe.write().get(), STDERR_FILENO);\n            if (Result < 0)\n            {\n                LOG_ERROR(\"dup2 failed {}\", errno);\n                goto CreateProcessUtilityVmEnd;\n            }\n        }\n\n        //\n        // Parse common create process information and create the process in\n        // the child.\n        //\n\n        Result = CreateProcessParseCommon(&Parsed, Span.subspan(offsetof(LX_INIT_CREATE_PROCESS_UTILITY_VM, Common)), Config);\n        if (Result < 0)\n        {\n            goto CreateProcessUtilityVmEnd;\n        }\n\n        //\n        // Set the unique interop socket name as an environment variable.\n        //\n\n        if (InteropEnabled)\n        {\n            Result = Parsed.Environment.AddVariableNoThrow(WSL_INTEROP_ENV, InteropServer.Path());\n            if (Result < 0)\n            {\n                goto CreateProcessUtilityVmEnd;\n            }\n        }\n\n        CreateProcessCommon(&Parsed, TtyFd, Sockets.size() >= 6 ? Sockets[5].get() : -1, Config);\n        TtyFd = -1;\n        goto CreateProcessUtilityVmEnd;\n    }\n\n    //\n    // Parent...\n    //\n\n    ChildPid = Result;\n\n    if (Sockets.size() >= 6)\n    {\n        Sockets[5].reset();\n    }\n\n    //\n    // Add the child pid to the thread name for convenience.\n    //\n\n    UtilSetThreadName(std::format(\"Relay({})\", ChildPid).c_str());\n\n    //\n    // Close the unneeded ends of the std pipes.\n    //\n\n    StdInPipe.read().reset();\n    StdOutPipe.write().reset();\n    StdErrPipe.write().reset();\n\n    //\n    // Create a signalfd to detect when the child process exits.\n    //\n\n    SignalFd = signalfd(-1, &SignalMask, 0);\n    if (SignalFd < 0)\n    {\n        Result = -1;\n        LOG_ERROR(\"signalfd failed {}\", errno);\n        goto CreateProcessUtilityVmEnd;\n    }\n\n    //\n    // Duplicate the stdin file descriptor.\n    //\n\n    if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdInConsole))\n    {\n        StdIn = dup(Master);\n    }\n    else\n    {\n        StdIn = dup(StdInPipe.write().get());\n        StdInPipe.write().reset();\n    }\n\n    if (StdIn < 0)\n    {\n        Result = -1;\n        LOG_ERROR(\"dup failed {}\", errno);\n        goto CreateProcessUtilityVmEnd;\n    }\n\n    THROW_LAST_ERROR_IF(fcntl(StdIn, F_SETFL, O_NONBLOCK) < 0);\n\n    //\n    // Fill the poll descriptors.\n    //\n    // N.B. Any files descriptors that are -1 are ignored by poll.\n    //\n\n    PollDescriptors[0].fd = Sockets[0].get();\n    PollDescriptors[0].events = POLLIN;\n    PollDescriptors[1].fd = StdOutPipe.read().get();\n    PollDescriptors[1].events = POLLIN;\n    PollDescriptors[2].fd = StdErrPipe.read().get();\n    PollDescriptors[2].events = POLLIN;\n    PollDescriptors[3].fd = Master;\n    PollDescriptors[3].events = POLLIN;\n    PollDescriptors[4].fd = InteropServer.Socket();\n    PollDescriptors[4].events = POLLIN;\n    PollDescriptors[5].fd = SignalFd;\n    PollDescriptors[5].events = POLLIN;\n    PollDescriptors[6].fd = Sockets[3].get();\n    PollDescriptors[6].events = POLLIN;\n\n    TerminalControlChannel = {{Sockets[3].get()}, \"TerminalControl\"};\n\n    //\n    // This is required because sequence numbers can be reset during handover from wsl.exe to wslhost.exe.\n    //\n\n    TerminalControlChannel.IgnoreSequenceNumbers();\n\n    ControlChannel = {{Sockets[4].get()}, \"Control\"};\n\n    //\n    // Begin relaying data from the stdin socket to stdin file descriptor and\n    // from the master PTY endpoint and output pipes to the stdout and stderr\n    // sockets.\n    //\n\n    for (;;)\n    {\n        BytesWritten = 0;\n\n        Result = poll(PollDescriptors, COUNT_OF(PollDescriptors), PendingStdin.empty() ? -1 : 100);\n        if (!PendingStdin.empty())\n        {\n            BytesWritten = write(StdIn, PendingStdin.data(), PendingStdin.size());\n            if (BytesWritten < 0)\n            {\n                if (errno != EAGAIN && errno != EWOULDBLOCK)\n                {\n                    LOG_ERROR(\"delayed stdin write failed {}, ChildPid={}\", errno, ChildPid);\n                }\n            }\n            else if (BytesWritten <= PendingStdin.size()) // Partial or complete write\n            {\n                PendingStdin.erase(PendingStdin.begin(), PendingStdin.begin() + BytesWritten);\n            }\n            else\n            {\n                LOG_ERROR(\"Unexpected write result {}, pending={}\", BytesWritten, PendingStdin.size());\n            }\n        }\n\n        if (Result < 0)\n        {\n            LOG_ERROR(\"poll failed {}\", errno);\n            break;\n        }\n\n        //\n        // Relay input from the stdin socket to the stdin file descriptor.\n        //\n\n        if (PollDescriptors[0].revents & (POLLIN | POLLHUP | POLLERR) && PendingStdin.empty())\n        {\n            BytesRead = UtilReadBuffer(Sockets[0].get(), Buffer);\n            if (BytesRead < 0)\n            {\n                LOG_ERROR(\"read failed {}\", errno);\n                break;\n            }\n\n            //\n            // A zero-byte read means that the stdin socket has closed. Close\n            // the corresponding stdin file descriptor and remove the stdin\n            // socket from the poll descriptors list.\n            //\n\n            if (BytesRead == 0)\n            {\n                CLOSE(StdIn);\n                StdIn = -1;\n                PollDescriptors[0].fd = -1;\n\n                //\n                // If stdin is a console, close the pseudoterminal master.\n                //\n\n                if ((WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdInConsole)) && (Master != -1))\n                {\n                    CLOSE(Master);\n                    Master = -1;\n                    PollDescriptors[3].fd = -1;\n                }\n            }\n            else\n            {\n                BytesWritten = write(StdIn, Buffer.data(), BytesRead);\n                if (BytesWritten < 0)\n                {\n                    //\n                    // If writing on stdin's pipe would block, mark the write as pending and continue.\n                    // This is required blocking on the write() could lead to a deadlock if the child process\n                    // is blocking trying to write on stderr / stdout while the relay tries to write stdin.\n                    //\n\n                    if (errno == EWOULDBLOCK || errno == EAGAIN)\n                    {\n                        assert(PendingStdin.empty());\n                        PendingStdin.assign(Buffer.begin(), Buffer.begin() + BytesRead);\n                    }\n                    else\n                    {\n                        LOG_ERROR(\"write failed {}\", errno);\n                        break;\n                    }\n                }\n            }\n        }\n\n        //\n        // Relay output from the stdout and stderr pipes.\n        //\n\n        for (Index = 1; Index < 3; Index += 1)\n        {\n            if (PollDescriptors[Index].revents & (POLLIN | POLLHUP | POLLERR))\n            {\n                BytesRead = UtilReadBuffer(PollDescriptors[Index].fd, Buffer);\n                if (BytesRead <= 0)\n                {\n                    if (BytesRead < 0)\n                    {\n                        LOG_ERROR(\"read failed {} {}\", BytesRead, errno);\n                    }\n\n                    PollDescriptors[Index].fd = -1;\n                    UtilSocketShutdown(Sockets[Index].get(), SHUT_WR);\n                    continue;\n                }\n\n                BytesWritten = UtilWriteBuffer(Sockets[Index].get(), Buffer.data(), BytesRead);\n                if (BytesWritten < 0)\n                {\n                    if (errno == EPIPE)\n                    {\n                        CLOSE(PollDescriptors[Index].fd);\n                        PollDescriptors[Index].fd = -1;\n\n                        if (Index == 1)\n                        {\n                            StdOutPipe.read().reset();\n                        }\n                        else if (Index == 2)\n                        {\n                            StdErrPipe.read().reset();\n                        }\n                    }\n                    else\n                    {\n                        LOG_ERROR(\"write failed {}, index={}, ChildPid={}, fd={}\", errno, Index, ChildPid, Sockets[Index].get());\n                    }\n                }\n            }\n        }\n\n        //\n        // Relay output from the PTY master to the stdout or stderr socket.\n        //\n\n        if (PollDescriptors[3].revents & (POLLIN | POLLHUP | POLLERR))\n        {\n            BytesRead = UtilReadBuffer(Master, Buffer);\n\n            //\n            // N.B. The pty will fail with EIO on read on hangup instead of\n            //      indicating EOF.\n            //\n\n            if (BytesRead == 0 || (BytesRead < 0 && errno == EIO))\n            {\n                PollDescriptors[3].fd = -1;\n                if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdOutConsole))\n                {\n                    UtilSocketShutdown(Sockets[1].get(), SHUT_WR);\n                }\n\n                if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdErrConsole))\n                {\n                    UtilSocketShutdown(Sockets[2].get(), SHUT_WR);\n                }\n            }\n            else if (BytesRead < 0)\n            {\n                LOG_ERROR(\"read failed {} {}\", BytesRead, errno);\n                break;\n            }\n            else\n            {\n                if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdOutConsole))\n                {\n                    BytesWritten = UtilWriteBuffer(Sockets[1].get(), Buffer.data(), BytesRead);\n                }\n                else if (WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsStdErrConsole))\n                {\n                    BytesWritten = UtilWriteBuffer(Sockets[2].get(), Buffer.data(), BytesRead);\n                }\n                else\n                {\n                    LOG_ERROR(\"Unexpected output from PTY master\");\n                }\n\n                if (BytesWritten < 0)\n                {\n                    LOG_ERROR(\"write failed {}\", errno);\n                    break;\n                }\n            }\n        }\n\n        //\n        // Ensure all data has been written.\n        //\n\n        if (BytesWritten > 0)\n        {\n            continue;\n        }\n\n        //\n        // Handle interop requests by relaying create process messages from\n        // children over the control channel.\n        //\n\n        if (PollDescriptors[4].revents & POLLIN)\n        {\n\n            wsl::shared::SocketChannel channel(InteropServer.Accept(), \"InteropRelay\");\n            if (channel.Socket() < 0)\n            {\n                continue;\n            }\n\n            auto [Header, Span] = channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\n            if (Header != nullptr)\n            {\n                try\n                {\n                    ConfigHandleInteropMessage(\n                        channel, ControlChannel, WI_IsFlagSet(CreateProcess.Common.Flags, LxInitCreateProcessFlagsElevated), Span, Header, Config);\n                }\n                CATCH_LOG();\n            }\n        }\n\n        //\n        // Handle signalfd.\n        //\n\n        if (PollDescriptors[5].revents & POLLIN)\n        {\n            BytesRead = TEMP_FAILURE_RETRY(read(PollDescriptors[5].fd, &SignalInfo, sizeof(SignalInfo)));\n            if (BytesRead != sizeof(SignalInfo))\n            {\n                LOG_ERROR(\"read failed {} {}\", BytesRead, errno);\n                break;\n            }\n\n            if (SignalInfo.ssi_signo != SIGCHLD)\n            {\n                LOG_ERROR(\"Unexpected signal {}\", SignalInfo.ssi_signo);\n                break;\n            }\n\n            //\n            // Reap any zombie child processes.\n            //\n\n            for (;;)\n            {\n                Result = waitpid(-1, &Status, WNOHANG);\n                if (Result <= 0)\n                {\n                    break;\n                }\n\n                //\n                // If the child process exits, write the exit status message\n                // via the control channel and shut down the stdin / stdout /\n                // stderr sockets.\n                //\n\n                if (ChildPid == Result)\n                {\n                    if (WIFEXITED(Status))\n                    {\n                        Status = WEXITSTATUS(Status);\n                    }\n\n                    try\n                    {\n\n                        ExitStatus.Header.MessageType = LxInitMessageExitStatus;\n                        ExitStatus.Header.MessageSize = sizeof(ExitStatus);\n                        ExitStatus.ExitCode = Status;\n                        ControlChannel.SendMessage(ExitStatus);\n\n                        // The result is purposefully ignored here.\n                        ControlChannel.ReceiveMessage<LX_INIT_PROCESS_EXIT_STATUS>();\n                    }\n                    catch (...)\n                    {\n                        Result = -1;\n                        LOG_ERROR(\"Failed to write exit status {}\", errno);\n                        break;\n                    }\n\n                    ChildPid = -1;\n                    UtilSocketShutdown(Sockets[0].get(), SHUT_RD);\n                    UtilSocketShutdown(Sockets[1].get(), SHUT_WR);\n                    UtilSocketShutdown(Sockets[2].get(), SHUT_WR);\n                    PollDescriptors[6].fd = -1;\n                }\n            }\n\n            //\n            // Exit the relay if no more children exist.\n            //\n\n            if (Result < 0)\n            {\n                if (errno != ECHILD)\n                {\n                    LOG_ERROR(\"waitpid failed {}\", errno);\n                }\n\n                break;\n            }\n        }\n\n        //\n        // Process messages from wsl.exe / wslhost.exe.\n        //\n\n        if (PollDescriptors[6].revents & POLLIN)\n        {\n            auto [Message, _] = TerminalControlChannel.ReceiveMessageOrClosed<LX_INIT_WINDOW_SIZE_CHANGED>();\n\n            //\n            // A zero-byte read means that the control channel has been closed\n            // and that the relay process should exit.\n            //\n\n            if (Message == nullptr)\n            {\n                break;\n            }\n\n            memset(&WindowSize, 0, sizeof(WindowSize));\n            WindowSize.ws_col = Message->Columns;\n            WindowSize.ws_row = Message->Rows;\n            Result = ioctl(Master, TIOCSWINSZ, &WindowSize);\n            if (Result < 0)\n            {\n                LOG_ERROR(\"ioctl(TIOCSWINSZ) failed {}\", errno);\n            }\n        }\n    }\n\n    //\n    // Cleanly shut down the sockets.\n    //\n    // N.B. If the socket has already been shut down, this is a no-op.\n    //\n\n    UtilSocketShutdown(Sockets[0].get(), SHUT_RD);\n    UtilSocketShutdown(Sockets[1].get(), SHUT_WR);\n    UtilSocketShutdown(Sockets[2].get(), SHUT_WR);\n    UtilSocketShutdown(Sockets[3].get(), SHUT_RD);\n    UtilSocketShutdown(Sockets[4].get(), SHUT_WR);\n    Result = 0;\n\nCreateProcessUtilityVmEnd:\n    if (ListenSocket != -1)\n    {\n        CLOSE(ListenSocket);\n    }\n\n    if (Master != -1)\n    {\n        CLOSE(Master);\n    }\n\n    if (SignalFd != -1)\n    {\n        CLOSE(SignalFd);\n    }\n\n    if (StdIn != -1)\n    {\n        CLOSE(StdIn);\n    }\n\n    if (TtyFd != -1)\n    {\n        CLOSE(TtyFd);\n    }\n\n    //\n    // The interop server needs to be manually reset so it deletes\n    // its interop socket. See https://github.com/microsoft/WSL/issues/7506.\n    //\n\n    InteropServer.Reset();\n\n    //\n    // The relay process should always exit.\n    //\n\n    if (RelayPid == 0)\n    {\n        _exit(Result);\n    }\n\n    return Result;\n}\n\nvoid InitEntry(int Argc, char* Argv[])\n\n/*++\n\nRoutine Description:\n\n    This routine is the entry point for the init process.\n\nArguments:\n\n    Argc - Supplies command line argument count.\n\n    Argv - Supplies command line arguments.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    //\n    // Initialize the startup environment.\n    //\n\n    try\n    {\n        wil::ScopedWarningsCollector collector;\n\n        auto config = ConfigInitializeCommon(g_SavedSignalActions);\n\n        //\n        // Check if the binary is being run on WSL or in a Utility VM.\n        //\n\n        if (!UtilIsUtilityVm())\n        {\n            InitEntryWsl(config);\n        }\n        else\n        {\n            InitEntryUtilityVm(config);\n        }\n    }\n    catch (...)\n    {\n        LOG_CAUGHT_EXCEPTION();\n    }\n\n    FATAL_ERROR(\"Init not expected to exit\");\n    return;\n}\n\nvoid InitEntryUtilityVm(wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine is the entry point for the init process when running inside\n    a utility VM.\n\nArguments:\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    UtilSetThreadName(\"init-distro\");\n\n    //\n    // Set the close-on-exec flag on the socket file descriptor inherited from mini_init.\n    //\n\n    wsl::shared::SocketChannel channel{wil::unique_fd{LX_INIT_UTILITY_VM_INIT_SOCKET_FD}, \"init\"};\n    if (fcntl(channel.Socket(), F_SETFD, FD_CLOEXEC) < 0)\n    {\n        FATAL_ERROR(\"fcntl failed {}\", errno);\n        return;\n    }\n\n    if (getenv(LX_WSL2_DISTRO_READ_ONLY_ENV) != nullptr)\n    {\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageReadOnlyDistro());\n        unsetenv(LX_WSL2_DISTRO_READ_ONLY_ENV);\n    }\n\n    auto Value = getenv(LX_WSL2_NETWORKING_MODE_ENV);\n    if (Value != nullptr)\n    {\n        Config.NetworkingMode = static_cast<LX_MINI_INIT_NETWORKING_MODE>(std::atoi(Value));\n        unsetenv(LX_WSL2_NETWORKING_MODE_ENV);\n    }\n\n    Value = getenv(LX_WSL2_VM_ID_ENV);\n    if (Value != nullptr)\n    {\n        Config.VmId = Value;\n\n        //\n        // Unset the environment variable for user distros.\n        //\n\n        Value = getenv(LX_WSL2_SHARED_MEMORY_OB_DIRECTORY);\n        if (!Value)\n        {\n            unsetenv(LX_WSL2_VM_ID_ENV);\n        }\n    }\n\n    //\n    // If the boot.systemd option is specified in /etc/wsl.conf, launch the distro init process as pid 1.\n    // WSL init and session leaders continue as children of the distro init process.\n    //\n\n    const auto pid = getenv(LX_WSL_PID_ENV);\n    assert(pid != nullptr);\n    unsetenv(LX_WSL_PID_ENV);\n\n    //\n    // Send the create instance result to the service.\n    //\n\n    wsl::shared::MessageWriter<LX_MINI_INIT_CREATE_INSTANCE_RESULT> message;\n    message->Pid = std::stoul(pid);\n    message->Result = 0;\n\n    auto Warnings = wil::ScopedWarningsCollector::ConsumeWarnings();\n    if (!Warnings.empty())\n    {\n        message.WriteString(message->WarningsOffset, Warnings);\n    }\n\n    channel.SendMessage<LX_MINI_INIT_CREATE_INSTANCE_RESULT>(message.Span());\n\n    std::optional<pid_t> distroInitPid;\n    const auto distroInitPidString = getenv(LX_WSL2_DISTRO_INIT_PID);\n    if (distroInitPidString != nullptr)\n    {\n        distroInitPid = std::stoul(distroInitPidString);\n        unsetenv(LX_WSL2_DISTRO_INIT_PID);\n    }\n\n    std::vector<gsl::byte> Buffer;\n    if (Config.BootInit)\n    {\n        int SocketPair[2];\n        if (socketpair(AF_UNIX, (SOCK_STREAM | SOCK_CLOEXEC), 0, SocketPair) < 0)\n        {\n            FATAL_ERROR(\"socketpair failed {}\", errno);\n        }\n\n        wil::unique_fd BootStartReadSocket{SocketPair[0]};\n        Config.BootStartWriteSocket = SocketPair[1];\n\n        const int ChildPid = fork();\n        if (ChildPid < 0)\n        {\n            FATAL_ERROR(\"fork failed {}\", errno);\n        }\n        else if (ChildPid != 0)\n        {\n            UtilSetThreadName(\"init-systemd\");\n\n            //\n            // Wait to boot the distro init process until the first session leader has been created.\n            // This ensures that the entire boot is not done when a distro is trigger-started by accessing \\\\wsl.localhost.\n            //\n\n            auto Message = wsl::shared::socket::RecvMessage(BootStartReadSocket.get(), Buffer);\n            if (Message.empty())\n            {\n                FATAL_ERROR(\"recv failed {}\", errno);\n            }\n\n            auto* StartMessage = gslhelpers::get_struct<MESSAGE_HEADER>(Message);\n            if (StartMessage->MessageType != LxInitMessageStartDistroInit)\n            {\n                FATAL_ERROR(\"unexpected Messagetype {}\", StartMessage->MessageType);\n            }\n\n            //\n            // Initialize distro init arguments and environment.\n            //\n\n            auto InitializeStringVector = [&](std::vector<const char*>& PointerVector,\n                                              std::vector<std::string>& StringVector,\n                                              const std::optional<std::string>& String) {\n                if (String.has_value())\n                {\n                    std::string_view StringView{String.value()};\n                    while (!StringView.empty())\n                    {\n                        StringVector.emplace_back(UtilStringNextToken(StringView, \" \"));\n                    }\n\n                    for (const auto& TokenString : StringVector)\n                    {\n                        PointerVector.push_back(TokenString.c_str());\n                    }\n                }\n\n                PointerVector.push_back(nullptr);\n            };\n\n            CreateWslSystemdUnits(Config);\n\n            const char* Argv[] = {INIT_PATH, nullptr};\n            std::vector<const char*> Env;\n            std::vector<std::string> Environment;\n            InitializeStringVector(\n                Env, Environment, \"container=wsl container_host_id=windows container_host_version_id=\" WSL_PACKAGE_VERSION);\n\n            execvpe(INIT_PATH, const_cast<char**>(Argv), const_cast<char**>(Env.data()));\n            LOG_ERROR(\"execvpe({}) failed {}\", INIT_PATH, errno);\n            _exit(1);\n        }\n\n        //\n        // Keep track of the new pid for WSL init.\n        //\n\n        Config.InitPid = getpid();\n    }\n\n    //\n    // Loop waiting on the socket for requests from the Windows server.\n    // A zero-byte read means that the connection to the wsl has been closed and the init daemon should shut down.\n    //\n\n    wil::unique_fd SignalFd;\n    std::vector<pollfd> PollDescriptors(1);\n    PollDescriptors[0].fd = channel.Socket();\n    PollDescriptors[0].events = POLLIN;\n\n    //\n    // If a distro init pid was passed, set up a signalfd to watch it so the distribution can be terminated\n    // when that process exits.\n    //\n\n    if (distroInitPid.has_value())\n    {\n        // Reset sigchld so we get notified when children exit.\n        signal(SIGCHLD, SIG_DFL);\n\n        sigset_t SignalMask;\n        sigemptyset(&SignalMask);\n        sigaddset(&SignalMask, SIGCHLD);\n        if (UtilSaveBlockedSignals(SignalMask) < 0)\n        {\n            FATAL_ERROR(\"sigprocmask failed {}\", errno);\n        }\n\n        SignalFd = {signalfd(-1, &SignalMask, SFD_CLOEXEC)};\n        if (!SignalFd)\n        {\n            FATAL_ERROR(\"signalfd failed {}\", errno);\n        }\n\n        PollDescriptors.resize(2);\n        PollDescriptors[1].fd = SignalFd.get();\n        PollDescriptors[1].events = POLLIN;\n    }\n\n    for (;;)\n    {\n        auto Result = poll(PollDescriptors.data(), PollDescriptors.size(), -1);\n        if (Result < 0)\n        {\n            FATAL_ERROR(\"poll failed {}\", errno);\n        }\n\n        if (PollDescriptors[0].revents & (POLLHUP | POLLERR))\n        {\n            break;\n        }\n        else if (PollDescriptors[0].revents & POLLIN)\n        {\n            auto [Header, Span] = channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\n            if (Header == nullptr)\n            {\n                break;\n            }\n\n            switch (Header->MessageType)\n            {\n            case LxInitMessageCreateSession:\n                if (InitCreateSessionLeader(Span, channel, -1, Config) < 0)\n                {\n                    FATAL_ERROR(\"InitCreateSessionLeader failed\");\n                }\n\n                break;\n\n            case LxInitMessageInitialize:\n                ConfigInitializeInstance(channel, Span, Config);\n                break;\n\n            case LxInitMessageTimezoneInformation:\n                UpdateTimezone(Span, Config);\n                break;\n\n            case LxInitMessageRemountDrvfs:\n\n                //\n                // If systemd is enabled, some units (like snapd) might be in the process\n                // of creating mountpoints.\n                // Because these mountpoints should be available in both namespaces, the elevated\n                // and non-elevated namespaces shouldn't fork until systemd is done initializing.\n                //\n\n                WaitForBootProcess(Config);\n                ConfigRemountDrvFs(Span, channel, Config);\n                break;\n\n            case LxInitMessageTerminateInstance:\n                InitTerminateInstance(Span, channel, Config);\n                break;\n\n            case LxInitCreateProcess:\n                ProcessCreateProcessMessage(channel, Span);\n                break;\n\n            default:\n                FATAL_ERROR(\"Unexpected message {}\", Header->MessageType);\n            }\n        }\n\n        if (distroInitPid.has_value() && PollDescriptors[1].revents & POLLIN)\n        {\n            signalfd_siginfo SignalInfo{};\n            auto BytesRead = TEMP_FAILURE_RETRY(read(PollDescriptors[1].fd, &SignalInfo, sizeof(SignalInfo)));\n            if (BytesRead != sizeof(SignalInfo))\n            {\n                FATAL_ERROR(\"read failed {} {}\", BytesRead, errno);\n            }\n\n            if (SignalInfo.ssi_signo != SIGCHLD)\n            {\n                LOG_ERROR(\"Unexpected signal {}\", SignalInfo.ssi_signo);\n                continue;\n            }\n\n            int Status{};\n            auto Pid = waitpid(-1, &Status, WNOHANG);\n            if (Result == 0)\n            {\n                continue;\n            }\n            else if (Result > 0)\n            {\n                if (Pid == distroInitPid.value())\n                {\n                    LOG_ERROR(\"Init has exited. Terminating distribution\");\n                    break;\n                }\n            }\n            else if (errno != ECHILD)\n            {\n                FATAL_ERROR(\"waitpid failed {}\", errno);\n            }\n        }\n    }\n\n    InitTerminateInstanceInternal(Config);\n    return;\n}\n\nvoid InitEntryWsl(wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine is the entry point for the init process when running inside\n    WSL.\n\nArguments:\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    auto Warnings = wil::ScopedWarningsCollector::ConsumeWarnings();\n    if (!Warnings.empty())\n    {\n        LOG_ERROR(\"{}\", Warnings.c_str());\n    }\n\n    //\n    // Connect to the windows server.\n    //\n\n    wil::unique_fd LxBusFd = TEMP_FAILURE_RETRY(open(LXBUS_DEVICE_NAME, O_RDWR | O_CLOEXEC));\n    if (!LxBusFd)\n    {\n        FATAL_ERROR(\"open({}) failed {}\", LXBUS_DEVICE_NAME, errno);\n        return;\n    }\n\n    wsl::shared::SocketChannel Channel{wil::unique_fd{InitConnectToServer(LxBusFd.get(), true)}, \"init\"};\n    if (Channel.Socket() < 0)\n    {\n        return;\n    }\n\n    //\n    // Loop waiting on the message port for requests from the Windows server.\n    //\n\n    std::vector<gsl::byte> Buffer;\n    ssize_t BytesRead;\n    for (;;)\n    {\n        BytesRead = UtilReadMessageLxBus(Channel.Socket(), Buffer, true);\n        if (BytesRead < 0)\n        {\n            return;\n        }\n\n        auto Message = gsl::make_span(Buffer.data(), BytesRead);\n        auto* Header = gslhelpers::try_get_struct<MESSAGE_HEADER>(Message);\n        if (!Header)\n        {\n            FATAL_ERROR(\"Invalid message size {}\", Message.size());\n        }\n\n        switch (Header->MessageType)\n        {\n        case LxInitMessageCreateSession:\n            if (InitCreateSessionLeader(Message, Channel, LxBusFd.get(), Config) < 0)\n            {\n                //\n                // If this distro has no children, exit on failure.\n                //\n\n                int status;\n                if (waitpid(-1, &status, WNOHANG) == -1 && (errno == ECHILD))\n                {\n                    FATAL_ERROR(\"InitCreateSessionLeader failed\");\n                }\n\n                LOG_ERROR(\"InitCreateSessionLeader failed\");\n            }\n\n            break;\n\n        case LxInitMessageNetworkInformation:\n            ConfigUpdateNetworkInformation(Message, Config);\n            break;\n\n        case LxInitMessageInitialize:\n            ConfigInitializeInstance(Channel, Message, Config);\n            break;\n\n        case LxInitMessageTimezoneInformation:\n            UpdateTimezone(Message, Config);\n            break;\n\n        case LxInitMessageTerminateInstance:\n            InitTerminateInstance(Message, Channel, Config);\n            break;\n\n        default:\n            FATAL_ERROR(\"Unexpected message {}\", Header->MessageType);\n        }\n    }\n\n    return;\n}\n\nvoid InitTerminateInstance(gsl::span<gsl::byte> Buffer, wsl::shared::SocketChannel& Channel, wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine processes a terminate instance request from the service.\n\nArguments:\n\n    Buffer - Supplies the message buffer.\n\n    Channel - Supplies a channel to send the response.\n\n    Config - Supplies the distribution config.\n\nReturn Value:\n\n    None.\n\n--*/\ntry\n{\n\n    auto* Message = gslhelpers::try_get_struct<LX_INIT_TERMINATE_INSTANCE>(Buffer);\n    if (!Message)\n    {\n        FATAL_ERROR(\"Invalid message size {}\", Buffer.size());\n    }\n\n    //\n    // Attempt to stop the plan9 server, if it is not able to be stopped because of an\n    // in-use file, reply to the service that the instance could not be terminated.\n    //\n\n    if (!StopPlan9Server(Message->Force, Config))\n    {\n        Channel.SendResultMessage<bool>(false);\n        return;\n    }\n\n    InitTerminateInstanceInternal(Config);\n}\nCATCH_LOG();\n\nvoid InitTerminateInstanceInternal(const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine attempts to cleanly terminate the instance.\n\nArguments:\n\n    Config - Supplies the distribution config.\n\nReturn Value:\n\n    None.\n\n--*/\ntry\n{\n    //\n    // If systemd is enabled, attempt to poweroff the instance via systemctl.\n    //\n\n    if (Config.BootInit && !Config.BootStartWriteSocket)\n    {\n        THROW_LAST_ERROR_IF(UtilSetSignalHandlers(g_SavedSignalActions, false) < 0);\n\n        if (UtilExecCommandLine(\"systemctl poweroff\", nullptr) == 0)\n        {\n            std::this_thread::sleep_for(std::chrono::milliseconds(Config.BootInitTimeout));\n            LOG_ERROR(\"systemctl poweroff did not terminate the instance in {} ms, calling reboot(RB_POWER_OFF)\", Config.BootInitTimeout);\n        }\n    }\n\n    reboot(RB_POWER_OFF);\n    FATAL_ERROR(\"reboot(RB_POWER_OFF) failed {}\", errno);\n}\nCATCH_LOG();\n\nvoid InstallSystemdUnit(const char* Path, const std::string& Name, const char* Content)\ntry\n{\n    std::string target = std::format(\"{}/{}.service\", Path, Name);\n    std::string defaultTarget = std::format(\"{}/default.target.wants\", Path);\n    THROW_LAST_ERROR_IF(UtilMkdirPath(Path, 0755) < 0);\n    THROW_LAST_ERROR_IF(WriteToFile(target.c_str(), Content) < 0);\n    THROW_LAST_ERROR_IF(UtilMkdirPath(defaultTarget.c_str(), 0755) < 0);\n\n    std::string symlinkPath = std::format(\"{}/{}.service\", defaultTarget, Name);\n    THROW_LAST_ERROR_IF(symlink(target.c_str(), symlinkPath.c_str()) < 0);\n}\nCATCH_LOG();\n\nvoid CreateWslSystemdUnits(const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This method creates systemd unit files to protect WSL functionality from being disabled by systemd.\n\nArguments:\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    if (Config.NetworkingMode == LxMiniInitNetworkingModeMirrored)\n    {\n        HardenMirroredNetworkingSettingsAgainstSystemd();\n    }\n\n    constexpr auto folder = \"/run/systemd/system-generators\";\n\n    THROW_LAST_ERROR_IF(UtilMkdirPath(folder, 0755) < 0);\n    THROW_LAST_ERROR_IF(symlink(\"/init\", std::format(\"{}/{}\", folder, LX_INIT_WSL_GENERATOR).c_str()));\n\n    if (Config.GuiAppsEnabled)\n    {\n        constexpr auto folder = \"/run/systemd/user-generators\";\n\n        THROW_LAST_ERROR_IF(UtilMkdirPath(folder, 0755) < 0);\n        THROW_LAST_ERROR_IF(symlink(\"/init\", std::format(\"{}/{}\", folder, LX_INIT_WSL_USER_GENERATOR).c_str()));\n    }\n}\nCATCH_LOG();\n\nvoid HardenMirroredNetworkingSettingsAgainstSystemd()\n\n/*++\n\nRoutine Description:\n\n    This routine writes configuration required for the mirrored networking mode loopback datapath to\n    a .conf file applied by systemd. Some distros come with default .conf files only applied when\n    systemd is enabled, and some of these contain network configurations that conflict with mirrored\n    networking mode. By writing to a .conf file that has higher precedence, we can prevent these\n    conflicting settings from being applied.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    const char* NetworkingConfigFileDirectory = \"/run/sysctl.d\";\n    const char* NetworkingConfigFileName = \"wsl-networking.conf\";\n    const std::string NetworkingConfigFilePath = std::format(\"{}/{}\", NetworkingConfigFileDirectory, NetworkingConfigFileName);\n    constexpr auto NetworkingConfig =\n        \"# Note: This file is generated by WSL to prevent default .conf files applied by systemd from overwriting critical \"\n        \"networking settings\\n\"\n        \"net.ipv4.conf.all.rp_filter=0\\n\"\n        \"net.ipv4.conf.\" LX_INIT_LOOPBACK_DEVICE_NAME \".rp_filter=0\\n\";\n\n    THROW_LAST_ERROR_IF(UtilMkdirPath(NetworkingConfigFileDirectory, 0755) < 0);\n    THROW_LAST_ERROR_IF(WriteToFile(NetworkingConfigFilePath.c_str(), NetworkingConfig) < 0);\n}\nCATCH_LOG();\n\nvoid SessionLeaderCreateProcess(gsl::span<gsl::byte> Buffer, int MessageFd, int TtyFd, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine creates a process from a session leader.\n\nArguments:\n\n    Buffer - Supplies the message buffer.\n\n    MessageFd - Supplies a message port file descriptor.\n\n    TtyFd - Supplies a Tty file descriptor.\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    //\n    // Parse the create process message buffer and create the new child process.\n    //\n\n    CREATE_PROCESS_PARSED Parsed = CreateProcessParse(Buffer, MessageFd, Config);\n    auto CreateProcessPid = fork();\n    THROW_LAST_ERROR_IF(CreateProcessPid < 0);\n\n    if (CreateProcessPid > 0)\n    {\n        //\n        // Remember the current process group.\n        //\n\n        if (g_SessionGroup == -1)\n        {\n            g_SessionGroup = CreateProcessPid;\n        }\n\n        //\n        // Reply with pid of the child process.\n        //\n\n        THROW_LAST_ERROR_IF(CreateProcessReplyToServer(&Parsed, CreateProcessPid, MessageFd) < 0);\n\n        return;\n    }\n\n    //\n    // Child...\n    //\n    // The child process should be part of a separate foreground process group.\n    // If a separate foreground process group does not exist, create one here.\n    //\n\n    int Result = 0;\n    if (g_SessionGroup != -1)\n    {\n        //\n        // Attempt to join an existing foreground process group.\n        //\n\n        Result = setpgid(0, g_SessionGroup);\n    }\n\n    if ((g_SessionGroup == -1) || (Result < 0))\n    {\n        //\n        // Create a new process group.\n        //\n\n        THROW_LAST_ERROR_IF(setpgid(0, 0) < 0);\n    }\n\n    //\n    // Always bring the process group to the foreground. This will give\n    // the newly launched process access to the terminal. In cases where\n    // multiple processes are being launched in the same session due to\n    // piping commands together (e.g. bash.exe -c ls | bash.exe -c less)\n    // or calling bash.exe from within a running WSL instance (inception),\n    // there may be issues when this process terminates, as restoring the\n    // foreground group does not happen by default. If the launcher is a\n    // shell program like /bin/bash, then it typically assumes that the\n    // foreground needs to be restored and all will work well.\n    //\n\n    //\n    // N.B. SIGTTOU along with most other signals are blocked. Otherwise,\n    //      this could generate a signal with the default behavior of\n    //      stopping the process (waiting for SIGCONT to continue).\n    //\n\n    if (tcsetpgrp(TtyFd, getpgid(0)) < 0)\n    {\n        LOG_ERROR(\"tcsetpgrp failed {}\", errno);\n    }\n\n    //\n    // Exec the new process.\n    //\n\n    //\n    // Resources are not released for the child process because it will call execv.\n    //\n    // N.B. CreateProcess does not return.\n    //\n\n    CreateProcess(&Parsed, TtyFd, Config);\n    FATAL_ERROR(\"CreateProcess not expected to return\");\n}\n\nvoid SessionLeaderSigchldHandler(__attribute__((unused)) int Signal, __attribute__((unused)) siginfo_t* SigInfo, __attribute__((unused)) void* UContext)\n\n/*++\n\nRoutine Description:\n\n    This routine determines if the process group assigned to processes launched\n    by the session leader has terminated.\n\nArguments:\n\n    Signal - Supplies the signal that was received.\n\n    SigInfo - Supplies additional information about the signal.\n\n    UContext - Supplies the scheduling context from the process before the\n        signal handler was invoked.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    pid_t child;\n    int status;\n\n    while ((child = waitpid(-1, &status, WNOHANG)) > 0)\n    {\n        if (child == g_SessionGroup)\n        {\n            g_SessionGroup = -1;\n        }\n    }\n\n    return;\n}\n\nvoid SessionLeaderEntryUtilityVm(wsl::shared::SocketChannel& channel, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine is the entry point for the session leader process.\n\nArguments:\n\n    channel - Supplies a message channel\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    std::vector<gsl::byte> Buffer;\n    MESSAGE_HEADER* Header{};\n    struct sigaction SignalAction;\n\n    //\n    // Create a new session.\n    //\n\n    if (setsid() < 0)\n    {\n        FATAL_ERROR(\"setsid failed {}\", errno);\n    }\n\n    //\n    // Set up a signal handler to reap child processes and track session\n    // leader.\n    //\n\n    memset(&SignalAction, 0, sizeof(SignalAction));\n    SignalAction.sa_flags = SA_SIGINFO;\n    SignalAction.sa_sigaction = SessionLeaderSigchldHandler;\n    if (sigaction(SIGCHLD, &SignalAction, NULL) < 0)\n    {\n        FATAL_ERROR(\"sigaction SIGCHLD failed {}\", errno);\n    }\n\n    //\n    // Loop waiting on the socket for requests from the Windows server. A zero-byte read means that there is no\n    // longer any active console applications for this session and that the session leader should exit.\n    //\n\n    for (;;)\n    {\n        auto [Message, Span] = channel.ReceiveMessageOrClosed<LX_INIT_CREATE_PROCESS_UTILITY_VM>();\n        if (Message == nullptr)\n        {\n            _exit(0);\n        }\n\n        switch (Message->Header.MessageType)\n        {\n        case LxInitMessageCreateProcessUtilityVm:\n            if (InitCreateProcessUtilityVm(Span, *Message, channel, Config) < 0)\n            {\n                FATAL_ERROR(\"InitCreateProcessUtilityVm failed\");\n            }\n\n            break;\n\n        default:\n            FATAL_ERROR(\"Unexpected message {}\", Header->MessageType);\n        }\n    }\n\n    FATAL_ERROR(\"Session leader not expected to exit\");\n    return;\n}\n\nvoid SessionLeaderEntry(int MessageFd, int TtyFd, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine is the entry point for the session leader process.\n\nArguments:\n\n    MessageFd - Supplies a message port file descriptor.\n\n    TtyFd - Supplies a Tty file descriptor.\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    std::vector<gsl::byte> Buffer;\n    ssize_t BytesRead;\n    struct sigaction SignalAction;\n\n    //\n    // Create a new session and set the controlling session on the Tty device.\n    //\n\n    if (setsid() < 0)\n    {\n        FATAL_ERROR(\"setsid failed {}\", errno);\n    }\n\n    if (TEMP_FAILURE_RETRY(ioctl(TtyFd, TIOCSCTTY, NULL)) < 0)\n    {\n        FATAL_ERROR(\"ioctl failed for TIOCSCTTY {}\", errno);\n    }\n\n    //\n    // Set up a signal handler to reap child processes and track session\n    // leader.\n    //\n\n    memset(&SignalAction, 0, sizeof(SignalAction));\n    SignalAction.sa_flags = SA_SIGINFO;\n    SignalAction.sa_sigaction = SessionLeaderSigchldHandler;\n    if (sigaction(SIGCHLD, &SignalAction, NULL) < 0)\n    {\n        FATAL_ERROR(\"sigaction SIGCHLD failed {}\", errno);\n    }\n\n    //\n    // Loop waiting on the message port for requests from the Windows server.\n    //\n\n    for (;;)\n    {\n        BytesRead = UtilReadMessageLxBus(MessageFd, Buffer, false);\n        if (BytesRead < 0)\n        {\n            FATAL_ERROR(\"read failed {}\", errno);\n        }\n\n        auto Message = gsl::make_span(Buffer.data(), BytesRead);\n        auto* Header = gslhelpers::try_get_struct<MESSAGE_HEADER>(Message);\n        if (!Header)\n        {\n            FATAL_ERROR(\"Invalid message size {}\", Message.size());\n        }\n\n        if (Header->MessageType == LxInitMessageCreateProcess)\n        {\n            SessionLeaderCreateProcess(Message, MessageFd, TtyFd, Config);\n        }\n        else\n        {\n            FATAL_ERROR(\"Unexpected message {}\", Header->MessageType);\n        }\n    }\n\n    FATAL_ERROR(\"Session leader not expected to exit\");\n    return;\n}\n\nbool StopPlan9Server(bool Force, wsl::linux::WslDistributionConfig& Config)\n{\n    if (Config.Plan9ControlChannel.Socket() < 0)\n    {\n        return true;\n    }\n\n    LX_INIT_STOP_PLAN9_SERVER Message{};\n    Message.Header.MessageType = LxInitMessageStopPlan9Server;\n    Message.Header.MessageSize = sizeof(Message);\n    Message.Force = Message.Force;\n\n    const auto& Response = Config.Plan9ControlChannel.Transaction(Message);\n\n    if (Response.Result)\n    {\n        // The plan9 server is terminated, release the socket.\n        Config.Plan9ControlChannel.Close();\n    }\n\n    return Response.Result;\n}\n\nwil::unique_fd UnmarshalConsoleFromServer(int MessageFd, LXBUS_IPC_CONSOLE_ID ConsoleId)\n\n/*++\n\nRoutine Description:\n\n    This routine unmarshals a console.\n\nArguments:\n\n    MessageFd - Supplies a message port file descriptor.\n\n    ConsoleId - Supplies a console ID.\n\n    TtyFd - Supplies a buffer to store a Tty file descriptor.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    LXBUS_IPC_MESSAGE_UNMARSHAL_CONSOLE_PARAMETERS UnmarshalConsole{};\n\n    //\n    // N.B. Failures to unmarshall the console are non-fatal.\n    //\n\n    UnmarshalConsole.Input.ConsoleId = ConsoleId;\n\n    if (TEMP_FAILURE_RETRY(ioctl(MessageFd, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_CONSOLE, &UnmarshalConsole)))\n    {\n        LOG_ERROR(\"Failed to unmarshal console {}\", errno);\n        return {};\n    }\n\n    return UnmarshalConsole.Output.FileDescriptor;\n}\n\nunsigned int StartPlan9(int Argc, char** Argv)\n{\n    constexpr auto* Usage = \"Usage: plan9 \" LX_INIT_PLAN9_CONTROL_SOCKET_ARG \" fd \" LX_INIT_PLAN9_SOCKET_PATH_ARG\n                            \" path \" LX_INIT_PLAN9_SERVER_FD_ARG \" fd \" LX_INIT_PLAN9_LOG_FILE_ARG\n                            \" log-file \" LX_INIT_PLAN9_LOG_LEVEL_ARG \" level \" LX_INIT_PLAN9_PIPE_FD_ARG \" fd [--log-truncate]\\n\";\n\n    bool LogTruncate = false;\n    int LogLevel = TRACE_LEVEL_INFORMATION;\n    wil::unique_fd PipeFd;\n    const char* SocketPath{};\n    const char* LogFile{};\n    wil::unique_fd ControlSocket;\n    wil::unique_fd ServerFd;\n\n    ArgumentParser parser(Argc, Argv);\n    parser.AddArgument(UniqueFd{ControlSocket}, LX_INIT_PLAN9_CONTROL_SOCKET_ARG);\n    parser.AddArgument(SocketPath, LX_INIT_PLAN9_SOCKET_PATH_ARG);\n    parser.AddArgument(UniqueFd{ServerFd}, LX_INIT_PLAN9_SERVER_FD_ARG);\n    parser.AddArgument(LogFile, LX_INIT_PLAN9_LOG_FILE_ARG);\n    parser.AddArgument(Integer{LogLevel}, LX_INIT_PLAN9_LOG_LEVEL_ARG);\n    parser.AddArgument(UniqueFd{PipeFd}, LX_INIT_PLAN9_PIPE_FD_ARG);\n    parser.AddArgument(LogTruncate, LX_INIT_PLAN9_TRUNCATE_LOG_ARG);\n\n    try\n    {\n        parser.Parse();\n    }\n    catch (const wil::ExceptionWithUserMessage& e)\n    {\n        std::cerr << e.what() << \"\\n\" << Usage;\n        return 1;\n    }\n\n    RunPlan9Server(SocketPath, LogFile, LogLevel, LogTruncate, ControlSocket.get(), ServerFd.get(), PipeFd);\n\n    return 0;\n}\n\nunsigned int StartGns(int Argc, char** Argv)\n{\n    constexpr auto* Usage =\n        \"Usage: gns [\" LX_INIT_GNS_SOCKET_ARG \" fd] [\" LX_INIT_GNS_DNS_SOCKET_ARG \" fd] [\" LX_INIT_GNS_ADAPTER_ARG\n        \" guid] [\" LX_INIT_GNS_MESSAGE_TYPE_ARG \" int] [\" LX_INIT_GNS_DNS_TUNNELING_IP \" ip]\\n\";\n\n    UtilSetThreadName(\"GNS\");\n\n    // Initialize error and telemetry logging.\n    InitializeLogging(false);\n\n    // hvsocket file descriptor used for DNS tunneling\n    std::optional<int> DnsFd;\n    std::optional<GUID> AdapterId;\n    std::optional<LX_MESSAGE_TYPE> MessageType;\n    std::string DnsTunnelingIp;\n    wil::unique_fd Socket;\n\n    ArgumentParser parser(Argc, Argv);\n    parser.AddArgument(UniqueFd{Socket}, LX_INIT_GNS_SOCKET_ARG);\n    parser.AddArgument(Integer{DnsFd}, LX_INIT_GNS_DNS_SOCKET_ARG);\n    parser.AddArgument(AdapterId, LX_INIT_GNS_ADAPTER_ARG);\n    parser.AddArgument(Integer{MessageType}, LX_INIT_GNS_MESSAGE_TYPE_ARG);\n    parser.AddArgument(DnsTunnelingIp, LX_INIT_GNS_DNS_TUNNELING_IP);\n\n    try\n    {\n        parser.Parse();\n    }\n    catch (const wil::ExceptionWithUserMessage& e)\n    {\n        std::cerr << e.what() << \"\\n\" << Usage;\n        return 1;\n    }\n\n    wsl::shared::SocketChannel channel{std::move(Socket), \"GNS\"};\n\n    GnsEngine::NotificationRoutine readNotification;\n    GnsEngine::StatusRoutine returnStatus;\n\n    // returns the most recent error when init is created for unit tests (i.e. Fd == -1)\n    int exitCode = 0;\n\n    if (channel.Socket() == -1)\n    {\n        readNotification = [&]() -> std::optional<GnsEngine::Message> {\n            std::string content{std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>()};\n            if (content.empty())\n            {\n                return {};\n            }\n            if (MessageType.has_value())\n            {\n                return {{MessageType.value(), content, AdapterId}};\n            }\n\n            return {{AdapterId.has_value() ? LxGnsMessageNotification : LxGnsMessageInterfaceConfiguration, content, AdapterId}};\n        };\n\n        returnStatus = [&](int Result, const std::string& Error) {\n            GNS_LOG_INFO(\"Returning LxGnsMessageResult (no output fd) [{} - {}]\", Result, Error.c_str());\n            // exitCode keeps the most recent error in the test path\n            if (Result != 0)\n            {\n                exitCode = Result;\n            }\n            return true;\n        };\n    }\n    else\n    {\n        readNotification = [&]() -> std::optional<GnsEngine::Message> {\n            std::vector<gsl::byte> Buffer;\n            auto [Message, Span] = channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\n            if (Message == nullptr)\n            {\n                return {};\n            }\n\n            auto type = Message->MessageType;\n            GNS_LOG_INFO(\"Processing LX_MESSAGE_TYPE {}\", ToString(type));\n            switch (type)\n            {\n            case LxGnsMessageNoOp:\n            case LxGnsMessageGlobalNetFilter:\n            {\n                return {{type, {}, {}}};\n            }\n            case LxGnsMessageInterfaceConfiguration:\n            {\n                auto size = Span.size() - offsetof(LX_GNS_INTERFACE_CONFIGURATION, Content) - 1;\n                assert(size > 0);\n\n                std::string Content{reinterpret_cast<PLX_GNS_INTERFACE_CONFIGURATION>(Span.data())->Content, size};\n\n                return {{type, Content, {}}};\n            }\n\n            case LxGnsMessageNotification:\n            {\n                auto size = Span.size() - offsetof(LX_GNS_NOTIFICATION, Content) - 1;\n                assert(size > 0);\n\n                const auto* NotificationMessage = reinterpret_cast<PLX_GNS_NOTIFICATION>(Span.data());\n                std::string Content{NotificationMessage->Content, size};\n                return {{type, Content, {NotificationMessage->AdapterId}}};\n            }\n\n            case LxGnsMessageVmNicCreatedNotification:\n            case LxGnsMessageCreateDeviceRequest:\n            case LxGnsMessageModifyGuestDeviceSettingRequest:\n            case LxGnsMessageLoopbackRoutesRequest:\n            case LxGnsMessageInitialIpConfigurationNotification:\n            case LxGnsMessageInterfaceNetFilter:\n            case LxGnsMessageDeviceSettingRequest:\n            case LxGnsMessageSetupIpv6:\n            case LxGnsMessageConnectTestRequest:\n            {\n                auto size = Span.size() - offsetof(LX_GNS_JSON_MESSAGE, Content) - 1;\n                if (size == 0)\n                {\n                    throw RuntimeErrorWithSourceLocation(\n                        std::format(\"Failed to find content for LX_MESSAGE_TYPE : {}\", static_cast<int>(type)));\n                }\n\n                std::string Content{reinterpret_cast<PLX_GNS_JSON_MESSAGE>(Span.data())->Content, size};\n                return {{type, Content, {}}};\n            }\n\n            default:\n            {\n                throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected LX_MESSAGE_TYPE : {}\", static_cast<int>(type)));\n            }\n            }\n        };\n\n        returnStatus = [&](int Result, const std::string& Error) {\n            std::vector<gsl::byte> Buffer(sizeof(LX_GNS_RESULT) + Error.size() + 1);\n\n            GNS_LOG_INFO(\"Returning LxGnsMessageResult [{} - {}]\", Result, Error.c_str());\n\n            wsl::shared::MessageWriter<LX_GNS_RESULT> response(LX_GNS_RESULT::Type);\n            response->Result = Result;\n            if (!Error.empty())\n            {\n                response.WriteString(Error);\n            }\n\n            return channel.SendMessage<LX_GNS_RESULT>(response.Span());\n        };\n    }\n\n    RoutingTable routingTable(RT_TABLE_MAIN);\n    NetworkManager manager(routingTable);\n    GnsEngine engine(readNotification, returnStatus, manager, DnsFd, DnsTunnelingIp);\n\n    engine.run();\n\n    GNS_LOG_INFO(\"StartGns returning {} (GNS Socket {}, MessageType {})\", exitCode, channel.Socket(), MessageType.value_or(LxMiniInitMessageAny));\n    return exitCode;\n}\n\nvoid WaitForBootProcess(wsl::linux::WslDistributionConfig& Config)\n{\n    if (!Config.BootStartWriteSocket)\n    {\n        return;\n    }\n\n    //\n    // Launch the boot process wait for it to finish booting.\n    //\n\n    MESSAGE_HEADER Message{};\n    Message.MessageType = LxInitMessageStartDistroInit;\n    Message.MessageSize = sizeof(Message);\n    if (UtilWriteBuffer(Config.BootStartWriteSocket.get(), gslhelpers::struct_as_bytes(Message)) < 0)\n    {\n        LOG_ERROR(\"write failed {}\", errno);\n    }\n\n    Config.BootStartWriteSocket.reset();\n    if (Config.BootInitTimeout > 0)\n    {\n        try\n        {\n            //\n            // N.B. Init needs to not ignore SIGCHLD so it can wait for the child process.\n            //\n\n            signal(SIGCHLD, SIG_DFL);\n            auto restoreDisposition = wil::scope_exit([]() { signal(SIGCHLD, SIG_IGN); });\n            wsl::shared::retry::RetryWithTimeout<void>(\n                [&]() {\n                    std::string Output;\n                    THROW_LAST_ERROR_IF(\n                        UtilExecCommandLine(\"systemctl is-system-running | grep -E \\\"running|degraded\\\"\", &Output, 0, false) < 0);\n                },\n                std::chrono::milliseconds{250},\n                std::chrono::milliseconds{Config.BootInitTimeout});\n        }\n        catch (...)\n        {\n            LOG_ERROR(\"{} failed to start within {}ms\", INIT_PATH, Config.BootInitTimeout);\n        }\n    }\n}"
  },
  {
    "path": "src/linux/init/localhost.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#include \"common.h\"\r\n#include <memory>\r\n#include <string>\r\n#include <string_view>\r\n#include <vector>\r\n#include <iostream>\r\n\r\n#include <libgen.h>\r\n#include <sys/epoll.h>\r\n#include <sys/socket.h>\r\n#include <netinet/ip.h>\r\n#include <sys/syscall.h>\r\n#include <linux/unistd.h>\r\n#include <linux/sock_diag.h>\r\n#include <linux/inet_diag.h>\r\n#include <lxwil.h>\r\n#include <linux/if_tun.h>\r\n\r\n#include \"util.h\"\r\n#include \"SocketChannel.h\"\r\n#include \"GnsPortTracker.h\"\r\n#include \"SecCompDispatcher.h\"\r\n#include \"seccomp_defs.h\"\r\n#include \"CommandLine.h\"\r\n#include \"NetlinkChannel.h\"\r\n#include \"NetlinkTransactionError.h\"\r\n\r\n#define TCP_LISTEN 10\r\n\r\nnamespace {\r\n\r\nvoid ListenThread(sockaddr_vm hvSocketAddress, int listenSocket)\r\n{\r\n    pollfd pollDescriptors[] = {{listenSocket, POLLIN}};\r\n    for (;;)\r\n    {\r\n        int result = poll(pollDescriptors, COUNT_OF(pollDescriptors), -1);\r\n        if (result < 0)\r\n        {\r\n            LOG_ERROR(\"poll failed {}\", errno);\r\n            return;\r\n        }\r\n\r\n        if ((pollDescriptors[0].revents & POLLIN) == 0)\r\n        {\r\n            LOG_ERROR(\"unexpected revents {:x}\", pollDescriptors[0].revents);\r\n            return;\r\n        }\r\n\r\n        // Accept a connection and start a relay worker thread.\r\n        wil::unique_fd relaySocket{UtilAcceptVsock(listenSocket, hvSocketAddress)};\r\n        THROW_LAST_ERROR_IF(!relaySocket);\r\n\r\n        std::thread([relaySocket = std::move(relaySocket)]() {\r\n            try\r\n            {\r\n                // Read a message to determine which TCP port to connect to.\r\n                std::vector<gsl::byte> buffer(sizeof(LX_INIT_START_SOCKET_RELAY));\r\n                auto bytesRead = UtilReadBuffer(relaySocket.get(), buffer);\r\n                if (bytesRead == 0)\r\n                {\r\n                    return;\r\n                }\r\n\r\n                auto* message = gslhelpers::try_get_struct<LX_INIT_START_SOCKET_RELAY>(gsl::make_span(buffer.data(), bytesRead));\r\n                THROW_ERRNO_IF(EINVAL, !message || (message->Header.MessageType != LxInitMessageStartSocketRelay));\r\n\r\n                // Connect to the actual socket address and set up a relay.\r\n                //\r\n                // N.B. While the relay was being set up, the server may have\r\n                //      stopped listening.\r\n                sockaddr* socketAddress;\r\n                int socketAddressSize;\r\n                sockaddr_in sockaddrIn{};\r\n                sockaddr_in6 sockaddrIn6{};\r\n                if (message->Family == AF_INET)\r\n                {\r\n                    sockaddrIn.sin_family = AF_INET;\r\n                    sockaddrIn.sin_port = htons(message->Port);\r\n                    sockaddrIn.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r\n                    socketAddress = reinterpret_cast<sockaddr*>(&sockaddrIn);\r\n                    socketAddressSize = sizeof(sockaddrIn);\r\n                }\r\n                else if (message->Family == AF_INET6)\r\n                {\r\n                    sockaddrIn6.sin6_family = AF_INET6;\r\n                    sockaddrIn6.sin6_port = htons(message->Port);\r\n                    sockaddrIn6.sin6_addr = IN6ADDR_LOOPBACK_INIT;\r\n                    socketAddress = reinterpret_cast<sockaddr*>(&sockaddrIn6);\r\n                    socketAddressSize = sizeof(sockaddrIn6);\r\n                }\r\n                else\r\n                {\r\n                    THROW_ERRNO(EINVAL);\r\n                }\r\n\r\n                wil::unique_fd tcpSocket{socket(socketAddress->sa_family, SOCK_STREAM, IPPROTO_TCP)};\r\n                THROW_LAST_ERROR_IF(!tcpSocket);\r\n\r\n                if (TEMP_FAILURE_RETRY(connect(tcpSocket.get(), socketAddress, socketAddressSize)) < 0)\r\n                {\r\n                    return;\r\n                }\r\n\r\n                // Resize the buffer to be the requested size.\r\n                buffer.resize(message->BufferSize);\r\n\r\n                // Begin relaying data.\r\n                int outFd[2] = {tcpSocket.get(), relaySocket.get()};\r\n                pollfd pollDescriptors[] = {{relaySocket.get(), POLLIN}, {tcpSocket.get(), POLLIN}};\r\n\r\n                for (;;)\r\n                {\r\n                    if ((pollDescriptors[0].fd == -1) || (pollDescriptors[1].fd == -1))\r\n                    {\r\n                        return;\r\n                    }\r\n\r\n                    THROW_LAST_ERROR_IF(poll(pollDescriptors, COUNT_OF(pollDescriptors), -1) < 0);\r\n\r\n                    bytesRead = 0;\r\n                    for (int Index = 0; Index < COUNT_OF(pollDescriptors); Index += 1)\r\n                    {\r\n                        if (pollDescriptors[Index].revents & POLLIN)\r\n                        {\r\n                            bytesRead = UtilReadBuffer(pollDescriptors[Index].fd, buffer);\r\n                            if (bytesRead == 0)\r\n                            {\r\n                                pollDescriptors[Index].fd = -1;\r\n                                shutdown(outFd[Index], SHUT_WR);\r\n                            }\r\n                            else if (bytesRead < 0)\r\n                            {\r\n                                return;\r\n                            }\r\n                            else if (UtilWriteBuffer(outFd[Index], buffer.data(), bytesRead) < 0)\r\n                            {\r\n                                return;\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            CATCH_LOG()\r\n        }).detach();\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nstd::vector<sockaddr_storage> QueryListeningSockets(NetlinkChannel& channel)\r\n{\r\n    std::vector<sockaddr_storage> sockets{};\r\n    try\r\n    {\r\n        inet_diag_req_v2 message{};\r\n        message.sdiag_protocol = IPPROTO_TCP;\r\n        message.idiag_states = (1 << TCP_LISTEN);\r\n\r\n        auto onMessage = [&](const NetlinkResponse& response) {\r\n            for (const auto& e : response.Messages<inet_diag_msg>(SOCK_DIAG_BY_FAMILY))\r\n            {\r\n                const auto* payload = e.Payload();\r\n                sockaddr_storage sock{};\r\n\r\n                if (payload->idiag_family == AF_INET)\r\n                {\r\n                    auto* ipv4 = reinterpret_cast<sockaddr_in*>(&sock);\r\n                    ipv4->sin_family = AF_INET;\r\n                    ipv4->sin_addr.s_addr = payload->id.idiag_src[0];\r\n                    ipv4->sin_port = payload->id.idiag_sport;\r\n                }\r\n                else if (payload->idiag_family == AF_INET6)\r\n                {\r\n                    auto* ipv6 = reinterpret_cast<sockaddr_in6*>(&sock);\r\n                    ipv6->sin6_family = AF_INET6;\r\n                    static_assert(sizeof(ipv6->sin6_addr.s6_addr32) == sizeof(payload->id.idiag_src));\r\n                    memcpy(ipv6->sin6_addr.s6_addr32, payload->id.idiag_src, sizeof(ipv6->sin6_addr.s6_addr32));\r\n                    ipv6->sin6_port = payload->id.idiag_sport;\r\n                }\r\n\r\n                sockets.emplace_back(sock);\r\n            }\r\n        };\r\n\r\n        // Query IPv4 listening sockets.\r\n        {\r\n            message.sdiag_family = AF_INET;\r\n            auto transaction = channel.CreateTransaction(message, SOCK_DIAG_BY_FAMILY, NLM_F_DUMP);\r\n            transaction.Execute(onMessage);\r\n        }\r\n\r\n        // Query IPv6 listening sockets.\r\n        {\r\n            message.sdiag_family = AF_INET6;\r\n            auto transaction = channel.CreateTransaction(message, SOCK_DIAG_BY_FAMILY, NLM_F_DUMP);\r\n            transaction.Execute(onMessage);\r\n        }\r\n    }\r\n    catch (const NetlinkTransactionError& e)\r\n    {\r\n        // Log but don't fail - network state might be temporarily unavailable\r\n        LOG_ERROR(\"Failed to query listening sockets via sock_diag: {}\", e.what());\r\n    }\r\n\r\n    return sockets;\r\n}\r\n\r\nint SendRelayListenerSocket(wsl::shared::SocketChannel& channel, int hvSocketPort)\r\ntry\r\n{\r\n    LX_GNS_SET_PORT_LISTENER message{};\r\n    message.Header.MessageType = LxGnsMessageSetPortListener;\r\n    message.Header.MessageSize = sizeof(message);\r\n    message.HvSocketPort = hvSocketPort;\r\n\r\n    channel.SendMessage(message);\r\n\r\n    return 0;\r\n}\r\nCATCH_RETURN_ERRNO();\r\n\r\nLX_GNS_PORT_LISTENER_RELAY SockToRelayMessage(const sockaddr_storage& sock)\r\n{\r\n    LX_GNS_PORT_LISTENER_RELAY message{};\r\n    message.Header.MessageSize = sizeof(message);\r\n    message.Family = sock.ss_family;\r\n    if (sock.ss_family == AF_INET)\r\n    {\r\n        auto ipv4 = reinterpret_cast<const sockaddr_in*>(&sock);\r\n        message.Address[0] = ipv4->sin_addr.s_addr;\r\n        message.Port = ntohs(ipv4->sin_port);\r\n    }\r\n    else if (sock.ss_family == AF_INET6)\r\n    {\r\n        auto ipv6 = reinterpret_cast<const sockaddr_in6*>(&sock);\r\n        message.Port = ntohs(ipv6->sin6_port);\r\n        memcpy(message.Address, ipv6->sin6_addr.__in6_union.__s6_addr, sizeof(message.Address));\r\n    }\r\n    return message;\r\n}\r\n\r\nint StartHostListener(wsl::shared::SocketChannel& channel, const sockaddr_storage& sock)\r\ntry\r\n{\r\n    auto message = SockToRelayMessage(sock);\r\n    message.Header.MessageType = LxGnsMessagePortListenerRelayStart;\r\n    channel.SendMessage(message);\r\n\r\n    return 0;\r\n}\r\nCATCH_RETURN_ERRNO();\r\n\r\nint StopHostListener(wsl::shared::SocketChannel& channel, const sockaddr_storage& sock)\r\ntry\r\n{\r\n    auto message = SockToRelayMessage(sock);\r\n    message.Header.MessageType = LxGnsMessagePortListenerRelayStop;\r\n    channel.SendMessage(message);\r\n\r\n    return 0;\r\n}\r\nCATCH_RETURN_ERRNO();\r\n\r\nbool IsSameSockAddr(const sockaddr_storage& left, const sockaddr_storage& right)\r\n{\r\n    if (left.ss_family != right.ss_family)\r\n    {\r\n        return false;\r\n    }\r\n\r\n    if (left.ss_family == AF_INET)\r\n    {\r\n        auto leftIpv4 = reinterpret_cast<const sockaddr_in*>(&left);\r\n        auto rightIpv4 = reinterpret_cast<const sockaddr_in*>(&right);\r\n        return (leftIpv4->sin_addr.s_addr == rightIpv4->sin_addr.s_addr && leftIpv4->sin_port == rightIpv4->sin_port);\r\n    }\r\n    else if (left.ss_family == AF_INET6)\r\n    {\r\n        auto leftIpv6 = reinterpret_cast<const sockaddr_in6*>(&left);\r\n        auto rightIpv6 = reinterpret_cast<const sockaddr_in6*>(&right);\r\n        return (leftIpv6->sin6_port == rightIpv6->sin6_port && memcmp(&leftIpv6->sin6_addr, &rightIpv6->sin6_addr, sizeof(in6_addr)) == 0);\r\n    }\r\n\r\n    FATAL_ERROR(\"Unrecognized socket family {}\", left.ss_family);\r\n    return false;\r\n}\r\n\r\n// Monitor listening TCP sockets using sock_diag netlink interface.\r\nint MonitorListeningSockets(wsl::shared::SocketChannel& channel)\r\n{\r\n    NetlinkChannel netlinkChannel(SOCK_RAW, NETLINK_SOCK_DIAG);\r\n    std::vector<sockaddr_storage> relays{};\r\n    int result = 0;\r\n\r\n    for (;;)\r\n    {\r\n        auto sockets = QueryListeningSockets(netlinkChannel);\r\n\r\n        // Stop any relays that no longer match listening ports.\r\n        std::erase_if(relays, [&](const auto& entry) {\r\n            auto found =\r\n                std::find_if(sockets.begin(), sockets.end(), [&](const auto& socket) { return IsSameSockAddr(entry, socket); });\r\n\r\n            bool remove = (found == sockets.end());\r\n            if (remove)\r\n            {\r\n                if (StopHostListener(channel, entry) < 0)\r\n                {\r\n                    result = -1;\r\n                }\r\n            }\r\n\r\n            return remove;\r\n        });\r\n\r\n        // Create relays for any new ports.\r\n        std::for_each(sockets.begin(), sockets.end(), [&](const auto& socket) {\r\n            auto found =\r\n                std::find_if(relays.begin(), relays.end(), [&](const auto& entry) { return IsSameSockAddr(entry, socket); });\r\n\r\n            if (found == relays.end())\r\n            {\r\n                if (StartHostListener(channel, socket) < 0)\r\n                {\r\n                    result = -1;\r\n                }\r\n                else\r\n                {\r\n                    relays.push_back(socket);\r\n                }\r\n            }\r\n        });\r\n\r\n        // Ensure all start / stop operations were successful.\r\n        if (result < 0)\r\n        {\r\n            break;\r\n        }\r\n\r\n        // Sleep before scanning again.\r\n        std::this_thread::sleep_for(std::chrono::seconds(1));\r\n    }\r\n\r\n    return result;\r\n}\r\n} // namespace\r\n\r\n// Create a thread to monitor for connections to relay.\r\nint StartLocalhostRelay(wsl::shared::SocketChannel& channel, int GuestRelayFd, bool ScanForPorts)\r\ntry\r\n{\r\n    // If the other end of a socket is reset, write will result in EPIPE. Ignore\r\n    // this signal and just use the write return value.\r\n    THROW_LAST_ERROR_IF(signal(SIGPIPE, SIG_IGN) == SIG_ERR);\r\n\r\n    sockaddr_vm hvSocketAddress = {};\r\n    socklen_t hvSocketAddressLen = sizeof(hvSocketAddress);\r\n    if (getsockname(GuestRelayFd, reinterpret_cast<sockaddr*>(&hvSocketAddress), &hvSocketAddressLen) < 0 ||\r\n        hvSocketAddressLen != sizeof(hvSocketAddress))\r\n    {\r\n        LOG_ERROR(\"Failed to get hvsocket port: {}, {}\", errno, hvSocketAddressLen);\r\n        return -1;\r\n    }\r\n\r\n    wil::unique_fd listenSocket{GuestRelayFd};\r\n    THROW_LAST_ERROR_IF(!listenSocket);\r\n\r\n    // Create a thread to accept incoming connections from the host listener\r\n    std::thread([hvSocketAddress, listenSocket = std::move(listenSocket)]() {\r\n        try\r\n        {\r\n            ListenThread(hvSocketAddress, listenSocket.get());\r\n        }\r\n        CATCH_LOG()\r\n    }).detach();\r\n\r\n    if (SendRelayListenerSocket(channel, hvSocketAddress.svm_port) < 0)\r\n    {\r\n        LOG_ERROR(\"Unable to send relay listener socket\");\r\n        return -1;\r\n    }\r\n\r\n    if (ScanForPorts)\r\n    {\r\n        return MonitorListeningSockets(channel);\r\n    }\r\n\r\n    return 0;\r\n}\r\ncatch (...)\r\n{\r\n    LOG_CAUGHT_EXCEPTION_MSG(\"Could not start localhost relay.\")\r\n    return -1;\r\n}\r\n\r\nint RunPortTracker(int Argc, char** Argv)\r\n{\r\n    using namespace wsl::shared;\r\n\r\n    constexpr auto* Usage = \"Usage: localhost \" INIT_PORT_TRACKER_FD_ARG\r\n                            \" fd\"\r\n                            \" [\" INIT_BPF_FD_ARG\r\n                            \" fd]\"\r\n                            \" [\" INIT_NETLINK_FD_ARG\r\n                            \" fd]\"\r\n                            \" [\" INIT_PORT_TRACKER_LOCALHOST_RELAY \" fd]\\n\";\r\n\r\n    // This is only supported on VM mode.\r\n    if (!UtilIsUtilityVm())\r\n    {\r\n        return -1;\r\n    }\r\n\r\n    // Initialize error and telemetry logging.\r\n    InitializeLogging(true);\r\n\r\n    int BpfFd = -1;\r\n    int PortTrackerFd = -1;\r\n    int NetlinkSocketFd = -1;\r\n    int GuestRelayFd = -1;\r\n\r\n    ArgumentParser parser(Argc, Argv);\r\n    parser.AddArgument(Integer{BpfFd}, INIT_BPF_FD_ARG);\r\n    parser.AddArgument(Integer{PortTrackerFd}, INIT_PORT_TRACKER_FD_ARG);\r\n    parser.AddArgument(Integer{NetlinkSocketFd}, INIT_NETLINK_FD_ARG);\r\n    parser.AddArgument(Integer{GuestRelayFd}, INIT_PORT_TRACKER_LOCALHOST_RELAY);\r\n\r\n    try\r\n    {\r\n        parser.Parse();\r\n    }\r\n    catch (const wil::ExceptionWithUserMessage& e)\r\n    {\r\n        std::cerr << e.what() << \"\\n\" << Usage;\r\n        return 1;\r\n    }\r\n\r\n    const bool synchronousMode = BpfFd != -1 && NetlinkSocketFd != -1;\r\n    const bool localhostRelay = GuestRelayFd != -1;\r\n    auto hvSocketChannel = std::make_shared<wsl::shared::SocketChannel>(wil::unique_fd{PortTrackerFd}, \"localhost\");\r\n\r\n    if (localhostRelay)\r\n    {\r\n        // This needs to be the first message sent over the PortTrackerFd channel,\r\n        // before running the seccomp dispatcher loop.\r\n        const int ret = StartLocalhostRelay(*hvSocketChannel, GuestRelayFd, !synchronousMode);\r\n        if (ret < 0)\r\n        {\r\n            LOG_ERROR(\"Failed to start the guest side of the localhost relay\");\r\n        }\r\n        if (!synchronousMode)\r\n        {\r\n            return ret;\r\n        }\r\n    }\r\n\r\n    if (!synchronousMode)\r\n    {\r\n        std::cerr << \"either both or none of --bpf-fd and --netlink-socket can be passed\\n\";\r\n        return 1;\r\n    }\r\n\r\n    auto channel = NetlinkChannel::FromFd(NetlinkSocketFd);\r\n\r\n    auto seccompDispatcher = std::make_shared<SecCompDispatcher>(BpfFd);\r\n\r\n    GnsPortTracker portTracker(hvSocketChannel, std::move(channel), seccompDispatcher);\r\n\r\n    seccompDispatcher->RegisterHandler(\r\n        __NR_bind, [&portTracker](seccomp_notif* notification) { return portTracker.ProcessSecCompNotification(notification); });\r\n\r\n#ifdef __x86_64__\r\n    seccompDispatcher->RegisterHandler(I386_NR_socketcall, [&portTracker](seccomp_notif* notification) {\r\n        return portTracker.ProcessSecCompNotification(notification);\r\n    });\r\n#else\r\n    seccompDispatcher->RegisterHandler(ARMV7_NR_bind, [&portTracker](seccomp_notif* notification) {\r\n        return portTracker.ProcessSecCompNotification(notification);\r\n    });\r\n#endif\r\n\r\n    seccompDispatcher->RegisterHandler(__NR_ioctl, [hvSocketChannel, seccompDispatcher](auto notification) -> int {\r\n        LX_GNS_TUN_BRIDGE_REQUEST request{};\r\n        request.Header.MessageType = LxGnsMessageIfStateChangeRequest;\r\n        request.Header.MessageSize = sizeof(request);\r\n        auto ifreqMemory =\r\n            seccompDispatcher->ReadProcessMemory(notification->id, notification->pid, notification->data.args[2], sizeof(ifreq));\r\n        if (!ifreqMemory.has_value())\r\n        {\r\n            return -1;\r\n        }\r\n\r\n        auto& ifRequest = *reinterpret_cast<ifreq*>(ifreqMemory->data());\r\n        memcpy(request.InterfaceName, ifRequest.ifr_ifrn.ifrn_name, sizeof(request.InterfaceName));\r\n        request.InterfaceUp = ifRequest.ifr_ifru.ifru_flags & IFF_UP;\r\n        const auto& reply = hvSocketChannel->Transaction(request);\r\n\r\n        return reply.Result;\r\n    });\r\n\r\n    try\r\n    {\r\n        portTracker.Run();\r\n    }\r\n    catch (const std::exception& e)\r\n    {\r\n        std::cerr << \"Port tracker exiting with fatal error, \" << e.what() << std::endl;\r\n    }\r\n\r\n    return 1;\r\n}\r\n"
  },
  {
    "path": "src/linux/init/localhost.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nint RunPortTracker(int argc, char** argv);\n"
  },
  {
    "path": "src/linux/init/main.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.cpp\n\nAbstract:\n\n    This file contains the entrypoint of the WSL init implementation.\n\n--*/\n\n#include <sys/mount.h>\n#include <sys/signalfd.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/sysinfo.h>\n#include <sys/sysmacros.h>\n#include <sys/reboot.h>\n#include <sys/resource.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <net/if.h>\n#include <net/route.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <linux/audit.h> /* Definition of AUDIT_* constants */\n#include <linux/if_tun.h>\n#include <linux/loop.h>\n#include <linux/net.h>\n#include <linux/random.h>\n#include <linux/vm_sockets.h>\n#include <linux/filter.h>\n#include <linux/seccomp.h>\n#include <linux/sock_diag.h>\n#include <sys/utsname.h>\n#include <linux/netlink.h>\n#include <dirent.h>\n#include <errno.h>\n#include <limits.h>\n#include <poll.h>\n#include <pthread.h>\n#include <sched.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <unistd.h>\n#include <utmp.h>\n#include <assert.h>\n#include \"configfile.h\"\n#include \"lxfsshares.h\"\n#include \"common.h\"\n#include \"util.h\"\n#include \"seccomp_defs.h\"\n#include \"mountutilcpp.h\"\n#include \"message.h\"\n#include \"binfmt.h\"\n#include \"address.h\"\n#include \"SocketChannel.h\"\n\n#define BSDTAR_PATH \"/usr/bin/bsdtar\"\n#define BINFMT_REGISTER_STRING \":\" LX_INIT_BINFMT_NAME \":M::MZ::\" LX_INIT_PATH \":FP\\n\"\n#define BINFMT_PATH PROCFS_PATH \"/sys/fs/binfmt_misc\"\n#define CHRONY_CONF_PATH ETC_PATH \"/chrony.conf\"\n#define CHRONYD_PATH \"/sbin/chronyd\"\n#define CROSS_DISTRO_SHARE_PATH \"/mnt/wsl\"\n#define DEVFS_PATH \"/dev\"\n#define DEVNULL_PATH DEVFS_PATH \"/null\"\n#define DHCPCD_CONF_PATH \"/dhcpcd.conf\"\n#define DHCPCD_PATH \"/usr/sbin/dhcpcd\"\n\n#define DISTRO_PATH \"/distro\"\n#define ETC_PATH \"/etc\"\n#define GPU_SHARE_PREFIX \"/gpu_\"\n#define GPU_SHARE_DRIVERS GPU_SHARE_PREFIX LXSS_GPU_DRIVERS_SHARE\n#define GPU_SHARE_LIB GPU_SHARE_PREFIX LXSS_GPU_LIB_SHARE\n#define GPU_SHARE_LIB_INBOX GPU_SHARE_LIB \"_inbox\"\n#define GPU_SHARE_LIB_PACKAGED GPU_SHARE_LIB \"_packaged\"\n#define KERNEL_MODULES_PATH \"/lib/modules\"\n#define KERNEL_MODULES_VHD_PATH \"/modules\"\n#define KERNEL_MODULES_OVERLAY \"/modules_overlay\"\n#define MODPROBE_PATH \"/sbin/modprobe\"\n#define PROCFS_PATH \"/proc\"\n#define RESOLV_CONF_FILE \"resolv.conf\"\n#define RESOLV_CONF_PATH ETC_PATH \"/\" RESOLV_CONF_FILE\n#define RECLAIM_PATH \"/sys/fs/cgroup/memory.reclaim\"\n#define SCSI_DEVICE_PATH \"/sys/bus/scsi/devices\"\n#define SCSI_DEVICE_NAME_PREFIX \"0:0:0:\"\n#define SCSI_DEVICE_PREFIX SCSI_DEVICE_PATH \"/\" SCSI_DEVICE_NAME_PREFIX\n#define SYSFS_PATH \"/sys\"\n#define SYSTEM_DISTRO_PATH \"/system\"\n#define SYSTEM_DISTRO_VHD_PATH \"/systemvhd\"\n#define WSLG_PATH \"/wslg\"\n\n#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))\n#define syscall_nr (offsetof(struct seccomp_data, nr))\n#define syscall_arch (offsetof(struct seccomp_data, arch))\n\nconstexpr auto c_trueString = \"1\";\n\nstruct VmConfiguration\n{\n    bool EnableGpuSupport = false;\n    bool EnableGuiApps = false;\n    bool EnableInboxGpuLibs = false;\n    bool EnableSafeMode = false;\n    bool EnableSystemDistro = false;\n    bool EnableCrashDumpCollection = false;\n    std::string KernelModulesPath;\n    LX_MINI_INIT_NETWORKING_MODE NetworkingMode = LxMiniInitNetworkingModeNone;\n};\n\nint g_LogFd = STDERR_FILENO;\nint g_TelemetryFd = -1;\nstd::optional<bool> g_EnableSocketLogging;\n\nint Chroot(const char* Target);\n\nvoid ConfigureMemoryReduction(int PageReportingOrder, LX_MINI_INIT_MEMORY_RECLAIM_MODE Mode);\n\nvoid CreateSwap(unsigned int Lun);\n\nint CreateTempDirectory(const char* ParentPath, std::string& Path);\n\nint DetachScsiDisk(unsigned int Lun);\n\nint EjectScsi(unsigned int Lun);\n\nint EnableInterface(int Socket, const char* Name);\n\nint ExportToSocket(const char* Source, int Socket, int ErrorSocket, unsigned int flags);\n\nint FormatDevice(unsigned int Lun);\n\nstd::string GetLunDeviceName(unsigned int Lun);\n\nstd::string GetLunDevicePath(unsigned int Lun);\n\nint GetDiskPartitionIndex(const char* DiskPath, const char* PartitionName);\n\nstd::string GetMountTarget(const char* Name);\n\nlong long int GetUserCpuTime(void);\n\nssize_t GetMemoryInUse(void);\n\nint ImportFromSocket(const char* Destination, int Socket, int ErrorSocket, unsigned int Flags);\n\nint Initialize(const char* Hostname);\n\nvoid InjectEntropy(gsl::span<gsl::byte> EntropyBuffer);\n\nvoid LaunchInit(\n    int SocketFd,\n    const char* Target,\n    bool EnableGuiApps,\n    const VmConfiguration& Config,\n    const char* VmId = nullptr,\n    const char* DistributionName = nullptr,\n    const char* SharedMemoryRoot = nullptr,\n    const char* InstallPath = nullptr,\n    const char* UserProfile = nullptr,\n    std::optional<pid_t> DistroInitPid = {});\n\nvoid LaunchSystemDistro(\n    int SocketFd,\n    const char* Target,\n    const VmConfiguration& Config,\n    const char* VmId,\n    const char* DistributionName,\n    const char* SharedMemoryRoot,\n    const char* InstallPath,\n    const char* UserProfile,\n    pid_t DistroInitPid);\n\nstd::map<unsigned long, std::string> ListDiskPartitions(const std::string& DeviceName, std::optional<unsigned long> WaitForIndex = {});\n\nstd::vector<unsigned int> ListScsiDisks();\n\nvoid LogException(const char* Message, const char* Description) noexcept;\n\nint MountDevice(LX_MINI_INIT_MOUNT_DEVICE_TYPE DeviceType, unsigned int DeviceId, const char* Target, const char* FsType, unsigned int Flags, const char* Options);\n\nint MountSystemDistro(LX_MINI_INIT_MOUNT_DEVICE_TYPE DeviceType, unsigned int DeviceId);\n\nint MountInit(const char* Target);\n\nint MountPlan9(const char* Name, const char* Target, bool ReadOnly, std::optional<int> BufferSize = {});\n\nint ProcessMessage(wsl::shared::SocketChannel& channel, LX_MESSAGE_TYPE Type, gsl::span<gsl::byte> Buffer, VmConfiguration& Config);\n\nwil::unique_fd RegisterSeccompHook();\n\nint ReportMountStatus(wsl::shared::SocketChannel& Channel, int Result, LX_MINI_MOUNT_STEP Step);\n\nint SendCapabilities(wsl::shared::SocketChannel& Channel);\n\nint SetCloseOnExec(int Fd, bool Enable);\n\nint SetEphemeralPortRange(uint16_t Start, uint16_t End);\n\nvoid StartDebugShell();\n\nint StartDhcpClient(int DhcpTimeout);\n\nint StartGuestNetworkService(int GnsFd, wil::unique_fd&& DnsTunnelingFd, uint32_t DnsTunnelingIpAddress);\n\nvoid StartPortTracker(LX_MINI_INIT_PORT_TRACKER_TYPE Type);\n\nvoid StartTimeSyncAgent(void);\n\nvoid WaitForBlockDevice(const char* Path);\n\nint WaitForChild(pid_t Pid, const char* Name);\n\nint Chroot(const char* Target)\n\n/*++\n\nRoutine Description:\n\n    This routine changes the root directory of the calling process to the specified\n    path.\n\nArguments:\n\n    Target - Supplies the path to chroot to.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    //\n    // Set the current working directory to the distro mount point, move the\n    // mount to the root, and chroot.\n    //\n\n    if (chdir(Target) < 0)\n    {\n        LOG_ERROR(\"chdir({}) failed {}\", Target, errno);\n        return -1;\n    }\n\n    if (mount(\".\", \"/\", nullptr, MS_MOVE, nullptr) < 0)\n    {\n        LOG_ERROR(\"mount(MS_MOVE) failed {}\", errno);\n        return -1;\n    }\n\n    if (chroot(\".\") < 0)\n    {\n        LOG_ERROR(\"chroot failed {}\", errno);\n        return -1;\n    }\n\n    return 0;\n}\n\nvoid ConfigureMemoryReduction(int PageReportingOrder, LX_MINI_INIT_MEMORY_RECLAIM_MODE Mode)\n\n/*++\n\nRoutine Description:\n\n    This routine sets the page reporting order.\n\nArguments:\n\n    PageReportingOrder - Supplies the page reporting order. This value determines the size of cold discard hints\n        by using the equation: 2^PageReportingOrder * PAGE_SIZE\n        Example: 2^9 * 4096 = 2MB\n\n    Mode - Supplies the memory reclaim mode.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    //\n    // Ensure the value falls within a reasonable range (single page to 2MB).\n    //\n\n    if (PageReportingOrder < 0 || PageReportingOrder > 9)\n    {\n        LOG_WARNING(\"Invalid page_reporting_order {}\", PageReportingOrder);\n        PageReportingOrder = 0;\n    }\n    else\n    {\n        WriteToFile(\"/sys/module/page_reporting/parameters/page_reporting_order\", std::to_string(PageReportingOrder).c_str());\n    }\n\n    //\n    // Create a worker thread to periodically check if the VM is idle and performs memory compaction.\n    // This ensures that the maximum number of pages can be discarded to the host.\n    //\n    // N.B. Compaction is not needed if page reporting order is set to single page mode.\n    //\n\n    if (PageReportingOrder == 0 && Mode == LxMiniInitMemoryReclaimModeDisabled)\n    {\n        return;\n    }\n\n    std::thread([PageReportingOrder = PageReportingOrder, Mode = Mode]() mutable {\n        try\n        {\n            //\n            // Set the thread's scheduling policy to idle.\n            //\n\n            sched_param Parameter{};\n            Parameter.sched_priority = 0;\n            THROW_LAST_ERROR_IF(pthread_setschedparam(pthread_self(), SCHED_IDLE, &Parameter) < 0);\n\n            //\n            // Periodically check if the machine is idle by querying procfs for CPU usage.\n            // Memory compaction will occur if both of the following conditions are true:\n            //     1. The CPU time since the last check is greater than the idle threshold.\n            //     2. The current CPU usage is below the idle threshold. This is measured by taking two readings one second apart.\n            //\n\n            double MemoryLow = 1024 * 1024 * 1024;\n            double MemoryHigh = 1.1 * 1024.0 * 1024.0 * 1024.0;\n            const int IdleThreshold = get_nprocs(); // Change math to adjust if sysconf(_SC_CLK_TCK) != 100? Is 1%\n            long long int Start, Stop = 0;\n            auto constexpr SleepDuration = std::chrono::seconds(30);\n            size_t ReclaimIndex = 0;\n            long long int const ReclaimThreshold = (get_nprocs() * sysconf(_SC_CLK_TCK) * SleepDuration / std::chrono::seconds(1)) / 200; // 0.5%\n            long long int ReclaimWindow[20] = {}; // 10 minutes\n            long long int ReclaimWindowLength = COUNT_OF(ReclaimWindow);\n            bool ReclaimIdling;\n\n            //\n            // Fall back to drop cache if the required cgroup path is not present.\n            //\n\n            if (Mode == LxMiniInitMemoryReclaimModeGradual && access(RECLAIM_PATH, W_OK) < 0)\n            {\n                LOG_WARNING(\"access({}, W_OK) failed {}, falling back to autoMemoryReclaim = dropcache\", RECLAIM_PATH, errno);\n                Mode = LxMiniInitMemoryReclaimModeDropCache;\n            }\n\n            if (Mode == LxMiniInitMemoryReclaimModeGradual)\n            {\n                static_assert(COUNT_OF(ReclaimWindow) >= 6);\n                ReclaimWindowLength = 6; // Set to 3 minutes.\n            }\n\n            for (auto i = 1; i < ReclaimWindowLength; i++)\n            {\n                ReclaimWindow[i] = LLONG_MIN;\n            }\n\n            std::this_thread::sleep_for(SleepDuration);\n            for (;;)\n            {\n                auto const Target = std::chrono::steady_clock::now() + SleepDuration;\n                Start = GetUserCpuTime();\n                THROW_LAST_ERROR_IF(Start == -1);\n\n                if (Mode != LxMiniInitMemoryReclaimModeDisabled)\n                {\n                    //\n                    // Ensure that utilization is below 0.5% from the last 30 seconds, and last n minutes, of usage.\n                    //\n\n                    size_t const LastIndex = (ReclaimIndex + 1) % ReclaimWindowLength;\n                    if ((ReclaimWindow[LastIndex] > Start - ReclaimThreshold * (ReclaimWindowLength + 1)) &&\n                        (ReclaimWindow[ReclaimIndex] > Start - ReclaimThreshold))\n                    {\n                        if (Mode == LxMiniInitMemoryReclaimModeGradual)\n                        {\n                            double MemorySize = GetMemoryInUse();\n                            THROW_LAST_ERROR_IF(MemorySize < 0);\n\n                            if (MemorySize > MemoryHigh)\n                            {\n                                ReclaimIdling = false;\n                            }\n\n                            if (!ReclaimIdling && MemorySize > MemoryLow)\n                            {\n                                double MemoryTargetSize = MemorySize * 0.97;\n                                std::string MemoryToFree = std::to_string(size_t(MemorySize - MemoryTargetSize));\n                                // EAGAIN Means that it attempted, but was unable to evict sufficient pages.\n                                THROW_LAST_ERROR_IF(WriteToFile(RECLAIM_PATH, MemoryToFree.c_str()) < 0 && errno != EAGAIN);\n\n                                if (MemoryTargetSize < MemoryLow)\n                                {\n                                    ReclaimIdling = true;\n                                }\n                            }\n                        }\n                        else if (!ReclaimIdling)\n                        {\n                            ReclaimIdling = true;\n                            THROW_LAST_ERROR_IF(WriteToFile(PROCFS_PATH \"/sys/vm/drop_caches\", \"1\\n\") < 0);\n                        }\n                    }\n                    else\n                    {\n                        ReclaimIdling = false;\n                    }\n\n                    ReclaimIndex = LastIndex;\n                    ReclaimWindow[ReclaimIndex] = Start;\n                }\n\n                //\n                // Perform memory compaction if the VM is idle.\n                //\n                // N.B. Memory compaction is not needed if the page reporting order is set to single page (0).\n                //\n\n                if (PageReportingOrder != 0 && (Start - Stop) > IdleThreshold)\n                {\n                    std::this_thread::sleep_for(std::chrono::seconds(1));\n                    const long long int Stop = GetUserCpuTime();\n                    THROW_LAST_ERROR_IF(Stop == -1);\n                    if ((Stop - Start) < IdleThreshold)\n                    {\n                        THROW_LAST_ERROR_IF(WriteToFile(PROCFS_PATH \"/sys/vm/compact_memory\", \"1\\n\") < 0);\n                    }\n                }\n\n                std::this_thread::sleep_until(Target);\n            }\n        }\n        CATCH_LOG()\n    }).detach();\n}\nCATCH_LOG()\n\nwil::unique_fd CreateNetlinkSocket(void)\n\n/*++\n\nRoutine Description:\n\n    Create and bind a netlink socket.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    The socket file descriptor or < 0 on failure.\n\n--*/\n\n{\n    wil::unique_fd Fd{socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG)};\n    if (!Fd)\n    {\n        LOG_ERROR(\"socket failed {}\", errno);\n        return {};\n    }\n\n    struct sockaddr_nl Address;\n    Address.nl_family = AF_NETLINK;\n    if (bind(Fd.get(), (struct sockaddr*)&Address, sizeof(Address)) < 0)\n    {\n        LOG_ERROR(\"bind failed {}\", errno);\n        return {};\n    }\n\n    return Fd;\n}\n\nvoid CreateSwap(unsigned int Lun)\n\n/*++\n\nRoutine Description:\n\n    This routine sets up a swap area on the specified SCSI device.\n\nArguments:\n\n    Lun - Supplies the LUN number of the SCSI device.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    //\n    // Create the swap file asynchronously using the mkswap and swapon utilities in the system distro.\n    //\n    // N.B. This is done because creating the swap file can take some time and\n    //      the swap file does not need to be available immediately.\n    //\n\n    UtilCreateChildProcess(\"CreateSwap\", [Lun]() {\n        std::string DevicePath = GetLunDevicePath(Lun);\n\n        WaitForBlockDevice(DevicePath.c_str());\n\n        std::string CommandLine = std::format(\"/usr/sbin/mkswap '{}'\", DevicePath);\n        THROW_LAST_ERROR_IF(UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0);\n\n        CommandLine = std::format(\"/usr/sbin/swapon '{}'\", DevicePath);\n        UtilExecCommandLine(CommandLine.c_str(), nullptr);\n    });\n}\n\nint CreateTempDirectory(const char* ParentPath, std::string& Path)\n\n/*++\n\nRoutine Description:\n\n    This routine creates a unique directory under the specified parent path.\n\nArguments:\n\n    ParentPath - Supplies the path of the parent directory.\n\n    Path - Supplies a buffer to receive the path of the child directory that was\n        created.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    if (ParentPath)\n    {\n        Path = ParentPath;\n    }\n\n    //\n    // Generate a random name for the directory.\n    //\n    // N.B. mkdtemp requires a template string that ends in \"XXXXXX\".\n    //\n\n    Path += \"/wslXXXXXX\";\n\n    if (mkdtemp(Path.data()) == NULL)\n    {\n        LOG_ERROR(\"mkdtemp({}) failed {}\", Path.c_str(), errno);\n        return -1;\n    }\n\n    return 0;\n}\n\ndev_t GetBlockDeviceNumber(const std::string& BlockDeviceName)\n\n/*++\n\nRoutine Description:\n\n    This method return the device number of a given block device.\n\nArguments:\n\n    BlockDeviceName - Supplies the name of the block device.\n\nReturn Value:\n\n    The device block number. Throws on error.\n\n--*/\n\n{\n    std::string content = wsl::shared::string::ReadFile<char, char>(std::format(\"/sys/block/{}/dev\", BlockDeviceName).c_str());\n    auto separator = content.find(':');\n\n    if (separator == 0 || separator - 1 >= content.size() || separator == std::string::npos)\n    {\n        LOG_ERROR(\"Failed to parse device number '{}' for device '{}'\", content.c_str(), BlockDeviceName.c_str());\n        THROW_ERRNO(EINVAL);\n    }\n\n    try\n    {\n        return makedev(std::strtoul(content.c_str(), nullptr, 10), std::strtoul(content.substr(separator + 1).c_str(), nullptr, 10));\n    }\n    catch (...)\n    {\n        LOG_ERROR(\"Failed to parse device number '{}' for device '{}'\", content.c_str(), BlockDeviceName.c_str());\n        THROW_ERRNO(EINVAL);\n    }\n}\n\nint DetachScsiDisk(unsigned int Lun)\n\n/*++\n\nRoutine Description:\n\n    This routine detaches a SCSI disk.\n\nArguments:\n\n    Lun - Supplies the LUN of the disk to detach.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    auto deviceName = GetLunDeviceName(Lun);\n\n    try\n    {\n        auto deviceNumbers = std::set<dev_t>{GetBlockDeviceNumber(deviceName)};\n        for (const auto& e : ListDiskPartitions(deviceName.c_str()))\n        {\n            deviceNumbers.insert(GetBlockDeviceNumber(std::format(\"{}/{}\", deviceName, e.second)));\n        }\n\n        mountutil::MountEnum mounts;\n        while (mounts.Next())\n        {\n            if (deviceNumbers.find(mounts.Current().Device) != deviceNumbers.end())\n            {\n                if (umount(mounts.Current().MountPoint) < 0)\n                {\n                    LOG_ERROR(\"Failed to unmount '{}', {}\", mounts.Current().MountPoint, errno);\n                }\n            }\n        }\n    }\n    CATCH_LOG();\n\n    // Flush the block device.\n    std::string DevicePath = DEVFS_PATH + std::string(\"/\") + deviceName;\n    wil::unique_fd BlockDevice{open(DevicePath.c_str(), O_RDONLY)};\n    int Result = ioctl(BlockDevice.get(), BLKFLSBUF);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"Failed to flush block device: '{}', {}\", DevicePath.c_str(), errno);\n        return Result;\n    }\n\n    // Close the device before trying to delete it.\n    BlockDevice.reset();\n\n    // Remove the block device.\n    return WriteToFile(std::format(\"/sys/block/{}/device/delete\", deviceName).c_str(), \"1\");\n}\n\nint DetectFilesystem(const char* BlockDevice, std::string& Output)\n\n/*++\n\nRoutine Description:\n\n    This routine performs file system detect on a block device.\n\nArguments:\n\n    BlockDevice - Path to the block device.\n\n    Output - Detected filesystem, if any.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\ntry\n{\n    //\n    // Wait for the block device to be available.\n    //\n\n    wsl::shared::retry::RetryWithTimeout<void>(\n        [&]() { THROW_LAST_ERROR_IF(!wil::unique_fd{open(BlockDevice, O_RDONLY)}); },\n        c_defaultRetryPeriod,\n        c_defaultRetryTimeout,\n        []() {\n            auto err = wil::ResultFromCaughtException();\n            return err == ENOENT || err == ENXIO;\n        });\n\n    auto CommandLine = std::format(\"/usr/sbin/blkid '{}' -p -s TYPE -o value -u filesystem\", BlockDevice);\n    if (UtilExecCommandLine(CommandLine.c_str(), &Output) < 0)\n    {\n        return -1;\n    }\n\n    while (!Output.empty() && Output.back() == '\\n')\n    {\n        Output.pop_back();\n    }\n\n    LOG_INFO(\"Detected {} filesystem for device: {}\", Output, BlockDevice);\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nint EjectScsi(unsigned int Lun)\n\n/*++\n\nRoutine Description:\n\n    This routine ejects the specified SCSI device.\n\nArguments:\n\n    Lun - Supplies the LUN of the SCSI device to eject.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    //\n    // Perform a sync to ensure all writes are flushed.\n    //\n\n    sync();\n\n    //\n    // Write \"1\" to /sys/bus/scsi/devices/0:0:<controller>:<lun>/delete to eject the SCSI device.\n    //\n\n    std::string Path = std::format(\"{}{}/delete\", SCSI_DEVICE_PREFIX, Lun);\n    if (WriteToFile(Path.c_str(), c_trueString) < 0)\n    {\n        return -1;\n    }\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nvoid EnableCrashDumpCollection()\n{\n    if (symlink(\"/init\", \"/\" LX_INIT_WSL_CAPTURE_CRASH) < 0)\n    {\n        LOG_ERROR(\"symlink({}, {}) failed {}\", \"/init\", \"/\" LX_INIT_WSL_CAPTURE_CRASH, errno);\n        return;\n    }\n\n    // If the first character is a pipe, then the kernel will interpret this path as a command.\n    constexpr auto core_pattern = \"|/\" LX_INIT_WSL_CAPTURE_CRASH \" %t %E %p %s\";\n    WriteToFile(\"/proc/sys/kernel/core_pattern\", core_pattern);\n}\n\nint EnableInterface(int Socket, const char* Name)\n\n/*++\n\nRoutine Description:\n\n    This routine marks the specified interface as up / running.\n\nArguments:\n\n    Socket - Supplies a socket file descriptor.\n\n    Name - Supplies the name of an interface.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    ifreq InterfaceRequest{};\n    strncpy(InterfaceRequest.ifr_name, Name, IFNAMSIZ - 1);\n    if (ioctl(Socket, SIOCGIFFLAGS, &InterfaceRequest) < 0)\n    {\n        LOG_ERROR(\"SIOCGIFFLAGS failed {}\", errno);\n        return -1;\n    }\n\n    InterfaceRequest.ifr_flags |= (IFF_UP | IFF_RUNNING);\n    if (ioctl(Socket, SIOCSIFFLAGS, &InterfaceRequest) < 0)\n    {\n        LOG_ERROR(\"SIOCSIFFLAGS failed {}\", errno);\n        return -1;\n    }\n\n    return 0;\n}\n\nint ExportToSocket(const char* Source, int Socket, int ErrorSocket, unsigned int Flags)\n\n/*++\n\nRoutine Description:\n\n    This routine uses bsdtar to export a source directory in tar format via a\n    socket.\n\nArguments:\n\n    Source - Supplies the path to export.\n\n    Socket - Supplies the socket to write to.\n\n    Flags - Additional compression flags.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    //\n    // Create a child process running bsdtar with the socket set to stdout.\n    //\n\n    int ChildPid = UtilCreateChildProcess(\"ExportDistro\", [Source, TarFd = Socket, ErrorSocket = ErrorSocket, Flags = Flags]() {\n        THROW_LAST_ERROR_IF(TEMP_FAILURE_RETRY(dup2(TarFd, STDOUT_FILENO)) < 0);\n        THROW_LAST_ERROR_IF(TEMP_FAILURE_RETRY(dup2(ErrorSocket, STDERR_FILENO)) < 0);\n\n        std::string compressionArguments;\n\n        if (WI_IsFlagSet(Flags, LxMiniInitMessageFlagExportCompressGzip))\n        {\n            assert(!WI_IsFlagSet(Flags, LxMiniInitMessageFlagExportCompressXzip));\n\n            compressionArguments = \"-cz\";\n        }\n        else if (WI_IsFlagSet(Flags, LxMiniInitMessageFlagExportCompressXzip))\n        {\n            compressionArguments = \"-cJ\";\n        }\n        else\n        {\n            compressionArguments = \"-c\";\n        }\n\n        if (WI_IsFlagSet(Flags, LxMiniInitMessageFlagVerbose))\n        {\n            compressionArguments += \"vv\";\n        }\n\n        std::vector<const char*> arguments{\n            BSDTAR_PATH,\n            \"-C\",\n            Source,\n            compressionArguments.c_str(),\n            \"--one-file-system\",\n            \"--xattrs\",\n            \"--numeric-owner\",\n            \"-f\",\n            \"-\",\n            \".\",\n            nullptr};\n\n        if (WI_IsFlagSet(Flags, LxMiniInitMessageFlagVerbose))\n        {\n            arguments.emplace(arguments.begin() + 3, \"--totals\");\n        }\n\n        execv(BSDTAR_PATH, const_cast<char**>(arguments.data()));\n        LOG_ERROR(\"execl failed, {}\", errno);\n    });\n\n    if (ChildPid < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Wait for the child to exit and shut down the socket.\n    //\n\n    const int Result = WaitForChild(ChildPid, BSDTAR_PATH);\n    if (shutdown(Socket, SHUT_WR) < 0)\n    {\n        LOG_ERROR(\"shutdown failed {}\", errno);\n    }\n\n    return Result;\n}\n\nint FormatDevice(unsigned int Lun)\n\n/*++\n\nRoutine Description:\n\n    This routine formats the specified SCSI device with the ext4 file system.\n    N.B. The group size was chosen based on the best practices for Linux VHDs:\n         https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/best-practices-for-running-linux-on-hyper-v\n\nArguments:\n\n    Lun - Supplies the LUN number of the SCSI device.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\ntry\n{\n    std::string DevicePath = GetLunDevicePath(Lun);\n\n    WaitForBlockDevice(DevicePath.c_str());\n\n    std::string CommandLine = std::format(\"/usr/sbin/mkfs.ext4 -G 4096 '{}'\", DevicePath);\n    if (UtilExecCommandLine(CommandLine.c_str(), nullptr) < 0)\n    {\n        return -1;\n    }\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nstd::string GetLunDeviceName(unsigned int Lun)\n\n/*++\n\nRoutine Description:\n\n    This routine returns the device name(sdX) for the specified SCSI device.\n\nArguments:\n\nLun - Supplies a SCSI LUN.\n\nReturn Value:\n\n    The device name (throws on error).\n\n--*/\n\n{\n    //\n    // Construct a path to the block directory which contains a single directory\n    // entry with the name of the device where the vhd is attached, for example: sda.\n    //\n    // N.B. A retry loop is needed because there is a delay between when the vhd\n    //      is hot-added from the host, and when the sysfs directory is\n    //      available in the guest.\n    //\n\n    std::string Path = std::format(\"{}{}/block\", SCSI_DEVICE_PREFIX, Lun);\n    return wsl::shared::retry::RetryWithTimeout<std::string>(\n        [&]() {\n            wil::unique_dir Dir{opendir(Path.c_str())};\n            THROW_LAST_ERROR_IF(!Dir);\n\n            //\n            // Find the first directory entry that does not begin with a dot.\n            //\n\n            dirent64* Entry{};\n            while ((Entry = readdir64(Dir.get())) != nullptr)\n            {\n                if (Entry->d_name[0] != '.')\n                {\n                    return std::string(Entry->d_name);\n                }\n            }\n\n            THROW_ERRNO(ENXIO);\n        },\n        c_defaultRetryPeriod,\n        c_defaultRetryTimeout);\n}\n\nstd::string GetLunDevicePath(unsigned int Lun)\n\n/*++\n\nRoutine Description:\n\n    This routine returns the device path for the specified SCSI device.\n\nArguments:\n\n    Lun - Supplies a SCSI LUN.\n\nReturn Value:\n\n    The device path;\n\n--*/\n\n{\n    auto DeviceName = GetLunDeviceName(Lun);\n\n    return std::format(\"{}/{}\", DEVFS_PATH, DeviceName.c_str());\n}\n\nint GetDiskPartitionIndex(const char* DiskPath, const char* PartitionName)\n\n/*++\n\nRoutine Description:\n\n    Finds the partition number of a specified partition path.\n\nArguments:\n\n    DiskPath - Supplies the path to the Disk (ex: /sys/block/sda).\n\n    PartitionName - Supplies the partition name (ex: sda1).\n\nReturn Value:\n\n    > 0 (the partition number), < 0 on failure.\n\n--*/\n\ntry\n{\n    std::string FilePath = std::format(\"{}/{}/partition\", DiskPath, PartitionName);\n    wil::unique_fd Fd{open(FilePath.c_str(), O_RDONLY)};\n    if (!Fd)\n    {\n        LOG_ERROR(\"open({}) failed {}\", FilePath, errno);\n        return -errno;\n    }\n\n    char Buffer[64];\n    int Result = TEMP_FAILURE_RETRY(read(Fd.get(), Buffer, (sizeof(Buffer) - 1)));\n    if (Result < 0)\n    {\n        LOG_ERROR(\"read failed {}\", errno);\n        return -errno;\n    }\n\n    Buffer[Result] = '\\0';\n    return atol(Buffer);\n}\nCATCH_RETURN_ERRNO()\n\nlong long int GetUserCpuTime(void)\n\n/*++\n\nRoutine Description:\n\n    This routine parses /proc/stat to query a summary of all user CPU time.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    The current user CPU counter for all cores.\n\n--*/\n\n{\n    wil::unique_fd Fd{open(PROCFS_PATH \"/stat\", O_RDONLY)};\n    if (!Fd)\n    {\n        LOG_ERROR(\"open failed {}\", errno);\n        return -1;\n    }\n\n    char Buffer[32];\n    int Result = TEMP_FAILURE_RETRY(read(Fd.get(), Buffer, (sizeof(Buffer) - 1)));\n    if (Result < 0)\n    {\n        LOG_ERROR(\"read failed {}\", errno);\n        return -1;\n    }\n\n    //\n    // Parse the first line of /proc/stat which is in the format\n    // \"cpu  <counter>\".\n    //\n\n    Buffer[Result] = '\\0';\n    char* Sp1;\n    char* Info = strtok_r(Buffer, \" \\n\", &Sp1);\n    Info = strtok_r(nullptr, \" \\n\", &Sp1);\n    return strtoll(Info, nullptr, 10);\n}\n\nssize_t GetMemoryInUse(void)\n\n/*++\n\nRoutine Description:\n\n    This routine returns the amount memory in use in bytes.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    Total memory - Free memory. Includes that used by cache and buffers.\n\n--*/\ntry\n{\n    struct sysinfo Info = {};\n    THROW_LAST_ERROR_IF(sysinfo(&Info) < 0);\n    return Info.totalram - Info.freeram;\n}\nCATCH_RETURN_ERRNO()\n\nint ImportFromSocket(const char* Destination, int Socket, int ErrorSocket, unsigned int Flags)\n\n/*++\n\nRoutine Description:\n\n    This routine uses bsdtar to extract a tar file via a socket.\n\nArguments:\n\n    Destination - Supplies the path to extract the tar.\n\n    Socket - Supplies the socket to read from.\n\n    Flags - Import flags.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    //\n    // Create a child process running bsdtar with the socket set to stdin.\n    //\n\n    int ChildPid = UtilCreateChildProcess(\"ImportDistro\", [Destination, TarFd = Socket, ErrorSocket = ErrorSocket, Flags]() {\n        THROW_LAST_ERROR_IF(TEMP_FAILURE_RETRY(dup2(TarFd, STDIN_FILENO)) < 0);\n        THROW_LAST_ERROR_IF(TEMP_FAILURE_RETRY(dup2(ErrorSocket, STDERR_FILENO)) < 0);\n\n        execl(\n            BSDTAR_PATH,\n            BSDTAR_PATH,\n            \"-C\",\n            Destination,\n            \"-x\",\n            WI_IsFlagSet(Flags, LxMiniInitMessageFlagVerbose) ? \"-vvp\" : \"-p\",\n            \"--xattrs\",\n            \"--numeric-owner\",\n            \"-f\",\n            \"-\",\n            NULL);\n        LOG_ERROR(\"execl failed, {}\", errno);\n    });\n\n    if (ChildPid < 0)\n    {\n        return -1;\n    }\n\n    return WaitForChild(ChildPid, BSDTAR_PATH);\n}\n\nvoid StartDebugShell()\n\n/*++\n\nRoutine Description:\n\n    This routine starts the debug shell.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    // Spawn a child process to handle relaunching the debug shell if it exits.\n    UtilCreateChildProcess(\"DebugShell\", []() {\n        for (;;)\n        {\n            const auto Pid = UtilCreateChildProcess(\"agetty\", []() {\n                execl(\"/usr/bin/setsid\", \"/usr/bin/setsid\", \"/sbin/agetty\", \"-w\", \"-L\", LX_INIT_HVC_DEBUG_SHELL, \"-a\", \"root\", NULL);\n                LOG_ERROR(\"execl failed, {}\", errno);\n            });\n\n            if (Pid < 0)\n            {\n                _exit(1);\n            }\n\n            int Status = -1;\n            if (TEMP_FAILURE_RETRY(waitpid(Pid, &Status, 0)) < 0)\n            {\n                LOG_ERROR(\"waitpid failed {}\", errno);\n                _exit(1);\n            }\n        }\n    });\n}\n\nint StartDhcpClient(int DhcpTimeout)\n\n/*++\n\nRoutine Description:\n\n    Starts the dhcp client daemon. Blocks until the initial DHCP lease is acquired,\n    then the daemon continues running in the background to handle renewals.\n\nArguments:\n\n    DhcpTimeout - Supplies the timeout in seconds for the DHCP request.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\n{\n    int ChildPid = UtilCreateChildProcess(\"dhcpcd\", [DhcpTimeout]() {\n        //\n        // Write the dhcpcd.conf config file.\n        //\n\n        std::string Config = std::format(\n            \"option subnet_mask, routers, broadcast, domain_name, domain_name_servers, domain_search, host_name, interface_mtu\\n\"\n            \"noarp\\n\"\n            \"timeout {}\\n\",\n            DhcpTimeout);\n\n        THROW_LAST_ERROR_IF(WriteToFile(DHCPCD_CONF_PATH, Config.c_str()) < 0);\n\n        execl(DHCPCD_PATH, DHCPCD_PATH, \"-w\", \"-4\", \"-f\", DHCPCD_CONF_PATH, \"eth0\", NULL);\n        LOG_ERROR(\"execl({}) failed, {}\", DHCPCD_PATH, errno);\n    });\n\n    if (ChildPid < 0)\n    {\n        return -1;\n    }\n\n    return WaitForChild(ChildPid, DHCPCD_PATH);\n}\n\nint StartGuestNetworkService(int GnsFd, wil::unique_fd&& DnsTunnelingFd, uint32_t DnsTunnelingIpAddress)\n\n/*++\n\nRoutine Description:\n\n    Start the guest network service.\n\nArguments:\n\n    GnsFd - Supplies the socket file descriptor to use for the guest network service.\n\n    DnsTunnelingFd - Supplies an optional file descriptor to be used for DNS tunneling.\n\n    DnsTunnelingIpAddress - IP address to be used by the DNS tunneling listener.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    const auto ChildPid =\n        UtilCreateChildProcess(\"GuestNetworkService\", [GnsFd, DnsTunnelingFd = std::move(DnsTunnelingFd), DnsTunnelingIpAddress]() {\n            std::string GnsSocketArg = std::to_string(GnsFd);\n            THROW_LAST_ERROR_IF(SetCloseOnExec(GnsFd, false) < 0);\n\n            if (DnsTunnelingFd)\n            {\n                std::string DnsSocketArg = std::to_string(DnsTunnelingFd.get());\n                THROW_LAST_ERROR_IF(SetCloseOnExec(DnsTunnelingFd.get(), false) < 0);\n\n                in_addr address{.s_addr = DnsTunnelingIpAddress};\n                Address dnsIp = Address::FromBinary(AF_INET, 32, &address);\n                execl(\n                    LX_INIT_PATH,\n                    LX_INIT_GNS,\n                    LX_INIT_GNS_SOCKET_ARG,\n                    GnsSocketArg.c_str(),\n                    LX_INIT_GNS_DNS_SOCKET_ARG,\n                    DnsSocketArg.c_str(),\n                    LX_INIT_GNS_DNS_TUNNELING_IP,\n                    dnsIp.Addr().c_str(),\n                    nullptr);\n            }\n            else\n            {\n                execl(LX_INIT_PATH, LX_INIT_GNS, LX_INIT_GNS_SOCKET_ARG, GnsSocketArg.c_str(), nullptr);\n            }\n\n            LOG_ERROR(\"execl failed, {}\", errno);\n        });\n\n    return (ChildPid < 0) ? -1 : 0;\n}\n\nvoid StartPortTracker(LX_MINI_INIT_PORT_TRACKER_TYPE Type)\n\n/*++\n\nRoutine Description:\n\n    Start a port tracker daemon.\n\nArguments:\n\n    Type - specifies the type of port tracker (localhost relay or mirrored).\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    auto PortTrackerFd = UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, false);\n    if (!PortTrackerFd)\n    {\n        return;\n    }\n\n    wil::unique_fd NetlinkSocket{};\n    wil::unique_fd BpfFd{};\n    wil::unique_fd GuestRelayFd{};\n    switch (Type)\n    {\n    case LxMiniInitPortTrackerTypeMirrored:\n    {\n\n        //\n        // Create a netlink socket before registering the bpf filter so creation of the socket\n        // does not trigger the filter.\n        //\n\n        NetlinkSocket = CreateNetlinkSocket();\n        if (!NetlinkSocket)\n        {\n            return;\n        }\n\n        BpfFd = RegisterSeccompHook();\n        if (!BpfFd)\n        {\n            return;\n        }\n\n        break;\n    }\n    case LxMiniInitPortTrackerTypeRelay:\n    {\n        sockaddr_vm HvSocketAddress = {};\n        GuestRelayFd.reset(UtilListenVsockAnyPort(&HvSocketAddress, -1, false));\n        if (!GuestRelayFd)\n        {\n            return;\n        }\n\n        break;\n    }\n    default:\n        assert(false);\n        return;\n    }\n\n    UtilCreateChildProcess(\n        \"PortTracker\",\n        [PortTrackerFd = std::move(PortTrackerFd),\n         NetlinkSocket = std::move(NetlinkSocket),\n         BpfFd = std::move(BpfFd),\n         GuestRelayFd = std::move(GuestRelayFd)]() {\n            execl(\n                LX_INIT_PATH,\n                LX_INIT_LOCALHOST_RELAY,\n                INIT_PORT_TRACKER_FD_ARG,\n                std::format(\"{}\", PortTrackerFd.get()).c_str(),\n                INIT_BPF_FD_ARG,\n                std::format(\"{}\", BpfFd.get()).c_str(),\n                INIT_NETLINK_FD_ARG,\n                std::format(\"{}\", NetlinkSocket.get()).c_str(),\n                INIT_PORT_TRACKER_LOCALHOST_RELAY,\n                std::format(\"{}\", GuestRelayFd.get()).c_str(),\n                NULL);\n\n            LOG_ERROR(\"execl failed {}\", errno);\n        });\n}\n\nint Initialize(const char* Hostname)\n\n/*++\n\nRoutine Description:\n\n    This routine performs initialization required for mini_init functionality.\n\nArguments:\n\n    Hostname - Supplies a string specifying the hostname.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\n{\n    //\n    // Allow unprivileged users to view the kernel log.\n    //\n\n    if (WriteToFile(PROCFS_PATH \"/sys/kernel/dmesg_restrict\", \"0\\n\") < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Set max inotify watches to the value suggested by Visual Studio Code Remote.\n    //\n\n    if (WriteToFile(PROCFS_PATH \"/sys/fs/inotify/max_user_watches\", \"524288\\n\") < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Increase the soft and hard limit for number of open file descriptors.\n    // N.B. the soft limit shouldn't be too high. See https://github.com/microsoft/WSL/issues/12985 .\n    //\n\n    rlimit Limit{};\n    Limit.rlim_cur = 1024 * 10;\n    Limit.rlim_max = 1024 * 1024;\n    if (setrlimit(RLIMIT_NOFILE, &Limit) < 0)\n    {\n        LOG_ERROR(\"setrlimit(RLIMIT_NOFILE) failed {}\", errno);\n        return -1;\n    }\n\n    //\n    // Increase the maximum number of bytes of memory that may be locked into RAM.\n    //\n\n    Limit.rlim_cur = 0x4000000;\n    Limit.rlim_max = 0x4000000;\n    if (setrlimit(RLIMIT_MEMLOCK, &Limit) < 0)\n    {\n        LOG_ERROR(\"setrlimit(RLIMIT_MEMLOCK) failed {}\", errno);\n        return -1;\n    }\n\n    //\n    // Enable the loopback interface.\n    //\n\n    wil::unique_fd Fd{socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)};\n    if (!Fd)\n    {\n        LOG_ERROR(\"socket failed {}\", errno);\n        return -1;\n    }\n\n    if (EnableInterface(Fd.get(), \"lo\") < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Enable logging when processes receive fatal signals.\n    //\n\n    if (WriteToFile(\"/proc/sys/kernel/print-fatal-signals\", \"1\\n\") < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Disable rate limiting of user writes to dmesg.\n    //\n\n    if (WriteToFile(\"/proc/sys/kernel/printk_devkmsg\", \"on\\n\") < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Set the hostname.\n    //\n\n    if (sethostname(Hostname, strlen(Hostname)) < 0)\n    {\n        LOG_ERROR(\"sethostname({}) failed {}\", Hostname, errno);\n    }\n\n    //\n    // Create a tmpfs mount for the cross-distro shared mount.\n    //\n\n    if (UtilMount(nullptr, CROSS_DISTRO_SHARE_PATH, \"tmpfs\", 0, nullptr) < 0)\n    {\n        return -1;\n    }\n\n    if (mount(nullptr, CROSS_DISTRO_SHARE_PATH, nullptr, MS_SHARED, nullptr) < 0)\n    {\n        LOG_ERROR(\"mount({}, MS_SHARED) failed {}\", CROSS_DISTRO_SHARE_PATH, errno);\n        return -1;\n    }\n\n    //\n    // Create the resolv.conf symlink in the cross-distro share (gns writes to /etc/resolv.conf).\n    //\n\n    remove(RESOLV_CONF_PATH);\n    if (symlink(CROSS_DISTRO_SHARE_PATH \"/\" RESOLV_CONF_FILE, RESOLV_CONF_PATH) < 0)\n    {\n        LOG_ERROR(\"symlink({}, {}) failed {}\", CROSS_DISTRO_SHARE_PATH \"/\" RESOLV_CONF_FILE, RESOLV_CONF_PATH, errno);\n        return -1;\n    }\n\n    //\n    // Mount the binfmt_misc filesystem.\n    //\n\n    if (UtilMount(nullptr, BINFMT_PATH, \"binfmt_misc\", MS_RELATIME, nullptr) < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Register the Windows interop interpreter using the 'F' flag which makes\n    // it available in other mount namespaces and chroot environments.\n    //\n\n    if (WriteToFile(BINFMT_PATH \"/register\", BINFMT_REGISTER_STRING) < 0)\n    {\n        return -1;\n    }\n\n    return 0;\n}\n\nint InitializeLogging(bool SetStderr, wil::LogFunction* ExceptionCallback) noexcept\n\n/*++\n\nRoutine Description:\n\n    This routine opens /dev/kmsg for logging and optionally sets it as stderr.\n\nArguments:\n\n    SetStderr - Supplies a boolean specifying if kmsg should be set as stderr.\n\n    ExceptionCallback - Supplies an optional callback to log exceptions.\n        If not callback is specified, the default callback is used.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\n{\n    wil::g_LogExceptionCallback = ExceptionCallback ? ExceptionCallback : LogException;\n    auto devicePath = DEVFS_PATH \"/kmsg\";\n    g_LogFd = TEMP_FAILURE_RETRY(open(devicePath, (O_WRONLY | O_CLOEXEC)));\n    if (g_LogFd < 0)\n    {\n        g_LogFd = STDERR_FILENO;\n        LOG_ERROR(\"open({}) failed {}\", devicePath, errno);\n        return -1;\n    }\n    else if (SetStderr)\n    {\n        if (g_LogFd != STDERR_FILENO)\n        {\n            if (dup2(g_LogFd, STDERR_FILENO) < 0)\n            {\n                LOG_ERROR(\"dup2({}, {}) failed {}\", g_LogFd, STDERR_FILENO, errno);\n                return -1;\n            }\n\n            close(g_LogFd);\n            g_LogFd = STDERR_FILENO;\n        }\n\n        if (SetCloseOnExec(g_LogFd, false) < 0)\n        {\n            return -1;\n        }\n    }\n\n    // Initialize logging to the hvc console device responsible for logging telemetry.\n    if (UtilIsUtilityVm())\n    {\n        devicePath = DEVFS_PATH \"/\" LX_INIT_HVC_TELEMETRY;\n        g_TelemetryFd = TEMP_FAILURE_RETRY(open(devicePath, (O_WRONLY | O_CLOEXEC)));\n        if (g_TelemetryFd < 0)\n        {\n            LOG_ERROR(\"open({}) failed {}\", devicePath, errno);\n        }\n    }\n\n    return 0;\n}\n\nvoid InjectEntropy(gsl::span<gsl::byte> EntropyBuffer)\n\n/*++\n\nRoutine Description:\n\n    This routine injects boot-time entropy from the provided source.\n\nArguments:\n\n    EntropyBuffer - Supplies a buffer of bytes to use as entropy.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    wil::unique_fd Fd{open(DEVFS_PATH \"/random\", O_RDWR)};\n    if (!Fd)\n    {\n        LOG_ERROR(\"open failed {}\", errno);\n        return;\n    }\n\n    std::vector<gsl::byte> Buffer(sizeof(rand_pool_info) + EntropyBuffer.size());\n    auto* PoolInfo = gslhelpers::get_struct<rand_pool_info>(gsl::make_span(Buffer));\n    PoolInfo->entropy_count = EntropyBuffer.size() * 8;\n    PoolInfo->buf_size = EntropyBuffer.size();\n    gsl::copy(EntropyBuffer, gsl::as_writable_bytes(gsl::make_span(PoolInfo->buf, PoolInfo->buf_size)));\n    if (ioctl(Fd.get(), RNDADDENTROPY, PoolInfo) < 0)\n    {\n        LOG_ERROR(\"ioctl(RNDADDENTROPY) failed {}\", errno);\n    }\n\n    return;\n}\n\nvoid LaunchInit(\n    int SocketFd,\n    const char* Target,\n    bool EnableGuiApps,\n    const VmConfiguration& Config,\n    const char* VmId,\n    const char* DistributionName,\n    const char* SharedMemoryRoot,\n    const char* InstallPath,\n    const char* UserProfile,\n    std::optional<pid_t> DistroInitPid)\n\n/*++\n\nRoutine Description:\n\n    This routine launches the init daemon for the specified distro.\n\nArguments:\n\n    SocketFd - Supplies a file descriptor to communicate with the init daemon.\n        This routine takes ownership of this file descriptor.\n\n    Target - Supplies the location where the distro filesystem is mounted.\n\n    EnableGuiApps - True if GUI apps should be enabled.\n\n    VmConfiguration - Supplies the VM configuration.\n\n    VmId - Supplies the GUID of the VM. If this value is a non-empty string it\n        is passed to init as an environment variable.\n\n    DistributionName - Supplies the name of the distribution. If this value is a\n        non-empty string it is passed to init as an environment variable.\n\n    SharedMemoryRoot - Supplies the Windows OB path for virtiofs shared memory.\n        If this value is a non-empty string, it is passed to init as an\n        environment variable.\n\n    InstallPath - Supplies the Windows path for the location where the lifted\n        WSL package is installed. If this value is a non-empty string, it is\n        passed to init as an environment variable.\n\n    UserProfile - Supplies the Windows path for user profile of the VM owner.\n        If this value is a non-empty string, it is passed to init as an\n        environment variable.\n\n    DistroInitPid - Supplies the pid of the user distribution's init process.\n\nReturn Value:\n\n    None. This method does not return.\n\n--*/\n\ntry\n{\n    std::vector<std::string> Variables;\n    auto AddEnvironmentVariable = [&Variables](const char* Name, const char* Value) {\n        if ((Value) && (*Value != '\\0'))\n        {\n            Variables.emplace_back(std::format(\"{}={}\", Name, Value));\n        }\n    };\n\n    size_t TargetPathLength = strlen(Target);\n    auto AddTemporaryMount = [&](const char* Name, const char* Source, unsigned long MountFlags) {\n        std::string Path;\n        THROW_LAST_ERROR_IF(CreateTempDirectory(Target, Path) < 0);\n        THROW_LAST_ERROR_IF(mount(Source, Path.c_str(), nullptr, MountFlags, nullptr) < 0);\n        AddEnvironmentVariable(Name, Path.substr(TargetPathLength).data());\n    };\n\n    //\n    // Set the communication channel to expected file descriptor value.\n    //\n\n    if (SocketFd != LX_INIT_UTILITY_VM_INIT_SOCKET_FD)\n    {\n        THROW_LAST_ERROR_IF(TEMP_FAILURE_RETRY(dup2(SocketFd, LX_INIT_UTILITY_VM_INIT_SOCKET_FD)) < 0);\n\n        close(SocketFd);\n        SocketFd = LX_INIT_UTILITY_VM_INIT_SOCKET_FD;\n    }\n    else\n    {\n\n        //\n        // Remove the CLOEXEC flag since this fd is to be passed down to init.\n        //\n\n        THROW_LAST_ERROR_IF(SetCloseOnExec(SocketFd, false));\n    }\n\n    //\n    // Move the cross-distro shared mount to a temporary location. This mount\n    // will be moved by the distro init.\n    //\n\n    bool readOnly = false;\n    try\n    {\n        AddTemporaryMount(LX_WSL2_CROSS_DISTRO_ENV, CROSS_DISTRO_SHARE_PATH, (MS_MOVE | MS_REC));\n    }\n    catch (...)\n    {\n        //\n        // Creating the temporary mount can fail if:\n        // - The distro VHD was mounted read-only (because a fsck is needed)\n        // - The distro VHD is full\n        //\n        // Mount a writable overlay if that's the case so the distro can start.\n        //\n\n        LOG_WARNING(\"Detected read-only or full filesystem. Adding a tmpfs overlay\");\n\n        const std::string tmpfsTarget = std::format(\"{}-rw\", Target);\n        THROW_LAST_ERROR_IF(UtilMkdir(Target, 0755) < 0);\n\n        THROW_LAST_ERROR_IF(UtilMountOverlayFs(tmpfsTarget.c_str(), Target) < 0);\n        THROW_LAST_ERROR_IF(mount(tmpfsTarget.c_str(), Target, NULL, MS_BIND, NULL) < 0);\n\n        AddTemporaryMount(LX_WSL2_CROSS_DISTRO_ENV, CROSS_DISTRO_SHARE_PATH, (MS_MOVE | MS_REC));\n        readOnly = true;\n        AddEnvironmentVariable(LX_WSL2_DISTRO_READ_ONLY_ENV, \"1\");\n    }\n\n    //\n    // If GUI support is enabled, move the WSLg shared mount to a temporary\n    // location. This mount will be moved by the distro init.\n    //\n\n    if (EnableGuiApps)\n    {\n        AddTemporaryMount(LX_WSL2_SYSTEM_DISTRO_SHARE_ENV, WSLG_PATH, (MS_MOVE | MS_REC));\n    }\n\n    //\n    // Add other environment variables.\n    //\n\n    //\n    // Init needs to know its pid relative to the root pid namespace.\n    // Since the root namespace /proc is still mounted, it can be recovered by /proc/self.\n    //\n\n    auto pid = std::filesystem::read_symlink(PROCFS_PATH \"/self\");\n\n    AddEnvironmentVariable(LX_WSL_PID_ENV, pid.c_str());\n    AddEnvironmentVariable(LX_WSL2_VM_ID_ENV, VmId);\n    AddEnvironmentVariable(LX_WSL2_DISTRO_NAME_ENV, DistributionName);\n    AddEnvironmentVariable(LX_WSL2_SHARED_MEMORY_OB_DIRECTORY, SharedMemoryRoot);\n    AddEnvironmentVariable(LX_WSL2_INSTALL_PATH, InstallPath);\n    AddEnvironmentVariable(LX_WSL2_USER_PROFILE, UserProfile);\n    AddEnvironmentVariable(LX_WSL2_NETWORKING_MODE_ENV, std::to_string(static_cast<int>(Config.NetworkingMode)).c_str());\n\n    if (DistroInitPid.has_value())\n    {\n        AddEnvironmentVariable(LX_WSL2_DISTRO_INIT_PID, std::to_string(static_cast<int>(DistroInitPid.value())).c_str());\n    }\n\n    if (Config.EnableSafeMode)\n    {\n        AddEnvironmentVariable(LX_WSL2_SAFE_MODE, c_trueString);\n    }\n\n    //\n    // If GPU support is enabled, move the GPU share mounts to temporary\n    // mount points inside the distro. These will be moved by the distro init\n    // process, or unmounted if GPU support is disabled via /etc/wsl.conf.\n    //\n\n    if (Config.EnableGpuSupport)\n    {\n        std::string Lower = GPU_SHARE_LIB_PACKAGED;\n        if (Config.EnableInboxGpuLibs)\n        {\n            Lower += std::format(\":{}\", GPU_SHARE_LIB_INBOX);\n        }\n\n        THROW_LAST_ERROR_IF(UtilMountOverlayFs(GPU_SHARE_LIB, Lower.c_str(), (MS_NOATIME | MS_NOSUID | MS_NODEV), c_defaultRetryTimeout) < 0);\n\n        for (int ShareIndex = 0; ShareIndex < COUNT_OF(g_gpuShares); ShareIndex += 1)\n        {\n            auto SharePath = std::format(\"{}{}\", GPU_SHARE_PREFIX, g_gpuShares[ShareIndex].Name);\n            auto ShareVariable = std::format(\"{}{}\", LX_WSL2_GPU_SHARE_ENV, g_gpuShares[ShareIndex].Name);\n            AddTemporaryMount(ShareVariable.c_str(), SharePath.c_str(), MS_MOVE);\n        }\n    }\n\n    //\n    // If kernel modules are supported, move the mount to a temporary location.\n    // This mount will be moved by the distro init.\n    //\n\n    if (!Config.KernelModulesPath.empty())\n    {\n        AddTemporaryMount(LX_WSL2_KERNEL_MODULES_MOUNT_ENV, Config.KernelModulesPath.c_str(), (MS_MOVE | MS_REC));\n        AddEnvironmentVariable(LX_WSL2_KERNEL_MODULES_PATH_ENV, Config.KernelModulesPath.c_str());\n    }\n\n    //\n    // Bind mount the init daemon into the distro namespace.\n    //\n\n    auto Path = std::format(\"{}{}\", Target, LX_INIT_PATH);\n    THROW_LAST_ERROR_IF(MountInit(Path.c_str()) < 0);\n\n    if (readOnly)\n    {\n        //\n        // If a rw overlay was added, mark it as read-only.\n        //\n\n        THROW_LAST_ERROR_IF(mount(nullptr, Target, nullptr, MS_REMOUNT | MS_RDONLY, nullptr) < 0);\n    }\n\n    //\n    // Change the root of the calling process to the distro mountpoint.\n    //\n\n    THROW_LAST_ERROR_IF(Chroot(Target) < 0);\n\n    //\n    // Exec the init daemon.\n    //\n\n    std::vector<char*> Environment;\n    for (auto& e : Variables)\n    {\n        Environment.emplace_back(e.data());\n    }\n\n    assert(Environment.size() == Variables.size());\n\n    Environment.push_back(nullptr);\n\n    execle(LX_INIT_PATH, LX_INIT_PATH, nullptr, Environment.data());\n    LOG_ERROR(\"execle({}) failed {}\", LX_INIT_PATH, errno);\n    _exit(1);\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    _exit(1);\n}\n\nvoid LaunchSystemDistro(\n    int SocketFd,\n    const char* Target,\n    const VmConfiguration& Config,\n    const char* VmId,\n    const char* DistributionName,\n    const char* SharedMemoryRoot,\n    const char* InstallPath,\n    const char* UserProfile,\n    pid_t DistroInitPid)\n\n/*++\n\nRoutine Description:\n\n    This routine launches the system distro.\n\nArguments:\n\n    SocketFd - Supplies a file descriptor to communicate with the init daemon.\n        This routine takes ownership of this file descriptor.\n\n    Target - Supplies the location where the distro filesystem is mounted.\n\n    VmConfiguration - Supplies the VM configuration.\n\n    VmId - Supplies the GUID of the VM. If this value is a non-empty string it\n        is passed to init as an environment variable.\n\n    DistributionName - Supplies the name of the distribution. If this value is a\n        non-empty string it is passed to init as an environment variable.\n\n    SharedMemoryRoot - Supplies the Windows OB path for virtiofs shared memory.\n        If this value is a non-empty string, it is passed to init as an\n        environment variable.\n\n    InstallPath - Supplies the Windows path for the location where the lifted\n        WSL package is installed. If this value is a non-empty string, it is\n        passed to init as an environment variable.\n\n    UserProfile - Supplies the Windows path for user profile of the VM owner.\n        If this value is a non-empty string, it is passed to init as an\n        environment variable.\n\n    DistroInitPid - Supplies the pid of the user distribution's init process.\n\nReturn Value:\n\n    None. This method does not return.\n\n--*/\n\ntry\n{\n    //\n    // Create a writable layer on top of the read-only vhd.\n    //\n\n    THROW_LAST_ERROR_IF(UtilMountOverlayFs(Target, SYSTEM_DISTRO_VHD_PATH) < 0);\n\n    //\n    // Launch the init daemon, this method does not return.\n    //\n\n    LaunchInit(SocketFd, Target, true, Config, VmId, DistributionName, SharedMemoryRoot, InstallPath, UserProfile, DistroInitPid);\n    _exit(1);\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    _exit(1);\n}\n\nstd::set<pid_t> ListInitChildProcesses()\n{\n    std::set<pid_t> children;\n\n    auto content = wsl::shared::string::ReadFile<char>(\"/proc/self/task/1/children\");\n\n    for (const auto& e : wsl::shared::string::Split<char>(content, ' '))\n    {\n        children.insert(std::stoul(e, nullptr, 10));\n    }\n\n    return children;\n}\n\nstd::vector<unsigned int> ListScsiDisks()\n{\n    std::vector<unsigned int> disks;\n\n    for (const auto& e : std::filesystem::directory_iterator(SCSI_DEVICE_PATH))\n    {\n        auto filename = e.path().filename().string();\n        if (filename.find(SCSI_DEVICE_NAME_PREFIX) == 0)\n        {\n            try\n            {\n                disks.emplace_back(std::stoul(filename.substr(strlen(SCSI_DEVICE_NAME_PREFIX))));\n            }\n            CATCH_LOG();\n        }\n    }\n\n    return disks;\n}\n\nvoid LogException(const char* Message, const char* Description) noexcept\n\n/*++\n\nRoutine Description:\n\n    Callback to log exception information.\n\nArguments:\n\n    Message - Supplies the message to log.\n\n    Description - Supplies the exception description.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    if (Message)\n    {\n        dprintf(g_LogFd, \"<3>WSL (%d) ERROR: %s %s\", getpid(), Message, Description);\n    }\n    else\n    {\n        dprintf(g_LogFd, \"<3>WSL (%d) ERROR: %s\", getpid(), Description);\n    }\n}\n\nint MountDevice(LX_MINI_INIT_MOUNT_DEVICE_TYPE DeviceType, unsigned int DeviceId, const char* Target, const char* FsType, unsigned int Flags, const char* Options)\n\n/*++\n\nRoutine Description:\n\n    This routine mounts the specified device.\n\nArguments:\n\n    DeviceType - Supplies the type of device to mount.\n\n    DeviceId - Supplies identifier for the SCSI or pmem device to mount.\n\n    Target - Supplies the target of the mount.\n\n    FsType - Supplies the filesystem type.\n\n    Flags - Supplies flags for the operation.\n\n    Options - Supplies mount options.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\ntry\n{\n    //\n    // Build the /dev path of the device.\n    //\n\n    std::string DevicePath;\n    switch (DeviceType)\n    {\n    case LxMiniInitMountDeviceTypeLun:\n        DevicePath = GetLunDevicePath(DeviceId);\n        break;\n\n    case LxMiniInitMountDeviceTypePmem:\n        DevicePath = std::format(\"{}/pmem{}\", DEVFS_PATH, DeviceId);\n        break;\n\n    default:\n        LOG_ERROR(\"Unexpected DeviceType {}\", DeviceType);\n        return -EINVAL;\n    }\n\n    //\n    // Mount to a temporary location if overlayfs was requested; otherwise, mount\n    // the device directly on the target.\n    //\n\n    std::string MountPoint;\n    if (Flags & LxMiniInitMessageFlagCreateOverlayFs)\n    {\n        if (CreateTempDirectory(Target, MountPoint) < 0)\n        {\n            return -1;\n        }\n    }\n    else\n    {\n        MountPoint += Target;\n    }\n\n    //\n    // Perform the mount.\n    //\n\n    const unsigned long MountFlags = (Flags & LxMiniInitMessageFlagMountReadOnly) ? MS_RDONLY : 0;\n    if (UtilMount(DevicePath.c_str(), MountPoint.c_str(), FsType, MountFlags, Options, c_defaultRetryTimeout) < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Create an overlayfs mount for a read/write layer if requested.\n    //\n\n    if (Flags & LxMiniInitMessageFlagCreateOverlayFs)\n    {\n        if (UtilMountOverlayFs(Target, MountPoint.c_str()) < 0)\n        {\n            return -1;\n        }\n    }\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nint MountPlan9(const char* Name, const char* Target, bool ReadOnly, std::optional<int> BufferSize)\n\n/*++\n\nRoutine Description:\n\n    This routine will mount a 9p share.\n\nArguments:\n\n    Name - Supplies the aname of the 9p share to mount.\n\n    Target - Supplies the mount target.\n\n    ReadOnly - Supplies a boolean specifying if the share should be mounted as read-only.\n\n    BufferSize - Optionally supplies a buffer size to use for the hvsocket send / receive buffers and 9p msize.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    int Size = BufferSize.value_or(LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE);\n    wil::unique_fd Fd{UtilConnectVsock(LX_INIT_UTILITY_VM_PLAN9_PORT, true, Size)};\n    if (!Fd)\n    {\n        return -1;\n    }\n\n    unsigned long Flags = MS_NOATIME | MS_NOSUID | MS_NODEV;\n    auto Options = std::format(\"msize={},trans=fd,rfdno={},wfdno={},cache=mmap,aname={}\", Size, Fd.get(), Fd.get(), Name);\n    if (ReadOnly)\n    {\n        WI_SetFlag(Flags, MS_RDONLY);\n        Options += \";fmask=222;dmask=222\";\n    }\n\n    return UtilMount(Name, Target, PLAN9_FS_TYPE, Flags, Options.c_str(), c_defaultRetryTimeout);\n}\nCATCH_RETURN_ERRNO()\n\nint MountSystemDistro(LX_MINI_INIT_MOUNT_DEVICE_TYPE DeviceType, unsigned int DeviceId)\n\n/*++\n\nRoutine Description:\n\n    This routine mounts the system distro as read-only, creates a writable\n    tmpfs layer using overlayfs, and chroots to the mount point.\n\nArguments:\n\n    DeviceType - Supplies the type of device to mount.\n\n    DeviceId - Supplies identifier for the SCSI or pmem device to mount.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\n{\n    //\n    // Mount the system distro device as read-only.\n    //\n\n    const unsigned int Flags = LxMiniInitMessageFlagMountReadOnly;\n    auto* Options = (DeviceType == LxMiniInitMountDeviceTypePmem) ? \"dax\" : nullptr;\n    if (MountDevice(DeviceType, DeviceId, SYSTEM_DISTRO_VHD_PATH, \"ext4\", Flags, Options) < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Create a read / write overlay layer.\n    //\n\n    if (UtilMountOverlayFs(SYSTEM_DISTRO_PATH, SYSTEM_DISTRO_VHD_PATH) < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Move the devtmpfs, procfs, sysfs and system distro vhd mounts before chrooting.\n    //\n\n    for (const auto* Source : {DEVFS_PATH, PROCFS_PATH, SYSFS_PATH, SYSTEM_DISTRO_VHD_PATH})\n    {\n        auto Target = std::format(\"{}{}\", SYSTEM_DISTRO_PATH, Source);\n        if (UtilMount(Source, Target.c_str(), nullptr, (MS_MOVE | MS_REC), nullptr) < 0)\n        {\n            return -1;\n        }\n    }\n\n    //\n    // Create a bind mount of WSL init.\n    //\n\n    if (MountInit(SYSTEM_DISTRO_PATH LX_INIT_PATH) < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Chroot to system distro mount point.\n    //\n    // N.B. This allows running binaries present in the system distro without having to chroot.\n    //\n\n    return Chroot(SYSTEM_DISTRO_PATH);\n}\n\nstd::map<unsigned long, std::string> ListDiskPartitions(const std::string& DeviceName, std::optional<unsigned long> SearchForIndex)\n\n/*++\n\nRoutine Description:\n\n    This routine returns the list of partitions in a block device.\n\nArguments:\n\n    DeviceName - Supplies the block device name.\n\n    SearchForIndex - Supplies a partition index to search for.\n\nReturn Value:\n\n    A map from partition index to device name.\n\n--*/\n\n{\n    std::string DevicePath = std::format(\"/sys/block/{}\", DeviceName);\n\n    return wsl::shared::retry::RetryWithTimeout<std::map<unsigned long, std::string>>(\n        [&]() {\n            wil::unique_dir Dir{opendir(DevicePath.c_str())};\n            THROW_LAST_ERROR_IF(!Dir);\n\n            std::map<unsigned long, std::string> partitions;\n\n            for (auto Entry = readdir64(Dir.get()); Entry != nullptr; Entry = readdir64(Dir.get()))\n            {\n                if ((Entry->d_type != DT_DIR) || (strstr(Entry->d_name, DeviceName.c_str()) != Entry->d_name))\n                {\n                    continue;\n                }\n\n                partitions.emplace(GetDiskPartitionIndex(DevicePath.c_str(), Entry->d_name), Entry->d_name);\n            }\n\n            THROW_ERRNO_IF(ENOENT, SearchForIndex.has_value() && partitions.find(SearchForIndex.value()) == partitions.end());\n\n            return partitions;\n        },\n        c_defaultRetryPeriod,\n        c_defaultRetryTimeout,\n        []() {\n            auto err = wil::ResultFromCaughtException();\n            return err == ENOENT || err == ENXIO;\n        });\n}\n\nint MountDiskPartition(const char* DevicePath, const char* Type, const char* Target, unsigned long Flags, const char* Options, size_t PartitionIndex, PLX_MINI_MOUNT_STEP Step)\n\n/*++\n\nRoutine Description:\n\n    Mount a disk partition with a timeout.\n\nArguments:\n\n    DevicePath - Path of the device to mount.\n\n    Type - The filesystem to use.\n\n    Target - The mount target.\n\n    Flags - The mount flags.\n\n    Options - The mount options.\n\n    PartitionIndex - The partition to mount.\n\n    Step - Pointer to update the current mount step.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\ntry\n{\n    *Step = LxMiniInitMountStepFindPartition;\n    if (!wsl::shared::string::StartsWith(DevicePath, DEVFS_PATH \"/\"))\n    {\n        LOG_ERROR(\"unexpected device path {}\", DevicePath);\n        return -1;\n    }\n\n    auto* DeviceName = &DevicePath[sizeof(DEVFS_PATH)];\n\n    //\n    // Find the partition on the specified device.\n    //\n    // N.B. A retry is needed because there is a delay between when a device is\n    //      hot-added, and when the device is available in the guest.\n    //\n\n    auto partitions = ListDiskPartitions(DeviceName, PartitionIndex);\n\n    auto partition = partitions.find(PartitionIndex);\n\n    THROW_ERRNO_IF(ENOENT, partition == partitions.end());\n\n    std::string partitionPath = std::format(\"/dev/{}\", partition->second);\n    LOG_INFO(\"Mapped partition {} from device {} to {}\", PartitionIndex, DeviceName, partitionPath);\n\n    //\n    // Detect the filesystem type.\n    //\n\n    *Step = LxMiniInitMountStepDetectFilesystem;\n    std::string DetectedFilesystem;\n    if (Type == nullptr)\n    {\n        if (DetectFilesystem(partitionPath.c_str(), DetectedFilesystem) < 0)\n        {\n            return -1;\n        }\n\n        Type = DetectedFilesystem.c_str();\n    }\n\n    *Step = LxMiniInitMountStepMount;\n    return UtilMount(partitionPath.c_str(), Target, Type, Flags, Options, c_defaultRetryTimeout);\n}\nCATCH_RETURN()\n\nint MountInit(const char* Target)\n\n/*++\n\nRoutine Description:\n\n    This routine create a read-only bind mount of the init daemon at the specified target.\n\nArguments:\n\n    Target - Supplies the target for the mount.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\ntry\n{\n    wil::unique_fd InitFd{open(Target, (O_CREAT | O_WRONLY | O_TRUNC), 0755)};\n    THROW_LAST_ERROR_IF(!InitFd);\n\n    THROW_LAST_ERROR_IF(mount(LX_INIT_PATH, Target, nullptr, (MS_RDONLY | MS_BIND), nullptr) < 0);\n\n    THROW_LAST_ERROR_IF(mount(nullptr, Target, nullptr, (MS_RDONLY | MS_REMOUNT | MS_BIND), nullptr) < 0);\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nstd::string GetMountTarget(const char* Name)\n\n/*++\n\nRoutine Description:\n\n    Generate the path to a mount target.\n\nArguments:\n\n    Name - Supplies the mount name.\n\n    Target - The buffer receiving the mountpoint target.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\n{\n    return std::format(\"{}/{}\", CROSS_DISTRO_SHARE_PATH, Name);\n}\n\nvoid ProcessLaunchInitMessage(\n    const LX_MINI_INIT_MESSAGE* Message,\n    gsl::span<gsl::byte> Buffer,\n    wsl::shared::SocketChannel&& Channel,\n    wil::unique_fd&& SystemDistroSocketFd,\n    const VmConfiguration& Config)\n{\n    //\n    // Send a message back to the service that contains the pid of the child process.\n    // If the distribution terminates unexpectedly, this pid will be sent to the service so it knows that the instance\n    // has terminated.\n    //\n\n    LX_MINI_CREATE_INSTANCE_STEP Step = LxInitCreateInstanceStepMountDisk;\n\n    auto ReportStatus = [&Channel, &Step](auto result) {\n        LX_MINI_INIT_CREATE_INSTANCE_RESULT message{};\n        message.Header.MessageType = LxMiniInitMessageCreateInstanceResult;\n        message.Header.MessageSize = sizeof(message);\n        message.FailureStep = Step;\n        message.Result = result;\n\n        Channel.SendMessage(message);\n    };\n\n    try\n    {\n        auto* FsType = wsl::shared::string::FromSpan(Buffer, Message->FsTypeOffset);\n        auto* MountOptions = wsl::shared::string::FromSpan(Buffer, Message->MountOptionsOffset);\n\n        //\n        // Mount the device.\n        //\n\n        THROW_LAST_ERROR_IF(MountDevice(Message->MountDeviceType, Message->DeviceId, DISTRO_PATH, FsType, Message->Flags, MountOptions) < 0);\n\n        //\n        // Allow /etc/wsl.conf in the user distro to opt-out of GUI support.\n        //\n        // N.B. A connection for the system distro must established even if the distro opts out\n        //      of GUI app support because WslService is waiting to accept a connection.\n        //\n\n        bool enableGuiApps = Config.EnableGuiApps;\n        if (Message->Flags & LxMiniInitMessageFlagLaunchSystemDistro && Config.EnableGuiApps)\n        {\n            Step = LxInitCreateInstanceStepLaunchSystemDistro;\n            wil::unique_file File{fopen(DISTRO_PATH ETC_PATH \"/wsl.conf\", \"r\")};\n            if (File)\n            {\n                std::vector<ConfigKey> ConfigKeys = {ConfigKey(\"general.guiApplications\", enableGuiApps)};\n                ParseConfigFile(ConfigKeys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(CONFIG_FILE));\n                File.reset();\n            }\n\n            //\n            // If the distro did not opt-out of GUI applications, continue launching the system distro.\n            //\n\n            if (enableGuiApps)\n            {\n                //\n                // Create a tmpfs mount for a shared folder between user and system distro.\n                //\n\n                THROW_LAST_ERROR_IF(UtilMount(nullptr, WSLG_PATH, \"tmpfs\", 0, nullptr) < 0);\n\n                THROW_LAST_ERROR_IF(mount(nullptr, WSLG_PATH, nullptr, MS_SHARED, nullptr) < 0);\n\n                //\n                // Create a directory to store x11 sockets.\n                //\n                // N.B. This needs to be created early so a bind mount into the shared WSLg location\n                //      can be created on top of the hard-coded location expected by x11 clients.\n                //\n\n                THROW_LAST_ERROR_IF(UtilMkdir(WSLG_PATH \"/\" X11_SOCKET_NAME, 0777) < 0);\n\n                //\n                // Create a read-only bind mount of the user distro into the shared WSLg folder so fonts and icons can be accessed.\n                //\n\n                THROW_LAST_ERROR_IF(UtilMount(DISTRO_PATH, WSLG_PATH DISTRO_PATH, nullptr, (MS_BIND | MS_RDONLY), nullptr) < 0);\n\n                THROW_LAST_ERROR_IF(UtilMount(nullptr, WSLG_PATH DISTRO_PATH, nullptr, (MS_RDONLY | MS_REMOUNT | MS_BIND), nullptr) < 0);\n\n                //\n                // Create a child process in a new mount, pid, and UTS namespace (with a shared IPC namespace).\n                // This child process will become the user distro init daemon.\n                //\n\n                auto ChildPid = CLONE(CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD);\n                THROW_LAST_ERROR_IF(ChildPid < 0);\n\n                if (ChildPid > 0)\n                {\n                    //\n                    // Close the socket for the user distro and launch the system\n                    // distro. This method does not return.\n                    //\n\n                    Channel.Close();\n\n                    LaunchSystemDistro(\n                        SystemDistroSocketFd.get(),\n                        SYSTEM_DISTRO_PATH,\n                        Config,\n                        wsl::shared::string::FromSpan(Buffer, Message->VmIdOffset),\n                        wsl::shared::string::FromSpan(Buffer, Message->DistributionNameOffset),\n                        wsl::shared::string::FromSpan(Buffer, Message->SharedMemoryRootOffset),\n                        wsl::shared::string::FromSpan(Buffer, Message->InstallPathOffset),\n                        wsl::shared::string::FromSpan(Buffer, Message->UserProfileOffset),\n                        ChildPid);\n                }\n            }\n\n            SystemDistroSocketFd.reset();\n        }\n\n        //\n        // Launch the distro init daemon, this method does not return.\n        //\n\n        Step = LxInitCreateInstanceStepLaunchInit;\n        LaunchInit(\n            Channel.Socket(),\n            DISTRO_PATH,\n            enableGuiApps,\n            Config,\n            wsl::shared::string::FromSpan(Buffer, Message->VmIdOffset),\n            wsl::shared::string::FromSpan(Buffer, Message->DistributionNameOffset),\n            nullptr,\n            wsl::shared::string::FromSpan(Buffer, Message->InstallPathOffset),\n            wsl::shared::string::FromSpan(Buffer, Message->UserProfileOffset));\n    }\n    catch (...)\n    {\n        ReportStatus(wil::ResultFromCaughtException());\n        _exit(1);\n    }\n}\n\nvoid PostProcessImportedDistribution(wsl::shared::MessageWriter<LX_MINI_INIT_IMPORT_RESULT>& Message, const char* ExtractedPath)\n{\n    //\n    // Save the current working directory as a file descriptor so it can be restored.\n    //\n\n    wil::unique_fd cwdFd{open(\".\", O_RDONLY | O_DIRECTORY)};\n    THROW_LAST_ERROR_IF(!cwdFd);\n\n    auto restoreCwd = wil::scope_exit([&cwdFd]() {\n        THROW_LAST_ERROR_IF(fchdir(cwdFd.get()) < 0);\n        THROW_LAST_ERROR_IF(chroot(\".\") < 0);\n    });\n\n    //\n    // Chroot to the extracted path to validate distro contents.\n    //\n    // N.B. The chroot is needed because the distro may contain absolute symlinks (for example, /bin/sh may symlink to /bin/toolbox).\n    //\n\n    THROW_LAST_ERROR_IF(chdir(ExtractedPath) < 0);\n    THROW_LAST_ERROR_IF(chroot(\".\") < 0);\n\n    Message->ValidDistribution = false;\n\n    for (auto* path : {\"/etc\", \"/bin/sh\"})\n    {\n        if (access(path, F_OK) >= 0)\n        {\n            Message->ValidDistribution = true;\n        }\n    }\n\n    if (!Message->ValidDistribution)\n    {\n        return;\n    }\n\n    auto [flavor, version] = UtilReadFlavorAndVersion(\"/etc/os-release\");\n\n    if (flavor.has_value())\n    {\n        Message.WriteString(Message->FlavorIndex, flavor.value());\n    }\n\n    if (version.has_value())\n    {\n        Message.WriteString(Message->VersionIndex, version.value());\n    }\n\n    std::string defaultName{};\n    std::string shortcutIconPath;\n    std::string terminalProfileTemplatePath;\n    Message->GenerateTerminalProfile = true;\n    Message->GenerateShortcut = true;\n\n    std::vector<ConfigKey> keys = {\n        ConfigKey(\"shortcut.icon\", shortcutIconPath),\n        ConfigKey(\"shortcut.enabled\", Message->GenerateShortcut),\n        ConfigKey(\"oobe.defaultName\", defaultName),\n        ConfigKey(\"windowsterminal.profileTemplate\", terminalProfileTemplatePath),\n        ConfigKey(\"windowsterminal.enabled\", Message->GenerateTerminalProfile)};\n\n    {\n        wil::unique_file File{fopen(WSL_DISTRIBUTION_CONF, \"r\")};\n        ParseConfigFile(keys, File.get(), CFG_SKIP_UNKNOWN_VALUES, STRING_TO_WSTRING(WSL_DISTRIBUTION_CONF));\n    }\n\n    if (!defaultName.empty())\n    {\n        Message.WriteString(Message->DefaultNameIndex, defaultName);\n    }\n\n    try\n    {\n        if (!shortcutIconPath.empty())\n        {\n            // Prevent escaping the distribution install path.\n            if (shortcutIconPath.find(\"..\") != std::string::npos)\n            {\n                LOG_ERROR(\"Invalid format for shortcut.icon: {}\", shortcutIconPath.c_str());\n                THROW_ERRNO(EINVAL);\n            }\n\n            auto iconBuffer = UtilReadFileRaw(shortcutIconPath.c_str(), 1024 * 1024);\n            gsl::copy(\n                gsl::as_writable_bytes(gsl::make_span(iconBuffer)),\n                Message.InsertBuffer(Message->ShortcutIconIndex, iconBuffer.size(), Message->ShortcutIconSize));\n        }\n    }\n    CATCH_LOG();\n\n    try\n    {\n        if (Message->GenerateTerminalProfile && !terminalProfileTemplatePath.empty())\n        {\n            // Prevent escaping the distribution install path.\n            if (terminalProfileTemplatePath.find(\"..\") != std::string::npos)\n            {\n                LOG_ERROR(\"Invalid format for windows-terminal.profile_template: {}\", terminalProfileTemplatePath.c_str());\n                THROW_ERRNO(EINVAL);\n            }\n\n            auto content = UtilReadFileRaw(terminalProfileTemplatePath.c_str(), 1024 * 1024);\n            gsl::copy(\n                gsl::as_writable_bytes(gsl::make_span(content)),\n                Message.InsertBuffer(Message->TerminalProfileIndex, content.size(), Message->TerminalProfileSize));\n        }\n    }\n    CATCH_LOG();\n}\n\nvoid ProcessImportExportMessage(gsl::span<gsl::byte> Buffer, wsl::shared::SocketChannel&& Channel)\n{\n    const LX_MINI_INIT_MESSAGE* Message{};\n    sockaddr_vm ListenAddress{};\n    wil::unique_fd ListenSocket;\n    int Result = -1;\n\n    {\n        auto ReportStatus = wil::scope_exit([&Channel, &Result, &ListenAddress]() {\n            LX_MINI_INIT_CREATE_INSTANCE_RESULT message{};\n            message.Header.MessageType = LxMiniInitMessageCreateInstanceResult;\n            message.Header.MessageSize = sizeof(message);\n            message.FailureStep = LxInitCreateInstanceStepMountDisk;\n            message.Result = Result;\n            message.ConnectPort = ListenAddress.svm_port;\n            Channel.SendMessage(message);\n        });\n\n        try\n        {\n            Message = gslhelpers::try_get_struct<LX_MINI_INIT_MESSAGE>(Buffer);\n            THROW_ERRNO_IF(EINVAL, !Message);\n\n            ListenSocket = UtilListenVsockAnyPort(&ListenAddress, 2, true);\n            THROW_LAST_ERROR_IF(!ListenSocket);\n\n            if (Message->Header.MessageType == LxMiniInitMessageImport)\n            {\n                THROW_LAST_ERROR_IF(FormatDevice(Message->DeviceId) < 0);\n            }\n\n            auto* FsType = wsl::shared::string::FromSpan(Buffer, Message->FsTypeOffset);\n            auto* MountOptions = wsl::shared::string::FromSpan(Buffer, Message->MountOptionsOffset);\n            THROW_LAST_ERROR_IF(MountDevice(Message->MountDeviceType, Message->DeviceId, DISTRO_PATH, FsType, Message->Flags, MountOptions) < 0);\n\n            Result = 0;\n        }\n        catch (...)\n        {\n            Result = wil::ResultFromCaughtException();\n        }\n    }\n\n    if (Result < 0)\n    {\n        LOG_ERROR(\"ProcessImportExportMessage failed, {}\", errno);\n        return;\n    }\n\n    Result = -1;\n    auto ReportStatus = wil::scope_exit([&Channel, &Result, MessageType = Message->Header.MessageType]() {\n        if (MessageType == LxMiniInitMessageExport)\n        {\n            if (UtilWriteBuffer(Channel.Socket(), &Result, sizeof(Result)) < 0)\n            {\n                LOG_ERROR(\"response write failed {}\", errno);\n            }\n        }\n        else\n        {\n            wsl::shared::MessageWriter<LX_MINI_INIT_IMPORT_RESULT> message;\n            message->Result = Result;\n            if (Result == 0)\n            {\n                PostProcessImportedDistribution(message, DISTRO_PATH);\n            }\n\n            Channel.SendMessage<LX_MINI_INIT_IMPORT_RESULT>(message.Span());\n        }\n    });\n\n    wil::unique_fd DataSocket{UtilAcceptVsock(ListenSocket.get(), ListenAddress, SESSION_LEADER_ACCEPT_TIMEOUT_MS)};\n    THROW_LAST_ERROR_IF(!DataSocket);\n\n    wil::unique_fd ErrorSocket{UtilAcceptVsock(ListenSocket.get(), ListenAddress, SESSION_LEADER_ACCEPT_TIMEOUT_MS)};\n    THROW_LAST_ERROR_IF(!ErrorSocket);\n\n    switch (Message->Header.MessageType)\n    {\n    case LxMiniInitMessageImport:\n        Result = ImportFromSocket(DISTRO_PATH, DataSocket.get(), ErrorSocket.get(), Message->Flags);\n        break;\n\n    case LxMiniInitMessageExport:\n        Result = ExportToSocket(DISTRO_PATH, DataSocket.get(), ErrorSocket.get(), Message->Flags);\n        break;\n\n    case LxMiniInitMessageImportInplace:\n        Result = 0;\n        break;\n\n    default:\n        LOG_ERROR(\"Unexpected message type {}\", Message->Header.MessageType);\n    }\n}\n\nint ProcessMountFolderMessage(wsl::shared::SocketChannel& Channel, gsl::span<gsl::byte> Buffer)\n\n/*++\n\nRoutine Description:\n\n    Mount a filesystem as requested by the mount message\n\nArguments:\n\n    Buffer - Supplies the mount message.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\n{\n    auto* Message = gslhelpers::try_get_struct<LX_MINI_INIT_MOUNT_FOLDER_MESSAGE>(Buffer);\n    if (!Message)\n    {\n        LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n        return -1;\n    }\n\n    const auto* Target = wsl::shared::string::FromSpan(Buffer, Message->PathIndex);\n    const auto* Name = wsl::shared::string::FromSpan(Buffer, Message->NameIndex);\n\n    if (Target == nullptr || Name == nullptr)\n    {\n        LOG_ERROR(\"Invalid name or path index in LX_MINI_INIT_MOUNT_FOLDER_MESSAGE\");\n        return -1;\n    }\n\n    int Result = MountPlan9(Name, Target, Message->ReadOnly);\n    Channel.SendResultMessage<int32_t>(Result);\n    return 0;\n}\n\nint ProcessMountMessage(gsl::span<gsl::byte> Buffer)\n\n/*++\n\nRoutine Description:\n\n    Mount a filesystem as requested by the mount message\n\nArguments:\n\n    Buffer - Supplies the mount message.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\n{\n    wil::unique_fd SocketFd{UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, true)};\n    if (!SocketFd)\n    {\n        return -1;\n    }\n\n    const int ChildPid = UtilCreateChildProcess(\n        \"DiskMount\", [Buffer, Channel = wsl::shared::SocketChannel{std::move(SocketFd), \"MountResult\"}]() mutable {\n            // Set up a scope exit variable to report mount status.\n            int Result = -1;\n            LX_MINI_MOUNT_STEP Step = LxMiniInitMountStepFindDevice;\n            auto ReportStatus = wil::scope_exit([&Channel, &Result, &Step]() { ReportMountStatus(Channel, Result, Step); });\n\n            auto* Header = gslhelpers::try_get_struct<MESSAGE_HEADER>(Buffer);\n            if (!Header)\n            {\n                LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n                return;\n            }\n\n            std::string Device;\n            std::string DetectedFilesystem;\n            std::string Target;\n            if (Header->MessageType == LxMiniInitMessageMount)\n            {\n                auto* Message = gslhelpers::try_get_struct<LX_MINI_INIT_MOUNT_MESSAGE>(Buffer);\n                if (!Message)\n                {\n                    LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n                    return;\n                }\n\n                Device = GetLunDevicePath(Message->ScsiLun);\n\n                //\n                // Construct the target of the mount.\n                //\n\n                Target = GetMountTarget(wsl::shared::string::FromSpan(Buffer, Message->TargetNameOffset));\n\n                //\n                // Determine the type of mount. If no type was specified, detect it with blkid.\n                //\n\n                const auto* Type = wsl::shared::string::FromSpan(Buffer, Message->TypeOffset);\n                if (*Type == '\\0')\n                {\n                    Type = nullptr;\n                }\n\n                //\n                // Parse the mount flags.\n                //\n\n                auto* MountOptions = wsl::shared::string::FromSpan(Buffer, Message->OptionsOffset);\n                auto ParsedOptions = mountutil::MountParseFlags(MountOptions == nullptr ? \"\" : MountOptions);\n\n                //\n                // Perform the mount.\n                //\n\n                if (Message->PartitionIndex == 0)\n                {\n                    Step = LxMiniInitMountStepDetectFilesystem;\n                    if (Type == nullptr)\n                    {\n                        Result = DetectFilesystem(Device.c_str(), DetectedFilesystem);\n                        if (Result < 0)\n                        {\n                            return;\n                        }\n\n                        Type = DetectedFilesystem.c_str();\n                    }\n\n                    Step = LxMiniInitMountStepMount;\n                    Result = UtilMount(\n                        Device.c_str(), Target.c_str(), Type, ParsedOptions.MountFlags, ParsedOptions.StringOptions.c_str(), c_defaultRetryTimeout);\n                }\n                else\n                {\n                    Result = MountDiskPartition(\n                        Device.c_str(),\n                        Type,\n                        Target.c_str(),\n                        ParsedOptions.MountFlags,\n                        ParsedOptions.StringOptions.c_str(),\n                        Message->PartitionIndex,\n                        &Step);\n                }\n            }\n            else if (Header->MessageType == LxMiniInitMessageUnmount)\n            {\n                auto* Message = gslhelpers::try_get_struct<LX_MINI_INIT_UNMOUNT_MESSAGE>(Buffer);\n                if (!Message)\n                {\n                    LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n                    return;\n                }\n\n                Target = GetMountTarget(Message->Buffer);\n\n                Step = LxMiniInitMountStepUnmount;\n                Result = umount(Target.c_str());\n                if (Result < 0)\n                {\n                    Result = -errno;\n                    LOG_ERROR(\"umount({}) failed, {}\", Target.c_str(), errno);\n                    return;\n                }\n\n                Step = LxMiniInitMountStepRmDir;\n                Result = rmdir(Target.c_str());\n                if (Result < 0)\n                {\n                    Result = -errno;\n                    LOG_ERROR(\"rmdir({}) failed, {}\", Target.c_str(), errno);\n                }\n            }\n            else\n            {\n                assert(Header->MessageType == LxMiniInitMessageDetach);\n                auto* Message = gslhelpers::try_get_struct<LX_MINI_INIT_DETACH_MESSAGE>(Buffer);\n                if (!Message)\n                {\n                    LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n                    return;\n                }\n\n                Result = DetachScsiDisk(Message->ScsiLun);\n            }\n        });\n\n    return (ChildPid < 0) ? -1 : 0;\n}\n\nint ReportMountStatus(wsl::shared::SocketChannel& Channel, int Result, LX_MINI_MOUNT_STEP Step)\n\n/*++\n\nRoutine Description:\n\n    Report the result of a mount / unmount operation to via an hvsocket.\n\nArguments:\n\n    Channel - Supplies the socket channel.\n\n    Result - Supplies the operation result code.\n\n    Step - Supplies the step at which the mount operation failed, if any.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\ntry\n{\n    LX_MINI_INIT_MOUNT_RESULT_MESSAGE Message;\n    Message.Header.MessageSize = sizeof(Message);\n    Message.Header.MessageType = LxMiniInitMessageMountStatus;\n    Message.Result = Result;\n    Message.FailureStep = Step;\n\n    Channel.SendMessage(Message);\n\n    return 0;\n}\nCATCH_RETURN_ERRNO();\n\nint ProcessWaitForPmemDeviceMessage(PLX_MINI_INIT_WAIT_FOR_PMEM_DEVICE_MESSAGE Message)\n\n/*++\n\nRoutine Description:\n\n    This routine processes a message that waits for a pmem device to appear under /dev.\n    The actual waiting is performed asynchronously.\n\nArguments:\n\n    Message - The wait for pmem device message\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\n{\n    wsl::shared::SocketChannel Channel{wil::unique_fd{UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, true)}, \"WaitForPmem\"};\n    if (Channel.Socket() < 0)\n    {\n        return -1;\n    }\n\n    const int ChildPid = UtilCreateChildProcess(\"PMemDeviceWait\", [&Channel, PmemId = Message->PmemId]() {\n        int Result = -1;\n        auto ReportStatus = wil::scope_exit([&Channel, &Result]() { Channel.SendResultMessage<int32_t>(Result); });\n\n        //\n        // Construct the device path.\n        //\n\n        std::string DevicePath = std::format(\"{}/pmem{}\", DEVFS_PATH, PmemId);\n\n        //\n        // Poll for the device to appear. Ideally we'd replace this with something\n        // like libudev so we can be notified when devices appear.\n        //\n\n        struct stat Buffer;\n        wsl::shared::retry::RetryWithTimeout<void>(\n            [&]() { THROW_LAST_ERROR_IF(stat(DevicePath.c_str(), &Buffer) < 0); },\n            c_defaultRetryPeriod,\n            c_defaultRetryTimeout,\n            [&]() {\n                Result = -wil::ResultFromCaughtException();\n                return Result == -ENOENT;\n            });\n\n        Result = 0;\n    });\n\n    if (ChildPid < 0)\n    {\n        Channel.SendResultMessage<int32_t>(errno);\n        return -1;\n    }\n\n    return 0;\n}\n\nint ProcessResizeDistributionMessage(gsl::span<gsl::byte> Buffer)\ntry\n{\n    auto* Message = gslhelpers::try_get_struct<LX_MINI_INIT_RESIZE_DISTRIBUTION_MESSAGE>(Buffer);\n\n    if (!Message)\n    {\n        LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n        return -1;\n    }\n\n    wil::unique_fd SocketFd{UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, true)};\n    if (!SocketFd)\n    {\n        return -1;\n    }\n\n    wil::unique_fd OutputSocketFd{UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, true)};\n    if (!OutputSocketFd)\n    {\n        return -1;\n    }\n\n    const int ChildPid = UtilCreateChildProcess(\n        \"ResizeDistribution\",\n        [Message, Channel = wsl::shared::SocketChannel{std::move(SocketFd), \"ResizeDistribution\"}, OutputSocket = std::move(OutputSocketFd)]() mutable {\n            int ResponseCode = -1;\n            auto ReportStatus = wil::scope_exit([&]() {\n                LX_MINI_INIT_RESIZE_DISTRIBUTION_RESPONSE ResponseMessage{};\n                ResponseMessage.ResponseCode = ResponseCode;\n                ResponseMessage.Header.MessageType = LxMiniInitMessageResizeDistributionResponse;\n                ResponseMessage.Header.MessageSize = sizeof(ResponseMessage);\n\n                Channel.SendMessage(ResponseMessage);\n            });\n\n            THROW_LAST_ERROR_IF(TEMP_FAILURE_RETRY(dup2(OutputSocket.get(), STDOUT_FILENO)) < 0);\n            THROW_LAST_ERROR_IF(TEMP_FAILURE_RETRY(dup2(OutputSocket.get(), STDERR_FILENO)) < 0);\n\n            auto DevicePath = GetLunDevicePath(Message->ScsiLun);\n\n            auto CommandLine = std::format(\"/usr/sbin/e2fsck -f -y '{}'\", DevicePath);\n            THROW_LAST_ERROR_IF(UtilExecCommandLine(CommandLine.c_str()) < 0);\n\n            if (Message->NewSize == 0)\n            {\n                CommandLine = std::format(\"/usr/sbin/resize2fs '{}'\", DevicePath);\n            }\n            else\n            {\n                CommandLine = std::format(\"/usr/sbin/resize2fs '{}' '{}K'\", DevicePath, ((Message->NewSize + 1024) - 1) / 1024);\n            }\n\n            THROW_LAST_ERROR_IF(UtilExecCommandLine(CommandLine.c_str()) < 0);\n\n            ResponseCode = 0;\n        });\n\n    return (ChildPid < 0) ? -1 : 0;\n}\nCATCH_RETURN_ERRNO();\n\nint ProcessMessage(wsl::shared::SocketChannel& Channel, LX_MESSAGE_TYPE Type, gsl::span<gsl::byte> Buffer, VmConfiguration& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine processes messages from the service.\n\nArguments:\n\n    MessageFd - Supplies a file descriptor to the socket on which the message was\n        received. This is used for operations that require responses, for example a\n        VHD eject request.\n\n    Buffer - Supplies the message.\n\n    Config - Supplies the VM configuration.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\ntry\n{\n\n    //\n    // Validate the message and handle operations that do not require creating a child process.\n    //\n\n    switch (Type)\n    {\n    case LxMiniInitMessageLaunchInit:\n    case LxMiniInitMessageImport:\n    case LxMiniInitMessageImportInplace:\n    case LxMiniInitMessageExport:\n        try\n        {\n            const auto Message = gslhelpers::try_get_struct<LX_MINI_INIT_MESSAGE>(Buffer);\n            THROW_ERRNO_IF(EINVAL, !Message);\n\n            wsl::shared::SocketChannel Channel{UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, false), \"Init\"};\n            if (Channel.Socket() < 0)\n            {\n                return -1;\n            }\n\n            wil::unique_fd SystemDistroSocketFd{};\n            if (Message->Flags & LxMiniInitMessageFlagLaunchSystemDistro && Config.EnableGuiApps)\n            {\n                SystemDistroSocketFd = UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, false);\n                if (!SystemDistroSocketFd)\n                {\n                    return -1;\n                }\n            }\n\n            auto ChildPid = UtilCreateChildProcess(\n                \"LaunchDistro\",\n                [Type, Message, Buffer, Channel = std::move(Channel), SystemDistroSocketFd = std::move(SystemDistroSocketFd), &Config]() mutable {\n                    //\n                    // Restore the default signal flags so anything blocked by mini_init doesn't get\n                    // inherited by init and session leaders.\n                    //\n\n                    THROW_LAST_ERROR_IF(UtilRestoreBlockedSignals() < 0);\n\n                    if (Type == LxMiniInitMessageLaunchInit)\n                    {\n                        ProcessLaunchInitMessage(Message, Buffer, std::move(Channel), std::move(SystemDistroSocketFd), Config);\n                        FATAL_ERROR(\"Unexpected return from ProcessLaunchInitMessage\");\n                    }\n                    else\n                    {\n                        ProcessImportExportMessage(Buffer, std::move(Channel));\n                    }\n                },\n                (CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD));\n\n            return (ChildPid < 0) ? -1 : 0;\n        }\n        CATCH_RETURN_ERRNO()\n\n    case LxMiniInitMessageEjectVhd:\n    {\n        //\n        // Eject the scsi device and inform the service that the operation is complete.\n        //\n\n        const auto* EjectMessage = gslhelpers::try_get_struct<EJECT_VHD_MESSAGE>(Buffer);\n        if (!EjectMessage)\n        {\n            LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n            return -1;\n        }\n\n        Channel.SendResultMessage(EjectScsi(EjectMessage->Lun));\n        return 0;\n    }\n\n    case LxMiniInitMessageEarlyConfig:\n    {\n        const auto EarlyConfig = gslhelpers::try_get_struct<LX_MINI_INIT_EARLY_CONFIG_MESSAGE>(Buffer);\n        if (!EarlyConfig)\n        {\n            LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n            return -1;\n        }\n\n        if (EarlyConfig->EnableSafeMode)\n        {\n            LOG_WARNING(\"{} - many features will be disabled\", WSL_SAFE_MODE_WARNING);\n            Config.EnableSafeMode = true;\n        }\n\n        //\n        // Establish the connection for the guest network service.\n        //\n\n        auto SocketFd = UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, true);\n        if (!SocketFd)\n        {\n            return -1;\n        }\n\n        //\n        // If DNS tunneling is enabled, open a separate hvsocket connection for it.\n        //\n\n        wil::unique_fd DnsTunnelingSocketFd{};\n        if (EarlyConfig->EnableDnsTunneling)\n        {\n            DnsTunnelingSocketFd = UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, true);\n            if (!DnsTunnelingSocketFd)\n            {\n                return -1;\n            }\n        }\n\n        //\n        // Configure page reporting and memory reclamation.\n        //\n\n        ConfigureMemoryReduction(EarlyConfig->PageReportingOrder, EarlyConfig->MemoryReclaimMode);\n\n        //\n        // Initialize system distro if supported.\n        //\n\n        if (EarlyConfig->SystemDistroDeviceId != UINT_MAX)\n        {\n            if (MountSystemDistro(EarlyConfig->SystemDistroDeviceType, EarlyConfig->SystemDistroDeviceId) < 0)\n            {\n                return -1;\n            }\n\n            //\n            // Crash dump collection needs to be reconfigured here, because we called chroot.\n            //\n\n            if (Config.EnableCrashDumpCollection)\n            {\n                EnableCrashDumpCollection();\n            }\n\n            Config.EnableSystemDistro = true;\n\n            //\n            // Set the $LANG environment variable.\n            //\n            // N.B. This is needed by bsdtar for path conversions (to support .xz file format).\n            //\n\n            if (setenv(\"LANG\", \"en_US.UTF-8\", 1) < 0)\n            {\n                LOG_ERROR(\"setenv(LANG, en_US.UTF-8) failed {}\", errno);\n            }\n\n            //\n            // Start the debug shell if enabled.\n            //\n\n            if (EarlyConfig->EnableDebugShell)\n            {\n                StartDebugShell();\n            }\n\n            //\n            // Configure swap space.\n            //\n\n            if (EarlyConfig->SwapLun != UINT_MAX)\n            {\n                CreateSwap(EarlyConfig->SwapLun);\n            }\n\n            //\n            // Start the time sync agent (chronyd) to keep guest clock in sync with the host.\n            //\n\n            StartTimeSyncAgent();\n        }\n\n        //\n        // Mount kernel modules if supported.\n        //\n        // N.B. The VHD is mounted as read-only but with a writable overlayfs layer. The modules\n        //      directory must be writable for tools like depmod to work.\n        //\n\n        if (EarlyConfig->KernelModulesDeviceId != UINT_MAX)\n        {\n            THROW_LAST_ERROR_IF(\n                MountDevice(LxMiniInitMountDeviceTypeLun, EarlyConfig->KernelModulesDeviceId, KERNEL_MODULES_VHD_PATH, \"ext4\", LxMiniInitMessageFlagMountReadOnly, nullptr) <\n                0);\n\n            utsname UnameBuffer{};\n            THROW_LAST_ERROR_IF(uname(&UnameBuffer) < 0);\n\n            std::string Target = std::format(\"{}/{}\", KERNEL_MODULES_PATH, UnameBuffer.release);\n            THROW_LAST_ERROR_IF(UtilMountOverlayFs(Target.c_str(), KERNEL_MODULES_VHD_PATH, (MS_NOATIME | MS_NOSUID | MS_NODEV)) < 0);\n\n            const std::string KernelModulesList = wsl::shared::string::FromSpan(Buffer, EarlyConfig->KernelModulesListOffset);\n            for (const auto& Module : wsl::shared::string::Split(KernelModulesList, ','))\n            {\n                const char* Argv[] = {MODPROBE_PATH, Module.c_str(), nullptr};\n                int Status = -1;\n                auto result = UtilCreateProcessAndWait(MODPROBE_PATH, Argv, &Status);\n                if (result < 0)\n                {\n                    LOG_ERROR(\"Failed to load module '{}', {}\", Module, Status);\n                }\n            }\n\n            Config.KernelModulesPath = std::move(Target);\n        }\n\n        //\n        // Initialization required by mini_init.\n        //\n\n        if (Initialize(wsl::shared::string::FromSpan(Buffer, EarlyConfig->HostnameOffset)) < 0)\n        {\n            return -1;\n        }\n\n        //\n        // Start the guest network service.\n        //\n\n        if (StartGuestNetworkService(SocketFd.get(), std::move(DnsTunnelingSocketFd), EarlyConfig->DnsTunnelingIpAddress) < 0)\n        {\n            return -1;\n        }\n\n        return 0;\n    }\n\n    case LxMiniInitMessageInitialConfig:\n    {\n        const auto ConfigMessage = gslhelpers::try_get_struct<LX_MINI_INIT_CONFIG_MESSAGE>(Buffer);\n        if (!ConfigMessage)\n        {\n            LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n            return -1;\n        }\n\n        auto NetworkingConfiguration = &ConfigMessage->NetworkingConfiguration;\n        Config.NetworkingMode = NetworkingConfiguration->NetworkingMode;\n        if (NetworkingConfiguration->PortTrackerType != LxMiniInitPortTrackerTypeNone)\n        {\n            StartPortTracker(NetworkingConfiguration->PortTrackerType);\n        }\n\n        if (NetworkingConfiguration->DisableIpv6)\n        {\n            WriteToFile(\"/proc/sys/net/ipv6/conf/all/disable_ipv6\", c_trueString);\n        }\n\n        if (NetworkingConfiguration->EnableDhcpClient)\n        {\n            StartDhcpClient(NetworkingConfiguration->DhcpTimeout);\n        }\n\n        if (SetEphemeralPortRange(NetworkingConfiguration->EphemeralPortRangeStart, NetworkingConfiguration->EphemeralPortRangeEnd) < 0)\n        {\n            return -1;\n        }\n\n        if (ConfigMessage->EntropySize > 0)\n        {\n            InjectEntropy(Buffer.subspan(ConfigMessage->EntropyOffset, ConfigMessage->EntropySize));\n        }\n\n        if (ConfigMessage->MountGpuShares)\n        {\n            if (MountPlan9(LXSS_GPU_DRIVERS_SHARE, GPU_SHARE_DRIVERS, true) < 0)\n            {\n                return -1;\n            }\n\n            if (MountPlan9(LXSS_GPU_PACKAGED_LIB_SHARE, GPU_SHARE_LIB_PACKAGED, true) < 0)\n            {\n                return -1;\n            }\n\n            if (ConfigMessage->EnableInboxGpuLibs)\n            {\n                if (MountPlan9(LXSS_GPU_INBOX_LIB_SHARE, GPU_SHARE_LIB_INBOX, true) < 0)\n                {\n                    return -1;\n                }\n            }\n        }\n\n        Config.EnableInboxGpuLibs = ConfigMessage->EnableInboxGpuLibs;\n        Config.EnableGpuSupport = ConfigMessage->MountGpuShares;\n        Config.EnableGuiApps = ConfigMessage->EnableGuiApps;\n        return 0;\n    }\n    case LxMiniInitMessageMount:\n    case LxMiniInitMessageUnmount:\n    case LxMiniInitMessageDetach:\n        ProcessMountMessage(Buffer);\n\n        //\n        // Ignore the return code from ProcessMountMessage so that we don't exit on error.\n        //\n\n        return 0;\n\n    case LxMiniInitMountFolder:\n        return ProcessMountFolderMessage(Channel, Buffer);\n\n    case LxInitCreateProcess:\n        return ProcessCreateProcessMessage(Channel, Buffer);\n\n    case LxMiniInitMessageWaitForPmemDevice:\n    {\n        const auto PmemMessage = gslhelpers::try_get_struct<LX_MINI_INIT_WAIT_FOR_PMEM_DEVICE_MESSAGE>(Buffer);\n        if (!PmemMessage)\n        {\n            LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n            return -1;\n        }\n\n        ProcessWaitForPmemDeviceMessage(PmemMessage);\n\n        //\n        // Ignore the return code from ProcessWaitForPmemDeviceMessage so that we don't exit on error.\n        //\n\n        return 0;\n    }\n\n    case LxMiniInitMessageResizeDistribution:\n    {\n\n        ProcessResizeDistributionMessage(Buffer);\n        return 0;\n    }\n\n    default:\n        LOG_ERROR(\"Unexpected message type {}\", Type);\n        return -1;\n    }\n\n    _exit(1);\n}\nCATCH_RETURN_ERRNO();\n\nwil::unique_fd RegisterSeccompHook()\n\n/*++\n\nRoutine Description:\n\n    Register a seccomp notification for bind() & ioctl(*, TUNSETIFF, *) calls.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    The notification file descriptor or < 0 on failure.\n\n--*/\n\n{\n    struct sock_filter Filter[] = {\n        // Structure of this program:\n        // For each architecture, there is a block of instructions to match specific calls.\n        // The first two instructions check for the arch and skip to the next one if it doesn't match.\n        // Each block contains a return SECCOMP_RET_USER_NOTIF/SECCOMP_RET_ALLOW so that\n        // offset within a block don't change as other blocks change.\n\n        // 64bit:\n        // If syscall_arch & __AUDIT_ARCH_64BIT then continue else goto :32bit\n        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_arch),\n        // For now, notify on all non-native arch\n        BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, __AUDIT_ARCH_64BIT, 0, 7),\n        // If syscall_nr == __NR_bind then goto user_notify: else continue\n        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr),\n        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_bind, 3, 0),\n        // if (syscall_nr == __NR_bind) then continue else goto allow:\n        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_ioctl, 0, 3),\n        // if (syscall arg1 == SIOCSIFFLAGS) goto user_notify else goto allow:\n        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_arg(1)),\n        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SIOCSIFFLAGS, 0, 1),\n        // user_notify:\n        //     return SECCOMP_RET_USER_NOTIF;\n        BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_USER_NOTIF),\n        // allow:\n        //     return SECCOMP_RET_ALLOW;\n        BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),\n\n    // Note: 32bit on x86_64 uses the __NR_socketcall with the first argument\n    // set to SYS_BIND to make bind system call.\n#ifdef __x86_64__\n        // 32bit:\n        // If syscall_nr == __NR_socketcall then continue else goto allow:\n        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr),\n        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, I386_NR_socketcall, 0, 3),\n        // if syscall arg0 == SYS_BIND then goto user_notify: else goto allow:\n        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_arg(0)),\n        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SYS_BIND, 0, 1),\n        // user_notify:\n        //     return SECCOMP_RET_USER_NOTIF;\n        BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_USER_NOTIF),\n        // allow:\n        //     return SECCOMP_RET_ALLOW;\n        BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),\n#else\n        // 32bit:\n        // If syscall_nr == __NR_bind then goto user_notify: else goto allow:\n        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr),\n        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARMV7_NR_bind, 0, 1),\n        // user_notify:\n        //     return SECCOMP_RET_USER_NOTIF;\n        BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_USER_NOTIF),\n        // allow:\n        //     return SECCOMP_RET_ALLOW;\n        BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),\n#endif\n    };\n\n    struct sock_fprog Prog = {\n        .len = sizeof(Filter) / sizeof(Filter[0]),\n        .filter = Filter,\n    };\n\n    wil::unique_fd Fd{syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, &Prog)};\n    if (!Fd)\n    {\n        LOG_ERROR(\"Failed to register bpf syscall hook, {}\", errno);\n        return {};\n    }\n\n    if (SetCloseOnExec(Fd.get(), false) < 0)\n    {\n        return {};\n    }\n\n    return Fd;\n}\n\nint SendCapabilities(wsl::shared::SocketChannel& Channel)\n\n/*++\n\nRoutine Description:\n\n    Send the kernel capabilities on the specified channel.\n\nArguments:\n\n    Channel - The channel to send the message on.\n\nReturn Value:\n\n    0 on success or < 0 on failure.\n\n--*/\n\ntry\n{\n    utsname Version;\n    THROW_LAST_ERROR_IF(uname(&Version) < 0);\n\n    wsl::shared::MessageWriter<LX_INIT_GUEST_CAPABILITIES> Message(LxMiniInitMessageGuestCapabilities);\n    Message.WriteString(Version.release);\n\n    //\n    // SECCOMP_USER_NOTIF_FLAG_CONTINUE is the latest flag that flow steering needs\n    // but there's no way to test for its presence. The assumption is that if seccomp is available\n    // and the kernel version is >= 5.10, then SECCOMP_USER_NOTIF_FLAG_CONTINUE is available\n    //\n\n    uint32_t SeccompFlag = SECCOMP_RET_USER_NOTIF;\n    Message->SeccompAvailable = syscall(__NR_seccomp, SECCOMP_GET_ACTION_AVAIL, 0, &SeccompFlag) == 0;\n\n    Channel.SendMessage<LX_INIT_GUEST_CAPABILITIES>(Message.Span());\n    return 0;\n}\nCATCH_RETURN_ERRNO();\n\nint SetCloseOnExec(int Fd, bool Enable)\n\n/*++\n\nRoutine Description:\n\n    Sets or clears the FD_CLOEXEC flag on the file descriptor.\n\nArguments:\n\n    Fd - Supplies the file descriptor to modify.\n\n    Enable - true to set the flag, false to clear.\n\nReturn Value:\n\n    0 on success or -1 on failure.\n\n--*/\n\n{\n    int Result = fcntl(Fd, F_GETFD, 0);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"fcntl(F_GETFD) failed {}\", errno);\n        return -1;\n    }\n\n    int Flags = Enable ? (Result | FD_CLOEXEC) : (Result & ~FD_CLOEXEC);\n    Result = fcntl(Fd, F_SETFD, Flags);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"fcntl(F_SETFD, {}) failed {}\", Flags, errno);\n        return -1;\n    }\n\n    return 0;\n}\n\nint SetEphemeralPortRange(uint16_t Start, uint16_t End)\n\n/*++\n\nRoutine Description:\n\n    This routine sets the ephemeral port range.\n\nArguments:\n\n    Start - Supplies the first port of the range (inclusive)\n\n    End - Supplies the last port of the range (inclusive).\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    if (Start == 0 && End == 0)\n    {\n        return 0;\n    }\n\n    std::string Content = std::format(\"{} {}\", Start, End);\n\n    //\n    // N.B. IPv6 reads from /proc/sys/net/ipv4/ip_local_port_range as well according to\n    //      https://tldp.org/HOWTO/Linux+IPv6-HOWTO/ch11s03.html.\n    //\n\n    return WriteToFile(\"/proc/sys/net/ipv4/ip_local_port_range\", Content.c_str());\n}\nCATCH_RETURN_ERRNO()\n\nvoid StartTimeSyncAgent()\n\n/*++\n\nRoutine Description:\n\n    This routine configures and launches chronyd.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    //\n    // Check if the /dev/ptp0 device is present.\n    //\n\n    if (access(\"/dev/ptp0\", F_OK) < 0)\n    {\n        LOG_ERROR(\"/dev/ptp0 not found - kernel must be built with CONFIG_PTP_1588_CLOCK\");\n        return;\n    }\n\n    //\n    // Create a child process to run chronyd.\n    //\n\n    UtilCreateChildProcess(\"chrony\", []() {\n        const auto FileContents =\n            \"driftfile /var/lib/chrony/drift\\n\" // Record the rate at which the system clock gains/losses time.\n            \"makestep 1.0 3\\n\" // Allow the system clock to be stepped in the first three updates if its offset is larger than 1 second.\n            \"rtcsync\\n\"        // Enable kernel synchronization of the real-time clock (RTC).\n            \"leapsectz right/UTC\\n\"    // Get TAI-UTC offset and leap seconds from the system tz database.\n            \"logdir /var/log/chrony\\n\" // Specify directory for log files.\n            \"refclock PHC /dev/ptp0 poll 3 dpoll -2 offset 0\\n\"; // Use the /dev/ptp0 device as a clock source.\n\n        remove(CHRONY_CONF_PATH);\n        THROW_LAST_ERROR_IF(WriteToFile(CHRONY_CONF_PATH, FileContents) < 0);\n\n        execl(CHRONYD_PATH, CHRONYD_PATH, NULL);\n        LOG_ERROR(\"execl failed {}\", errno);\n    });\n}\n\nvoid WaitForBlockDevice(const char* Path)\n\n/*++\n\nRoutine Description:\n\n    Wait for a block device to be available.\n\nArguments:\n\n    Path - Supplies the path to the block device.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    wsl::shared::retry::RetryWithTimeout<void>(\n        [&]() {\n            wil::unique_fd device{open(Path, O_RDONLY)};\n            THROW_LAST_ERROR_IF(!device);\n        },\n        c_defaultRetryPeriod,\n        c_defaultRetryTimeout,\n        [&]() {\n            errno = wil::ResultFromCaughtException();\n            return errno == ENOENT || errno == ENXIO || errno == EIO;\n        });\n}\n\nint WaitForChild(pid_t Pid, const char* Name)\n\n/*++\n\nRoutine Description:\n\n    Wait for a child process to exit and check that it exited successfully.\n\nArguments:\n\n    Pid - Supplies the pid to wait for.\n\n    Name - Supplies the process image name, for logging.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    int Status = -1;\n    if (TEMP_FAILURE_RETRY(waitpid(Pid, &Status, 0)) < 0)\n    {\n        LOG_ERROR(\"Waiting for child '{}' failed, waitpid failed {}\", Name, errno);\n        return -1;\n    }\n\n    return UtilProcessChildExitCode(Status, Name);\n}\n\nint WslEntryPoint(int Argc, char* Argv[]);\n\nvoid EnableDebugMode(const std::string& Mode)\n{\n    if (Mode == \"hvsocket\")\n    {\n        // Mount the debugfs.\n        THROW_LAST_ERROR_IF(UtilMount(\"none\", \"/sys/kernel/debug\", \"debugfs\", 0, nullptr) < 0);\n\n        // Enable hvsocket events.\n        std::vector<const char*> files{\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_on_msg_dpc/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_on_message/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_onoffer/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_onoffer_rescind/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_onopen_result/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_ongpadl_created/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_ongpadl_torndown/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_open/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_close_internal/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_establish_gpadl_header/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_establish_gpadl_body/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_teardown_gpadl/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_release_relid/enable\",\n            \"/sys/kernel/debug/tracing/events/hyperv/vmbus_send_tl_connect_request/enable\"};\n\n        for (auto* e : files)\n        {\n            WriteToFile(e, \"1\");\n        }\n\n        // Relay logs to the host.\n        std::thread relayThread{[]() {\n            constexpr auto path = \"/sys/kernel/debug/tracing/trace_pipe\";\n            std::ifstream file(path);\n\n            if (!file)\n            {\n                LOG_ERROR(\"Failed to open {}, {}\", path, errno);\n                return;\n            }\n\n            std::string line;\n            while (std::getline(file, line))\n            {\n                LOG_INFO(\"{}\", line);\n            }\n\n            LOG_ERROR(\"{}: closed\", path);\n        }};\n\n        relayThread.detach();\n    }\n    else\n    {\n        LOG_ERROR(\"Unknown debugging mode: '{}'\", Mode);\n    }\n}\n\nint main(int Argc, char* Argv[])\n{\n    std::vector<gsl::byte> Buffer;\n    ssize_t BytesRead;\n    VmConfiguration Config{};\n    wil::unique_fd ConsoleFd{};\n    wsl::shared::SocketChannel channel;\n    wil::unique_fd NotifyFd{};\n    struct pollfd PollDescriptors[2];\n    wil::unique_fd SignalFd{};\n    struct signalfd_siginfo SignalInfo;\n    sigset_t SignalMask;\n    int Status;\n\n    //\n    // Determine which entrypoint should be used.\n    //\n\n    if (getpid() != 1 || !getenv(WSL_ROOT_INIT_ENV))\n    {\n        return WslEntryPoint(Argc, Argv);\n    }\n\n    if (unsetenv(WSL_ROOT_INIT_ENV))\n    {\n        LOG_ERROR(\"unsetenv failed {}\", errno);\n    }\n\n    // Use an env variable to determine whether socket logging is enabled since /proc isn't mounted yet\n    // so SocketChannel can't look at the kernel command line.\n    wsl::shared::SocketChannel::EnableSocketLogging(getenv(WSL_SOCKET_LOG_ENV) != nullptr);\n\n    if (unsetenv(WSL_SOCKET_LOG_ENV))\n    {\n        LOG_ERROR(\"unsetenv failed {}\", errno);\n    }\n\n    //\n    // Mount devtmpfs.\n    //\n\n    int Result = UtilMount(nullptr, DEVFS_PATH, \"devtmpfs\", 0, nullptr);\n    if (Result < 0)\n    {\n        goto ErrorExit;\n    }\n\n    //\n    // Open kmsg for logging and ensure that the file descriptor is not set to one of the standard file descriptors.\n    //\n    // N.B. This is to work around a rare race condition where init is launched without /dev/console set as the controlling terminal.\n    //\n\n    InitializeLogging(false);\n    if (g_LogFd <= STDERR_FILENO)\n    {\n        LOG_ERROR(\"/init was started without /dev/console\");\n        if (dup2(g_LogFd, 3) < 0)\n        {\n            LOG_ERROR(\"dup2 failed {}\", errno);\n        }\n\n        close(g_LogFd);\n        g_LogFd = 3;\n    }\n\n    //\n    // Log the WSL version to kmesg.\n    //\n\n    LOG_INFO(\"WSL version {}\", WSL_PACKAGE_VERSION);\n\n    //\n    // Ensure /dev/console is present and set as the controlling terminal.\n    // If opening /dev/console times out, stdout and stderr to the logging file descriptor.\n    //\n\n    try\n    {\n        wsl::shared::retry::RetryWithTimeout<void>(\n            [&]() {\n                ConsoleFd = open(\"/dev/console\", O_RDWR);\n                THROW_LAST_ERROR_IF(!ConsoleFd);\n            },\n            c_defaultRetryPeriod,\n            c_defaultRetryTimeout);\n\n        THROW_LAST_ERROR_IF(login_tty(ConsoleFd.get()) < 0);\n    }\n    catch (...)\n    {\n        if (dup2(g_LogFd, STDOUT_FILENO) < 0)\n        {\n            LOG_ERROR(\"dup2 failed {}\", errno);\n        }\n\n        if (dup2(g_LogFd, STDERR_FILENO) < 0)\n        {\n            LOG_ERROR(\"dup2 failed {}\", errno);\n        }\n    }\n\n    //\n    // Open /dev/null for stdin.\n    //\n\n    {\n        wil::unique_fd Fd{TEMP_FAILURE_RETRY(open(DEVNULL_PATH, O_RDONLY))};\n        if (!Fd)\n        {\n            LOG_ERROR(\"open({}) failed {}\", DEVNULL_PATH, errno);\n            return -1;\n        }\n\n        if (Fd.get() == STDIN_FILENO)\n        {\n            Fd.release();\n        }\n        else\n        {\n            if (TEMP_FAILURE_RETRY(dup2(Fd.get(), STDIN_FILENO)) < 0)\n            {\n                LOG_ERROR(\"dup2 failed {}\", errno);\n                return -1;\n            }\n        }\n    }\n\n    //\n    // Create the etc directory and mount procfs and sysfs.\n    //\n\n    if (UtilMkdir(ETC_PATH, 0755) < 0)\n    {\n        return -1;\n    }\n\n    if (UtilMount(nullptr, PROCFS_PATH, \"proc\", 0, nullptr) < 0)\n    {\n        return -1;\n    }\n\n    if (UtilMount(nullptr, SYSFS_PATH, \"sysfs\", 0, nullptr) < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Enable debug mode, if specified.\n    //\n\n    if (const auto* debugMode = getenv(WSL_DEBUG_ENV))\n    {\n        LOG_ERROR(\"Running in debug mode: '{}'\", debugMode);\n        EnableDebugMode(debugMode);\n\n        unsetenv(WSL_DEBUG_ENV);\n    }\n\n    //\n    // Establish the message channel with the service via hvsocket.\n    //\n\n    channel = {UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, true), \"mini_init\"};\n    if (channel.Socket() < 0)\n    {\n        Result = -1;\n        goto ErrorExit;\n    }\n\n    if (SendCapabilities(channel) < 0)\n    {\n        goto ErrorExit;\n    }\n    //\n    // Create another channel for guest-driven communication, for example, to\n    // notify the service when a distribution terminates unexpectedly.\n    //\n\n    NotifyFd = UtilConnectVsock(LX_INIT_UTILITY_VM_INIT_PORT, true);\n    if (!NotifyFd)\n    {\n        Result = -1;\n        goto ErrorExit;\n    }\n\n    if (getenv(WSL_ENABLE_CRASH_DUMP_ENV))\n    {\n        Config.EnableCrashDumpCollection = true;\n\n        EnableCrashDumpCollection();\n        if (unsetenv(WSL_ENABLE_CRASH_DUMP_ENV) < 0)\n        {\n            LOG_ERROR(\"unsetenv failed {}\", errno);\n        }\n    }\n\n    UtilMount(nullptr, CGROUP_MOUNTPOINT, CGROUP2_DEVICE, 0, nullptr);\n\n    UtilSetThreadName(\"mini_init\");\n\n    //\n    // Create a signalfd to detect when the child process exits.\n    //\n\n    sigemptyset(&SignalMask);\n    sigaddset(&SignalMask, SIGCHLD);\n    Result = UtilSaveBlockedSignals(SignalMask);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"sigprocmask failed {}\", errno);\n        goto ErrorExit;\n    }\n\n    SignalFd = signalfd(-1, &SignalMask, SFD_CLOEXEC);\n    if (!SignalFd)\n    {\n        Result = -1;\n        LOG_ERROR(\"signalfd failed {}\", errno);\n        goto ErrorExit;\n    }\n\n    //\n    // Fill the poll descriptors and begin worker loop.\n    //\n\n    PollDescriptors[0].fd = channel.Socket();\n    PollDescriptors[0].events = POLLIN;\n    PollDescriptors[1].fd = SignalFd.get();\n    PollDescriptors[1].events = POLLIN;\n    for (;;)\n    {\n        Result = poll(PollDescriptors, COUNT_OF(PollDescriptors), -1);\n        if (Result < 0)\n        {\n            LOG_ERROR(\"poll failed {}\", errno);\n            break;\n        }\n\n        //\n        // Process messages from the service. Break out of the loop if the socket is closed.\n        //\n\n        assert((PollDescriptors[0].revents & POLLNVAL) == 0);\n        if (PollDescriptors[0].revents & (POLLHUP | POLLERR))\n        {\n            break;\n        }\n        else if (PollDescriptors[0].revents & POLLIN)\n        {\n            auto [Message, Range] = channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\n            if (Message == nullptr)\n            {\n                break; // Socket was closed, exit\n            }\n\n            Result = ProcessMessage(channel, Message->MessageType, Range, Config);\n            if (Result < 0)\n            {\n                goto ErrorExit;\n            }\n        }\n\n        //\n        // Handle signalfd.\n        //\n\n        assert((PollDescriptors[1].revents & (POLLHUP | POLLERR | POLLNVAL)) == 0);\n        if (PollDescriptors[1].revents & POLLIN)\n        {\n            BytesRead = TEMP_FAILURE_RETRY(read(PollDescriptors[1].fd, &SignalInfo, sizeof(SignalInfo)));\n            if (BytesRead != sizeof(SignalInfo))\n            {\n                Result = -1;\n                LOG_ERROR(\"read failed {} {}\", BytesRead, errno);\n                goto ErrorExit;\n            }\n\n            if (SignalInfo.ssi_signo != SIGCHLD)\n            {\n                LOG_ERROR(\"Unexpected signal {}\", SignalInfo.ssi_signo);\n                goto ErrorExit;\n            }\n\n            //\n            // Reap zombies and notify the service when child processes exit.\n            //\n\n            for (;;)\n            {\n                Result = waitpid(-1, &Status, WNOHANG);\n                if (Result == 0)\n                {\n                    break;\n                }\n                else if (Result > 0)\n                {\n                    //\n                    // Perform a sync to flush all writes.\n                    //\n\n                    sync();\n\n                    //\n                    // Send a message with the child's pid to the service.\n                    //\n\n                    LX_MINI_INIT_CHILD_EXIT_MESSAGE Message{};\n                    Message.Header.MessageType = LxMiniInitMessageChildExit;\n                    Message.Header.MessageSize = sizeof(Message);\n                    Message.ChildPid = Result;\n                    Result = UtilWriteBuffer(NotifyFd.get(), gslhelpers::struct_as_bytes(Message));\n                    if (Result < 0)\n                    {\n                        LOG_ERROR(\"write failed {}\", errno);\n                    }\n                }\n                else\n                {\n                    //\n                    // No more children exist.\n                    //\n\n                    if (errno != ECHILD)\n                    {\n                        LOG_ERROR(\"waitpid failed {}\", errno);\n                    }\n\n                    break;\n                }\n            }\n        }\n    }\n\nErrorExit:\n    try\n    {\n        auto children = ListInitChildProcesses();\n\n        while (!children.empty())\n        {\n\n            // send SIGKILL to all running processes.\n            for (auto pid : children)\n            {\n                if (kill(pid, SIGKILL) < 0)\n                {\n                    LOG_ERROR(\"Failed to send SIGKILL to {}: {}\", pid, errno);\n                }\n            }\n\n            // Wait for processes to actually exit.\n            while (!children.empty())\n            {\n                auto Result = waitpid(-1, nullptr, 0);\n                THROW_ERRNO_IF(errno, Result <= 0);\n                LOG_INFO(\"Process {} exited\", Result);\n                children.erase(Result);\n            }\n\n            children = ListInitChildProcesses();\n        }\n    }\n    CATCH_LOG();\n\n    sync();\n\n    try\n    {\n        for (auto disk : ListScsiDisks())\n        {\n            if (DetachScsiDisk(disk) < 0)\n            {\n                LOG_ERROR(\"Failed to detach disk: {}\", disk);\n            }\n        }\n    }\n    CATCH_LOG();\n\n    reboot(RB_POWER_OFF);\n\n    return Result;\n}\n"
  },
  {
    "path": "src/linux/init/plan9.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"common.h\"\n#include <memory>\n#include <string>\n#include <string_view>\n\n#include <sys/resource.h>\n#include <sys/socket.h>\n\n#include <lxwil.h>\n#include <p9fs.h>\n#include <p9tracelogging.h>\n#include <optional>\n\n#include \"wslpath.h\"\n\n#include \"util.h\"\n#include \"SocketChannel.h\"\n#include \"WslDistributionConfig.h\"\n\nnamespace {\n\n// Callback used if the Plan 9 server encounters an exception.\nvoid LogPlan9Exception(const char* message, const char* exceptionDescription) noexcept\n{\n    LogException(message, exceptionDescription);\n\n    // Also log the message to the tracelogging output, if that is enabled.\n    p9fs::Plan9TraceLoggingProvider::LogException(message, exceptionDescription);\n}\n\n// C++ helper for translating Windows paths to Linux paths.\nstd::string TranslatePath(char* windowsPath)\n{\n    std::string translatedPath = WslPathTranslate(windowsPath, TRANSLATE_FLAG_ABSOLUTE, TRANSLATE_MODE_UNIX);\n    THROW_ERRNO_IF(EINVAL, translatedPath.empty());\n\n    return translatedPath;\n}\n\n// Create a unix socket and bind it to the specified path.\nwil::unique_fd CreateUnixServerSocket(const char* path)\n{\n    // Set up so the old working directory will be restored if it needs to be changed below.\n    char oldCwdBuffer[PATH_MAX];\n    char* oldCwd{};\n    auto restoreCwd = wil::scope_exit([&oldCwd]() {\n        if (oldCwd != nullptr)\n        {\n            chdir(oldCwd);\n        }\n    });\n\n    // Check if the path will fit in a sockaddr_un (with room for null terminator).\n    std::string_view pathView{path};\n    if (pathView.length() >= sizeof(sockaddr_un::sun_path))\n    {\n        // It won't, so split the parent path and child name.\n        auto index = pathView.find_last_of('/');\n\n        // This really shouldn't happen unless the WSL service has a bug.\n        THROW_ERRNO_IF(EINVAL, index == std::string_view::npos);\n\n        const std::string parent{pathView.substr(0, index)};\n        pathView = pathView.substr(index + 1);\n\n        // Ensure the child name fits in sun_path (with null terminator).\n        THROW_ERRNO_IF(ENAMETOOLONG, pathView.length() >= sizeof(sockaddr_un::sun_path));\n\n        // Get the current working directory to restore it later, and change to the socket's parent\n        // path.\n        oldCwd = getcwd(oldCwdBuffer, sizeof(oldCwdBuffer));\n        THROW_LAST_ERROR_IF(oldCwd == nullptr);\n        THROW_LAST_ERROR_IF(chdir(parent.c_str()) < 0);\n    }\n\n    // Create the socket.\n    wil::unique_fd server{socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)};\n    THROW_LAST_ERROR_IF(!server);\n\n    // Delete the socket file if an old instance left it behind (e.g. if a crash occurred).\n    if (unlink(path) < 0)\n    {\n        THROW_LAST_ERROR_IF(errno != ENOENT);\n    }\n\n    // Bind to the path.\n    sockaddr_un address{};\n    address.sun_family = AF_UNIX;\n    memcpy(address.sun_path, pathView.data(), pathView.length());\n    THROW_LAST_ERROR_IF(bind(server.get(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) < 0);\n\n    return server;\n}\n\n// Opens the log file, if one is specified, and sets the log level.\nwil::unique_fd EnableLogging(const char* logFile, int logLevel, bool truncateLog)\n{\n    // Don't enable logging if no log file was specified.\n    if (logFile == nullptr || strlen(logFile) == 0)\n    {\n        return {};\n    }\n\n    int flags = O_CREAT | O_WRONLY | O_APPEND;\n    WI_SetFlagIf(flags, O_TRUNC, truncateLog);\n    wil::unique_fd logFd{open(logFile, flags, 0600)};\n    if (!logFd)\n    {\n        LOG_ERROR(\"FS: Could not open log file {}: {}\", logFile, errno);\n        return {};\n    }\n\n    p9fs::Plan9TraceLoggingProvider::SetLevel(logLevel);\n    p9fs::Plan9TraceLoggingProvider::SetLogFileDescriptor(logFd.get());\n\n    return logFd;\n}\n\n// Shut down the server, optionally only if there are no clients.\n// Returns true if the server was stopped, false if there were clients preventing it from stopping.\nbool StopPlan9Server(p9fs::IPlan9FileSystem& fileSystem, bool force)\ntry\n{\n    if (!force)\n    {\n        if (fileSystem.HasConnections())\n        {\n            // Can't shut down because there are connections.\n            return false;\n        }\n    }\n\n    // Disable exception logging to ignore expected errors from the server\n    // shutting down.\n    wil::g_LogExceptionCallback = nullptr;\n\n    // Close all connections and stop listening.\n    fileSystem.Pause();\n\n    // Tear down the socket.\n    fileSystem.Teardown();\n\n    return true;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION_MSG(\"Could not stop file system server.\");\n\n    // Allow instance termination on failure to stop.\n    return true;\n}\n\nvoid RunPlan9ControlFile(p9fs::IPlan9FileSystem& fileSystem, wsl::shared::SocketChannel& channel)\ntry\n{\n    std::vector<gsl::byte> Buffer;\n    for (;;)\n    {\n        auto [Message, _] = channel.ReceiveMessageOrClosed<LX_INIT_STOP_PLAN9_SERVER>();\n        if (Message == nullptr)\n        {\n            _exit(0);\n        }\n\n        channel.SendResultMessage<bool>(StopPlan9Server(fileSystem, Message->Force));\n    }\n}\nCATCH_LOG();\n\n} // namespace\n\nvoid RunPlan9Server(const char* socketPath, const char* logFile, int logLevel, bool truncateLog, int controlSocket, int serverFd, wil::unique_fd& pipeFd)\n{\n    // Initialize logging.\n    InitializeLogging(false, LogPlan9Exception);\n    auto logFd = EnableLogging(logFile, logLevel, truncateLog);\n\n    // Increase the limit for number of open file descriptors to the max allowed.\n    rlimit limit{};\n    THROW_LAST_ERROR_IF(getrlimit(RLIMIT_NOFILE, &limit) < 0);\n\n    limit.rlim_cur = limit.rlim_max;\n    if (setrlimit(RLIMIT_NOFILE, &limit) < 0)\n    {\n        LOG_ERROR(\"setrlimit(RLIMIT_NOFILE, {}lu, {}lu) failed {}\", limit.rlim_cur, limit.rlim_max, errno);\n    }\n\n    // Open the root.\n    wil::unique_fd rootFd{open(\"/\", O_PATH | O_DIRECTORY | O_CLOEXEC)};\n    THROW_LAST_ERROR_IF(!rootFd);\n\n    {\n        // Create the file system server.\n        auto fileSystem = p9fs::CreateFileSystem(serverFd);\n\n        // Add the share (the share takes ownership of the fd).\n        fileSystem->AddShare(\"\", rootFd.get());\n        rootFd.release();\n\n        fileSystem->Resume();\n\n        // Close the pipe to signal the parent process that the plan9 server is started.\n        pipeFd.reset();\n\n        wsl::shared::SocketChannel channel({controlSocket}, \"Plan9Control\");\n        RunPlan9ControlFile(*fileSystem, channel);\n    }\n\n    // Unlink the socket path (don't care about failure).\n    if (socketPath != nullptr)\n    {\n        unlink(socketPath);\n    }\n}\n\n// Start listening for Plan 9 file server clients.\nstd::pair<unsigned int, wsl::shared::SocketChannel> StartPlan9Server(const char* socketWindowsPath, const wsl::linux::WslDistributionConfig& Config)\ntry\n{\n    unsigned int result = LX_INIT_UTILITY_VM_INVALID_PORT;\n\n    // Don't run the server if no socket was specified by init.\n    // N.B. This is used to prevent the server from running when disabled with feature staging.\n    // N.B. VM mode does not use a socket path.\n    if (!UtilIsUtilityVm() && strlen(socketWindowsPath) == 0)\n    {\n        return {LX_INIT_UTILITY_VM_INVALID_PORT, wsl::shared::SocketChannel{}};\n    }\n\n    int sockets[] = {-1, -1};\n    THROW_LAST_ERROR_IF(socketpair(PF_LOCAL, SOCK_STREAM, 0, sockets) < 0);\n\n    wil::unique_fd parentSocket{sockets[0]};\n    wil::unique_fd childSocket{sockets[1]};\n\n    THROW_LAST_ERROR_IF(fcntl(parentSocket.get(), F_SETFD, FD_CLOEXEC) < 0);\n\n    // Set the umask to the default.\n    umask(Config.Umask);\n\n    std::string translatedSocketPath;\n    wil::unique_fd server;\n    if (UtilIsUtilityVm())\n    {\n        sockaddr_vm address;\n        server.reset(UtilBindVsockAnyPort(&address, (SOCK_STREAM | SOCK_NONBLOCK)));\n        THROW_LAST_ERROR_IF(!server);\n\n        // Increase the vsock send/receive buffers to increase throughput.\n        int bufferSize = LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE;\n        THROW_LAST_ERROR_IF(setsockopt(server.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) < 0);\n        THROW_LAST_ERROR_IF(setsockopt(server.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) < 0);\n        result = address.svm_port;\n    }\n    else\n    {\n        // Translate the socket path (store a copy for unlinking on shutdown).\n        translatedSocketPath = TranslatePath(const_cast<char*>(socketWindowsPath));\n\n        // Create the server socket.\n        server = CreateUnixServerSocket(translatedSocketPath.c_str());\n    }\n\n    wil::unique_pipe pipe = wil::unique_pipe::create(0);\n    THROW_LAST_ERROR_IF(fcntl(pipe.read().get(), F_SETFD, FD_CLOEXEC) < 0)\n\n    const int childPid = UtilCreateChildProcess(\n        \"Plan9\",\n        [&translatedSocketPath, localChildSocket = std::move(childSocket), &Config, server = std::move(server), pipe = std::move(pipe.write())]() {\n            const std::string controlFdStr = std::to_string(localChildSocket.get());\n            const std::string logLevelStr = std::to_string(Config.Plan9LogLevel);\n            const std::string serverFdStr = std::to_string(server.get());\n            const std::string pipeFdStr = std::to_string(pipe.get());\n            std::vector<const char*> Arguments{\n                LX_INIT_PLAN9,\n                LX_INIT_PLAN9_CONTROL_SOCKET_ARG,\n                controlFdStr.c_str(),\n                LX_INIT_PLAN9_LOG_LEVEL_ARG,\n                logLevelStr.c_str(),\n                LX_INIT_PLAN9_SERVER_FD_ARG,\n                serverFdStr.c_str(),\n                LX_INIT_PLAN9_PIPE_FD_ARG,\n                pipeFdStr.c_str()};\n\n            if (!translatedSocketPath.empty())\n            {\n                Arguments.emplace_back(LX_INIT_PLAN9_SOCKET_PATH_ARG);\n                Arguments.emplace_back(translatedSocketPath.c_str());\n            }\n\n            if (Config.Plan9LogTruncate)\n            {\n                Arguments.emplace_back(LX_INIT_PLAN9_TRUNCATE_LOG_ARG);\n            }\n\n            if (Config.Plan9LogFile.has_value())\n            {\n                Arguments.emplace_back(LX_INIT_PLAN9_LOG_FILE_ARG);\n                Arguments.emplace_back(Config.Plan9LogFile->c_str());\n            }\n\n            Arguments.emplace_back(nullptr);\n\n            if (execv(LX_INIT_PATH, (char* const*)(Arguments.data())) < 0)\n            {\n                LOG_ERROR(\"execv failed {}\", errno);\n            }\n\n            _exit(0);\n        });\n\n    THROW_LAST_ERROR_IF(childPid < 0);\n\n    // The child will close the pipe once the plan9 server has been started.\n    // This wait is necessary because we want to make sure that no connection request\n    // comes before the plan9 server is ready to accept it.\n    char readBuf = 0;\n    THROW_LAST_ERROR_IF(read(pipe.read().get(), &readBuf, 1) != 0);\n\n    return {result, wsl::shared::SocketChannel{std::move(parentSocket), \"Plan9Control\"}};\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION_MSG(\"Could not start file system server.\")\n    return {LX_INIT_UTILITY_VM_INVALID_PORT, wsl::shared::SocketChannel{}};\n}"
  },
  {
    "path": "src/linux/init/plan9.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <optional>\n#include <lxwil.h>\n#include \"SocketChannel.h\"\n#include \"WslDistributionConfig.h\"\n\nstd::pair<unsigned int, wsl::shared::SocketChannel> StartPlan9Server(const char* socketWindowsPath, const wsl::linux::WslDistributionConfig& Config);\n\nvoid RunPlan9Server(const char* socketPath, const char* logFile, int logLevel, bool truncateLog, int controlSocket, int serverFd, wil::unique_fd& pipeFd);\n\nbool StopPlan9Server(bool force, wsl::linux::WslDistributionConfig& Config);"
  },
  {
    "path": "src/linux/init/telemetry.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    telemetry.cpp\n\nAbstract:\n\n    This file contains the telemetry agent implementation.\n\n--*/\n\n#include \"common.h\"\n#include <dirent.h>\n#include <errno.h>\n#include <libgen.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n#include <linux/connector.h>\n#include <linux/netlink.h>\n#include <linux/cn_proc.h>\n#include <string>\n#include <lxwil.h>\n#include \"util.h\"\n#include \"mountutil.h\"\n#include \"SocketChannel.h\"\n#include \"message.h\"\n\nnamespace {\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wgnu-variable-sized-type-not-at-end\"\n\nunion messageBuffer\n{\n    struct\n    {\n        nlmsghdr netlinkHeader;\n        cn_msg connectorMessage;\n        proc_cn_mcast_op operation;\n    } Send;\n\n    struct\n    {\n        nlmsghdr netlinkHeader;\n        cn_msg connectorMessage;\n        proc_event event;\n    } Receive;\n};\n\n#pragma GCC diagnostic pop\n\n// Map containing the binaries and commands that are filesystem-intensive that we want to warn users against using in DrvFs.\nconst std::map<std::string, std::string> g_drvFsUsageMap = {\n    {\"git\", \"clone\"}, {\"node\", \"/usr/bin/npm install\"}, {\"cargo\", \"build\"}};\n\nbool g_drvFsUsageEnabled = true;\n\n} // namespace\n\nstd::pair<std::string, bool> GetProcessInformation(int pid)\n{\n    // N.B. Procfs files may no longer be present for short-lived processes that exit before the\n    //      process creation notification can be processed.\n    const std::string procPidPath = std::format(\"/proc/{}\", pid);\n    wil::unique_fd fd{open(std::format(\"{}/cmdline\", procPidPath).c_str(), O_RDONLY)};\n    if (!fd)\n    {\n        return {};\n    }\n\n    // /proc/pid/cmdline contains all the arguments separated by NULL characters.\n    LX_MINI_INIT_TELEMETRY_MESSAGE message{};\n    message.Header.MessageSize = sizeof(message);\n    message.Header.MessageType = LX_MINI_INIT_TELEMETRY_MESSAGE::Type;\n\n    std::string commandLine(256, '\\0');\n    auto bytesRead = TEMP_FAILURE_RETRY(read(fd.get(), commandLine.data(), commandLine.size()));\n    if (bytesRead <= 0)\n    {\n        return {};\n    }\n\n    commandLine.resize(bytesRead - 1);\n    const auto* executable = basename(commandLine.data());\n\n    bool showDrvfsNotification = false;\n\n    // Determine if the DrvFs perf notification should be displayed.\n    if (g_drvFsUsageEnabled)\n    {\n        // Check the if the binary name and first argument are in the list of scenarios.\n        auto found = g_drvFsUsageMap.find(executable);\n        if (found != g_drvFsUsageMap.end())\n        {\n            auto length = strlen(executable);\n            if (bytesRead > length + 1)\n            {\n                auto argument = std::string_view(commandLine.data(), bytesRead).substr(length + 1);\n                if (strcmp(argument.data(), found->second.c_str()) == 0)\n                {\n                    // Determine if the current working directory is a DrvFs mount.\n                    std::error_code errorCode;\n                    auto cwd = std::filesystem::read_symlink(std::format(\"{}/cwd\", procPidPath), errorCode);\n                    if (!errorCode)\n                    {\n                        auto mountInfo = std::format(\"{}{}\", procPidPath, MOUNT_INFO_FILE_NAME);\n                        size_t prefixLength;\n                        auto drvFsPrefix = UtilFindMount(mountInfo.c_str(), cwd.c_str(), false, &prefixLength);\n                        if (!drvFsPrefix.empty())\n                        {\n                            showDrvfsNotification = true;\n                            g_drvFsUsageEnabled = false;\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    return std::make_pair(executable, showDrvfsNotification);\n}\n\nunsigned int StartTelemetryAgent()\n{\n    try\n    {\n        constexpr auto flushPeriod = std::chrono::minutes(30);\n\n        // The telemetry agent is only supported on VM mode.\n        if (!UtilIsUtilityVm())\n        {\n            return 1;\n        }\n\n        // Initialize logging.\n        InitializeLogging(false);\n\n        // Open and bind a netlink socket.\n        wil::unique_fd fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);\n        THROW_LAST_ERROR_IF(!fd);\n\n        sockaddr_nl address{};\n        address.nl_family = AF_NETLINK;\n        address.nl_groups = CN_IDX_PROC;\n        address.nl_pid = getpid();\n        THROW_LAST_ERROR_IF(bind(fd.get(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) < 0);\n\n        // Fill in the netlink header and connector message and send a message.\n        messageBuffer buffer{};\n        buffer.Send.netlinkHeader.nlmsg_len = sizeof(buffer.Send);\n        buffer.Send.netlinkHeader.nlmsg_type = NLMSG_DONE;\n        buffer.Send.netlinkHeader.nlmsg_pid = getpid();\n        buffer.Send.connectorMessage.id.idx = CN_IDX_PROC;\n        buffer.Send.connectorMessage.id.val = CN_VAL_PROC;\n        buffer.Send.connectorMessage.len = sizeof(buffer.Send.operation);\n        buffer.Send.operation = proc_cn_mcast_op::PROC_CN_MCAST_LISTEN;\n        auto bytes = send(fd.get(), &buffer, sizeof(buffer.Send), 0);\n        THROW_LAST_ERROR_IF(bytes != sizeof(buffer.Send));\n\n        // Set the receive timeout to 1 minute so the thread has an opportunity to flush even when no events are received.\n        timeval tv{};\n        tv.tv_sec = 10;\n        THROW_LAST_ERROR_IF(setsockopt(fd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0);\n\n        wsl::shared::SocketChannel channel({STDOUT_FILENO}, \"Telemetry\");\n\n        std::map<std::string, size_t> events;\n        std::optional<std::string> drvfsNotifyCommand;\n\n        // Schedule the next flush in 30 seconds so that some events are captured even if WSL shuts down quickly.\n        auto nextFlush = std::chrono::steady_clock::now() + std::chrono::seconds(30);\n\n        // Begin reading netlink messages.\n        for (;;)\n        {\n            memset(&buffer, 0, sizeof(buffer));\n            sockaddr_nl fromAddress{};\n            fromAddress.nl_family = AF_NETLINK;\n            fromAddress.nl_groups = CN_IDX_PROC;\n            fromAddress.nl_pid = 1;\n            socklen_t addressLength = sizeof(fromAddress);\n            bytes = TEMP_FAILURE_RETRY(recvfrom(fd.get(), &buffer, sizeof(buffer), 0, reinterpret_cast<sockaddr*>(&fromAddress), &addressLength));\n\n            if (bytes <= 0)\n            {\n                THROW_LAST_ERROR_IF(errno != ETIMEDOUT && errno != EAGAIN);\n            }\n            else\n            {\n                for (nlmsghdr* netlinkHeader = &buffer.Receive.netlinkHeader; NLMSG_OK(netlinkHeader, bytes);\n                     netlinkHeader = NLMSG_NEXT(netlinkHeader, bytes))\n                {\n                    if ((netlinkHeader->nlmsg_type == NLMSG_ERROR) || (netlinkHeader->nlmsg_type == NLMSG_OVERRUN))\n                    {\n                        break;\n                    }\n\n                    if (netlinkHeader->nlmsg_type == NLMSG_NOOP)\n                    {\n                        continue;\n                    }\n\n                    // For exec events, log app usage telemetry.\n                    auto event = reinterpret_cast<proc_event*>(((cn_msg*)NLMSG_DATA(netlinkHeader))->data);\n                    if (event->what == PROC_EVENT_EXEC)\n                    {\n                        auto [executable, showNotification] = GetProcessInformation(event->event_data.exec.process_pid);\n                        if (showNotification)\n                        {\n                            drvfsNotifyCommand = executable;\n                        }\n\n                        // Make sure the name doesn't contain a '/' so it doesn't break our message format.\n                        if (!executable.empty() && executable.find(\"/\") == std::string::npos)\n                        {\n                            auto it = events.find(executable);\n                            if (it == events.end())\n                            {\n                                events.emplace(std::move(executable), 1);\n                            }\n                            else\n                            {\n                                it->second++;\n                            }\n                        }\n                    }\n\n                    if (netlinkHeader->nlmsg_type == NLMSG_DONE)\n                    {\n                        break;\n                    }\n                }\n            }\n\n            // Regularly flush messages back to the service.\n\n            auto now = std::chrono::steady_clock::now();\n            if (drvfsNotifyCommand.has_value() || now > nextFlush)\n            {\n                if (!events.empty())\n                {\n                    std::stringstream content;\n\n                    if (drvfsNotifyCommand.has_value())\n                    {\n                        content << drvfsNotifyCommand.value() << \"/1/\";\n                    }\n\n                    for (auto [executable, count] : events)\n                    {\n                        // Having an extra ',' at the end makes parsing simpler.\n                        content << executable << '/' << count << '/';\n                    }\n\n                    wsl::shared::MessageWriter<LX_MINI_INIT_TELEMETRY_MESSAGE> message;\n                    message->ShowDrvFsNotification = drvfsNotifyCommand.has_value();\n                    message.WriteString(content.str());\n\n                    channel.SendMessage<LX_MINI_INIT_TELEMETRY_MESSAGE>(message.Span());\n\n                    events.clear();\n                    drvfsNotifyCommand = {};\n                }\n\n                nextFlush = now + flushPeriod;\n            }\n        }\n\n        return 0;\n    }\n    catch (...)\n    {\n        LOG_CAUGHT_EXCEPTION();\n        return 1;\n    }\n\n    return 0;\n}"
  },
  {
    "path": "src/linux/init/telemetry.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nunsigned int StartTelemetryAgent();\n"
  },
  {
    "path": "src/linux/init/timezone.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    timezone.c\n\nAbstract:\n\n    This file contains methods for configuring the timezone.\n\n--*/\n\n#include \"common.h\"\n#include \"util.h\"\n#include \"WslDistributionConfig.h\"\n\n#define TIMEZONE_LOCALTIME_FILE ETC_FOLDER \"localtime\"\n#define TIMEZONE_SETTING_FILE ETC_FOLDER \"timezone\"\n\nvoid UpdateTimezone(std::string_view Timezone, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine updates the instance's timezone information by creating the\n    /etc/localtime symlink and writing /etc/timezone.\n\nArguments:\n\n    Timezone - Supplies the Linux timezone.\n\n    Config - Supplies the distribution configuration.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    //\n    // If automatic timezone translation is disabled, do nothing.\n    //\n\n    if (!Config.AutoUpdateTimezone)\n    {\n        return;\n    }\n\n    if (Timezone.empty())\n    {\n        LOG_WARNING(\"Windows to Linux timezone mapping was not possible.\");\n        return;\n    }\n\n    //\n    // Construct the /etc/localtime symlink target and ensure it will exist.\n    //\n\n    std::string Target{\"/usr/share/zoneinfo/\"};\n    Target += Timezone;\n    if (access(Target.c_str(), F_OK) < 0)\n    {\n        LOG_WARNING(\"{} not found. Is the tzdata package installed?\", Target.c_str());\n        return;\n    }\n\n    //\n    // Update the /etc/localtime symlink.\n    //\n\n    if ((unlink(TIMEZONE_LOCALTIME_FILE) < 0) && (errno != ENOENT))\n    {\n        LOG_ERROR(\"unlink failed {}\", errno);\n        return;\n    }\n\n    if (symlink(Target.c_str(), TIMEZONE_LOCALTIME_FILE) < 0)\n    {\n        LOG_ERROR(\"symlink failed {}\", errno);\n        return;\n    }\n\n    //\n    // Write the contents of /etc/timezone to contain the IANA identifier.\n    //\n\n    wil::unique_fd TimezoneFile{TEMP_FAILURE_RETRY(open(TIMEZONE_SETTING_FILE, (O_CREAT | O_TRUNC | O_RDWR), 0644))};\n\n    if (!TimezoneFile)\n    {\n        LOG_ERROR(\"open({}) failed {}\", TIMEZONE_SETTING_FILE, errno);\n        return;\n    }\n\n    std::string FileContents(Timezone);\n    FileContents += '\\n';\n    if (UtilWriteStringView(TimezoneFile.get(), FileContents) < 0)\n    {\n        LOG_ERROR(\"write failed {}\", errno);\n        return;\n    }\n\n    return;\n}\nCATCH_LOG()\n\nvoid UpdateTimezone(gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config)\n\n/*++\n\nRoutine Description:\n\n    This routine processes an update timezone message.\n\nArguments:\n\n    Buffer - Supplies the message.\n\n    Config - Supplies the distribution configuration.\n\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    auto* TimezoneInfo = gslhelpers::try_get_struct<const LX_INIT_TIMEZONE_INFORMATION>(Buffer);\n    if (!TimezoneInfo)\n    {\n        LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n        return;\n    }\n\n    UpdateTimezone(wsl::shared::string::FromSpan(Buffer, TimezoneInfo->TimezoneOffset), Config);\n}\n"
  },
  {
    "path": "src/linux/init/timezone.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    timezone.h\n\nAbstract:\n\n    This file contains timezone function declarations.\n\n--*/\n\n#pragma once\n\n#include \"WslDistributionConfig.h\"\n\nvoid UpdateTimezone(std::string_view Timezone, const wsl::linux::WslDistributionConfig& Config);\n\nvoid UpdateTimezone(gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config);\n"
  },
  {
    "path": "src/linux/init/util.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    util.c\n\nAbstract:\n\n    This file utility function definitions.\n\n--*/\n\n#include <sys/mount.h>\n#include <sys/wait.h>\n#include <sys/epoll.h>\n#include <sys/utsname.h>\n#include <sys/types.h>\n#include <grp.h>\n#include <unistd.h>\n#include <sys/prctl.h>\n#include <ctype.h>\n#include <optional>\n#include <fstream>\n#include <iostream>\n#include <sstream>\n#include <regex>\n#include \"common.h\"\n#include \"wslpath.h\"\n#include \"util.h\"\n#include \"drvfs.h\"\n#include \"escape.h\"\n#include \"config.h\"\n#include \"mountutilcpp.h\"\n#include \"message.h\"\n#include \"RuntimeErrorWithSourceLocation.h\"\n#include \"SocketChannel.h\"\n#include \"Localization.h\"\n\n#define INITIAL_MESSAGE_BUFFER_SIZE (0x1000)\n\n#define PLAN9_RDR_PREFIX \"\\\\\\\\wsl.localhost\\\\\"\n#define PLAN9_RDR_COMPAT_PREFIX \"\\\\\\\\wsl$\\\\\"\n\n#define WSLENV_ENV \"WSLENV\"\n\n#define WSL_CGROUPS_FIELD_ENABLED (3)\n#define WSL_CGROUPS_FIELD_MAX WSL_CGROUPS_FIELD_ENABLED\n#define WSL_CGROUPS_FIELD_SEP '\\t'\n#define WSL_CGROUPS_FIELD_SUBSYSTEM (0)\n\n#define WSL_MOUNT_OPTION_SEP ','\n\nint g_IsVmMode = -1;\nstatic std::optional<int> g_CachedFeatureFlags;\nstatic sigset_t g_originalSignals;\nthread_local std::string g_threadName;\n\nnamespace wil {\n\nthread_local std::optional<std::stringstream> ScopedWarningsCollector::g_collectedWarnings;\n\n}\n\nint InteropServer::Create()\n\n/*++\n\nRoutine Description:\n\n    This routine creates an interop server unix socket and starts listening on it.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    if (!m_InteropSocketPath.empty())\n    {\n        LOG_ERROR(\"Interop server already created\");\n        return -1;\n    }\n\n    //\n    // Generate a unique name to be used for the interop socket path.\n    //\n\n    m_InteropSocketPath = std::format(WSL_INTEROP_SOCKET_FORMAT, WSL_TEMP_FOLDER, getpid(), WSL_INTEROP_SOCKET);\n\n    //\n    // Ensure the WSL temp folder exists and has the correct mode.\n    //\n\n    if (UtilMkdir(WSL_TEMP_FOLDER, WSL_TEMP_FOLDER_MODE) < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Create a unix socket to handle interop requests.\n    //\n    // N.B. This is done before the child process is created to ensure that\n    //      the socket is ready for connections.\n    //\n\n    m_InteropSocket.reset(socket(AF_UNIX, (SOCK_STREAM | SOCK_CLOEXEC), 0));\n    if (!m_InteropSocket)\n    {\n        LOG_ERROR(\"socket failed {}\", errno);\n        return -1;\n    }\n\n    sockaddr_un InteropSocketAddress{};\n    InteropSocketAddress.sun_family = AF_UNIX;\n    strncpy(InteropSocketAddress.sun_path, m_InteropSocketPath.c_str(), (sizeof(InteropSocketAddress.sun_path) - 1));\n\n    auto Result = bind(m_InteropSocket.get(), reinterpret_cast<sockaddr*>(&InteropSocketAddress), sizeof(InteropSocketAddress));\n    if (Result < 0)\n    {\n        LOG_ERROR(\"bind failed {}\", errno);\n        return -1;\n    }\n\n    Result = listen(m_InteropSocket.get(), -1);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"listen failed {}\", errno);\n        return -1;\n    }\n\n    //\n    // Ensure that any users can connect to the interop socket.\n    //\n\n    Result = chmod(m_InteropSocketPath.c_str(), 0777);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"chmod failed {}\", errno);\n        return -1;\n    }\n\n    return 0;\n}\n\nwil::unique_fd InteropServer::Accept() const\n\n/*++\n\nRoutine Description:\n\n    This routine accepts a connection on the interop server.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    The socket.\n\n--*/\n\n{\n    wil::unique_fd InteropConnection{accept4(m_InteropSocket.get(), nullptr, nullptr, SOCK_CLOEXEC)};\n    if (!InteropConnection)\n    {\n        LOG_ERROR(\"accept4 failed {}\", errno);\n    }\n\n    timeval Timeout{};\n    Timeout.tv_sec = INTEROP_TIMEOUT_SEC;\n    if (setsockopt(InteropConnection.get(), SOL_SOCKET, SO_RCVTIMEO, &Timeout, sizeof(Timeout)) < 0)\n    {\n        LOG_ERROR(\"setsockopt(SO_RCVTIMEO) failed {}\", errno);\n    }\n\n    return InteropConnection;\n}\n\nvoid InteropServer::Reset()\n{\n    if (!m_InteropSocketPath.empty())\n    {\n        unlink(m_InteropSocketPath.c_str());\n        m_InteropSocketPath = {};\n    }\n}\n\nInteropServer::~InteropServer()\n{\n    Reset();\n}\n\nint UtilAcceptVsock(int SocketFd, sockaddr_vm SocketAddress, int Timeout)\n\n/*++\n\nRoutine Description:\n\n    This routine accepts a socket connection.\n\nArguments:\n\n    SocketFd - Supplies a socket file descriptor.\n\n    SocketAddress - Supplies the socket address. This is passed by value instead\n        of by reference because accept4 modifies the structure to contain the\n        address of the peer socket.\n\n    Timeout - Supplies a timeout.\n\nReturn Value:\n\n    A file descriptor representing the socket, -1 on failure.\n\n--*/\n\n{\n    //\n    // If a timeout was specified, use a pollfd to wait for the accept.\n    //\n\n    int Result = 0;\n    if (Timeout == -1)\n    {\n        pollfd PollDescriptor{SocketFd, POLLIN, 0};\n\n        while (true)\n        {\n            Result = poll(&PollDescriptor, 1, 60 * 1000);\n            if (Result < 0)\n            {\n                LOG_ERROR(\"poll({}) failed, {}\", SocketFd, errno);\n                return Result;\n            }\n            else if ((Result == 0) || ((PollDescriptor.revents & POLLIN) == 0))\n            {\n                LOG_ERROR(\"Waiting for abnormally long accept({})\", SocketFd);\n            }\n            else\n            {\n                break;\n            }\n        }\n    }\n    else\n    {\n        pollfd PollDescriptor{SocketFd, POLLIN, 0};\n        Result = poll(&PollDescriptor, 1, Timeout);\n        if ((Result <= 0) || ((PollDescriptor.revents & POLLIN) == 0))\n        {\n            errno = ETIMEDOUT;\n            Result = -1;\n        }\n    }\n\n    if (Result != -1)\n    {\n        socklen_t SocketAddressSize = sizeof(SocketAddress);\n        Result = accept4(SocketFd, reinterpret_cast<sockaddr*>(&SocketAddress), &SocketAddressSize, SOCK_CLOEXEC);\n    }\n\n    if (Result < 0)\n    {\n        LOG_ERROR(\"accept4 failed {}\", errno);\n    }\n\n    return Result;\n}\n\nint UtilBindVsockAnyPort(struct sockaddr_vm* SocketAddress, int Type)\n\n/*++\n\nRoutine Description:\n\n    This routine creates a bound vsock socket an available port.\n\nArguments:\n\n    SocketAddress - Supplies a buffer to receive the socket address of the\n        socket.\n\n    Type - Supplies the socket type.\n\nReturn Value:\n\n    A file descriptor representing the bound socket, -1 on failure.\n\n--*/\n\n{\n    int Result;\n    socklen_t SocketAddressSize;\n    int SocketFd;\n\n    SocketFd = socket(AF_VSOCK, Type, 0);\n    if (SocketFd < 0)\n    {\n        Result = -1;\n        LOG_ERROR(\"socket failed {}\", errno);\n        goto BindVsockAnyPortExit;\n    }\n\n    memset(SocketAddress, 0, sizeof(*SocketAddress));\n    SocketAddress->svm_family = AF_VSOCK;\n    SocketAddress->svm_cid = VMADDR_CID_ANY;\n    SocketAddress->svm_port = VMADDR_PORT_ANY;\n    SocketAddressSize = sizeof(*SocketAddress);\n    Result = bind(SocketFd, (const struct sockaddr*)SocketAddress, SocketAddressSize);\n\n    if (Result < 0)\n    {\n        LOG_ERROR(\"bind failed {}\", errno);\n        goto BindVsockAnyPortExit;\n    }\n\n    //\n    // Query the socket name to get the assigned port.\n    //\n\n    Result = getsockname(SocketFd, (struct sockaddr*)SocketAddress, &SocketAddressSize);\n\n    if (Result < 0)\n    {\n        LOG_ERROR(\"getsockname failed {}\", errno);\n        goto BindVsockAnyPortExit;\n    }\n\n    Result = SocketFd;\n    SocketFd = -1;\n\nBindVsockAnyPortExit:\n    if (SocketFd != -1)\n    {\n        CLOSE(SocketFd);\n    }\n\n    return Result;\n}\n\nsize_t UtilCanonicalisePathSeparator(char* Path, char Separator)\n\n/*++\n\nRoutine Description:\n\n    This routine ensures all separators in Path use the specified separator.\n\nArguments:\n\n    Path - Supplies the path to canonicalise.\n\n    Separator - Supplies the separator character to be used.\n\nReturn Value:\n\n    The size of the new string.\n\n--*/\n\n{\n    size_t DestIndex;\n    size_t PathLength;\n    size_t SourceIndex;\n\n    DestIndex = 0;\n    SourceIndex = 0;\n    PathLength = strlen(Path);\n\n    //\n    // Iterate through the path, replacing all separators.\n    //\n\n    for (; SourceIndex < PathLength; SourceIndex++)\n    {\n        if (Path[SourceIndex] == PATH_SEP || Path[SourceIndex] == PATH_SEP_NT)\n        {\n            //\n            // Don't add a separator if previous char already is a separator.\n            // Also handle the special case where 'Path' is a UNC path (\\\\X or //X)\n            // where both separators should be kept.\n            //\n\n            if (DestIndex > 1 && Path[DestIndex - 1] == Separator)\n            {\n                continue;\n            }\n\n            Path[DestIndex] = Separator;\n        }\n        else\n        {\n            Path[DestIndex] = Path[SourceIndex];\n        }\n\n        DestIndex++;\n    }\n\n    Path[DestIndex] = '\\0';\n    return DestIndex;\n}\n\nvoid UtilCanonicalisePathSeparator(std::string& Path, char Separator)\n\n/*++\n\nRoutine Description:\n\n    This routine ensures all separators in Path use the specified separator.\n\nArguments:\n\n    Path - Supplies the path to canonicalise.\n\n    Separator - Supplies the separator character to be used.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    Path.resize(UtilCanonicalisePathSeparator(Path.data(), Separator));\n}\n\nwil::unique_fd UtilConnectToInteropServer(std::optional<pid_t> Pid)\n\n/*++\n\nRoutine Description:\n\n    This routine connects to the interop server of the current client process.\n\nArguments:\n\n    Pid - Supplies an optional process ID to connect to.\n\nReturn Value:\n\n    A file descriptor representing the connected socket, -1 on failure.\n\n--*/\n\ntry\n{\n    char* InteropSocketPath;\n    std::string Path;\n    if (Pid.has_value())\n    {\n        Path = std::format(WSL_INTEROP_SOCKET_FORMAT, WSL_TEMP_FOLDER, Pid.value(), WSL_INTEROP_SOCKET);\n        InteropSocketPath = Path.data();\n    }\n    else\n    {\n        //\n        // Query the interop server environment variable. If the process does not\n        // have the environment variable, or if the socket does not exists, search through parent process tree for an\n        // interop server.\n        //\n\n        InteropSocketPath = getenv(WSL_INTEROP_ENV);\n        if (InteropSocketPath == nullptr || (access(InteropSocketPath, F_OK) < 0 && errno == ENOENT))\n        {\n            pid_t Parent = getppid();\n            while (Parent > 0)\n            {\n                Path = std::format(WSL_INTEROP_SOCKET_FORMAT, WSL_TEMP_FOLDER, Parent, WSL_INTEROP_SOCKET);\n                if (access(Path.c_str(), F_OK) == 0)\n                {\n                    InteropSocketPath = Path.data();\n                    break;\n                }\n\n                Parent = UtilGetPpid(Parent);\n            }\n\n            if (InteropSocketPath == nullptr)\n            {\n                return {};\n            }\n\n            setenv(WSL_INTEROP_ENV, InteropSocketPath, 1);\n        }\n    }\n\n    //\n    // Connect to the server and return the connected socket to the caller.\n    //\n\n    return UtilConnectUnix(InteropSocketPath);\n}\nCATCH_RETURN_ERRNO()\n\nwil::unique_fd UtilConnectUnix(const char* Path)\n\n/*++\n\nRoutine Description:\n\n    This routine connects to the specified unix socket path.\n\nArguments:\n\n    Path - Supplies the path of the unix socket.\n\nReturn Value:\n\n    The connected socket, or a default-initialized value on failure.\n\n--*/\n\n{\n    wil::unique_fd Socket{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)};\n    if (!Socket)\n    {\n        LOG_ERROR(\"socket failed {}\", errno);\n        return {};\n    }\n\n    sockaddr_un SocketAddress{};\n    SocketAddress.sun_family = AF_UNIX;\n    strncpy(SocketAddress.sun_path, Path, sizeof(SocketAddress.sun_path) - 1);\n    if (connect(Socket.get(), reinterpret_cast<sockaddr*>(&SocketAddress), sizeof(SocketAddress)) < 0)\n    {\n        LOG_ERROR(\"connect failed {}\", errno);\n        return {};\n    }\n\n    return Socket;\n}\n\nwil::unique_fd UtilConnectVsock(unsigned int Port, bool CloseOnExec, std::optional<int> SocketBuffer, const std::source_location& Source) noexcept\n\n/*++\n\nRoutine Description:\n\n    This routine connects to a vsock with the specified port.\n\nArguments:\n\n    Port - Supplies the port to connect to.\n\n    CloseOnExec - Supplies a boolean specifying if the socket file descriptor should be closed on exec.\n\n    SocketBuffer - Optionally supplies the size to use for the socket send and receive buffers.\n\n    Source - Supplies the caller location.\n\nReturn Value:\n\n    A file descriptor representing the connected socket, -1 on failure.\n\n--*/\n\n{\n    int Type = SOCK_STREAM;\n    WI_SetFlagIf(Type, SOCK_CLOEXEC, CloseOnExec);\n    wil::unique_fd SocketFd{socket(AF_VSOCK, Type, 0)};\n    if (!SocketFd)\n    {\n        LOG_ERROR(\"socket failed {} (from: {})\", errno, Source);\n        return {};\n    }\n\n    //\n    // Set the socket connect timeout.\n    //\n\n    timeval Timeout{};\n    Timeout.tv_sec = LX_INIT_HVSOCKET_TIMEOUT_SECONDS;\n    if (setsockopt(SocketFd.get(), AF_VSOCK, SO_VM_SOCKETS_CONNECT_TIMEOUT, &Timeout, sizeof(Timeout)) < 0)\n    {\n        LOG_ERROR(\"setsockopt SO_VM_SOCKETS_CONNECT_TIMEOUT failed {}, (from: {})\", errno, Source);\n        return {};\n    }\n\n    if (SocketBuffer)\n    {\n        int BufferSize = *SocketBuffer;\n        if (setsockopt(SocketFd.get(), SOL_SOCKET, SO_SNDBUF, &BufferSize, sizeof(BufferSize)) < 0)\n        {\n            LOG_ERROR(\"setsockopt(SO_SNDBUF, {}) failed {}, (from: {})\", BufferSize, errno, Source);\n            return {};\n        }\n\n        if (setsockopt(SocketFd.get(), SOL_SOCKET, SO_RCVBUF, &BufferSize, sizeof(BufferSize)) < 0)\n        {\n            LOG_ERROR(\"setsockopt(SO_RCVBUF, {}) failed {}, (from: {})\", BufferSize, errno, Source);\n            return {};\n        }\n    }\n\n    sockaddr_vm SocketAddress{};\n    SocketAddress.svm_family = AF_VSOCK;\n    SocketAddress.svm_cid = VMADDR_CID_HOST;\n    SocketAddress.svm_port = Port;\n    if (connect(SocketFd.get(), (const struct sockaddr*)&SocketAddress, sizeof(SocketAddress)) < 0)\n    {\n        LOG_ERROR(\"connect port {} failed {} (from: {})\", Port, errno, Source);\n        return {};\n    }\n\n    return SocketFd;\n}\n\nint UtilCreateProcessAndWait(const char* const File, const char* const Argv[], int* Status, const std::map<std::string, std::string>& Env)\n\n/*++\n\nRoutine Description:\n\n    This routine creates a helper process from init and waits for it to exit.\n\nArguments:\n\n    File - Supplies the file name to execute.\n\n    Argv - Supplies the arguments for the command.\n\n    Status - Supplies an optional pointer that receives the exit status of the\n        process.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n{\n    pid_t ChildPid;\n    int Result;\n    int LocalStatus;\n    pid_t WaitResult;\n\n    Result = -1;\n\n    //\n    // Init needs to not ignore SIGCHLD so it can wait for this child.\n    //\n\n    auto restore = signal(SIGCHLD, SIG_DFL);\n\n    ChildPid = fork();\n    if (ChildPid < 0)\n    {\n        LOG_ERROR(\"Forking child process for {} failed with {}\", File, errno);\n        goto CreateProcessAndWaitEnd;\n    }\n\n    if (ChildPid == 0)\n    {\n        //\n        // Restore default signal dispositions for the child process.\n        //\n\n        if (UtilSetSignalHandlers(g_SavedSignalActions, false) < 0 || UtilRestoreBlockedSignals() < 0)\n        {\n            exit(-1);\n        }\n\n        //\n        // Set environment variables.\n        //\n\n        for (const auto& e : Env)\n        {\n            setenv(e.first.c_str(), e.second.c_str(), 1);\n        }\n\n        //\n        // Invoke the executable.\n        //\n\n        // This explicit cast is okay for now because:\n        // 1. execv function is guaranteed to not alter the arguments\n        // 2. In sometime we probably will replace most of these string constants\n        // with std::string anyway.\n        execv(File, const_cast<char* const*>(Argv));\n        LOG_ERROR(\"execv({}) failed with {}\", File, errno);\n        exit(-1);\n    }\n\n    if (Status == nullptr)\n    {\n        Status = &LocalStatus;\n    }\n\n    //\n    // TODO_LX: Do we need a timeout when waiting for the process?\n    //\n\n    WaitResult = waitpid(ChildPid, Status, 0);\n    if (WaitResult < 0)\n    {\n        LOG_ERROR(\"Waiting for {} failed with {}\", File, errno);\n        goto CreateProcessAndWaitEnd;\n    }\n\n    if (*Status != 0)\n    {\n        LOG_ERROR(\"{} failed with status {:#x}\", File, *Status);\n        goto CreateProcessAndWaitEnd;\n    }\n\n    Result = 0;\n\nCreateProcessAndWaitEnd:\n\n    //\n    // Restore the disposition of SIGCHLD.\n    //\n\n    signal(SIGCHLD, restore);\n\n    return Result;\n}\n\nint UtilExecCommandLine(const char* CommandLine, std::string* Output, int ExpectedStatus, bool PrintError)\n\n/*++\n\nRoutine Description:\n\n    This routine runs the command and optionally returns the output.\n\nArguments:\n\n    CommandLine - Supplies the command line of the process to launch.\n\n    Output - Supplies an optional pointer to a std::string to receive the output of the command.\n        If no buffer is provided the output will appear in stdout.\n\n    ExpectedStatus - Supplies the expected return status of the command.\n\n    PrintError - Supplies a boolean that specifies if an error should be printed if the process does not return the expected status.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    //\n    // Exec the command and read the output.\n    //\n\n    wil::unique_file Pipe{popen(CommandLine, \"re\")};\n    if (!Pipe)\n    {\n        LOG_ERROR(\"popen({}) failed {}\", CommandLine, errno);\n        return -1;\n    }\n\n    std::vector<char> Buffer(1024);\n    int Result = -1;\n    while (fgets(Buffer.data(), Buffer.size(), Pipe.get()) != nullptr)\n    {\n        if (Output)\n        {\n            (*Output) += Buffer.data();\n            if (Result < 0)\n            {\n                goto ErrorExit;\n            }\n        }\n        else\n        {\n            fputs(Buffer.data(), stdout);\n        }\n    }\n\n    if (ferror(Pipe.get()))\n    {\n        Result = -1;\n        LOG_ERROR(\"fgets failed {}\", errno);\n        goto ErrorExit;\n    }\n\n    Result = 0;\n\nErrorExit:\n    if (Pipe)\n    {\n        Result = pclose(Pipe.release());\n        if (Result == -1)\n        {\n            LOG_ERROR(\"pclose failed {}\", errno);\n        }\n        else\n        {\n            Result = UtilProcessChildExitCode(Result, CommandLine, ExpectedStatus, PrintError);\n        }\n    }\n\n    return Result;\n}\n\nstd::string UtilFindMount(const char* MountInfoFile, const char* Path, bool WinPath, size_t* PrefixLength)\n\n/*++\n\nRoutine Description:\n\n    This routine parses the /proc/self/mountinfo file to find a mount that\n    matches the specified path.\n\n    N.B. The caller is responsible for freeing the returned replacement prefix\n         buffer.\n\nArguments:\n\n    MountInfoFile - Supplies the path to the mountinfo file.\n\n    Path - Supplies the path.\n\n    WinPath - Supplies a value that indicates whether the path is a Windows\n        path.\n\n    PrefixLength - Supplies a pointer which receives the length of the prefix\n        that should be stripped from the path.\n\nReturn Value:\n\n    The replacement prefix on success, or an empty string on failure.\n\n--*/\n\ntry\n{\n    char** MatchField;\n    char** ReplacementField;\n\n    mountutil::MountEnum MountEnum{MountInfoFile};\n    if (WinPath != false)\n    {\n        MatchField = &MountEnum.Current().Source;\n        ReplacementField = &MountEnum.Current().MountPoint;\n    }\n    else\n    {\n        MatchField = &MountEnum.Current().MountPoint;\n        ReplacementField = &MountEnum.Current().Source;\n    }\n\n    std::string FoundReplacement;\n    size_t FoundPrefixLength = 0;\n    while (MountEnum.Next())\n    {\n        //\n        // If a mount point was previously found, and this mount point is a\n        // prefix of the path (or the previously found mount point, for Windows\n        // to Linux translation), it means that the path is not actually on\n        // the previously found mount, so discard that result.\n        //\n        // For example:\n        // - When translating /mnt/c/foo/bar, first /mnt/c is found, but a\n        //   later entry indicates /mnt/c/foo is also a mount point (e.g. using\n        //   tmpfs). This means /mnt/c/foo/bar is not on the /mnt/c mount.\n        // - When translating C:\\foo, first /mnt/c is found. A later entry\n        //   indicates /mnt itself is a mount point, making the earlier /mnt/c\n        //   mount unreachable.\n        //\n        // TODO_LX: This doesn't catch the case when translating C:\\foo\\bar and\n        //          /mnt/c/foo is a mount point. Handling that is more complicated.\n        //\n\n        if (!FoundReplacement.empty())\n        {\n            const char* LinuxPath = WinPath ? FoundReplacement.c_str() : Path;\n            size_t LinuxPrefixLength = UtilIsPathPrefix(LinuxPath, MountEnum.Current().MountPoint, false);\n            if (LinuxPrefixLength > 0)\n            {\n                FoundReplacement.resize(0);\n            }\n        }\n\n        //\n        // For Plan 9, parse the actual mount source from the superblock options.\n        // For virtiofs, parse the mount source from source (for example drvfsC or drvfsaC).\n        // If the file system isn't Plan 9, virtiofs, or DrvFs, skip this mount.\n        //\n\n        std::string MountSource;\n        if (strcmp(MountEnum.Current().FileSystemType, PLAN9_FS_TYPE) == 0)\n        {\n            MountSource = UtilParsePlan9MountSource(MountEnum.Current().SuperOptions);\n            if (MountSource.empty())\n            {\n                continue;\n            }\n\n            MountEnum.Current().Source = MountSource.data();\n        }\n        else if (strcmp(MountEnum.Current().FileSystemType, VIRTIO_FS_TYPE) == 0)\n        {\n            MountSource = QueryVirtiofsMountSource(MountEnum.Current().Source);\n            if (MountSource.empty())\n            {\n                continue;\n            }\n\n            MountEnum.Current().Source = MountSource.data();\n        }\n        else if (strcmp(MountEnum.Current().FileSystemType, DRVFS_FS_TYPE) == 0)\n        {\n            //\n            // The mount source is a Windows path and may use forward slashes;\n            // flip them to backslashes.\n            //\n\n            UtilCanonicalisePathSeparator(MountEnum.Current().Source, PATH_SEP_NT);\n        }\n        else\n        {\n            continue;\n        }\n\n        //\n        // Strip the trailing backslash if present.\n        //\n\n        size_t Length = strlen(MountEnum.Current().Source);\n        if ((Length > 0) && (MountEnum.Current().Source[Length - 1] == PATH_SEP_NT))\n        {\n            MountEnum.Current().Source[Length - 1] = '\\0';\n        }\n\n        //\n        // For bind mounts, use the concatenation of the mount source and root\n        // of the mount as the mount source string.\n        //\n\n        std::string CombinedMountSource;\n        if (strcmp(MountEnum.Current().Root, \"/\") != 0)\n        {\n            CombinedMountSource += MountEnum.Current().Source;\n            CombinedMountSource += MountEnum.Current().Root;\n            UtilCanonicalisePathSeparator(CombinedMountSource, PATH_SEP_NT);\n            MountEnum.Current().Source = CombinedMountSource.data();\n        }\n\n        //\n        // Check if the match field is a prefix of the path.\n        //\n        // N.B. For Windows paths, only matches longer than the existing match\n        //      are considered. This is because Windows mounts aren't\n        //      guaranteed to be in order and NTFS directory mounts should be\n        //      preferred over plain drive letter mounts if they match.\n        //\n\n        Length = UtilIsPathPrefix(Path, *MatchField, WinPath);\n        if ((Length == 0) || ((WinPath != false) && (Length < FoundPrefixLength)))\n        {\n            continue;\n        }\n\n        //\n        // Store the length of the prefix so the caller can strip it from the\n        // string.\n        //\n\n        FoundPrefixLength = Length;\n\n        //\n        // Store the replacement.\n        //\n\n        FoundReplacement = *ReplacementField;\n\n        //\n        // Continue searching the file even if a mount has been found, since\n        // newer mounts could shadow this one or be a nested mount.\n        //\n    }\n\n    if (!FoundReplacement.empty() && PrefixLength != nullptr)\n    {\n        *PrefixLength = FoundPrefixLength;\n    }\n\n    return FoundReplacement;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\nstd::optional<std::string> UtilGetEnv(const char* Name, char* Environment)\n\n/*++\n\nRoutine Description:\n\n    This queries the specified environment variable.\n\nArguments:\n\n    Name - Supplies the name to query.\n\n    Environment - Supplies an environment block to search. If NULL is provided\n        the environment of the calling process is used.\n\nReturn Value:\n\n    The value of the specified environment variable, NULL if there is no match.\n\n--*/\n\n{\n    char* Current;\n    size_t Length;\n    size_t NameLength;\n    std::optional<std::string> Value;\n\n    if (Environment == nullptr)\n    {\n        const auto* EnvValue = getenv(Name);\n        if (EnvValue != nullptr)\n        {\n            Value = std::string{EnvValue};\n        }\n    }\n    else\n    {\n        NameLength = strlen(Name);\n        for (size_t Index = 0;;)\n        {\n            Current = Environment + Index;\n            Length = strlen(Current);\n            if (Length == 0)\n            {\n                break;\n            }\n\n            if ((strncmp(Current, Name, NameLength) == 0) && (Current[NameLength] == '='))\n            {\n                Value = std::string{&Current[NameLength + 1]};\n                break;\n            }\n\n            Index += Length + 1;\n        }\n    }\n\n    return Value;\n}\n\nstd::string UtilGetEnvironmentVariable(const char* Name)\n\n/*++\n\nRoutine Description:\n\n    This queries the specified environment variable. If the value does not exist it gets the value from\n    the WSL interop server.\n\nArguments:\n\n    Name - Supplies the name to query.\n\nReturn Value:\n\n    The value of the specified environment variable if there is a match.\n\n--*/\n\ntry\n{\n    //\n    // Try to get the environment variable value. If it is not set, query the interop server for value.\n    //\n\n    std::vector<gsl::byte> Buffer;\n    auto Value = getenv(Name);\n    if (Value == nullptr)\n    {\n        wsl::shared::SocketChannel channel{UtilConnectToInteropServer(), \"InteropClient\"};\n        if (channel.Socket() < 0)\n        {\n            return {};\n        }\n\n        wsl::shared::MessageWriter<LX_INIT_QUERY_ENVIRONMENT_VARIABLE> Message(LxInitMessageQueryEnvironmentVariable);\n        Message.WriteString(Name);\n\n        channel.SendMessage<LX_INIT_QUERY_ENVIRONMENT_VARIABLE>(Message.Span());\n\n        //\n        // Read a response, this will contain the environment variable value if it exists.\n        //\n\n        Value = channel.ReceiveMessage<LX_INIT_QUERY_ENVIRONMENT_VARIABLE>().Buffer;\n\n        //\n        // Set the environment variable for future queries.\n        //\n\n        if (setenv(Name, Value, 1) < 0)\n        {\n            LOG_ERROR(\"setenv({}, {}, 1) failed {}\", Name, Value, errno);\n        }\n    }\n\n    return Value;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\nint UtilGetFeatureFlags()\n\n/*++\n\nRoutine Description:\n\n    This routine gets the feature flags, either directly, from an environment\n    variable, or by querying it from the init process.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    The feature flags.\n\n--*/\n\n{\n    //\n    // If feature flags are already known, return them.\n    //\n\n    if (g_CachedFeatureFlags)\n    {\n        return *g_CachedFeatureFlags;\n    }\n\n    //\n    // Check if the environment variable is present.\n    //\n    // N.B. This is used for processes launched directly from init (e.g.\n    //      mount.drvfs during initial configuration), because they may not be\n    //      able to connect to init.\n    //\n\n    int FeatureFlags = LxInitFeatureNone;\n    const char* FeatureFlagEnv = getenv(WSL_FEATURE_FLAGS_ENV);\n    if (FeatureFlagEnv != nullptr)\n    {\n        FeatureFlags = strtol(FeatureFlagEnv, nullptr, 16);\n    }\n    else\n    {\n        //\n        // Query init for the value. If an error occurs, just return no features.\n        //\n\n        wsl::shared::SocketChannel channel{UtilConnectUnix(WSL_INIT_INTEROP_SOCKET), \"wslinfo\"};\n        if (channel.Socket() < 0)\n        {\n            return FeatureFlags;\n        }\n\n        MESSAGE_HEADER Message;\n        Message.MessageType = LxInitMessageQueryFeatureFlags;\n        Message.MessageSize = sizeof(Message);\n\n        channel.SendMessage(Message);\n        FeatureFlags = channel.ReceiveMessage<RESULT_MESSAGE<int32_t>>().Result;\n    }\n\n    UtilSetFeatureFlags(FeatureFlags, FeatureFlagEnv == nullptr);\n    return FeatureFlags;\n}\n\nvoid UtilSetFeatureFlags(int FeatureFlags, bool UpdateEnv)\n\n/*++\n\nRoutine Description:\n\n    This routine sets the feature flags and updates the cached value and environment variable.\n\nArguments:\n\n    FeatureFlags - Supplies the feature flags to set.\n\n    UpdateEnv - Supplies a boolean that indicates whether the environment variable should be updated.\n\nReturn Value:\n\n    None.\n\n--*/\n\ntry\n{\n    g_CachedFeatureFlags = FeatureFlags;\n    if (UpdateEnv)\n    {\n        auto FeatureFlagsString = std::format(\"{:x}\", FeatureFlags);\n        if (setenv(WSL_FEATURE_FLAGS_ENV, FeatureFlagsString.c_str(), 1) < 0)\n        {\n            LOG_ERROR(\"setenv({}, {}, 1) failed {}\", WSL_FEATURE_FLAGS_ENV, FeatureFlagsString, errno);\n        }\n    }\n}\nCATCH_LOG()\n\nstd::optional<LX_MINI_INIT_NETWORKING_MODE> UtilGetNetworkingMode(void)\n\n/*++\n\nRoutine Description:\n\n    This routine queries the networking mode from the init process.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    The networking mode if successful, std::nullopt otherwise.\n\n--*/\n\ntry\n{\n    wsl::shared::SocketChannel channel{UtilConnectUnix(WSL_INIT_INTEROP_SOCKET), \"wslinfo\"};\n    THROW_LAST_ERROR_IF(channel.Socket() < 0);\n\n    MESSAGE_HEADER Message;\n    Message.MessageType = LxInitMessageQueryNetworkingMode;\n    Message.MessageSize = sizeof(Message);\n\n    channel.SendMessage(Message);\n\n    const auto& response = channel.ReceiveMessage<RESULT_MESSAGE<uint8_t>>();\n    auto NetworkingMode = static_cast<LX_MINI_INIT_NETWORKING_MODE>(response.Result);\n\n    THROW_ERRNO_IF(EINVAL, NetworkingMode < LxMiniInitNetworkingModeNone || NetworkingMode > LxMiniInitNetworkingModeVirtioProxy);\n\n    return NetworkingMode;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\npid_t UtilGetPpid(pid_t Pid)\n\n/*++\n\nRoutine Description:\n\n    This routine returns the parent process id of the specified process.\n\nArguments:\n\n    Pid - Supplies the process id to get the parent of.\n\nReturn Value:\n\n    The parent process id if successful, -1 otherwise.\n\n--*/\n\n{\n    //\n    // Open the /proc/[pid]/stat file.\n    //\n\n    const auto FilePath = std::format(\"/proc/{}/stat\", Pid);\n    std::ifstream File(FilePath);\n\n    std::string Line;\n    if (!File || !std::getline(File, Line))\n    {\n        return -1;\n    }\n\n    //\n    // Parse the file. Sample format: \"86 (bash) S 9\".\n    // N.B. The second entry can contain a space so we can't just use strtok.\n    //\n\n    const std::regex Pattern(\"^[0-9]+ \\\\(.*\\\\) \\\\w ([0-9]+).*\");\n    std::smatch Match;\n    if (!std::regex_match(Line, Match, Pattern) || Match.size() != 2)\n    {\n        LOG_ERROR(\"Failed to parse: {}, content: {}\", FilePath, Line);\n        return -1;\n    }\n\n    auto Result = strtol(Match.str(1).c_str(), nullptr, 10);\n    if (Result == 0)\n    {\n        LOG_ERROR(\"Failed to parse: {}, content: {}\", FilePath, Line);\n        return -1;\n    }\n\n    return Result;\n}\n\nstd::string UtilGetVmId(void)\n\n/*++\n\nRoutine Description:\n\n    This routine queries the VM ID from the init process.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    The VM ID if successful, an empty string otherwise.\n\n--*/\n\ntry\n{\n    wsl::shared::SocketChannel channel{UtilConnectUnix(WSL_INIT_INTEROP_SOCKET), \"wslinfo\"};\n    THROW_LAST_ERROR_IF(channel.Socket() < 0);\n\n    wsl::shared::MessageWriter<LX_INIT_QUERY_VM_ID> Message(LxInitMessageQueryVmId);\n    channel.SendMessage<LX_INIT_QUERY_VM_ID>(Message.Span());\n\n    return channel.ReceiveMessage<LX_INIT_QUERY_VM_ID>().Buffer;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\nvoid UtilInitGroups(const char* User, gid_t Gid)\n\n/*++\n\nRoutine Description:\n\n    This routine initializes the groups for the current process.\n    N.B. This is needed because the musl version of initgroups has a hard-coded 32 group max.\n\nArguments:\n\n    User - Supplies the user name.\n\n    Gid - Supplies the group id.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    if (initgroups(User, Gid) < 0)\n    {\n        int Count{};\n        getgrouplist(User, Gid, nullptr, &Count);\n        std::vector<gid_t> Groups(Count);\n        THROW_LAST_ERROR_IF(getgrouplist(User, Gid, Groups.data(), &Count) < 0);\n\n        THROW_LAST_ERROR_IF(setgroups(Count, Groups.data()) < 0);\n    }\n}\n\nvoid UtilInitializeMessageBuffer(std::vector<gsl::byte>& Buffer)\n\n/*++\n\nRoutine Description:\n\n    This routine ensures the supplied buffer is initialized.\n\nArguments:\n\n    Buffer - Supplies the buffer to be initialized.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    if (Buffer.size() < INITIAL_MESSAGE_BUFFER_SIZE)\n    {\n        Buffer.resize(INITIAL_MESSAGE_BUFFER_SIZE);\n    }\n}\n\nbool UtilIsAbsoluteWindowsPath(const char* Path)\n\n/*++\n\nRoutine Description:\n\n    This routine determines if the supplied path is an absolute Windows path.\n\nArguments:\n\n    Path - Supplies the path to check.\n\nReturn Value:\n\n    true if the supplied path is an absolute Windows path, false otherwise.\n\n--*/\n\n{\n    if ((strlen(Path) < 3) || (!(((Path[0] == PATH_SEP_NT || Path[0] == PATH_SEP) && (Path[1] == PATH_SEP_NT || Path[1] == PATH_SEP)) ||\n                                 (isalpha(Path[0]) && Path[1] == DRIVE_SEP_NT))))\n    {\n        return false;\n    }\n\n    return true;\n}\n\nsize_t UtilIsPathPrefix(const char* Path, const char* Prefix, bool WinPath)\n\n/*++\n\nRoutine Description:\n\n    This routine checks if one path is a prefix of another.\n\nArguments:\n\n    Path - Supplies the path to check for a prefix.\n\n    Prefix - Supplies the prefix to check for.\n\n    WinPath - Supplies a value that indicates whether Path is a Windows path.\n\nReturn Value:\n\n    The length of the prefix, or 0 if there is no match.\n\n--*/\n\n{\n    size_t PathLength;\n    size_t PrefixLength;\n    char Separator;\n\n    if (WinPath != false)\n    {\n        Separator = PATH_SEP_NT;\n    }\n    else\n    {\n        Separator = PATH_SEP;\n    }\n\n    //\n    // Check the lengths and make sure the next character is a separator.\n    //\n\n    PathLength = strlen(Path);\n    PrefixLength = strlen(Prefix);\n    if ((PathLength < PrefixLength) || ((PathLength > PrefixLength) && (Path[PrefixLength] != Separator)))\n    {\n        return 0;\n    }\n\n    //\n    // Check if the prefix matches.\n    //\n    // N.B. For Windows paths, this is done case-insensitive.\n    //\n\n    if (!wsl::shared::string::StartsWith(Path, Prefix, WinPath))\n    {\n        return 0;\n    }\n\n    return PrefixLength;\n}\n\nbool UtilIsUtilityVm(void)\n\n/*++\n\nRoutine Description:\n\n    This routine determines if the current process is running in a Utility VM or\n    an WSL1 based instance.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    true if this instance is VM Mode, false otherwise.\n\n--*/\n\n{\n    //\n    // If this process has not yet checked, inspect the Linux kernel release\n    // string.\n    //\n\n    if (g_IsVmMode == -1)\n    {\n        //\n        // The VM Mode kernel release contains \"microsoft\", the lxcore kernel\n        // contains \"Microsoft\".\n        //\n        // TODO: Come up with a different way to detect if we are running under\n        // lxcore versus a Utility VM.\n        //\n\n        struct utsname UnameBuffer;\n        memset(&UnameBuffer, 0, sizeof(UnameBuffer));\n        if (uname(&UnameBuffer) < 0)\n        {\n            FATAL_ERROR(\"uname failed {}\", errno);\n        }\n\n        g_IsVmMode = (strstr(UnameBuffer.release, \"Microsoft\") == NULL);\n    }\n\n    return g_IsVmMode;\n}\n\nint UtilListenVsockAnyPort(struct sockaddr_vm* Address, int Backlog, bool CloseOnExec)\n\n/*++\n\nRoutine Description:\n\n    This routine creates a bound and listening vsock socket an available port.\n\nArguments:\n\n    Address - Supplies a buffer to receive the socket address of the socket.\n\n    Backlog - Supplies the length of the backlog.\n\nReturn Value:\n\n    A file descriptor representing the listening socket, -1 on failure.\n\n--*/\n\n{\n    int Result;\n    int SocketFd;\n\n    int flags = SOCK_STREAM;\n    WI_SetFlagIf(flags, SOCK_CLOEXEC, CloseOnExec);\n\n    SocketFd = UtilBindVsockAnyPort(Address, flags);\n    if (SocketFd < 0)\n    {\n        Result = -1;\n        goto ListenVsockAnyPortExit;\n    }\n\n    Result = listen(SocketFd, Backlog);\n    if (Result < 0)\n    {\n        LOG_ERROR(\"listen failed {}\", errno);\n        goto ListenVsockAnyPortExit;\n    }\n\n    Result = SocketFd;\n    SocketFd = -1;\n\nListenVsockAnyPortExit:\n    if (SocketFd != -1)\n    {\n        CLOSE(SocketFd);\n    }\n\n    return Result;\n}\n\nint UtilMkdir(const char* Path, mode_t Mode)\n\n/*++\n\nRoutine Description:\n\n    This routine ensures the directory exists.\n\nArguments:\n\n    Path - Supplies the path of the directory to create.\n\n    Mode - Supplies the mode.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    if ((mkdir(Path, Mode) < 0) && (errno != EEXIST))\n    {\n        LOG_ERROR(\"mkdir({}, {:o}) failed {}\", Path, Mode, errno);\n        return -1;\n    }\n\n    return 0;\n}\n\nint UtilMkdirPath(const char* Path, mode_t Mode, bool SkipLast)\n\n/*++\n\nRoutine Description:\n\n    This routine ensures the directory exists. If necessary, all its parents\n    are created as well.\n\nArguments:\n\n    Path - Supplies the path of the directory to create.\n\n    Mode - Supplies the mode.\n\n    SkipLast - Indicates whether to skip creating the final entry in the path.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    std::string LocalPath{Path};\n    std::string::size_type Index = 0;\n\n    //\n    // Because the search is always from index + 1, the first leading / is skipped.\n    //\n\n    for (;;)\n    {\n        Index = LocalPath.find_first_of(PATH_SEP, Index + 1);\n        if (Index != std::string::npos)\n        {\n            LocalPath[Index] = '\\0';\n        }\n        else if (SkipLast)\n        {\n            break;\n        }\n\n        if (UtilMkdir(LocalPath.c_str(), Mode) < 0)\n        {\n            return -1;\n        }\n\n        if (Index == std::string::npos)\n        {\n            break;\n        }\n\n        LocalPath[Index] = PATH_SEP;\n    }\n\n    return 0;\n}\n\nint UtilMount(const char* Source, const char* Target, const char* Type, unsigned long MountFlags, const char* Options, std::optional<std::chrono::seconds> TimeoutSeconds)\n\n/*++\n\nRoutine Description:\n\n    This routine performs a mount with a retry and timeout.\n\nArguments:\n\n    Source - Supplies the source of the mount.\n\n    Target - Supplies the target of the mount.\n\n    Type - Supplies the filesystem type.\n\n    MountFlags - Supplies mount flags.\n\n    Options - Supplies the mount options.\n\n    TimeoutSeconds - Supplies an optional retry timeout in seconds.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\n{\n    //\n    // Ensure the mount point exists.\n    //\n\n    if (UtilMkdirPath(Target, 0755) < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Mount the device to the mount point.\n    //\n    // N.B. The mount operation is retried if:\n    //      - The mount source does not yet exist (hot-added devices)\n    //      - For Plan9 (9p): device is busy or not found\n    //      - For VirtioFS: invalid tag (device not ready)\n    //\n\n    try\n    {\n        if (TimeoutSeconds.has_value())\n        {\n            wsl::shared::retry::RetryWithTimeout<void>(\n                [&]() { THROW_LAST_ERROR_IF(mount(Source, Target, Type, MountFlags, Options) < 0); },\n                c_defaultRetryPeriod,\n                TimeoutSeconds.value(),\n                [&]() {\n                    errno = wil::ResultFromCaughtException();\n\n                    // Generic device not ready errors\n                    if (errno == ENXIO || errno == EIO || errno == ENOENT)\n                    {\n                        return true;\n                    }\n\n                    // Filesystem-specific device readiness errors\n                    if (Type != nullptr)\n                    {\n                        if ((strcmp(Type, PLAN9_FS_TYPE) == 0 && errno == EBUSY) || (strcmp(Type, VIRTIO_FS_TYPE) == 0 && errno == EINVAL))\n                        {\n                            return true;\n                        }\n                    }\n\n                    return false;\n                });\n        }\n        else\n        {\n            THROW_LAST_ERROR_IF(mount(Source, Target, Type, MountFlags, Options) < 0);\n        }\n    }\n    catch (...)\n    {\n        errno = wil::ResultFromCaughtException();\n        LOG_ERROR(\"mount({}, {}, {}, {:#x}, {}) failed {}\", Source, Target, Type, MountFlags, Options, errno);\n        return -errno;\n    }\n\n    return 0;\n}\n\nint UtilMountOverlayFs(const char* Target, const char* Lower, unsigned long MountFlags, std::optional<std::chrono::seconds> TimeoutSeconds)\n\n/*++\n\nRoutine Description:\n\n    This routine mounts an overlayfs at the specified location.\n\nArguments:\n\n    Target - Supplies target for the overlayfs mount.\n\n    Lower - Supplies the lower layer for the overlayfs mount. Multiple lower\n        layers can be specified by a colon-separated list.\n\n    MountFlags - Supplies mount flags for the operation.\n\n    TimeoutSeconds - Supplies an optional timeout if the mount should be retried.\n\nReturn Value:\n\n    0 on success, < 0 on failure.\n\n--*/\n\ntry\n{\n    //\n    // Set up the state required for overlayfs mount:\n    //\n    // <Target> - mount point for read/write overlayfs (this happens last)\n    // <Target>/rw - tmpfs mount for upper and work dirs\n    // <Target>/rw/upper - upper dir\n    // <Target>/rw/work - work dir\n    //\n\n    if (UtilMkdirPath(Target, 0755) < 0)\n    {\n        return -1;\n    }\n\n    auto Path = std::format(\"{}/rw\", Target);\n\n    //\n    // Create a tmpfs mount for the read/write layer\n    //\n\n    if (UtilMount(nullptr, Path.c_str(), \"tmpfs\", 0, nullptr) < 0)\n    {\n        return -1;\n    }\n\n    //\n    // Create upper and work directories.\n    //\n\n    Path = std::format(\"{}/rw/upper\", Target);\n    if (UtilMkdir(Path.c_str(), 0755) < 0)\n    {\n        return -1;\n    }\n\n    auto MountOptions = std::format(\"lowerdir={},upperdir={},\", Lower, Path);\n    Path = std::format(\"{}/rw/work\", Target);\n    if (UtilMkdir(Path.c_str(), 0755) < 0)\n    {\n        return -1;\n    }\n\n    MountOptions += std::format(\"workdir={}\", Path);\n    if (UtilMount(nullptr, Target, \"overlay\", MountFlags, MountOptions.c_str(), TimeoutSeconds) < 0)\n    {\n        return -1;\n    }\n\n    return 0;\n}\nCATCH_RETURN_ERRNO()\n\nint UtilOpenMountNamespace(void)\n\n/*++\n\nRoutine Description:\n\n    This routine opens a file descriptor to the current mount namespace.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    A file descriptor representing the current mount namespace, -1 on failure.\n\n--*/\n\n{\n    int Fd;\n\n    Fd = open(\"/proc/self/ns/mnt\", (O_RDONLY | O_CLOEXEC));\n    if (Fd < 0)\n    {\n        LOG_ERROR(\"open failed {}\", errno);\n    }\n\n    return Fd;\n}\n\nint UtilParseCgroupsLine(char* Line, char** SubsystemName, bool* Enabled)\n\n/*++\n\nRoutine Description:\n\n    This routine parses a line from the /proc/cgroups file. The output\n    buffers will be pointers into the provided line buffer and does not need to\n    be freed.\n\n    N.B. The Line buffer will be modified to insert NULL terminators.\n\nArguments:\n\n    Line - Supplies the line to parse.\n\n    SubsystemName - Supplies a buffer to receive the subsystem name.\n\n    Enabled - Supplies a buffer to receive true if the subsystem is enabled,\n        false otherwise.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    char* Current;\n    int Field;\n    int Result;\n\n    //\n    // Ignore comments.\n    //\n\n    Current = strchr(Line, '#');\n    if (Current != nullptr)\n    {\n        *Current = '\\0';\n    }\n\n    for (Field = 0, Current = Line; ((Current != nullptr) && (Field <= WSL_CGROUPS_FIELD_MAX));\n         Field += 1, Current = strchr(Current, WSL_CGROUPS_FIELD_SEP))\n    {\n        //\n        // Replace field separators with NULL characters and skip past them.\n        //\n\n        if (Field > 0)\n        {\n            *Current = '\\0';\n            Current += 1;\n        }\n\n        switch (Field)\n        {\n        case WSL_CGROUPS_FIELD_SUBSYSTEM:\n            *SubsystemName = Current;\n            break;\n\n        case WSL_CGROUPS_FIELD_ENABLED:\n            *Enabled = (*Current == '1');\n            break;\n        }\n    }\n\n    //\n    // Check if all the fields were found. If not, this is a malformed line.\n    //\n\n    if (Field < WSL_CGROUPS_FIELD_MAX)\n    {\n        Result = -1;\n        goto ParseCgroupsLineEnd;\n    }\n\n    Result = 0;\n\nParseCgroupsLineEnd:\n    return Result;\n}\n\nstd::string UtilParsePlan9MountSource(std::string_view MountOptions)\n\n/*++\n\nRoutine Description:\n\n    This routine parses the mount options to determine the actual source of a\n    a Plan 9 mount.\n\nArguments:\n\n    MountOptions - Supplies the mount options.\n\nReturn Value:\n\n    The mount source, or NULL if no valid option could be found.\n\n--*/\n\n{\n    //\n    // Search each option.\n    //\n    // N.B. The first option is always \"ro\" or \"rw\" so doesn't need to be\n    //      considered.\n    //\n\n    while (!MountOptions.empty())\n    {\n        auto Current = UtilStringNextToken(MountOptions, WSL_MOUNT_OPTION_SEP);\n        if (wsl::shared::string::StartsWith(Current, PLAN9_ANAME_DRVFS))\n        {\n            //\n            // Search for the sub path.\n            //\n\n            auto MountSource = Current.substr(PLAN9_ANAME_DRVFS_LENGTH);\n            auto Index = Current.find(PLAN9_ANAME_PATH_OPTION);\n            if (Index == std::string_view::npos)\n            {\n                break;\n            }\n\n            MountSource = Current.substr(Index + PLAN9_ANAME_PATH_OPTION_LENGTH);\n            MountSource = UtilStringNextToken(MountSource, PLAN9_ANAME_OPTION_SEP);\n\n            //\n            // The value can only be used if it starts with a drive letter or\n            // the UNC prefix \"UNC\\\"\n            //\n\n            std::string Plan9Source;\n            if (MountSource.length() > 1 && isalpha(MountSource[0]) && MountSource[1] == DRIVE_SEP_NT)\n            {\n                Plan9Source = MountSource;\n            }\n            else if (wsl::shared::string::StartsWith(MountSource, PLAN9_UNC_TRANSLATED_PREFIX))\n            {\n                Plan9Source = PLAN9_UNC_PREFIX;\n                Plan9Source += MountSource.substr(PLAN9_UNC_TRANSLATED_PREFIX_LENGTH);\n            }\n            else\n            {\n                break;\n            }\n\n            //\n            // Ensure the returned path uses Windows path separators.\n            //\n\n            UtilCanonicalisePathSeparator(Plan9Source, PATH_SEP_NT);\n            return Plan9Source;\n        }\n    }\n\n    return {};\n}\n\nstd::vector<char> UtilParseWslEnv(char* NtEnvironment)\n\n/*++\n\nRoutine Description:\n\n    This routine parses the WSLENV environment variable and constructs an\n    environment block with the resulting values.\n\nArguments:\n\n    NtEnvironment - Supplies an NT environment block. If NULL is provided,\n        the current processes's environment block is used.\n\nReturn Value:\n\n    The constructed env block.\n\n--*/\n\n{\n    std::optional<std::string> EnvList;\n    bool Reverse = false;\n\n    std::vector<char> Output;\n\n    auto Append = [&Output](const std::string_view& Content) {\n        for (auto e : Content)\n        {\n            Output.push_back(e);\n        }\n    };\n\n    Reverse = (NtEnvironment != nullptr);\n\n    //\n    // Always add WSLENV to the block.\n    //\n\n    Append(WSLENV_ENV \"=\");\n\n    EnvList = UtilGetEnv(WSLENV_ENV, NtEnvironment);\n    if (EnvList.has_value())\n    {\n        Append(EnvList.value());\n    }\n\n    Output.push_back('\\0');\n    if (EnvList.has_value())\n    {\n        //\n        // Trim any whitespace from the end of the list.\n        //\n\n        while (!EnvList->empty() && isspace(EnvList->back()))\n        {\n            EnvList->pop_back();\n        }\n\n        for (char *Sp, *EnvName = strtok_r(EnvList->data(), \":\", &Sp); EnvName != nullptr; EnvName = strtok_r(NULL, \":\", &Sp))\n        {\n            char Mode = 0;\n            bool SkipTranslation = false;\n            char* Slash = strchr(EnvName, '/');\n            if (Slash != nullptr)\n            {\n                *Slash = '\\0';\n                for (char* Flags = Slash + 1; *Flags != '\\0'; Flags++)\n                {\n                    switch (*Flags)\n                    {\n                    case 'p': // path\n                    case 'l': // path list\n                        if (Mode != 0 && Mode != 'p' && Mode != 'l')\n                        {\n                            SkipTranslation = true;\n                        }\n                        else\n                        {\n                            Mode = *Flags;\n                        }\n                        break;\n\n                    case 'u': // Win32 -> WSL translation only\n                        if (Reverse == false)\n                        {\n                            SkipTranslation = true;\n                        }\n\n                        break;\n\n                    case 'w': // WSL -> Win32 translation only\n                        if (Reverse != false)\n                        {\n                            SkipTranslation = true;\n                        }\n\n                        break;\n\n                    default:\n\n                        //\n                        // Ignore entries with an unknown flag to support future\n                        // extensibility.\n                        //\n\n                        SkipTranslation = true;\n                        break;\n                    }\n                }\n            }\n\n            auto EnvVal = UtilGetEnv(EnvName, NtEnvironment);\n            if (!SkipTranslation && EnvVal.has_value())\n            {\n                switch (Mode)\n                {\n                case 'p':\n                case 'l':\n                {\n\n                    //\n                    // Translate the path or path list.\n                    //\n\n                    auto Result = UtilTranslatePathList(EnvVal->data(), Reverse);\n                    if (Result.has_value())\n                    {\n                        EnvVal = std::move(Result.value());\n                    }\n                    else\n                    {\n                        SkipTranslation = true;\n                    }\n\n                    break;\n                }\n\n                default:\n                    break;\n                }\n            }\n\n            if (!SkipTranslation)\n            {\n                Append(std::format(\"{}={}\", EnvName, EnvVal.value_or(\"\")));\n                Output.push_back('\\0');\n            }\n        }\n    }\n\n    Output.push_back('\\0');\n\n    return Output;\n}\n\nint UtilProcessChildExitCode(int Status, const char* Name, int ExpectedStatus, bool PrintError)\n\n/*++\n\nRoutine Description:\n\n    Handles the exit status of a child process.\n\nArguments:\n\n    Status - Supplies the exit status.\n\n    Name - Supplies the process image name, for logging.\n\n    ExpectedStatus - Supplies the expected exit status.\n\n    PrintError - Supplies a boolean that specifies if an error should be printed if the process does not return the expected status.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    if (WIFEXITED(Status))\n    {\n        Status = WEXITSTATUS(Status);\n        if (Status == ExpectedStatus)\n        {\n            return 0;\n        }\n    }\n    else if (WIFSIGNALED(Status))\n    {\n        LOG_ERROR(\"{} killed by signal {}\", Name, WTERMSIG(Status));\n        return -1;\n    }\n\n    if (PrintError)\n    {\n        LOG_ERROR(\"{} returned {}\", Name, Status);\n    }\n\n    return -1;\n}\n\nssize_t UtilRead(int Fd, void* Buffer, size_t BufferSize, int Timeout)\n\n/*++\n\nRoutine Description:\n\n    This routine reads a message from the given file descriptor with an optional\n    timeout.\n\nArguments:\n\n    Fd - Supplies a file descriptor.\n\n    Buffer - Supplies a buffer.\n\n    BufferSize - Supplies the size of the buffer in bytes.\n\n    Timeout - Supplies a timeout in milliseconds.\n\nReturn Value:\n\n    The number of bytes read, -1 on failure.\n\n--*/\n\n{\n    //\n    // If a timeout was specified, use a pollfd.\n    //\n\n    ssize_t Result = 0;\n    if (Timeout != -1)\n    {\n        pollfd PollDescriptor{Fd, POLLIN, 0};\n        Result = poll(&PollDescriptor, 1, Timeout);\n        if ((Result <= 0) || ((PollDescriptor.revents & POLLIN) == 0))\n        {\n            errno = ETIMEDOUT;\n            Result = -1;\n        }\n    }\n\n    if (Result != -1)\n    {\n        Result = TEMP_FAILURE_RETRY(read(Fd, Buffer, BufferSize));\n    }\n\n    return Result;\n}\n\nssize_t UtilReadBuffer(int Fd, std::vector<gsl::byte>& Buffer, int Timeout)\n\n/*++\n\nRoutine Description:\n\n    This routine reads a message from the given file descriptor and\n    automatically grows the buffer when needed.\n\nArguments:\n\n    Fd - Supplies a file descriptor.\n\n    Buffer - Supplies a buffer; this buffer will be resized if needed.\n\n    Timeout - Supplies a timeout in milliseconds.\n\nReturn Value:\n\n    The number of bytes read, -1 on failure.\n\n--*/\n\ntry\n{\n    ssize_t Result;\n\n    UtilInitializeMessageBuffer(Buffer);\n    for (;;)\n    {\n        Result = UtilRead(Fd, Buffer.data(), Buffer.size(), Timeout);\n        if (Result < 0)\n        {\n            //\n            // When the message buffer is too small, EOVERFLOW is returned and\n            // the buffer size is doubled.\n            //\n\n            if (errno == EOVERFLOW)\n            {\n                Buffer.resize(Buffer.size() * 2);\n                continue;\n            }\n        }\n\n        break;\n    }\n\n    return Result;\n}\nCATCH_RETURN_ERRNO()\n\nstd::string UtilReadFile(FILE* File)\n\n/*++\n\nRoutine Description:\n\n    This routine reads an entire file into a buffer.\n\nArguments:\n\n    File - Supplies a open file stream.\n\nReturn Value:\n\n    A std::string that contains the contents of the file.\n\n--*/\n\n{\n    char* Line = nullptr;\n    size_t LineLength;\n    std::string output;\n\n    //\n    // Ensure the file is at the beginning of the stream.\n    //\n\n    rewind(File);\n\n    //\n    // Read the entire file into a buffer.\n    //\n\n    LineLength = 0;\n    while (getline(&Line, &LineLength, File) != -1)\n    {\n        output += Line;\n        output += '\\n';\n    }\n\n    return output;\n}\n\nstd::vector<gsl::byte> UtilReadFileRaw(const char* Path, size_t MaxSize)\n{\n    wil::unique_fd file{open(Path, O_RDONLY)};\n    THROW_LAST_ERROR_IF(!file);\n\n    constexpr auto bufferSize = 4096;\n\n    size_t offset = 0;\n    std::vector<gsl::byte> buffer;\n    while (true)\n    {\n        buffer.resize(offset + bufferSize);\n\n        int result = read(file.get(), buffer.data() + offset, bufferSize);\n        THROW_LAST_ERROR_IF(result < 0);\n\n        if (result == 0)\n        {\n            break;\n        }\n\n        offset += result;\n\n        if (offset > MaxSize)\n        {\n            LOG_ERROR(\"File \\\"{}\\\" is too big. Maximum size: {}\", Path, MaxSize);\n            THROW_ERRNO(E2BIG);\n        }\n    }\n\n    buffer.resize(offset);\n\n    return buffer;\n}\n\nstd::pair<std::optional<std::string>, std::optional<std::string>> UtilReadFlavorAndVersion(const char* Path)\ntry\n{\n    // See reference format: https://www.freedesktop.org/software/systemd/man/latest/os-release.html\n    std::ifstream file;\n    file.open(Path);\n\n    std::optional<std::string> version;\n    std::optional<std::string> flavor;\n    std::regex versionPattern(\"^VERSION_ID=\\\"?([a-zA-Z0-9\\\\-_\\\\.,]*)\\\"?$\");\n    std::regex flavorPattern(\"^ID=\\\"?([a-zA-Z0-9\\\\-_\\\\.,]*)\\\"?$\");\n\n    std::string line;\n    while (file && std::getline(file, line) && (!version.has_value() || !flavor.has_value()))\n    {\n        std::smatch match;\n        if (std::regex_search(line, match, versionPattern))\n        {\n            version = match.str(1);\n        }\n        else if (std::regex_search(line, match, flavorPattern))\n        {\n            flavor = match.str(1);\n        }\n    }\n\n    return std::make_pair(std::move(flavor), std::move(version));\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n\n    return {};\n}\n\nssize_t UtilReadMessageLxBus(int MessageFd, std::vector<gsl::byte>& Buffer, bool ShutdownOnDisconnect)\n\n/*++\n\nRoutine Description:\n\n    This routine reads a message from the server.\n\nArguments:\n\n    MessageFd - Supplies a message port file descriptor.\n\n    Buffer - Supplies a buffer; this buffer will be resized if needed.\n\n    ShutdownOnDisconnect - Supplies true for shutdown on disconnect, false for\n        _exit.\n\nReturn Value:\n\n    The number of bytes read, -1 on failure.\n\n--*/\n\ntry\n{\n    UtilInitializeMessageBuffer(Buffer);\n    wil::unique_fd Epoll{epoll_create1(EPOLL_CLOEXEC)};\n    if (!Epoll)\n    {\n        FATAL_ERROR(\"Failed to create epoll {}\", errno);\n    }\n\n    epoll_event EpollEvent{};\n    EpollEvent.events = EPOLLIN | EPOLLHUP;\n    EpollEvent.data.fd = MessageFd;\n    if (epoll_ctl(Epoll.get(), EPOLL_CTL_ADD, MessageFd, &EpollEvent) < 0)\n    {\n        FATAL_ERROR(\"Failed epoll_ctl {}\", errno);\n    }\n\n    ssize_t BytesRead;\n    for (;;)\n    {\n        //\n        // Message port read/write operations are blocking, so use an epoll to\n        // allow any incoming signals to be processed while waiting for the\n        // message.\n        //\n\n        if (TEMP_FAILURE_RETRY(epoll_wait(Epoll.get(), &EpollEvent, 1, -1)) != 1)\n        {\n            FATAL_ERROR(\"Failed epoll_wait {}\", errno);\n        }\n\n        BytesRead = TEMP_FAILURE_RETRY(read(MessageFd, Buffer.data(), Buffer.size()));\n        if (BytesRead >= static_cast<ssize_t>(sizeof(MESSAGE_HEADER)))\n        {\n            break;\n        }\n\n        if (BytesRead < 0)\n        {\n            //\n            // When the message buffer is too small, EOVERFLOW is returned and\n            // the buffer is increased. When the Windows server disconnects\n            // EPIPE is returned and the process handles the disconnect.\n            //\n\n            if (errno == EOVERFLOW)\n            {\n                auto BufferSizeNew = *(reinterpret_cast<size_t*>(Buffer.data()));\n                if (BufferSizeNew <= Buffer.size())\n                {\n                    BufferSizeNew = Buffer.size() * 2;\n                }\n\n                Buffer.resize(BufferSizeNew);\n                continue;\n            }\n\n            if (errno == EPIPE)\n            {\n                if (ShutdownOnDisconnect == false)\n                {\n                    _exit(0);\n                }\n            }\n\n            FATAL_ERROR(\"Failed to read message {}\", errno);\n            break;\n        }\n\n        FATAL_ERROR(\"Unexpected message size {}\", BytesRead);\n        break;\n    }\n\n    return BytesRead;\n}\nCATCH_RETURN_ERRNO()\n\nint UtilRestoreBlockedSignals()\n{\n    return sigprocmask(SIG_SETMASK, &g_originalSignals, nullptr);\n}\n\nint UtilSaveBlockedSignals(const sigset_t& SignalMask)\n{\n    return sigprocmask(SIG_BLOCK, &SignalMask, &g_originalSignals);\n}\n\nint UtilSaveSignalHandlers(struct sigaction* SavedSignalActions)\n\n/*++\n\nRoutine Description:\n\n    This routine saves all settable signal handlers except SIGHUP.\n\nArguments:\n\n    SavedSignalActions - Supplies an array to save default signal actions.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    for (unsigned int Index = 1; Index < _NSIG; Index += 1)\n    {\n        switch (Index)\n        {\n        case SIGKILL:\n        case SIGSTOP:\n        case SIGCONT:\n        case SIGHUP:\n        case 32:\n        case 33:\n        case 34:\n            continue;\n        }\n\n        if (sigaction(Index, NULL, &SavedSignalActions[Index]) < 0)\n        {\n            FATAL_ERROR(\"sigaction ({}) query failed {}\", Index, errno);\n        }\n    }\n\n    return 0;\n}\n\nint UtilSetSignalHandlers(struct sigaction* SavedSignalActions, bool Ignore)\n\n/*++\n\nRoutine Description:\n\n    This routine sets all settable signal handlers except SIGHUP to the given\n    handler.\n\nArguments:\n\n    SavedSignalActions - Supplies an array of signal handlers to set.\n\n    Ignore - Supplies a boolean specifying if signals should be ignored.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    struct sigaction SignalAction;\n\n    for (unsigned int Index = 1; Index < _NSIG; Index += 1)\n    {\n        switch (Index)\n        {\n        case SIGKILL:\n        case SIGSTOP:\n        case SIGCONT:\n        case SIGHUP:\n        case 32:\n        case 33:\n        case 34:\n            continue;\n        }\n\n        memcpy(&SignalAction, &SavedSignalActions[Index], sizeof(SignalAction));\n        if (Ignore != false)\n        {\n            SignalAction.sa_handler = SIG_IGN;\n        }\n\n        if (sigaction(Index, &SignalAction, NULL) < 0)\n        {\n            FATAL_ERROR(\"sigaction ({}) set failed {}\", Index, errno);\n        }\n    }\n\n    return 0;\n}\n\nvoid UtilSetThreadName(const char* Name)\n{\n    g_threadName = Name;\n\n    if (prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(Name), 0, 0, 0) < 0)\n    {\n        LOG_ERROR(\"prctl failed {}\", errno);\n    }\n}\n\nvoid UtilSocketShutdown(int Fd, int How)\n\n/*++\n\nRoutine Description:\n\n    This routine cleanly shuts down a socket.\n\nArguments:\n\n    Fd - Supplies a socket file descriptor.\n\n    How - Supplies the type of shutdown.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    if (shutdown(Fd, How) < 0)\n    {\n        LOG_ERROR(\"shutdown({}) failed {}\", How, errno);\n    }\n}\n\nbool UtilSizeTAdd(size_t Left, size_t Right, size_t* Out)\n\n/*++\n\nRoutine Description:\n\n    This routine checks if overflow will occur when adding two size_t values and\n    if overflow is not possible, adds the values.\n\nArguments:\n\n    Left - Supplies the first value.\n\n    Right - Supplies the second value.\n\n    Out - Supplies a pointer to receive sum on success.\n\nReturn Value:\n\n    true if addition was done without overflow, false otherwise.\n\n--*/\n\n{\n    bool Success = false;\n    if (Right > (Right + Left))\n    {\n        goto SizeTAddExit;\n    }\n\n    *Out = Left + Right;\n    Success = true;\n\nSizeTAddExit:\n    return Success;\n}\n\nstd::string_view UtilStringNextToken(std::string_view& View, std::string_view Separators)\n\n/*++\n\nRoutine Description:\n\n    This routine extracts the next token identified by one of the specified\n    separators.\n\nArguments:\n\n    View - Supplies the string view to tokenize. On return, this parameter is\n        modified to contain the remaining portion of the string after the\n        separator, or an empty string if no separator was found.\n\n    Separators - Supplies the separators that appear between the tokens.\n\nReturn Value:\n\n    The contents of the string up to the next separator, or the entire string\n    if no separator was found.\n\n--*/\n\n{\n    std::string_view Result;\n    auto Pos = View.find_first_of(Separators);\n    if (Pos == std::string_view::npos)\n    {\n        Result = View;\n        View = {};\n    }\n    else\n    {\n        Result = View.substr(0, Pos);\n        View = View.substr(Pos + 1);\n    }\n\n    return Result;\n}\n\nstd::string_view UtilStringNextToken(std::string_view& View, char Separator)\n\n/*++\n\nRoutine Description:\n\n    This routine extracts the next token identified by the specified separator.\n\nArguments:\n\n    View - Supplies the string view to tokenize. On return, this parameter is\n        modified to contain the remaining portion of the string after the\n        separator, or an empty string if no separator was found.\n\n    Separators - Supplies the separator that appears between the tokens.\n\nReturn Value:\n\n    The contents of the string up to the next separator, or the entire string\n    if no separator was found.\n\n--*/\n\n{\n    std::string_view Result;\n    auto Pos = View.find_first_of(Separator);\n    if (Pos == std::string_view::npos)\n    {\n        Result = View;\n        View = {};\n    }\n    else\n    {\n        Result = View.substr(0, Pos);\n        View = View.substr(Pos + 1);\n    }\n\n    return Result;\n}\n\nstd::optional<std::string> UtilTranslatePathList(char* PathList, bool IsNtPathList)\n\n/*++\n\nRoutine Description:\n\n    This routine translates a semicolon-separated list of NT paths into a\n    colon-separated list of Linux paths.\n\nArguments:\n\n    PathList - Supplies the semicolon-separated list of paths to translate.\n\n    IsNtPathList - Supplies a boolean specifying if the list contains NT-style\n        paths.\n\n    LxPath - Supplies a buffer to receive an allocated string with the\n        translated path list.\n\nReturn Value:\n\n    0 on success, -error for failure.\n\n--*/\n\n{\n    char Mode;\n    const char* SourceSeparator;\n    char TargetSeparator;\n    std::string TranslatedList;\n\n    if (IsNtPathList != false)\n    {\n        Mode = TRANSLATE_MODE_UNIX;\n        SourceSeparator = \";\";\n        TargetSeparator = ':';\n    }\n    else\n    {\n        Mode = TRANSLATE_MODE_WINDOWS;\n        SourceSeparator = \":\";\n        TargetSeparator = ';';\n    }\n\n    //\n    // Translate each element in the list. If an element in the list fails to\n    // translate, ignore it.\n    //\n\n    for (char *Sp, *Path = strtok_r(PathList, SourceSeparator, &Sp); Path != nullptr; Path = strtok_r(NULL, SourceSeparator, &Sp))\n    {\n        //\n        // Skip relative Windows paths.\n        //\n\n        if ((Mode == TRANSLATE_MODE_UNIX) && (!UtilIsAbsoluteWindowsPath(Path)))\n        {\n            continue;\n        }\n\n        std::string TranslatedPath = WslPathTranslate(Path, 0, Mode);\n        if (TranslatedPath.empty())\n        {\n            auto WarningMessage = wsl::shared::Localization::MessageFailedToTranslate(Path);\n            if (wil::ScopedWarningsCollector::CanCollectWarning())\n            {\n                EMIT_USER_WARNING(std::move(WarningMessage));\n            }\n            else\n            {\n                LOG_WARNING(\"{}\", WarningMessage);\n            }\n\n            continue;\n        }\n\n        if (!TranslatedList.empty())\n        {\n            TranslatedList += TargetSeparator;\n        }\n\n        TranslatedList += TranslatedPath;\n    }\n\n    if (TranslatedList.empty())\n    {\n        return {};\n    }\n\n    return TranslatedList;\n}\n\nstd::string UtilWinPathTranslate(const char* Path, bool Reverse)\n\n/*++\n\nRoutine Description:\n\n    This routine translates an absolute Linux path or an absolute\n    Windows path into the other.\n\nArguments:\n\n    Path - Supplies the path to translate.\n\n    Reverse - Supplies a bool, if set perform translation from Windows->Linux\n        path; otherwise, translation from Linux->Windows path.\n\nReturn Value:\n\n    0 on success, -error for general failure.\n\n--*/\n\ntry\n{\n    size_t PathLength;\n    size_t PrefixLength;\n    char* Suffix;\n    size_t SuffixLength;\n    size_t TranslatedLength;\n    size_t TranslatedSuffixLength;\n\n    //\n    // Find if there is a DrvFs or Plan 9 mount for the specified path.\n    //\n\n    auto PrefixReplacement = UtilFindMount(MOUNT_INFO_FILE, Path, Reverse, &PrefixLength);\n    if (PrefixReplacement.empty())\n    {\n        //\n        // The path is not part of a DrvFs or Plan 9 mount, so the path should\n        // be translated to use the plan9 redirector.\n        //\n\n        return UtilWinPathTranslateInternal(Path, Reverse);\n    }\n\n    //\n    // If translating Linux to Windows, see if any characters need to be\n    // escaped.\n    //\n\n    PathLength = strlen(Path);\n    SuffixLength = PathLength - PrefixLength;\n    if (Reverse == false)\n    {\n        //\n        // If the suffix is empty and the replacement prefix is just a drive\n        // letter, make space to append a backslash.\n        //\n\n        if ((SuffixLength == 0) && (PrefixReplacement.length() == 2) && (PrefixReplacement[1] == DRIVE_SEP_NT))\n        {\n            TranslatedSuffixLength = 1;\n        }\n        else\n        {\n            TranslatedSuffixLength = EscapePathForNtLength(&Path[PrefixLength]);\n        }\n    }\n    else\n    {\n        TranslatedSuffixLength = SuffixLength;\n    }\n\n    //\n    // Construct the new path out of the replacement prefix and the remainder\n    // of the path, escaping it if necessary.\n    //\n\n    std::string TranslatedPath{PrefixReplacement};\n    TranslatedLength = PrefixReplacement.length() + TranslatedSuffixLength;\n    TranslatedPath.resize(TranslatedLength, '\\0');\n    Suffix = &TranslatedPath[PrefixReplacement.length()];\n    if (TranslatedSuffixLength != SuffixLength)\n    {\n        //\n        // If the suffix is empty and the replacement prefix is just a drive\n        // letter, append a backslash. Otherwise, escape and append the\n        // suffix.\n        //\n\n        if ((SuffixLength == 0) && (TranslatedSuffixLength == 1))\n        {\n            *Suffix = PATH_SEP_NT;\n        }\n        else\n        {\n            EscapePathForNt(&Path[PrefixLength], Suffix);\n        }\n    }\n    else\n    {\n        memcpy(Suffix, &Path[PrefixLength], SuffixLength);\n\n        //\n        // Make sure the translated path uses the correct separators.\n        //\n        // N.B. This is done by the escape method if escaping is necessary.\n        //\n\n        UtilCanonicalisePathSeparator(Suffix, Reverse ? PATH_SEP : PATH_SEP_NT);\n    }\n\n    return TranslatedPath;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\nstd::string UtilWinPathTranslateInternal(const char* Path, bool Reverse)\n\n/*++\n\nRoutine Description:\n\n    This routine translates an absolute Linux path or an absolute\n    Windows path into the other.\n\nArguments:\n\n    Path - Supplies the path to translate.\n\n    Reverse - Supplies a bool, if set perform translation from Windows->Linux\n        path; otherwise, translation from Linux->Windows path.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\ntry\n{\n    //\n    // Get the distribution name from the environment variable.\n    //\n\n    const auto DistributionName = UtilGetEnvironmentVariable(WSL_DISTRO_NAME_ENV);\n    if (DistributionName.empty())\n    {\n        return {};\n    }\n\n    //\n    // Construct a prefix (\\\\wsl.localhost\\DistributionName).\n    //\n\n    std::string Prefix{PLAN9_RDR_PREFIX};\n    Prefix += DistributionName;\n\n    //\n    // For Windows to Linux translation, concatenate the prefix and the escaped\n    // version of the path. For Linux to Windows translation, ensure the path\n    // begins with the prefix, remove the prefix, and unescape the path.\n    //\n\n    std::string TranslatedPath{};\n    if (Reverse == false)\n    {\n        TranslatedPath += Prefix;\n        const size_t EscapedPathLength = EscapePathForNtLength(Path);\n        std::string EscapedPath(EscapedPathLength, '\\0');\n        EscapePathForNt(Path, EscapedPath.data());\n        TranslatedPath += EscapedPath;\n    }\n    else\n    {\n        auto PrefixLength = Prefix.length();\n        if (!wsl::shared::string::StartsWith(Path, Prefix, true))\n        {\n            //\n            // Check the old \\\\wsl$ prefix if it's not \\\\wsl.localhost.\n            //\n\n            std::string CompatPrefix{PLAN9_RDR_COMPAT_PREFIX};\n            CompatPrefix += DistributionName;\n            if (!wsl::shared::string::StartsWith(Path, CompatPrefix, true))\n            {\n                return {};\n            }\n\n            PrefixLength = CompatPrefix.length();\n        }\n\n        Path += PrefixLength;\n        if (strlen(Path) == 0)\n        {\n            TranslatedPath += PATH_SEP;\n        }\n        else\n        {\n            TranslatedPath += Path;\n\n            //\n            // Canonicalize the path separators and unescape the string.\n            //\n\n            UtilCanonicalisePathSeparator(TranslatedPath.data(), PATH_SEP);\n            UnescapePathInplace(TranslatedPath.data());\n        }\n    }\n\n    return TranslatedPath;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\nssize_t UtilWriteBuffer(int Fd, gsl::span<const gsl::byte> Buffer)\n\n/*++\n\nRoutine Description:\n\n    This routine writes an entire buffer to the given file descriptor.\n\nArguments:\n\n    Fd - Supplies a file descriptor.\n\n    Buffer - Supplies the buffer to write.\n\nReturn Value:\n\n    The total number of bytes written, -1 on failure.\n\n--*/\n\n{\n    return UtilWriteBuffer(Fd, Buffer.data(), Buffer.size());\n}\n\nssize_t UtilWriteBuffer(int Fd, const void* Buffer, size_t BufferSize)\n\n/*++\n\nRoutine Description:\n\n    This routine writes an entire buffer to the given file descriptor.\n\nArguments:\n\n    Fd - Supplies a file descriptor.\n\n    Buffer - Supplies a buffer pointer.\n\n    BufferSize - Supplies the buffer size.\n\nReturn Value:\n\n    The total number of bytes written, -1 on failure.\n\n--*/\n\n{\n    ssize_t BytesWritten;\n    ssize_t Result;\n    ssize_t TotalBytesWritten;\n    auto* Offset = static_cast<const char*>(Buffer);\n\n    Result = -1;\n    TotalBytesWritten = 0;\n    do\n    {\n        BytesWritten = TEMP_FAILURE_RETRY(write(Fd, Offset, BufferSize));\n        if (BytesWritten < 0)\n        {\n            goto WriteBufferExit;\n        }\n\n        BufferSize -= BytesWritten;\n        Offset += BytesWritten;\n        TotalBytesWritten += BytesWritten;\n    } while (BufferSize > 0);\n\n    Result = TotalBytesWritten;\n\nWriteBufferExit:\n    return Result;\n}\n\nssize_t UtilWriteStringView(int Fd, std::string_view StringView)\n\n/*++\n\nRoutine Description:\n\n    This routine writes a string view to the given file descriptor.\n\nArguments:\n\n    Fd - Supplies a file descriptor.\n\n    StringView - Supplies the string view to write.\n\nReturn Value:\n\n    The total number of bytes written, -1 on failure.\n\n--*/\n\n{\n    return UtilWriteBuffer(Fd, StringView.data(), StringView.size());\n}\n\nstd::wstring UtilReadFileContentW(std::string_view path)\n{\n    std::wifstream file;\n    file.exceptions(std::ifstream::failbit | std::ifstream::badbit);\n    file.open(path);\n\n    return {std::istreambuf_iterator<wchar_t>(file), {}};\n}\n\nstd::string UtilReadFileContent(std::string_view path)\n{\n    std::ifstream file;\n    file.exceptions(std::ifstream::failbit | std::ifstream::badbit);\n    file.open(path);\n\n    return {std::istreambuf_iterator<char>(file), {}};\n}\n\nuint16_t UtilWinAfToLinuxAf(uint16_t WinAddressFamily)\n{\n    uint16_t LinuxAddressFamily = AF_UNSPEC;\n\n    switch (WinAddressFamily)\n    {\n    case 2:\n        LinuxAddressFamily = AF_INET;\n        break;\n    case 23:\n        LinuxAddressFamily = AF_INET6;\n        break;\n    }\n\n    return LinuxAddressFamily;\n}\n\nint WriteToFile(const char* Path, const char* Content, int permissions)\n\n/*++\n\nRoutine Description:\n\n    Write content to the specified file.\n\nArguments:\n\n    Path - Supplies the path to the file to write.\n\n    Content - Supplies the content to be written to the file.\n\nReturn Value:\n\n    0 on success, -1 on failure.\n\n--*/\n\n{\n    wil::unique_fd Fd{open(Path, (O_WRONLY | O_CLOEXEC | O_CREAT), permissions)};\n    if (!Fd)\n    {\n        int errnoPrev = errno;\n        LOG_ERROR(\"open({}) failed {}\", Path, errno);\n        errno = errnoPrev;\n        return -1;\n    }\n\n    std::string_view Buffer{Content};\n    auto Result = UtilWriteStringView(Fd.get(), Buffer);\n    if (Result != Buffer.size())\n    {\n        int errnoPrev = errno;\n        LOG_ERROR(\"write({}, {}) failed {} {}\", Path, Content, Result, errno);\n        errno = errnoPrev;\n        return -1;\n    }\n\n    return 0;\n}\n\nint ProcessCreateProcessMessage(wsl::shared::SocketChannel& channel, gsl::span<gsl::byte> Buffer)\n{\n    auto* Message = gslhelpers::try_get_struct<CREATE_PROCESS_MESSAGE>(Buffer);\n    if (!Message)\n    {\n        LOG_ERROR(\"Unexpected message size {}\", Buffer.size());\n        return -1;\n    }\n\n    auto sendResult = [&](unsigned long Result) { channel.SendResultMessage<int32_t>(Result); };\n\n    sockaddr_vm SocketAddress{};\n    wil::unique_fd ListenSocket{UtilListenVsockAnyPort(&SocketAddress, 1, false)};\n    THROW_LAST_ERROR_IF(!ListenSocket);\n\n    sendResult(SocketAddress.svm_port);\n\n    // Always return the execution result, since the service expects it\n    int execResult = -1;\n    auto sendExecResult = wil::scope_exit([&]() { sendResult(execResult); });\n\n    const char* Path = wsl::shared::string::FromSpan(Buffer, Message->PathIndex);\n    const char* Arguments = wsl::shared::string::FromSpan(Buffer, Message->CommandLineIndex);\n\n    // Note: this makes the assumption that no empty arguments are in the message\n    std::vector<const char*> ArgumentArray;\n    while (*Arguments != '\\0')\n    {\n        ArgumentArray.emplace_back(Arguments);\n        Arguments += strlen(Arguments) + 1;\n    }\n    ArgumentArray.emplace_back(nullptr);\n\n    auto ControlPipe = wil::unique_pipe::create(O_CLOEXEC);\n\n    const int ChildPid = UtilCreateChildProcess(\"CreateChildProcess\", [&]() {\n        try\n        {\n            wil::unique_fd ProcessSocket{UtilAcceptVsock(ListenSocket.get(), SocketAddress, SESSION_LEADER_ACCEPT_TIMEOUT_MS)};\n            THROW_LAST_ERROR_IF(!ProcessSocket);\n\n            THROW_LAST_ERROR_IF(dup2(ProcessSocket.get(), STDIN_FILENO) < 0);\n            THROW_LAST_ERROR_IF(dup2(ProcessSocket.get(), STDOUT_FILENO) < 0);\n            execv(Path, (char* const*)(ArgumentArray.data()));\n\n            // If this point is reached, an error needs to be reported back since execv() failed.\n            THROW_LAST_ERROR();\n        }\n        catch (...)\n        {\n            auto error = wil::ResultFromCaughtException();\n            LOG_ERROR(\"Command execution failed: {}\", errno);\n\n            if (write(ControlPipe.write().get(), &error, sizeof(error)) != sizeof(error))\n            {\n                LOG_ERROR(\"Failed to write command execution status: {}\", errno);\n            }\n        }\n    });\n\n    THROW_LAST_ERROR_IF(ChildPid < 0);\n    ControlPipe.write().reset();\n\n    int ReadResult = TEMP_FAILURE_RETRY(read(ControlPipe.read().get(), &execResult, sizeof(execResult)));\n    THROW_LAST_ERROR_IF(ReadResult < 0);\n\n    // If the pipe closed without data, then exec() was successful\n    if (ReadResult == 0)\n    {\n        execResult = 0;\n    }\n    else if (execResult == sizeof(execResult))\n    {\n        // Otherwise, return the error code to the service\n        execResult = abs(execResult);\n    }\n    else\n    {\n        execResult = EINVAL;\n    }\n\n    return 0;\n}"
  },
  {
    "path": "src/linux/init/util.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    util.h\n\nAbstract:\n\n    This file contains utility function declarations.\n\n--*/\n\n#pragma once\n\n#include <sys/socket.h>\n#include <linux/vm_sockets.h>\n#include <lxwil.h>\n#include <array>\n#include <gsl/gsl>\n#include <gslhelpers.h>\n#include <chrono>\n#include <functional>\n#include <optional>\n#include <thread>\n#include <map>\n#include <future>\n#include <filesystem>\n#include <vector>\n#include <source_location>\n#include \"lxinitshared.h\"\n#include \"lxdef.h\"\n#include \"common.h\"\n\nnamespace wsl::shared {\nclass SocketChannel;\n}\n\nnamespace wsl::linux {\nstruct WslDistributionConfig;\n}\n\n#define CGROUP_MOUNTPOINT \"/sys/fs/cgroup\"\n#define CGROUP2_DEVICE \"cgroup2\"\n#define MOUNT_COMMAND \"/bin/mount\"\n#define MOUNT_FSTAB_ARG \"-a\"\n#define MOUNT_INTERNAL_ONLY_ARG \"-i\"\n#define MOUNT_OPTIONS_ARG \"-o\"\n#define MOUNT_TYPES_ARG \"-t\"\n\n#define LDCONFIG_COMMAND \"/sbin/ldconfig\"\n\n#define PLAN9_ANAME_OPTION \"aname=\"\n#define PLAN9_ANAME_DRVFS PLAN9_ANAME_OPTION LX_INIT_UTILITY_VM_DRVFS_SHARE_NAME\n#define PLAN9_ANAME_DRVFS_LENGTH (sizeof(PLAN9_ANAME_DRVFS) - 1)\n#define PLAN9_ANAME_OPTION_SEP ';'\n#define PLAN9_ANAME_PATH_OPTION \"path=\"\n#define PLAN9_ANAME_PATH_OPTION_LENGTH (sizeof(PLAN9_ANAME_PATH_OPTION) - 1)\n#define PLAN9_UNC_PREFIX \"\\\\\\\\\"\n#define PLAN9_UNC_TRANSLATED_PREFIX \"UNC\\\\\"\n#define PLAN9_UNC_TRANSLATED_PREFIX_LENGTH (sizeof(PLAN9_UNC_TRANSLATED_PREFIX) - 1)\n\n#define PLAN9_FS_TYPE \"9p\"\n#define VIRTIO_FS_TYPE \"virtiofs\"\n\n#define PATH_SEP '/'\n#define PATH_SEP_NT '\\\\'\n#define DRIVE_SEP_NT ':'\n\n#define WSL_DISTRO_NAME_ENV \"WSL_DISTRO_NAME\"\n#define WSL_INTEROP_ENV \"WSL_INTEROP\"\n#define WSL_DRVFS_ELEVATED_ENV \"WSL_DRVFS_ELEVATED\"\n#define WSL_FEATURE_FLAGS_ENV \"WSL_FEATURE_FLAGS\"\n#define WSL_INTEROP_SOCKET \"interop\"\n#define WSL_INTEROP_SOCKET_FORMAT \"{}/{}_{}\"\n#define WSL_TEMP_FOLDER RUN_FOLDER \"/WSL\"\n#define WSL_TEMP_FOLDER_MODE 0777\n#define WSL_INIT_INTEROP_SOCKET WSL_TEMP_FOLDER \"/1_\" WSL_INTEROP_SOCKET\n\n#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))\n\nconstexpr auto c_defaultRetryPeriod = std::chrono::milliseconds{10};\nconstexpr auto c_defaultRetryTimeout = std::chrono::seconds{15};\n\nclass InteropServer\n{\npublic:\n    InteropServer() = default;\n    ~InteropServer();\n    InteropServer(const InteropServer&) = delete;\n    InteropServer& operator=(const InteropServer&) = delete;\n\n    InteropServer(InteropServer&& other) noexcept :\n        m_InteropSocketPath(std::move(other.m_InteropSocketPath)), m_InteropSocket(std::move(other.m_InteropSocket))\n    {\n    }\n\n    int Create();\n\n    wil::unique_fd Accept() const;\n\n    int Socket() const\n    {\n        return m_InteropSocket.get();\n    }\n\n    const char* Path() const\n    {\n        return m_InteropSocketPath.c_str();\n    }\n\n    void Reset();\n\nprivate:\n    std::string m_InteropSocketPath{};\n    wil::unique_fd m_InteropSocket;\n};\n\nint UtilAcceptVsock(int SocketFd, sockaddr_vm Address, int Timeout = -1);\n\nint UtilBindVsockAnyPort(struct sockaddr_vm* SocketAddress, int Type);\n\nsize_t UtilCanonicalisePathSeparator(char* Path, char Separator);\n\nvoid UtilCanonicalisePathSeparator(std::string& Path, char Separator);\n\nwil::unique_fd UtilConnectToInteropServer(std::optional<pid_t> Pid = {});\n\nwil::unique_fd UtilConnectUnix(const char* Path);\n\nwil::unique_fd UtilConnectVsock(\n    unsigned int Port, bool CloseOnExec, std::optional<int> SocketBuffer = {}, const std::source_location& Source = std::source_location::current()) noexcept;\n\n// Needs to be declared before UtilCreateChildProcess().\nvoid UtilSetThreadName(const char* Name);\n\ntemplate <typename TMethod>\nint UtilCreateChildProcess(const char* ChildName, TMethod&& ChildFunction, std::optional<int> CloneFlags = {})\n\n/*++\n\nRoutine Description:\n\n    This routine create child process to run the specified function.\n\nArguments:\n\n    ChildName - Supplies the child thread name.\n\n    ChildFunction - Supplies a function to be executed in the child process.\n\n    CloneFlags - Supplies an optional value containing flags to use for the clone syscall.\n        If no flags are specified, fork is used instead.\n\nReturn Value:\n\n    The pid of the child process on success, -1 on failure. The child process does not return.\n\n--*/\n\n{\n    int ChildPid;\n\n    if (CloneFlags)\n    {\n        ChildPid = CLONE(CloneFlags.value());\n    }\n    else\n    {\n        ChildPid = fork();\n    }\n\n    if (ChildPid < 0)\n    {\n        LOG_ERROR(\"{} for {} failed {}\", CloneFlags ? \"clone\" : \"fork\", ChildName, errno);\n        return -1;\n    }\n    else if (ChildPid > 0)\n    {\n        return ChildPid;\n    }\n\n    try\n    {\n        UtilSetThreadName(ChildName);\n        ChildFunction();\n    }\n    CATCH_LOG()\n\n    _exit(1);\n}\n\nint UtilCreateProcessAndWait(const char* File, const char* const Argv[], int* Status = nullptr, const std::map<std::string, std::string>& Env = {});\n\ntemplate <typename TMethod>\nvoid UtilCreateWorkerThread(const char* Name, TMethod&& ThreadFunction)\n{\n    std::promise<void> Promise;\n    std::thread([ThreadFunction = std::move(ThreadFunction), &Promise, Name]() mutable {\n        try\n        {\n            UtilSetThreadName(Name);\n\n            int Result = unshare(CLONE_FS);\n            Promise.set_value();\n            THROW_LAST_ERROR_IF(Result < 0);\n\n            ThreadFunction();\n        }\n        CATCH_LOG()\n    }).detach();\n\n    // Wait for the thread to unshare the filesystem so the next call to setns can succeed.\n    Promise.get_future().wait();\n}\n\nint UtilExecCommandLine(const char* CommandLine, std::string* Output = nullptr, int ExpectedStatus = 0, bool PrintError = true);\n\nstd::string UtilFindMount(const char* MountInfoFile, const char* Path, bool WinPath, size_t* PrefixLength);\n\nstd::optional<std::string> UtilGetEnv(const char* Name, char* Environment);\n\nstd::string UtilGetEnvironmentVariable(const char* Name);\n\nint UtilGetFeatureFlags();\n\nvoid UtilSetFeatureFlags(int FeatureFlags, bool UpdateEnv = true);\n\nstd::optional<LX_MINI_INIT_NETWORKING_MODE> UtilGetNetworkingMode(void);\n\npid_t UtilGetPpid(pid_t Pid);\n\nstd::string UtilGetVmId(void);\n\nvoid UtilInitGroups(const char* User, gid_t Gid);\n\nvoid UtilInitializeMessageBuffer(std::vector<gsl::byte>& Buffer);\n\nbool UtilIsAbsoluteWindowsPath(const char* Path);\n\nsize_t UtilIsPathPrefix(const char* Path, const char* Prefix, bool WinPath);\n\nbool UtilIsUtilityVm(void);\n\nint UtilListenVsockAnyPort(struct sockaddr_vm* Address, int Backlog, bool CloseOnExec = true);\n\nint UtilMkdir(const char* Path, mode_t Mode);\n\nint UtilMkdirPath(const char* Path, mode_t Mode, bool SkipLast = false);\n\nint UtilMount(const char* Source, const char* Target, const char* Type, unsigned long MountFlags, const char* Options, std::optional<std::chrono::seconds> TimeoutSeconds = {});\n\nint UtilMountOverlayFs(const char* Target, const char* Lower, unsigned long MountFlags = 0, std::optional<std::chrono::seconds> TimeoutSeconds = {});\n\nint UtilOpenMountNamespace(void);\n\nint UtilParseCgroupsLine(char* Line, char** SubsystemName, bool* Enabled);\n\nstd::string UtilParsePlan9MountSource(std::string_view MountOptions);\n\nstd::vector<char> UtilParseWslEnv(char* NtEnvironment);\n\nint UtilProcessChildExitCode(int Status, const char* Name, int ExpectedStatus = 0, bool PrintError = true);\n\nssize_t UtilRead(int Fd, void* Buffer, size_t BufferSize, int Timeout = -1);\n\nssize_t UtilReadBuffer(int Fd, std::vector<gsl::byte>& Buffer, int Timeout = -1);\n\nstd::string UtilReadFile(FILE* File);\n\nstd::vector<gsl::byte> UtilReadFileRaw(const char* Path, size_t MaxSize);\n\nstd::pair<std::optional<std::string>, std::optional<std::string>> UtilReadFlavorAndVersion(const char* Path);\n\nssize_t UtilReadMessageLxBus(int MessageFd, std::vector<gsl::byte>& Buffer, bool ShutdownOnDisconnect);\n\nint UtilRestoreBlockedSignals();\n\nint UtilSaveBlockedSignals(const sigset_t& NewMask);\n\nint UtilSaveSignalHandlers(struct sigaction* SavedSignalActions);\n\nint UtilSetSignalHandlers(struct sigaction* SavedSignalActions, bool Ignore);\n\nvoid UtilSocketShutdown(int Fd, int How);\n\nbool UtilSizeTAdd(size_t Left, size_t Right, size_t* Out);\n\nstd::string_view UtilStringNextToken(std::string_view& View, std::string_view Separators);\n\nstd::string_view UtilStringNextToken(std::string_view& View, char Separator);\n\nstd::optional<std::string> UtilTranslatePathList(char* PathList, bool IsNtPathList);\n\nstd::string UtilWinPathTranslate(const char* Path, bool Reverse);\n\nstd::string UtilWinPathTranslateInternal(const char* Path, bool Reverse);\n\nssize_t UtilWriteBuffer(int Fd, gsl::span<const gsl::byte> Buffer);\n\nssize_t UtilWriteBuffer(int Fd, const void* Buffer, size_t BufferSize);\n\nssize_t UtilWriteStringView(int Fd, std::string_view StringView);\n\nstd::wstring UtilReadFileContentW(std::string_view path);\n\nstd::string UtilReadFileContent(std::string_view path);\n\nuint16_t UtilWinAfToLinuxAf(uint16_t AddressFamily);\n\nint WriteToFile(const char* Path, const char* Content, int permissions = 0644);\n\nint ProcessCreateProcessMessage(wsl::shared::SocketChannel& channel, gsl::span<gsl::byte> Buffer);"
  },
  {
    "path": "src/linux/init/waitablevalue.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include <optional>\n#include <mutex>\n#include <condition_variable>\n\n/**\n * @brief Class that contains a value T that can contain a single value and\n * blocks until post or get can be satisfied.\n *\n * @tparam Value stored in this class.\n */\ntemplate <typename T>\nstruct WaitableValue\n{\npublic:\n    /**\n     * @brief Store value the value. Blocks until the value can be stored.\n     *\n     * @param[in] value Value to be stored.\n     */\n    void post(T& value)\n    {\n        std::unique_lock lck(m_mtx);\n        while (m_value.has_value())\n        {\n            m_cv.wait(lck);\n        }\n        m_value = value;\n        m_cv.notify_all();\n    }\n\n    /**\n     * @brief Retrieve the value. Blocks until a value is available.\n     *\n     * @return Value that was previously stored.\n     */\n    T get()\n    {\n        std::unique_lock lck(m_mtx);\n        while (!m_value.has_value())\n        {\n            m_cv.wait(lck);\n        }\n        auto return_value = m_value.value();\n        m_value.reset();\n        m_cv.notify_all();\n        return return_value;\n    }\n\n    /**\n     * @brief Attempt to retrieve the value with timeout.\n     *\n     * @param[in] timeout Duration to wait before returning empty.\n     * @return Either the value or a std::nullopt on timeout.\n     */\n    template <typename duration>\n    std::optional<T> try_get(duration timeout)\n    {\n        std::unique_lock lck(m_mtx);\n        while (!m_value.has_value())\n        {\n            if (m_cv.wait_for(lck, timeout) == std::cv_status::timeout)\n            {\n                return std::nullopt;\n            }\n        }\n        auto return_value = m_value.value();\n        m_value.reset();\n        m_cv.notify_all();\n        return {return_value};\n    }\n\nprivate:\n    std::mutex m_mtx;\n    std::condition_variable m_cv;\n    std::optional<T> m_value;\n};\n"
  },
  {
    "path": "src/linux/init/wslinfo.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslinfo.cpp\n\nAbstract:\n\n    This file wslpath function definitions.\n\n--*/\n\n#include \"common.h\"\n#include <iostream>\n#include <assert.h>\n#include \"getopt.h\"\n#include \"util.h\"\n#include \"wslpath.h\"\n#include \"wslinfo.h\"\n#include \"lxinitshared.h\"\n#include \"defs.h\"\n#include \"Localization.h\"\n#include \"CommandLine.h\"\n#include \"../../shared/inc/lxinitshared.h\"\n\nenum class WslInfoMode\n{\n    GetNetworkingMode,\n    MsalProxyPath,\n    WslVersion,\n    VMId\n};\n\nint WslInfoEntry(int Argc, char* Argv[])\n\n/*++\n\nRoutine Description:\n\n    This routine is the entrypoint for the wslinfo binary.\n\nArguments:\n\n    Argc - Supplies the argument count.\n\n    Argv - Supplies the command line arguments.\n\nReturn Value:\n\n    0 on success, 1 on failure.\n\n--*/\n\n{\n    using namespace wsl::shared;\n\n    constexpr auto Usage = std::bind(Localization::MessageWslInfoUsage, Localization::Options::Default);\n\n    std::optional<WslInfoMode> Mode;\n    bool noNewLine = false;\n\n    ArgumentParser parser(Argc, Argv);\n\n    parser.AddArgument(UniqueSetValue<WslInfoMode, WslInfoMode::GetNetworkingMode>{Mode, Usage}, WSLINFO_NETWORKING_MODE);\n    parser.AddArgument(UniqueSetValue<WslInfoMode, WslInfoMode::MsalProxyPath>{Mode, Usage}, WSLINFO_MSAL_PROXY_PATH);\n    parser.AddArgument(UniqueSetValue<WslInfoMode, WslInfoMode::WslVersion>{Mode, Usage}, WSLINFO_WSL_VERSION);\n    parser.AddArgument(UniqueSetValue<WslInfoMode, WslInfoMode::WslVersion>{Mode, Usage}, WSLINFO_WSL_VERSION_LEGACY);\n    parser.AddArgument(UniqueSetValue<WslInfoMode, WslInfoMode::VMId>{Mode, Usage}, WSLINFO_WSL_VMID);\n    parser.AddArgument(NoOp{}, WSLINFO_WSL_HELP);\n    parser.AddArgument(noNewLine, nullptr, WSLINFO_NO_NEWLINE);\n\n    try\n    {\n        parser.Parse();\n    }\n    catch (const wil::ExceptionWithUserMessage& e)\n    {\n        std::cerr << e.what() << \"\\n\";\n        return 1;\n    }\n\n    if (!Mode.has_value())\n    {\n        std::cerr << Usage() << \"\\n\";\n        return 1;\n    }\n    else if (Mode.value() == WslInfoMode::GetNetworkingMode)\n    {\n        if (UtilIsUtilityVm())\n        {\n            auto NetworkingMode = UtilGetNetworkingMode();\n            if (!NetworkingMode)\n            {\n                std::cerr << Localization::MessageFailedToQueryNetworkingMode() << \"\\n\";\n                return 1;\n            }\n\n            switch (NetworkingMode.value())\n            {\n            case LxMiniInitNetworkingModeNat:\n                std::cout << \"nat\";\n                break;\n\n            case LxMiniInitNetworkingModeBridged:\n                std::cout << \"bridged\";\n                break;\n\n            case LxMiniInitNetworkingModeMirrored:\n                std::cout << \"mirrored\";\n                break;\n\n            case LxMiniInitNetworkingModeVirtioProxy:\n                std::cout << \"virtioproxy\";\n                break;\n\n            default:\n                std::cout << \"none\";\n                break;\n            }\n        }\n        else\n        {\n            std::cout << \"wsl1\";\n        }\n    }\n    else if (Mode.value() == WslInfoMode::MsalProxyPath)\n    {\n        auto value = UtilGetEnvironmentVariable(LX_WSL2_INSTALL_PATH);\n        if (value.empty())\n        {\n            std::cerr << Localization::MessageNoValueFound() << \"\\n\";\n            return 1;\n        }\n\n        auto translatedPath = WslPathTranslate(value.data(), TRANSLATE_FLAG_ABSOLUTE, TRANSLATE_MODE_UNIX);\n        if (translatedPath.empty())\n        {\n            std::cerr << Localization::MessageFailedToTranslate(value.data()) << \"\\n\";\n            return 1;\n        }\n\n        std::cout << translatedPath << \"/msal.wsl.proxy.exe\";\n    }\n    else if (Mode.value() == WslInfoMode::WslVersion)\n    {\n        std::cout << WSL_PACKAGE_VERSION;\n    }\n    else if (Mode.value() == WslInfoMode::VMId)\n    {\n        if (UtilIsUtilityVm())\n        {\n            auto vmId = UtilGetVmId();\n            if (vmId.empty())\n            {\n                std::cerr << Localization::MessageNoValueFound() << \"\\n\";\n                return 1;\n            }\n\n            std::cout << vmId;\n        }\n        else\n        {\n            std::cout << \"wsl1\";\n        }\n    }\n    else\n    {\n        assert(false && \"Unknown WslInfoMode\");\n        return 1;\n    }\n\n    if (!noNewLine)\n    {\n        std::cout << '\\n';\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/linux/init/wslinfo.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslinfo.h\n\nAbstract:\n\n    This file contains wslinfo function declarations.\n\n--*/\n\n#pragma once\n\n#define WSLINFO_NAME \"wslinfo\"\n\n#define WSLINFO_MSAL_PROXY_PATH \"--msal-proxy-path\"\n#define WSLINFO_NETWORKING_MODE \"--networking-mode\"\n#define WSLINFO_WSL_VERSION \"--version\"\n#define WSLINFO_WSL_VERSION_LEGACY \"--wsl-version\"\n#define WSLINFO_WSL_VMID \"--vm-id\"\n#define WSLINFO_WSL_HELP \"--help\"\n#define WSLINFO_NO_NEWLINE 'n'\n\nint WslInfoEntry(int Argc, char* Argv[]);\n"
  },
  {
    "path": "src/linux/init/wslpath.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslpath.c\n\nAbstract:\n\n    This file contains the function definitions for wslpath.\n\n--*/\n\n#include \"common.h\"\n#include <sys/mount.h>\n#include <sys/utsname.h>\n#include <ctype.h>\n#include <locale.h>\n#include <stdarg.h>\n#include <signal.h>\n#include <endian.h>\n#include <lxbusapi.h>\n#include <pwd.h>\n#include \"wslpath.h\"\n\n#include \"util.h\"\n#include \"CommandLine.h\"\n\nusing namespace wsl::shared;\n\n#define INVALID_USAGE() Die(Argv[0], EINVAL, true, NULL)\n\nstd::string AbsolutePath(char* Path, char* Cwd, size_t CwdSize, bool* Relative);\n\nstd::string AbsoluteWindowsPath(char* RelativePath, const char* Cwd);\n\nint CollapsePath(char* Path, char Separator);\n\nvoid Die(const char* Argv0, int Error, bool PrintUsage, const char* Message, ...);\n\nstd::string DosToCanonicalPath(char* Path, char* UnixCwd, size_t UnixCwdSize, bool* Relative);\n\nstd::string AbsolutePath(char* Path, char* Cwd, size_t CwdSize, bool* Relative)\n\n/*++\n\nRoutine Description:\n\n    This routine converts a Unix path to an absolute path.\n\nArguments:\n\n    RelativePath - Supplies a pointer to the relative path.\n\n    Cwd - Supplies a buffer which receives the current working directory, only\n        if the path is relative.\n\n    CwdSize - Supplies the size of the Cwd buffer.\n\n    Relative - Supplies a pointer to receive whether the input path was\n        relative.\n\nReturn Value:\n\n    The absolute path as a string.\n    Returns an empty string on failure.\n\n--*/\n\ntry\n{\n    std::string NewPath{};\n    if (Path[0] != PATH_SEP)\n    {\n        if (getcwd(Cwd, CwdSize) == NULL)\n        {\n            return {};\n        }\n\n        NewPath += Cwd;\n        NewPath += PATH_SEP;\n        NewPath += Path;\n        *Relative = true;\n    }\n    else\n    {\n        NewPath = Path;\n        *Relative = false;\n    }\n\n    if (CollapsePath(NewPath.data(), PATH_SEP) < 0)\n    {\n        return {};\n    }\n\n    return NewPath;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\nstd::string AbsoluteWindowsPath(char* RelativePath, const char* Cwd)\n\n/*++\n\nRoutine Description:\n\n    This routine converts a relative Win32 path to an absolute path.\n\n    The input path must be relative.\n\nArguments:\n\n    RelativePath - Supplies a pointer to the relative path.\n\n    Cwd - Supplies a pointer to the Windows current working directory.\n\nReturn Value:\n\n    0 on success, <0 on failure.\n\n--*/\n\ntry\n{\n    std::string AbsolutePath{};\n    if (RelativePath[0] == PATH_SEP_NT)\n    {\n        //\n        // This path is relative to the drive letter root.\n        //\n\n        if (!isalpha(Cwd[0]) || Cwd[1] != ':')\n        {\n            return {};\n        }\n\n        AbsolutePath += Cwd[0];\n        AbsolutePath += Cwd[1];\n    }\n    else\n    {\n        AbsolutePath += Cwd;\n        AbsolutePath += PATH_SEP_NT;\n    }\n\n    AbsolutePath += RelativePath;\n    return AbsolutePath;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\nint CollapsePath(char* Path, char Separator)\n\n/*++\n\nRoutine Description:\n\n    Collapses, where possible, relative path segments in a Path.\n\nArguments:\n\n    Path - Supplies the path to collapse.\n\n    Separator - The directory separator.\n\nReturn Value:\n\n    0 on success, <0 on failure.\n\n--*/\n\n{\n    char* LastSegment;\n    char* NextSeparator;\n    size_t PathLength;\n    size_t Remaining;\n    int RemoveSegmentCount;\n    char* Segment;\n    size_t SegmentLength;\n\n    PathLength = strlen(Path);\n    LastSegment = Path + PathLength;\n    RemoveSegmentCount = 0;\n    for (Remaining = PathLength; Remaining > 0;)\n    {\n        NextSeparator = static_cast<char*>(memrchr(Path, Separator, Remaining));\n        if (NextSeparator == nullptr)\n        {\n            break;\n        }\n\n        Segment = NextSeparator + 1;\n        SegmentLength = Remaining - (Segment - Path);\n        Remaining -= SegmentLength + 1;\n        if (SegmentLength == 2 && Segment[0] == '.' && Segment[1] == '.')\n        {\n            //\n            // Eliminate this and the next segment.\n            //\n\n            RemoveSegmentCount++;\n        }\n        else\n        {\n            if (RemoveSegmentCount == 0 && (SegmentLength == 0 || (SegmentLength == 1 && Segment[0] == '.')))\n            {\n                RemoveSegmentCount++;\n            }\n\n            if (RemoveSegmentCount > 0)\n            {\n                memmove(Segment, LastSegment, Path + PathLength - LastSegment + 1);\n                RemoveSegmentCount--;\n            }\n\n            LastSegment = Segment;\n        }\n    }\n\n    return RemoveSegmentCount == 0 ? 0 : -EINVAL;\n}\n\nvoid Die(const char* Argv0, int Error, bool PrintUsage, const char* Message, ...)\n\n/*++\n\nRoutine Description:\n\n    This routine aborts program execution with an error message.\n\nArguments:\n\n    Argv0 - Supplies the name of the program.\n\n    Error - Supplies the error code or 0.\n\n    PrintUsage - Supplies a boolean specifying if usage should be printed.\n\n    Message - Supplies the message in printf format.\n\n    ... - Additional arguments.\n\nReturn Value:\n\n    None. Does not return.\n\n--*/\n\n{\n    va_list Args;\n\n    va_start(Args, Message);\n\n    fprintf(stderr, \"%s: \", Argv0);\n    if (Message != nullptr)\n    {\n        vfprintf(stderr, Message, Args);\n    }\n\n    if (Error != 0)\n    {\n        if (Message != nullptr)\n        {\n            fputs(\": \", stderr);\n        }\n\n        fputs(strerror(Error), stderr);\n    }\n\n    fputs(\"\\n\", stderr);\n    if (PrintUsage)\n    {\n        printf(\"%s\\n\", Localization::MessageWslPathUsage().c_str());\n    }\n\n    exit(1);\n    va_end(Args);\n}\n\nstd::string DosToCanonicalPath(char* Path, char* UnixCwd, size_t UnixCwdSize, bool* Relative)\n\n/*++\n\nRoutine Description:\n\n    This routine gets the canonical representation of a DOS path:\n    - The path is made absolute.\n    - All separators are changed to backslashes.\n    - Duplicate separators and .. components are compacted.\n    - The \\\\?\\ prefix is stripped from long paths.\n\nArguments:\n\n    Path - Supplies the path to translate.\n\n    UnixCwd - Supplies a buffer which receives the Linux current working\n        directory, only if the path was relative.\n\n    UnixCwdSize - Supplies the size of the cwd buffer.\n\n    Relative - Supplies a pointer to receive whether the input path was\n        a relative path.\n\nReturn Value:\n\n    0 on success, -error for errors.\n\n--*/\n\n{\n    std::string AbsolutePath{};\n    size_t CollapseStartIndex;\n    char* SuffixString;\n    size_t StartIndex;\n    size_t PathLength;\n    int Result = -1;\n\n    *Relative = false;\n\n    //\n    // Convert relative paths to absolute paths.\n    //\n\n    if (!UtilIsAbsoluteWindowsPath(Path))\n    {\n        if (getcwd(UnixCwd, UnixCwdSize) == NULL)\n        {\n            return {};\n        }\n\n        const auto Cwd = UtilWinPathTranslate(UnixCwd, false);\n        if (Cwd.empty())\n        {\n            return {};\n        }\n\n        AbsolutePath = AbsoluteWindowsPath(Path, Cwd.c_str());\n        if (AbsolutePath.empty())\n        {\n            return {};\n        }\n\n        Path = AbsolutePath.data();\n        *Relative = true;\n    }\n\n    PathLength = strlen(Path);\n    if (PathLength < 2)\n    {\n        return {};\n    }\n\n    if (Path[1] == DRIVE_SEP_NT && Path[2] == PATH_SEP_NT)\n    {\n        //\n        // This is a drive letter path C:\\foo -> \\??\\C:\\foo\n        //\n\n        CollapseStartIndex = 0;\n        StartIndex = 0;\n    }\n    else if (Path[0] == PATH_SEP_NT && Path[1] == PATH_SEP_NT)\n    {\n        //\n        // This is either a long path or a UNC path. If it's a long path,\n        // strip the prefix.\n        //\n\n        if (Path[2] == '?' && Path[3] == PATH_SEP_NT)\n        {\n            StartIndex = 4;\n            CollapseStartIndex = 0;\n        }\n        else\n        {\n            StartIndex = 0;\n\n            //\n            // Make sure the starting \\\\ aren't collapsed\n            //\n\n            CollapseStartIndex = 2;\n        }\n    }\n    else\n    {\n        return {};\n    }\n\n    SuffixString = Path + StartIndex;\n    Result = CollapsePath(&SuffixString[CollapseStartIndex], PATH_SEP_NT);\n    if (Result < 0)\n    {\n        return {};\n    }\n\n    return std::string(SuffixString);\n}\n\nint WslPathEntry(int Argc, char* Argv[])\n\n/*++\n\nRoutine Description:\n\n    This routine will output to stdout the NT path for a DrvFs path.\n\nArguments:\n\n    Argc - Supplies the argument count.\n\n    Argv - Supplies the command line arguments.\n\nReturn Value:\n\n    0 on success, 1 on failure.\n\n--*/\n\n{\n    int Flags = TRANSLATE_FLAG_RESOLVE_SYMLINKS;\n    std::optional<char> Mode;\n    const char* OriginalPath{};\n    std::string OutputPath;\n    int Result{};\n    char* SourcePath = nullptr;\n\n    //\n    // With the current version of musl, this has no useful effect but is also\n    // not harmful.\n    //\n\n    setlocale(LC_ALL, \"\");\n\n    ArgumentParser parser(Argc, Argv);\n\n    constexpr auto Usage = std::bind(Localization::MessageWslPathUsage, Localization::Options::Default);\n\n    parser.AddPositionalArgument(OriginalPath, 0);\n    parser.AddArgument(SetFlag<int, TRANSLATE_FLAG_ABSOLUTE>{Flags}, nullptr, TRANSLATE_MODE_ABSOLUTE);\n    parser.AddArgument(UniqueSetValue<char, TRANSLATE_MODE_UNIX>{Mode, Usage}, nullptr, TRANSLATE_MODE_UNIX);\n    parser.AddArgument(UniqueSetValue<char, TRANSLATE_MODE_WINDOWS>{Mode, Usage}, nullptr, TRANSLATE_MODE_WINDOWS);\n    parser.AddArgument(UniqueSetValue<char, TRANSLATE_MODE_MIXED>{Mode, Usage}, nullptr, TRANSLATE_MODE_MIXED);\n    parser.AddArgument(UniqueSetValue<char, TRANSLATE_MODE_HELP>{Mode, Usage}, \"--help\");\n\n    try\n    {\n        parser.Parse();\n    }\n    catch (const wil::ExceptionWithUserMessage& e)\n    {\n        fprintf(stderr, \"%s\\n\", e.what());\n        return 1;\n    }\n\n    if (OriginalPath == nullptr || Mode == TRANSLATE_MODE_HELP)\n    {\n        INVALID_USAGE();\n    }\n\n    SourcePath = strdup(OriginalPath);\n    if (SourcePath == nullptr)\n    {\n        Die(Argv[0], errno, false, nullptr);\n    }\n\n    //\n    // Translate the path.\n    //\n\n    OutputPath = WslPathTranslate(SourcePath, Flags, Mode.value_or(TRANSLATE_MODE_UNIX));\n    if (OutputPath.empty())\n    {\n        Die(Argv[0], errno, false, \"%s\", OriginalPath);\n    }\n\n    //\n    // Print the translated path and a newline.\n    //\n\n    Result = printf(\"%s\\n\", OutputPath.empty() ? \".\" : OutputPath.c_str());\n    if (Result < 0)\n    {\n        Die(Argv[0], errno, false, nullptr);\n    }\n\n    //\n    // Don't bother cleaning up since the process is exiting.\n    //\n\n    return 0;\n}\n\nstd::string WslPathTranslate(char* Path, int Flags, char Mode)\n\n/*++\n\nRoutine Description:\n\n    This routine translates an absolute or relative NT or DrvFs path.\n\nArguments:\n\n    Path - Supplies the path to translate.\n\n    Flags - Supplies flags for the operation.\n\n    Mode - Supplies the translation mode.\n\nReturn Value:\n\n    The translated path.\n\n--*/\n\n{\n    bool Absolute;\n    std::string CanonicalPath{};\n    char* OutputCwd = nullptr;\n    size_t OutputCwdLength;\n    char* RealPath = nullptr;\n    bool Relative;\n    std::string TranslatedPath{};\n    char UnixCwd[PATH_MAX];\n    std::string WindowsCwd{};\n\n    //\n    // Validate input.\n    //\n\n    if (!Path || Flags & ~(TRANSLATE_FLAG_ABSOLUTE | TRANSLATE_FLAG_RESOLVE_SYMLINKS))\n    {\n        goto WslPathTranslateExit;\n    }\n\n    Absolute = ((Flags & TRANSLATE_FLAG_ABSOLUTE) != 0);\n\n    //\n    // Validate the translation mode.\n    //\n\n    switch (Mode)\n    {\n    case TRANSLATE_MODE_UNIX:\n    case TRANSLATE_MODE_WINDOWS:\n    case TRANSLATE_MODE_MIXED:\n        break;\n\n    default:\n        goto WslPathTranslateExit;\n    }\n\n    //\n    // Get the current working directory for relative path translations.\n    //\n\n    if (getcwd(UnixCwd, sizeof(UnixCwd)) == NULL)\n    {\n        goto WslPathTranslateExit;\n    }\n\n    //\n    // Get the canonical path.\n    //\n\n    if (Mode == TRANSLATE_MODE_UNIX)\n    {\n        UtilCanonicalisePathSeparator(Path, PATH_SEP_NT);\n        OutputCwd = UnixCwd;\n        CanonicalPath = DosToCanonicalPath(Path, UnixCwd, sizeof(UnixCwd), &Relative);\n        if (CanonicalPath.empty())\n        {\n            goto WslPathTranslateExit;\n        }\n    }\n    else\n    {\n        //\n        // If the resolve symlinks flag is specified, resolve any dot entries or\n        // symlinks in the path. For paths that do not exist, use the supplied path\n        // as-is.\n        //\n\n        if ((Flags & TRANSLATE_FLAG_RESOLVE_SYMLINKS) != 0)\n        {\n            RealPath = realpath(Path, NULL);\n            if (RealPath != nullptr)\n            {\n                //\n                // Preserve trailing slashes.\n                //\n\n                std::string_view PathView = Path;\n                if (!PathView.empty() && PathView.back() == PATH_SEP)\n                {\n                    CanonicalPath = RealPath;\n                    CanonicalPath += PATH_SEP;\n                    Path = CanonicalPath.data();\n                }\n                else\n                {\n                    Path = RealPath;\n                }\n            }\n            else if (errno != ENOENT)\n            {\n                goto WslPathTranslateExit;\n            }\n        }\n\n        CanonicalPath = AbsolutePath(Path, UnixCwd, sizeof(UnixCwd), &Relative);\n        if (CanonicalPath.empty())\n        {\n            goto WslPathTranslateExit;\n        }\n\n        //\n        // Translate the cwd if it will be needed to translate back to a\n        // relative path.\n        //\n\n        if (Relative && !Absolute)\n        {\n            WindowsCwd = UtilWinPathTranslate(UnixCwd, false);\n            OutputCwd = WindowsCwd.empty() ? NULL : WindowsCwd.data();\n        }\n    }\n\n    //\n    // Perform the translation\n    //\n\n    TranslatedPath = UtilWinPathTranslate(CanonicalPath.data(), Mode == TRANSLATE_MODE_UNIX);\n    if (TranslatedPath.empty())\n    {\n        goto WslPathTranslateExit;\n    }\n\n    //\n    // Convert the absolute path back into a relative one\n    // if it is a subpath of the current directory.\n    //\n\n    if (Relative && !Absolute && OutputCwd != nullptr)\n    {\n        OutputCwdLength = strlen(OutputCwd);\n        if (strncmp(TranslatedPath.c_str(), OutputCwd, OutputCwdLength) == 0)\n        {\n            switch (TranslatedPath[OutputCwdLength])\n            {\n            case PATH_SEP_NT:\n            case PATH_SEP:\n                if (OutputCwdLength + 1 == TranslatedPath.length())\n                {\n                    // Special case for wslpath -u .\n                    TranslatedPath = \".\";\n                }\n                else\n                {\n                    TranslatedPath = TranslatedPath.substr(OutputCwdLength + 1);\n                }\n                break;\n\n            case '\\0':\n                TranslatedPath = TranslatedPath.substr(OutputCwdLength);\n                break;\n            }\n        }\n    }\n\n    if (Mode == TRANSLATE_MODE_MIXED)\n    {\n        UtilCanonicalisePathSeparator(TranslatedPath, PATH_SEP);\n    }\n\nWslPathTranslateExit:\n    free(RealPath);\n    return TranslatedPath;\n}\n"
  },
  {
    "path": "src/linux/init/wslpath.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslpath.h\n\nAbstract:\n\n    This file contains wslpath function declarations.\n\n--*/\n\n#pragma once\n\n#define WSLPATH_NAME \"wslpath\"\n\n#define TRANSLATE_FLAG_ABSOLUTE (0x1)\n#define TRANSLATE_FLAG_RESOLVE_SYMLINKS (0x2)\n\n#define TRANSLATE_MODE_ABSOLUTE 'a'\n#define TRANSLATE_MODE_UNIX 'u'\n#define TRANSLATE_MODE_WINDOWS 'w'\n#define TRANSLATE_MODE_MIXED 'm'\n#define TRANSLATE_MODE_HELP 'h'\n\nint WslPathEntry(int Argc, char* Argv[]);\n\nstd::string WslPathTranslate(char* Path, int Flags, char Mode);\n"
  },
  {
    "path": "src/linux/mountutil/CMakeLists.txt",
    "content": "set(SOURCES\n    mountflags.cpp\n    mountutil.c)\n\nset(HEADERS\n    mountutil.h\n    mountutilcpp.h)\n\nadd_linux_library(libmountutil \"${SOURCES}\" \"${HEADERS}\")\nset_target_properties(libmountutil PROPERTIES FOLDER linux)"
  },
  {
    "path": "src/linux/mountutil/mountflags.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <string>\n#include <string_view>\n#include <cerrno>\n#include <cstdio>\n#include <functional>\n#include <sys/types.h>\n#include <sys/mount.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <dirent.h>\n#include <unistd.h>\n#include <lxdef.h>\n#include <lxwil.h>\n#include \"mountutilcpp.h\"\n\nnamespace {\n\nenum class ParseFlags\n{\n    None = 0,\n    Remove = 0x1,\n    NoFail = 0x2,\n    OptionalValue = 0x4,\n};\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunused-function\"\nDEFINE_ENUM_FLAG_OPERATORS(ParseFlags);\n#pragma clang diagnostic pop\n\nstruct MountFlag\n{\n    const char* Name;\n    int MountFlags;\n    ParseFlags ParseFlags;\n};\n\n#define FLAG_WITH_NAMED_INVERSE(name, inverse, flag) \\\n    {(name), (flag), ParseFlags::None}, \\\n    { \\\n        (inverse), (flag), ParseFlags::Remove \\\n    }\n\n// \"opt\", \"noopt\" pair where the \"noopt\" version adds a flag, and \"opt\" removes it.\n#define NO_FLAG_WITH_INVERSE(name, flag) FLAG_WITH_NAMED_INVERSE(\"no\" name, name, flag)\n\n// \"opt\", \"noopt\" pair where the \"opt\" version adds a flag, and \"noopt\" removes it.\n#define FLAG_WITH_INVERSE(name, flag) FLAG_WITH_NAMED_INVERSE(name, \"no\" name, flag)\n\n// List of mount options that translate into mount flags.\n// This is based on the information in the mount(8) manpage. Note that not all options are present,\n// since this is intended to be used by a mount helper, and not all options are forwarded to the\n// helpers by /bin/mount.\nconst MountFlag c_flagMap[] = {\n    FLAG_WITH_NAMED_INVERSE(\"sync\", \"async\", MS_SYNCHRONOUS),\n    NO_FLAG_WITH_INVERSE(\"atime\", MS_NOATIME),\n    {\"defaults\", 0, ParseFlags::None},\n    NO_FLAG_WITH_INVERSE(\"dev\", MS_NODEV),\n    NO_FLAG_WITH_INVERSE(\"diratime\", MS_NODIRATIME),\n    {\"dirsync\", MS_DIRSYNC, ParseFlags::None},\n    NO_FLAG_WITH_INVERSE(\"exec\", MS_NOEXEC),\n    {\"group\", MS_NOSUID | MS_NODEV, ParseFlags::None},\n    {\"nogroup\", 0, ParseFlags::None},\n    FLAG_WITH_INVERSE(\"iversion\", MS_I_VERSION),\n    FLAG_WITH_INVERSE(\"mand\", MS_MANDLOCK),\n    {\"_netdev\", 0, ParseFlags::None},\n    {\"nofail\", 0, ParseFlags::NoFail},\n    FLAG_WITH_INVERSE(\"relatime\", MS_RELATIME),\n    FLAG_WITH_INVERSE(\"strictatime\", MS_STRICTATIME),\n    FLAG_WITH_INVERSE(\"lazytime\", MS_LAZYTIME),\n    NO_FLAG_WITH_INVERSE(\"suid\", MS_NOSUID),\n    FLAG_WITH_NAMED_INVERSE(\"silent\", \"loud\", MS_SILENT),\n    {\"owner\", MS_NODEV | MS_NOSUID, ParseFlags::None},\n    {\"noowner\", 0, ParseFlags::None},\n    {\"remount\", MS_REMOUNT, ParseFlags::None},\n    FLAG_WITH_NAMED_INVERSE(\"ro\", \"rw\", MS_RDONLY),\n    {\"user\", MS_NOEXEC | MS_NODEV | MS_NOSUID, ParseFlags::OptionalValue},\n    {\"nouser\", 0, ParseFlags::None},\n    {\"users\", MS_NOEXEC | MS_NODEV | MS_NOSUID, ParseFlags::None},\n    {\"nousers\", 0, ParseFlags::None},\n};\n\n// Determine if an option should be a flag.\n// Returns the flag information if found; otherwise, null.\nconst MountFlag* FindOption(std::string_view option)\n{\n    // Check if the option has a value.\n    bool hasValue = false;\n    auto index = option.find_first_of('=');\n    if (index != std::string_view::npos)\n    {\n        hasValue = true;\n        option = option.substr(0, index);\n    }\n\n    for (auto& flag : c_flagMap)\n    {\n        // If the option has a value, ignore the entry if it doesn't allow one.\n        if (hasValue && !WI_IsFlagSet(flag.ParseFlags, ParseFlags::OptionalValue))\n        {\n            continue;\n        }\n\n        if (option == flag.Name)\n        {\n            return &flag;\n        }\n    }\n\n    return nullptr;\n}\n\n// Retrieves the next character-separated token from a string view, returning\n// the token and updating the view to be the remainder of the string.\nstd::string_view NextToken(std::string_view& view, char separator)\n{\n    std::string_view result;\n    auto pos = view.find_first_of(separator);\n    if (pos == view.npos)\n    {\n        result = view;\n        view = {};\n    }\n    else\n    {\n        result = view.substr(0, pos);\n        view = view.substr(pos + 1);\n    }\n\n    return result;\n}\n\n} // namespace\n\nnamespace mountutil {\n\nParsedOptions MountParseFlags(std::string_view options)\n{\n    ParsedOptions result{};\n    while (!options.empty())\n    {\n        // Get the next option and check if it's a flag.\n        auto option = NextToken(options, ',');\n        auto flag = FindOption(option);\n        if (flag == nullptr)\n        {\n            // Not a flag, so append to the string options.\n            if (!result.StringOptions.empty())\n            {\n                result.StringOptions += ',';\n            }\n\n            result.StringOptions += option;\n        }\n        else\n        {\n            // Modify the mount flags.\n            if (WI_IsFlagSet(flag->ParseFlags, ParseFlags::Remove))\n            {\n                WI_ClearAllFlags(result.MountFlags, flag->MountFlags);\n            }\n            else\n            {\n                WI_SetAllFlags(result.MountFlags, flag->MountFlags);\n            }\n\n            if (WI_IsFlagSet(flag->ParseFlags, ParseFlags::NoFail))\n            {\n                result.NoFail = true;\n            }\n        }\n    }\n\n    return result;\n}\n\nint MountFilesystem(const char* source, const char* target, const char* type, const char* options)\n{\n    auto parsedOptions = MountParseFlags(options);\n    int result = mount(source, target, type, parsedOptions.MountFlags, parsedOptions.StringOptions.c_str());\n\n    // If the nofail option was specified, ENOENT on the source only must be ignored.\n    if (result < 0 && errno == ENOENT && parsedOptions.NoFail)\n    {\n        struct stat st;\n\n        // If the target exists, the error must be about the source, so ignore it.\n        if (stat(target, &st) == 0)\n        {\n            return 0;\n        }\n\n        // In case stat changed errno.\n        errno = ENOENT;\n    }\n\n    return result;\n}\n\n} // namespace mountutil\n"
  },
  {
    "path": "src/linux/mountutil/mountutil.c",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdbool.h>\n#include <string.h>\n#include <errno.h>\n#include <sys/types.h>\n#include <sys/sysmacros.h>\n#include \"mountutil.h\"\n\n#define MOUNT_FIELD_SEP \" \"\n#define MOUNT_OPTIONAL_FIELD_TERMINATOR \"-\"\n#define MOUNT_DEVICE_SEP ':'\n#define MOUNT_ESCAPE_CHAR '\\\\'\n#define MOUNT_ESCAPE_LENGTH (3)\n\n// Field indices of fields in the /proc/self/mountinfo file.\n// N.B. There can be one or more optional fields, terminated by a single hyphen. This enumeration\n//      counts all optional fields including the hyphen separator as a single field.\ntypedef enum _MOUNT_FIELD\n{\n    MountFieldId,\n    MountFieldParentId,\n    MountFieldDevice,\n    MountFieldRoot,\n    MountFieldMountPoint,\n    MountFieldMountOptions,\n    MountFieldOptionalFields,\n    MountFieldFileSystemType,\n    MountFieldSource,\n    MountFieldSuperOptions,\n    MountFieldMax = MountFieldSuperOptions\n} MOUNT_FIELD,\n    *PMOUNT_FIELD;\n\nvoid MountFieldUnescape(char* field);\n\nbool MountFieldUnescapeOctal(const char* string, char* unescaped);\n\nchar* MountNextField(char** line, MOUNT_FIELD field);\n\nint MountParseDevice(char* field, dev_t* device);\n\n// Initializes a MOUNT_ENUM structure.\nint MountEnumCreate(PMOUNT_ENUM mountEnum)\n{\n    return MountEnumCreateEx(mountEnum, MOUNT_INFO_FILE);\n}\n\n// Initializes a MOUNT_ENUM structure from a custom mount info file.\nint MountEnumCreateEx(PMOUNT_ENUM mountEnum, const char* mountInfoFile)\n{\n    int result;\n\n    memset(mountEnum, 0, sizeof(*mountEnum));\n    mountEnum->MountInfo = fopen(mountInfoFile, \"r\");\n    if (mountEnum->MountInfo == NULL)\n    {\n        result = -1;\n        goto MountEnumCreateEnd;\n    }\n\n    result = 0;\n\nMountEnumCreateEnd:\n    return result;\n}\n\n// Frees the resources held by a MOUNT_ENUM structure.\nvoid MountEnumFree(PMOUNT_ENUM mountEnum)\n{\n    if (mountEnum->Line != NULL)\n    {\n        free(mountEnum->Line);\n    }\n\n    if (mountEnum->MountInfo != NULL)\n    {\n        fclose(mountEnum->MountInfo);\n    }\n}\n\n// Reads the next line of the /proc/self/mountinfo file, and parses it.\nint MountEnumNext(PMOUNT_ENUM mountEnum)\n{\n    ssize_t bytesRead;\n    int result;\n\n    do\n    {\n        // Get the next line.\n        // N.B. Set errno to zero first so EOF can be distinguished.\n        errno = 0;\n        bytesRead = getline(&mountEnum->Line, &mountEnum->LineLength, mountEnum->MountInfo);\n        if (bytesRead < 0)\n        {\n            result = -1;\n            goto MountEnumNextEnd;\n        }\n\n        // Parse the line. Invalid lines are skipped.\n        result = MountParseMountInfoLine(mountEnum->Line, &mountEnum->Current);\n    } while (result < 0);\n\n    result = 0;\n\nMountEnumNextEnd:\n    return result;\n}\n\n// Unescape a field in a mount entry that uses octal escape sequences.\nvoid MountFieldUnescape(char* field)\n{\n    size_t index;\n    size_t insertionIndex;\n    char unescaped;\n\n    // Scan until the end of the string.\n    // N.B. The last field may contain a newline character, which is stripped\n    //      by this function.\n    for (index = 0, insertionIndex = 0; field[index] != '\\0'; index += 1, insertionIndex += 1)\n    {\n        // If the character is a \\, see if the following three characters can\n        // be unescaped. Otherwise, move the current character back if a\n        // previous escape sequence was encountered.\n        if ((field[index] == MOUNT_ESCAPE_CHAR) && (MountFieldUnescapeOctal(&field[index + 1], &unescaped) != false))\n        {\n            field[insertionIndex] = unescaped;\n            index += MOUNT_ESCAPE_LENGTH;\n        }\n        else if (insertionIndex < index)\n        {\n            field[insertionIndex] = field[index];\n        }\n    }\n\n    // NULL terminate the field (if there were no escapes, this will just overwrite the existing\n    // terminator).\n    field[insertionIndex] = '\\0';\n}\n\n// Unescape a single octal escape sequence.\nbool MountFieldUnescapeOctal(const char* string, char* unescaped)\n{\n    char localUnescaped;\n    char character;\n    size_t index;\n\n    localUnescaped = 0;\n    for (index = 0; index < MOUNT_ESCAPE_LENGTH; index += 1)\n    {\n        character = string[index];\n        if ((character < '0') || (character > '7'))\n        {\n            return false;\n        }\n\n        localUnescaped <<= 3;\n        localUnescaped |= (character - '0');\n    }\n\n    *unescaped = localUnescaped;\n    return true;\n}\n\n// Find the next field in the mountinfo line, and make sure it's NULL terminated.\n// N.B. The start of the line is updated to the remaining text after the returned field.\nchar* MountNextField(char** line, MOUNT_FIELD field)\n{\n    if (field == 0)\n    {\n        return strtok_r(*line, MOUNT_FIELD_SEP, line);\n    }\n    else if (field < MountFieldMax)\n    {\n        return strtok_r(NULL, MOUNT_FIELD_SEP, line);\n    }\n\n    // The last field may contain separators, so don't look for the next separator in that case.\n    // However, it may end with a newline.\n    return strtok_r(NULL, \"\\n\", line);\n}\n\n// Parse a device number of the form \"major:minor\" into a dev_t.\nint MountParseDevice(char* field, dev_t* device)\n{\n    int major;\n    int minor;\n    int result;\n    char* separator;\n\n    separator = strchr(field, MOUNT_DEVICE_SEP);\n    if (separator == NULL)\n    {\n        result = -1;\n        goto MountParseDeviceEnd;\n    }\n\n    *separator = '\\0';\n    major = atoi(field);\n    minor = atoi(separator + 1);\n    *device = makedev(major, minor);\n    result = 0;\n\nMountParseDeviceEnd:\n    return result;\n}\n\n// Parse a line from the /proc/self/mountinfo file.\nint MountParseMountInfoLine(char* line, PMOUNT_ENTRY entry)\n{\n    char* current;\n    MOUNT_FIELD field;\n    int result;\n\n    for (field = 0, current = MountNextField(&line, field); ((current != NULL) && (field <= MountFieldMax));\n         field += 1, current = MountNextField(&line, field))\n    {\n        switch (field)\n        {\n        case MountFieldId:\n            entry->Id = atoi(current);\n            break;\n\n        case MountFieldParentId:\n            entry->ParentId = atoi(current);\n            break;\n\n        case MountFieldDevice:\n            result = MountParseDevice(current, &entry->Device);\n            if (result < 0)\n            {\n                goto ParseMountInfoLineEnd;\n            }\n\n        case MountFieldRoot:\n            entry->Root = current;\n            break;\n\n        case MountFieldMountPoint:\n            entry->MountPoint = current;\n            break;\n\n        case MountFieldMountOptions:\n            entry->MountOptions = current;\n            break;\n\n        case MountFieldOptionalFields:\n            // Find the end of the optional fields.\n            while (current != NULL && strcmp(current, MOUNT_OPTIONAL_FIELD_TERMINATOR) != 0)\n            {\n                current = MountNextField(&line, field);\n            }\n\n            break;\n\n        case MountFieldFileSystemType:\n            entry->FileSystemType = current;\n            break;\n\n        case MountFieldSource:\n            entry->Source = current;\n            break;\n\n        case MountFieldSuperOptions:\n            entry->SuperOptions = current;\n            break;\n        }\n    }\n\n    // Check if all the fields were found. If not, this is a malformed line.\n    if (field <= MountFieldMax)\n    {\n        result = -1;\n        goto ParseMountInfoLineEnd;\n    }\n\n    // Decode any octal escape sequences in the fields that may be escaped.\n    MountFieldUnescape(entry->Root);\n    MountFieldUnescape(entry->Source);\n    MountFieldUnescape(entry->MountPoint);\n    result = 0;\n\nParseMountInfoLineEnd:\n    return result;\n}\n"
  },
  {
    "path": "src/linux/mountutil/mountutil.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#define MOUNT_INFO_FILE_NAME \"/mountinfo\"\n#define MOUNT_INFO_FILE \"/proc/self\" MOUNT_INFO_FILE_NAME\n\n// Represents the parsed data from a line in the /proc/self/mountinfo file.\ntypedef struct _MOUNT_ENTRY\n{\n    int Id;\n    int ParentId;\n    dev_t Device;\n    char* Root;\n    char* MountPoint;\n    char* MountOptions;\n    char* FileSystemType;\n    char* Source;\n    char* SuperOptions;\n} MOUNT_ENTRY, *PMOUNT_ENTRY;\n\n// Represents an enumeration of entries in the /proc/self/mountinfo file.\ntypedef struct _MOUNT_ENUM\n{\n    FILE* MountInfo;\n    char* Line;\n    size_t LineLength;\n    MOUNT_ENTRY Current;\n} MOUNT_ENUM, *PMOUNT_ENUM;\n\nint MountEnumCreate(PMOUNT_ENUM mountEnum);\n\nint MountEnumCreateEx(PMOUNT_ENUM mountEnum, const char* mountInfoFile);\n\nvoid MountEnumFree(PMOUNT_ENUM mountEnum);\n\nint MountEnumNext(PMOUNT_ENUM mountEnum);\n\nint MountParseMountInfoLine(char* line, PMOUNT_ENTRY entry);\n"
  },
  {
    "path": "src/linux/mountutil/mountutilcpp.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nextern \"C\" {\n#include \"mountutil.h\"\n}\n\nnamespace mountutil {\n\n// C++ wrapper for the MOUNT_ENUM structure.\nclass MountEnum\n{\npublic:\n    // Initialize a new instance of the MountEnum class.\n    MountEnum(const char* mountInfoFile = MOUNT_INFO_FILE)\n    {\n        THROW_LAST_ERROR_IF(MountEnumCreateEx(&m_mountEnum, mountInfoFile) < 0);\n    }\n\n    // Destruct this instance of the MountEnum class.\n    ~MountEnum()\n    {\n        MountEnumFree(&m_mountEnum);\n    }\n\n    MountEnum(const MountEnum&) = delete;\n    MountEnum& operator=(const MountEnum&) = delete;\n\n    // Get the next entry in the mountinfo file. Returns false if the end is reached.\n    bool Next()\n    {\n        if (MountEnumNext(&m_mountEnum) < 0)\n        {\n            if (errno == 0)\n            {\n                return false;\n            }\n\n            THROW_ERRNO(errno);\n        }\n\n        return true;\n    }\n\n    // Return the current entry.\n    // N.B. You must call Next at least once before this is valid.\n    // N.B. The strings in the current entry are valid only until Next is called again or until this\n    //      class is destructed.\n    MOUNT_ENTRY& Current()\n    {\n        return m_mountEnum.Current;\n    }\n\n    // Finds a mount using the specified predicate. Returns false if there is no matching entry.\n    // N.B. If the function returns true, use Current to get the matching entry.\n    bool FindMount(const std::function<bool(const MOUNT_ENTRY&)>& predicate)\n    {\n        while (Next())\n        {\n            if (predicate(m_mountEnum.Current))\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\nprivate:\n    MOUNT_ENUM m_mountEnum{};\n};\n\nstruct ParsedOptions\n{\n    std::string StringOptions;\n    int MountFlags;\n    bool NoFail;\n};\n\nParsedOptions MountParseFlags(std::string_view options);\n\nint MountFilesystem(const char* source, const char* target, const char* type, const char* options);\n\n} // namespace mountutil\n"
  },
  {
    "path": "src/linux/netlinkutil/Address.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <iostream>\n#include <regex>\n#include \"address.h\"\n#include \"Syscall.h\"\n\nAddress::Address(short family, size_t prefixLength, const std::string& address, IpPrefixOrigin prefixOrigin, IpSuffixOrigin suffixOrigin, int preferredLifetime) :\n    m_family(family), m_prefixLength(prefixLength), m_address(address), m_prefixOrigin(prefixOrigin), m_suffixOrigin(suffixOrigin)\n{\n    // We cannot plumb temporary addresses (identified by RA prefix and random suffix) in Linux.\n    // In order for Linux to prefer temporary addresses over public addresses during source address\n    // selection, we instead mark public addresses as deprecated and plumb temporary addresses as\n    // public preferred addresses.\n    if (preferredLifetime == 0 || (prefixOrigin == IpPrefixOrigin::RouterAdvertisement && suffixOrigin == IpSuffixOrigin::LinkLayerAddress))\n    {\n        m_preferredLifetime = 0;\n    }\n    else\n    {\n        m_preferredLifetime = 0xFFFFFFFF;\n    }\n}\n\nAddress::Address(short family, size_t prefixLength, const std::string& address) :\n    Address(family, prefixLength, address, IpPrefixOrigin::Manual, IpSuffixOrigin::Manual, 0xFFFFFFFF)\n{\n}\n\nbool Address::operator==(const Address& other) const\n{\n    return m_family == other.Family() && m_address == other.Addr() && m_prefixLength == other.PrefixLength();\n}\n\nbool Address::operator!=(const Address& other) const\n{\n    return !(*this == other);\n}\n\nstd::ostream& operator<<(std::ostream& out, const Address& address)\n{\n    return out << address.Addr() << \"/\" << address.PrefixLength();\n}\n\nAddress Address::FromBytes(int family, int prefixLength, const void* ptr)\n{\n    static_assert(INET6_ADDRSTRLEN > INET_ADDRSTRLEN);\n\n    std::string addressString(INET6_ADDRSTRLEN, '\\0');\n\n    if (inet_ntop(family, ptr, addressString.data(), addressString.size()) == nullptr)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"inet_ntop failed, {}\", errno));\n    }\n\n    return {family, static_cast<size_t>(prefixLength), addressString.c_str()};\n}\n\nvoid Address::ConvertToBytes(void* ptr) const\n{\n    Syscall(inet_pton, m_family, m_address.c_str(), ptr);\n}\n\nAddress Address::FromPrefixString(int family, const std::string& prefix)\n{\n    const std::regex exp(R\"((.*)\\/((\\d){1,3}))\");\n    std::smatch match;\n    if (!std::regex_search(prefix.begin(), prefix.end(), match, exp) || match.size() != 4)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Failed to parse prefix: {}\", prefix));\n    }\n\n    in6_addr tmpAddr;\n    int err = inet_pton(family, match.str(1).c_str(), &tmpAddr);\n    if (err != 1)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\n            \"Parsed invalid address ({}) from prefix: {} and family: {} with error {} and errno {}\", match.str(1), prefix, family, err, errno));\n    }\n\n    return Address{family, std::stoul(match.str(2)), match.str(1)};\n}\n\nAddress Address::FromBinary(int family, int prefixLength, const void* data)\n{\n    std::string addr(INET6_ADDRSTRLEN, '\\0');\n    if (inet_ntop(family, data, addr.data(), addr.size()) == nullptr)\n    {\n        throw RuntimeErrorWithSourceLocation(\n            std::format(\"inet_ntop failed for family {} prefixLength {} with errno {}\", family, prefixLength, errno));\n    }\n\n    addr.resize(strlen(addr.c_str()));\n\n    return {family, static_cast<size_t>(prefixLength), addr};\n}\n\nshort Address::Family() const noexcept\n{\n    return m_family;\n}\n\nsize_t Address::PrefixLength() const noexcept\n{\n    return m_prefixLength;\n}\n\nconst std::string Address::Addr() const noexcept\n{\n    return m_address;\n}\n\nint Address::Flags() const noexcept\n{\n    // Note: the below is only useful if the kernel ever supports plumbing temporary addresses\n    int flags = 0;\n    if (m_prefixOrigin == IpPrefixOrigin::RouterAdvertisement && m_suffixOrigin == IpSuffixOrigin::Random)\n    {\n        flags |= IFA_F_TEMPORARY;\n    }\n\n    return flags;\n}\n\nint Address::Scope() const noexcept\n{\n    if (m_prefixOrigin == IpPrefixOrigin::WellKnown && m_suffixOrigin == IpSuffixOrigin::LinkLayerAddress)\n    {\n        return RT_SCOPE_LINK;\n    }\n\n    return RT_SCOPE_UNIVERSE;\n}\n\nint Address::PreferredLifetime() const noexcept\n{\n    return m_preferredLifetime;\n}\n\nbool Address::IsIpv4() const noexcept\n{\n    return m_family == AF_INET;\n}\n\nbool Address::IsPrefixRouteAutogenerationDisabled() const noexcept\n{\n    return m_disableAutogeneratedPrefixRoute;\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/CMakeLists.txt",
    "content": "set(SOURCES\n    Address.cpp\n    Interface.cpp\n    IpNeighborManager.cpp\n    IpRuleManager.cpp\n    Neighbor.cpp\n    NetlinkError.cpp\n    NetlinkParseException.cpp\n    NetlinkResponse.cpp\n    NetlinkTransaction.cpp\n    NetlinkTransactionError.cpp\n    Route.cpp\n    RoutingTable.cpp\n    Rule.cpp\n    RuntimeErrorWithSourceLocation.cpp\n    SyscallError.cpp\n    Utils.cpp)\n\nset(HEADERS\n    address.h\n    Forwarder.h\n    Fwd.h\n    Interface.h\n    InterfaceConfiguration.h\n    IpNeighborManager.h\n    IpRuleManager.h\n    Neighbor.h\n    NetlinkError.h\n    NetlinkChannel.h\n    NetlinkMessage.h\n    NetlinkParseException.h\n    NetlinkResponse.h\n    NetlinkTransaction.h\n    NetlinkTransactionError.h\n    Packet.h\n    Protocol.h\n    Operation.h\n    Route.h\n    RoutingTable.h\n    Rule.h\n    RuntimeErrorWithSourceLocation.h\n    Syscall.h\n    SyscallError.h\n    Utils.h\n    Forwarder.hxx\n    NetlinkChannel.hxx\n    NetlinkMessage.hxx\n    NetlinkResponse.hxx\n    NetLinkStrings.h\n    Syscall.hxx\n    Utils.hxx)\n\nset(LINUX_CXXFLAGS ${LINUX_CXXFLAGS} -I \"${CMAKE_CURRENT_LIST_DIR}/../init\")\n\nadd_linux_library(libnetlinkutil \"${SOURCES}\" \"${HEADERS}\")\nset_target_properties(libnetlinkutil PROPERTIES FOLDER linux)\n"
  },
  {
    "path": "src/linux/netlinkutil/Forwarder.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <stdint.h>\n#include <thread>\n#include <memory>\n\n#include \"Packet.h\"\n\nclass IForwarder\n{\npublic:\n    virtual ~IForwarder() {};\n};\n\ntemplate <typename ProcessingFunction, typename ExceptionHandler>\nclass Forwarder : public IForwarder\n{\npublic:\n    Forwarder(int SourceFd, int DestinationFd, ProcessingFunction Handler, ExceptionHandler exceptionHandler);\n    ~Forwarder() override;\n\nprivate:\n    std::thread Worker;\n    int SourceFd;\n    int DestinationFd;\n    int TerminateFd;\n};\n\n#include \"Forwarder.hxx\"\n"
  },
  {
    "path": "src/linux/netlinkutil/Forwarder.hxx",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n// SPDX-License-Identifier: MIT\n#pragma once\n\n#include <poll.h>\n#include <signal.h>\n#include <sys/signalfd.h>\n#include \"lxwil.h\"\n#include \"Forwarder.h\"\n#include \"Packet.h\"\n#include \"Syscall.h\"\n\n#include <stdio.h>\n\n#define _countof(a) (sizeof(a) / sizeof(*(a)))\n\ntemplate <typename ProcessingFunction, typename ExceptionHandler>\nForwarder<ProcessingFunction, ExceptionHandler>::Forwarder(int SourceFd, int DestinationFd, ProcessingFunction Handler, ExceptionHandler exceptionHandler)\n{\n    // Create a pipe to signal the thread to stop.\n    int pipes[2];\n    Syscall(pipe2, pipes, 0);\n    TerminateFd = pipes[1];\n\n    Worker = std::thread([=]() {\n        try\n        {\n            wil::unique_fd terminate = pipes[0];\n            auto wait_for_fd = [&terminate](int fd, int event) -> bool {\n                struct pollfd poll_fds[2];\n                poll_fds[0] = {.fd = fd, .events = event, .revents = 0};\n                poll_fds[1] = {.fd = terminate.get(), .events = POLLIN, .revents = 0};\n                for (;;)\n                {\n                    const int return_value = poll(poll_fds, _countof(poll_fds), -1);\n                    if (return_value < 0)\n                    {\n                        if (errno == EINTR)\n                        {\n                            continue;\n                        }\n\n                        throw std::runtime_error(std::string(\"poll returned \") + std::string(strerror(errno)));\n                    }\n                    else if (return_value == 0)\n                    {\n                        continue;\n                    }\n                    else if (poll_fds[1].revents)\n                    {\n                        return false;\n                    }\n                    else if (poll_fds[0].revents & event)\n                    {\n                        return true;\n                    }\n                }\n            };\n\n            Packet packet;\n            for (;;)\n            {\n                packet.reset();\n\n                // Grow the packet to provide space.\n                packet.adjust_tail(Packet::InitialPacketSize);\n                if (!wait_for_fd(SourceFd, POLLIN))\n                {\n                    break;\n                }\n\n                int bytes_read = Syscall(read, SourceFd, packet.data(), packet.data_end() - packet.data());\n\n                // Shrink packet to size of data read.\n                packet.adjust_tail(bytes_read - (packet.data_end() - packet.data()));\n\n                // If the handler returns true, write the packet to the destination fd.\n                if (Handler(packet))\n                {\n                    if (!wait_for_fd(DestinationFd, POLLOUT))\n                    {\n                        break;\n                    }\n\n                    Syscall(write, DestinationFd, packet.data(), packet.data_end() - packet.data());\n                }\n            }\n        }\n        catch (std::exception& e)\n        {\n            if (!exceptionHandler(e))\n            {\n                throw;\n            }\n        }\n    });\n}\n\ntemplate <typename ProcessingFunction, typename ExceptionHandler>\nForwarder<ProcessingFunction, ExceptionHandler>::~Forwarder()\n{\n    // Close the write end of the pipe to signal the thread to stop.\n    close(TerminateFd);\n    if (Worker.joinable())\n    {\n        Worker.join();\n    }\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/Fwd.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nclass NetlinkResponse;\nclass NetlinkChannel;\n"
  },
  {
    "path": "src/linux/netlinkutil/Interface.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <sys/ioctl.h>\n#include <sys/types.h>\n#include <arpa/inet.h>\n#include <net/if.h>\n#include <linux/netlink.h>\n#include <linux/nl80211.h>\n#include <linux/rtnetlink.h>\n#include <iostream>\n#include <optional>\n#include <gsl/gsl>\n#include <gslhelpers.h>\n#include <string.h>\n#include <ifaddrs.h>\n#include <linux/if_packet.h>\n#include <linux/pkt_cls.h>\n#include <lxwil.h>\n#include <linux/if_tun.h>\n#include \"NetlinkChannel.h\"\n#include \"Syscall.h\"\n#include \"Utils.h\"\n#include \"Interface.h\"\n#include \"lxwil.h\"\n\nusing utils::Attribute;\n\nconstexpr char c_value0[] = \"0\\n\";\nconstexpr char c_value1[] = \"1\\n\";\n\ntemplate <typename TAddr>\nstruct AddressMessage\n{\n    ifaddrmsg ifaddr;\n    utils::AddressAttribute<TAddr> localAddress;\n    utils::AddressAttribute<TAddr> address;\n    utils::CacheInfoAttribute cacheInfo;\n    utils::IntegerAttribute addressFlags;\n} __attribute__((packed));\n\ntemplate <typename TAddr>\nstruct AddressMessageWithBroadcast : AddressMessage<TAddr>\n{\n    utils::AddressAttribute<TAddr> broadcastAddress;\n} __attribute__((packed));\n\nInterface::Interface()\n{\n}\n\nInterface::Interface(const Interface& source) : m_index(source.m_index), m_name(source.m_name)\n{\n}\n\nInterface::Interface(int index, const std::string& name) : m_index(index), m_name(name)\n{\n}\n\nInterface::operator bool() const\n{\n    return m_index != -1;\n}\n\ntemplate <typename TAddr>\nvoid Interface::ChangeAddress(const Address& address, const std::optional<Address>& broadcastAddress, Operation operation)\n{\n    if (broadcastAddress.has_value())\n    {\n        ChangeAddressImpl<TAddr, AddressMessageWithBroadcast<TAddr>>(address, broadcastAddress, operation);\n    }\n    else\n    {\n        ChangeAddressImpl<TAddr, AddressMessage<TAddr>>(address, broadcastAddress, operation);\n    }\n}\n\n// TMessage must be derived from AddressMessage or one of its children\ntemplate <typename TAddr, typename TMessage>\nvoid Interface::ChangeAddressImpl(const Address& address, const std::optional<Address>& broadcastAddress, Operation operation)\n{\n    TMessage message{};\n\n    message.ifaddr.ifa_family = address.Family();\n    message.ifaddr.ifa_prefixlen = address.PrefixLength();\n    message.ifaddr.ifa_index = m_index;\n    message.ifaddr.ifa_scope = address.Scope();\n\n    utils::InitializeAddressAttribute<TAddr>(message.address, address, IFA_ADDRESS);\n    utils::InitializeAddressAttribute<TAddr>(message.localAddress, address, IFA_LOCAL);\n    utils::InitializeCacheInfoAttribute(message.cacheInfo, address);\n\n    // For remove, most flags are ignored, including noprefixroute and nodad.\n    int addrFlags = address.IsPrefixRouteAutogenerationDisabled() ? IFA_F_NOPREFIXROUTE : 0;\n    if (address.Family() == AF_INET6)\n    {\n        addrFlags |= IFA_F_NODAD;\n    }\n    utils::InitializeIntegerAttribute(message.addressFlags, addrFlags, IFA_FLAGS);\n\n    if constexpr (std::is_same<TMessage, AddressMessageWithBroadcast<TAddr>>::value)\n    {\n        utils::InitializeAddressAttribute<TAddr>(message.broadcastAddress, broadcastAddress.value(), IFA_BROADCAST);\n    }\n\n    int flags = 0;\n    if (operation == Update)\n    {\n        flags = NLM_F_CREATE | NLM_F_REPLACE;\n    }\n    else if (operation == Create)\n    {\n        flags = NLM_F_CREATE | NLM_F_EXCL;\n    }\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction<TMessage>(message, operation == Remove ? RTM_DELADDR : RTM_NEWADDR, flags);\n\n    transaction.Execute();\n}\n\nvoid Interface::ModifyIpAddress(const Address& address, Operation operation)\n{\n    // NOTE: in-place address update is not supported via NetLink, so remove the address first and\n    // then re-add it. We assume that the caller has saved any other related state beforehand to\n    // restore, such as routes.\n    if (address.IsIpv4())\n    {\n        std::optional<Address> broadcastAddress = utils::ComputeBroadcastAddress(address);\n\n        if (operation == Update)\n        {\n            std::vector<Address> v4Addresses = Ipv4Configuration().Addresses;\n            if (std::find(v4Addresses.begin(), v4Addresses.end(), address) != v4Addresses.end())\n            {\n                ChangeAddress<in_addr>(address, broadcastAddress, Remove);\n            }\n        }\n\n        ChangeAddress<in_addr>(address, broadcastAddress, operation);\n    }\n    else\n    {\n        if (operation == Update)\n        {\n            std::vector<Address> v6Addresses = Ipv6Configuration().Addresses;\n            if (std::find(v6Addresses.begin(), v6Addresses.end(), address) != v6Addresses.end())\n            {\n                ChangeAddress<in6_addr>(address, {}, Remove);\n            }\n        }\n\n        ChangeAddress<in6_addr>(address, {}, operation);\n    }\n}\n\ntemplate <size_t wifiTypeArraySize>\nvoid Interface::CreateWifiAdapter(const std::string& wifiName, const char (&wifiType)[wifiTypeArraySize])\n{\n    constexpr int wifiTypeSize = wifiTypeArraySize - 1;\n    struct Request\n    {\n        ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\n        utils::IntegerAttribute LinkAttribute;\n        Attribute<Attribute<char[wifiTypeSize]>> LinkInfoAttribute;\n        Attribute<char> NameAttribute;\n    } __attribute__((packed));\n\n    auto buffer = std::vector<char>(RTA_ALIGN(offsetof(Request, NameAttribute.value) + wifiName.size()));\n    auto* request = gslhelpers::get_struct<Request>(gsl::make_span(buffer));\n\n    utils::InitializeIntegerAttribute(request->LinkAttribute, m_index, IFLA_LINK);\n\n    request->LinkInfoAttribute.header.rta_len = RTA_SPACE(sizeof(Attribute<char[wifiTypeSize]>));\n    request->LinkInfoAttribute.header.rta_type = IFLA_LINKINFO;\n    request->LinkInfoAttribute.value.header.rta_len = RTA_SPACE(wifiTypeSize);\n    request->LinkInfoAttribute.value.header.rta_type = IFLA_INFO_KIND;\n    auto typeBuffer = gsl::make_span(buffer).subspan(offsetof(Request, LinkInfoAttribute.value.value));\n    gsl::copy(gsl::make_span(wifiType, wifiTypeSize), typeBuffer);\n\n    request->NameAttribute.header.rta_len = RTA_SPACE(wifiName.size());\n    request->NameAttribute.header.rta_type = IFLA_IFNAME;\n    auto nameBuffer = gsl::make_span(buffer).subspan(offsetof(Request, NameAttribute.value));\n    gsl::copy(gsl::make_span(wifiName), nameBuffer);\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(buffer.data(), buffer.size(), RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL);\n    transaction.Execute();\n}\n\nvoid Interface::CreateVirtualWifiAdapter(const std::string& wifiName)\n{\n    constexpr char virtualWifiType[] = \"virt_wifi\";\n    CreateWifiAdapter(wifiName, virtualWifiType);\n}\n\nvoid Interface::CreateProxyWifiAdapter(const std::string& wifiName)\n{\n    constexpr char proxyWifiType[] = \"proxy_wifi\";\n    CreateWifiAdapter(wifiName, proxyWifiType);\n}\n\nvoid Interface::CreateBondAdapter(const std::string& bondName)\n{\n    constexpr char bondType[] = \"bond\";\n    // This corresponds to the command generated by:\n    // ip link add type bond mode active-backup fail_over_mac active\n    // Format appears to be:\n    // uint16_t 0x5\n    // uint16_t option (0x1 = bond mode, 0xd = fail_over_mac)\n    // uint32_t setting (0x1 = active-backup for bond_mode, 0x1 = active for fail_over_mac)\n    constexpr char bondData[] = {0x05, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x00};\n    constexpr int bondTypeSize = sizeof(bondType) - 1;\n    constexpr int bondDataSize = sizeof(bondData);\n    struct LinkInfo\n    {\n        Attribute<char[bondTypeSize]> KindAttribute;\n        Attribute<char[bondDataSize]> DataAttribute;\n    } __attribute__((packed));\n    struct Request\n    {\n        ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\n        Attribute<LinkInfo> LinkInfoAttribute;\n        Attribute<char> NameAttribute;\n    } __attribute__((packed));\n\n    auto buffer = std::vector<char>(RTA_ALIGN(offsetof(Request, NameAttribute.value) + bondName.size()));\n    auto* request = gslhelpers::get_struct<Request>(gsl::make_span(buffer));\n\n    request->LinkInfoAttribute.header.rta_len = RTA_SPACE(sizeof(LinkInfo));\n    request->LinkInfoAttribute.header.rta_type = IFLA_LINKINFO;\n\n    request->LinkInfoAttribute.value.KindAttribute.header.rta_len = RTA_SPACE(bondTypeSize);\n    request->LinkInfoAttribute.value.KindAttribute.header.rta_type = IFLA_INFO_KIND;\n    auto typeBuffer = gsl::make_span(buffer).subspan(offsetof(Request, LinkInfoAttribute.value.KindAttribute.value));\n    gsl::copy(gsl::make_span(bondType, bondTypeSize), typeBuffer);\n\n    request->LinkInfoAttribute.value.DataAttribute.header.rta_len = RTA_SPACE(bondDataSize);\n    request->LinkInfoAttribute.value.DataAttribute.header.rta_type = IFLA_INFO_DATA;\n    auto dataBuffer = gsl::make_span(buffer).subspan(offsetof(Request, LinkInfoAttribute.value.DataAttribute.value));\n    gsl::copy(gsl::make_span(bondData, bondDataSize), dataBuffer);\n\n    request->NameAttribute.header.rta_len = RTA_SPACE(bondName.size());\n    request->NameAttribute.header.rta_type = IFLA_IFNAME;\n    auto nameBuffer = gsl::make_span(buffer).subspan(offsetof(Request, NameAttribute.value));\n    gsl::copy(gsl::make_span(bondName), nameBuffer);\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(buffer.data(), buffer.size(), RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL);\n    transaction.Execute();\n}\n\nvoid Interface::RemoveFromBond(const Interface& child_interface)\n{\n    struct Message\n    {\n        ifinfomsg ifinfo;\n        utils::IntegerAttribute master;\n    } __attribute__((packed));\n\n    Message message{};\n    message.ifinfo.ifi_index = child_interface.Index();\n    message.master.header.rta_len = sizeof(message.master);\n    message.master.header.rta_type = IFLA_MASTER;\n    message.master.value = 0;\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);\n    transaction.Execute([](const NetlinkResponse& response) {\n        fprintf(stderr, \"Interface::RemoveFromBond netlink response: %s\\n\", utils::Stringify(response).c_str());\n    });\n}\n\nvoid Interface::AddToBond(const Interface& child_interface)\n{\n    struct Message\n    {\n        ifinfomsg ifinfo;\n        utils::IntegerAttribute master;\n    } __attribute__((packed));\n\n    Message message{};\n    message.ifinfo.ifi_index = child_interface.Index();\n    utils::InitializeIntegerAttribute(message.master, m_index, IFLA_MASTER);\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);\n    transaction.Execute([](const NetlinkResponse& response) {\n        fprintf(stderr, \"Interface::AddToBond netlink response: %s\\n\", utils::Stringify(response).c_str());\n    });\n}\n\nvoid Interface::DeleteInterface()\n{\n    struct Message\n    {\n        ifinfomsg ifinfo;\n    } __attribute__((packed));\n\n    Message message{};\n    message.ifinfo.ifi_index = m_index;\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(message, RTM_DELLINK, 0);\n    transaction.Execute([](const NetlinkResponse& response) {\n        fprintf(stderr, \"Interface::DeleteInterface netlink response: %s\\n\", utils::Stringify(response).c_str());\n    });\n}\n\nvoid Interface::SetActiveChild(const Interface& child_interface)\n{\n    constexpr char bondType[] = \"bond\";\n    constexpr int bondTypeSize = sizeof(bondType) - 1;\n\n    struct ActiveChildInfo\n    {\n        utils::IntegerAttribute ActiveChildAttribute;\n    } __attribute__((packed));\n\n    struct LinkInfo\n    {\n        Attribute<gsl::byte[bondTypeSize]> KindAttribute;\n        Attribute<ActiveChildInfo> DataAttribute;\n    } __attribute__((packed));\n\n    struct Request\n    {\n        ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\n        Attribute<LinkInfo> LinkInfoAttribute;\n    } __attribute__((packed));\n\n    auto buffer = std::vector<gsl::byte>(RTA_ALIGN(sizeof(Request)));\n    auto* request = gslhelpers::get_struct<Request>(gsl::make_span(buffer));\n\n    request->Message.ifi_index = m_index;\n\n    request->LinkInfoAttribute.header.rta_len = sizeof(request->LinkInfoAttribute);\n    request->LinkInfoAttribute.header.rta_type = IFLA_LINKINFO;\n\n    request->LinkInfoAttribute.value.KindAttribute.header.rta_len = sizeof(request->LinkInfoAttribute.value.KindAttribute);\n    request->LinkInfoAttribute.value.KindAttribute.header.rta_type = IFLA_INFO_KIND;\n    auto typeBuffer = gsl::make_span(buffer).subspan(offsetof(Request, LinkInfoAttribute.value.KindAttribute.value));\n    gsl::copy(gsl::as_bytes(gsl::make_span(bondType, bondTypeSize)), typeBuffer);\n\n    request->LinkInfoAttribute.value.DataAttribute.header.rta_len = sizeof(request->LinkInfoAttribute.value.DataAttribute);\n    request->LinkInfoAttribute.value.DataAttribute.header.rta_type = IFLA_INFO_DATA;\n\n    utils::InitializeIntegerAttribute(\n        request->LinkInfoAttribute.value.DataAttribute.value.ActiveChildAttribute, child_interface.Index(), IFLA_BOND_ACTIVE_SLAVE);\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(buffer.data(), buffer.size(), RTM_NEWLINK, 0);\n    transaction.Execute([](const NetlinkResponse& response) {\n        fprintf(stderr, \"Interface::SetActiveChild(bond) netlink response: %s\\n\", utils::Stringify(response).c_str());\n    });\n}\n\nvoid Interface::CreateTunTapAdapter(const std::string& name, bool TunAdapter)\n{\n    wil::unique_fd fd;\n    if (name.size() > IFNAMSIZ)\n    {\n        throw RuntimeErrorWithSourceLocation(\"Tun adapter name exceeds IFNAMSIZ\");\n    }\n\n    ifreq ifr{};\n    ifr.ifr_flags = TunAdapter ? IFF_TUN : IFF_TAP;\n    fd = Syscall(open, \"/dev/net/tun\", O_RDWR);\n    std::copy(name.begin(), name.end(), ifr.ifr_name);\n    Syscall(ioctl, fd.get(), TUNSETIFF, &ifr);\n    Syscall(ioctl, fd.get(), TUNSETPERSIST, 1);\n}\n\nvoid Interface::CreateTunAdapter(const std::string& name)\n{\n    CreateTunTapAdapter(name, true);\n}\n\nvoid Interface::CreateTapAdapter(const std::string& name)\n{\n    CreateTunTapAdapter(name, false);\n}\n\nvoid Interface::SetMtu(int mtu)\n{\n    struct Message\n    {\n        ifinfomsg ifinfo;\n        utils::IntegerAttribute mtu;\n    } __attribute__((packed));\n\n    Message message{};\n    message.ifinfo.ifi_index = m_index;\n    utils::InitializeIntegerAttribute(message.mtu, mtu, IFLA_MTU);\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);\n    transaction.Execute();\n}\n\nvoid Interface::SetMetric(int metric)\n{\n    struct Message\n    {\n        ifinfomsg ifinfo;\n        utils::IntegerAttribute metric;\n    } __attribute__((packed));\n\n    Message message{};\n    message.ifinfo.ifi_index = m_index;\n    utils::InitializeIntegerAttribute(message.metric, metric, IFLA_PRIORITY);\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);\n    transaction.Execute();\n}\n\nvoid Interface::SetIpv4Configuration(const InterfaceConfiguration& configuration)\n{\n    SetConfiguration<in_addr>(Ipv4Configuration(), configuration);\n}\n\nvoid Interface::SetIpv6Configuration(const InterfaceConfiguration& configuration)\n{\n    SetConfiguration<in6_addr>(Ipv6Configuration(), configuration);\n}\n\nvoid Interface::SetName(const std::string& newName)\n{\n    struct Request\n    {\n        struct ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\n        Attribute<char> Attribute;\n    };\n\n    auto buffer = std::vector<char>(RTA_ALIGN(offsetof(Request, Attribute.value) + newName.size()));\n    auto* request = gslhelpers::get_struct<Request>(gsl::make_span(buffer));\n    request->Message.ifi_index = m_index;\n    request->Attribute.header.rta_len = RTA_LENGTH(newName.size());\n    request->Attribute.header.rta_type = IFLA_IFNAME;\n    auto nameBuffer = gsl::make_span(buffer).subspan(offsetof(Request, Attribute.value));\n    gsl::copy(gsl::make_span(newName), nameBuffer);\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(buffer.data(), buffer.size(), RTM_NEWLINK, 0);\n    transaction.Execute();\n}\n\nvoid Interface::SetWiphyNamespace(int namespaceFd)\n{\n    struct Request\n    {\n        int Command;\n        utils::IntegerAttribute Wiphy;\n        utils::IntegerAttribute NamespaceFd;\n    };\n\n    Request request{};\n    request.Command = NL80211_CMD_SET_WIPHY_NETNS;\n\n    utils::InitializeIntegerAttribute(request.Wiphy, 0, NL80211_ATTR_WIPHY);\n    utils::InitializeIntegerAttribute(request.NamespaceFd, namespaceFd, NL80211_ATTR_NETNS_FD);\n\n    NetlinkChannel channel(SOCK_RAW, NETLINK_GENERIC);\n    auto transaction = channel.CreateTransaction(request, 0x15, 0);\n    transaction.Execute();\n}\n\nvoid Interface::SetNamespace(int namespaceFd)\n{\n    struct Request\n    {\n        struct ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\n        utils::IntegerAttribute Attribute;\n    };\n\n    Request request{};\n    request.Message.ifi_index = m_index;\n\n    utils::InitializeIntegerAttribute(request.Attribute, namespaceFd, IFLA_NET_NS_FD);\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction<Request>(request, RTM_NEWLINK, 0);\n    transaction.Execute();\n}\n\nvoid Interface::AddFlags(int flags)\n{\n    NetlinkChannel channel;\n    const auto currentFlags = channel.GetInterfaceFlags(m_name);\n\n    channel.SetInterfaceFlags(m_name, currentFlags | flags);\n}\n\nvoid Interface::RemoveFlags(int flags)\n{\n    NetlinkChannel channel;\n    const auto currentFlags = channel.GetInterfaceFlags(m_name);\n\n    channel.SetInterfaceFlags(m_name, currentFlags & ~flags);\n}\n\nvoid Interface::SetUp()\n{\n    AddFlags(IFF_UP | IFF_RUNNING);\n}\n\nvoid Interface::SetDown()\n{\n    RemoveFlags(IFF_UP | IFF_RUNNING);\n}\n\nvoid Interface::SetMacAddress(const MacAddress& address)\n{\n    static_assert(sizeof(address) == 6);\n\n    struct Message\n    {\n        ifinfomsg ifinfo;\n        utils::MacAddressAttribute address;\n    } __attribute__((packed));\n\n    Message message{};\n    message.ifinfo.ifi_index = m_index;\n    message.address.header.nla_len = sizeof(message.address);\n    message.address.header.nla_type = IFLA_ADDRESS;\n    message.address.address = address;\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(message, RTM_NEWLINK, 0);\n    transaction.Execute();\n}\n\nMacAddress Interface::GetMacAddress()\n{\n    MacAddress address;\n    ifreq interface_request{};\n    wil::unique_fd fd;\n    fd = Syscall(socket, AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));\n    std::copy(m_name.begin(), m_name.end(), interface_request.ifr_ifrn.ifrn_name);\n    Syscall(ioctl, fd.get(), SIOCGIFHWADDR, &interface_request);\n    std::copy(\n        interface_request.ifr_ifru.ifru_hwaddr.sa_data, interface_request.ifr_ifru.ifru_hwaddr.sa_data + address.size(), address.begin());\n    return address;\n}\n\ntemplate <typename TAddr>\nvoid Interface::SetConfiguration(const InterfaceConfiguration& currentConfiguration, const InterfaceConfiguration& configuration)\n{\n    std::vector<const Address*> add;\n    std::vector<const Address*> remove;\n\n    auto compare = [](const std::vector<Address>& left, const std::vector<Address>& right, std::vector<const Address*>& dest) {\n        for (const auto& e : left)\n        {\n            if (std::find(right.begin(), right.end(), e) == right.end())\n            {\n                dest.emplace_back(&e);\n            }\n        }\n    };\n\n    compare(currentConfiguration.Addresses, configuration.Addresses, remove);\n    compare(configuration.Addresses, currentConfiguration.Addresses, add);\n\n    for (const auto* address : remove)\n    {\n        ChangeAddress<TAddr>(*address, configuration.BroadcastAddress, Remove);\n    }\n\n    for (const auto* address : add)\n    {\n        ChangeAddress<TAddr>(*address, configuration.BroadcastAddress, Create);\n    }\n}\n\nInterfaceConfiguration Interface::Ipv6Configuration()\n{\n    return ListAddressesImpl(AF_INET6);\n}\n\nInterfaceConfiguration Interface::Ipv4Configuration()\n{\n    return ListAddressesImpl(AF_INET);\n}\n\nInterfaceConfiguration Interface::ListAddressesImpl(int af)\n{\n    InterfaceConfiguration configuration;\n    auto routine = [&](const NetlinkResponse& response) {\n        auto messages = response.Messages<ifaddrmsg>(RTM_NEWADDR);\n\n        for (const auto& message : messages)\n        {\n            const auto* ifaddr = message.Payload();\n            if (ifaddr->ifa_index != static_cast<size_t>(m_index))\n            {\n                continue;\n            }\n\n            auto getAddresses = [&](int type, std::vector<Address>& dest) {\n                for (const auto attribute : message.Attributes<const void*>(type))\n                {\n                    auto messageAf = message.Payload()->ifa_family;\n                    int prefixLength = message.Payload()->ifa_prefixlen;\n                    dest.emplace_back(Address::FromBinary(messageAf, prefixLength, attribute));\n                }\n            };\n\n            getAddresses(IFA_ADDRESS, configuration.Addresses);\n            getAddresses(IFA_LOCAL, configuration.LocalAddresses);\n\n            std::vector<Address> broadcastAddresses;\n            getAddresses(IFA_BROADCAST, broadcastAddresses);\n            if (broadcastAddresses.size() == 1)\n            {\n                configuration.BroadcastAddress = std::move(broadcastAddresses[0]);\n            }\n            else if (broadcastAddresses.size() > 1)\n            {\n                throw RuntimeErrorWithSourceLocation(\"More than one broadcast address found\");\n            }\n        }\n    };\n\n    NetlinkChannel channel;\n\n    ifaddrmsg payload = {};\n    payload.ifa_family = af;\n    payload.ifa_index = m_index;\n    auto transaction = channel.CreateTransaction(payload, RTM_GETADDR, NLM_F_DUMP);\n\n    transaction.Execute(routine);\n\n    return configuration;\n}\n\n/*\n    Implements functionality equivalent to sysctl -w net.ipv(4/6).conf.<interface>.<setting>, by writing to\n    /proc/sys/net/ipv(4/6)/conf/m_name\n*/\nvoid Interface::EnableNetworkSetting(const char* settingName, int addressFamily)\n{\n    const std::string settingFilePath =\n        std::format(\"/proc/sys/net/{}/conf/{}/{}\", (addressFamily == AF_INET ? \"ipv4\" : \"ipv6\"), m_name, settingName);\n\n    wil::unique_fd fd(Syscall(open, settingFilePath.c_str(), (O_WRONLY | O_CLOEXEC)));\n\n    Syscall(write, fd.get(), c_value1, sizeof(c_value1));\n}\n\nvoid Interface::DisableNetworkSetting(const char* settingName, int addressFamily)\n{\n    const std::string settingFilePath =\n        std::format(\"/proc/sys/net/{}/conf/{}/{}\", (addressFamily == AF_INET ? \"ipv4\" : \"ipv6\"), m_name, settingName);\n\n    wil::unique_fd fd(Syscall(open, settingFilePath.c_str(), (O_WRONLY | O_CLOEXEC)));\n\n    Syscall(write, fd.get(), c_value0, sizeof(c_value0));\n}\n\nvoid Interface::ResetIpv6State()\n{\n    // Disable IPv6, turn off address autogeneration and router discovery, and then re-enable IPv6.\n    // Some process (presumably netd) re-enables some of these settings after receiving new\n    // adapter configuration (eg, after connecting to a new Wi-Fi network), so this function should\n    // be called  after that to restore the settings to the desired values.\n    EnableNetworkSetting(\"disable_ipv6\", AF_INET6);\n\n    DisableNetworkSetting(\"accept_ra\", AF_INET6);\n    DisableNetworkSetting(\"autoconf\", AF_INET6);\n    DisableNetworkSetting(\"use_tempaddr\", AF_INET6);\n    EnableNetworkSetting(\"addr_gen_mode\", AF_INET6); // A value of 1 means address generation is disabled.\n\n    DisableNetworkSetting(\"disable_ipv6\", AF_INET6);\n}\n\nint Interface::Index() const\n{\n    return m_index;\n}\n\nconst std::string& Interface::Name() const\n{\n    return m_name;\n}\n\nInterface Interface::Open(const std::string& name)\n{\n    NetlinkChannel channel;\n    return {channel.GetInterfaceIndex(name), name};\n}\n\nvoid Interface::ModifyTcClassifier(bool Add)\n{\n    char kind[] = \"clsact\";\n    struct Message\n    {\n        tcmsg TcMsg;\n        Attribute<char[sizeof(kind)]> Kind;\n    } __attribute__((packed));\n    Message message{};\n\n    message.TcMsg.tcm_family = AF_UNSPEC;\n    message.TcMsg.tcm_ifindex = m_index;\n    message.TcMsg.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);\n    message.TcMsg.tcm_parent = TC_H_CLSACT;\n    message.TcMsg.tcm_info = 0;\n    message.Kind.header.rta_len = sizeof(kind) + sizeof(rtattr);\n    message.Kind.header.rta_type = TCA_KIND;\n    std::copy(kind, kind + sizeof(kind), message.Kind.value);\n\n    NetlinkChannel channel;\n    auto transaction =\n        channel.CreateTransaction(message, Add ? RTM_NEWQDISC : RTM_DELQDISC, NLM_F_REQUEST | (Add ? (NLM_F_CREATE | NLM_F_EXCL) : 0));\n    transaction.Execute();\n}\n\nvoid Interface::BpfAttachTcClassifier(int ProgramFd, bool Ingress)\n{\n    char name[] = \"gns\";\n    struct TcOptions\n    {\n        Attribute<int> Fd;\n        Attribute<char[sizeof(name)]> Name;\n        Attribute<int> Flags;\n    } __attribute__((packed));\n    char kind[] = \"bpf\";\n    struct Message\n    {\n        tcmsg TcMsg;\n        Attribute<char[sizeof(kind)]> Kind;\n        Attribute<TcOptions> TcOptions;\n    } __attribute__((packed));\n    Message message{};\n\n    message.TcMsg.tcm_family = AF_UNSPEC;\n    message.TcMsg.tcm_ifindex = m_index;\n    message.TcMsg.tcm_handle = 0;\n    message.TcMsg.tcm_parent = TC_H_MAKE(TC_H_CLSACT, Ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);\n    message.TcMsg.tcm_info = htons(ETH_P_ALL);\n    message.Kind.header.rta_len = sizeof(kind) + sizeof(rtattr);\n    message.Kind.header.rta_type = TCA_KIND;\n    std::copy(kind, kind + sizeof(kind), message.Kind.value);\n    message.TcOptions.header.rta_len = sizeof(TcOptions) + sizeof(rtattr);\n    message.TcOptions.header.rta_type = TCA_OPTIONS;\n    message.TcOptions.value.Fd.header.rta_len = sizeof(int) + sizeof(rtattr);\n    message.TcOptions.value.Fd.header.rta_type = TCA_BPF_FD;\n    message.TcOptions.value.Fd.value = ProgramFd;\n    message.TcOptions.value.Name.header.rta_len = sizeof(name) + sizeof(rtattr);\n    message.TcOptions.value.Name.header.rta_type = TCA_BPF_NAME;\n    message.TcOptions.value.Flags.header.rta_len = sizeof(int) + sizeof(rtattr);\n    message.TcOptions.value.Flags.header.rta_type = TCA_BPF_FLAGS;\n    message.TcOptions.value.Flags.value = TCA_BPF_FLAG_ACT_DIRECT;\n    std::copy(name, name + sizeof(name), message.TcOptions.value.Name.value);\n\n    NetlinkChannel channel;\n    auto transaction = channel.CreateTransaction(message, RTM_NEWTFILTER, NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE);\n    transaction.Execute();\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/Interface.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#pragma once\r\n\r\n#include <string>\r\n#include <net/if.h>\r\n#include <vector>\r\n#include <optional>\r\n#include <linux/netlink.h>\r\n#include \"InterfaceConfiguration.h\"\r\n#include \"NetlinkResponse.h\"\r\n#include \"Operation.h\"\r\n\r\nclass Interface\r\n{\r\npublic:\r\n    Interface();\r\n    Interface(const Interface& source);\r\n    Interface(int index, const std::string& name);\r\n\r\n    operator bool() const;\r\n\r\n    InterfaceConfiguration Ipv4Configuration();\r\n    InterfaceConfiguration Ipv6Configuration();\r\n\r\n    void CreateVirtualWifiAdapter(const std::string& wifiName);\r\n    void CreateProxyWifiAdapter(const std::string& wifiName);\r\n    static void CreateBondAdapter(const std::string& bondName);\r\n    static void CreateTunAdapter(const std::string& name);\r\n    static void CreateTapAdapter(const std::string& name);\r\n    void DeleteInterface();\r\n\r\n    void SetIpv4Configuration(const InterfaceConfiguration& configuration);\r\n    void SetIpv6Configuration(const InterfaceConfiguration& configuration);\r\n\r\n    void ModifyIpAddress(const Address& address, Operation operation);\r\n\r\n    void SetMacAddress(const MacAddress& address);\r\n    MacAddress GetMacAddress();\r\n\r\n    void SetName(const std::string& newName);\r\n\r\n    void SetNamespace(int namespaceFd);\r\n\r\n    void SetWiphyNamespace(int namespaceFd);\r\n\r\n    void AddFlags(int flags);\r\n\r\n    void RemoveFlags(int flags);\r\n\r\n    void SetUp();\r\n\r\n    void SetDown();\r\n\r\n    int Index() const;\r\n\r\n    void SetMtu(int mtu);\r\n\r\n    void SetMetric(int metric);\r\n\r\n    void RemoveFromBond(const Interface& child_interface);\r\n\r\n    void AddToBond(const Interface& child_interface);\r\n\r\n    void SetActiveChild(const Interface& child_interface);\r\n\r\n    void DisableNetworkSetting(const char* settingName, int addressFamily);\r\n    void EnableNetworkSetting(const char* settingName, int addressFamily);\r\n\r\n    void ResetIpv6State();\r\n\r\n    const std::string& Name() const;\r\n\r\n    static Interface Open(const std::string& name);\r\n\r\n    void ModifyTcClassifier(bool Add);\r\n    void BpfAttachTcClassifier(int ProgramFd, bool Ingress);\r\n\r\nprivate:\r\n    template <size_t wifiTypeArraySize>\r\n    void CreateWifiAdapter(const std::string& wifiName, const char (&wifiType)[wifiTypeArraySize]);\r\n\r\n    template <typename TAddr>\r\n    void ChangeAddress(const Address& address, const std::optional<Address>& broadcastAddress, Operation operation);\r\n\r\n    template <typename TAddr, typename TMessage>\r\n    void ChangeAddressImpl(const Address& address, const std::optional<Address>& broadcastAddress, Operation operation);\r\n\r\n    template <typename TAddr>\r\n    void SetConfiguration(const InterfaceConfiguration& current, const InterfaceConfiguration& configuration);\r\n\r\n    static void CreateTunTapAdapter(const std::string& name, bool TunAdapter);\r\n\r\n    InterfaceConfiguration ListAddressesImpl(int af);\r\n\r\n    int m_index = -1;\r\n    std::string m_name;\r\n};\r\n"
  },
  {
    "path": "src/linux/netlinkutil/InterfaceConfiguration.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <string>\n#include <vector>\n#include <optional>\n#include \"address.h\"\n\nstruct InterfaceConfiguration\n{\n    std::vector<Address> Addresses;\n    std::vector<Address> LocalAddresses;\n    std::optional<Address> BroadcastAddress;\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/IpNeighborManager.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include <linux/if_packet.h>\r\n#include <linux/if_ether.h>\r\n#include <chrono>\r\n#include <arpa/inet.h>\r\n#include <poll.h>\r\n#include \"lxwil.h\"\r\n#include \"RuntimeErrorWithSourceLocation.h\"\r\n#include \"IpNeighborManager.h\"\r\n#include \"NetlinkTransactionError.h\"\r\n#include \"Utils.h\"\r\n\r\n#define ARPHRD_ETHER 1\r\n#define ARPOP_REQUEST 1\r\n#define ARPOP_REPLY 2\r\n\r\nconst MacAddress BroadcastMac{0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r\n\r\ntemplate <size_t TProtocolAddressLength>\r\nstruct _arp_packet_header\r\n{\r\n    using IPAddress = std::array<uint8_t, TProtocolAddressLength>;\r\n    MacAddress Destination;\r\n    MacAddress Source;\r\n    uint16_t EthernetType;\r\n    uint16_t HardwareType;\r\n    uint16_t ProtocolType;\r\n    uint8_t HardwareAddressLength;\r\n    uint8_t ProtocolAddressLength;\r\n    uint16_t Operation;\r\n    MacAddress SenderHardwareAddress;\r\n    IPAddress SenderIpAddress;\r\n    MacAddress TargetHardwareAddress;\r\n    IPAddress TargetIpAddress;\r\n} __attribute__((packed));\r\n\r\nusing arp_packet_ipv4_t = _arp_packet_header<4>;\r\nusing arp_packet_ipv6_t = _arp_packet_header<16>;\r\n\r\ntemplate <typename T>\r\nvoid ComposeArpRequest(T& ArpRequest, uint16_t ProtocolType, const Neighbor& Source, const Neighbor& Target)\r\n{\r\n    // Ethernet header\r\n    ArpRequest.Destination = BroadcastMac;\r\n    ArpRequest.Source = Source.macAddress;\r\n    ArpRequest.EthernetType = htons(ETH_P_ARP);\r\n    ArpRequest.HardwareType = htons(ARPHRD_ETHER);\r\n    ArpRequest.ProtocolType = htons(ProtocolType);\r\n    ArpRequest.HardwareAddressLength = sizeof(ArpRequest.SenderHardwareAddress);\r\n    ArpRequest.ProtocolAddressLength = sizeof(ArpRequest.SenderIpAddress);\r\n    ArpRequest.Operation = htons(ARPOP_REQUEST);\r\n    ArpRequest.SenderHardwareAddress = Source.macAddress;\r\n    ArpRequest.TargetHardwareAddress.fill(0);\r\n    Source.ipAddress.ConvertToBytes(ArpRequest.SenderIpAddress.data());\r\n    Target.ipAddress.ConvertToBytes(ArpRequest.TargetIpAddress.data());\r\n}\r\n\r\ntemplate <typename T>\r\nbool ParseArpReply(const T& ArpReply, uint16_t ProtocolType, const Neighbor& Source, Neighbor& Target)\r\n{\r\n    typename T::IPAddress SourceIp;\r\n    typename T::IPAddress TargetIp;\r\n    Source.ipAddress.ConvertToBytes(SourceIp.data());\r\n    Target.ipAddress.ConvertToBytes(TargetIp.data());\r\n\r\n    if (ArpReply.Destination != Source.macAddress)\r\n    {\r\n        return false;\r\n    }\r\n    if (ArpReply.EthernetType != htons(ETH_P_ARP))\r\n    {\r\n        return false;\r\n    }\r\n    if (ArpReply.HardwareType != htons(ARPHRD_ETHER))\r\n    {\r\n        return false;\r\n    }\r\n    if (ArpReply.ProtocolType != htons(ProtocolType))\r\n    {\r\n        return false;\r\n    }\r\n    if (ArpReply.HardwareAddressLength != sizeof(ArpReply.SenderHardwareAddress))\r\n    {\r\n        return false;\r\n    }\r\n    if (ArpReply.ProtocolAddressLength != sizeof(ArpReply.SenderIpAddress))\r\n    {\r\n        return false;\r\n    }\r\n    if (ArpReply.Operation != htons(ARPOP_REPLY))\r\n    {\r\n        return false;\r\n    }\r\n    if (ArpReply.TargetHardwareAddress != Source.macAddress)\r\n    {\r\n        return false;\r\n    }\r\n    if (ArpReply.TargetIpAddress != SourceIp)\r\n    {\r\n        return false;\r\n    }\r\n\r\n    Target.macAddress = ArpReply.SenderHardwareAddress;\r\n    return true;\r\n}\r\n\r\nbool IpNeighborManager::PerformNeighborDiscovery(Neighbor& Local, Neighbor& Neighbor)\r\n{\r\n    sockaddr_ll address{};\r\n    wil::unique_fd packet_socket = Syscall(socket, AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(ETH_P_ALL));\r\n    address.sll_family = AF_PACKET;\r\n    address.sll_protocol = htons(ETH_P_ALL);\r\n    address.sll_ifindex = Neighbor.dev;\r\n    Syscall(bind, packet_socket.get(), reinterpret_cast<sockaddr*>(&address), sizeof(address));\r\n\r\n    union\r\n    {\r\n        arp_packet_ipv4_t IPv4;\r\n        arp_packet_ipv6_t IPv6;\r\n    } ArpRequest, ArpReply;\r\n    size_t ArpPacketSize = Local.getFamily() == AF_INET ? sizeof(arp_packet_ipv4_t) : sizeof(arp_packet_ipv6_t);\r\n\r\n    if (Local.getFamily() == AF_INET)\r\n    {\r\n        ComposeArpRequest(ArpRequest.IPv4, ETH_P_IP, Local, Neighbor);\r\n    }\r\n    else\r\n    {\r\n        ComposeArpRequest(ArpRequest.IPv6, ETH_P_IPV6, Local, Neighbor);\r\n    }\r\n\r\n    auto wait_for_read = [](int fd, std::chrono::milliseconds timeout_ms) -> bool {\r\n        short poll_events = POLLIN | POLLPRI;\r\n        struct pollfd pollfds[1];\r\n        pollfds[0] = {.fd = fd, .events = poll_events, .revents = 0};\r\n\r\n        int return_value = Syscall(poll, pollfds, 1, timeout_ms.count());\r\n        return (return_value == 1) && (pollfds[0].revents == POLLIN);\r\n    };\r\n\r\n    for (size_t retry = 0; retry < 5; retry++)\r\n    {\r\n        auto expiry = std::chrono::steady_clock::now() + std::chrono::milliseconds(500);\r\n        Syscall(write, packet_socket.get(), &ArpRequest, ArpPacketSize);\r\n\r\n        while (std::chrono::steady_clock::now() < expiry)\r\n        {\r\n            if (!wait_for_read(packet_socket.get(), std::chrono::duration_cast<std::chrono::milliseconds>(expiry - std::chrono::steady_clock::now())))\r\n            {\r\n                continue;\r\n            }\r\n            int bytes_read = Syscall(read, packet_socket.get(), &ArpReply, ArpPacketSize);\r\n            if (bytes_read != ArpPacketSize)\r\n            {\r\n                continue;\r\n            }\r\n            if (Local.getFamily() == AF_INET)\r\n            {\r\n                if (ParseArpReply(ArpReply.IPv4, ETH_P_IP, Local, Neighbor))\r\n                {\r\n                    return true;\r\n                }\r\n            }\r\n            else\r\n            {\r\n                if (ParseArpReply(ArpReply.IPv6, ETH_P_IPV6, Local, Neighbor))\r\n                {\r\n                    return true;\r\n                }\r\n            }\r\n        }\r\n    }\r\n    return false;\r\n}\r\n\r\nvoid IpNeighborManager::ModifyNeighborEntry(const Neighbor& Neighbor, Operation operation)\r\n{\r\n    assert(operation == Operation::Create || operation == Operation::Update || operation == Operation::Remove);\r\n\r\n    // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK.\r\n    int flags = 0;\r\n    if (operation == Update)\r\n    {\r\n        flags = NLM_F_CREATE | NLM_F_REPLACE;\r\n    }\r\n    else if (operation == Create)\r\n    {\r\n        flags = NLM_F_CREATE;\r\n    }\r\n\r\n    int netlinkOperation = operation == Remove ? RTM_DELNEIGH : RTM_NEWNEIGH;\r\n    if (Neighbor.getFamily() == AF_INET)\r\n    {\r\n        ModifyNeighborEntryImpl<in_addr>(Neighbor, netlinkOperation, flags);\r\n    }\r\n    else\r\n    {\r\n        ModifyNeighborEntryImpl<in6_addr>(Neighbor, netlinkOperation, flags);\r\n    }\r\n}\r\n\r\ntemplate <typename T>\r\nvoid IpNeighborManager::SendMessage(const Neighbor& Neighbor, int operation, int flags, const std::function<void(T&)>& routine)\r\n{\r\n    T message{};\r\n    message.header.ndm_family = Neighbor.getFamily();\r\n    message.header.ndm_ifindex = Neighbor.dev;\r\n    message.header.ndm_state = NUD_PERMANENT;\r\n    message.header.ndm_type = RTN_UNICAST;\r\n\r\n    routine(message);\r\n\r\n    auto transaction = m_channel.CreateTransaction(message, operation, flags);\r\n    try\r\n    {\r\n        transaction.Execute();\r\n    }\r\n    catch (const NetlinkTransactionError& transactionErr)\r\n    {\r\n        auto errorCode = transactionErr.Error();\r\n        if (errorCode.has_value())\r\n        {\r\n            // Errors \"file exists\" and \"file not found\" are ignored in order to avoid keeping\r\n            // track in GnsDaemon of what neighbour entries were added/deleted and allow the same entry\r\n            // to be added/deleted multiple times.\r\n            if (errorCode.value() == -EEXIST || errorCode.value() == -ENOENT)\r\n            {\r\n                return;\r\n            }\r\n        }\r\n\r\n        throw;\r\n    }\r\n}\r\n\r\ntemplate <typename TAddr>\r\nvoid IpNeighborManager::ModifyNeighborEntryImpl(const Neighbor& Neighbor, int operation, int flags)\r\n{\r\n    struct Message\r\n    {\r\n        ndmsg header;\r\n        utils::AddressAttribute<TAddr> ip;\r\n        utils::MacAddressAttribute mac;\r\n    } __attribute__((packed));\r\n\r\n    SendMessage<Message>(Neighbor, operation, flags, [&](Message& message) {\r\n        utils::InitializeAddressAttribute<TAddr>(message.ip, Neighbor.ipAddress, NDA_DST);\r\n\r\n        message.mac.header.nla_len = sizeof(message.mac);\r\n        message.mac.header.nla_type = NDA_LLADDR;\r\n        message.mac.address = Neighbor.macAddress;\r\n    });\r\n}\r\n"
  },
  {
    "path": "src/linux/netlinkutil/IpNeighborManager.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#pragma once\r\n\r\n#include <functional>\r\n#include \"NetlinkChannel.h\"\r\n#include \"Neighbor.h\"\r\n#include \"Operation.h\"\r\n\r\nclass IpNeighborManager\r\n{\r\npublic:\r\n    /*\r\n        Implement netlink equivalent of \"ip neigh <operation> <IP> lladdr <MAC> dev <interface> nud permanent\".\r\n    */\r\n    void ModifyNeighborEntry(const Neighbor& Neighbor, Operation operation);\r\n\r\n    static bool PerformNeighborDiscovery(Neighbor& Local, Neighbor& Neighbor);\r\n\r\nprivate:\r\n    template <typename TAddr>\r\n    void ModifyNeighborEntryImpl(const Neighbor& Neighbor, int operation, int flags);\r\n\r\n    /*\r\n        Creates the message using the routine, then sends the message via netlink.\r\n    */\r\n    template <typename T>\r\n    void SendMessage(const Neighbor& Neighbor, int operation, int flags, const std::function<void(T&)>& routine = [](auto&) {});\r\n\r\n    NetlinkChannel m_channel;\r\n};\r\n"
  },
  {
    "path": "src/linux/netlinkutil/IpRuleManager.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#include \"RuntimeErrorWithSourceLocation.h\"\r\n#include \"IpRuleManager.h\"\r\n#include \"NetlinkTransactionError.h\"\r\n#include \"Utils.h\"\r\n#include \"common.h\"\r\n\r\nstruct LoopbackInterfaceAttribute\r\n{\r\n    rtattr header;\r\n    // A buffer of size 4 is used for alignment purposes (aligning to NLA_ALIGNTO).\r\n    // The loopback interface name is hardcoded as 'lo'.\r\n    char interface[4] = {'l', 'o', '\\0', '\\0'};\r\n} __attribute__((packed));\r\n\r\ntemplate <DerivedRuleMessage TMessage>\r\nvoid IpRuleManager::SendMessage(unsigned char family, int routingTable, int operation, int flags, const std::function<void(TMessage&)>& routine)\r\n{\r\n    TMessage message{};\r\n    message.rule.rtm_family = family;\r\n    message.rule.rtm_table = 0;\r\n    message.rule.rtm_protocol = RTPROT_BOOT;\r\n    message.rule.rtm_type = RTN_UNICAST;\r\n    message.rule.rtm_scope = RT_SCOPE_UNIVERSE;\r\n\r\n    utils::InitializeIntegerAttribute(message.tableId, routingTable, FRA_TABLE);\r\n\r\n    routine(message);\r\n\r\n    auto transaction = m_channel.CreateTransaction(message, operation, flags);\r\n    try\r\n    {\r\n        transaction.Execute();\r\n    }\r\n    catch (const NetlinkTransactionError& transactionErr)\r\n    {\r\n        auto errorCode = transactionErr.Error();\r\n        if (errorCode.has_value())\r\n        {\r\n            // Errors \"file exists\" and \"file not found\" are ignored in order to avoid keeping\r\n            // track in GnsDaemon of what rules were added/deleted and allow the same rule\r\n            // to be added/deleted multiple times.\r\n            if (errorCode.value() == -EEXIST || errorCode.value() == -ENOENT)\r\n            {\r\n                return;\r\n            }\r\n        }\r\n\r\n        throw;\r\n    }\r\n}\r\n\r\nvoid IpRuleManager::ModifyLoopbackRule(const Rule& rule, Operation operation)\r\n{\r\n    if (!rule.protocol.has_value())\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(\"Loopback rule missing protocol\");\r\n    }\r\n    if (operation != Operation::Create && operation != Operation::Remove)\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected operation: {}\", static_cast<int>(operation)));\r\n    }\r\n    if (rule.iif.empty())\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(\"Loopback rule has empty iif name\");\r\n    }\r\n\r\n    GNS_LOG_INFO(\"{} rule {}\", operation == Operation::Create ? \"Add\" : \"Remove\", utils::Stringify(rule).c_str());\r\n\r\n    struct Message\r\n    {\r\n        rtmsg rule;\r\n        utils::IntegerAttribute tableId;\r\n        utils::IntegerAttribute priority;\r\n        utils::Attribute<uint8_t> protocol;\r\n        utils::Attribute<char> iifName;\r\n    } __attribute__((packed));\r\n\r\n    // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK.\r\n    int flags = 0;\r\n    if (operation == Create)\r\n    {\r\n        flags = NLM_F_CREATE;\r\n    }\r\n\r\n    int netlinkOperation = operation == Remove ? RTM_DELRULE : RTM_NEWRULE;\r\n\r\n    auto buffer = std::vector<char>(RTA_ALIGN(offsetof(Message, iifName.value) + rule.iif.size()));\r\n    auto* message = gslhelpers::get_struct<Message>(gsl::make_span(buffer));\r\n\r\n    message->rule.rtm_family = rule.family;\r\n    message->rule.rtm_table = 0;\r\n    message->rule.rtm_protocol = RTPROT_BOOT;\r\n    message->rule.rtm_type = RTN_UNICAST;\r\n    message->rule.rtm_scope = RT_SCOPE_UNIVERSE;\r\n\r\n    utils::InitializeIntegerAttribute(message->tableId, rule.routingTable, FRA_TABLE);\r\n    utils::InitializeIntegerAttribute(message->priority, rule.priority, RTA_PRIORITY);\r\n\r\n    message->protocol.header.rta_len = RTA_LENGTH(sizeof(uint8_t));\r\n    message->protocol.header.rta_type = FRA_IP_PROTO;\r\n    message->protocol.value = rule.protocol.value() == Tcp ? IPPROTO_TCP : IPPROTO_UDP;\r\n\r\n    message->iifName.header.rta_len = RTA_SPACE(rule.iif.size());\r\n    message->iifName.header.rta_type = FRA_IIFNAME;\r\n    auto iifNameBuffer = gsl::make_span(buffer).subspan(offsetof(Message, iifName.value));\r\n    gsl::copy(gsl::make_span(rule.iif), iifNameBuffer);\r\n\r\n    // The SendMessage helper cannot be used here because we are sending a variable sized message\r\n    // (the message contains the iifName field with variable length)\r\n    auto transaction = m_channel.CreateTransaction(buffer.data(), buffer.size(), netlinkOperation, flags);\r\n    try\r\n    {\r\n        transaction.Execute();\r\n    }\r\n    catch (const NetlinkTransactionError& transactionErr)\r\n    {\r\n        auto errorCode = transactionErr.Error();\r\n        if (errorCode.has_value())\r\n        {\r\n            // Errors \"file exists\" and \"file not found\" are ignored in order to avoid keeping\r\n            // track in GnsDaemon of what rules were added/deleted and allow the same rule\r\n            // to be added/deleted multiple times.\r\n            if (errorCode.value() == -EEXIST || errorCode.value() == -ENOENT)\r\n            {\r\n                return;\r\n            }\r\n        }\r\n\r\n        throw;\r\n    }\r\n}\r\n\r\nvoid IpRuleManager::ModifyLoopbackRuleWithSourceAddress(const Rule& rule, Operation action)\r\n{\r\n    if (rule.family != AF_INET && rule.family != AF_INET6)\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected address family: {}\", rule.family));\r\n    }\r\n    if (action != Operation::Create && action != Operation::Remove)\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected operation: {}\", static_cast<int>(action)));\r\n    }\r\n\r\n    if (rule.family == AF_INET)\r\n    {\r\n        ModifyLoopbackRuleWithSourceAddressImpl<in_addr>(rule, action);\r\n    }\r\n    else\r\n    {\r\n        ModifyLoopbackRuleWithSourceAddressImpl<in6_addr>(rule, action);\r\n    }\r\n}\r\n\r\ntemplate <typename TAddr>\r\nvoid IpRuleManager::ModifyLoopbackRuleWithSourceAddressImpl(const Rule& rule, Operation operation)\r\n{\r\n    if (!rule.protocol.has_value())\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(\"Rule missing protocol\");\r\n    }\r\n    if (!rule.sourceAddress.has_value())\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(\"Rule missing source IP\");\r\n    }\r\n\r\n    GNS_LOG_INFO(\"{} rule {}\", operation == Operation::Create ? \"Add\" : \"Remove\", utils::Stringify(rule).c_str());\r\n\r\n    int flags = (operation == Create) ? NLM_F_CREATE : 0;\r\n\r\n    struct Message : RuleMessage\r\n    {\r\n        LoopbackInterfaceAttribute devName;\r\n        utils::AddressAttribute<TAddr> from;\r\n        utils::IntegerAttribute priority;\r\n        utils::Attribute<uint8_t> protocol;\r\n    } __attribute__((packed));\r\n\r\n    int netlinkOperation = operation == Remove ? RTM_DELRULE : RTM_NEWRULE;\r\n\r\n    SendMessage<Message>(rule.family, rule.routingTable, netlinkOperation, flags, [&](Message& message) {\r\n        message.devName.header.rta_len = sizeof(message.devName);\r\n        message.devName.header.rta_type = FRA_IIFNAME;\r\n\r\n        // Set source address in the rule\r\n        message.rule.rtm_src_len = rule.sourceAddress->PrefixLength();\r\n        utils::InitializeAddressAttribute<TAddr>(message.from, rule.sourceAddress.value(), FRA_SRC);\r\n\r\n        utils::InitializeIntegerAttribute(message.priority, rule.priority, RTA_PRIORITY);\r\n\r\n        message.protocol.header.rta_len = RTA_LENGTH(sizeof(uint8_t));\r\n        message.protocol.header.rta_type = FRA_IP_PROTO;\r\n        message.protocol.value = rule.protocol.value() == Tcp ? IPPROTO_TCP : IPPROTO_UDP;\r\n    });\r\n}\r\n\r\nvoid IpRuleManager::ModifyRoutingTablePriority(const Rule& rule, Operation operation)\r\n{\r\n    if (operation != Operation::Create && operation != Operation::Remove)\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected operation: {}\", static_cast<int>(operation)));\r\n    }\r\n\r\n    GNS_LOG_INFO(\"{} rule {}\", operation == Operation::Create ? \"Add\" : \"Remove\", utils::Stringify(rule));\r\n\r\n    struct Message : RuleMessage\r\n    {\r\n        utils::IntegerAttribute priority;\r\n    } __attribute__((packed));\r\n\r\n    // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK.\r\n    int flags = 0;\r\n    if (operation == Create)\r\n    {\r\n        flags = NLM_F_CREATE;\r\n    }\r\n\r\n    int netlinkOperation = operation == Remove ? RTM_DELRULE : RTM_NEWRULE;\r\n\r\n    SendMessage<Message>(rule.family, rule.routingTable, netlinkOperation, flags, [&](Message& message) {\r\n        utils::InitializeIntegerAttribute(message.priority, rule.priority, RTA_PRIORITY);\r\n    });\r\n}\r\n\r\nvoid IpRuleManager::ModifyRoutingTablePriorityWithProtocol(const Rule& rule, Operation operation)\r\n{\r\n    if (operation != Operation::Create && operation != Operation::Remove)\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected operation: {}\", static_cast<int>(operation)));\r\n    }\r\n    if (!rule.protocol.has_value())\r\n    {\r\n        throw RuntimeErrorWithSourceLocation(\"Rule missing protocol\");\r\n    }\r\n\r\n    GNS_LOG_INFO(\"{} rule {}\", operation == Operation::Create ? \"Add\" : \"Remove\", utils::Stringify(rule));\r\n\r\n    struct Message : RuleMessage\r\n    {\r\n        utils::IntegerAttribute priority;\r\n        utils::Attribute<uint8_t> protocol;\r\n    } __attribute__((packed));\r\n\r\n    // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK.\r\n    int flags = 0;\r\n    if (operation == Create)\r\n    {\r\n        flags = NLM_F_CREATE;\r\n    }\r\n\r\n    int netlinkOperation = operation == Remove ? RTM_DELRULE : RTM_NEWRULE;\r\n\r\n    SendMessage<Message>(rule.family, rule.routingTable, netlinkOperation, flags, [&](Message& message) {\r\n        utils::InitializeIntegerAttribute(message.priority, rule.priority, RTA_PRIORITY);\r\n\r\n        message.protocol.header.rta_len = RTA_LENGTH(sizeof(uint8_t));\r\n        message.protocol.header.rta_type = FRA_IP_PROTO;\r\n        message.protocol.value = rule.protocol.value() == Tcp ? IPPROTO_TCP : IPPROTO_UDP;\r\n    });\r\n}\r\n\r\nstd::vector<Rule> IpRuleManager::ListRules(int family, int tableId)\r\n{\r\n    struct Message\r\n    {\r\n        rtmsg rule;\r\n    } __attribute__((packed));\r\n\r\n    std::vector<Rule> rules{};\r\n\r\n    auto onMessage = [&](const NetlinkResponse& response) {\r\n        for (const auto& e : response.Messages<rtmsg>(RTM_NEWRULE))\r\n        {\r\n            const auto msg = e.Payload();\r\n            const auto priorityAttr = e.UniqueAttribute<int>(FRA_PRIORITY);\r\n            const auto oifAttr = e.UniqueAttribute<char>(FRA_OIFNAME);\r\n            const auto protocolAttr = e.UniqueAttribute<uint8_t>(FRA_IP_PROTO);\r\n            const auto tableAttr = e.UniqueAttribute<int>(FRA_TABLE);\r\n\r\n            int priority = priorityAttr.has_value() ? *priorityAttr.value() : -1;\r\n            std::string oif(oifAttr.value_or(\"\"));\r\n            uint8_t proto = protocolAttr.has_value() ? *protocolAttr.value() : -1;\r\n            std::optional<Protocol> protocol =\r\n                (proto == IPPROTO_TCP) ? std::optional(Tcp) : ((proto == IPPROTO_UDP) ? std::optional(Udp) : std::optional<Protocol>());\r\n            int tableAttrId = tableAttr.has_value() ? *tableAttr.value() : msg->rtm_table;\r\n\r\n            rules.emplace_back(Rule(msg->rtm_family, tableAttrId, priority, oif, protocol));\r\n        }\r\n    };\r\n\r\n    rtmsg message{};\r\n    message.rtm_family = family;\r\n    message.rtm_table = 0;\r\n\r\n    auto transaction = m_channel.CreateTransaction(message, RTM_GETRULE, NLM_F_DUMP);\r\n    transaction.Execute(onMessage);\r\n\r\n    return rules;\r\n}\r\n"
  },
  {
    "path": "src/linux/netlinkutil/IpRuleManager.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#pragma once\r\n\r\n#include <functional>\r\n#include \"NetlinkChannel.h\"\r\n#include \"Rule.h\"\r\n#include \"Operation.h\"\r\n\r\nstruct RuleMessage\r\n{\r\n    rtmsg rule;\r\n    utils::IntegerAttribute tableId;\r\n} __attribute__((packed));\r\n\r\n// The below a means to ensure that messages have a common set of fields.  It just means\r\n// that a type T must inherit from RuleMessage, and any functions that reference\r\n// DerivedRuleMessage can be assured that they can safely access the fields in RuleMessage.\r\ntemplate <typename TMessage>\r\nconcept DerivedRuleMessage = std::is_base_of<RuleMessage, TMessage>::value;\r\n\r\nclass IpRuleManager\r\n{\r\npublic:\r\n    /*\r\n        Implements netlink equivalent of \"ip rule <operation> iif <interface> ipproto <protocol> prio <priority> table <table>\".\r\n    */\r\n    void ModifyLoopbackRule(const Rule& rule, Operation operation);\r\n\r\n    /*\r\n        Implements netlink equivalent of \"ip rule <operation> from <source IP> iif lo ipproto <protocol> prio <priority> table <table>\".\r\n    */\r\n    void ModifyLoopbackRuleWithSourceAddress(const Rule& rule, Operation operation);\r\n\r\n    /*\r\n        Implements netlink equivalent of \"ip rule <operation> ipproto <protocol> prio <priority> table <table>\".\r\n    */\r\n    void ModifyRoutingTablePriorityWithProtocol(const Rule& rule, Operation operation);\r\n\r\n    /*\r\n        Implements netlink equivalent of \"ip rule <operation> prio <priority> table <table>\".\r\n    */\r\n    void ModifyRoutingTablePriority(const Rule& rule, Operation operation);\r\n\r\n    /*\r\n        Implements netlink equivalent of \"ip rule show\".\r\n    */\r\n    std::vector<Rule> ListRules(int family = AF_INET, int tableId = RT_TABLE_UNSPEC);\r\n\r\nprivate:\r\n    /*\r\n        Creates the message using the routine, then sends the message via netlink.\r\n    */\r\n    template <DerivedRuleMessage TMessage>\r\n    void SendMessage(unsigned char family, int routingTable, int operation, int flags, const std::function<void(TMessage&)>& routine = [](auto&) {});\r\n\r\n    template <typename TAddr>\r\n    void ModifyLoopbackRuleWithSourceAddressImpl(const Rule& rule, Operation operation);\r\n\r\n    NetlinkChannel m_channel;\r\n};\r\n"
  },
  {
    "path": "src/linux/netlinkutil/Makefile.linux",
    "content": "# This makefile is used to work from a linux environment and is not part of the OS build process.\n\nCXXFLAGS += -std=c++20 -Wall -Wextra -g3 -fPIC -I../inc\nLDFLAGS +=\n\nSRC = SyscallError main Interface NetlinkResponse NetlinkParseException RuntimeErrorWithSourceLocation Utils Address NetlinkTransaction NetlinkTransactionError\nOBJ = $(addsuffix .o, $(SRC))\nBIN = test\nCXX=clang++\n\n$(BIN): $(OBJ)\n\t$(CXX) $(CXXFLAGS) $(OBJ) $(LDFLAGS) -o $(BIN)\n\nclean:\n\t$(RM) $(OBJ) $(LIB) $(BIN)\n\ncheck: test\n\t./test\n\nall: check\n"
  },
  {
    "path": "src/linux/netlinkutil/Neighbor.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#include \"Neighbor.h\"\r\n\r\nNeighbor::Neighbor(const Address& ipAddress, const MacAddress& macAddress, int dev) :\r\n    ipAddress(ipAddress), macAddress(macAddress), dev(dev)\r\n{\r\n}\r\n\r\nint Neighbor::getFamily() const\r\n{\r\n    return ipAddress.Family();\r\n}\r\n"
  },
  {
    "path": "src/linux/netlinkutil/Neighbor.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#pragma once\r\n\r\n#include \"address.h\"\r\n\r\nstruct Neighbor\r\n{\r\n    Address ipAddress;\r\n    MacAddress macAddress;\r\n    int dev = -1;\r\n\r\n    Neighbor(const Address& ipAddress, const MacAddress& macAddress, int dev);\r\n\r\n    int getFamily() const;\r\n};\r\n"
  },
  {
    "path": "src/linux/netlinkutil/NetLinkStrings.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#pragma once\r\n#include <string>\r\n#include <linux/rtnetlink.h>\r\n#include <linux/rtnetlink.h>\r\n\r\ninline std::string NetLinkFormatFlagsToString(int flags)\r\n{\r\n    if (flags == 0)\r\n    {\r\n        return \"[zero]\";\r\n    }\r\n\r\n    std::string returnString;\r\n    if (flags & NLM_F_CREATE)\r\n    {\r\n        returnString.append(\"NLM_F_CREATE \");\r\n    }\r\n    if (flags & NLM_F_REPLACE)\r\n    {\r\n        returnString.append(\"NLM_F_REPLACE \");\r\n    }\r\n    if (flags & NLM_F_DUMP)\r\n    {\r\n        returnString.append(\"NLM_F_DUMP \");\r\n    }\r\n    if (flags & NLM_F_REQUEST)\r\n    {\r\n        returnString.append(\"NLM_F_REQUEST \");\r\n    }\r\n    if (flags & NLM_F_EXCL)\r\n    {\r\n        returnString.append(\"NLM_F_EXCL \");\r\n    }\r\n    return returnString;\r\n}\r\n\r\ninline auto* RouteOperationToString(int operation)\r\n{\r\n    if (operation == RTM_DELROUTE)\r\n    {\r\n        return \"RTM_DELROUTE \";\r\n    }\r\n    if (operation == RTM_NEWROUTE)\r\n    {\r\n        return \"RTM_NEWROUTE \";\r\n    }\r\n    return \"[unknown route operation]\";\r\n}\r\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkChannel.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"NetlinkResponse.h\"\n#include \"NetlinkTransaction.h\"\n#include <sys/socket.h>\n#include \"lxwil.h\"\n\nclass NetlinkChannel\n{\npublic:\n    NetlinkChannel(int socketType = SOCK_RAW, int netlinkFamily = NETLINK_ROUTE, int groups = 0);\n    ~NetlinkChannel();\n\n    NetlinkChannel(const NetlinkChannel&) = delete;\n    NetlinkChannel(NetlinkChannel&& other);\n\n    const NetlinkChannel& operator=(const NetlinkChannel&) = delete;\n    const NetlinkChannel& operator=(NetlinkChannel&& other);\n\n    template <typename TRequest>\n    NetlinkTransaction CreateTransaction(const TRequest& message, int type, int flags);\n\n    NetlinkTransaction CreateTransaction(const void* message, size_t messageSize, int type, int flags);\n\n    NetlinkTransaction CreateTransaction(int type, int flags);\n\n    void SendMessage(const std::vector<char>& message);\n\n    NetlinkResponse ReceiveNetlinkResponse();\n\n    int GetInterfaceIndex(const std::string& name);\n\n    int GetInterfaceFlags(const std::string& name);\n\n    int SetInterfaceFlags(const std::string& name, int flags);\n\n    static NetlinkChannel FromFd(int fd);\n\nprivate:\n    struct Tag\n    {\n    };\n\n    NetlinkChannel(Tag);\n\n    NetlinkTransaction CreateTransactionImpl(std::vector<char>&& message, int type, int flags);\n\n    wil::unique_fd m_socket;\n\n    std::atomic<int> seqNumber;\n};\n\n#include \"NetlinkChannel.hxx\"\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkChannel.hxx",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n#include <sys/ioctl.h>\n#include <arpa/inet.h>\n#include <net/if.h>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include <unistd.h>\n#include <string.h>\n#include <atomic>\n\n#include \"NetlinkChannel.h\"\n#include \"Syscall.h\"\n\ninline NetlinkChannel::NetlinkChannel(int socketType, int netlinkFamily, int groups)\n{\n    m_socket.reset(Syscall(::socket, AF_NETLINK, socketType, netlinkFamily));\n\n    sockaddr_nl address = {};\n    address.nl_family = AF_NETLINK;\n    address.nl_groups = groups;\n\n    Syscall(bind, m_socket.get(), reinterpret_cast<sockaddr*>(&address), sizeof(address));\n}\n\ninline NetlinkChannel::NetlinkChannel(NetlinkChannel&& other)\n{\n    *this = std::move(other);\n}\n\ninline const NetlinkChannel& NetlinkChannel::operator=(NetlinkChannel&& other)\n{\n    m_socket = std::move(other.m_socket);\n\n    return *this;\n}\n\ninline NetlinkChannel::NetlinkChannel(Tag)\n{\n}\n\ninline NetlinkChannel::~NetlinkChannel()\n{\n}\n\ninline NetlinkChannel NetlinkChannel::FromFd(int fd)\n{\n    assert(fd != -1);\n\n    NetlinkChannel channel(Tag{});\n    channel.m_socket.reset(fd);\n\n    return channel;\n}\n\ninline NetlinkTransaction NetlinkChannel::CreateTransactionImpl(std::vector<char>&& message, int type, int flags)\n{\n    auto header = reinterpret_cast<nlmsghdr*>(message.data());\n    header->nlmsg_len = message.size();\n    header->nlmsg_type = type;\n    header->nlmsg_seq = ++seqNumber;\n    header->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;\n\n    return {*this, std::move(message), header->nlmsg_seq};\n}\n\ninline void NetlinkChannel::SendMessage(const std::vector<char>& message)\n{\n    Syscall(sendto, m_socket.get(), message.data(), message.size(), 0, nullptr, 0);\n}\n\ninline NetlinkTransaction NetlinkChannel::CreateTransaction(int type, int flags)\n{\n    auto header = std::vector<char>(sizeof(nlmsghdr));\n    return CreateTransactionImpl(std::move(header), type, flags);\n}\n\ntemplate <typename T>\nNetlinkTransaction NetlinkChannel::CreateTransaction(const T& message, int type, int flags)\n{\n    return CreateTransaction(&message, sizeof(T), type, flags);\n}\n\ninline NetlinkTransaction NetlinkChannel::CreateTransaction(const void* message, size_t messageSize, int type, int flags)\n{\n    struct Request\n    {\n        nlmsghdr header;\n        char message;\n    } __attribute__((packed));\n\n    if (messageSize == 0)\n    {\n        return CreateTransaction(type, flags);\n    }\n\n    std::vector<char> buffer(offsetof(Request, message) + messageSize);\n    const auto request = reinterpret_cast<Request*>(buffer.data());\n    memcpy(&request->message, message, messageSize);\n    return CreateTransactionImpl(std::move(buffer), type, flags);\n}\n\ninline NetlinkResponse NetlinkChannel::ReceiveNetlinkResponse()\n{\n    std::vector<char> buffer;\n\n    sockaddr_storage src = {};\n    iovec iov = {};\n\n    msghdr message = {};\n    message.msg_name = &src;\n    message.msg_namelen = sizeof(src);\n    message.msg_iov = &iov;\n    message.msg_iovlen = 1;\n\n    int size = Syscall(recvmsg, m_socket.get(), &message, MSG_PEEK | MSG_TRUNC);\n    buffer.resize(size);\n\n    size = Syscall(recvfrom, m_socket.get(), buffer.data(), buffer.size(), 0, nullptr, nullptr);\n    if (size != static_cast<int>(buffer.size()))\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected response size: {} != {}\", size, buffer.size()));\n    }\n\n    return {std::move(buffer)};\n}\n\ninline int NetlinkChannel::GetInterfaceIndex(const std::string& name)\n{\n    ifreq ifr = {};\n    strncpy(ifr.ifr_name, name.c_str(), sizeof(ifr.ifr_name) - 1);\n    Syscall(ioctl, m_socket.get(), SIOCGIFINDEX, &ifr);\n\n    return ifr.ifr_ifindex;\n}\n\ninline int NetlinkChannel::SetInterfaceFlags(const std::string& name, int flags)\n{\n    ifreq ifr = {};\n    strncpy(ifr.ifr_name, name.c_str(), sizeof(ifr.ifr_name) - 1);\n    ifr.ifr_flags = flags;\n\n    Syscall(ioctl, m_socket.get(), SIOCSIFFLAGS, &ifr);\n\n    return ifr.ifr_flags;\n}\n\ninline int NetlinkChannel::GetInterfaceFlags(const std::string& name)\n{\n    ifreq ifr = {};\n    strncpy(ifr.ifr_name, name.c_str(), sizeof(ifr.ifr_name) - 1);\n\n    Syscall(ioctl, m_socket.get(), SIOCGIFFLAGS, &ifr);\n\n    return ifr.ifr_flags;\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkError.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <format>\n#include <string>\n#include \"NetlinkError.h\"\n\nNetlinkError::NetlinkError(int error, const std::source_location& source) :\n    RuntimeErrorWithSourceLocation(std::format(\"Netlink returned error: {}\", error), source), m_error(error)\n{\n}\n\nint NetlinkError::Error() const\n{\n    return m_error;\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkError.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"RuntimeErrorWithSourceLocation.h\"\n\nclass NetlinkError : public RuntimeErrorWithSourceLocation\n{\npublic:\n    NetlinkError(int error, const std::source_location& source = std::source_location::current());\n\n    int Error() const;\n\nprivate:\n    int m_error = -1;\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkMessage.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <vector>\n#include <optional>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include \"Fwd.h\"\n\ntemplate <typename TMessage>\nclass NetlinkMessage\n{\npublic:\n    using Titerator = std::vector<char>::const_iterator;\n\n    NetlinkMessage(const NetlinkResponse& response, Titerator responseBegin, Titerator begin, Titerator end);\n\n    const TMessage* Payload() const;\n\n    const nlmsghdr* Header() const;\n\n    template <typename TAttribute>\n    std::vector<const TAttribute*> Attributes(int type) const;\n\n    template <typename TAttribute>\n    std::optional<const TAttribute*> UniqueAttribute(int type) const;\n\nprivate:\n    const rtattr* FirstAttribute() const;\n\n    const NetlinkResponse& m_response;\n    Titerator m_responseBegin;\n    Titerator m_begin;\n    Titerator m_end;\n};\n\n#include \"NetlinkMessage.hxx\"\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkMessage.hxx",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <format>\n#include <string>\n#include <linux/if_link.h>\n\n#include \"NetlinkMessage.h\"\n#include \"NetlinkParseException.h\"\n\ntemplate <typename TMessage>\nNetlinkMessage<TMessage>::NetlinkMessage(const NetlinkResponse& response, Titerator responseBegin, Titerator begin, Titerator end) :\n    m_response(response), m_responseBegin(responseBegin), m_begin(begin), m_end(end)\n{\n}\n\ntemplate <typename TMessage>\nconst TMessage* NetlinkMessage<TMessage>::Payload() const\n{\n    const auto* data = reinterpret_cast<const char*>(NLMSG_DATA(&*m_begin));\n    if (data + sizeof(TMessage) > &*m_end)\n    {\n        throw NetlinkParseException(\n            m_response,\n            std::format(\n                \"Message at offset {}: attempted to access beyond message offset ({} > {})\",\n                (m_begin - m_responseBegin),\n                sizeof(TMessage),\n                (m_end - m_begin)));\n    }\n\n    return reinterpret_cast<const TMessage*>(NLMSG_DATA(&*m_begin));\n}\n\ntemplate <>\ninline const rtattr* NetlinkMessage<rtmsg>::FirstAttribute() const\n{\n    return RTM_RTA(NLMSG_DATA(&*m_begin));\n}\n\ntemplate <>\ninline const rtattr* NetlinkMessage<ifaddrmsg>::FirstAttribute() const\n{\n    return IFA_RTA(NLMSG_DATA(&*m_begin));\n}\n\ntemplate <typename TAttribute>\nconst rtattr* NetlinkMessage<TAttribute>::FirstAttribute() const\n{\n    throw RuntimeErrorWithSourceLocation(\"Tried listing attributes for a message without attributes\");\n}\n\ntemplate <typename TMessage>\ntemplate <typename TAttribute>\nstd::vector<const TAttribute*> NetlinkMessage<TMessage>::Attributes(int type) const\n{\n    std::vector<const TAttribute*> attributes;\n\n    auto len = NLMSG_PAYLOAD(Header(), sizeof(TMessage));\n    for (const rtattr* e = FirstAttribute(); RTA_OK(e, len); e = RTA_NEXT(e, len))\n    {\n        if (e->rta_type == type)\n        {\n            const auto* ptr = reinterpret_cast<const TAttribute*>(RTA_DATA(e));\n\n            if (sizeof(TAttribute) > e->rta_len)\n            {\n                throw NetlinkParseException(\n                    m_response,\n                    std::format(\n                        \"Attribute at offset {}: attempted to access beyond attribute offset ({} > {})\",\n                        (reinterpret_cast<const char*>(e) - &*m_responseBegin),\n                        sizeof(TMessage),\n                        e->rta_len));\n            }\n\n            attributes.push_back(ptr);\n        }\n    }\n\n    return attributes;\n}\n\ntemplate <typename TMessage>\ntemplate <typename TAttribute>\nstd::optional<const TAttribute*> NetlinkMessage<TMessage>::UniqueAttribute(int type) const\n{\n    auto attributes = Attributes<TAttribute>(type);\n\n    if (attributes.empty())\n    {\n        return {};\n    }\n    else if (attributes.size() == 1)\n    {\n        return attributes[0];\n    }\n\n    throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected attribute count: {} for attribute type: {}\", attributes.size(), type));\n}\n\ntemplate <typename TMessage>\nconst nlmsghdr* NetlinkMessage<TMessage>::Header() const\n{\n    return reinterpret_cast<const nlmsghdr*>(&*m_begin);\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkParseException.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <sstream>\n#include \"NetlinkParseException.h\"\n#include \"NetlinkResponse.h\"\n#include \"Utils.h\"\n\nNetlinkParseException::NetlinkParseException(const NetlinkResponse& response, const std::string& reason, const std::source_location& source) :\n    RuntimeErrorWithSourceLocation(BuildMessage(response, reason), source)\n{\n}\n\nstd::string NetlinkParseException::BuildMessage(const NetlinkResponse& response, const std::string& reason)\n{\n    std::stringstream str;\n    str << reason << \" Netlink response: \";\n    utils::FormatBinary(str, &*response.Begin(), response.End() - response.Begin());\n\n    return str.str();\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkParseException.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"RuntimeErrorWithSourceLocation.h\"\n#include \"Fwd.h\"\n\nclass NetlinkParseException : public RuntimeErrorWithSourceLocation\n{\npublic:\n    NetlinkParseException(const NetlinkResponse& response, const std::string& reason, const std::source_location& source = std::source_location::current());\n\nprivate:\n    static std::string BuildMessage(const NetlinkResponse& response, const std::string& reason);\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkResponse.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"NetlinkResponse.h\"\n#include \"NetlinkError.h\"\n#include \"Utils.h\"\n\nNetlinkResponse::NetlinkResponse(std::vector<char>&& content) : m_data(std::move(content))\n{\n}\n\nvoid NetlinkResponse::ThrowIfErrorFound() const\n{\n    auto results = Messages<nlmsgerr>(NLMSG_ERROR);\n\n    if (results.empty())\n    {\n        return;\n    }\n\n    for (const auto& e : results)\n    {\n        int result = e.Payload()->error;\n\n        if (result != 0)\n        {\n            throw NetlinkError(result);\n        }\n    }\n}\n\n__u32 NetlinkResponse::Sequence() const\n{\n    return reinterpret_cast<const nlmsghdr*>(m_data.data())->nlmsg_seq;\n}\n\nbool NetlinkResponse::MultiMessage() const\n{\n    size_t size = m_data.size();\n    for (const auto* header = reinterpret_cast<const nlmsghdr*>(m_data.data()); NLMSG_OK(header, size); header = NLMSG_NEXT(header, size))\n    {\n        if (header->nlmsg_flags & NLM_F_MULTI)\n        {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nNetlinkResponse::Titerator NetlinkResponse::Begin() const\n{\n    return m_data.begin();\n}\n\nNetlinkResponse::Titerator NetlinkResponse::End() const\n{\n    return m_data.end();\n}\n\nbool NetlinkResponse::Done() const\n{\n    auto doneMessages = Messages<nlmsghdr>(NLMSG_DONE);\n\n    return !doneMessages.empty();\n}\n\nstd::ostream& operator<<(std::ostream& out, const NetlinkResponse& response)\n{\n    const auto begin = &*response.Begin();\n    const auto size = response.End() - response.Begin();\n\n    return utils::FormatBinary(out, begin, size);\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkResponse.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <vector>\n#include \"NetlinkMessage.h\"\n\nclass NetlinkResponse\n{\npublic:\n    using Titerator = std::vector<char>::const_iterator;\n\n    NetlinkResponse(std::vector<char>&& content);\n\n    void ThrowIfErrorFound() const;\n\n    std::vector<char>::const_iterator Begin() const;\n    std::vector<char>::const_iterator End() const;\n\n    template <typename T>\n    std::vector<NetlinkMessage<T>> Messages(int type) const;\n\n    __u32 Sequence() const;\n\n    bool MultiMessage() const;\n\n    bool Done() const;\n\nprivate:\n    std::vector<char> m_data;\n};\n\nstd::ostream& operator<<(std::ostream& out, const NetlinkResponse& response);\n\n#include \"NetlinkResponse.hxx\"\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkResponse.hxx",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"NetlinkResponse.h\"\n#include \"NetlinkParseException.h\"\n\ntemplate <typename T>\nstd::vector<NetlinkMessage<T>> NetlinkResponse::Messages(int type) const\n{\n    std::vector<NetlinkMessage<T>> messages;\n\n    size_t size = m_data.size();\n    for (const auto* header = reinterpret_cast<const nlmsghdr*>(m_data.data()); NLMSG_OK(header, size); header = NLMSG_NEXT(header, size))\n    {\n        if (header->nlmsg_type == type)\n        {\n            auto begin = m_data.begin() + (reinterpret_cast<const char*>(header) - m_data.data());\n            auto end = begin + header->nlmsg_len;\n            messages.emplace_back(*this, m_data.begin(), begin, end);\n        }\n    }\n\n    if (size != 0)\n    {\n        throw NetlinkParseException(*this, std::format(\"Netlink message is truncated. Missing bytes: {}\", size));\n    }\n\n    return messages;\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkTransaction.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"NetlinkChannel.h\"\n#include \"NetlinkTransaction.h\"\n#include \"NetlinkTransactionError.h\"\n\nNetlinkTransaction::NetlinkTransaction(NetlinkChannel& channel, std::vector<char>&& request, __u32 seq) :\n    m_channel(channel), m_request(std::move(request)), m_seq(seq)\n{\n}\n\nvoid NetlinkTransaction::Execute(const std::function<void(const NetlinkResponse&)>& routine)\n{\n    m_channel.SendMessage(m_request);\n\n    std::vector<NetlinkResponse> responses;\n\n    try\n    {\n        do\n        {\n            NetlinkResponse response = m_channel.ReceiveNetlinkResponse();\n            if (response.Sequence() != m_seq)\n            {\n                response.ThrowIfErrorFound();\n                continue;\n            }\n            responses.emplace_back(std::move(response));\n            responses.back().ThrowIfErrorFound();\n\n            routine(responses.back());\n        } while (responses.empty() || (responses.back().MultiMessage() && !responses.back().Done()));\n    }\n    catch (const std::exception& e)\n    {\n        throw NetlinkTransactionError(m_request, responses, e);\n    }\n}\n\nvoid NetlinkTransaction::PrintRequest() const\n{\n    throw NetlinkTransactionError(m_request, {}, RuntimeErrorWithSourceLocation(\"Print netlink transaction request\"));\n}\n\nstd::string NetlinkTransaction::GetRawRequestString() const\n{\n    std::stringstream str;\n    utils::FormatBinary(str, m_request.data(), m_request.size());\n    return str.str();\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkTransaction.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <functional>\n#include <sys/types.h>\n#include \"Fwd.h\"\n\nclass NetlinkTransaction\n{\npublic:\n    NetlinkTransaction(NetlinkChannel& channel, std::vector<char>&& request, __u32 seq);\n\n    void Execute(const std::function<void(const NetlinkResponse&)>& routine = [](const auto&) {});\n\n    // Useful for debugging how netlink requests are composed\n    void PrintRequest() const;\n    std::string GetRawRequestString() const;\n\nprivate:\n    NetlinkChannel& m_channel;\n    std::vector<char> m_request;\n    __u32 m_seq;\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkTransactionError.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <sstream>\n#include \"NetlinkTransactionError.h\"\n#include \"NetlinkError.h\"\n#include \"NetlinkResponse.h\"\n#include \"Utils.h\"\n\nNetlinkTransactionError::NetlinkTransactionError(\n    const std::vector<char>& request, const std::vector<NetlinkResponse>& responses, const std::exception& inner, const std::source_location& source) :\n    RuntimeErrorWithSourceLocation(BuildMessage(request, responses, inner), source)\n{\n    const auto* error = dynamic_cast<const NetlinkError*>(&inner);\n    if (error != nullptr)\n    {\n        m_error = error->Error();\n    }\n}\n\nstd::optional<int> NetlinkTransactionError::Error() const\n{\n    return m_error;\n}\n\nstd::string NetlinkTransactionError::BuildMessage(const std::vector<char>& request, const std::vector<NetlinkResponse>& responses, const std::exception& inner)\n{\n    std::stringstream str;\n\n    str << \"Error in netlink transaction.\\n\";\n    str << \"Innermost exception: \" << inner.what();\n    str << \"\\nRequest: \";\n    utils::FormatBinary(str, request.data(), request.size());\n    str << \"\\nResponses: (seen: \" << std::to_string(responses.size()) << \") \";\n    utils::FormatArray(str, responses);\n\n    return str.str();\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/NetlinkTransactionError.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"RuntimeErrorWithSourceLocation.h\"\n#include \"NetlinkMessage.h\"\n\nclass NetlinkTransactionError : public RuntimeErrorWithSourceLocation\n{\npublic:\n    NetlinkTransactionError(\n        const std::vector<char>& request,\n        const std::vector<NetlinkResponse>& responses,\n        const std::exception& inner,\n        const std::source_location& source = std::source_location::current());\n\n    std::optional<int> Error() const;\n\nprivate:\n    static std::string BuildMessage(const std::vector<char>& request, const std::vector<NetlinkResponse>& responses, const std::exception& inner);\n\n    std::optional<int> m_error;\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/Operation.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nenum Operation\n{\n    Create,\n    Update,\n    Remove\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/Packet.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n// SPDX-License-Identifier: MIT\n\n#pragma once\n#include <vector>\n\nclass Packet\n{\npublic:\n    static constexpr long InitialReservedHeader = 128;\n    static constexpr long InitialPacketSize = 2048;\n\n    // Initial state is data_offset==data_end_offset\n    void reset()\n    {\n        data_offset = InitialReservedHeader;\n        data_end_offset = data_offset + InitialPacketSize;\n        Buffer.resize(InitialReservedHeader + InitialPacketSize);\n    }\n\n    uint8_t* data()\n    {\n        return &Buffer[data_offset];\n    }\n\n    uint8_t* data_end()\n    {\n        return &Buffer[data_end_offset];\n    }\n\n    bool adjust_head(long count)\n    {\n        if ((count + data_offset) < 0)\n        {\n            return false;\n        }\n\n        if ((count + data_offset) > data_end_offset)\n        {\n            return false;\n        }\n\n        data_offset += count;\n        return true;\n    }\n\n    bool adjust_tail(long count)\n    {\n        if ((count + data_end_offset) < data_offset)\n        {\n            return false;\n        }\n        if ((count + data_end_offset) > Buffer.size())\n        {\n            Buffer.resize(count + data_end_offset);\n        }\n\n        data_end_offset += count;\n        return true;\n    }\n\nprivate:\n    long data_offset = 0;\n    long data_end_offset = 0;\n    std::vector<uint8_t> Buffer;\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/Protocol.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#pragma once\r\n\r\nenum Protocol\r\n{\r\n    Tcp,\r\n    Udp\r\n};\r\n"
  },
  {
    "path": "src/linux/netlinkutil/Route.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <iostream>\n#include \"Route.h\"\n#include \"Utils.h\"\n\nRoute::Route(int family, const std::optional<Address>& via, int dev, bool defaultRoute, const std::optional<Address>& to, int metric) :\n    family(family), via(via), dev(dev), defaultRoute(defaultRoute), to(to), metric(metric)\n{\n}\n\nstd::ostream& operator<<(std::ostream& out, const Route& route)\n{\n    if (route.defaultRoute)\n    {\n        out << \"default \";\n    }\n\n    if (route.to.has_value())\n    {\n        out << route.to.value() << \" \";\n    }\n\n    if (route.via.has_value())\n    {\n        out << \" via \" << route.via.value() << \" \";\n    }\n\n    return out << \"dev \" << route.dev << \" metric \" << route.metric;\n}\n\nbool Route::IsOnlink() const\n{\n    return !via.has_value() || (family == AF_INET && via->Addr() == \"0.0.0.0\") || (family == AF_INET6 && via->Addr() == \"::\");\n}\n\nbool Route::IsMulticast() const\n{\n    if (!to.has_value())\n    {\n        return false;\n    }\n\n    if (family == AF_INET)\n    {\n        in_addr address = {};\n        Syscall(::inet_pton, to->Family(), to->Addr().c_str(), &address);\n        return IN_MULTICAST(ntohl(address.s_addr));\n    }\n\n    in6_addr address = {};\n    Syscall(::inet_pton, to->Family(), to->Addr().c_str(), &address);\n    return IN6_IS_ADDR_MULTICAST(&address);\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/Route.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <optional>\n#include \"address.h\"\n\nstruct Route\n{\n    int family = -1;\n    std::optional<Address> via;\n    int dev = -1;\n    bool defaultRoute = false;\n    std::optional<Address> to;\n    int metric = 0;\n    bool isLoopbackRoute = false;\n\n    Route(int family, const std::optional<Address>& via, int dev, bool defaultRoute, const std::optional<Address>& to, int metric);\n\n    bool IsOnlink() const;\n    bool IsMulticast() const;\n};\n\nstd::ostream& operator<<(std::ostream& out, const Route& route);\n"
  },
  {
    "path": "src/linux/netlinkutil/RoutingTable.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"RuntimeErrorWithSourceLocation.h\"\n#include \"RoutingTable.h\"\n#include \"NetlinkTransactionError.h\"\n#include \"NetLinkStrings.h\"\n#include \"Utils.h\"\n#include \"common.h\"\n\nconst Address c_ipv4LoopbackRouteSource = {AF_INET, 32, \"127.0.0.1\"};\n\nRoutingTable::RoutingTable(int table) : m_table(table)\n{\n}\n\nvoid RoutingTable::ChangeTableId(int newTableId)\n{\n    m_table = newTableId;\n}\n\nstd::vector<Route> RoutingTable::ListRoutes(int family)\n{\n    if (family != AF_UNSPEC && family != AF_INET && family != AF_INET6)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected address family: {}\", family));\n    }\n\n    std::vector<Route> routes;\n    auto processRoute = [&](const NetlinkResponse& response) {\n        for (const auto& e : response.Messages<rtmsg>(RTM_NEWROUTE))\n        {\n            const auto* message = e.Payload();\n            auto tableId = e.UniqueAttribute<int>(RTA_TABLE);\n            if ((family != AF_UNSPEC && family != message->rtm_family) || !tableId.has_value() || *tableId.value() != m_table)\n            {\n                continue;\n            }\n\n            auto readOptionalAddress = [&](int type) -> std::optional<Address> {\n                auto attribute = e.UniqueAttribute<const void*>(type);\n                if (!attribute.has_value())\n                {\n                    return {};\n                }\n\n                return Address::FromBinary(message->rtm_family, message->rtm_dst_len, attribute.value());\n            };\n\n            auto to = readOptionalAddress(RTA_DST);\n            auto device = e.UniqueAttribute<int>(RTA_OIF);\n            auto metric = e.UniqueAttribute<int>(RTA_PRIORITY);\n            routes.emplace_back(\n                message->rtm_family,\n                readOptionalAddress(RTA_GATEWAY),\n                device.has_value() ? *device.value() : -1,\n                !to.has_value(),\n                to,\n                metric.has_value() ? *metric.value() : 0);\n        }\n    };\n\n    rtmsg message{};\n    message.rtm_family = family;\n    auto transaction = m_channel.CreateTransaction(message, RTM_GETROUTE, NLM_F_DUMP);\n    transaction.Execute(processRoute);\n\n    return routes;\n}\n\nvoid RoutingTable::ModifyRoute(const Route& route, Operation action)\n{\n    if (route.family != AF_INET && route.family != AF_INET6)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Unexpected address family: {}\", route.family));\n    }\n\n    assert(action == Operation::Create || action == Operation::Update || action == Operation::Remove);\n\n    if (route.family == AF_INET)\n    {\n        ModifyRouteImpl<in_addr>(route, action);\n    }\n    else\n    {\n        ModifyRouteImpl<in6_addr>(route, action);\n    }\n}\n\ntemplate <typename TAddr>\nvoid RoutingTable::ModifyRouteImpl(const Route& route, Operation action)\n{\n    int flags = 0;\n    int operation = 0;\n    if (action == Update)\n    {\n        flags = NLM_F_CREATE | NLM_F_REPLACE;\n        operation = RTM_NEWROUTE;\n    }\n    else if (action == Create)\n    {\n        flags = NLM_F_CREATE;\n        operation = RTM_NEWROUTE;\n    }\n    else\n    {\n        // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK\n        // which is set later in NetlinkChannel CreateTransaction\n        operation = RTM_DELROUTE;\n    }\n\n    if (route.isLoopbackRoute)\n    {\n        ModifyLoopbackRouteImpl<TAddr>(route, operation, flags);\n    }\n    else if (route.defaultRoute)\n    {\n        ModifyDefaultRouteImpl<TAddr>(route, operation, flags);\n    }\n    else if (route.IsOnlink())\n    {\n        ModifyLinkLocalRouteImpl<TAddr>(route, operation, flags);\n    }\n    else\n    {\n        ModifyOfflinkRouteImpl<TAddr>(route, operation, flags);\n    }\n}\n\ntemplate <DerivedRouteMessage TMessage>\nvoid RoutingTable::SendMessage(const Route& route, int operation, int flags, const std::function<void(TMessage&)>& routine)\n{\n    TMessage message{};\n    message.route.rtm_family = route.family;\n    message.route.rtm_table = RT_TABLE_UNSPEC; // == 0\n    message.route.rtm_protocol = operation == RTM_DELROUTE ? RTPROT_UNSPEC : RTPROT_KERNEL;\n    message.route.rtm_type = route.IsMulticast() ? RTN_MULTICAST : RTN_UNICAST;\n    message.route.rtm_scope = route.IsOnlink() ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;\n    message.route.rtm_flags = RTM_F_NOTIFY;\n    message.route.rtm_dst_len = route.to.has_value() ? route.to.value().PrefixLength() : 0;\n\n    utils::InitializeIntegerAttribute(message.tableId, m_table, RTA_TABLE);\n    utils::InitializeIntegerAttribute(message.dev, route.dev, RTA_OIF);\n\n    routine(message);\n\n    auto transaction = m_channel.CreateTransaction(message, operation, flags);\n    try\n    {\n        transaction.Execute();\n    }\n    catch (const NetlinkTransactionError& transactionErr)\n    {\n        auto errorCode = transactionErr.Error();\n        if (errorCode.has_value())\n        {\n            if (operation == RTM_DELROUTE)\n            {\n                // If the route already doesn't exist, we'll receive error \"no such process\".  Ignore that error and return success.\n                if (errorCode.value() == -ESRCH)\n                {\n                    return;\n                }\n            }\n            else\n            {\n                // Errors \"file exists\", \"file not found\", \"no such process\" are ignored in order to avoid keeping\n                // track in GnsDaemon of what routes were added/updated and allow the same route to be\n                // added/updated multiple times.\n                if (errorCode.value() == -EEXIST || errorCode.value() == -ENOENT || errorCode.value() == -ESRCH)\n                {\n                    return;\n                }\n            }\n        }\n\n        throw;\n    }\n}\n\ntemplate <typename TAddr>\nvoid RoutingTable::ModifyLoopbackRouteImpl(const Route& route, int operation, int flags)\n{\n    if (!route.to.has_value() || !route.via.has_value())\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Loopback route {} missing destination or gateway address\", utils::Stringify(route)));\n    }\n\n    struct Message : RouteMessage\n    {\n        utils::AddressAttribute<TAddr> to;\n        utils::AddressAttribute<TAddr> via;\n        utils::AddressAttribute<TAddr> preferredSource;\n    } __attribute__((packed));\n\n    GNS_LOG_INFO(\n        \"SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})\",\n        route.to.has_value() ? route.to.value().Addr().c_str() : \"[empty]\",\n        route.via.has_value() ? route.via.value().Addr().c_str() : \"[empty]\",\n        RouteOperationToString(operation),\n        NetLinkFormatFlagsToString(flags).c_str());\n\n    SendMessage<Message>(route, operation, flags, [&](Message& message) {\n        // For local IPs, the preferred source is set equal to destination. For a route to loopback address\n        // range 127.0.0.0/8, preferred source is set to 127.0.0.1. This is done to ensure that when routing\n        // loopback or local packets out of the guest, they won't have different source and destination\n        // IPs, since they won't be accepted by the Windows host. Having the routes with source == destination is\n        // also consistent with the how routes from the \"local\" routing table look like.\n        //\n        // Note: Since the IPv6 loopback range is ::1/128, we don't need separate code such as the one below\n        // converting from 127.0.0.0 to 127.0.0.1.\n        if (route.to.value().Addr().compare(\"127.0.0.0\") == 0)\n        {\n            GNS_LOG_INFO(\n                \"InitializeAddressAttribute (preferred source address) RTA_PREFSRC to {}\", c_ipv4LoopbackRouteSource.Addr().c_str());\n            utils::InitializeAddressAttribute<TAddr>(message.preferredSource, c_ipv4LoopbackRouteSource, RTA_PREFSRC);\n        }\n        else\n        {\n            // Set the preferred source address to be the same as the route destination.\n            GNS_LOG_INFO(\"InitializeAddressAttribute (preferred source address) RTA_PREFSRC to {}\", route.to.value().Addr().c_str());\n            utils::InitializeAddressAttribute<TAddr>(message.preferredSource, route.to.value(), RTA_PREFSRC);\n        }\n\n        message.route.rtm_flags |= RTNH_F_ONLINK;\n        GNS_LOG_INFO(\n            \"InitializeAddressAttribute RTA_DST ({}) RTA_GATEWAY ({}) RTA_PRIORITY ([not set])\",\n            route.to.value().Addr().c_str(),\n            route.via.value().Addr().c_str());\n        utils::InitializeAddressAttribute<TAddr>(message.to, route.to.value(), RTA_DST);\n        utils::InitializeAddressAttribute<TAddr>(message.via, route.via.value(), RTA_GATEWAY);\n    });\n}\n\ntemplate <typename TAddr>\nvoid RoutingTable::ModifyDefaultRouteImpl(const Route& route, int operation, int flags)\n{\n    if (!route.via.has_value())\n    {\n        throw RuntimeErrorWithSourceLocation(\"Default route is missing its gateway address\");\n    }\n\n    struct Message : RouteMessage\n    {\n        utils::AddressAttribute<TAddr> via;\n        utils::IntegerAttribute metric;\n    } __attribute__((packed));\n\n    GNS_LOG_INFO(\n        \"SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})\",\n        route.to.has_value() ? route.to.value().Addr().c_str() : \"[empty]\",\n        route.via.has_value() ? route.via.value().Addr().c_str() : \"[empty]\",\n        RouteOperationToString(operation),\n        NetLinkFormatFlagsToString(flags).c_str());\n\n    SendMessage<Message>(route, operation, flags, [&](Message& message) {\n        GNS_LOG_INFO(\n            \"InitializeAddressAttribute RTA_DST ([not set]) RTA_GATEWAY ({}), RTA_PRIORITY ({})\",\n            route.to.has_value() ? route.to.value().Addr().c_str() : \"[empty]\",\n            route.metric);\n        utils::InitializeAddressAttribute<TAddr>(message.via, route.via.value(), RTA_GATEWAY);\n        utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);\n    });\n}\n\ntemplate <typename TAddr>\nvoid RoutingTable::ModifyLinkLocalRouteImpl(const Route& route, int operation, int flags)\n{\n    struct Message : RouteMessage\n    {\n        utils::AddressAttribute<TAddr> to;\n        utils::IntegerAttribute metric;\n    } __attribute__((packed));\n\n    GNS_LOG_INFO(\n        \"SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})\",\n        route.to.has_value() ? route.to.value().Addr().c_str() : \"[empty]\",\n        route.via.has_value() ? route.via.value().Addr().c_str() : \"[empty]\",\n        RouteOperationToString(operation),\n        NetLinkFormatFlagsToString(flags).c_str());\n\n    SendMessage<Message>(route, operation, flags, [&](Message& message) {\n        GNS_LOG_INFO(\n            \"InitializeAddressAttribute RTA_DST ({}) RTA_GATEWAY ([not set]), RTA_PRIORITY ({})\",\n            route.to.has_value() ? route.to.value().Addr().c_str() : \"[empty]\",\n            route.metric);\n        utils::InitializeAddressAttribute<TAddr>(message.to, route.to.value(), RTA_DST);\n        utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);\n    });\n}\n\ntemplate <typename TAddr>\nvoid RoutingTable::ModifyOfflinkRouteImpl(const Route& route, int operation, int flags)\n{\n    if (!route.via.has_value())\n    {\n        throw RuntimeErrorWithSourceLocation(\"Offlink route is missing its next hop\");\n    }\n\n    struct Message : RouteMessage\n    {\n        utils::AddressAttribute<TAddr> to;\n        utils::AddressAttribute<TAddr> via;\n        utils::IntegerAttribute metric;\n    } __attribute__((packed));\n\n    GNS_LOG_INFO(\n        \"SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})\",\n        route.to.has_value() ? route.to.value().Addr().c_str() : \"[empty]\",\n        route.via.has_value() ? route.via.value().Addr().c_str() : \"[empty]\",\n        RouteOperationToString(operation),\n        NetLinkFormatFlagsToString(flags).c_str());\n\n    SendMessage<Message>(route, operation, flags, [&](Message& message) {\n        GNS_LOG_INFO(\n            \"InitializeAddressAttribute RTA_DST ({}) RTA_GATEWAY ({}), RTA_PRIORITY ({})\",\n            route.to.has_value() ? route.to.value().Addr().c_str() : \"[empty]\",\n            route.via.has_value() ? route.via.value().Addr().c_str() : \"[empty]\",\n            route.metric);\n        utils::InitializeAddressAttribute<TAddr>(message.to, route.to.value(), RTA_DST);\n        utils::InitializeAddressAttribute<TAddr>(message.via, route.via.value(), RTA_GATEWAY);\n        utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);\n    });\n}\n\n// Delete all routes from the specified address family\nvoid RoutingTable::RemoveAll(int addressFamily)\n{\n    for (const auto& route : ListRoutes(addressFamily))\n    {\n        ModifyRoute(route, Remove);\n    }\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/RoutingTable.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <functional>\n#include \"NetlinkChannel.h\"\n#include \"Route.h\"\n#include \"Operation.h\"\n\nstruct RouteMessage\n{\n    rtmsg route;\n    utils::IntegerAttribute tableId;\n    utils::IntegerAttribute dev;\n} __attribute__((packed));\n\n// The below is a means to ensure that messages have a common set of fields.  It just means\n// that a type T must inherit from RouteMessage, and any functions that reference\n// DerivedRouteMessage can be assured that they can safely access the fields in RouteMessage.\ntemplate <typename TMessage>\nconcept DerivedRouteMessage = std::is_base_of<RouteMessage, TMessage>::value;\n\nclass RoutingTable\n{\npublic:\n    RoutingTable(int table);\n    RoutingTable(const RoutingTable&) = delete;\n    RoutingTable(RoutingTable&&) = delete;\n\n    const RoutingTable& operator=(const RoutingTable&) const = delete;\n    const RoutingTable& operator=(RoutingTable&&) const = delete;\n\n    void ChangeTableId(int newTableId);\n\n    void ModifyRoute(const Route& route, Operation action);\n\n    std::vector<Route> ListRoutes(int family);\n\n    void RemoveAll(int addressFamily);\n\nprivate:\n    /*\n        Implement netlink equivalent of\n        \"ip route <operation> table <id> <destination> via <gateway> src <source> dev <interface> onlink\".\n\n        Note: the preferred source is set equal to destination. See comments in method implementation for more\n        details.\n    */\n    template <typename TAddr>\n    void ModifyLoopbackRouteImpl(const Route& route, int operation, int flags);\n\n    template <typename TAddr>\n    void ModifyDefaultRouteImpl(const Route& route, int operation, int flags);\n\n    template <typename TAddr>\n    void ModifyLinkLocalRouteImpl(const Route& route, int operation, int flags);\n\n    /*\n        By \"offlink route\", we mean a route with a specific destination prefix and a specific next hop.\n\n        Contrast this to a default route which has no specific destination prefix and a link local\n        route which has no specific next hop.\n    */\n    template <typename TAddr>\n    void ModifyOfflinkRouteImpl(const Route& route, int operation, int flags);\n\n    template <typename TAddr>\n    void ModifyRouteImpl(const Route& route, Operation action);\n\n    /*\n        Creates the message using the routine, then sends the message via netlink.\n    */\n    template <DerivedRouteMessage TMessage>\n    void SendMessage(const Route& route, int operation, int flags, const std::function<void(TMessage&)>& routine = [](auto&) {});\n\n    NetlinkChannel m_channel;\n    int m_table;\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/Rule.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#include <iostream>\r\n#include \"Rule.h\"\r\n\r\nRule::Rule(int family, int routingTable, int priority) : family(family), routingTable(routingTable), priority(priority)\r\n{\r\n}\r\n\r\nRule::Rule(int family, int routingTable, int priority, const std::optional<Protocol> protocol) :\r\n    family(family), routingTable(routingTable), priority(priority), protocol(protocol)\r\n{\r\n}\r\n\r\nRule::Rule(int family, int routingTable, int priority, const std::string& oif, const std::optional<Protocol> protocol) :\r\n    family(family), routingTable(routingTable), priority(priority), oif(oif), protocol(protocol)\r\n{\r\n}\r\n\r\nstd::ostream& operator<<(std::ostream& out, const Rule& rule)\r\n{\r\n    out << \"priority \" << rule.priority << \" family \" << (rule.family == AF_INET ? \"ipv4\" : \"ipv6\") << \" \";\r\n\r\n    if (rule.sourceAddress)\r\n    {\r\n        out << \"from \" << rule.sourceAddress.value() << \" \";\r\n    }\r\n\r\n    if (rule.protocol)\r\n    {\r\n        out << \"ipproto \" << (rule.protocol.value() == Protocol::Tcp ? \"tcp\" : \"udp\") << \" \";\r\n    }\r\n\r\n    if (!rule.oif.empty())\r\n    {\r\n        out << \"oif \" << rule.oif << \" \";\r\n    }\r\n\r\n    if (!rule.iif.empty())\r\n    {\r\n        out << \"iif \" << rule.iif << \" \";\r\n    }\r\n\r\n    return out << \"lookup table \" << rule.routingTable << \" \";\r\n}\r\n"
  },
  {
    "path": "src/linux/netlinkutil/Rule.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#pragma once\r\n\r\n#include <optional>\r\n#include \"address.h\"\r\n#include \"Protocol.h\"\r\n\r\n/*\r\n    Currently Rule only supports family, table, priority, input or output interface, source IP and protocol (tcp/udp).\r\n    It can be extended destination IP and source/destination port.\r\n*/\r\nstruct Rule\r\n{\r\n    int family = AF_INET;\r\n    int routingTable = -1;\r\n    int priority = -1;\r\n    std::string iif;\r\n    std::string oif;\r\n    std::optional<Protocol> protocol;\r\n    std::optional<Address> sourceAddress;\r\n\r\n    Rule(int family, int routingTable, int priority);\r\n\r\n    Rule(int family, int routingTable, int priority, std::optional<Protocol> protocol);\r\n\r\n    Rule(int family, int routingTable, int priority, const std::string& oif, std::optional<Protocol> protocol);\r\n};\r\n\r\nstd::ostream& operator<<(std::ostream& out, const Rule& rule);"
  },
  {
    "path": "src/linux/netlinkutil/RuntimeErrorWithSourceLocation.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <sstream>\n#include <optional>\n#include \"RuntimeErrorWithSourceLocation.h\"\n\nRuntimeErrorWithSourceLocation::RuntimeErrorWithSourceLocation(const std::string& reason, const std::source_location& location) :\n    std::runtime_error(BuildMessage(reason, {}, location))\n{\n}\n\nRuntimeErrorWithSourceLocation::RuntimeErrorWithSourceLocation(const std::string& reason, const std::exception& inner, const std::source_location& location) :\n    std::runtime_error(BuildMessage(reason, inner, location))\n{\n}\n\nstd::string RuntimeErrorWithSourceLocation::BuildMessage(const std::string& reason, const std::optional<std::exception>& inner, const std::source_location& source)\n{\n    std::stringstream str;\n    str << \"Exception thrown by \" << source.function_name() << \" in \" << source.file_name() << \":\" << source.line() << \":\" << reason;\n    if (inner.has_value())\n    {\n        str << \"\\nInner exception: \" << inner.value().what();\n    }\n\n    return str.str();\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/RuntimeErrorWithSourceLocation.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <stdexcept>\n#include <optional>\n#include <source_location>\n\n#pragma once\n\nclass RuntimeErrorWithSourceLocation : public std::runtime_error\n{\npublic:\n    RuntimeErrorWithSourceLocation(const std::string& reason, const std::source_location& location = std::source_location::current());\n\n    RuntimeErrorWithSourceLocation(\n        const std::string& reason, const std::exception& inner, const std::source_location& location = std::source_location::current());\n\nprivate:\n    static std::string BuildMessage(const std::string& reason, const std::optional<std::exception>& inner, const std::source_location& location);\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/Syscall.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n#pragma once\r\n\r\n#include <type_traits>\r\n#include <source_location>\r\n\r\ntemplate <typename Routine, typename... Args>\r\ntypename std::invoke_result<Routine, Args...>::type _Syscall(const std::source_location& source, Routine routine, Args... args);\r\n#define Syscall(...) _Syscall(std::source_location::current(), __VA_ARGS__)\r\n\r\ntemplate <typename Routine, typename... Args>\r\ntypename std::invoke_result<Routine, Args...>::type _SyscallInterruptable(const std::source_location& source, Routine routine, Args... args);\r\n#define SyscallInterruptable(...) _SyscallInterruptable(std::source_location::current(), __VA_ARGS__)\r\n\r\n#include \"Syscall.hxx\"\r\n"
  },
  {
    "path": "src/linux/netlinkutil/Syscall.hxx",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n/*\n * This class contains the implementation for the Syscall() wrapper.\n * This wrapper automatically throws a detailed exception if the\n * underlying call fails.\n * Example exception message:\n *  what():  Exception thrown by NetlinkChannel in ./NetlinkChannel.hxx:18 :\n *  socket(16, 1248, 0) failed with errno=22 (Invalid argument)\n */\n\n#include <sys/ioctl.h>\n#include <sys/prctl.h>\n#include <arpa/inet.h>\n#include <type_traits>\n#include <sys/xattr.h>\n#include <sys/stat.h>\n#include <sys/epoll.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <poll.h>\n#include <string>\n#include <sstream>\n#include <map>\n#include <cassert>\n#include \"SyscallError.h\"\n#include \"Utils.h\"\n\n#define X(Method) {(void*)&Method, #Method}\n\nnamespace detail {\nstatic const std::map<const void*, const char*> syscalls{\n    X(bind),     X(ioctl),   X(socket),        X(inet_pton), X(send),       X(sendto), X(recv),   X(sendto),\n    X(recvfrom), X(recvmsg), X(read),          X(lseek),     X(open),       X(prctl),  X(fork),   X(execl),\n    X(poll),     X(pipe),    X(socketpair),    X(readlink),  X(getxattr),   X(dup),    X(write),  X(pipe2),\n    X(syscall),  X(stat),    X(epoll_create1), X(epoll_ctl), X(epoll_wait), X(listen), X(accept4)};\n#undef X\n\ninline std::string ArgumentToString(const std::nullptr_t&)\n{\n    return \"nullptr\";\n}\n\ntemplate <typename T>\nstd::string ArgumentToString(T* arg)\n{\n    std::stringstream output;\n    if constexpr (std::is_class_v<T>)\n    {\n        utils::FormatBinary(output, arg, sizeof(*arg));\n    }\n    else\n    {\n        output << utils::BytesToHex(&arg, sizeof(arg), \"\");\n    }\n\n    return output.str();\n}\n\ntemplate <typename T>\nstd::string ArgumentToString(T arg)\n{\n    return std::to_string(arg);\n}\n\ninline void PrettyPrintArguments(std::ostream&)\n{\n}\n\ntemplate <typename T>\nvoid PrettyPrintArguments(std::ostream& out, T first)\n{\n    out << ArgumentToString(first);\n}\n\ntemplate <typename T, typename... Args>\nvoid PrettyPrintArguments(std::ostream& out, T first, Args... args)\n{\n    out << ArgumentToString(first) << \", \";\n    PrettyPrintArguments(out, std::forward<Args>(args)...);\n}\n} // namespace detail\n\ntemplate <typename Routine, typename... Args>\ntypename std::invoke_result<Routine, Args...>::type _Syscall(const std::source_location& source, Routine routine, Args... args)\n{\n    const auto call = detail::syscalls.find(reinterpret_cast<void*>(routine));\n    assert(call != detail::syscalls.end());\n\n    auto result = routine(std::forward<Args>(args)...);\n    if (result >= 0)\n    {\n        return result;\n    }\n\n    const int savedErrno = errno;\n\n    std::stringstream argString;\n    detail::PrettyPrintArguments(argString, std::forward<Args>(args)...);\n\n    throw SyscallError(call->second, argString.str(), savedErrno, source);\n}\n\ntemplate <typename Routine, typename... Args>\ntypename std::invoke_result<Routine, Args...>::type _SyscallInterruptable(const std::source_location& source, Routine routine, Args... args)\n{\n    const auto call = detail::syscalls.find(reinterpret_cast<void*>(routine));\n    assert(call != detail::syscalls.end());\n\n    auto result = routine(std::forward<Args>(args)...);\n    if (result >= 0)\n    {\n        return result;\n    }\n\n    const int savedErrno = errno;\n    if (savedErrno == EINTR)\n    {\n        return result;\n    }\n\n    std::stringstream argString;\n    detail::PrettyPrintArguments(argString, std::forward<Args>(args)...);\n\n    throw SyscallError(call->second, argString.str(), savedErrno, source);\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/SyscallError.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include <sstream>\n#include <string.h>\n#include \"SyscallError.h\"\n\nSyscallError::SyscallError(const std::string& method, const std::string& arguments, int err, const std::source_location& source) :\n    RuntimeErrorWithSourceLocation(BuildMessage(method, arguments, err), source), savedErrno(err)\n{\n}\n\nstd::string SyscallError::BuildMessage(const std::string& method, const std::string& arguments, int err)\n{\n    std::stringstream str;\n\n    str << method << \"(\" << arguments << \") failed with errno=\" << err << \" (\" << strerror(err) << \")\";\n\n    return str.str();\n}\n\nint SyscallError::GetErrno() const\n{\n    return savedErrno;\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/SyscallError.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"RuntimeErrorWithSourceLocation.h\"\n\nclass SyscallError : public RuntimeErrorWithSourceLocation\n{\npublic:\n    SyscallError(const std::string& method, const std::string& arguments, int err, const std::source_location& source);\n\n    static std::string BuildMessage(const std::string& method, const std::string& arguments, int err);\n\n    int GetErrno() const;\n\nprivate:\n    int savedErrno;\n};\n"
  },
  {
    "path": "src/linux/netlinkutil/Utils.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"Utils.h\"\n#include \"RuntimeErrorWithSourceLocation.h\"\n#include <iomanip>\n#include <sstream>\n#include <string.h>\n\nstd::ostream& utils::FormatBinary(std::ostream& out, const void* ptr, size_t bytes)\n{\n    out << \"(\" << bytes << \" bytes) {\";\n    out << BytesToHex(ptr, bytes, \",\");\n\n    return out << \"}\";\n}\n\nstd::string utils::BytesToHex(const void* ptr, size_t bytes, const std::string& separator)\n{\n    std::stringstream out;\n    for (size_t i = 0; i < bytes; i++)\n    {\n        if (i != 0)\n        {\n            out << separator;\n        }\n\n        out << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(reinterpret_cast<const std::uint8_t*>(ptr)[i]);\n    }\n\n    return out.str();\n}\n\nAddress utils::ComputeBroadcastAddress(const Address& address)\n{\n    if (address.Family() != AF_INET)\n    {\n        throw RuntimeErrorWithSourceLocation(std::format(\"Can't compute broadcast address for address family: {}\", address.Family()));\n    }\n\n    auto bytes = address.AsBytes<in_addr>();\n\n    // Set all the bits between the prefixLength and 32 to 1\n    std::uint32_t suffix = (1 << (32 - address.PrefixLength())) - 1;\n    bytes.s_addr |= htonl(suffix);\n\n    return Address::FromBytes(AF_INET, address.PrefixLength(), bytes);\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/Utils.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <ostream>\n#include <vector>\n#include <linux/rtnetlink.h>\n#include <linux/fib_rules.h>\n#include \"address.h\"\n\n// TODO: Maybe use a template and __function__ to write the type name compile\n// time ?\n\nnamespace utils {\ntemplate <typename T>\nstruct Attribute\n{\n    rtattr header __attribute__((aligned(RTA_ALIGNTO)));\n    T value __attribute__((aligned(RTA_ALIGNTO)));\n} __attribute__((packed));\n\ntemplate <bool Empty, typename T>\nstruct Optional;\n\ntemplate <typename T>\nstruct Optional<false, T>\n{\n    /// empty, though note that empty structs are of size 1 byte.\n};\n\ntemplate <typename T>\nstruct Optional<true, T>\n{\n    T attribute;\n};\n\ntemplate <typename TAddr>\nstruct AddressAttribute\n{\n    rtattr header;\n    TAddr address;\n} __attribute__((packed));\n\nstruct CacheInfoAttribute\n{\n    rtattr header;\n    struct ifa_cacheinfo cacheinfo;\n} __attribute((packed));\n\nstruct MacAddressAttribute\n{\n    nlattr header;\n    MacAddress address;\n} __attribute__((packed));\n\nstruct IntegerAttribute\n{\n    rtattr header;\n    int value;\n} __attribute__((packed));\n\nstd::ostream& FormatBinary(std::ostream& out, const void* ptr, size_t bytes);\n\ntemplate <typename T>\nstd::ostream& FormatArray(std::ostream& out, const std::vector<T>& content);\n\nstd::string BytesToHex(const void* ptr, size_t bytes, const std::string& separator);\n\ntemplate <typename TAddr>\nvoid InitializeAddressAttribute(AddressAttribute<TAddr>& attribute, const Address& address, int type);\n\nvoid InitializeCacheInfoAttribute(CacheInfoAttribute& attribute, const Address& address);\n\nvoid InitializeIntegerAttribute(IntegerAttribute& attribute, int value, int type);\n\nAddress ComputeBroadcastAddress(const Address& address);\n\ntemplate <typename T>\nstd::string Stringify(const T& value);\n} // namespace utils\n\n#include \"Utils.hxx\"\n"
  },
  {
    "path": "src/linux/netlinkutil/Utils.hxx",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <arpa/inet.h>\n#include \"Syscall.h\"\n#include \"Utils.h\"\n\ntemplate <typename T>\nstd::ostream& utils::FormatArray(std::ostream& out, const std::vector<T>& content)\n{\n    out << \"[\";\n\n    for (size_t i = 0; i < content.size(); i++)\n    {\n        if (i != 0)\n        {\n            out << \",\";\n        }\n\n        out << content[i];\n    }\n\n    return out << \"]\";\n}\n\ntemplate <typename TAddr>\nvoid utils::InitializeAddressAttribute(AddressAttribute<TAddr>& attribute, const Address& address, int type)\n{\n    // We can't pass &attribute.address because the structure is packed,\n    // so that could cause an unaligned access\n    TAddr netAddress = {};\n    Syscall(::inet_pton, address.Family(), address.Addr().c_str(), &netAddress);\n\n    attribute.header.rta_len = RTA_LENGTH(sizeof(netAddress));\n    attribute.header.rta_type = type;\n    attribute.address = netAddress;\n}\n\ninline void utils::InitializeCacheInfoAttribute(CacheInfoAttribute& attribute, const Address& address)\n{\n    attribute.header.rta_len = RTA_LENGTH(sizeof(struct ifa_cacheinfo));\n    attribute.header.rta_type = IFA_CACHEINFO;\n    attribute.cacheinfo.ifa_prefered = address.PreferredLifetime();\n    attribute.cacheinfo.ifa_valid = 0xFFFFFFFF;\n}\n\ninline void utils::InitializeIntegerAttribute(IntegerAttribute& attribute, int value, int type)\n{\n    attribute.header.rta_len = RTA_LENGTH(sizeof(int));\n    attribute.header.rta_type = type;\n    attribute.value = value;\n}\n\ntemplate <typename T>\nstd::string utils::Stringify(const T& value)\n{\n    std::stringstream str;\n    str << value;\n    return str.str();\n}\n"
  },
  {
    "path": "src/linux/netlinkutil/address.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <string>\n#include <array>\n#include <arpa/inet.h>\n\nusing MacAddress = std::array<std::uint8_t, 6>;\n\nconstexpr unsigned int Ipv6MaxPrefixLen = 128;\nconstexpr unsigned int Ipv4MaxPrefixLen = 32;\n\n#define MAX_PREFIX_LEN(AddressFamily) (((AddressFamily) == AF_INET) ? Ipv4MaxPrefixLen : Ipv6MaxPrefixLen)\n\nenum class IpPrefixOrigin\n{\n    Other = 0,\n    Manual,\n    WellKnown,\n    Dhcp,\n    RouterAdvertisement,\n};\n\nenum class IpSuffixOrigin\n{\n    Other = 0,\n    Manual,\n    WellKnown,\n    Dhcp,\n    LinkLayerAddress,\n    Random,\n};\n\nclass Address\n{\npublic:\n    Address(short family, size_t prefixLength, const std::string& address);\n    Address(short family, size_t prefixLength, const std::string& address, IpPrefixOrigin prefixOrigin, IpSuffixOrigin suffixOrigin, int preferredLifetime);\n\n    bool operator==(const Address& other) const;\n    bool operator!=(const Address& other) const;\n\n    void ConvertToBytes(void* ptr) const;\n\n    void SetIsPrefixRouteAutogenerationDisabled(bool disable) noexcept\n    {\n        m_disableAutogeneratedPrefixRoute = disable;\n    }\n\n    template <typename T>\n    T AsBytes() const\n    {\n        T output;\n        ConvertToBytes(&output);\n\n        return output;\n    }\n\n    template <typename T>\n    static Address FromBytes(int family, int prefixLength, const T& address)\n    {\n        return FromBytes(family, prefixLength, static_cast<const void*>(&address));\n    }\n\n    static Address FromBytes(int family, int prefixLength, const void* ptr);\n\n    static Address FromPrefixString(int family, const std::string& address);\n\n    static Address FromBinary(int family, int prefixLength, const void* data);\n\n    short Family() const noexcept;\n    size_t PrefixLength() const noexcept;\n    const std::string Addr() const noexcept;\n    int Flags() const noexcept;\n    int Scope() const noexcept;\n    int PreferredLifetime() const noexcept;\n    bool IsIpv4() const noexcept;\n    bool IsPrefixRouteAutogenerationDisabled() const noexcept;\n\nprivate:\n    short m_family = 0;\n    uint32_t m_prefixLength = 0;\n    std::string m_address = \"\";\n    IpPrefixOrigin m_prefixOrigin = IpPrefixOrigin::Other;\n    IpSuffixOrigin m_suffixOrigin = IpSuffixOrigin::Other;\n    uint32_t m_preferredLifetime = 0;\n    bool m_disableAutogeneratedPrefixRoute = false;\n};\n\nstd::ostream& operator<<(std::ostream& out, const Address& address);\n"
  },
  {
    "path": "src/linux/plan9/CMakeLists.txt",
    "content": "set(SOURCES\n    p9fid.cpp\n    p9file.cpp\n    p9fs.cpp\n    p9handler.cpp\n    p9io.cpp\n    p9lx.cpp\n    p9readdir.cpp\n    p9scheduler.cpp\n    p9tracelogging.cpp\n    p9util.cpp\n    p9xattr.cpp)\n\nset(HEADERS\n    p9fid.h\n    p9file.h\n    p9fs.h\n    p9handler.h\n    p9io.h\n    p9lx.h\n    p9readdir.h\n    p9scheduler.h\n    p9tracelogging.h\n    p9tracelogginghelper.h\n    p9util.h\n    p9xattr.h\n    p9defs.h\n    p9protohelpers.h\n    p9await.h\n    p9errors.h\n    result_macros.h\n    expected.h\n    p9platform.h\n    p9ihandler.h\n    p9log.h\n    p9data.h\n    p9commonutil.h\n    precomp.h)\n\nadd_linux_library(libplan9 \"${SOURCES}\" \"${HEADERS}\")\nset_target_properties(libplan9 PROPERTIES FOLDER linux)"
  },
  {
    "path": "src/linux/plan9/expected.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <optional>\n\n#define P9_EXPECTED_STD std::\n\n#include \"result_macros.h\"\n\nnamespace util {\n\n// Represents an error condition with the specified type.\ntemplate <typename E>\nstruct Unexpected\n{\n    // Creates anew instance with the specified error value.\n    // N.B. This constructor allows initialization without specifying the template argument.\n    Unexpected(const E& value) : Value{value}\n    {\n    }\n\n    E Value;\n};\n\nnamespace details {\n\n    template <typename T, typename... Args>\n    void ConstructInPlace(T& value, Args&&... args)\n    {\n        new (P9_EXPECTED_STD addressof(value)) T(P9_EXPECTED_STD forward<Args>(args)...);\n    }\n\n    // Storage for the BasicExpected class for types that are trivially destructible.\n    template <typename T, typename E>\n    struct TrivialExpectedStorage\n    {\n        // Creates a new instance with a default initialized value.\n        constexpr TrivialExpectedStorage() : HasValue{true}, Value{}\n        {\n        }\n\n        // Creates a new instance by invoking a constructor on the value type.\n        template <typename... Args>\n        constexpr TrivialExpectedStorage(P9_EXPECTED_STD in_place_t, Args&&... args) :\n            HasValue{true}, Value{P9_EXPECTED_STD forward<Args>(args)...}\n        {\n        }\n\n        // Creates a new instance with the specified error.\n        constexpr TrivialExpectedStorage(const Unexpected<E>& error) : HasValue{false}, Error{error.Value}\n        {\n        }\n\n        // Move-constructs a new instance from an existing instance.\n        constexpr TrivialExpectedStorage(TrivialExpectedStorage&& other) : HasValue{other.HasValue}, NoInit{}\n        {\n            if (other.HasValue)\n            {\n                ConstructInPlace(Value, P9_EXPECTED_STD move(other.Value));\n            }\n            else\n            {\n                ConstructInPlace(Error, P9_EXPECTED_STD move(other.Error));\n            }\n        }\n\n        // Move-assigns the value of an existing instance.\n        TrivialExpectedStorage& operator=(TrivialExpectedStorage&& other)\n        {\n            if (P9_EXPECTED_STD addressof(other) != this)\n            {\n                if (other.HasValue)\n                {\n                    if (HasValue)\n                    {\n                        Value = P9_EXPECTED_STD move(other.Value);\n                    }\n                    else\n                    {\n                        ConstructInPlace(Value, P9_EXPECTED_STD move(other.Value));\n                    }\n                }\n                else\n                {\n                    if (HasValue)\n                    {\n                        ConstructInPlace(Error, P9_EXPECTED_STD move(other.Error));\n                    }\n                    else\n                    {\n                        Error = P9_EXPECTED_STD move(other.Error);\n                    }\n                }\n\n                HasValue = other.HasValue;\n            }\n\n            return *this;\n        }\n\n        // Default destructor, because the value type is trivially destructible.\n        ~TrivialExpectedStorage() = default;\n\n        bool HasValue;\n\n        union\n        {\n            T Value;\n            E Error;\n            char NoInit;\n        };\n    };\n\n    // Storage for the BasicExpected class for types that are not trivially destructible.\n    template <typename T, typename E>\n    struct NonTrivialExpectedStorage\n    {\n        // Creates a new instance with a default initialized value.\n        constexpr NonTrivialExpectedStorage() : HasValue{true}, Value{}\n        {\n        }\n\n        // Creates a new instance by invoking a constructor on the value type.\n        template <typename... Args>\n        constexpr NonTrivialExpectedStorage(P9_EXPECTED_STD in_place_t, Args&&... args) :\n            HasValue{true}, Value{P9_EXPECTED_STD forward<Args>(args)...}\n        {\n        }\n\n        // Creates a new instance with the specified error.\n        constexpr NonTrivialExpectedStorage(const Unexpected<E>& error) : HasValue{false}, Error{error.Value}\n        {\n        }\n\n        // Move-constructs a new instance from an existing instance.\n        constexpr NonTrivialExpectedStorage(NonTrivialExpectedStorage&& other) : HasValue{other.HasValue}, NoInit{}\n        {\n            if (other.HasValue)\n            {\n                ConstructInPlace(Value, P9_EXPECTED_STD move(other.Value));\n            }\n            else\n            {\n                ConstructInPlace(Error, P9_EXPECTED_STD move(other.Error));\n            }\n        }\n\n        // Move-assigns the value of an existing instance.\n        NonTrivialExpectedStorage& operator=(NonTrivialExpectedStorage&& other)\n        {\n            if (P9_EXPECTED_STD addressof(other) != this)\n            {\n                if (other.HasValue)\n                {\n                    if (HasValue)\n                    {\n                        Value = P9_EXPECTED_STD move(other.Value);\n                    }\n                    else\n                    {\n                        Error.~E();\n                        ConstructInPlace(Value, P9_EXPECTED_STD move(other.Value));\n                    }\n                }\n                else\n                {\n                    if (HasValue)\n                    {\n                        Value.~T();\n                        ConstructInPlace(Error, P9_EXPECTED_STD move(other.Error));\n                    }\n                    else\n                    {\n                        Error = P9_EXPECTED_STD move(other.Error);\n                    }\n                }\n\n                HasValue = other.HasValue;\n            }\n\n            return *this;\n        }\n\n        // Explicitly destructs either the value or the error.\n        ~NonTrivialExpectedStorage()\n        {\n            if (HasValue)\n            {\n                Value.~T();\n            }\n            else\n            {\n                Error.~E();\n            }\n        }\n\n        bool HasValue;\n\n        union\n        {\n            T Value;\n            E Error;\n            char NoInit;\n        };\n    };\n\n    // Class that helps to select between the storage for trivially and non-trivially destructible\n    // types.\n    template <typename T, typename E>\n    struct ExpectedStorage\n    {\n        using Type = P9_EXPECTED_STD conditional_t<\n            P9_EXPECTED_STD is_trivially_destructible<T>::value && P9_EXPECTED_STD is_trivially_destructible<E>::value,\n            TrivialExpectedStorage<T, E>,\n            NonTrivialExpectedStorage<T, E>>;\n    };\n\n} // namespace details\n\n// This is a simple implementation of the proposed standard std::excepted type, which can be\n// used as the return type for functions that return either a value or an error.\n// N.B. This is named BasicExpected so that consumers can create an Expected typedef with their\n//      most common error type.\ntemplate <typename T, typename E>\nclass BasicExpected\n{\npublic:\n    // Creates a new instance with a default initialized value.\n    constexpr BasicExpected() = default;\n\n    // Creates a new instance holding the specified value.\n    constexpr BasicExpected(const T& value) : m_storage{P9_EXPECTED_STD in_place, value}\n    {\n    }\n\n    // Creates a new instance moving the specified value.\n    constexpr BasicExpected(T&& value) : m_storage{P9_EXPECTED_STD in_place, P9_EXPECTED_STD move(value)}\n    {\n    }\n\n    // Creates a new instance holding the specified error.\n    constexpr BasicExpected(const Unexpected<E>& error) : m_storage{error}\n    {\n    }\n\n    // Creates a new instance moving the specified error.\n    constexpr BasicExpected(Unexpected<E>&& error) : m_storage{P9_EXPECTED_STD move(error)}\n    {\n    }\n\n    // Move-constructs a new instance from an existing instance.\n    constexpr BasicExpected(BasicExpected&& other) : m_storage{P9_EXPECTED_STD move(other.m_storage)}\n    {\n    }\n\n    // Move-assigns from an existing instance.\n    BasicExpected& operator=(BasicExpected&& other)\n    {\n        if (P9_EXPECTED_STD addressof(other) != this)\n        {\n            m_storage = P9_EXPECTED_STD move(other.m_storage);\n        }\n\n        return *this;\n    }\n\n    // Tests whether or not the instance holds a value.\n    explicit operator bool() const\n    {\n        return m_storage.HasValue;\n    }\n\n    // Gets a reference to the contained value.\n    T& Get()\n    {\n        FAIL_FAST_IF(!m_storage.HasValue);\n        return m_storage.Value;\n    }\n\n    // Gets a const-reference to the contained value.\n    const T& Get() const\n    {\n        FAIL_FAST_IF(!m_storage.HasValue);\n        return m_storage.Value;\n    }\n\n    // Accesses members of the contained value.\n    // N.B. Behavior is undefined if the instance does not contain a value.\n    T* operator->()\n    {\n        return P9_EXPECTED_STD addressof(m_storage.Value);\n    }\n\n    // Accesses members of the contained value.\n    // N.B. Behavior is undefined if the instance does not contain a value.\n    const T* operator->() const\n    {\n        return P9_EXPECTED_STD addressof(m_storage.Value);\n    }\n\n    // Gets a reference to the contained value.\n    // N.B. Behavior is undefined if the instance does not contain a value.\n    T& operator*()\n    {\n        return m_storage.Value;\n    }\n\n    // Gets a const-reference to the contained value.\n    // N.B. Behavior is undefined if the instance does not contain a value.\n    const T& operator*() const\n    {\n        return m_storage.Value;\n    }\n\n    // Gets the contained error value.\n    // N.B. Behavior is undefined if the instance contains a value.\n    const E& Error() const\n    {\n        return m_storage.Error;\n    }\n\n    // Gets the contained error value if the instance contains an error.\n    // N.B. This function allows the RETURN_ macros to work without accessing their argument twice.\n    // N.B. This creates a copy of the error value, but since the error value is usually expected\n    //      to be a trivial type (e.g. int) that should not be a problem.\n    P9_EXPECTED_STD optional<E> OptionalError() const\n    {\n        if (m_storage.HasValue)\n        {\n            return {};\n        }\n\n        return m_storage.Error;\n    }\n\n    // Gets the contained error value, wrapped in an Unexpected struct.\n    // N.B. This makes it easy to return the error in a function that also returns a BasicExpected type.\n    // N.B. Behavior is undefined if the instance contains a value.\n    Unexpected<E> Unexpected() const\n    {\n        FAIL_FAST_IF(m_storage.HasValue);\n        return m_storage.Error;\n    }\n\nprivate:\n    typename details::ExpectedStorage<T, E>::Type m_storage;\n};\n\n} // namespace util\n"
  },
  {
    "path": "src/linux/plan9/p9await.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9scheduler.h\"\n#include \"p9errors.h\"\n\nnamespace p9fs {\n\n// Provides a movable wrapper around std::coroutine_handle that\n// destroys the coroutine when it goes out of scope.\ntemplate <class PromiseType>\nclass UniqueCoroutineHandle\n{\npublic:\n    UniqueCoroutineHandle() = default;\n\n    ~UniqueCoroutineHandle()\n    {\n        Reset();\n    }\n\n    UniqueCoroutineHandle(std::coroutine_handle<PromiseType> handle) : m_Handle(handle)\n    {\n    }\n\n    UniqueCoroutineHandle(UniqueCoroutineHandle&& handle) : m_Handle(handle.m_Handle)\n    {\n        handle.m_Handle = nullptr;\n    }\n\n    UniqueCoroutineHandle& operator=(UniqueCoroutineHandle&& handle)\n    {\n        if (this != &handle)\n        {\n            Reset();\n            m_Handle = handle.m_Handle;\n            handle.m_Handle = nullptr;\n        }\n\n        return *this;\n    }\n\n    UniqueCoroutineHandle(const UniqueCoroutineHandle&) = delete;\n    UniqueCoroutineHandle& operator=(const UniqueCoroutineHandle&) = delete;\n\n    void Reset()\n    {\n        if (m_Handle)\n        {\n            FAIL_FAST_IF(!m_Handle.done());\n            m_Handle.destroy();\n            m_Handle = nullptr;\n        }\n    }\n\n    std::coroutine_handle<PromiseType> Get() const\n    {\n        return m_Handle;\n    }\n\n    explicit operator bool() const\n    {\n        return static_cast<bool>(m_Handle);\n    }\n\nprivate:\n    std::coroutine_handle<PromiseType> m_Handle{};\n};\n\n// Async task is an awaitable task object whose resources are released\n// asynchronously from being awaited on. This requires an extra heap allocation,\n// so only use this when you need to have a task that you don't plan to\n// immediately await.\nclass AsyncTask\n{\nprivate:\n    struct Storage\n    {\n        std::exception_ptr Exception;\n        std::mutex Mutex;\n        std::condition_variable Condition;\n        std::atomic<bool> Done;\n        std::atomic<void*> Waiter;\n    };\n\npublic:\n    AsyncTask() = default;\n\n    AsyncTask(const std::shared_ptr<Storage>& storage) : m_Storage(storage)\n    {\n    }\n\n    bool await_ready() const\n    {\n        return m_Storage->Done;\n    }\n\n    // Wait for the promise to resume us, unless the coroutine has\n    // already completed.\n    bool await_suspend(std::coroutine_handle<> handle)\n    {\n        m_Storage->Waiter = handle.address();\n        if ((m_Storage->Done || m_Storage->Exception) && m_Storage->Waiter.exchange(nullptr) != nullptr)\n        {\n            return false;\n        }\n\n        return true;\n    }\n\n    // Return the result from the promise object.\n    void await_resume() const\n    {\n        if (m_Storage->Exception)\n        {\n            std::rethrow_exception(m_Storage->Exception);\n        }\n    }\n\n    explicit operator bool()\n    {\n        return bool{m_Storage};\n    }\n\n    void Get()\n    {\n        std::unique_lock<std::mutex> lock{m_Storage->Mutex};\n        m_Storage->Condition.wait(lock, [this]() -> bool { return m_Storage->Done; });\n\n        await_resume();\n    }\n\n    class promise_type\n    {\n    public:\n        std::suspend_never initial_suspend()\n        {\n            return {};\n        }\n\n        std::suspend_never final_suspend() noexcept\n        {\n            return {};\n        }\n\n        void return_void()\n        {\n            Signal();\n        }\n\n        void unhandled_exception()\n        {\n            m_Storage->Exception = std::current_exception();\n            Signal();\n        }\n\n        AsyncTask get_return_object() const\n        {\n            return AsyncTask{m_Storage};\n        }\n\n    private:\n        void Signal()\n        {\n            {\n                std::lock_guard<std::mutex> lock{m_Storage->Mutex};\n                m_Storage->Done = true;\n            }\n\n            m_Storage->Condition.notify_all();\n            auto address = m_Storage->Waiter.exchange(nullptr);\n            if (address)\n            {\n                auto waiter = std::coroutine_handle<>::from_address(address);\n                g_Scheduler.Schedule(waiter);\n            }\n\n            m_Storage.reset();\n        }\n\n        std::shared_ptr<Storage> m_Storage{std::make_shared<Storage>()};\n    };\n\nprivate:\n    std::shared_ptr<Storage> m_Storage;\n};\n\ntemplate <class T>\nclass PromiseBase : public T\n{\npublic:\n    auto initial_suspend()\n    {\n        return std::suspend_never{};\n    }\n\n    // When this coroutine function exits, signal the waiter and then suspend so\n    // that the promise stays alive long enough to get the result out of it.\n    auto final_suspend() noexcept\n    {\n        return FinalAwaiter{};\n    }\n\n    // Stores an exception object for rethrowing when awaiting on\n    // the associated task.\n    void unhandled_exception()\n    {\n        m_Exception = std::current_exception();\n    }\n\nprotected:\n    // Rethrows the coroutine's exception if there is one.\n    void CheckException()\n    {\n        if (m_Exception)\n        {\n            std::rethrow_exception(m_Exception);\n        }\n    }\n\nprivate:\n    struct FinalAwaiter\n    {\n        static bool await_ready() noexcept\n        {\n            return false;\n        }\n\n        template <class U>\n        void await_suspend(std::coroutine_handle<U> handle) noexcept\n        {\n            handle.promise().Resume();\n        }\n\n        static void await_resume() noexcept\n        {\n        }\n    };\n\n    std::exception_ptr m_Exception;\n};\n\n// Promise type for coroutines that return values.\ntemplate <class T, class Task, class Base>\nclass Promise : public PromiseBase<Base>\n{\npublic:\n    Task get_return_object()\n    {\n        return {*this};\n    }\n\n    template <class U>\n    void return_value(U&& value)\n    {\n        m_Value = std::forward<U>(value);\n    }\n\n    T GetResult()\n    {\n        PromiseBase<Base>::CheckException();\n        return std::move(m_Value);\n    }\n\nprivate:\n    T m_Value;\n};\n\n// Promise type for coroutines that do not return values.\ntemplate <class Task, class Base>\nclass Promise<void, Task, Base> : public PromiseBase<Base>\n{\npublic:\n    Task get_return_object()\n    {\n        return {*this};\n    }\n\n    void return_void()\n    {\n    }\n\n    void GetResult()\n    {\n        PromiseBase<Base>::CheckException();\n    }\n};\n\n// Promise type for Task objects. Represents a coroutine that will eventually\n// return or throw an exception.\nclass TaskPromise\n{\npublic:\n    // Sets the unique waiting task. Returns false if the promise already\n    // completed.\n    bool SetWaiter(std::coroutine_handle<> handle)\n    {\n        m_Waiter = handle.address();\n        return !m_WaiterOrDone.exchange(true);\n    }\n\n    // Resumes the waiting task, if there is one.\n    void Resume()\n    {\n        // Set the value to true to indicate we're done. If it was already true,\n        // it indicates a waiter was registered before completion so resume it.\n        if (m_WaiterOrDone.exchange(true))\n        {\n            auto waiter = std::coroutine_handle<>::from_address(m_Waiter);\n            m_Waiter = nullptr;\n            g_Scheduler.Schedule(waiter);\n        }\n    }\n\n    bool Done() const\n    {\n        return m_WaiterOrDone.load(std::memory_order_acquire);\n    }\n\nprivate:\n    void* m_Waiter{};\n    std::atomic<bool> m_WaiterOrDone{false};\n};\n\n// Task is an awaitable task that is designed to be co_awaited immediately. It\n// will not release the underlying coroutine's resources until it goes out of\n// scope.\ntemplate <class T>\nclass Task\n{\npublic:\n    using promise_type = Promise<T, Task<T>, TaskPromise>;\n\n    bool await_ready()\n    {\n        return Promise().Done();\n    }\n\n    // Start the coroutine and wait for the promise to resume us.\n    bool await_suspend(std::coroutine_handle<> handle)\n    {\n        // Try to set the waiter. If the coroutine has already completed, abort\n        // the suspend.\n        return Promise().SetWaiter(handle);\n    }\n\n    // Return the result from the promise object.\n    auto await_resume()\n    {\n        return Promise().GetResult();\n    }\n\n    Task() = default;\n\n    Task(promise_type& promise) : m_Coroutine(std::coroutine_handle<promise_type>::from_promise(promise))\n    {\n    }\n\nprivate:\n    promise_type& Promise()\n    {\n        return m_Coroutine.Get().promise();\n    }\n\n    UniqueCoroutineHandle<promise_type> m_Coroutine;\n};\n\nclass ScheduledTask\n{\npublic:\n    struct promise_type\n    {\n        ScheduledTask get_return_object()\n        {\n            return {*this};\n        }\n\n        std::suspend_always initial_suspend()\n        {\n            return {};\n        }\n\n        std::suspend_never final_suspend() noexcept\n        {\n            return {};\n        }\n\n        void unhandled_exception()\n        {\n            try\n            {\n                std::rethrow_exception(std::current_exception());\n            }\n            catch (...)\n            {\n                FAIL_FAST_CAUGHT_EXCEPTION();\n            }\n        }\n\n        static void return_void()\n        {\n        }\n    };\n\n    ScheduledTask(promise_type& promise)\n    {\n        g_Scheduler.Schedule(std::coroutine_handle<promise_type>::from_promise(promise));\n    }\n};\n\n/// Non-awaitable wrapper to schedule a coroutine to run on another thread.\ntemplate <class T>\nvoid RunScheduledTask(T&& awaitable)\n{\n    [](T func) -> ScheduledTask { co_await std::move(func)(); }(std::forward<T>(awaitable));\n}\n\ntemplate <typename T>\nvoid RunAsyncTask(T&& awaitable)\n{\n    [](T func) -> AsyncTask { co_await std::move(func)(); }(std::forward<T>(awaitable));\n}\n\n/// Awaitable wrapper to run synchronous blocking code without blocking\n/// outstanding coroutines.\ntemplate <class T>\nauto BlockingCode(T func) -> Task<decltype(func())>\n{\n    const bool unblock = g_Scheduler.Block();\n    auto result = func();\n    if (unblock)\n    {\n        co_await g_Scheduler.Unblock();\n    }\n\n    co_return result;\n}\n\n// Awaitable semaphore.\nclass AsyncSemaphore\n{\npublic:\n    class AsyncSemaphoreTask\n    {\n    public:\n        AsyncSemaphoreTask(AsyncSemaphore& evt, uint64_t count) : m_Semaphore{evt}, m_Count{count}\n        {\n        }\n\n        bool await_ready() const noexcept\n        {\n            return m_Count == 0;\n        }\n\n        bool await_suspend(std::coroutine_handle<> awaiter) noexcept\n        {\n            m_Awaiter = awaiter;\n            return m_Semaphore.Enqueue(this, m_Count);\n        }\n\n        static void await_resume() noexcept\n        {\n        }\n\n    private:\n        friend class AsyncSemaphore;\n        AsyncSemaphore& m_Semaphore;\n        AsyncSemaphoreTask* m_Next;\n        std::coroutine_handle<> m_Awaiter;\n        uint64_t m_Count;\n    };\n\n    AsyncSemaphore(uint64_t initialCount) : m_Count(initialCount)\n    {\n    }\n\n    AsyncSemaphore(AsyncSemaphore&) = delete;\n\n    AsyncSemaphoreTask Acquire(uint64_t count) noexcept\n    {\n        if (TryAcquire(count))\n        {\n            count = 0;\n        }\n\n        return AsyncSemaphoreTask(*this, count);\n    }\n\n    bool TryAcquire(uint64_t count) noexcept\n    {\n        std::lock_guard<std::mutex> lock{m_Lock};\n        if (m_Count >= count)\n        {\n            m_Count -= count;\n            return true;\n        }\n\n        return false;\n    }\n\n    void Release(uint64_t count) noexcept\n    {\n        std::lock_guard<std::mutex> lock{m_Lock};\n        m_Count += count;\n\n        // Wake all possible waiters.\n        AsyncSemaphoreTask** head = &m_Waiter;\n        if (*head != nullptr)\n        {\n            const auto waiter = *head;\n            if (m_Count >= waiter->m_Count)\n            {\n                m_Count -= waiter->m_Count;\n                *head = waiter->m_Next;\n                g_Scheduler.Schedule(waiter->m_Awaiter);\n            }\n            else\n            {\n                head = &waiter->m_Next;\n            }\n        }\n    }\n\nprivate:\n    bool Enqueue(AsyncSemaphoreTask* task, uint64_t count)\n    {\n        std::lock_guard<std::mutex> lock{m_Lock};\n        if (m_Count >= count)\n        {\n            m_Count -= count;\n            return false;\n        }\n\n        task->m_Next = m_Waiter;\n        m_Waiter = task;\n        return true;\n    }\n\n    std::mutex m_Lock;\n    uint64_t m_Count;\n    AsyncSemaphoreTask* m_Waiter{};\n};\n\nclass AsyncEvent\n{\npublic:\n    class AsyncEventTask\n    {\n    public:\n        AsyncEventTask(AsyncEvent& evt) : m_Event{evt}\n        {\n        }\n\n        bool await_ready() const noexcept\n        {\n            return m_Event.IsSet();\n        }\n\n        bool await_suspend(std::coroutine_handle<> awaiter) noexcept\n        {\n            m_Awaiter = awaiter;\n            ULONG_PTR state = m_Event.m_State;\n            while (true)\n            {\n                if (state == m_SetState)\n                {\n                    return false;\n                }\n\n                m_Next = reinterpret_cast<AsyncEventTask*>(state);\n                if (m_Event.m_State.compare_exchange_weak(state, reinterpret_cast<ULONG_PTR>(this)))\n                {\n                    return true;\n                }\n            }\n        }\n\n        static void await_resume() noexcept\n        {\n        }\n\n    private:\n        friend class AsyncEvent;\n        AsyncEvent& m_Event;\n        AsyncEventTask* m_Next;\n        std::coroutine_handle<> m_Awaiter;\n    };\n\n    AsyncEvent() : m_State{m_UnsetState}\n    {\n    }\n\n    AsyncEvent(AsyncEvent&) = delete;\n\n    AsyncEventTask operator co_await() noexcept\n    {\n        return AsyncEventTask(*this);\n    }\n\n    bool IsSet() const noexcept\n    {\n        return m_State == m_SetState;\n    }\n\n    void Set() noexcept\n    {\n        const ULONG_PTR state = m_State.exchange(m_SetState);\n        if (state != m_SetState)\n        {\n            // Resume all waiters.\n            auto task = reinterpret_cast<AsyncEventTask*>(state);\n            while (task != nullptr)\n            {\n                const auto next = task->m_Next;\n                g_Scheduler.Schedule(task->m_Awaiter);\n                task = next;\n            }\n        }\n    }\n\n    void Reset()\n    {\n        ULONG_PTR state = m_SetState;\n        m_State.compare_exchange_strong(state, m_UnsetState);\n    }\n\nprivate:\n    static constexpr ULONG_PTR m_SetState = 1;\n    static constexpr ULONG_PTR m_UnsetState = 0;\n\n    // State is NULL if the event is not set with no waiters, 1 if it's set,\n    // or a pointer to the first waiter.\n    std::atomic<ULONG_PTR> m_State;\n};\n\n// Mutex lock that can be waited on with co_await.\n// N.B. Any waiters will be resumed on the thread that calls Unlock()\nclass AsyncLock\n{\npublic:\n    class AsyncLockTask;\n\n    // RAII class that releases the lock on scope exit.\n    // N.B. Unlike std::lock_guard, it does not acquire the lock; it must be\n    //      created when the lock is already owned.\n    class AsyncLockGuard\n    {\n    public:\n        AsyncLockGuard(AsyncLockGuard&& lock) : m_Lock(lock.m_Lock)\n        {\n            lock.m_Lock = nullptr;\n        }\n\n        AsyncLockGuard(const AsyncLockGuard&) = delete;\n\n        ~AsyncLockGuard()\n        {\n            if (m_Lock != nullptr)\n            {\n                m_Lock->Unlock();\n            }\n        }\n\n    private:\n        friend class AsyncLockTask;\n\n        AsyncLockGuard(AsyncLock& lock) : m_Lock{&lock}\n        {\n        }\n\n        AsyncLock* m_Lock;\n    };\n\n    class AsyncLockTask\n    {\n    public:\n        AsyncLockTask(AsyncLock& lock) : m_Lock{lock}, m_Next{nullptr}\n        {\n        }\n\n        static bool await_ready() noexcept\n        {\n            return false;\n        }\n\n        bool await_suspend(std::coroutine_handle<> awaiter)\n        {\n            m_Awaiter = awaiter;\n            ULONG_PTR state = m_Lock.m_State;\n            while (true)\n            {\n                if (state == m_UnlockedState)\n                {\n                    // Acquire the lock and complete synchronously if it is\n                    // not currently held.\n                    if (m_Lock.m_State.compare_exchange_weak(state, m_LockedState))\n                    {\n                        return false;\n                    }\n                }\n                else\n                {\n                    // Add this instance to the list of new waiters.\n                    if (state != m_LockedState)\n                    {\n                        m_Next = reinterpret_cast<AsyncLockTask*>(state);\n                    }\n                    else\n                    {\n                        // Must reset in case the loop ran more than once.\n                        m_Next = nullptr;\n                    }\n\n                    if (m_Lock.m_State.compare_exchange_weak(state, reinterpret_cast<ULONG_PTR>(this)))\n                    {\n                        return true;\n                    }\n                }\n            }\n        }\n\n        AsyncLockGuard await_resume() const noexcept\n        {\n            return AsyncLockGuard(m_Lock);\n        }\n\n    private:\n        friend class AsyncLock;\n        AsyncLock& m_Lock;\n        AsyncLockTask* m_Next;\n        std::coroutine_handle<> m_Awaiter;\n    };\n\n    AsyncLock() : m_State{m_UnlockedState}, m_WaitList{nullptr}\n    {\n    }\n\n    AsyncLock(const AsyncLock&) = delete;\n\n    AsyncLockTask Lock() noexcept\n    {\n        return AsyncLockTask(*this);\n    }\n\n    bool TryLock() noexcept\n    {\n        ULONG_PTR unlockedState = m_UnlockedState;\n        return m_State.compare_exchange_strong(unlockedState, m_LockedState);\n    }\n\n    void Unlock() noexcept\n    {\n        // If there are no existing waiters and no new waiters, unlock and\n        // return.\n        // N.B. Since m_WaitList is only accessed from the unlock method,\n        //      which should only be called with the lock held, it needs no\n        //      synchronization.\n        ULONG_PTR state = m_LockedState;\n        if ((m_WaitList == nullptr) && (m_State.compare_exchange_strong(state, m_UnlockedState)))\n        {\n            return;\n        }\n\n        // If there are no existing waiters, transfer the list of new waiters.\n        if (m_WaitList == nullptr)\n        {\n            // Take ownership of the list of new waiters, leaving the state\n            // locked.\n            state = m_State.exchange(m_LockedState);\n            FAIL_FAST_IF(state == m_LockedState || state == m_UnlockedState);\n\n            // Reverse the list and transfer it. This ensures waiters are\n            // awoken in FIFO order.\n            auto* current = reinterpret_cast<AsyncLockTask*>(state);\n            AsyncLockTask* previous = nullptr;\n            AsyncLockTask* next = nullptr;\n            while (current != nullptr)\n            {\n                next = current->m_Next;\n                current->m_Next = previous;\n                previous = current;\n                current = next;\n            }\n\n            m_WaitList = previous;\n        }\n\n        // Awake the first waiter; this transfers lock ownership to them.\n        AsyncLockTask* head = m_WaitList;\n        m_WaitList = head->m_Next;\n        g_Scheduler.Schedule(head->m_Awaiter);\n    }\n\nprivate:\n    static constexpr ULONG_PTR m_LockedState = 1;\n    static constexpr ULONG_PTR m_UnlockedState = 0;\n\n    // State is NULL if the lock is not held, 1 if it's held and there are no\n    // new waiters, or a pointer to the first new waiter in LIFO order.\n    std::atomic<ULONG_PTR> m_State;\n    // List of existing waiters in FIFO order.\n    AsyncLockTask* m_WaitList;\n};\n\nclass ICancellable\n{\npublic:\n    virtual ~ICancellable() = default;\n\n    virtual void Cancel() = 0;\n};\n\n// Token used to cancel an outstanding IO operation.\nclass CancelToken\n{\npublic:\n    CancelToken()\n    {\n        InitializeListHead(&m_Children);\n    }\n\n    CancelToken(CancelToken& parent) : CancelToken()\n    {\n        if (parent.AddChild(*this))\n        {\n            m_Parent = &parent;\n        }\n        else\n        {\n            m_Cancelled = true;\n        }\n    }\n\n    ~CancelToken()\n    {\n        if (m_Parent)\n        {\n            m_Parent->RemoveChild(*this);\n        }\n    }\n\n    // Register a running IO as cancellable.\n    bool Register(ICancellable& operation)\n    {\n        std::lock_guard<std::mutex> lock{m_Lock};\n        if (m_Cancelled)\n        {\n            return false;\n        }\n\n        m_Operation = &operation;\n        return true;\n    }\n\n    // Unregister the currently registered overlapped structure.\n    void Unregister()\n    {\n        std::lock_guard<std::mutex> lock{m_Lock};\n        m_Operation = nullptr;\n    }\n\n    // Cancels the token, cancelling any associated outstanding IO.\n    void Cancel()\n    {\n        std::lock_guard<std::mutex> lock{m_Lock};\n        const bool wasCancelled = m_Cancelled;\n        m_Cancelled = true;\n        if (!wasCancelled)\n        {\n            if (m_Operation != nullptr)\n            {\n                m_Operation->Cancel();\n            }\n\n            for (auto entry = m_Children.Flink; entry != &m_Children; entry = entry->Flink)\n            {\n                const auto child = CONTAINING_RECORD(entry, CancelToken, m_Link);\n                child->Cancel();\n            }\n        }\n\n        m_Cancelled = true;\n    }\n\n    // Returns whether the token has already been cancelled.\n    bool Cancelled()\n    {\n        return m_Cancelled;\n    }\n\n    // Resets the state of the token. This should only be used when\n    // the token is no longer in use by any IOs.\n    void Reset()\n    {\n        std::lock_guard<std::mutex> lock{m_Lock};\n        FAIL_FAST_IF(m_Operation != nullptr || !IsListEmpty(&m_Children));\n        m_Cancelled = false;\n    }\n\nprivate:\n    bool AddChild(CancelToken& child)\n    {\n        std::lock_guard<std::mutex> lock{m_Lock};\n        if (m_Cancelled)\n        {\n            return false;\n        }\n\n        InsertTailList(&m_Children, &child.m_Link);\n        return true;\n    }\n\n    void RemoveChild(CancelToken& child)\n    {\n        std::lock_guard<std::mutex> lock{m_Lock};\n        RemoveEntryList(&child.m_Link);\n    }\n\n    std::mutex m_Lock{};\n    ICancellable* m_Operation{};\n    std::atomic<bool> m_Cancelled{};\n    CancelToken* m_Parent{};\n    LIST_ENTRY m_Children{};\n    LIST_ENTRY m_Link{};\n};\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9commonutil.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nnamespace p9fs::util {\n\n// Writes a StatResult to a SpanWriter using the format used by Rgetattr and Rwreaddir.\ninline void SpanWriteStatResult(SpanWriter& writer, const StatResult& stat)\n{\n    writer.U32(stat.Mode);\n    writer.U32(stat.Uid);\n    writer.U32(stat.Gid);\n    writer.U64(stat.NLink);\n    writer.U64(stat.RDev);\n    writer.U64(stat.Size);\n    writer.U64(stat.BlockSize);\n    writer.U64(stat.Blocks);\n    writer.U64(stat.AtimeSec);\n    writer.U64(stat.AtimeNsec);\n    writer.U64(stat.MtimeSec);\n    writer.U64(stat.MtimeNsec);\n    writer.U64(stat.CtimeSec);\n    writer.U64(stat.CtimeNsec);\n}\n\n// Writes a directory entry to a span writer, returning whether the entry fit.\ninline bool SpanWriteDirectoryEntry(SpanWriter& writer, std::string_view name, const Qid& qid, UINT64 nextOffset, UCHAR type, const StatResult* stat = nullptr)\n{\n    size_t dirEntrySize = QidSize + sizeof(UINT64) + sizeof(UCHAR) + sizeof(UINT16) + name.size();\n    if (stat != nullptr)\n    {\n        dirEntrySize += StatResultSize;\n    }\n\n    if (static_cast<size_t>(writer.Peek().size()) < dirEntrySize)\n    {\n        return false;\n    }\n\n    writer.Qid(qid);\n    writer.U64(nextOffset);\n    writer.U8(type); // type is bits 12-15 of the file mode\n    writer.String(name);\n    if (stat != nullptr)\n    {\n        SpanWriteStatResult(writer, *stat);\n    }\n\n    return true;\n}\n\n// Determines the QidType to use for a DT_* enumeration value.\ninline QidType DirEntryTypeToQidType(int type)\n{\n    switch (type)\n    {\n    case LX_DT_DIR:\n        return QidType::Directory;\n\n    case LX_DT_LNK:\n        return QidType::Symlink;\n\n    default:\n        return QidType::File;\n    }\n}\n\n// Converts a DT_* value to a S_IF* value.\n// N.B. These constants uses the same values for the same file types, just shifted by 12 bits to\n//      make space for the permission bits.\ninline LX_MODE_T DirEntryTypeToMode(int type)\n{\n    return (type << 12);\n}\n\n// Container-like wrapper around LIST_ENTRY based linked lists.\n// N.B. It's assumed the value type has an entry named Link of type LIST_ENTRY.\n// N.B. This is by no means intended to meet the requirements of a true STL container, but provides\n//      enough functionality to at least use a for-each loop.\ntemplate <typename T>\nclass LinkedList\n{\npublic:\n    using value_type = T;\n    using reference = T&;\n    using const_reference = const T&;\n    using difference_type = ptrdiff_t;\n    using size_type = size_t;\n\n    // Bidirectional forward iterator for the LinkedList class.\n    class iterator\n    {\n    public:\n        using iterator_category = std::bidirectional_iterator_tag;\n        using value_type = T;\n        using difference_type = ptrdiff_t;\n        using pointer = T*;\n        using reference = T&;\n\n        // Creates an iterator that refers to the specified list entry.\n        explicit iterator(PLIST_ENTRY entry) : m_entry{entry}\n        {\n        }\n\n        // Moves to the next element in the list.\n        iterator& operator++()\n        {\n            m_entry = m_entry->Flink;\n            return *this;\n        }\n\n        // Moves to the next element in the list.\n        iterator operator++(int)\n        {\n            auto result = *this;\n            m_entry = m_entry->Flink;\n            return result;\n        }\n\n        // Moves to the previous element in the list.\n        iterator& operator--()\n        {\n            m_entry = m_entry->Blink;\n            return *this;\n        }\n\n        // Moves to the previous element in the list.\n        iterator operator--(int)\n        {\n            auto result = *this;\n            m_entry = m_entry->Blink;\n            return result;\n        }\n\n        // Checks whether two iterators refer to the same entry.\n        bool operator==(const iterator& other) const\n        {\n            return m_entry == other.m_entry;\n        }\n\n        // Checks whether two iterators do not refer to the same entry.\n        bool operator!=(const iterator& other) const\n        {\n            return m_entry != other.m_entry;\n        }\n\n        // Returns the value referred to by the iterator.\n        reference operator*()\n        {\n            return *CONTAINING_RECORD(m_entry, T, Link);\n        }\n\n    private:\n        PLIST_ENTRY m_entry;\n    };\n\n    // Initializes a new LinkedList.\n    LinkedList()\n    {\n        InitializeListHead(&m_head);\n    }\n\n    // Destroys the LinkedList.\n    ~LinkedList()\n    {\n        // LinkedList doesn't own the items, so it can't clear the list on destruction. Instead,\n        // the list should already be cleared.\n        WI_ASSERT(IsListEmpty(&m_head));\n    }\n\n    // This class is not copyable or moveable.\n    // N.B. It could be made moveable, but that would require modifying the list to point to the\n    //      new list head, and is not done here.\n    LinkedList(const LinkedList&) = delete;\n    LinkedList& operator=(const LinkedList&) = delete;\n    LinkedList(LinkedList&&) = delete;\n    LinkedList& operator=(LinkedList&&) = delete;\n\n    // Inserts a new item into the list.\n    void Insert(T& value)\n    {\n        InsertTailList(&m_head, std::addressof(value.Link));\n    }\n\n    // Removes an item from the list.\n    // N.B. This could be static, but isn't for ease of invocation and so debug builds can assert\n    //      the entry belongs to this list.\n    void Remove(T& value)\n    {\n        WI_ASSERT(Contains(value));\n\n        RemoveEntryList(std::addressof(value.Link));\n    }\n\n    // Checks whether the list contains a specific entry.\n    bool Contains(const T& value)\n    {\n        for (auto entry = m_head.Flink; entry != &m_head; entry = entry->Flink)\n        {\n            if (entry == std::addressof(value.Link))\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    // Returns an iterator to the first element.\n    iterator begin()\n    {\n        return iterator{m_head.Flink};\n    }\n\n    // Returns an iterator beyond the last element.\n    iterator end()\n    {\n        return iterator{&m_head};\n    }\n\nprivate:\n    LIST_ENTRY m_head;\n};\n\n} // namespace p9fs::util\n"
  },
  {
    "path": "src/linux/plan9/p9data.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    p9data.h\n\nAbstract:\n\n    This file contains the plan9 message types to size mapping logic.\n\n--*/\n\n#pragma once\n\nnamespace p9fs {\n\n// Gets the minimum message size for each Plan 9 message type.\n// If the messages can be a dynamic length, this size is the minimum size of\n// the message if all dynamic content is omitted. In the case of strings, the\n// size of the two-byte length field is included, but the string length itself\n// is not. The omitted components are listed with each message type, and the\n// caller is responsible for adding the right values if necessary.\nconstexpr UINT32 GetMessageSize(MessageType messageType)\n{\n    switch (messageType)\n    {\n    case MessageType::Tversion:\n        // size[4] Tversion tag[2] msize[4] version[s]\n        // Excludes: version string data\n        return HeaderSize + /*msize*/ 4 + /*version*/ 2;\n\n    case MessageType::Rversion:\n        // size[4] Rversion tag[2] msize[4] version[s]\n        // Excludes: version string data\n        return HeaderSize + /*msize*/ 4 + /*version*/ 2;\n\n    case MessageType::Tflush:\n        // size[4] Tflush tag[2] oldtag[2]\n        return HeaderSize + /*oldtag*/ 2;\n\n    case MessageType::Rflush:\n        // size[4] Rflush tag[2]\n        return HeaderSize;\n\n    case MessageType::Twalk:\n        // size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])\n        // Excludes: repeated elements\n        return HeaderSize + /*fid*/ 4 + /*newfid*/ 4 + /*nwname*/ 2;\n\n    case MessageType::Rwalk:\n        // size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])\n        // Excludes: repeated elements\n        return HeaderSize + /*nwqid*/ 2;\n\n    case MessageType::Tread:\n        // size[4] Tread tag[2] fid[4] offset[8] count[4]\n        return HeaderSize + /*fid*/ 4 + /*offset*/ 8 + /*count*/ 4;\n\n    case MessageType::Rread:\n        // size[4] Rread tag[2] count[4] data[count]\n        // Excludes: data\n        return HeaderSize + /*count*/ 4;\n\n    case MessageType::Twrite:\n        // size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]\n        // Excludes: data\n        return HeaderSize + /*fid*/ 4 + /*offset*/ 8 + /*count*/ 4;\n\n    case MessageType::Rwrite:\n        // size[4] Rwrite tag[2] count[4]\n        return HeaderSize + /*count*/ 4;\n\n    case MessageType::Tclunk:\n        // size[4] Tclunk tag[2] fid[4]\n        return HeaderSize + /*fid*/ 4;\n\n    case MessageType::Rclunk:\n        // size[4] Rclunk tag[2]\n        return HeaderSize;\n\n    case MessageType::Tremove:\n        // size[4] Tremove tag[2] fid[4]\n        return HeaderSize + /*fid*/ 4;\n\n    case MessageType::Rremove:\n        // size[4] Rremove tag[2]\n        return HeaderSize;\n\n    case MessageType::Tauth:\n        // size[4] Tauth tag[2] afid[4] uname[s] aname[s] n_uname[4]\n        // Excludes: uname string data, aname string data\n        return HeaderSize + /*afid*/ 4 + /*uname*/ 2 + /*aname*/ 2 + /*n_uname*/ 4;\n\n    case MessageType::Rauth:\n        // size[4] Rauth tag[2] aqid[13]\n        return HeaderSize + /*aqid*/ 13;\n\n    case MessageType::Tattach:\n        // size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4]\n        // Excludes: uname string data, aname string data\n        return HeaderSize + /*fid*/ 4 + /*afid*/ 4 + /*uname*/ 2 + /*aname*/ 2 + /*n_uname*/ 4;\n\n    case MessageType::Rattach:\n        // size[4] Rattach tag[2] qid[13]\n        return HeaderSize + /*qid*/ 13;\n\n    case MessageType::Rlerror:\n        // size[4] Rlerror tag[2] ecode[4]\n        return HeaderSize + /*ecode*/ 4;\n\n    case MessageType::Tstatfs:\n        // size[4] Tstatfs tag[2] fid[4]\n        return HeaderSize + /*fid*/ 4;\n\n    case MessageType::Rstatfs:\n        // size[4] Rstatfs tag[2] type[4] bsize[4] blocks[8] bfree[8] bavail[8] files[8] ffree[8] fsid[8] namelen[4]\n        return HeaderSize + /*type*/ 4 + /*bsize*/ 4 + /*blocks*/ 8 + /*bfree*/ 8 + /*bavail*/ 8 + /*files*/ 8 + /*ffree*/ 8 +\n               /*fsid*/ 8 + /*namelen*/ 4;\n\n    case MessageType::Tlopen:\n        // size[4] Tlopen tag[2] fid[4] flags[4]\n        return HeaderSize + /*fid*/ 4 + /*flags*/ 4;\n\n    case MessageType::Rlopen:\n        // size[4] Rlopen tag[2] qid[13] iounit[4]\n        return HeaderSize + /*qid*/ 13 + /*iounit*/ 4;\n\n    case MessageType::Tlcreate:\n        // size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4]\n        // Excludes: name string data\n        return HeaderSize + /*fid*/ 4 + /*name*/ 2 + /*flags*/ 4 + /*mode*/ 4 + /*gid*/ 4;\n\n    case MessageType::Rlcreate:\n        // size[4] Rlcreate tag[2] qid[13] iounit[4]\n        return HeaderSize + /*qid*/ 13 + /*iounit*/ 4;\n\n    case MessageType::Tsymlink:\n        // size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4]\n        // Excludes: name string data, symtgt string data\n        return HeaderSize + /*fid*/ 4 + /*name*/ 2 + /*symtgt*/ 2 + /*gid*/ 4;\n\n    case MessageType::Rsymlink:\n        // size[4] Rsymlink tag[2] qid[13]\n        return HeaderSize + /*qid*/ 13;\n\n    case MessageType::Tmknod:\n        // size[4] Tmknod tag[2] dfid[4] name[s] mode[4] major[4] minor[4] gid[4]\n        // Excludes: name string data\n        return HeaderSize + /*dfid*/ 4 + /*name*/ 2 + /*mode*/ 4 + /*major*/ 4 + /*minor*/ 4 + /*gid*/ 4;\n\n    case MessageType::Rmknod:\n        // size[4] Rmknod tag[2] qid[13]\n        return HeaderSize + /*qid*/ 13;\n\n    case MessageType::Trename:\n        // size[4] Trename tag[2] fid[4] dfid[4] name[s]\n        // Excludes: name string data\n        return HeaderSize + /*fid*/ 4 + /*dfid*/ 4 + /*name*/ 2;\n\n    case MessageType::Rrename:\n        // size[4] Rrename tag[2]\n        return HeaderSize;\n\n    case MessageType::Treadlink:\n        // size[4] Treadlink tag[2] fid[4]\n        return HeaderSize + /*fid*/ 4;\n\n    case MessageType::Rreadlink:\n        // size[4] Rreadlink tag[2] target[s]\n        // Excludes: target string data\n        return HeaderSize + /*target*/ 2;\n\n    case MessageType::Tgetattr:\n        // size[4] Tgetattr tag[2] fid[4] request_mask[8]\n        return HeaderSize + /*fid*/ 4 + /*request_mask*/ 8;\n\n    case MessageType::Rgetattr:\n        // size[4] Rgetattr tag[2] valid[8] qid[13] mode[4] uid[4] gid[4] nlink[8] rdev[8] size[8] blksize[8] blocks[8] atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8] gen[8] data_version[8]\n        return HeaderSize + /*valid*/ 8 + /*qid*/ 13 + /*mode*/ 4 + /*uid*/ 4 + /*gid*/ 4 + /*nlink*/ 8 + /*rdev*/ 8 + /*size*/ 8 +\n               /*blksize*/ 8 + /*blocks*/ 8 + /*atime_sec*/ 8 + /*atime_nsec*/ 8 + /*mtime_sec*/ 8 + /*mtime_nsec*/ 8 +\n               /*ctime_sec*/ 8 + /*ctime_nsec*/ 8 + /*btime_sec*/ 8 + /*btime_nsec*/ 8 + /*gen*/ 8 + /*data_version*/ 8;\n\n    case MessageType::Tsetattr:\n        // size[4] Tsetattr tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8] atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]\n        return HeaderSize + /*fid*/ 4 + /*valid*/ 4 + /*mode*/ 4 + /*uid*/ 4 + /*gid*/ 4 + /*size*/ 8 + /*atime_sec*/ 8 +\n               /*atime_nsec*/ 8 + /*mtime_sec*/ 8 + /*mtime_nsec*/ 8;\n\n    case MessageType::Rsetattr:\n        // size[4] Rsetattr tag[2]\n        return HeaderSize;\n\n    case MessageType::Txattrwalk:\n        // size[4] Txattrwalk tag[2] fid[4] newfid[4] name[s]\n        // Excludes: name string data\n        return HeaderSize + /*fid*/ 4 + /*newfid*/ 4 + /*name*/ 2;\n\n    case MessageType::Rxattrwalk:\n        // size[4] Rxattrwalk tag[2] size[8]\n        return HeaderSize + /*size*/ 8;\n\n    case MessageType::Txattrcreate:\n        // size[4] Txattrcreate tag[2] fid[4] name[s] attr_size[8] flags[4]\n        // Excludes: name string data\n        return HeaderSize + /*fid*/ 4 + /*name*/ 2 + /*attr_size*/ 8 + /*flags*/ 4;\n\n    case MessageType::Rxattrcreate:\n        // size[4] Rxattrcreate tag[2]\n        return HeaderSize;\n\n    case MessageType::Treaddir:\n        // size[4] Treaddir tag[2] fid[4] offset[8] count[4]\n        return HeaderSize + /*fid*/ 4 + /*offset*/ 8 + /*count*/ 4;\n\n    case MessageType::Rreaddir:\n        // size[4] Rreaddir tag[2] count[4] data[count]\n        // Excludes: data\n        return HeaderSize + /*count*/ 4;\n\n    case MessageType::Tfsync:\n        // size[4] Tfsync tag[2] fid[4]\n        return HeaderSize + /*fid*/ 4;\n\n    case MessageType::Rfsync:\n        // size[4] Rfsync tag[2]\n        return HeaderSize;\n\n    case MessageType::Tlock:\n        // size[4] Tlock tag[2] fid[4] type[1] flags[4] start[8] length[8] proc_id[4] client_id[s]\n        // Excludes: client_id string data\n        return HeaderSize + /*fid*/ 4 + /*type*/ 1 + /*flags*/ 4 + /*start*/ 8 + /*length*/ 8 + /*proc_id*/ 4 + /*client_id*/ 2;\n\n    case MessageType::Rlock:\n        // size[4] Rlock tag[2] status[1]\n        return HeaderSize + /*status*/ 1;\n\n    case MessageType::Tgetlock:\n        // size[4] Tgetlock tag[2] fid[4] type[1] start[8] length[8] proc_id[4] client_id[s]\n        // Excludes: client_id string data\n        return HeaderSize + /*fid*/ 4 + /*type*/ 1 + /*start*/ 8 + /*length*/ 8 + /*proc_id*/ 4 + /*client_id*/ 2;\n\n    case MessageType::Rgetlock:\n        // size[4] Rgetlock tag[2] type[1] start[8] length[8] proc_id[4] client_id[s]\n        // Excludes: client_id string data\n        return HeaderSize + /*type*/ 1 + /*start*/ 8 + /*length*/ 8 + /*proc_id*/ 4 + /*client_id*/ 2;\n\n    case MessageType::Tlink:\n        // size[4] Tlink tag[2] dfid[4] fid[4] name[s]\n        // Excludes: name string data\n        return HeaderSize + /*dfid*/ 4 + /*fid*/ 4 + /*name*/ 2;\n\n    case MessageType::Rlink:\n        // size[4] Rlink tag[2]\n        return HeaderSize;\n\n    case MessageType::Tmkdir:\n        // size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4]\n        // Excludes: name string data\n        return HeaderSize + /*dfid*/ 4 + /*name*/ 2 + /*mode*/ 4 + /*gid*/ 4;\n\n    case MessageType::Rmkdir:\n        // size[4] Rmkdir tag[2] qid[13]\n        return HeaderSize + /*qid*/ 13;\n\n    case MessageType::Trenameat:\n        // size[4] Trenameat tag[2] olddirfid[4] oldname[s] newdirfid[4] newname[s]\n        // Excludes: oldname string data, newname string data\n        return HeaderSize + /*olddirfid*/ 4 + /*oldname*/ 2 + /*newdirfid*/ 4 + /*newname*/ 2;\n\n    case MessageType::Rrenameat:\n        // size[4] Rrenameat tag[2]\n        return HeaderSize;\n\n    case MessageType::Tunlinkat:\n        // size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4]\n        // Excludes: name string data\n        return HeaderSize + /*dirfd*/ 4 + /*name*/ 2 + /*flags*/ 4;\n\n    case MessageType::Runlinkat:\n        // size[4] Runlinkat tag[2]\n        return HeaderSize;\n\n    case MessageType::Taccess:\n        // size[4] Taccess tag[2] fid[4] flags[4]\n        return HeaderSize + /*fid*/ 4 + /*flags*/ 4;\n\n    case MessageType::Raccess:\n        // size[4] Raccess tag[2]\n        return HeaderSize;\n\n    case MessageType::Twreaddir:\n        // size[4] Twreaddir tag[2] fid[4] offset[8] count[4]\n        return HeaderSize + /*fid*/ 4 + /*offset*/ 8 + /*count*/ 4;\n\n    case MessageType::Rwreaddir:\n        // size[4] Rwreaddir tag[2] count[4] data[count]\n        // Excludes: data\n        return HeaderSize + /*count*/ 4;\n\n    case MessageType::Twopen:\n        // size[4] Twopen tag[2] fid[4] newfid[4] flags[4] wflags[4] mode[4] gid[4] attr_mask[8] nwname[2] nwname*(wname[s])\n        // Excludes: repeated elements\n        return HeaderSize + /*fid*/ 4 + /*newfid*/ 4 + /*flags*/ 4 + /*wflags*/ 4 + /*mode*/ 4 + /*gid*/ 4 + /*attr_mask*/ 8 + /*nwname*/ 2;\n\n    case MessageType::Rwopen:\n        // size[4] Rwopen tag[2] status[1] walked[2] qid[13] symlink_target[s] iounit[4] mode[4] uid[4] gid[4] nlink[8] rdev[8]\n        // size[8] blksize[8] blocks[8] atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] ctime_sec[8] ctime_nsec[8]\n        // btime_sec[8] btime_nsec[8] gen[8] data_version[8] Excludes: symlink_target string data\n        return HeaderSize + /*status*/ 1 + /*walked*/ 2 + /*qid*/ 13 + /*symlink_target*/ 2 + /*iounit*/ 4 + /*mode*/ 4 +\n               /*uid*/ 4 + /*gid*/ 4 + /*nlink*/ 8 + /*rdev*/ 8 + /*size*/ 8 + /*blksize*/ 8 + /*blocks*/ 8 + /*atime_sec*/ 8 +\n               /*atime_nsec*/ 8 + /*mtime_sec*/ 8 + /*mtime_nsec*/ 8 + /*ctime_sec*/ 8 + /*ctime_nsec*/ 8 + /*btime_sec*/ 8 +\n               /*btime_nsec*/ 8 + /*gen*/ 8 + /*data_version*/ 8;\n\n    default:\n        return 0;\n    }\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9defs.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nnamespace p9fs {\n\nconstexpr std::string_view ProtocolVersionL{\"9P2000.L\"};\nconstexpr std::string_view ProtocolVersionW{\"9P2000.W\"};\n\n// The plan9 message type. Messages starting with T are request messages, and\n// messages starting with R are response messages. The response message is always\n// one plus the request message.\nenum class MessageType : UINT8\n{\n    Tlerror = 6,\n    Rlerror,\n    Tstatfs = 8,\n    Rstatfs,\n    Tlopen = 12,\n    Rlopen,\n    Tlcreate = 14,\n    Rlcreate,\n    Tsymlink = 16,\n    Rsymlink,\n    Tmknod = 18,\n    Rmknod,\n    Trename = 20,\n    Rrename,\n    Treadlink = 22,\n    Rreadlink,\n    Tgetattr = 24,\n    Rgetattr,\n    Tsetattr = 26,\n    Rsetattr,\n    Txattrwalk = 30,\n    Rxattrwalk,\n    Txattrcreate = 32,\n    Rxattrcreate,\n    Treaddir = 40,\n    Rreaddir,\n    Tfsync = 50,\n    Rfsync,\n    Tlock = 52,\n    Rlock,\n    Tgetlock = 54,\n    Rgetlock,\n    Tlink = 70,\n    Rlink,\n    Tmkdir = 72,\n    Rmkdir,\n    Trenameat = 74,\n    Rrenameat,\n    Tunlinkat = 76,\n    Runlinkat,\n    Tversion = 100,\n    Rversion,\n    Tauth = 102,\n    Rauth,\n    Tattach = 104,\n    Rattach,\n    Terror = 106,\n    Rerror,\n    Tflush = 108,\n    Rflush,\n    Twalk = 110,\n    Rwalk,\n    Topen = 112,\n    Ropen,\n    Tcreate = 114,\n    Rcreate,\n    Tread = 116,\n    Rread,\n    Twrite = 118,\n    Rwrite,\n    Tclunk = 120,\n    Rclunk,\n    Tremove = 122,\n    Rremove,\n    Tstat = 124,\n    Rstat,\n    Twstat = 126,\n    Rwstat,\n    // 9P2000.W messages:\n    // N.B. 9P2000.W is a currently unofficial extension to 9P2000.L which includes some messages\n    //      used by the Windows Plan 9 redirector for improved functionality and performance.\n    Taccess = 128,\n    Raccess,\n    Twreaddir = 130,\n    Rwreaddir,\n    Twopen = 132,\n    Rwopen\n};\n\n// The type of the file, as indicated in a Qid.\nenum class QidType : UINT8\n{\n    File = 0x00,\n    Link = 0x01,\n    Symlink = 0x02,\n    Temp = 0x04,\n    Auth = 0x08,\n    MountPoint = 0x10,\n    Exclusive = 0x20,\n    Append = 0x40,\n    Directory = 0x80\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(QidType);\n\n// A type representing a unique file identifier. On Linux, the path is used as\n// the inode number.\nstruct Qid\n{\n    UINT64 Path;\n    UINT32 Version;\n    QidType Type;\n};\n\n// File system attributes, used in statfs\nstruct StatFsResult\n{\n    UINT32 Type;\n    UINT32 BlockSize;\n    UINT64 Blocks;\n    UINT64 BlocksFree;\n    UINT64 BlocksAvailable;\n    UINT64 Files;\n    UINT64 FilesFree;\n    UINT64 FsId;\n    UINT32 NameLength;\n};\n\n// Linux file attributes, used in getattr and setattr.\nstruct StatResult\n{\n    UINT32 Mode;\n    UINT32 Uid;\n    UINT32 Gid;\n    UINT64 NLink;\n    UINT64 RDev;\n    UINT64 Size;\n    UINT64 BlockSize;\n    UINT64 Blocks;\n    UINT64 AtimeSec;\n    UINT64 AtimeNsec;\n    UINT64 MtimeSec;\n    UINT64 MtimeNsec;\n    UINT64 CtimeSec;\n    UINT64 CtimeNsec;\n};\n\n// Wire size of the StatResult structure.\n// N.B. Can't use sizeof(StatResult) because it contains padding.\nconstexpr UINT32 StatResultSize = (3 * sizeof(UINT32)) + (11 * sizeof(UINT64));\n\nconstexpr UINT32 TagOffset = sizeof(UINT32) + sizeof(UINT8);\nconstexpr UINT32 HeaderSize = sizeof(UINT32) + sizeof(UINT8) + sizeof(UINT16);\nconstexpr UINT32 QidSize = sizeof(UINT8) + sizeof(UINT32) + sizeof(UINT64);\nconstexpr UINT32 NoFid = ~0u;\n\n// The \"diod\" server requires the max IO buffer to be at least 24 bytes smaller than the negotiated\n// message size. While our Plan 9 server does not require it, it's used just in case.\nconstexpr UINT32 IoHeaderSize = 24;\n\n// High bits for file modes. The low bits are used\n// for permission bits.\nconstexpr UINT32 ModeDirectory = 040000;\nconstexpr UINT32 ModeRegularFile = 0100000;\n\n// Bitmask of valid attributes for getattr requests.\nconstexpr UINT32 GetAttrMode = 0x1;\nconstexpr UINT32 GetAttrNlink = 0x2;\nconstexpr UINT32 GetAttrUid = 0x4;\nconstexpr UINT32 GetAttrGid = 0x8;\nconstexpr UINT32 GetAttrRdev = 0x10;\nconstexpr UINT32 GetAttrAtime = 0x20;\nconstexpr UINT32 GetAttrMtime = 0x40;\nconstexpr UINT32 GetAttrCtime = 0x80;\nconstexpr UINT32 GetAttrIno = 0x100;\nconstexpr UINT32 GetAttrSize = 0x200;\nconstexpr UINT32 GetAttrBlocks = 0x400;\nconstexpr UINT32 GetAttrBtime = 0x800;\nconstexpr UINT32 GetAttrGen = 0x1000;\nconstexpr UINT32 GetAttrDataVersion = 0x2000;\n\n// Bitmask of valid attributes for setattr requests.\nconstexpr UINT32 SetAttrMode = 0x1;\nconstexpr UINT32 SetAttrUid = 0x2;\nconstexpr UINT32 SetAttrGid = 0x4;\nconstexpr UINT32 SetAttrSize = 0x8;\nconstexpr UINT32 SetAttrAtime = 0x10;\nconstexpr UINT32 SetAttrMtime = 0x20;\nconstexpr UINT32 SetAttrCtime = 0x40;\nconstexpr UINT32 SetAttrAtimeSet = 0x80;\nconstexpr UINT32 SetAttrMtimeSet = 0x100;\n\n// Flags for the lopen and create messages.\n// N.B. These may not be identical to Linux open flags on all platforms.\nenum class OpenFlags\n{\n    ReadOnly = 0,\n    WriteOnly = 01,\n    ReadWrite = 02,\n    NoAccess = 03,\n    AccessMask = 03,\n    Create = 0100,\n    Exclusive = 0200,\n    NoCTTY = 0400,\n    Truncate = 01000,\n    Append = 02000,\n    NonBlock = 04000,\n    DSync = 010000,\n    FAsync = 00020000,\n    Direct = 00040000,\n    LargeFile = 00100000,\n    Directory = 0200000,\n    NoFollow = 00400000,\n    NoAccessTime = 01000000,\n    CloseOnExec = 02000000,\n    Sync = 04000000\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(OpenFlags);\n\n// File lock types\nenum class LockType\n{\n    ReadLock,\n    WriteLock,\n    Unlock\n};\n\n// Status values returned by the lock message.\nenum class LockStatus\n{\n    Success,\n    Blocked,\n    Error,\n    Grace\n};\n\nenum class AccessFlags\n{\n    Ok = 0,\n    Execute = 1,\n    Write = 2,\n    Read = 4,\n    Delete = 8,\n    All = Execute | Write | Read | Delete\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(AccessFlags);\n\n// Flags for the wopen message.\nenum class WOpenFlags\n{\n    None = 0,\n    DeleteAccess = 0x1,\n    NonDirectoryFile = 0x2,\n    OpenSymlink = 0x4,\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(WOpenFlags);\n\n// Status values returned by the wopen message.\nenum class WOpenStatus : UINT8\n{\n    Opened,\n    Created,\n    ParentNotFound,\n    NotFound,\n    Stopped,\n};\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9errors.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9defs.h\"\n#include \"expected.h\"\n#include \"result_macros.h\"\n\nnamespace p9fs {\n\nusing util::BasicExpected;\nusing util::Unexpected;\n\nusing LxError = Unexpected<LX_INT>;\n\ntemplate <typename T>\nusing Expected = BasicExpected<T, LX_INT>;\n\nnamespace util {\n\n    LX_INT LinuxErrorFromCaughtException();\n\n} // namespace util\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9fid.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9fid.h\"\n\nnamespace p9fs {\n\nExpected<Qid> Fid::Walk(std::string_view)\n{\n    return LxError{LX_EINVAL};\n}\n\nExpected<std::tuple<UINT64, Qid, StatResult>> Fid::GetAttr(UINT64)\n{\n    return LxError{LX_EINVAL};\n}\n\nLX_INT Fid::SetAttr(UINT32, const StatResult&)\n{\n    return LX_EINVAL;\n}\n\nExpected<Qid> Fid::Open(OpenFlags)\n{\n    return LxError{LX_EINVAL};\n}\n\nExpected<Qid> Fid::Create(std::string_view, OpenFlags, UINT32, UINT32)\n{\n    return LxError{LX_EINVAL};\n}\n\nExpected<Qid> Fid::MkDir(std::string_view, UINT32, UINT32)\n{\n    return LxError{LX_EINVAL};\n}\n\nLX_INT Fid::ReadDir(UINT64, SpanWriter&, bool)\n{\n    return LX_EINVAL;\n}\n\nTask<Expected<UINT32>> Fid::Read(UINT64, gsl::span<gsl::byte>)\n{\n    co_return LxError{LX_EINVAL};\n}\n\nTask<Expected<UINT32>> Fid::Write(UINT64, gsl::span<const gsl::byte>)\n{\n    co_return LxError{LX_EINVAL};\n}\n\nLX_INT Fid::UnlinkAt(std::string_view, UINT32)\n{\n    return LX_EINVAL;\n}\n\nLX_INT Fid::Remove()\n{\n    return LX_EINVAL;\n}\n\nLX_INT Fid::RenameAt(std::string_view, Fid&, std::string_view)\n{\n    return LX_EINVAL;\n}\n\nLX_INT Fid::Rename(Fid&, std::string_view)\n{\n    return LX_EINVAL;\n}\n\nExpected<Qid> Fid::SymLink(std::string_view, std::string_view, UINT32)\n{\n    return LxError{LX_EINVAL};\n}\n\nExpected<Qid> Fid::MkNod(std::string_view, UINT32, UINT32, UINT32, UINT32)\n{\n    return LxError{LX_EINVAL};\n}\n\nLX_INT Fid::Link(std::string_view, Fid&)\n{\n    return LX_EINVAL;\n}\n\nExpected<UINT32> Fid::ReadLink(gsl::span<char>)\n{\n    return LxError{LX_EINVAL};\n}\n\nLX_INT Fid::Fsync()\n{\n    return LX_EINVAL;\n}\n\nExpected<StatFsResult> Fid::StatFs()\n{\n    return LxError{LX_EINVAL};\n}\n\nExpected<LockStatus> Fid::Lock(LockType, UINT32, UINT64, UINT64, UINT32, const std::string_view)\n{\n    return LxError{LX_EINVAL};\n}\n\nExpected<std::tuple<LockType, UINT64, UINT64, UINT32, std::string_view>> Fid::GetLock(LockType, UINT64, UINT64, UINT32, const std::string_view)\n{\n    return LxError{LX_EINVAL};\n}\n\nExpected<std::shared_ptr<XAttrBase>> Fid::XattrWalk(const std::string&)\n{\n    return LxError{LX_EINVAL};\n}\n\nExpected<std::shared_ptr<XAttrBase>> Fid::XattrCreate(const std::string&, UINT64, UINT32)\n{\n    return LxError{LX_EINVAL};\n}\n\nLX_INT Fid::Clunk()\n{\n    // The default implementation of Clunk returns success rather than error,\n    // because all fid's must support clunk.\n    return {};\n}\n\nLX_INT Fid::Access(AccessFlags)\n{\n    return LX_ENOTSUP;\n}\n\nstd::shared_ptr<Fid> Fid::Clone() const\n{\n    THROW_INVALID();\n}\n\nbool Fid::IsOnRoot(const std::shared_ptr<const IRoot>&)\n{\n    THROW_INVALID();\n}\n\nbool Fid::IsFile() const\n{\n    return false;\n}\n\nQid Fid::GetQid() const\n{\n    THROW_INVALID();\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9fid.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9defs.h\"\n#include \"p9await.h\"\n#include \"p9errors.h\"\n#include \"p9protohelpers.h\"\n#include \"p9handler.h\"\n\nnamespace p9fs {\n\nclass XAttrBase;\n\nclass Fid\n{\npublic:\n    virtual ~Fid() = default;\n\n    virtual Expected<Qid> Walk(std::string_view Name);\n    virtual Expected<std::tuple<UINT64, Qid, StatResult>> GetAttr(UINT64 Mask);\n    virtual LX_INT SetAttr(UINT32 Valid, const StatResult& Stat);\n    virtual Expected<Qid> Open(OpenFlags Flags);\n    virtual Expected<Qid> Create(std::string_view Name, OpenFlags Flags, UINT32 Mode, UINT32 Gid);\n    virtual Expected<Qid> MkDir(std::string_view Name, UINT32 Mode, UINT32 Gid);\n    virtual LX_INT ReadDir(UINT64 Offset, SpanWriter& Writer, bool IncludeAttributes);\n    virtual Task<Expected<UINT32>> Read(UINT64 Offset, gsl::span<gsl::byte> Buffer);\n    virtual Task<Expected<UINT32>> Write(UINT64 Offset, gsl::span<const gsl::byte> Buffer);\n    virtual LX_INT UnlinkAt(std::string_view Name, UINT32 Flags);\n    virtual LX_INT Remove();\n    virtual LX_INT RenameAt(std::string_view OldName, Fid& NewParent, std::string_view NewName);\n    virtual LX_INT Rename(Fid& NewParent, std::string_view NewName);\n    virtual Expected<Qid> SymLink(std::string_view Name, std::string_view Target, UINT32 Gid);\n    virtual Expected<Qid> MkNod(std::string_view Name, UINT32 Mode, UINT32 Major, UINT32 Minor, UINT32 Gid);\n    virtual LX_INT Link(std::string_view Name, Fid& Target);\n    virtual Expected<UINT32> ReadLink(gsl::span<char> Name);\n    virtual LX_INT Fsync();\n    virtual Expected<StatFsResult> StatFs();\n    virtual Expected<LockStatus> Lock(LockType Type, UINT32 Flags, UINT64 Start, UINT64 Length, UINT32 ProcId, std::string_view ClientId);\n    virtual Expected<std::tuple<LockType, UINT64, UINT64, UINT32, std::string_view>> GetLock(\n        LockType Type, UINT64 Start, UINT64 Length, UINT32 ProcId, std::string_view ClientId);\n    virtual Expected<std::shared_ptr<XAttrBase>> XattrWalk(const std::string& Name);\n    virtual Expected<std::shared_ptr<XAttrBase>> XattrCreate(const std::string& Name, UINT64 Size, UINT32 Flags);\n    virtual LX_INT Clunk();\n\n    // 9P2000.W operations\n    virtual LX_INT Access(AccessFlags Flags);\n\n    virtual std::shared_ptr<Fid> Clone() const;\n    virtual bool IsOnRoot(const std::shared_ptr<const IRoot>& root);\n    virtual bool IsFile() const;\n    virtual Qid GetQid() const;\n\nprotected:\n    Fid() = default;\n};\n\nclass XAttrBase : public Fid\n{\npublic:\n    virtual Expected<UINT64> GetSize() = 0;\n};\n\n#undef CreateFile\nExpected<std::tuple<std::shared_ptr<Fid>, Qid>> CreateFile(std::shared_ptr<const IRoot> root, LX_UID_T uid);\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9file.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9errors.h\"\n#include \"p9file.h\"\n#include \"p9util.h\"\n#include \"p9commonutil.h\"\n#include \"p9xattr.h\"\n#include <mountutilcpp.h>\n#include <sys/syscall.h>\n#include <sys/sysmacros.h>\n\nusing namespace std::string_view_literals;\n\nnamespace p9fs {\n\nconstexpr std::string_view c_drvfsFsType = \"drvfs\"sv;\nconstexpr std::string_view c_p9FsType = \"9p\"sv;\nconstexpr std::string_view c_virtioFsType = \"virtiofs\"sv;\n\nstruct OpenFlagMapping\n{\n    OpenFlags P9Flag;\n    int LinuxFlag;\n};\n\nconst OpenFlagMapping c_openFlagsMapping[] = {\n    {OpenFlags::WriteOnly, O_WRONLY},\n    {OpenFlags::ReadWrite, O_RDWR},\n    {OpenFlags::Create, O_CREAT},\n    {OpenFlags::Exclusive, O_EXCL},\n    {OpenFlags::NoCTTY, O_NOCTTY},\n    {OpenFlags::Truncate, O_TRUNC},\n    {OpenFlags::Append, O_APPEND},\n    {OpenFlags::NonBlock, O_NONBLOCK},\n    {OpenFlags::DSync, O_DSYNC},\n    {OpenFlags::FAsync, O_ASYNC},\n    {OpenFlags::Direct, O_DIRECT},\n    {OpenFlags::LargeFile, O_LARGEFILE},\n    {OpenFlags::Directory, O_DIRECTORY},\n    {OpenFlags::NoFollow, O_NOFOLLOW},\n    {OpenFlags::NoAccessTime, O_NOATIME},\n    {OpenFlags::CloseOnExec, O_CLOEXEC},\n    {OpenFlags::Sync, O_SYNC}};\n\nExpected<std::tuple<std::shared_ptr<Fid>, Qid>> CreateFile(std::shared_ptr<const IRoot> root, LX_UID_T uid)\n{\n    auto realRoot = std::static_pointer_cast<const Root>(root);\n    auto file = std::make_shared<File>(realRoot);\n    auto qid = file->Initialize();\n    if (!qid)\n    {\n        return qid.Unexpected();\n    }\n\n    return std::tuple<std::shared_ptr<Fid>, Qid>{std::move(file), qid.Get()};\n}\n\nQidType ModeToQidType(mode_t mode)\n{\n    if (S_ISLNK(mode))\n    {\n        return QidType::Symlink;\n    }\n\n    if (S_ISDIR(mode))\n    {\n        return QidType::Directory;\n    }\n\n    return QidType::File;\n}\n\n// Converts the result of a stat system call to a qid value.\nQid StatToQid(const struct stat& st)\n{\n    return {st.st_ino, 0, ModeToQidType(st.st_mode)};\n}\n\n// Get the qid for a file.\n// N.B. The caller is responsible for setting the right thread uid/gid before calling this.\nExpected<Qid> GetFileQidByPath(int fd, const std::string& path)\n{\n    struct stat st;\n    int result = fstatat(fd, path.c_str(), &st, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH);\n    if (result < 0)\n    {\n        return LxError{-errno};\n    }\n\n    return StatToQid(st);\n}\n\n// Appends a valid Linux path segment to a Win32 path. It's assumed that the path has already been\n// scanned for internal NUL and / characters.\nvoid AppendPath(std::string& Base, std::string_view Name)\n{\n    // No need for a delimiter if the base path is empty or already ends in one.\n    if (!Base.empty() && Base.back() != '/')\n    {\n        Base += '/';\n    }\n\n    Base += Name;\n}\n\n// Converts 9P2000.L open flags to Linux open flags.\n// N.B. 9P2000.L and Linux flag values may be identical on some platforms, but not all.\nint OpenFlagsToLinuxFlags(OpenFlags flags)\n{\n    // Since OpenFlags::ReadOnly is zero, it's omitted from the mapping array. This is safe as long as O_RDONLY is also\n    // zero. If it's not, it would have to be handled separately.\n    static_assert(O_RDONLY == 0);\n\n    int result = 0;\n    for (const auto& flag : c_openFlagsMapping)\n    {\n        if (WI_AreAllFlagsSet(flags, flag.P9Flag))\n        {\n            WI_SetAllFlags(result, flag.LinuxFlag);\n        }\n    }\n\n    return result;\n}\n\n// Get the stat information of this file.\n// N.B. The caller is responsible for setting the right thread uid/gid before calling this.\nExpected<struct stat> File::Stat()\n{\n    struct stat st;\n    // Acquire the lock to prevent the file name from changing.\n    std::shared_lock<std::shared_mutex> lock{m_Lock};\n    int result = fstatat(m_Root->RootFd, m_FileName.c_str(), &st, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH);\n    if (result < 0)\n    {\n        return LxError{-errno};\n    }\n\n    return st;\n}\n\nbool File::IsOnRoot(const std::shared_ptr<const IRoot>& root)\n{\n    return m_Root == root;\n}\n\n// Opens the file.\n// N.B. The caller is responsible for setting the right thread uid/gid before calling this.\nExpected<wil::unique_fd> File::OpenFile(int openFlags)\n{\n    // Acquire the lock to prevent the file name from changing.\n    std::shared_lock<std::shared_mutex> lock{m_Lock};\n    return util::OpenAt(m_Root->RootFd, m_FileName, openFlags | O_NOFOLLOW);\n}\n\n// Validates that this file exists and sets the m_Qid member.\n// N.B. The caller is responsible for setting the right thread uid/gid before calling this.\nLX_INT File::ValidateExists()\n{\n    auto st = Stat();\n    if (!st)\n    {\n        return st.Error();\n    }\n\n    m_Qid = StatToQid(st.Get());\n    m_Device = st->st_dev;\n    return {};\n}\n\n// Initializes a file to a Win32 path.\nExpected<Qid> File::Initialize()\n{\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    LX_INT error = ValidateExists();\n    if (error < 0)\n    {\n        return LxError{error};\n    }\n\n    // No locking needed because initialize is only called on fids not yet\n    // reachable by other threads.\n    return m_Qid;\n}\n\nFile::File(std::shared_ptr<const Root> root) : m_Root{root}\n{\n}\n\n// Copies a file. This does not clone the open file state, just the name and qid.\nFile::File(const File& file) : m_FileName{file.m_FileName}, m_Root{file.m_Root}, m_Qid{file.m_Qid}\n{\n}\n\n// Updates the path to a child file entry in a directory. Must be called with a newly\n// constructed file, not one that has been opened.\nExpected<Qid> File::Walk(std::string_view name)\n{\n    // TODO: This is not safe if walk is called multiple times. While\n    // we verify that the item is not a symlink in this step, the file could've\n    // been replaced with a symlink since the qid was determined.\n    // The only way to make this foolproof is to open an fd for every file, and\n    // use fstatat for the next level. A chroot environment can be used to\n    // prevent the links from escaping the share root, but it can't avoid\n    // accidentally following links at all.\n    if (!WI_IsFlagSet(m_Qid.Type, QidType::Directory))\n    {\n        return LxError{LX_ENOTDIR};\n    }\n\n    // No lock is taken here; this function is only called on fid's that have\n    // not yet been inserted in the list and are therefore not reachable from\n    // other threads.\n    AppendPath(m_FileName, name);\n\n    // Revert to the old info on error.\n    const auto oldQid = m_Qid;\n    const auto oldDevice = m_Device;\n    auto restoreName = wil::scope_exit([&]() {\n        m_Qid = oldQid;\n        m_Device = oldDevice;\n        const auto index = m_FileName.find_last_of('/');\n        if (index == std::string::npos)\n        {\n            m_FileName.resize(0);\n        }\n        else\n        {\n            m_FileName.resize(index);\n        }\n    });\n\n    // TODO: Maybe handle multiple items in a single walk call so changing ids is done only once.\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    const auto parentDevice = m_Device;\n    LX_INT err = ValidateExists();\n    if (err != 0)\n    {\n        return LxError{err};\n    }\n\n    // Check if this is a mount point, and if so if it's a drvfs or 9p mount.\n    if (parentDevice != m_Device)\n    {\n        try\n        {\n            // Because this thread might not be in the same mount namespace than the rest of the process,\n            // look at /proc/<tid>/mountinfo instead of /proc/self/\n            const std::string mountInfoPath = std::format(\"/proc/{}/mountinfo\", gettid());\n            mountutil::MountEnum mountEnum(mountInfoPath.c_str());\n            bool found = mountEnum.FindMount([this](auto entry) { return entry.Device == m_Device; });\n\n            // If the mount was found and it's a drvfs mount, deny access.\n            if (found && (mountEnum.Current().FileSystemType == c_drvfsFsType || mountEnum.Current().FileSystemType == c_p9FsType ||\n                          mountEnum.Current().FileSystemType == c_virtioFsType))\n            {\n                return LxError{LX_EACCES};\n            }\n        }\n        CATCH_LOG()\n    }\n\n    restoreName.release();\n    return m_Qid;\n}\n\n// Reads the attributes of a file or directory.\nExpected<std::tuple<UINT64, Qid, StatResult>> File::GetAttr(UINT64 mask)\n{\n    std::string fileName;\n    Qid qid;\n    {\n        // Retrieve the qid and open a handle under lock.\n        std::shared_lock<std::shared_mutex> lock{m_Lock};\n        qid = m_Qid;\n        fileName = m_FileName;\n    }\n\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    struct stat stat;\n    int error = fstatat(m_Root->RootFd, fileName.c_str(), &stat, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH);\n    if (error < 0)\n    {\n        return LxError{-errno};\n    }\n\n    StatResult result{};\n    UINT64 valid = GetAttrIno;\n    if (WI_IsFlagSet(mask, GetAttrMode))\n    {\n        result.Mode = stat.st_mode;\n        WI_SetFlag(valid, GetAttrMode);\n    }\n\n    if (WI_IsFlagSet(mask, GetAttrNlink))\n    {\n        result.NLink = stat.st_nlink;\n        WI_SetFlag(valid, GetAttrNlink);\n    }\n\n    if (WI_IsFlagSet(mask, GetAttrRdev))\n    {\n        result.RDev = stat.st_rdev;\n        WI_SetFlag(valid, GetAttrRdev);\n    }\n\n    if (WI_IsFlagSet(mask, GetAttrSize))\n    {\n        result.Size = stat.st_size;\n        WI_SetFlag(valid, GetAttrSize);\n    }\n\n    if (WI_IsFlagSet(mask, GetAttrBlocks))\n    {\n        result.BlockSize = stat.st_blksize;\n        result.Blocks = stat.st_blocks;\n        WI_SetFlag(valid, GetAttrBlocks);\n    }\n\n    if (WI_IsFlagSet(mask, GetAttrAtime))\n    {\n        result.AtimeSec = stat.st_atim.tv_sec;\n        result.AtimeNsec = stat.st_atim.tv_nsec;\n        WI_SetFlag(valid, GetAttrAtime);\n    }\n\n    if (WI_IsFlagSet(mask, GetAttrMtime))\n    {\n        result.MtimeSec = stat.st_mtim.tv_sec;\n        result.MtimeNsec = stat.st_mtim.tv_nsec;\n        WI_SetFlag(valid, GetAttrMtime);\n    }\n\n    if (WI_IsFlagSet(mask, GetAttrCtime))\n    {\n        result.CtimeSec = stat.st_ctim.tv_sec;\n        result.CtimeNsec = stat.st_ctim.tv_nsec;\n        WI_SetFlag(valid, GetAttrCtime);\n    }\n\n    if (WI_IsFlagSet(mask, GetAttrUid))\n    {\n        result.Uid = stat.st_uid;\n        WI_SetFlag(valid, GetAttrUid);\n    }\n\n    if (WI_IsFlagSet(mask, GetAttrGid))\n    {\n        result.Gid = stat.st_gid;\n        WI_SetFlag(valid, GetAttrGid);\n    }\n\n    return std::make_tuple(valid, qid, result);\n}\n\n// Sets the attributes for a file or directory.\nLX_INT File::SetAttr(UINT32 valid, const StatResult& stat)\n{\n    if (m_Root->ReadOnly())\n    {\n        return LX_EROFS;\n    }\n\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n\n    // Multiple operations may be performed, so it would be preferable to open the file. However,\n    // most operations don't support O_PATH and any other flags will check for permissions that the\n    // operation may not need.\n    const auto fileName = GetFileName();\n\n    // Ctime is updated by most of the operations below, so don't explicitly\n    // update it if not needed.\n    bool needCTimeUpdate = WI_IsFlagSet(valid, SetAttrCtime);\n\n    if (WI_IsFlagSet(valid, SetAttrSize))\n    {\n        // Open the file to truncate because truncate will always follow symlinks and there is no\n        // ftruncateat.\n        auto file = OpenFile(O_WRONLY);\n        if (!file)\n        {\n            return file.Error();\n        }\n\n        int error = ftruncate(file->get(), stat.Size);\n        if (error < 0)\n        {\n            return -errno;\n        }\n\n        needCTimeUpdate = false;\n    }\n\n    if (WI_IsFlagSet(valid, SetAttrMode))\n    {\n        int error = fchmodat(m_Root->RootFd, fileName.c_str(), stat.Mode, AT_SYMLINK_NOFOLLOW);\n        if (error < 0)\n        {\n            return -errno;\n        }\n\n        needCTimeUpdate = false;\n    }\n\n    if (WI_IsAnyFlagSet(valid, SetAttrUid | SetAttrGid))\n    {\n        uid_t uid = WI_IsFlagSet(valid, SetAttrUid) ? stat.Uid : -1;\n        uid_t gid = WI_IsFlagSet(valid, SetAttrGid) ? stat.Gid : -1;\n        int error = fchownat(m_Root->RootFd, fileName.c_str(), uid, gid, AT_SYMLINK_NOFOLLOW);\n        if (error < 0)\n        {\n            return -errno;\n        }\n\n        needCTimeUpdate = false;\n    }\n\n    if (WI_IsAnyFlagSet(valid, SetAttrAtime | SetAttrMtime))\n    {\n        struct timespec times[2]{{0, UTIME_OMIT}, {0, UTIME_OMIT}};\n\n        //\n        // For atime and mtime, the time is set to the current time unless the\n        // respective \"set\" flag is set.\n        //\n\n        if (WI_IsFlagSet(valid, SetAttrAtime))\n        {\n            if (WI_IsFlagSet(valid, SetAttrAtimeSet))\n            {\n                times[0].tv_sec = stat.AtimeSec;\n                times[0].tv_nsec = stat.AtimeNsec;\n            }\n            else\n            {\n                times[0].tv_nsec = UTIME_NOW;\n            }\n        }\n\n        if (WI_IsFlagSet(valid, SetAttrMtime))\n        {\n            if (WI_IsFlagSet(valid, SetAttrMtimeSet))\n            {\n                times[1].tv_sec = stat.MtimeSec;\n                times[1].tv_nsec = stat.MtimeNsec;\n            }\n            else\n            {\n                times[1].tv_nsec = UTIME_NOW;\n            }\n        }\n\n        int error = utimensat(m_Root->RootFd, fileName.c_str(), times, AT_SYMLINK_NOFOLLOW);\n        if (error < 0)\n        {\n            return -errno;\n        }\n\n        needCTimeUpdate = false;\n    }\n\n    // If a ctime update was requested but didn't already happen, perform a no-op\n    // operation that has a ctime update as a side-effect.\n    if (needCTimeUpdate)\n    {\n        int error = fchownat(m_Root->RootFd, fileName.c_str(), -1, -1, AT_SYMLINK_NOFOLLOW);\n        if (error < 0)\n        {\n            return -errno;\n        }\n    }\n\n    return {};\n}\n\n// Opens a file or directory for read/write access.\nExpected<Qid> File::Open(OpenFlags flags)\n{\n    // Acquire the lock to protect the file name and to guard against\n    // concurrent open attempts.\n    std::lock_guard<std::shared_mutex> lock{m_Lock};\n    if (IsOpen())\n    {\n        return LxError{LX_EINVAL};\n    }\n\n    WI_ClearFlag(flags, OpenFlags::Create);\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    // Don't use OpenHandle because the lock is already held.\n    auto file{util::OpenAt(m_Root->RootFd, m_FileName, OpenFlagsToLinuxFlags(flags) | O_NOFOLLOW)};\n    if (!file)\n    {\n        return file.Unexpected();\n    }\n\n    m_Io = CoroutineIoIssuer(file->get());\n    m_File = std::move(file.Get());\n    return m_Qid;\n}\n\n// Creates a file in a directory, updating this object to point to the new file.\nExpected<Qid> File::Create(std::string_view name, OpenFlags flags, UINT32 mode, UINT32 /* gid */)\n{\n    // Acquire the lock exclusive because the file name will be modified,\n    // and to protect against concurrent opens and creates.\n    std::lock_guard<std::shared_mutex> lock{m_Lock};\n    if (IsOpen())\n    {\n        return LxError{LX_EINVAL};\n    }\n\n    if (m_Root->ReadOnly())\n    {\n        return LxError{LX_EROFS};\n    }\n\n    // The specified gid is currently ignored. Supporting it would be possible, but it would be\n    // necessary to make sure that the user is a member of the specified group.\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    auto newFileName = ChildPathWithLockHeld(name);\n    auto file{util::OpenAt(m_Root->RootFd, newFileName, OpenFlagsToLinuxFlags(flags) | O_CREAT | O_NOFOLLOW, mode)};\n    if (!file)\n    {\n        return file.Unexpected();\n    }\n\n    struct stat st;\n    int result = fstat(file->get(), &st);\n    if (result < 0)\n    {\n        return LxError{-errno};\n    }\n\n    m_FileName = std::move(newFileName);\n    m_Io = CoroutineIoIssuer(file->get());\n    m_File = std::move(file.Get());\n    m_Qid = StatToQid(st);\n    m_Device = st.st_dev;\n    return m_Qid;\n}\n\n// Creates a subdirectory.\nExpected<Qid> File::MkDir(std::string_view name, UINT32 mode, UINT32 /* gid */)\n{\n    const auto newFileName = ChildPath(name);\n\n    // The specified gid is currently ignored. Supporting it would be possible, but it would be\n    // necessary to make sure that the user is a member of the specified group.\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    int result = mkdirat(m_Root->RootFd, newFileName.c_str(), mode);\n    if (result < 0)\n    {\n        return LxError{-errno};\n    }\n\n    return GetFileQidByPath(m_Root->RootFd, newFileName);\n}\n\n// Reads the contents of a directory, starting at the specified offset.\nLX_INT File::ReadDir(UINT64 offset, SpanWriter& writer, bool includeAttributes)\n{\n    if (!IsOpen())\n    {\n        return LX_EBADF;\n    }\n\n    // Acquire an exclusive lock to protect enumerator state.\n    std::lock_guard<std::shared_mutex> lock{m_Lock};\n    if (!m_Enumerator)\n    {\n        m_Enumerator.reset(new DirectoryEnumerator(m_File.get()));\n        // The fd is now owned by the enumerator.\n        m_File.release();\n    }\n\n    m_Enumerator->Seek(offset);\n\n    bool dirEntriesWritten = false;\n    for (;;)\n    {\n        auto entry = m_Enumerator->Next();\n        if (entry == nullptr)\n        {\n            break;\n        }\n\n        StatResult attributes;\n        StatResult* attributesToUse = nullptr;\n        if (includeAttributes)\n        {\n            attributesToUse = &attributes;\n            struct stat st;\n            const char* name = entry->d_name;\n\n            // Return attributes of the directory for both . and ..\n            if (strcmp(name, \".\") == 0 || strcmp(name, \"..\") == 0)\n            {\n                name = \"\";\n            }\n\n            int result = fstatat(m_Enumerator->Fd(), name, &st, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH);\n            if (result < 0)\n            {\n                // Fill out basic attributes if real attributes can't be determined.\n                attributes = {};\n                attributes.Mode = util::DirEntryTypeToMode(entry->d_type);\n                attributes.NLink = 1;\n            }\n            else\n            {\n                attributes.Mode = st.st_mode;\n                attributes.Uid = st.st_uid;\n                attributes.Gid = st.st_gid;\n                attributes.NLink = st.st_nlink;\n                attributes.RDev = st.st_rdev;\n                attributes.Size = st.st_size;\n                attributes.BlockSize = st.st_blksize;\n                attributes.Blocks = st.st_blocks;\n                attributes.AtimeSec = st.st_atim.tv_sec;\n                attributes.AtimeNsec = st.st_atim.tv_nsec;\n                attributes.MtimeSec = st.st_mtim.tv_sec;\n                attributes.MtimeNsec = st.st_mtim.tv_nsec;\n                attributes.CtimeSec = st.st_ctim.tv_sec;\n                attributes.CtimeNsec = st.st_ctim.tv_nsec;\n            }\n        }\n\n        Qid qid{};\n        qid.Path = entry->d_ino;\n        qid.Type = util::DirEntryTypeToQidType(entry->d_type);\n        if (!util::SpanWriteDirectoryEntry(writer, entry->d_name, qid, entry->d_off, entry->d_type, attributesToUse))\n        {\n            if (!dirEntriesWritten)\n            {\n                return LX_EINVAL;\n            }\n\n            break;\n        }\n\n        dirEntriesWritten = true;\n    }\n\n    return {};\n}\n\n// Reads the contents of an open file.\nTask<Expected<UINT32>> File::Read(UINT64 offset, gsl::span<gsl::byte> buffer)\n{\n    // No locking needed; once open, the file will not be closed until the\n    // object is destructed, and the caller holds a reference.\n    if (!m_File)\n    {\n        co_return LxError{LX_EBADF};\n    }\n\n    CancelToken token;\n    auto result = co_await ReadAsync(m_Io, offset, buffer, token);\n    if (result.Error != 0 && result.Error != LX_EOVERFLOW)\n    {\n        co_return LxError{result.Error};\n    }\n\n    co_return static_cast<UINT32>(result.BytesTransferred);\n}\n\n// Writes to an open file.\nTask<Expected<UINT32>> File::Write(UINT64 offset, gsl::span<const gsl::byte> buffer)\n{\n    // Since the file could not have been opened for write on a read-only file\n    // system, there is no reason to check that here.\n\n    // No locking needed; once open, the file will not be closed until the\n    // object is destructed, and the caller holds a reference.\n    if (!m_File)\n    {\n        co_return LxError{LX_EBADF};\n    }\n\n    CancelToken token;\n    auto result = co_await WriteAsync(m_Io, offset, buffer, token);\n    if (result.Error != 0)\n    {\n        co_return LxError{result.Error};\n    }\n\n    co_return result.BytesTransferred;\n}\n\n// Unlinks a directory entry.\nLX_INT File::UnlinkAt(std::string_view name, UINT32 flags)\n{\n    if (m_Root->ReadOnly())\n    {\n        return LX_EROFS;\n    }\n\n    const auto fileName = ChildPath(name);\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n\n    // TODO: it's unclear whether this is the correct usage of the\n    // flags field. The Windows implementation unlinks either directory or\n    // file regardless of flags.\n    int result = unlinkat(m_Root->RootFd, fileName.c_str(), flags);\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    return {};\n}\n\n// Removes the directory entry represented by the current fid.\nLX_INT File::Remove()\n{\n    if (m_Root->ReadOnly())\n    {\n        return LX_EROFS;\n    }\n\n    int flags = 0;\n    WI_SetFlagIf(flags, AT_REMOVEDIR, WI_IsFlagSet(m_Qid.Type, QidType::Directory));\n    const std::string fileName = GetFileName();\n    if (fileName.length() == 0)\n    {\n        // Can't unlink the root.\n        return LX_EPERM;\n    }\n\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    const int result = unlinkat(m_Root->RootFd, fileName.c_str(), flags);\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    return {};\n}\n\n// Gets a copy of the file name, taking the lock to retrieve it.\nstd::string File::GetFileName() const\n{\n    std::shared_lock<std::shared_mutex> lock{m_Lock};\n    return m_FileName;\n}\n\n// Constructs a child path of the current path from a valid Linux path segment.\nstd::string File::ChildPath(std::string_view name)\n{\n    auto result = GetFileName();\n    AppendPath(result, name);\n    return result;\n}\n\nstd::string File::ChildPathWithLockHeld(std::string_view name)\n{\n    std::string result{m_FileName};\n    AppendPath(result, name);\n    return result;\n}\n\n// Renames a directory entry.\nLX_INT File::RenameAt(std::string_view oldName, Fid& newParent, std::string_view newName)\n{\n    if (!newParent.IsFile() || !newParent.IsOnRoot(m_Root))\n    {\n        return LX_EINVAL;\n    }\n\n    if (m_Root->ReadOnly())\n    {\n        return LX_EROFS;\n    }\n\n    auto newParentFile = static_cast<File&>(newParent);\n    const auto oldPath = ChildPath(oldName);\n    const auto newPath = newParentFile.ChildPath(newName);\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    int result = renameat(m_Root->RootFd, oldPath.c_str(), m_Root->RootFd, newPath.c_str());\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    return {};\n}\n\n// Renames the current directory entry.\nLX_INT File::Rename(Fid& newParent, std::string_view newName)\n{\n    if (!newParent.IsFile() || !newParent.IsOnRoot(m_Root))\n    {\n        return LX_EINVAL;\n    }\n\n    if (m_Root->ReadOnly())\n    {\n        return LX_EROFS;\n    }\n\n    auto newParentFile = static_cast<File&>(newParent);\n    // Take an exclusive lock because the file name will be changed.\n    std::lock_guard<std::shared_mutex> lock{m_Lock};\n    if (m_FileName.length() == 0)\n    {\n        // Can't rename the root.\n        return LX_EPERM;\n    }\n\n    const auto oldPath = m_FileName;\n    auto newPath = newParentFile.ChildPath(newName);\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    int result = renameat(m_Root->RootFd, oldPath.c_str(), m_Root->RootFd, newPath.c_str());\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    m_FileName = newPath;\n    return {};\n}\n\n// Creates a symbolic link in a directory.\nExpected<Qid> File::SymLink(std::string_view name, std::string_view target, UINT32 /* gid */)\n{\n    if (m_Root->ReadOnly())\n    {\n        return LxError{LX_EROFS};\n    }\n\n    // TODO: Gid is being ignored.\n    const auto linkName = ChildPath(name);\n    // Need a null-terminated string:\n    const std::string linkTarget{target.data(), target.size()};\n\n    // The specified gid is currently ignored. Supporting it would be possible, but it would be\n    // necessary to make sure that the user is a member of the specified group.\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    int result = symlinkat(linkTarget.c_str(), m_Root->RootFd, linkName.c_str());\n    if (result < 0)\n    {\n        return LxError{-errno};\n    }\n\n    return GetFileQidByPath(m_Root->RootFd, linkName);\n}\n\n// Reads the target of a symbolic link.\nExpected<UINT32> File::ReadLink(gsl::span<char> name)\n{\n    const auto fileName = GetFileName();\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    int result = readlinkat(m_Root->RootFd, fileName.c_str(), name.data(), name.size());\n    if (result < 0)\n    {\n        return LxError{-errno};\n    }\n\n    return result;\n}\n\n// Creates a hard link in a directory to another file.\nLX_INT File::Link(std::string_view newName, Fid& target)\n{\n    if (!target.IsFile() || !target.IsOnRoot(m_Root))\n    {\n        return LX_EINVAL;\n    }\n\n    if (m_Root->ReadOnly())\n    {\n        return LX_EROFS;\n    }\n\n    const auto targetFile = static_cast<File&>(target);\n\n    // Construct the new name relative to the share root.\n    const auto newLinkName = ChildPath(newName);\n    const auto targetName = targetFile.GetFileName();\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    int result = linkat(m_Root->RootFd, targetName.c_str(), m_Root->RootFd, newLinkName.c_str(), 0);\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    return {};\n}\n\n// Creates a device object in a directory.\nExpected<Qid> File::MkNod(std::string_view name, UINT32 mode, UINT32 major, UINT32 minor, UINT32 gid)\n{\n    if (m_Root->ReadOnly())\n    {\n        return LxError{LX_EROFS};\n    }\n\n    const auto path = ChildPath(name);\n\n    // The specified gid is currently ignored. Supporting it would be possible, but it would be\n    // necessary to make sure that the user is a member of the specified group.\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    int result = mknodat(m_Root->RootFd, path.c_str(), mode, makedev(major, minor));\n    if (result < 0)\n    {\n        return LxError{-errno};\n    }\n\n    return GetFileQidByPath(m_Root->RootFd, path);\n}\n\n// Flushes a file's buffers.\nLX_INT File::Fsync()\n{\n    if (!m_File)\n    {\n        return LX_EINVAL;\n    }\n\n    int result = fsync(m_File.get());\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    return {};\n}\n\n// Retrieves the file system attributes.\nExpected<StatFsResult> File::StatFs()\n{\n    // Open the file because there is no statfsat.\n    auto file{OpenFile(O_PATH)};\n    if (!file)\n    {\n        return file.Unexpected();\n    }\n\n    struct statfs statFs;\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    int result = fstatfs(file->get(), &statFs);\n    if (result < 0)\n    {\n        return LxError{-errno};\n    }\n\n    StatFsResult statFsResult;\n    statFsResult.Type = static_cast<UINT32>(statFs.f_type);\n    statFsResult.BlockSize = static_cast<UINT32>(statFs.f_bsize);\n    statFsResult.Blocks = statFs.f_blocks;\n    statFsResult.BlocksFree = statFs.f_bfree;\n    statFsResult.BlocksAvailable = statFs.f_bavail;\n    statFsResult.Files = statFs.f_files;\n    statFsResult.FilesFree = statFs.f_ffree;\n    statFsResult.NameLength = static_cast<UINT32>(statFs.f_namelen);\n\n    static_assert(sizeof(statFsResult.FsId) == sizeof(statFs.f_fsid));\n    // These two fields should be the same size (asserted above).\n    memcpy(&statFsResult.FsId, &statFs.f_fsid, sizeof(statFsResult.FsId));\n    return statFsResult;\n}\n\n// Locks a range of the file.\nExpected<LockStatus> File::Lock(LockType, UINT32, UINT64, UINT64, UINT32, const std::string_view)\n{\n    // The file has to be open for lock to work.\n    if (!IsOpen())\n    {\n        return LxError{LX_EBADF};\n    }\n\n    // This implementation always returns success. The Linux kernel still\n    // provides proper file locking, and this call seems to only be used\n    // to check for server locking between multiple clients. That means a\n    // no-op implementation works for a single client.\n    //\n    // TODO: Implement server-side locks.\n    return LockStatus::Success;\n}\n\n// Gets information about the current lock on the file.\nExpected<std::tuple<LockType, UINT64, UINT64, UINT32, std::string_view>> File::GetLock(\n    LockType, UINT64 Start, UINT64 Length, UINT32 ProcId, const std::string_view ClientId)\n{\n    // The file has to be open for getlock to work.\n    if (!IsOpen())\n    {\n        return LxError{LX_EBADF};\n    }\n\n    // This implementation always returns unlocked, and echoes the rest of\n    // the values back to the client. The Linux kernel still provides\n    // proper file locking, and returns the correct information even if the\n    // server says unlocked. That means a no-op implementation works for a\n    // single client.\n    //\n    // TODO: Implement server-side locks.\n    return std::make_tuple(LockType::Unlock, Start, Length, ProcId, ClientId);\n}\n\n// Created a new Fid representing an extended attribute.\nExpected<std::shared_ptr<XAttrBase>> File::XattrWalk(const std::string& name)\n{\n    // N.B. There is no *xattrat or equivalent, so f*xattr must be used\n    // to avoid constructing the full file name. However, f*xattr doesn't work\n    // on file descriptors opened with O_PATH, so they can't be used on symlinks,\n    // even though the various l*xattr functions do allow manipulating xattrs\n    // on symlinks. This means there's no way to support xattrs on symlinks\n    // without using the full file name, which is less than ideal.\n    // TODO: Use a chroot environment to make this safer.\n    auto path = util::GetFdPath(m_Root->RootFd);\n    AppendPath(path, GetFileName());\n    std::shared_ptr<XAttrBase> xattr = std::make_shared<XAttr>(m_Root, path, name, XAttr::Access::Read);\n    return xattr;\n}\n\nExpected<std::shared_ptr<XAttrBase>> File::XattrCreate(const std::string& name, UINT64 size, UINT32 flags)\n{\n    if (m_Root->ReadOnly())\n    {\n        return LxError{LX_EROFS};\n    }\n\n    // Since the caller will end up replacing the original fid with the one\n    // returned, make sure this wasn't an open fid.\n    if (IsOpen())\n    {\n        return LxError{LX_EINVAL};\n    }\n\n    // See above for the reason for doing this.\n    auto path = util::GetFdPath(m_Root->RootFd);\n    AppendPath(path, GetFileName());\n    std::shared_ptr<XAttrBase> xattr = std::make_shared<XAttr>(m_Root, path, name, XAttr::Access::Write, size, flags);\n    return xattr;\n}\n\nLX_INT File::Access(AccessFlags flags)\n{\n    AccessFlags flagsWithoutDelete = flags;\n    WI_ClearFlag(flagsWithoutDelete, AccessFlags::Delete);\n    const auto name = GetFileName();\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    LX_INT result = util::AccessHelper(m_Root->RootFd, name, static_cast<int>(flagsWithoutDelete));\n    if (result < 0)\n    {\n        return result;\n    }\n\n    // No delete check requested? Done!\n    if (!WI_IsFlagSet(flags, AccessFlags::Delete))\n    {\n        return {};\n    }\n\n    if (name.length() == 0)\n    {\n        // Can't delete the root.\n        return LX_EACCES;\n    }\n\n    std::string parentPath;\n    const int index = name.find_last_of('/');\n    if (index != std::string::npos)\n    {\n        parentPath = name.substr(0, index);\n    }\n\n    // Check for write access to the parent.\n    result = util::AccessHelper(m_Root->RootFd, parentPath, W_OK);\n    if (result < 0)\n    {\n        return result;\n    }\n\n    // Get the parent's attributes.\n    struct stat st;\n    result = fstatat(m_Root->RootFd, parentPath.c_str(), &st, AT_EMPTY_PATH);\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    // No sticky bit? Done!\n    if (!WI_IsFlagSet(st.st_mode, S_ISVTX))\n    {\n        return {};\n    }\n\n    // Check if this process has CAP_FOWNER, which means it can bypass the\n    // sticky bit.\n    result = util::CheckFOwnerCapability();\n    if (result == 0)\n    {\n        return {};\n    }\n    else if (result != LX_EPERM)\n    {\n        return result;\n    }\n\n    // Check for ownership of the parent directory.\n    uid_t uid = geteuid();\n    if (uid == st.st_uid)\n    {\n        return {};\n    }\n\n    // Check for ownership of the child.\n    result = fstatat(m_Root->RootFd, name.c_str(), &st, AT_EMPTY_PATH);\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    if (uid == st.st_uid)\n    {\n        return {};\n    }\n\n    // Stick bit checks failed.\n    return LX_EACCES;\n}\n\nstd::shared_ptr<Fid> File::Clone() const\n{\n    // Requires the lock to protect the file name.\n    std::shared_lock<std::shared_mutex> lock{m_Lock};\n    return std::make_shared<File>(*this);\n}\n\nbool File::IsOpen() const\n{\n    return bool{m_File} || m_Enumerator;\n}\n\nbool File::IsFile() const\n{\n    return true;\n}\n\nQid File::GetQid() const\n{\n    return m_Qid;\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9file.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9io.h\"\n#include \"p9fid.h\"\n#include \"p9readdir.h\"\n#include <pwd.h>\n#include <grp.h>\n\nnamespace p9fs {\n\nstruct Share\n{\n    wil::unique_fd RootFd;\n};\n\nstruct Root final : public IRoot\n{\n    Root(std::shared_ptr<const Share> share, int rootFd, uid_t uid, gid_t gid) : Share{share}, RootFd{rootFd}, Uid{uid}, Gid{gid}\n    {\n        Plan9TraceLoggingProvider::LogMessage(std::format(\"Instantiate root, uid={}\", uid));\n        if (uid == -1)\n        {\n            return; // No uid passed, don't try to get the additional groups.\n        }\n\n        auto bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);\n        if (bufsize == -1)\n        {\n            bufsize = 16384; // Recommended by the man page if _SC_GETPW_R_SIZE_MAX is not set.\n        }\n\n        std::vector<char> buffer(bufsize);\n        passwd pwd{};\n        passwd* result = nullptr;\n        if (getpwuid_r(uid, &pwd, buffer.data(), buffer.size(), &result) < 0 || result == nullptr)\n        {\n            Plan9TraceLoggingProvider::LogMessage(std::format(\"getpwuid_r failed for uid: {}, errno={}\", uid, errno));\n            return;\n        }\n\n        // Find the number of groups\n        int groupCount = 0;\n        getgrouplist(pwd.pw_name, gid, nullptr, &groupCount);\n        Groups.resize(groupCount);\n\n        // Query the groups\n        if (getgrouplist(pwd.pw_name, gid, Groups.data(), &groupCount) < 0)\n        {\n            Plan9TraceLoggingProvider::LogMessage(std::format(\"getgrouplist failed for user: {}, errno={}\", pwd.pw_name, errno));\n            Groups.clear();\n        }\n    }\n\n    std::shared_ptr<const Share> Share;\n\n    int RootFd;\n\n    // The uid that the client attached with, and the associated primary gid.\n    // If these are -1, then no change is necessary.\n    uid_t Uid;\n    gid_t Gid;\n    std::vector<gid_t> Groups;\n\n    bool ReadOnly() const\n    {\n        return false;\n    }\n};\n\nclass File final : public Fid\n{\npublic:\n    File(std::shared_ptr<const Root> root);\n    File(const File&);\n\n    Expected<Qid> Initialize();\n    Expected<Qid> Walk(std::string_view Name) override;\n    Expected<std::tuple<UINT64, Qid, StatResult>> GetAttr(UINT64 Mask) override;\n    LX_INT SetAttr(UINT32 Valid, const StatResult& Stat) override;\n    Expected<Qid> Open(OpenFlags Flags) override;\n    Expected<Qid> Create(std::string_view Name, OpenFlags /* Flags */, UINT32 /* Mode */, UINT32 /* Gid */) override;\n    Expected<Qid> MkDir(std::string_view Name, UINT32 /* Mode */, UINT32 /* Gid */) override;\n    LX_INT ReadDir(UINT64 Offset, SpanWriter& writer, bool includeAttributes) override;\n    Task<Expected<UINT32>> Read(UINT64 Offset, gsl::span<gsl::byte> Buffer) override;\n    Task<Expected<UINT32>> Write(UINT64 Offset, gsl::span<const gsl::byte> Buffer) override;\n    LX_INT UnlinkAt(std::string_view Name, UINT32 /* Flags */) override;\n    LX_INT Remove() override;\n    LX_INT RenameAt(std::string_view OldName, Fid& NewParent, std::string_view NewName) override;\n    LX_INT Rename(Fid& NewParent, std::string_view NewName) override;\n    Expected<Qid> SymLink(std::string_view /* name */, std::string_view /* target */, UINT32 /* gid */) override;\n    Expected<Qid> MkNod(std::string_view /* name */, UINT32 /* mode */, UINT32 /* major */, UINT32 /* minor */, UINT32 /* gid */) override;\n    LX_INT Link(std::string_view /* name */, Fid& /* target */) override;\n    Expected<UINT32> ReadLink(gsl::span<char> /* name */) override;\n    LX_INT Fsync() override;\n    Expected<StatFsResult> StatFs() override;\n    Expected<LockStatus> Lock(LockType Type, UINT32 Flags, UINT64 Start, UINT64 Length, UINT32 ProcId, std::string_view ClientId) override;\n    Expected<std::tuple<LockType, UINT64, UINT64, UINT32, std::string_view>> GetLock(\n        LockType Type, UINT64 Start, UINT64 Length, UINT32 ProcId, std::string_view ClientId) override;\n    Expected<std::shared_ptr<XAttrBase>> XattrWalk(const std::string& Name) override;\n    Expected<std::shared_ptr<XAttrBase>> XattrCreate(const std::string& Name, UINT64 Size, UINT32 Flags) override;\n\n    // 9P2000.W operations\n    LX_INT Access(AccessFlags Flags) override;\n\n    std::shared_ptr<Fid> Clone() const override;\n    bool IsOnRoot(const std::shared_ptr<const IRoot>& root) override;\n    bool IsFile() const override;\n    Qid GetQid() const override;\n    bool IsOpen() const;\n\nprivate:\n    Expected<wil::unique_fd> OpenFile(int openFlags);\n    LX_INT ValidateExists();\n    std::string GetFileName() const;\n    std::string ChildPath(std::string_view name);\n    std::string ChildPathWithLockHeld(std::string_view name);\n    Expected<struct stat> Stat();\n    LX_INT ReadDirHelper(UINT64 offset, SpanWriter& writer, bool extendedAttributes);\n\n    // This lock protects all state except:\n    // - Read access to m_File: once non-NULL, this member never becomes NULL\n    //   again.\n    // - m_Root, m_Uid: these members don't change after initialization.\n    mutable std::shared_mutex m_Lock;\n    std::string m_FileName;\n    std::unique_ptr<DirectoryEnumerator> m_Enumerator;\n    wil::unique_fd m_File;\n    CoroutineIoIssuer m_Io;\n    const std::shared_ptr<const Root> m_Root;\n    Qid m_Qid{};\n    dev_t m_Device{};\n};\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9fs.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9errors.h\"\n#include \"p9handler.h\"\n#include \"p9file.h\"\n#include \"p9fs.h\"\n#include \"p9lx.h\"\n#include \"p9util.h\"\n#include \"p9tracelogging.h\"\n\nnamespace p9fs {\n\nconstexpr const char* c_NobodyGroupName = \"nobody\";\n\nclass ShareList final : public IShareList\n{\npublic:\n    void Add(const std::string& name, int rootFd);\n    void Remove(const std::string& name);\n    std::shared_ptr<const Share> Get(std::string_view name);\n    size_t MaximumConnectionCount() override;\n    Expected<std::shared_ptr<const IRoot>> MakeRoot(std::string_view aname, LX_UID_T uid) override;\n\nprivate:\n    std::mutex m_ShareLock;\n    std::map<std::string, std::shared_ptr<Share>, std::less<>> m_Shares;\n};\n\nvoid ShareList::Add(const std::string& name, int rootFd)\n{\n    auto share = std::make_shared<Share>();\n    share->RootFd.reset(rootFd);\n    THROW_LAST_ERROR_IF(!share->RootFd);\n\n    std::lock_guard<std::mutex> lock{m_ShareLock};\n    const bool inserted = m_Shares.try_emplace(name, std::move(share)).second;\n    if (!inserted)\n    {\n        THROW_ERRNO(EEXIST);\n    }\n}\n\nvoid ShareList::Remove(const std::string& name)\n{\n    std::lock_guard<std::mutex> lock{m_ShareLock};\n    const auto share = m_Shares.find(name);\n    if (share == m_Shares.end())\n    {\n        THROW_ERRNO(ENOENT);\n    }\n\n    m_Shares.erase(share);\n}\n\nstd::shared_ptr<const Share> ShareList::Get(std::string_view name)\n{\n    std::lock_guard<std::mutex> lock{m_ShareLock};\n    auto it = m_Shares.find(name);\n    if (it != m_Shares.end())\n    {\n        return it->second;\n    }\n\n    return {};\n}\n\n// Returns the maximum number of concurrent connections that should be allowed\n// based on the number and configuration of the shares.\nsize_t ShareList::MaximumConnectionCount()\n{\n    return 4096;\n}\n\nExpected<std::shared_ptr<const IRoot>> ShareList::MakeRoot(std::string_view aname, LX_UID_T uid)\n{\n    auto share = Get(aname);\n    if (!share)\n    {\n        return LxError{LX_ENOENT};\n    }\n\n    gid_t gid;\n    uid_t currentUid = geteuid();\n    if (uid == currentUid)\n    {\n        // No need to change IDs if the requested user matches the user the server is running as.\n        uid = util::c_InvalidUid;\n        gid = util::c_InvalidGid;\n    }\n    else if (currentUid == 0)\n    {\n        gid = util::GetUserGroupId(uid);\n        if (gid == util::c_InvalidGid)\n        {\n            // The user wasn't found in /etc/passwd, so use \"nobody\" as the group.\n            gid = util::GetGroupIdByName(c_NobodyGroupName);\n            if (gid == util::c_InvalidGid)\n            {\n                // No group named nobody, so fail the connection.\n                return LxError{LX_EINVAL};\n            }\n        }\n    }\n    else\n    {\n        // The server is not running as root, which won't work.\n        // N.B. It's possible to make this work as long as the server has CAP_SETUID, but that\n        //      is currently not needed.\n        return LxError{LX_EPERM};\n    }\n\n    std::shared_ptr<const IRoot> root = std::make_shared<const Root>(share, share->RootFd.get(), uid, gid);\n    return root;\n}\n\nclass FileSystem final : public IPlan9FileSystem\n{\npublic:\n    // Creates a new file system, using the specified socket to listen.\n    // N.B. The socket must already be bound to an appropriate local address.\n    // N.B. The file system class takes ownership of the socket.\n    FileSystem(int socket)\n    {\n        if (!g_Watcher)\n        {\n            g_Watcher.Run();\n        }\n\n        m_Server.Reset(socket);\n        THROW_LAST_ERROR_IF(listen(socket, 1) < 0);\n    }\n\n    // Destructs a file system instance.\n    ~FileSystem() noexcept override\n    try\n    {\n        // Make sure the task finishes.\n        Pause();\n    }\n    CATCH_LOG()\n\n    // Add a share to the file system.\n    // N.B. The root FD is duplicated so this function does not take ownership of it.\n    void AddShare(const std::string& name, int rootFd) override\n    {\n        m_ShareList.Add(name, rootFd);\n    }\n\n    // Cancels any outstanding operations and stops listening for new connections.\n    void Pause() override\n    {\n        if (m_RunTask)\n        {\n            Plan9TraceLoggingProvider::ServerStop();\n            m_CancelToken.Cancel();\n            try\n            {\n                m_RunTask.Get();\n            }\n            catch (...)\n            {\n                auto error = util::LinuxErrorFromCaughtException();\n                if (error != LX_ECANCELED)\n                {\n                    LOG_CAUGHT_EXCEPTION();\n                }\n            }\n\n            m_CancelToken.Reset();\n            m_RunTask = {};\n        }\n    }\n\n    // Runs the file system.\n    void Resume() override\n    {\n        Plan9TraceLoggingProvider::ServerStart();\n        m_RunTask = Run();\n    }\n\n    // Tears down the server socket.\n    void Teardown() override\n    {\n        m_Server.Reset();\n    }\n\n    bool HasConnections() const noexcept override\n    {\n        return m_WaitGroup.HasMembers();\n    }\n\nprivate:\n    // Asynchronously handles incoming connections.\n    AsyncTask Run() noexcept\n    {\n        return HandleConnections(m_Server, m_ShareList, m_CancelToken, m_WaitGroup);\n    }\n\n    Socket m_Server;\n    AsyncTask m_RunTask;\n    CancelToken m_CancelToken;\n    WaitGroup m_WaitGroup;\n    ShareList m_ShareList;\n};\n\nstd::unique_ptr<IPlan9FileSystem> CreateFileSystem(int socket)\n{\n    return std::make_unique<FileSystem>(socket);\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9fs.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nnamespace p9fs {\n\n// Interface for running the Plan 9 server.\n// N.B. The main reason this is an interface, despite not needing COM like the Windows equivalent,\n//      is so consumers can just include this header rather than needing most of the library's\n//      headers and needing to compile with coroutine support, which would be required to directly\n//      use the implementation of this interface.\nclass IPlan9FileSystem\n{\npublic:\n    virtual ~IPlan9FileSystem() noexcept = default;\n\n    virtual void AddShare(const std::string& name, int rootFd) = 0;\n    virtual void Pause() = 0;\n    virtual void Resume() = 0;\n    virtual void Teardown() = 0;\n    virtual bool HasConnections() const noexcept = 0;\n};\n\nstd::unique_ptr<IPlan9FileSystem> CreateFileSystem(int socket);\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9handler.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9protohelpers.h\"\n#include \"p9errors.h\"\n#include \"p9defs.h\"\n#include \"p9tracelogging.h\"\n#include \"p9tracelogginghelper.h\"\n#include \"p9log.h\"\n#include \"p9data.h\"\n#include \"p9await.h\"\n#include \"p9fid.h\"\n#include \"p9handler.h\"\n#include \"p9commonutil.h\"\n\nnamespace p9fs {\n\n// Size to use for the stack-allocated response buffer.\nconstexpr UINT32 c_staticBufferSize = 256;\n\nconstexpr UINT32 c_createRetryCount = 3;\n\n// Handler for 9pfs protocol messages.\nclass Handler final : public IHandler\n{\npublic:\n    Handler(ISocket& s, IShareList& shareList) noexcept :\n        m_Socket{&s}, m_Requests{std::make_shared<RequestList>()}, m_ShareList{shareList}\n    {\n    }\n\n    Handler(IShareList& shareList, bool allowRenegotiate) noexcept :\n        m_Requests{std::make_shared<RequestList>()}, m_AllowRenegotiate{allowRenegotiate}, m_ShareList{shareList}\n    {\n    }\n\nprivate:\n    // Encapsulates the buffer and SpanWriter used for sending a response to the client.\n    class MessageResponse final\n    {\n    public:\n        // Initializes a new MessageResponse with the specified buffer.\n        MessageResponse(gsl::span<gsl::byte> initialBuffer, bool allowResize = true) :\n            Writer{initialBuffer}, m_allowResize{allowResize}\n        {\n            // Skip the header, which will be written last.\n            Writer.Next(HeaderSize);\n        }\n\n        // Checks if the current buffer is large enough for the message, taking any additional\n        // dynamic values into account. If not, a new buffer is allocated and used to write the\n        // response.\n        void EnsureSize(MessageType message, UINT32 extraSize, UINT32 maxSize)\n        {\n            // Ensure this function is called for a response message (which are always odd).\n            WI_ASSERT(static_cast<int>(message) % 2 == 1);\n\n            auto size = static_cast<UINT64>(GetMessageSize(message)) + extraSize;\n\n            // Check if the message is larger than the negotiated size. This could happen if the\n            // client is sending invalid requests.\n            if (size > maxSize)\n            {\n                THROW_INVALID();\n            }\n\n            // If the message is larger than the stack buffer, allocate a dynamic buffer and\n            // update the writer.\n            // N.B. This is not allowed if the initial buffer was based on a virtio write span.\n            if (size > Writer.MaxSize())\n            {\n                if (!m_allowResize)\n                {\n                    Plan9TraceLoggingProvider::InvalidResponseBufferSize();\n                    THROW_INVALID();\n                }\n\n                m_dynamicBuffer.resize(size);\n                Writer = SpanWriter{m_dynamicBuffer};\n\n                // Skip the header, which will be written last.\n                Writer.Next(HeaderSize);\n            }\n        }\n\n        SpanWriter Writer;\n\n    private:\n        MessageResponse(const MessageResponse&) = delete;\n        MessageResponse& operator=(const MessageResponse&) = delete;\n\n        std::vector<gsl::byte> m_dynamicBuffer;\n        bool m_allowResize;\n    };\n\n    // Encapsulates information about a request in progress, which is used by Tflush to wait\n    // for completion before responding.\n    struct RequestInfo\n    {\n        RequestInfo(UINT16 tag) : Tag{tag}\n        {\n        }\n\n        // Since this is part of a linked list, make sure it's never moved or copied.\n        RequestInfo(const RequestInfo&) = delete;\n        RequestInfo& operator=(const RequestInfo&) = delete;\n        RequestInfo(RequestInfo&&) = delete;\n        RequestInfo& operator=(RequestInfo&&) = delete;\n\n        LIST_ENTRY Link;\n        AsyncEvent Event;\n        UINT16 Tag;\n        bool Cancelled{};\n    };\n\n    struct RequestList\n    {\n        std::mutex Lock;\n        util::LinkedList<RequestInfo> Requests;\n    };\n\n    class RequestTracker\n    {\n    public:\n        RequestTracker(const std::shared_ptr<RequestList>& requests, UINT16 tag) :\n            m_requestList{requests}, m_request{std::make_unique<RequestInfo>(tag)}\n        {\n            // Insert into the list on construction.\n            std::lock_guard<std::mutex> lock{m_requestList->Lock};\n            m_requestList->Requests.Insert(*m_request);\n        }\n\n        RequestTracker(RequestTracker&& other) = default;\n\n        ~RequestTracker()\n        {\n            // The pointers can be invalid if this instance has been moved.\n            if (!m_requestList || !m_request)\n            {\n                return;\n            }\n\n            {\n                std::lock_guard<std::mutex> lock{m_requestList->Lock};\n\n                // Remove the request from the list of pending requests. This means that Cancelled\n                // can't change after the lock is dropped, since HandleFlush can't find the request\n                // anymore.\n                m_requestList->Requests.Remove(*m_request);\n            }\n\n            // If the request is marked Cancelled, it means a Tflush has taken ownership of this\n            // pointer and is waiting for the event. The pointer may become invalid as soon as the\n            // event is set.\n            // N.B. A shared_ptr would be nicer, but Tflush can only get the RequestInfo itself\n            //      from the linked list, so it wouldn't be able to access the shared_ptr's\n            //      reference count. Therefore, this ownership shuffling is required.\n            auto& localRequest = *m_request;\n            if (m_request->Cancelled)\n            {\n                m_request.release();\n            }\n\n            localRequest.Event.Set();\n        }\n\n        RequestInfo& Request() const\n        {\n            return *m_request;\n        }\n\n    private:\n        std::shared_ptr<RequestList> m_requestList;\n        std::unique_ptr<RequestInfo> m_request;\n    };\n\n    void LogMessage([[maybe_unused]] gsl::span<const gsl::byte> message)\n    {\n        TraceLogMessage(message);\n    }\n\n    Task<LX_INT> HandleMessage(MessageType messageType, SpanReader& reader, MessageResponse& response)\n    {\n        // Handle async operations.\n        switch (messageType)\n        {\n        case MessageType::Tread:\n            co_return co_await HandleRead(reader, response);\n\n        case MessageType::Twrite:\n            co_return co_await HandleWrite(reader, response);\n\n        case MessageType::Tflush:\n            co_return co_await HandleFlush(reader);\n\n        default:\n            // Default label prevents warning in clang.\n            break;\n        }\n\n        // Handle blocking operations.\n        co_return co_await BlockingCode([&]() -> LX_INT {\n            switch (messageType)\n            {\n            case MessageType::Tstatfs:\n                return HandleStatFs(reader, response);\n\n            case MessageType::Tlopen:\n                return HandleLOpen(reader, response);\n\n            case MessageType::Tlcreate:\n                return HandleLCreate(reader, response);\n\n            case MessageType::Tsymlink:\n                return HandleSymLink(reader, response);\n\n            case MessageType::Tmknod:\n                return HandleMkNod(reader, response);\n\n            case MessageType::Treadlink:\n                return HandleReadLink(reader, response);\n\n            case MessageType::Tgetattr:\n                return HandleGetAttr(reader, response);\n\n            case MessageType::Tsetattr:\n                return HandleSetAttr(reader);\n\n            case MessageType::Txattrwalk:\n                return HandleXattrWalk(reader, response);\n\n            case MessageType::Txattrcreate:\n                return HandleXattrCreate(reader);\n\n            case MessageType::Treaddir:\n            case MessageType::Twreaddir:\n                return HandleReadDir(reader, response, messageType == MessageType::Twreaddir);\n\n            case MessageType::Tfsync:\n                return HandleFsync(reader);\n\n            case MessageType::Tlock:\n                return HandleLock(reader, response);\n\n            case MessageType::Tgetlock:\n                return HandleGetLock(reader, response);\n\n            case MessageType::Tlink:\n                return HandleLink(reader);\n\n            case MessageType::Tmkdir:\n                return HandleMkDir(reader, response);\n\n            case MessageType::Trenameat:\n                return HandleRenameAt(reader);\n\n            case MessageType::Tunlinkat:\n                return HandleUnlinkAt(reader);\n\n            case MessageType::Tversion:\n                return HandleVersion(reader, response);\n\n            case MessageType::Tauth:\n                return HandleNotSupported(\"auth\");\n\n            case MessageType::Tattach:\n                return HandleAttach(reader, response);\n\n            case MessageType::Twalk:\n                return HandleWalk(reader, response);\n\n            case MessageType::Tclunk:\n                return HandleClunk(reader);\n\n            case MessageType::Tremove:\n                return HandleRemove(reader);\n\n            case MessageType::Trename:\n                return HandleRename(reader);\n\n            case MessageType::Taccess:\n                return HandleAccess(reader);\n\n            case MessageType::Twopen:\n                return HandleWOpen(reader, response);\n\n            default:\n                return LX_ENOTSUP;\n            }\n        });\n    }\n\n    LX_INT HandleNotSupported(PCSTR)\n    {\n        return LX_ENOTSUP;\n    }\n\n    LX_INT HandleVersion(SpanReader& reader, MessageResponse& response)\n    {\n        // Tversion can only be sent once, unless it's specifically allowed multiple times which\n        // is used for virtio.\n        if (m_Negotiated && !m_AllowRenegotiate)\n        {\n            return LX_ENOTSUP;\n        }\n\n        auto size = reader.U32();\n        auto version = reader.String();\n\n        if (size < MinimumRequestBufferSize)\n        {\n            return LX_ENOTSUP;\n        }\n\n        bool use9P2000W = false;\n        if (version == ProtocolVersionW)\n        {\n            use9P2000W = true;\n        }\n        else if (version != ProtocolVersionL)\n        {\n            return LX_ENOTSUP;\n        }\n\n        size = std::min(size, MaximumRequestBufferSize);\n\n        // If Tversion was allowed more than once, still require the values to match the previously\n        // negotiated values.\n        if (m_Negotiated && (use9P2000W != m_Use9P2000W || size != m_NegotiatedSize))\n        {\n            return LX_ENOTSUP;\n        }\n\n        m_Use9P2000W = use9P2000W;\n        m_NegotiatedSize = size;\n        m_Negotiated = true;\n        response.EnsureSize(MessageType::Rversion, static_cast<UINT32>(version.size()), m_NegotiatedSize);\n        response.Writer.U32(size);\n        response.Writer.String(version);\n        return {};\n    }\n\n    LX_INT HandleAttach(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        reader.U32();    // afid (unused)\n        reader.String(); // uname (unused)\n        auto aname = reader.String();\n        auto uid = reader.U32();\n\n        auto root = m_ShareList.MakeRoot(std::string_view{aname.data(), gsl::narrow_cast<size_t>(aname.size())}, uid);\n        if (!root)\n        {\n            return root.Error();\n        }\n\n        auto result = CreateFile(root.Get(), uid);\n        if (!result)\n        {\n            return result.Error();\n        }\n\n        auto [file, qid] = result.Get();\n\n        EmplaceFid(fid, file);\n\n        response.EnsureSize(MessageType::Rattach, 0, m_NegotiatedSize);\n        response.Writer.Qid(qid);\n        return {};\n    }\n\n    LX_INT HandleStatFs(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n\n        const auto file = LookupFid(fid);\n\n        auto result = file->StatFs();\n        if (!result)\n        {\n            return result.Error();\n        }\n\n        auto& statfs = result.Get();\n        response.EnsureSize(MessageType::Rstatfs, 0, m_NegotiatedSize);\n        response.Writer.U32(statfs.Type);\n        response.Writer.U32(statfs.BlockSize);\n        response.Writer.U64(statfs.Blocks);\n        response.Writer.U64(statfs.BlocksFree);\n        response.Writer.U64(statfs.BlocksAvailable);\n        response.Writer.U64(statfs.Files);\n        response.Writer.U64(statfs.FilesFree);\n        response.Writer.U64(statfs.FsId);\n        response.Writer.U32(statfs.NameLength);\n        return {};\n    }\n\n    LX_INT HandleGetAttr(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        const auto mask = reader.U64();\n        const auto file = LookupFid(fid);\n\n        auto result = file->GetAttr(mask);\n        if (!result)\n        {\n            return result.Error();\n        }\n\n        auto& [valid, qid, stat] = result.Get();\n        response.EnsureSize(MessageType::Rgetattr, 0, m_NegotiatedSize);\n        response.Writer.U64(valid);\n        response.Writer.Qid(qid);\n        util::SpanWriteStatResult(response.Writer, stat);\n        response.Writer.U64(0); // btime sec (reserved)\n        response.Writer.U64(0); // btime nsec (reserved)\n        response.Writer.U64(0); // gen (reserved)\n        response.Writer.U64(0); // data version (reserved)\n\n        return {};\n    }\n\n    LX_INT HandleWalk(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        const auto newfid = reader.U32();\n        const auto nameCount = reader.U16();\n        std::vector<std::string_view> names;\n        for (auto i = 0u; i < nameCount; ++i)\n        {\n            auto name = reader.Name();\n            names.push_back(name);\n        }\n\n        const auto entry = LookupFid(fid);\n        const auto newFile = entry->Clone();\n\n        response.EnsureSize(MessageType::Rwalk, nameCount * QidSize, m_NegotiatedSize);\n        response.Writer.U16(nameCount);\n        for (const auto& name : names)\n        {\n            auto qid = newFile->Walk(name);\n            if (!qid)\n            {\n                return qid.Error();\n            }\n\n            response.Writer.Qid(qid.Get());\n        }\n\n        EmplaceFid(newfid, newFile);\n        return {};\n    }\n\n    LX_INT HandleClunk(SpanReader& reader)\n    {\n        const auto fid = reader.U32();\n\n        std::shared_ptr<Fid> item;\n\n        {\n            std::lock_guard<std::shared_mutex> lock{m_FidsLock};\n            const auto iterator = m_Fids.find(fid);\n            if (iterator == m_Fids.end())\n            {\n                return LX_EINVAL;\n            }\n\n            item = std::move(iterator->second);\n            // Erase regardless of whether the clunk call succeeded.\n            m_Fids.erase(iterator);\n        }\n\n        return item->Clunk();\n    }\n\n    LX_INT HandleLOpen(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        auto flags = reader.U32();\n\n        const auto entry = LookupFid(fid);\n        auto qid = entry->Open(static_cast<OpenFlags>(flags));\n        if (!qid)\n        {\n            return qid.Error();\n        }\n\n        response.EnsureSize(MessageType::Rlopen, 0, m_NegotiatedSize);\n        response.Writer.Qid(qid.Get());\n        response.Writer.U32(IoUnit());\n        return {};\n    }\n\n    LX_INT HandleLCreate(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        auto name = reader.Name();\n        auto flags = reader.U32();\n        const auto mode = reader.U32();\n        const auto gid = reader.U32();\n\n        const auto file = LookupFid(fid);\n        auto qid = file->Create(name, static_cast<OpenFlags>(flags), mode, gid);\n        if (!qid)\n        {\n            return qid.Error();\n        }\n\n        response.EnsureSize(MessageType::Rlcreate, 0, m_NegotiatedSize);\n        response.Writer.Qid(qid.Get());\n        response.Writer.U32(IoUnit());\n        return {};\n    }\n\n    LX_INT HandleSymLink(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        auto name = reader.Name();\n        auto target = reader.String();\n        const auto gid = reader.U32();\n\n        const auto file = LookupFid(fid);\n        auto result = file->SymLink(name, target, gid);\n        if (!result)\n        {\n            return result.Error();\n        }\n\n        response.EnsureSize(MessageType::Rsymlink, 0, m_NegotiatedSize);\n        response.Writer.Qid(result.Get());\n        return {};\n    }\n\n    LX_INT HandleMkNod(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        auto name = reader.Name();\n        const auto mode = reader.U32();\n        const auto major = reader.U32();\n        const auto minor = reader.U32();\n        const auto gid = reader.U32();\n\n        const auto file = LookupFid(fid);\n        auto result = file->MkNod(name, mode, major, minor, gid);\n        if (!result)\n        {\n            return result.Error();\n        }\n\n        response.EnsureSize(MessageType::Rmknod, 0, m_NegotiatedSize);\n        response.Writer.Qid(result.Get());\n        return {};\n    }\n\n    LX_INT HandleReadLink(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n\n        const auto file = LookupFid(fid);\n\n        // The actual size of the symlink is unknown at this point, so allocate a buffer that is\n        // large enough for the biggest possible target.\n        response.EnsureSize(MessageType::Rreadlink, LX_PATH_MAX, m_NegotiatedSize);\n        auto buffer = response.Writer.Peek().subspan(sizeof(UINT16));\n        auto charSpan = gsl::span<char>(reinterpret_cast<char*>(buffer.data()), buffer.size());\n        auto result = file->ReadLink(charSpan);\n        if (!result)\n        {\n            return result.Error();\n        }\n\n        // Write the string length; we cannot use .String() because the string\n        // data has already been written.\n        response.Writer.U16(static_cast<UINT16>(result.Get()));\n        response.Writer.Next(result.Get());\n        return {};\n    }\n\n    LX_INT HandleReadDir(SpanReader& reader, MessageResponse& response, bool includeAttributes)\n    {\n        if (includeAttributes && !m_Use9P2000W)\n        {\n            return LX_ENOTSUP;\n        }\n\n        const auto fid = reader.U32();\n        const auto offset = reader.U64();\n        auto count = reader.U32();\n\n        const auto file = LookupFid(fid);\n\n        response.EnsureSize(MessageType::Rreaddir, count, m_NegotiatedSize);\n        SpanWriter direntWriter{response.Writer.Peek().subspan(sizeof(UINT32), count)};\n        auto error = file->ReadDir(offset, direntWriter, includeAttributes);\n        if (error != 0)\n        {\n            return error;\n        }\n\n        auto written = direntWriter.Result().size();\n        response.Writer.U32(static_cast<UINT32>(written));\n        response.Writer.Next(written);\n        return {};\n    }\n\n    LX_INT HandleFsync(SpanReader& reader)\n    {\n        const auto fid = reader.U32();\n\n        const auto file = LookupFid(fid);\n        return file->Fsync();\n    }\n\n    LX_INT HandleLink(SpanReader& reader)\n    {\n        const auto dfid = reader.U32();\n        const auto fid = reader.U32();\n        auto name = reader.Name();\n\n        auto [dir, file] = LookupFidPair(dfid, fid);\n        return dir->Link(name, *file);\n    }\n\n    Task<LX_INT> HandleRead(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        const auto offset = reader.U64();\n        const auto count = reader.U32();\n\n        const auto file = LookupFid(fid);\n        response.EnsureSize(MessageType::Rread, count, m_NegotiatedSize);\n        auto result = co_await file->Read(offset, response.Writer.Peek(sizeof(UINT32) + count).subspan(sizeof(UINT32)));\n        if (!result)\n        {\n            co_return result.Error();\n        }\n\n        response.Writer.U32(result.Get());\n        response.Writer.Next(result.Get());\n        co_return LX_INT{};\n    }\n\n    Task<LX_INT> HandleWrite(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        const auto offset = reader.U64();\n        const auto count = reader.U32();\n        auto data = reader.Read(count);\n\n        const auto file = LookupFid(fid);\n        auto result = co_await file->Write(offset, data);\n        if (!result)\n        {\n            co_return result.Error();\n        }\n\n        response.EnsureSize(MessageType::Rwrite, 0, m_NegotiatedSize);\n        response.Writer.U32(result.Get());\n        co_return LX_INT{};\n    }\n\n    LX_INT HandleUnlinkAt(SpanReader& reader)\n    {\n        const auto fid = reader.U32();\n        auto name = reader.Name();\n        const auto flags = reader.U32();\n\n        const auto file = LookupFid(fid);\n        return file->UnlinkAt(name, flags);\n    }\n\n    LX_INT HandleRemove(SpanReader& reader)\n    {\n        const auto fid = reader.U32();\n\n        const auto file = LookupFid(fid);\n        return file->Remove();\n    }\n\n    LX_INT HandleRenameAt(SpanReader& reader)\n    {\n        const auto oldfid = reader.U32();\n        auto oldname = reader.Name();\n        const auto newfid = reader.U32();\n        auto newname = reader.Name();\n\n        auto [olddir, newdir] = LookupFidPair(oldfid, newfid);\n        return olddir->RenameAt(oldname, *newdir, newname);\n    }\n\n    LX_INT HandleRename(SpanReader& reader)\n    {\n        const auto oldFid = reader.U32();\n        const auto newFid = reader.U32();\n        auto newName = reader.Name();\n\n        auto [oldFile, newDir] = LookupFidPair(oldFid, newFid);\n        return oldFile->Rename(*newDir, newName);\n    }\n\n    LX_INT HandleMkDir(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        auto name = reader.Name();\n        const auto mode = reader.U32();\n        const auto gid = reader.U32();\n\n        const auto file = LookupFid(fid);\n        auto result = file->MkDir(name, mode, gid);\n        if (!result)\n        {\n            return result.Error();\n        }\n\n        response.EnsureSize(MessageType::Rmkdir, 0, m_NegotiatedSize);\n        response.Writer.Qid(result.Get());\n        return {};\n    }\n\n    LX_INT HandleSetAttr(SpanReader& reader)\n    {\n        StatResult stat{};\n        const auto fid = reader.U32();\n        const auto valid = reader.U32();\n        stat.Mode = reader.U32();\n        stat.Uid = reader.U32();\n        stat.Gid = reader.U32();\n        stat.Size = reader.U64();\n        stat.AtimeSec = reader.U64();\n        stat.AtimeNsec = reader.U64();\n        stat.MtimeSec = reader.U64();\n        stat.MtimeNsec = reader.U64();\n\n        const auto file = LookupFid(fid);\n        return file->SetAttr(valid, stat);\n    }\n\n    LX_INT HandleLock(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        auto type = reader.U8();\n        const auto flags = reader.U32();\n        const auto start = reader.U64();\n        const auto length = reader.U64();\n        const auto procId = reader.U32();\n        auto clientId = reader.String();\n\n        const auto file = LookupFid(fid);\n        auto status = file->Lock(LockType{type}, flags, start, length, procId, clientId);\n        if (!status)\n        {\n            return status.Error();\n        }\n\n        response.EnsureSize(MessageType::Rlock, 0, m_NegotiatedSize);\n        response.Writer.U8(static_cast<UINT8>(status.Get()));\n        return {};\n    }\n\n    LX_INT HandleGetLock(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        auto type = reader.U8();\n        const auto start = reader.U64();\n        const auto length = reader.U64();\n        const auto procId = reader.U32();\n        auto clientId = reader.String();\n\n        const auto file = LookupFid(fid);\n        auto result = file->GetLock(LockType{type}, start, length, procId, clientId);\n        if (!result)\n        {\n            return result.Error();\n        }\n\n        auto [returnType, returnStart, returnLength, returnProcId, returnClientId] = result.Get();\n        response.EnsureSize(MessageType::Rgetlock, 0, m_NegotiatedSize);\n        response.Writer.U8(static_cast<UINT8>(returnType));\n        response.Writer.U64(returnStart);\n        response.Writer.U64(returnLength);\n        response.Writer.U32(returnProcId);\n        response.Writer.String(returnClientId);\n        return {};\n    }\n\n    LX_INT HandleXattrWalk(SpanReader& reader, MessageResponse& response)\n    {\n        const auto fid = reader.U32();\n        const auto newFid = reader.U32();\n        auto name = reader.String();\n\n        const auto entry = LookupFid(fid);\n        auto xattr = entry->XattrWalk(std::string{name.data(), static_cast<size_t>(name.size())});\n        if (!xattr)\n        {\n            return xattr.Error();\n        }\n\n        auto size = xattr.Get()->GetSize();\n        if (!size)\n        {\n            return size.Error();\n        }\n\n        EmplaceFid(newFid, xattr.Get());\n        response.EnsureSize(MessageType::Rxattrwalk, 0, m_NegotiatedSize);\n        response.Writer.U64(size.Get());\n        return {};\n    }\n\n    LX_INT HandleXattrCreate(SpanReader& reader)\n    {\n        const auto fid = reader.U32();\n        auto name = reader.String();\n        const auto size = reader.U64();\n        const auto flags = reader.U32();\n\n        const auto entry = LookupFid(fid);\n\n        const std::string nameString{name.data(), static_cast<size_t>(name.size())};\n        auto xattr = entry->XattrCreate(nameString, size, flags);\n        if (!xattr)\n        {\n            return xattr.Error();\n        }\n\n        // Unlike xattrwalk, xattrcreate updates the current fid, so replace\n        // it.\n        std::lock_guard<std::shared_mutex> lock{m_FidsLock};\n        const auto iterator = m_Fids.find(fid);\n        THROW_UNEXPECTED_IF((iterator == m_Fids.end()) || (iterator->second != entry));\n        iterator->second = xattr.Get();\n        return {};\n    }\n\n    LX_INT HandleAccess(SpanReader& reader)\n    {\n        if (!m_Use9P2000W)\n        {\n            return LX_ENOTSUP;\n        }\n\n        const auto fid = reader.U32();\n        auto flags = reader.U32();\n        const auto entry = LookupFid(fid);\n        return entry->Access(static_cast<AccessFlags>(flags));\n    }\n\n    // Handle the 9P2000.W Twopen message.\n    //\n    // This message combines the functionality of walk, open, create, mkdir, readlink, and getattr.\n    // Certain error conditions (a part of the path could not be found, or a component in the path\n    // was not a directory) are reported not using Rlerror, but using Rwopen with an appropriate\n    // status code. In this case, the response informs the caller how many components of the path\n    // were processed, and returns the attributes of the last successfully walked component.\n    //\n    // If a symlink is encountered in the path (including as the leaf component), its target is\n    // also returned. Whether a symlink as the leaf is treated as an error or success depends on\n    // whether OpenSymlink is specified.\n    //\n    // The return status indicates whether an existing file was opened or a new one was created.\n    // If a new file has to be created, this function creates a directory if O_DIRECTORY was\n    // specified.\n    //\n    // Only if the response indicates the status Opened or Created is the \"newfid\" argument used,\n    // and needs to be clunked. With any other status, the client can reuse that fid immediately.\n    LX_INT HandleWOpen(SpanReader& reader, MessageResponse& response)\n    {\n        if (!m_Use9P2000W)\n        {\n            return LX_ENOTSUP;\n        }\n\n        const auto fid = reader.U32();\n        const auto newFid = reader.U32();\n        auto flags = static_cast<OpenFlags>(reader.U32());\n        auto wflags = static_cast<WOpenFlags>(reader.U32());\n        const auto mode = reader.U32();\n        const auto gid = reader.U32();\n        const auto attrMask = reader.U64();\n        const auto nameCount = reader.U16();\n\n        const auto entry = LookupFid(fid);\n        const auto newFile = entry->Clone();\n\n        bool exists = false;\n        bool needOpen = true;\n        Qid entryQid = newFile->GetQid();\n        if (nameCount > 0)\n        {\n            // Step 1: Find the parent of the final item.\n            for (UINT16 i = 0; i < nameCount - 1; ++i)\n            {\n                auto qid = newFile->Walk(reader.Name());\n                if (!qid)\n                {\n                    // For ENOENT and ENOTDIR, indicate how many components were processed.\n                    switch (qid.Error())\n                    {\n                    case LX_ENOENT:\n                        return WriteWOpenReply(WOpenStatus::ParentNotFound, i, *newFile, attrMask, response);\n\n                    case LX_ENOTDIR:\n                        return WriteWOpenReply(WOpenStatus::Stopped, i, *newFile, attrMask, response);\n\n                    default:\n                        return qid.Error();\n                    }\n                }\n            }\n\n            auto name = reader.Name();\n\n            // Step 2: Find the item, unless it's an exclusive create.\n            int retries;\n            for (retries = 0; retries < c_createRetryCount; ++retries)\n            {\n                if (!WI_AreAllFlagsSet(flags, OpenFlags::Create | OpenFlags::Exclusive))\n                {\n                    auto qid = newFile->Walk(name);\n                    if (!qid)\n                    {\n                        // For ENOENT (only if not creating) and ENOTDIR, indicate how many components were processed.\n                        switch (qid.Error())\n                        {\n                        case LX_ENOENT:\n                            if (!WI_IsFlagSet(flags, OpenFlags::Create))\n                            {\n                                return WriteWOpenReply(WOpenStatus::NotFound, nameCount - 1, *newFile, attrMask, response);\n                            }\n\n                            break;\n\n                        case LX_ENOTDIR:\n                            return WriteWOpenReply(WOpenStatus::Stopped, nameCount - 1, *newFile, attrMask, response);\n\n                        default:\n                            return qid.Error();\n                        }\n                    }\n                    else\n                    {\n                        entryQid = *qid;\n                        exists = true;\n                    }\n                }\n\n                // Step 3: Create the item if it didn't exist and the user wants to create it.\n                if (!exists && WI_IsFlagSet(flags, OpenFlags::Create))\n                {\n                    Expected<Qid> qid;\n\n                    // This operation can create a directory if needed.\n                    if (WI_IsFlagSet(flags, OpenFlags::Directory))\n                    {\n                        qid = newFile->MkDir(name, mode, gid);\n                    }\n                    else\n                    {\n                        // This will already open the file.\n                        needOpen = false;\n                        qid = newFile->Create(name, flags | OpenFlags::Exclusive, mode, gid);\n                    }\n\n                    if (!qid)\n                    {\n                        // If this is a non-exclusive create, we tried to find the item above and\n                        // then tried to create it exclusively. There is the possibility of a race\n                        // if the file got created in between the two calls, so retry if that\n                        // happens.\n                        //\n                        // N.B. A non-exclusive create can't be used directly because the reply must\n                        //      indicate whether the file was created or opened.\n                        if (qid.Error() == LX_EEXIST && !WI_IsFlagSet(flags, OpenFlags::Exclusive))\n                        {\n                            needOpen = true;\n                            continue;\n                        }\n\n                        return qid.Error();\n                    }\n\n                    entryQid = *qid;\n                }\n\n                break;\n            }\n\n            // If a consistent result couldn't be reached, return an error.\n            if (retries == c_createRetryCount)\n            {\n                return LX_EIO;\n            }\n        }\n\n        // Step 4: Check the file type.\n        if (WI_IsFlagSet(wflags, WOpenFlags::NonDirectoryFile) && WI_IsFlagSet(entryQid.Type, QidType::Directory))\n        {\n            return LX_EISDIR;\n        }\n\n        // Check for O_DIRECTORY too in case the open call is skipped below.\n        if (WI_IsFlagSet(flags, OpenFlags::Directory) && !WI_IsFlagSet(entryQid.Type, QidType::Directory))\n        {\n            return LX_ENOTDIR;\n        }\n\n        // Step 5: Check for delete access.\n        if (WI_IsFlagSet(wflags, WOpenFlags::DeleteAccess))\n        {\n            auto result = newFile->Access(AccessFlags::Delete);\n            if (result < 0)\n            {\n                return result;\n            }\n        }\n\n        // Step 6: Check how to handle leaf symlinks.\n        if (WI_IsFlagSet(entryQid.Type, QidType::Symlink))\n        {\n            if (WI_IsFlagSet(wflags, WOpenFlags::OpenSymlink))\n            {\n                // No need to actually open, but do succeed.\n                needOpen = false;\n            }\n            else\n            {\n                // Return a stopped status.\n                return WriteWOpenReply(WOpenStatus::Stopped, nameCount, *newFile, attrMask, response);\n            }\n        }\n\n        // Step 7: Open if needed. This is only needed if:\n        //         - The file hasn't been opened already by a create; and\n        //         - Read/write access is requested to the file; or\n        //         - The open will have side effects (it will truncate the file).\n        const auto access = (flags & OpenFlags::AccessMask);\n        if (needOpen && (access != OpenFlags::NoAccess || WI_IsFlagSet(flags, OpenFlags::Truncate)))\n        {\n            // If the client specified O_NOACCESS, it means it doesn't want any access check done,\n            // but O_NOACCESS actually checks for read/write, so fall back on read-only.\n            // Also, directories can't be opened for write so change those to read-only too.\n            if ((access == OpenFlags::NoAccess) ||\n                (WI_IsFlagSet(entryQid.Type, QidType::Directory) && (access == OpenFlags::WriteOnly || access == OpenFlags::ReadWrite)))\n            {\n                flags = (flags & ~OpenFlags::AccessMask) | OpenFlags::ReadOnly;\n            }\n\n            // Create would've been handled above; don't do it here.\n            WI_ClearAllFlags(flags, OpenFlags::Create | OpenFlags::Exclusive);\n            auto result = newFile->Open(flags);\n            RETURN_ERROR_IF_UNEXPECTED(result);\n        }\n\n        // Step 8: Get the attributes and reply\n        const auto status = exists ? WOpenStatus::Opened : WOpenStatus::Created;\n        auto result = WriteWOpenReply(status, nameCount, *newFile, attrMask, response);\n        if (result < 0)\n        {\n            return result;\n        }\n\n        EmplaceFid(newFid, newFile);\n        return {};\n    }\n\n    // Create a Rwopen message.\n    LX_INT WriteWOpenReply(WOpenStatus status, UINT16 walked, Fid& fid, UINT64 mask, MessageResponse& response)\n    {\n        // Determine the attributes of the last entry found.\n        auto stat = fid.GetAttr(mask);\n        RETURN_ERROR_IF_UNEXPECTED(stat);\n\n        auto qid = std::get<Qid>(*stat);\n        if (WI_IsFlagSet(qid.Type, QidType::Symlink))\n        {\n            response.EnsureSize(MessageType::Rwopen, LX_PATH_MAX, m_NegotiatedSize);\n        }\n        else\n        {\n            response.EnsureSize(MessageType::Rwopen, 0, m_NegotiatedSize);\n        }\n\n        response.Writer.U8(static_cast<UINT8>(status));\n        response.Writer.U16(walked);\n        response.Writer.Qid(qid);\n\n        // If this is a symlink, get the target.\n        if (WI_IsFlagSet(qid.Type, QidType::Symlink))\n        {\n            auto buffer = response.Writer.Peek().subspan(sizeof(UINT16));\n            auto charSpan = gsl::span<char>(reinterpret_cast<char*>(buffer.data()), buffer.size());\n            auto size = fid.ReadLink(charSpan);\n            if (size)\n            {\n                response.Writer.U16(gsl::narrow_cast<UINT16>(*size));\n                response.Writer.Next(*size);\n            }\n            else\n            {\n                response.Writer.U16(0);\n            }\n        }\n        else\n        {\n            response.Writer.U16(0);\n        }\n\n        response.Writer.U32(IoUnit());\n        util::SpanWriteStatResult(response.Writer, std::get<StatResult>(*stat));\n        response.Writer.U64(0); // btime sec (reserved)\n        response.Writer.U64(0); // btime nsec (reserved)\n        response.Writer.U64(0); // gen (reserved)\n        response.Writer.U64(0); // data version (reserved)\n        return {};\n    }\n\n    // Cancel an outstanding request.\n    Task<LX_INT> HandleFlush(SpanReader& reader)\n    {\n        const auto oldTag = reader.U16();\n        std::unique_ptr<RequestInfo> waitRequest;\n\n        {\n            std::lock_guard<std::mutex> lock{m_Requests->Lock};\n\n            // Search the list for the specified request.\n            for (auto& request : m_Requests->Requests)\n            {\n                if (request.Tag == oldTag)\n                {\n                    // A client should not send more than one Tflush on the same request. If it\n                    // does, another Tflush request already has ownership and it can't be taken\n                    // away. In this case, return success immediately and the client will have to\n                    // deal with the result of its broken behavior (but at least the server didn't\n                    // crash).\n                    if (!request.Cancelled)\n                    {\n                        // Mark the request cancelled and take ownership of it, so it can be\n                        // waited on outside the lock.\n                        // N.B. See the destructor of RequestTracker for why this is done this\n                        //      way.\n                        request.Cancelled = true;\n                        waitRequest.reset(&request);\n                        break;\n                    }\n                }\n            }\n        }\n\n        // Wait until the request completes before sending the Rflush response. This is necessary\n        // because the server doesn't support true cancellation, and some messages may modify\n        // server state (e.g. Twalk), so the client must receive the response to the real request\n        // before it receives the Rflush response.\n        if (waitRequest)\n        {\n            co_await waitRequest->Event;\n        }\n\n        co_return 0;\n    }\n\n    Task<bool> FillData(UINT32 requiredBytes, CancelToken& token)\n    {\n        WI_ASSERT(m_RequestData.size() < requiredBytes);\n\n        UINT32 validLength = static_cast<UINT32>(m_RequestData.size());\n        if (validLength > 0 && m_RequestData.data() != m_RequestBuffer.data())\n        {\n            std::copy(m_RequestData.begin(), m_RequestData.end(), m_RequestBuffer.begin());\n        }\n\n        while (validLength < requiredBytes)\n        {\n            size_t count = co_await m_Socket->RecvAsync(gsl::span<gsl::byte>(m_RequestBuffer).subspan(validLength), token);\n            if (count == 0)\n            {\n                break;\n            }\n\n            validLength += static_cast<int>(count);\n        }\n\n        m_RequestData = gsl::span<gsl::byte>(m_RequestBuffer).subspan(0, validLength);\n        co_return validLength >= requiredBytes;\n    }\n\n    Task<gsl::span<gsl::byte>> NextMessage(CancelToken& token)\n    {\n        if (m_RequestData.size() < 4)\n        {\n            if (!co_await FillData(4, token))\n            {\n                co_return gsl::span<gsl::byte>{};\n            }\n        }\n\n        auto messageSize = SpanReader{m_RequestData}.U32();\n        THROW_INVALID_IF(messageSize < 7 || messageSize > m_NegotiatedSize);\n\n        if (m_RequestData.size() < messageSize)\n        {\n            if (!co_await FillData(messageSize, token))\n            {\n                co_return gsl::span<gsl::byte>{};\n            }\n        }\n\n        auto message = m_RequestData.subspan(0, messageSize);\n        m_RequestData = m_RequestData.subspan(messageSize);\n        co_return message;\n    }\n\n    // Process a message received from a socket.\n    Task<void> ProcessMessage(gsl::span<const gsl::byte> message, CancelToken& sendToken)\n    {\n        SpanReader reader{message};\n\n        // Utilize a small stack-allocated buffer that's large enough for the largest response\n        // without dynamic content (which is Rgetattr). Messages requiring a larger response will\n        // allocate a dynamic buffer by calling MessageResponse::EnsureSize.\n        // N.B. Message handlers that only return the header (e.g. HandleClunk) don't need to call\n        //      EnsureSize since the static buffer is always big enough for that.\n        gsl::byte staticBuffer[c_staticBufferSize];\n        MessageResponse response{staticBuffer};\n        co_await ProcessMessage(reader, response);\n        auto m = response.Writer.Result();\n\n        {\n            auto lock = co_await m_SocketLock.Lock();\n\n            // Send the response.\n            co_await m_Socket->SendAsync(m, sendToken);\n        }\n    }\n\n    // Process a Plan 9 message, and write the response to the specified buffer.\n    Task<void> ProcessMessage(SpanReader& reader, MessageResponse& response)\n    {\n        LogMessage(reader.Span());\n        reader.U32(); // message size, already validated\n        auto messageType = reader.U8();\n        const auto messageTag = reader.U16();\n        const SpanWriter errorWriter{response.Writer};\n\n        LX_INT error;\n        try\n        {\n            error = co_await HandleMessage(static_cast<MessageType>(messageType), reader, response);\n        }\n        catch (...)\n        {\n            LOG_CAUGHT_EXCEPTION();\n            error = util::LinuxErrorFromCaughtException();\n        }\n\n        if (error != 0)\n        {\n            response.Writer = errorWriter;\n            response.Writer.U32(static_cast<UINT32>(-error));\n            messageType = static_cast<UINT8>(MessageType::Tlerror);\n        }\n\n        response.Writer.Header(static_cast<MessageType>(messageType + 1), messageTag);\n        LogMessage(response.Writer.Result());\n    }\n\n    // Process a message received from virtio.\n    void ProcessMessageAsync(std::vector<gsl::byte>&& message, size_t responseSize, HandlerCallback&& callback) override\n    {\n        // Register the request so Tflush can wait on it if needed.\n        const auto tag = SpanReader{gsl::make_span(message).subspan(TagOffset)}.U16();\n        RequestTracker request{m_Requests, tag};\n\n        // Process the message in a coroutine. This routine will run synchronously until it hits\n        // a suspension point (which it may or may not depending on the message). The coroutine\n        // is not awaited here so if it does hit a suspension point the message will be completed\n        // asynchronously.\n        // N.B. The use of AsyncTask is required because the coroutine is not awaited.\n        // N.B. Since this thread is not running the scheduler it will not be used to run other\n        //      coroutines if this coroutine hits a suspension point.\n        RunAsyncTask(\n            [this,\n             localMessage = std::move(message),\n             localRequest = std::move(request),\n             responseSize,\n             completionCallback = std::move(callback)]() mutable -> Task<void> {\n                std::vector<gsl::byte> responseBuffer;\n                try\n                {\n                    SpanReader reader{localMessage};\n\n                    // Since the response buffer was sized based on the virtio write span, it's not\n                    // allowed to reallocate it for a bigger response.\n                    responseBuffer.resize(responseSize);\n                    MessageResponse response{responseBuffer, false};\n\n                    co_await ProcessMessage(reader, response);\n                    responseBuffer.resize(response.Writer.Size());\n                }\n                catch (...)\n                {\n                    LOG_CAUGHT_EXCEPTION();\n                    responseBuffer.clear();\n                }\n\n                completionCallback(responseBuffer);\n            });\n    }\n\npublic:\n    Task<void> Run(CancelToken& parentToken) noexcept\n    {\n        Plan9TraceLoggingProvider::AcceptedConnection();\n        CancelToken connectionToken(parentToken);\n        CancelToken recvToken(connectionToken);\n        CancelToken sendToken(connectionToken);\n        constexpr size_t maximumMessages = 32; // maximum number of concurrent messages\n        AsyncSemaphore messageSemaphore(maximumMessages);\n        while (!connectionToken.Cancelled())\n        {\n            // Only a single read is performed at a time, so no locking is\n            // necessary.\n            gsl::span<gsl::byte> message{};\n            try\n            {\n                message = co_await NextMessage(recvToken);\n            }\n            catch (...)\n            {\n                LOG_CAUGHT_EXCEPTION();\n            }\n\n            if (message.empty())\n            {\n                break;\n            }\n\n            // Register the request so Tflush can wait on it if needed.\n            const auto tag = SpanReader{message.subspan(TagOffset)}.U16();\n            RequestTracker request{m_Requests, tag};\n            co_await messageSemaphore.Acquire(1);\n\n            // Process the message on a separate scheduled coroutine. Receiving\n            // messages uses a shared buffer, which can be changed after the\n            // next call to NextMessage, so make a copy of the message.\n            RunScheduledTask(\n                [this,\n                 releaseSemaphore = wil::scope_exit([&]() { messageSemaphore.Release(1); }),\n                 localMessage = std::vector<gsl::byte>{message.begin(), message.end()},\n                 localRequest = std::move(request),\n                 &connectionToken,\n                 &sendToken]() mutable -> Task<void> {\n                    try\n                    {\n                        co_await ProcessMessage(localMessage, sendToken);\n                    }\n                    catch (...)\n                    {\n                        LOG_CAUGHT_EXCEPTION();\n                        connectionToken.Cancel();\n                    }\n                });\n        }\n\n        // Wait until all messages are finished.\n        connectionToken.Cancel();\n        co_await messageSemaphore.Acquire(maximumMessages);\n        Plan9TraceLoggingProvider::ConnectionDisconnected();\n        co_return;\n    }\n\nprivate:\n    std::shared_ptr<Fid> LookupFid(UINT32 fid)\n    {\n        std::shared_lock<std::shared_mutex> lock{m_FidsLock};\n        const auto it = m_Fids.find(fid);\n        THROW_UNEXPECTED_IF(it == m_Fids.end());\n        return it->second;\n    }\n\n    std::pair<std::shared_ptr<Fid>, std::shared_ptr<Fid>> LookupFidPair(UINT32 fid1, UINT32 fid2)\n    {\n        std::shared_lock<std::shared_mutex> lock{m_FidsLock};\n        const auto it1 = m_Fids.find(fid1);\n        THROW_UNEXPECTED_IF(it1 == m_Fids.end());\n        const auto it2 = m_Fids.find(fid2);\n        THROW_UNEXPECTED_IF(it2 == m_Fids.end());\n        return {it1->second, it2->second};\n    }\n\n    void EmplaceFid(UINT32 fid, std::shared_ptr<Fid> item)\n    {\n        std::lock_guard<std::shared_mutex> lock{m_FidsLock};\n        const auto result = m_Fids.try_emplace(fid, item);\n        THROW_INVALID_IF(!result.second);\n    }\n\n    // Returns the maximum size of an IO request (0 for no limit).\n    static UINT32 IoUnit()\n    {\n        return 0;\n    }\n\n    static constexpr UINT32 MinimumRequestBufferSize = 4096;\n    static constexpr UINT32 MaximumRequestBufferSize = 256 * 1024;\n    static constexpr UINT32 InitialResponseBufferSize = 64;\n\n    AsyncLock m_SocketLock;\n    ISocket* m_Socket{};\n    std::shared_mutex m_FidsLock;\n    std::map<UINT32, std::shared_ptr<Fid>> m_Fids;\n    std::vector<gsl::byte> m_RequestBuffer{MaximumRequestBufferSize};\n    gsl::span<gsl::byte> m_RequestData;\n    std::shared_ptr<RequestList> m_Requests;\n    UINT32 m_NegotiatedSize{InitialResponseBufferSize};\n    bool m_Negotiated{false};\n    bool m_AllowRenegotiate{false};\n    bool m_Use9P2000W{false};\n    IShareList& m_ShareList;\n};\n\nAsyncTask HandleConnections(ISocket& listen, IShareList& shareList, CancelToken& token, WaitGroup& waitGroup)\n{\n    std::atomic<size_t> connectionCount{};\n\n    try\n    {\n        while (!token.Cancelled())\n        {\n            Plan9TraceLoggingProvider::PreAccept();\n            auto client = co_await listen.AcceptAsync(token);\n            Plan9TraceLoggingProvider::PostAccept();\n\n            // If the operation was aborted, no socket is returned.\n            if (!client)\n            {\n                Plan9TraceLoggingProvider::OperationAborted();\n\n                token.Cancel();\n                break;\n            }\n\n            if (connectionCount >= shareList.MaximumConnectionCount())\n            {\n                Plan9TraceLoggingProvider::TooManyConnections();\n                // Terminate the client now so that there is quick feedback\n                // that no more connections are allowed.\n                client.reset();\n            }\n            else\n            {\n                ++connectionCount;\n                Plan9TraceLoggingProvider::ClientConnected(connectionCount);\n\n                RunScheduledTask([client = std::move(client), keepAlive = waitGroup.Add(), &connectionCount, &shareList, &token]() -> Task<void> {\n                    auto decrementCount = wil::scope_exit([&]() {\n                        --connectionCount;\n                        Plan9TraceLoggingProvider::ClientDisconnected(connectionCount);\n                    });\n                    Handler handler{*client, shareList};\n                    co_await handler.Run(token);\n                });\n            }\n        }\n    }\n    catch (...)\n    {\n        LOG_CAUGHT_EXCEPTION();\n        token.Cancel();\n    }\n\n    // Wait for the connection tasks to complete.\n    co_await waitGroup.Wait();\n\n    WI_ASSERT(connectionCount == 0);\n}\n\n// Creates a handler that can be used to process messages without a server socket, for use with\n// virtio servers.\nstd::unique_ptr<IHandler> HandlerFactory::CreateHandler() const\n{\n    // Since it's not possible to detect a \"disconnect\" with virtio, allow Tversion to be sent\n    // multiple times so the device can be mounted/dismounted more than once without restarting\n    // the VM.\n    return std::make_unique<Handler>(m_shareList, true);\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9handler.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9platform.h\"\n#include \"p9ihandler.h\"\n\nnamespace p9fs {\n\n// This class is basically a countdown event for active connections.\nclass WaitGroup\n{\npublic:\n    // Add a new wait group member. This should only be called by HandleConnections.\n    auto Add()\n    {\n        ++m_Count;\n        return std::unique_ptr<WaitGroup, Deleter>{this};\n    }\n\n    // Wait until all members are done. This should only be called by HandleConnections.\n    Task<void> Wait()\n    {\n        Done();\n        co_await m_Event;\n    }\n\n    // Check if there are members.\n    // N.B. The primary member (which is released by HandleConnections when the cancel token is\n    //      canceled), is not counted.\n    // N.B. This is the only member the caller of HandleConnections may use.\n    bool HasMembers() const noexcept\n    {\n        return m_Count > 1;\n    }\n\nprivate:\n    void Done()\n    {\n        if (--m_Count == 0)\n        {\n            m_Event.Set();\n        }\n    }\n\n    struct Deleter\n    {\n        void operator()(WaitGroup* group) const\n        {\n            group->Done();\n        }\n    };\n\n    AsyncEvent m_Event;\n    std::atomic<ULONG_PTR> m_Count{1};\n};\n\nAsyncTask HandleConnections(ISocket& listen, IShareList& shareList, CancelToken& token, WaitGroup& waitGroup);\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9ihandler.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nnamespace p9fs {\n\n// This class serves as a base for the platform-specific Root class. It has\n// no functionality.\nclass IRoot\n{\npublic:\n    virtual ~IRoot() = default;\n\nprotected:\n    IRoot() = default;\n};\n\nclass IShareList\n{\npublic:\n    virtual ~IShareList() = default;\n\n    virtual Expected<std::shared_ptr<const IRoot>> MakeRoot(std::string_view aname, LX_UID_T uid) = 0;\n    virtual size_t MaximumConnectionCount() = 0;\n};\n\n// Interface through which virtio can process messages on a handler.\n// N.B. This definition is not in p9handler so it can be used without needing to include all the\n//      coroutine related headers as well.\nclass IHandler\n{\npublic:\n    using HandlerCallback = std::function<void(const std::vector<gsl::byte>& response)>;\n    virtual void ProcessMessageAsync(std::vector<gsl::byte>&& message, size_t responseSize, HandlerCallback&& callback) = 0;\n};\n\nclass HandlerFactory\n{\npublic:\n    HandlerFactory(IShareList& shareList) : m_shareList{shareList}\n    {\n    }\n\n    std::unique_ptr<IHandler> CreateHandler() const;\n\nprivate:\n    IShareList& m_shareList;\n};\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9io.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9io.h\"\n\n#ifndef TEMP_FAILURE_RETRY\n#define TEMP_FAILURE_RETRY(expression) \\\n    (__extension__({ \\\n        long int __result; \\\n        do \\\n            __result = (long int)(expression); \\\n        while (__result == -1L && errno == EINTR); \\\n        __result; \\\n    }))\n#endif\n\nnamespace p9fs {\n\nEpollWatcher g_Watcher;\n\nCoroutineIoIssuer::CoroutineIoIssuer(int fd) : m_FileDescriptor(fd)\n{\n}\n\nvoid CoroutineIoIssuer::Callback(sigval value)\n{\n    const auto operation = static_cast<CoroutineIoOperation*>(value.sival_ptr);\n    auto bytesTransferred = aio_return(&operation->ControlBlock);\n    int error = 0;\n    if (bytesTransferred < 0)\n    {\n        error = aio_error(&operation->ControlBlock);\n    }\n\n    operation->Result = {error, static_cast<size_t>(bytesTransferred)};\n    if (!operation->DoneOrCoroutine.exchange(true))\n    {\n        return;\n    }\n\n    // TODO: Can we use this thread to resume the coroutine?\n    g_Scheduler.Schedule(operation->Coroutine);\n}\n\nbool CoroutineIoIssuer::PreIssue(CoroutineIoOperation& operation, CancelToken& token)\n{\n    // Register the IO for cancellation.\n    operation.ControlBlock = {};\n    operation.ControlBlock.aio_fildes = m_FileDescriptor;\n    operation.ControlBlock.aio_sigevent.sigev_notify = SIGEV_THREAD;\n    operation.ControlBlock.aio_sigevent.sigev_notify_function = Callback;\n    operation.ControlBlock.aio_sigevent.sigev_value.sival_ptr = &operation;\n    if (token.Register(operation))\n    {\n        return true;\n    }\n\n    // The operation has already been cancelled. Don't even issue the IO.\n    operation.Result = {ECANCELED, 0};\n    operation.DoneOrCoroutine = true;\n    return false;\n}\n\nvoid CoroutineIoIssuer::IssueFailed(CancelToken& token)\n{\n    // Unwind the work done in PreIssue.\n    token.Unregister();\n}\n\nvoid CoroutineIoIssuer::PostIssue(CoroutineIoOperation& operation, CancelToken& token, IoResult result)\n{\n    if (result.Error != 0)\n    {\n        // The IO completed synchronously.\n        operation.Result = result;\n        operation.DoneOrCoroutine = true;\n\n        WI_ASSERT(operation.Coroutine == nullptr);\n    }\n    else if (token.Cancelled())\n    {\n        // The IO did not complete synchronously, but the operation has been\n        // cancelled. Depending on when the cancel occurred, the IO may not have\n        // been cancelled, so cancel it now.\n        aio_cancel(operation.ControlBlock.aio_fildes, &operation.ControlBlock);\n    }\n}\n\n// Register an epoll operation for either an in or an out event.\n// Returns true if the operation must suspend to wait for the event, or false if the operation can\n// resume immediately.\n// N.B. There can be only one operation registered at a time for each event.\nbool EpollDispatcher::Register(int event, CoroutineEpollOperation& operation)\n{\n    std::scoped_lock<std::mutex> lock{m_lock};\n    if (event == EPOLLIN)\n    {\n        FAIL_FAST_IF(m_inOperation != nullptr);\n        if (WI_IsFlagSet(m_currentEvents, EPOLLIN))\n        {\n            WI_ClearFlag(m_currentEvents, EPOLLIN);\n\n            // Since Register is called before the operation is registered for cancellation,\n            // it's not possible for this to return false.\n            operation.SetResult(0);\n            return false;\n        }\n\n        m_inOperation = &operation;\n    }\n    else\n    {\n        FAIL_FAST_IF(event != EPOLLOUT);\n        FAIL_FAST_IF(m_outOperation != nullptr);\n        if (WI_IsFlagSet(m_currentEvents, EPOLLOUT))\n        {\n            WI_ClearFlag(m_currentEvents, EPOLLOUT);\n            operation.SetResult(0);\n            return false;\n        }\n\n        m_outOperation = &operation;\n    }\n\n    return true;\n}\n\n// Removes the handler for the specified event (EPOLLIN or EPOLLOUT).\nvoid EpollDispatcher::Remove(int event)\n{\n    std::scoped_lock<std::mutex> lock{m_lock};\n    if (event == EPOLLIN)\n    {\n        m_inOperation = nullptr;\n    }\n    else\n    {\n        FAIL_FAST_IF(event != EPOLLOUT);\n        m_outOperation = nullptr;\n    }\n}\n\n// Notifies the dispatcher an event has occurred.\nvoid EpollDispatcher::Notify(int events)\n{\n    std::scoped_lock<std::mutex> lock{m_lock};\n\n    // Resume the out operation first, since that is responding to an existing message rather than\n    // reading the request for a new one.\n    if (WI_IsFlagSet(events, EPOLLOUT))\n    {\n        if (m_outOperation != nullptr)\n        {\n            m_outOperation->Resume(0);\n            m_outOperation = nullptr;\n        }\n        else\n        {\n            // If no operation is registered, remember the event occurred for the next time one\n            // is registered.\n            WI_SetFlag(m_currentEvents, EPOLLOUT);\n        }\n    }\n\n    if (WI_IsFlagSet(events, EPOLLIN))\n    {\n        if (m_inOperation != nullptr)\n        {\n            m_inOperation->Resume(0);\n            m_inOperation = nullptr;\n        }\n        else\n        {\n            // If no operation is registered, remember the event occurred for the next time one\n            // is registered.\n            WI_SetFlag(m_currentEvents, EPOLLIN);\n        }\n    }\n}\n\nvoid EpollWatcher::Run()\n{\n    FAIL_FAST_IF(m_EpollFileDescriptor >= 0);\n\n    m_EpollFileDescriptor = epoll_create1(EPOLL_CLOEXEC);\n    THROW_LAST_ERROR_IF(m_EpollFileDescriptor < 0);\n\n    std::thread(WatchThread, this).detach();\n}\n\nvoid EpollWatcher::Add(int fd, int events, EpollDispatcher& dispatcher)\n{\n    epoll_event event{};\n    event.events = events;\n    event.data.ptr = &dispatcher;\n    THROW_LAST_ERROR_IF(epoll_ctl(m_EpollFileDescriptor, EPOLL_CTL_ADD, fd, &event) < 0);\n}\n\nvoid EpollWatcher::Remove(int fd)\n{\n    THROW_LAST_ERROR_IF(epoll_ctl(m_EpollFileDescriptor, EPOLL_CTL_DEL, fd, nullptr) < 0);\n}\n\nvoid EpollWatcher::WatchThread(EpollWatcher* watcher)\n{\n    for (;;)\n    {\n        epoll_event events[10];\n        int result = TEMP_FAILURE_RETRY(epoll_wait(watcher->m_EpollFileDescriptor, events, 10, -1));\n        THROW_LAST_ERROR_IF(result < 0);\n\n        for (int i = 0; i < result; ++i)\n        {\n            if (events[i].data.ptr != nullptr)\n            {\n                const auto dispatcher = static_cast<EpollDispatcher*>(events[i].data.ptr);\n                dispatcher->Notify(events[i].events);\n            }\n        }\n    }\n}\n\nTask<size_t> RecvAsync(CoroutineEpollIssuer& socket, gsl::span<gsl::byte> buffer, CancelToken& token)\n{\n    CoroutineEpollOperation operation;\n    auto result =\n        co_await socket.Issue<ssize_t>(operation, token, EPOLLIN, [&](int fd) { return recv(fd, buffer.data(), buffer.size(), 0); });\n\n    if (result < 0)\n    {\n        THROW_ERRNO(-result);\n    }\n\n    co_return static_cast<size_t>(result);\n}\n\nTask<size_t> SendAsync(CoroutineEpollIssuer& socket, gsl::span<const gsl::byte> buffer, CancelToken& token)\n{\n    CoroutineEpollOperation operation;\n    auto result = co_await socket.Issue<ssize_t>(\n        operation, token, EPOLLOUT, [&](int fd) { return send(fd, buffer.data(), buffer.size(), 0); });\n\n    if (result < 0)\n    {\n        THROW_ERRNO(-result);\n    }\n\n    co_return static_cast<size_t>(result);\n}\n\nTask<int> AcceptAsync(CoroutineEpollIssuer& listen, CancelToken& token)\n{\n    CoroutineEpollOperation operation;\n    auto result = co_await listen.Issue<int>(\n        operation, token, EPOLLIN, [&](int fd) { return accept4(fd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC); });\n\n    if (result < 0)\n    {\n        THROW_ERRNO(-result);\n    }\n\n    co_return result;\n}\n\nTask<IoResult> ReadAsync(CoroutineIoIssuer& file, std::uint64_t offset, gsl::span<gsl::byte> buffer, CancelToken& token)\n{\n    CoroutineIoOperation operation;\n    co_return co_await file.Issue(operation, token, [&](aiocb& cb) -> IoResult {\n        cb.aio_buf = buffer.data();\n        cb.aio_nbytes = buffer.size();\n        cb.aio_offset = offset;\n        if (aio_read(&cb) < 0)\n        {\n            return {-errno, 0};\n        }\n\n        return {};\n    });\n}\n\nTask<IoResult> WriteAsync(CoroutineIoIssuer& file, std::uint64_t offset, gsl::span<const gsl::byte> buffer, CancelToken& token)\n{\n    CoroutineIoOperation operation;\n    co_return co_await file.Issue(operation, token, [&](aiocb& cb) -> IoResult {\n        cb.aio_buf = (volatile void*)buffer.data();\n        cb.aio_nbytes = buffer.size();\n        cb.aio_offset = offset;\n        if (aio_write(&cb) < 0)\n        {\n            return {errno, 0};\n        }\n\n        return {};\n    });\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9io.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9await.h\"\n#include \"p9tracelogging.h\"\n\nnamespace p9fs {\n\nstruct IoResult\n{\n    int Error;\n    size_t BytesTransferred;\n};\n\nstruct CoroutineIoOperation final : public ICancellable\n{\n    aiocb ControlBlock;\n    IoResult Result;\n    std::coroutine_handle<> Coroutine{};\n    std::atomic<bool> DoneOrCoroutine{false};\n\n    void Cancel() override\n    {\n        aio_cancel(ControlBlock.aio_fildes, &ControlBlock);\n    }\n};\n\nstruct CoroutineEpollOperation final : public ICancellable\n{\n    std::atomic<int> Result{EWOULDBLOCK};\n    std::coroutine_handle<> Coroutine;\n\n    void Resume(int result)\n    {\n        if (SetResult(result))\n        {\n            g_Scheduler.Schedule(Coroutine);\n        }\n    }\n\n    bool SetResult(int result)\n    {\n        int expected = EWOULDBLOCK;\n        return Result.compare_exchange_strong(expected, result);\n    }\n\n    void Cancel() override\n    {\n        Resume(ECANCELED);\n    }\n};\n\nstruct CoroutineIoIssuer\n{\nprivate:\n    struct Awaiter\n    {\n        CoroutineIoOperation& m_Operation;\n        CancelToken& m_Token;\n\n        bool await_ready() const\n        {\n            return m_Operation.DoneOrCoroutine;\n        }\n\n        bool await_suspend(std::coroutine_handle<> handle)\n        {\n            WI_ASSERT(m_Operation.Coroutine == nullptr);\n\n            m_Operation.Coroutine = handle;\n            if (m_Operation.DoneOrCoroutine.exchange(true))\n            {\n                return false;\n            }\n\n            return true;\n        }\n\n        IoResult await_resume() const\n        {\n            m_Token.Unregister();\n            return m_Operation.Result;\n        }\n    };\n\npublic:\n    CoroutineIoIssuer() = default;\n    CoroutineIoIssuer(int fd);\n\n    explicit operator bool() const\n    {\n        return m_FileDescriptor >= 0;\n    }\n\n    template <class T>\n    Awaiter Issue(CoroutineIoOperation& operation, CancelToken& token, T&& func)\n    {\n        if (PreIssue(operation, token))\n        {\n            IoResult result;\n            try\n            {\n                result = func(operation.ControlBlock);\n            }\n            catch (...)\n            {\n                IssueFailed(token);\n                throw;\n            }\n\n            PostIssue(operation, token, result);\n        }\n\n        return Awaiter{operation, token};\n    }\n\nprivate:\n    static void Callback(sigval value);\n\n    bool PreIssue(CoroutineIoOperation& operation, CancelToken& token);\n    static void IssueFailed(CancelToken& token);\n    void PostIssue(CoroutineIoOperation& operation, CancelToken& token, IoResult result);\n\n    int m_FileDescriptor{-1};\n};\n\n// Class that handles suspending and resuming operations based on EPOLLIN and EPOLLOUT events.\nclass EpollDispatcher\n{\npublic:\n    bool Register(int event, CoroutineEpollOperation& operation);\n    void Remove(int event);\n    void Notify(int events);\n\nprivate:\n    std::mutex m_lock;\n    int m_currentEvents{};\n    CoroutineEpollOperation* m_outOperation{};\n    CoroutineEpollOperation* m_inOperation{};\n};\n\nclass EpollWatcher\n{\npublic:\n    void Run();\n    void Add(int fd, int events, EpollDispatcher& dispatcher);\n    void Remove(int fd);\n\n    explicit operator bool() const noexcept\n    {\n        return m_EpollFileDescriptor >= 0;\n    }\n\nprivate:\n    static void WatchThread(EpollWatcher* watcher);\n\n    int m_EpollFileDescriptor{-1};\n};\n\nextern EpollWatcher g_Watcher;\n\nclass CoroutineEpollIssuer\n{\nprivate:\n    struct Awaiter\n    {\n        EpollDispatcher& Dispatcher;\n        int Fd;\n        CoroutineEpollOperation& Operation;\n        int Events;\n        CancelToken& Token;\n\n        static constexpr bool await_ready() noexcept\n        {\n            return false;\n        }\n\n        bool await_suspend(std::coroutine_handle<> handle)\n        {\n            Operation.Result = EWOULDBLOCK;\n            Operation.Coroutine = handle;\n\n            // Check if the operation needs to be suspended.\n            if (!Dispatcher.Register(Events, Operation))\n            {\n                return false;\n            }\n\n            // Register the operation for cancellation only if it actually needs to suspend.\n            if (!Token.Register(Operation))\n            {\n                // If the operation is already cancelled, attempt to mark it cancelled. The function\n                // must return true if that fails, because it means the dispatcher already scheduled\n                // it for resumption.\n                return !Operation.SetResult(ECANCELED);\n            }\n\n            return true;\n        }\n\n        int await_resume() const\n        {\n            // If the function was resumed by cancellation, make sure the dispatcher can't access\n            // it after it goes out of scope.\n            // N.B. If the operation was not registered with the dispatcher, calling remove is a\n            //      no-op.\n            if (Operation.Result == ECANCELED)\n            {\n                Dispatcher.Remove(Events);\n            }\n\n            // Unregister from the cancel token.\n            // N.B. If the operation was not registered with the token, calling remove is a no-op.\n            Token.Unregister();\n            return Operation.Result;\n        }\n    };\n\npublic:\n    CoroutineEpollIssuer(EpollWatcher& watcher) : m_Watcher{watcher}\n    {\n    }\n\n    ~CoroutineEpollIssuer()\n    {\n        Reset();\n    }\n\n    explicit operator bool() const\n    {\n        return m_FileDescriptor >= 0;\n    }\n\n    template <typename TResult, typename T>\n    Task<TResult> Issue(CoroutineEpollOperation& operation, CancelToken& token, int events, T&& func)\n    {\n        for (;;)\n        {\n            TResult result = func(m_FileDescriptor);\n            if (result >= 0)\n            {\n                co_return result;\n            }\n\n            if (errno != EWOULDBLOCK)\n            {\n                co_return -errno;\n            }\n\n            // Wait for the epoll notification before retrying.\n            int waitResult = co_await Awaiter{m_Dispatcher, m_FileDescriptor, operation, events, token};\n            if (waitResult != 0)\n            {\n                co_return -waitResult;\n            }\n        }\n    }\n\n    void Reset(int fd = -1)\n    {\n        // If there is an existing file descriptor, remove it from the epoll.\n        if (m_FileDescriptor >= 0)\n        {\n            m_Watcher.Remove(m_FileDescriptor);\n        }\n\n        m_FileDescriptor = fd;\n\n        // Add the new file descriptor to epoll.\n        if (m_FileDescriptor >= 0)\n        {\n            m_Watcher.Add(m_FileDescriptor, EPOLLIN | EPOLLOUT | EPOLLET, m_Dispatcher);\n        }\n    }\n\nprivate:\n    EpollWatcher& m_Watcher;\n    EpollDispatcher m_Dispatcher;\n    int m_FileDescriptor{-1};\n};\n\nTask<int> AcceptAsync(CoroutineEpollIssuer& listen, CancelToken& token);\nTask<size_t> RecvAsync(CoroutineEpollIssuer& socket, gsl::span<gsl::byte> buffer, CancelToken& token);\nTask<size_t> SendAsync(CoroutineEpollIssuer& socket, gsl::span<const gsl::byte> buffer, CancelToken& token);\nTask<IoResult> ReadAsync(CoroutineIoIssuer& file, std::uint64_t offset, gsl::span<gsl::byte> buffer, CancelToken& token);\nTask<IoResult> WriteAsync(CoroutineIoIssuer& file, std::uint64_t offset, gsl::span<const gsl::byte> buffer, CancelToken& token);\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9log.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    p9log.h\n\nAbstract:\n\n    This file contains the plan9 logging implementation.\n\n--*/\n\n#pragma once\n\nnamespace p9fs {\n\n// Outputs a log message to stdout with the contents of the specified message.\ninline void TraceLogMessage([[maybe_unused]] gsl::span<const gsl::byte> message)\n{\n\n    if (!Plan9TraceLoggingProvider::IsEnabled(TRACE_LEVEL_VERBOSE))\n    {\n        return;\n    }\n\n    SpanReader reader{message};\n    reader.U32(); // size (unused)\n    auto messageType = static_cast<MessageType>(reader.U8());\n    auto tag = reader.U16();\n    LogMessageBuilder text;\n\n    switch (messageType)\n    {\n    case MessageType::Tversion:\n    {\n        // size[4] Tversion tag[2] msize[4] version[s]\n        text.AddName(\">>Tversion\");\n        text.AddField(\"tag\", tag);\n        auto msize = reader.U32();\n        text.AddField(\"msize\", msize);\n        auto version = reader.String();\n        text.AddField(\"version\", version);\n        break;\n    }\n\n    case MessageType::Rversion:\n    {\n        // size[4] Rversion tag[2] msize[4] version[s]\n        text.AddName(\"<<Rversion\");\n        text.AddField(\"tag\", tag);\n        auto msize = reader.U32();\n        text.AddField(\"msize\", msize);\n        auto version = reader.String();\n        text.AddField(\"version\", version);\n        break;\n    }\n\n    case MessageType::Tflush:\n    {\n        // size[4] Tflush tag[2] oldtag[2]\n        text.AddName(\">>Tflush\");\n        text.AddField(\"tag\", tag);\n        auto oldtag = reader.U16();\n        text.AddField(\"oldtag\", oldtag);\n        break;\n    }\n\n    case MessageType::Rflush:\n    {\n        // size[4] Rflush tag[2]\n        text.AddName(\"<<Rflush\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Twalk:\n    {\n        // size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])\n        text.AddName(\">>Twalk\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto newfid = reader.U32();\n        text.AddField(\"newfid\", newfid);\n        auto nwname = reader.U16();\n        text.AddField(\"nwname\", nwname);\n        for (UINT32 i = 0; i < nwname; ++i)\n        {\n            auto wname = reader.String();\n            text.AddValue(wname);\n        }\n        break;\n    }\n\n    case MessageType::Rwalk:\n    {\n        // size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])\n        text.AddName(\"<<Rwalk\");\n        text.AddField(\"tag\", tag);\n        auto nwqid = reader.U16();\n        text.AddField(\"nwqid\", nwqid);\n        for (UINT32 i = 0; i < nwqid; ++i)\n        {\n            auto wqid = reader.Qid();\n            text.AddValue(wqid);\n        }\n        break;\n    }\n\n    case MessageType::Tread:\n    {\n        // size[4] Tread tag[2] fid[4] offset[8] count[4]\n        text.AddName(\">>Tread\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto offset = reader.U64();\n        text.AddField(\"offset\", offset);\n        auto count = reader.U32();\n        text.AddField(\"count\", count);\n        break;\n    }\n\n    case MessageType::Rread:\n    {\n        // size[4] Rread tag[2] count[4] data[count]\n        text.AddName(\"<<Rread\");\n        text.AddField(\"tag\", tag);\n        auto count = reader.U32();\n        text.AddField(\"count\", count);\n        break;\n    }\n\n    case MessageType::Twrite:\n    {\n        // size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]\n        text.AddName(\">>Twrite\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto offset = reader.U64();\n        text.AddField(\"offset\", offset);\n        auto count = reader.U32();\n        text.AddField(\"count\", count);\n        break;\n    }\n\n    case MessageType::Rwrite:\n    {\n        // size[4] Rwrite tag[2] count[4]\n        text.AddName(\"<<Rwrite\");\n        text.AddField(\"tag\", tag);\n        auto count = reader.U32();\n        text.AddField(\"count\", count);\n        break;\n    }\n\n    case MessageType::Tclunk:\n    {\n        // size[4] Tclunk tag[2] fid[4]\n        text.AddName(\">>Tclunk\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        break;\n    }\n\n    case MessageType::Rclunk:\n    {\n        // size[4] Rclunk tag[2]\n        text.AddName(\"<<Rclunk\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Tremove:\n    {\n        // size[4] Tremove tag[2] fid[4]\n        text.AddName(\">>Tremove\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        break;\n    }\n\n    case MessageType::Rremove:\n    {\n        // size[4] Rremove tag[2]\n        text.AddName(\"<<Rremove\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Tauth:\n    {\n        // size[4] Tauth tag[2] afid[4] uname[s] aname[s] n_uname[4]\n        text.AddName(\">>Tauth\");\n        text.AddField(\"tag\", tag);\n        auto afid = reader.U32();\n        text.AddField(\"afid\", afid);\n        auto uname = reader.String();\n        text.AddField(\"uname\", uname);\n        auto aname = reader.String();\n        text.AddField(\"aname\", aname);\n        auto n_uname = reader.U32();\n        text.AddField(\"n_uname\", n_uname);\n        break;\n    }\n\n    case MessageType::Rauth:\n    {\n        // size[4] Rauth tag[2] aqid[13]\n        text.AddName(\"<<Rauth\");\n        text.AddField(\"tag\", tag);\n        auto aqid = reader.Qid();\n        text.AddField(\"aqid\", aqid);\n        break;\n    }\n\n    case MessageType::Tattach:\n    {\n        // size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4]\n        text.AddName(\">>Tattach\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto afid = reader.U32();\n        text.AddField(\"afid\", afid);\n        auto uname = reader.String();\n        text.AddField(\"uname\", uname);\n        auto aname = reader.String();\n        text.AddField(\"aname\", aname);\n        auto n_uname = reader.U32();\n        text.AddField(\"n_uname\", n_uname);\n        break;\n    }\n\n    case MessageType::Rattach:\n    {\n        // size[4] Rattach tag[2] qid[13]\n        text.AddName(\"<<Rattach\");\n        text.AddField(\"tag\", tag);\n        auto qid = reader.Qid();\n        text.AddField(\"qid\", qid);\n        break;\n    }\n\n    case MessageType::Rlerror:\n    {\n        // size[4] Rlerror tag[2] ecode[4]\n        text.AddName(\"<<Rlerror\");\n        text.AddField(\"tag\", tag);\n        auto ecode = reader.U32();\n        text.AddField(\"ecode\", ecode);\n        break;\n    }\n\n    case MessageType::Tstatfs:\n    {\n        // size[4] Tstatfs tag[2] fid[4]\n        text.AddName(\">>Tstatfs\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        break;\n    }\n\n    case MessageType::Rstatfs:\n    {\n        // size[4] Rstatfs tag[2] type[4] bsize[4] blocks[8] bfree[8] bavail[8] files[8] ffree[8] fsid[8] namelen[4]\n        text.AddName(\"<<Rstatfs\");\n        text.AddField(\"tag\", tag);\n        auto type = reader.U32();\n        text.AddField(\"type\", type);\n        auto bsize = reader.U32();\n        text.AddField(\"bsize\", bsize);\n        auto blocks = reader.U64();\n        text.AddField(\"blocks\", blocks);\n        auto bfree = reader.U64();\n        text.AddField(\"bfree\", bfree);\n        auto bavail = reader.U64();\n        text.AddField(\"bavail\", bavail);\n        auto files = reader.U64();\n        text.AddField(\"files\", files);\n        auto ffree = reader.U64();\n        text.AddField(\"ffree\", ffree);\n        auto fsid = reader.U64();\n        text.AddField(\"fsid\", fsid);\n        auto namelen = reader.U32();\n        text.AddField(\"namelen\", namelen);\n        break;\n    }\n\n    case MessageType::Tlopen:\n    {\n        // size[4] Tlopen tag[2] fid[4] flags[4]\n        text.AddName(\">>Tlopen\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto flags = reader.U32();\n        text.AddField(\"flags\", flags);\n        break;\n    }\n\n    case MessageType::Rlopen:\n    {\n        // size[4] Rlopen tag[2] qid[13] iounit[4]\n        text.AddName(\"<<Rlopen\");\n        text.AddField(\"tag\", tag);\n        auto qid = reader.Qid();\n        text.AddField(\"qid\", qid);\n        auto iounit = reader.U32();\n        text.AddField(\"iounit\", iounit);\n        break;\n    }\n\n    case MessageType::Tlcreate:\n    {\n        // size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4]\n        text.AddName(\">>Tlcreate\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto name = reader.String();\n        text.AddField(\"name\", name);\n        auto flags = reader.U32();\n        text.AddField(\"flags\", flags);\n        auto mode = reader.U32();\n        text.AddField(\"mode\", mode);\n        auto gid = reader.U32();\n        text.AddField(\"gid\", gid);\n        break;\n    }\n\n    case MessageType::Rlcreate:\n    {\n        // size[4] Rlcreate tag[2] qid[13] iounit[4]\n        text.AddName(\"<<Rlcreate\");\n        text.AddField(\"tag\", tag);\n        auto qid = reader.Qid();\n        text.AddField(\"qid\", qid);\n        auto iounit = reader.U32();\n        text.AddField(\"iounit\", iounit);\n        break;\n    }\n\n    case MessageType::Tsymlink:\n    {\n        // size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4]\n        text.AddName(\">>Tsymlink\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto name = reader.String();\n        text.AddField(\"name\", name);\n        auto symtgt = reader.String();\n        text.AddField(\"symtgt\", symtgt);\n        auto gid = reader.U32();\n        text.AddField(\"gid\", gid);\n        break;\n    }\n\n    case MessageType::Rsymlink:\n    {\n        // size[4] Rsymlink tag[2] qid[13]\n        text.AddName(\"<<Rsymlink\");\n        text.AddField(\"tag\", tag);\n        auto qid = reader.Qid();\n        text.AddField(\"qid\", qid);\n        break;\n    }\n\n    case MessageType::Tmknod:\n    {\n        // size[4] Tmknod tag[2] dfid[4] name[s] mode[4] major[4] minor[4] gid[4]\n        text.AddName(\">>Tmknod\");\n        text.AddField(\"tag\", tag);\n        auto dfid = reader.U32();\n        text.AddField(\"dfid\", dfid);\n        auto name = reader.String();\n        text.AddField(\"name\", name);\n        auto mode = reader.U32();\n        text.AddField(\"mode\", mode);\n        auto major = reader.U32();\n        text.AddField(\"major\", major);\n        auto minor = reader.U32();\n        text.AddField(\"minor\", minor);\n        auto gid = reader.U32();\n        text.AddField(\"gid\", gid);\n        break;\n    }\n\n    case MessageType::Rmknod:\n    {\n        // size[4] Rmknod tag[2] qid[13]\n        text.AddName(\"<<Rmknod\");\n        text.AddField(\"tag\", tag);\n        auto qid = reader.Qid();\n        text.AddField(\"qid\", qid);\n        break;\n    }\n\n    case MessageType::Trename:\n    {\n        // size[4] Trename tag[2] fid[4] dfid[4] name[s]\n        text.AddName(\">>Trename\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto dfid = reader.U32();\n        text.AddField(\"dfid\", dfid);\n        auto name = reader.String();\n        text.AddField(\"name\", name);\n        break;\n    }\n\n    case MessageType::Rrename:\n    {\n        // size[4] Rrename tag[2]\n        text.AddName(\"<<Rrename\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Treadlink:\n    {\n        // size[4] Treadlink tag[2] fid[4]\n        text.AddName(\">>Treadlink\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        break;\n    }\n\n    case MessageType::Rreadlink:\n    {\n        // size[4] Rreadlink tag[2] target[s]\n        text.AddName(\"<<Rreadlink\");\n        text.AddField(\"tag\", tag);\n        auto target = reader.String();\n        text.AddField(\"target\", target);\n        break;\n    }\n\n    case MessageType::Tgetattr:\n    {\n        // size[4] Tgetattr tag[2] fid[4] request_mask[8]\n        text.AddName(\">>Tgetattr\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto request_mask = reader.U64();\n        text.AddField(\"request_mask\", request_mask);\n        break;\n    }\n\n    case MessageType::Rgetattr:\n    {\n        // size[4] Rgetattr tag[2] valid[8] qid[13] mode[4] uid[4] gid[4] nlink[8] rdev[8] size[8] blksize[8] blocks[8] atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8] gen[8] data_version[8]\n        text.AddName(\"<<Rgetattr\");\n        text.AddField(\"tag\", tag);\n        auto valid = reader.U64();\n        text.AddField(\"valid\", valid);\n        auto qid = reader.Qid();\n        text.AddField(\"qid\", qid);\n        auto mode = reader.U32();\n        text.AddField(\"mode\", mode);\n        auto uid = reader.U32();\n        text.AddField(\"uid\", uid);\n        auto gid = reader.U32();\n        text.AddField(\"gid\", gid);\n        auto nlink = reader.U64();\n        text.AddField(\"nlink\", nlink);\n        auto rdev = reader.U64();\n        text.AddField(\"rdev\", rdev);\n        auto size = reader.U64();\n        text.AddField(\"size\", size);\n        auto blksize = reader.U64();\n        text.AddField(\"blksize\", blksize);\n        auto blocks = reader.U64();\n        text.AddField(\"blocks\", blocks);\n        auto atime_sec = reader.U64();\n        text.AddField(\"atime_sec\", atime_sec);\n        auto atime_nsec = reader.U64();\n        text.AddField(\"atime_nsec\", atime_nsec);\n        auto mtime_sec = reader.U64();\n        text.AddField(\"mtime_sec\", mtime_sec);\n        auto mtime_nsec = reader.U64();\n        text.AddField(\"mtime_nsec\", mtime_nsec);\n        auto ctime_sec = reader.U64();\n        text.AddField(\"ctime_sec\", ctime_sec);\n        auto ctime_nsec = reader.U64();\n        text.AddField(\"ctime_nsec\", ctime_nsec);\n        auto btime_sec = reader.U64();\n        text.AddField(\"btime_sec\", btime_sec);\n        auto btime_nsec = reader.U64();\n        text.AddField(\"btime_nsec\", btime_nsec);\n        auto gen = reader.U64();\n        text.AddField(\"gen\", gen);\n        auto data_version = reader.U64();\n        text.AddField(\"data_version\", data_version);\n        break;\n    }\n\n    case MessageType::Tsetattr:\n    {\n        // size[4] Tsetattr tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8] atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]\n        text.AddName(\">>Tsetattr\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto valid = reader.U32();\n        text.AddField(\"valid\", valid);\n        auto mode = reader.U32();\n        text.AddField(\"mode\", mode);\n        auto uid = reader.U32();\n        text.AddField(\"uid\", uid);\n        auto gid = reader.U32();\n        text.AddField(\"gid\", gid);\n        auto size = reader.U64();\n        text.AddField(\"size\", size);\n        auto atime_sec = reader.U64();\n        text.AddField(\"atime_sec\", atime_sec);\n        auto atime_nsec = reader.U64();\n        text.AddField(\"atime_nsec\", atime_nsec);\n        auto mtime_sec = reader.U64();\n        text.AddField(\"mtime_sec\", mtime_sec);\n        auto mtime_nsec = reader.U64();\n        text.AddField(\"mtime_nsec\", mtime_nsec);\n        break;\n    }\n\n    case MessageType::Rsetattr:\n    {\n        // size[4] Rsetattr tag[2]\n        text.AddName(\"<<Rsetattr\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Txattrwalk:\n    {\n        // size[4] Txattrwalk tag[2] fid[4] newfid[4] name[s]\n        text.AddName(\">>Txattrwalk\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto newfid = reader.U32();\n        text.AddField(\"newfid\", newfid);\n        auto name = reader.String();\n        text.AddField(\"name\", name);\n        break;\n    }\n\n    case MessageType::Rxattrwalk:\n    {\n        // size[4] Rxattrwalk tag[2] size[8]\n        text.AddName(\"<<Rxattrwalk\");\n        text.AddField(\"tag\", tag);\n        auto size = reader.U64();\n        text.AddField(\"size\", size);\n        break;\n    }\n\n    case MessageType::Txattrcreate:\n    {\n        // size[4] Txattrcreate tag[2] fid[4] name[s] attr_size[8] flags[4]\n        text.AddName(\">>Txattrcreate\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto name = reader.String();\n        text.AddField(\"name\", name);\n        auto attr_size = reader.U64();\n        text.AddField(\"attr_size\", attr_size);\n        auto flags = reader.U32();\n        text.AddField(\"flags\", flags);\n        break;\n    }\n\n    case MessageType::Rxattrcreate:\n    {\n        // size[4] Rxattrcreate tag[2]\n        text.AddName(\"<<Rxattrcreate\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Treaddir:\n    {\n        // size[4] Treaddir tag[2] fid[4] offset[8] count[4]\n        text.AddName(\">>Treaddir\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto offset = reader.U64();\n        text.AddField(\"offset\", offset);\n        auto count = reader.U32();\n        text.AddField(\"count\", count);\n        break;\n    }\n\n    case MessageType::Rreaddir:\n    {\n        // size[4] Rreaddir tag[2] count[4] data[count]\n        text.AddName(\"<<Rreaddir\");\n        text.AddField(\"tag\", tag);\n        auto count = reader.U32();\n        text.AddField(\"count\", count);\n        break;\n    }\n\n    case MessageType::Tfsync:\n    {\n        // size[4] Tfsync tag[2] fid[4]\n        text.AddName(\">>Tfsync\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        break;\n    }\n\n    case MessageType::Rfsync:\n    {\n        // size[4] Rfsync tag[2]\n        text.AddName(\"<<Rfsync\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Tlock:\n    {\n        // size[4] Tlock tag[2] fid[4] type[1] flags[4] start[8] length[8] proc_id[4] client_id[s]\n        text.AddName(\">>Tlock\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto type = reader.U8();\n        text.AddField(\"type\", type);\n        auto flags = reader.U32();\n        text.AddField(\"flags\", flags);\n        auto start = reader.U64();\n        text.AddField(\"start\", start);\n        auto length = reader.U64();\n        text.AddField(\"length\", length);\n        auto proc_id = reader.U32();\n        text.AddField(\"proc_id\", proc_id);\n        auto client_id = reader.String();\n        text.AddField(\"client_id\", client_id);\n        break;\n    }\n\n    case MessageType::Rlock:\n    {\n        // size[4] Rlock tag[2] status[1]\n        text.AddName(\"<<Rlock\");\n        text.AddField(\"tag\", tag);\n        auto status = reader.U8();\n        text.AddField(\"status\", status);\n        break;\n    }\n\n    case MessageType::Tgetlock:\n    {\n        // size[4] Tgetlock tag[2] fid[4] type[1] start[8] length[8] proc_id[4] client_id[s]\n        text.AddName(\">>Tgetlock\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto type = reader.U8();\n        text.AddField(\"type\", type);\n        auto start = reader.U64();\n        text.AddField(\"start\", start);\n        auto length = reader.U64();\n        text.AddField(\"length\", length);\n        auto proc_id = reader.U32();\n        text.AddField(\"proc_id\", proc_id);\n        auto client_id = reader.String();\n        text.AddField(\"client_id\", client_id);\n        break;\n    }\n\n    case MessageType::Rgetlock:\n    {\n        // size[4] Rgetlock tag[2] type[1] start[8] length[8] proc_id[4] client_id[s]\n        text.AddName(\"<<Rgetlock\");\n        text.AddField(\"tag\", tag);\n        auto type = reader.U8();\n        text.AddField(\"type\", type);\n        auto start = reader.U64();\n        text.AddField(\"start\", start);\n        auto length = reader.U64();\n        text.AddField(\"length\", length);\n        auto proc_id = reader.U32();\n        text.AddField(\"proc_id\", proc_id);\n        auto client_id = reader.String();\n        text.AddField(\"client_id\", client_id);\n        break;\n    }\n\n    case MessageType::Tlink:\n    {\n        // size[4] Tlink tag[2] dfid[4] fid[4] name[s]\n        text.AddName(\">>Tlink\");\n        text.AddField(\"tag\", tag);\n        auto dfid = reader.U32();\n        text.AddField(\"dfid\", dfid);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto name = reader.String();\n        text.AddField(\"name\", name);\n        break;\n    }\n\n    case MessageType::Rlink:\n    {\n        // size[4] Rlink tag[2]\n        text.AddName(\"<<Rlink\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Tmkdir:\n    {\n        // size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4]\n        text.AddName(\">>Tmkdir\");\n        text.AddField(\"tag\", tag);\n        auto dfid = reader.U32();\n        text.AddField(\"dfid\", dfid);\n        auto name = reader.String();\n        text.AddField(\"name\", name);\n        auto mode = reader.U32();\n        text.AddField(\"mode\", mode);\n        auto gid = reader.U32();\n        text.AddField(\"gid\", gid);\n        break;\n    }\n\n    case MessageType::Rmkdir:\n    {\n        // size[4] Rmkdir tag[2] qid[13]\n        text.AddName(\"<<Rmkdir\");\n        text.AddField(\"tag\", tag);\n        auto qid = reader.Qid();\n        text.AddField(\"qid\", qid);\n        break;\n    }\n\n    case MessageType::Trenameat:\n    {\n        // size[4] Trenameat tag[2] olddirfid[4] oldname[s] newdirfid[4] newname[s]\n        text.AddName(\">>Trenameat\");\n        text.AddField(\"tag\", tag);\n        auto olddirfid = reader.U32();\n        text.AddField(\"olddirfid\", olddirfid);\n        auto oldname = reader.String();\n        text.AddField(\"oldname\", oldname);\n        auto newdirfid = reader.U32();\n        text.AddField(\"newdirfid\", newdirfid);\n        auto newname = reader.String();\n        text.AddField(\"newname\", newname);\n        break;\n    }\n\n    case MessageType::Rrenameat:\n    {\n        // size[4] Rrenameat tag[2]\n        text.AddName(\"<<Rrenameat\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Tunlinkat:\n    {\n        // size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4]\n        text.AddName(\">>Tunlinkat\");\n        text.AddField(\"tag\", tag);\n        auto dirfd = reader.U32();\n        text.AddField(\"dirfd\", dirfd);\n        auto name = reader.String();\n        text.AddField(\"name\", name);\n        auto flags = reader.U32();\n        text.AddField(\"flags\", flags);\n        break;\n    }\n\n    case MessageType::Runlinkat:\n    {\n        // size[4] Runlinkat tag[2]\n        text.AddName(\"<<Runlinkat\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Taccess:\n    {\n        // size[4] Taccess tag[2] fid[4] flags[4]\n        text.AddName(\">>Taccess\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto flags = reader.U32();\n        text.AddField(\"flags\", flags);\n        break;\n    }\n\n    case MessageType::Raccess:\n    {\n        // size[4] Raccess tag[2]\n        text.AddName(\"<<Raccess\");\n        text.AddField(\"tag\", tag);\n        break;\n    }\n\n    case MessageType::Twreaddir:\n    {\n        // size[4] Twreaddir tag[2] fid[4] offset[8] count[4]\n        text.AddName(\">>Twreaddir\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto offset = reader.U64();\n        text.AddField(\"offset\", offset);\n        auto count = reader.U32();\n        text.AddField(\"count\", count);\n        break;\n    }\n\n    case MessageType::Rwreaddir:\n    {\n        // size[4] Rwreaddir tag[2] count[4] data[count]\n        text.AddName(\"<<Rwreaddir\");\n        text.AddField(\"tag\", tag);\n        auto count = reader.U32();\n        text.AddField(\"count\", count);\n        break;\n    }\n\n    case MessageType::Twopen:\n    {\n        // size[4] Twopen tag[2] fid[4] newfid[4] flags[4] wflags[4] mode[4] gid[4] attr_mask[8] nwname[2] nwname*(wname[s])\n        text.AddName(\">>Twopen\");\n        text.AddField(\"tag\", tag);\n        auto fid = reader.U32();\n        text.AddField(\"fid\", fid);\n        auto newfid = reader.U32();\n        text.AddField(\"newfid\", newfid);\n        auto flags = reader.U32();\n        text.AddField(\"flags\", flags);\n        auto wflags = reader.U32();\n        text.AddField(\"wflags\", wflags);\n        auto mode = reader.U32();\n        text.AddField(\"mode\", mode);\n        auto gid = reader.U32();\n        text.AddField(\"gid\", gid);\n        auto attr_mask = reader.U64();\n        text.AddField(\"attr_mask\", attr_mask);\n        auto nwname = reader.U16();\n        text.AddField(\"nwname\", nwname);\n        for (UINT32 i = 0; i < nwname; ++i)\n        {\n            auto wname = reader.String();\n            text.AddValue(wname);\n        }\n        break;\n    }\n\n    case MessageType::Rwopen:\n    {\n        // size[4] Rwopen tag[2] status[1] walked[2] qid[13] symlink_target[s] iounit[4] mode[4] uid[4] gid[4] nlink[8] rdev[8] size[8] blksize[8] blocks[8] atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8] gen[8] data_version[8]\n        text.AddName(\"<<Rwopen\");\n        text.AddField(\"tag\", tag);\n        auto status = reader.U8();\n        text.AddField(\"status\", status);\n        auto walked = reader.U16();\n        text.AddField(\"walked\", walked);\n        auto qid = reader.Qid();\n        text.AddField(\"qid\", qid);\n        auto symlink_target = reader.String();\n        text.AddField(\"symlink_target\", symlink_target);\n        auto iounit = reader.U32();\n        text.AddField(\"iounit\", iounit);\n        auto mode = reader.U32();\n        text.AddField(\"mode\", mode);\n        auto uid = reader.U32();\n        text.AddField(\"uid\", uid);\n        auto gid = reader.U32();\n        text.AddField(\"gid\", gid);\n        auto nlink = reader.U64();\n        text.AddField(\"nlink\", nlink);\n        auto rdev = reader.U64();\n        text.AddField(\"rdev\", rdev);\n        auto size = reader.U64();\n        text.AddField(\"size\", size);\n        auto blksize = reader.U64();\n        text.AddField(\"blksize\", blksize);\n        auto blocks = reader.U64();\n        text.AddField(\"blocks\", blocks);\n        auto atime_sec = reader.U64();\n        text.AddField(\"atime_sec\", atime_sec);\n        auto atime_nsec = reader.U64();\n        text.AddField(\"atime_nsec\", atime_nsec);\n        auto mtime_sec = reader.U64();\n        text.AddField(\"mtime_sec\", mtime_sec);\n        auto mtime_nsec = reader.U64();\n        text.AddField(\"mtime_nsec\", mtime_nsec);\n        auto ctime_sec = reader.U64();\n        text.AddField(\"ctime_sec\", ctime_sec);\n        auto ctime_nsec = reader.U64();\n        text.AddField(\"ctime_nsec\", ctime_nsec);\n        auto btime_sec = reader.U64();\n        text.AddField(\"btime_sec\", btime_sec);\n        auto btime_nsec = reader.U64();\n        text.AddField(\"btime_nsec\", btime_nsec);\n        auto gen = reader.U64();\n        text.AddField(\"gen\", gen);\n        auto data_version = reader.U64();\n        text.AddField(\"data_version\", data_version);\n        break;\n    }\n\n    default:\n    {\n        text.AddName(\"Unknown\");\n        text.AddField(\"tag\", tag);\n        text.AddField(\"type\", static_cast<UINT32>(messageType));\n        break;\n    }\n    }\n\n    Plan9TraceLoggingProvider::LogMessage(text.String());\n}\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9lx.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9lx.h\"\nusing namespace std::chrono_literals;\n\nnamespace p9fs {\n\nconstexpr auto ThreadPoolTimeout = 10s;\n\nThreadPool g_ThreadPool;\n\n// Create a socket class with a socket fd.\nSocket::Socket(int socket) : m_Io{g_Watcher}\n{\n    Reset(socket);\n}\n\n// Asynchronously wait for new connections.\nTask<std::unique_ptr<ISocket>> Socket::AcceptAsync(CancelToken& token)\n{\n    int socket = co_await p9fs::AcceptAsync(m_Io, token);\n    co_return std::make_unique<Socket>(socket);\n}\n\n// Asynchronously receive data.\nTask<size_t> Socket::RecvAsync(gsl::span<gsl::byte> buffer, CancelToken& token)\n{\n    return p9fs::RecvAsync(m_Io, buffer, token);\n}\n\n// Asynchronously send data.\nTask<size_t> Socket::SendAsync(gsl::span<const gsl::byte> buffer, CancelToken& token)\n{\n    size_t totalSent{};\n    do\n    {\n        totalSent += co_await p9fs::SendAsync(m_Io, buffer.subspan(totalSent), token);\n    } while (totalSent < buffer.size());\n\n    co_return totalSent;\n}\n\nvoid Socket::Reset(int socket)\n{\n    m_Io.Reset(socket);\n    m_Socket.reset(socket);\n}\n\n// Create a new work item for a specific callback.\nWorkItem::WorkItem(std::function<void()> callback) : m_Callback{callback}\n{\n}\n\n// Submit the work item to the thread pool.\nvoid WorkItem::Submit()\n{\n    g_ThreadPool.SubmitWork(m_Callback);\n}\n\n// Create a new work item for a specific callback.\nstd::unique_ptr<IWorkItem> CreateWorkItem(std::function<void()> callback)\n{\n    return std::make_unique<WorkItem>(callback);\n}\n\n// Create a new thread pool.\nThreadPool::ThreadPool() : m_MaxThreads{std::thread::hardware_concurrency()}\n{\n}\n\n// Submit work to the thread pool.\nvoid ThreadPool::SubmitWork(std::function<void()> callback)\n{\n    std::lock_guard<std::mutex> lock{m_Lock};\n    m_WorkQueue.push(callback);\n    // If there are no threads to run the work right now, and it's not at the\n    // max, start a new thread.\n    if (m_AvailableThreads == 0 && m_RunningThreads < m_MaxThreads)\n    {\n        ++m_RunningThreads;\n        std::thread(&ThreadPool::WorkerCallback, this).detach();\n    }\n    else\n    {\n        m_Condition.notify_one();\n    }\n}\n\n// Runs a worker thread that executes queued work items.\nvoid ThreadPool::WorkerCallback()\n{\n    std::unique_lock<std::mutex> lock{m_Lock, std::defer_lock};\n    for (;;)\n    {\n        lock.lock();\n        ++m_AvailableThreads;\n        // Wait for work.\n        if (m_WorkQueue.empty() && !m_Condition.wait_for(lock, ThreadPoolTimeout, [this]() { return !m_WorkQueue.empty(); }))\n        {\n            // The wait timed out, so shut down this thread.\n            --m_AvailableThreads;\n            --m_RunningThreads;\n            return;\n        }\n\n        // Take ownership of the work item.\n        --m_AvailableThreads;\n        auto work = m_WorkQueue.front();\n        m_WorkQueue.pop();\n        lock.unlock();\n\n        // Run the work outside the lock.\n        work();\n    }\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9lx.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9platform.h\"\n#include \"p9io.h\"\n\nnamespace p9fs {\n\nclass Socket final : public ISocket\n{\npublic:\n    Socket(int socket = -1);\n\n    Task<std::unique_ptr<ISocket>> AcceptAsync(CancelToken& token) override;\n    Task<size_t> RecvAsync(gsl::span<gsl::byte> buffer, CancelToken& token) override;\n    Task<size_t> SendAsync(gsl::span<const gsl::byte> buffer, CancelToken& token) override;\n    void Reset(int socket = -1);\n\nprivate:\n    wil::unique_fd m_Socket;\n    CoroutineEpollIssuer m_Io;\n};\n\nclass WorkItem final : public IWorkItem\n{\npublic:\n    WorkItem(std::function<void()> callback);\n\n    void Submit() override;\n\nprivate:\n    std::function<void()> m_Callback;\n};\n\nclass ThreadPool final\n{\npublic:\n    ThreadPool();\n\n    void SubmitWork(std::function<void()> callback);\n\nprivate:\n    void WorkerCallback();\n\n    std::queue<std::function<void()>> m_WorkQueue;\n    std::mutex m_Lock;\n    std::condition_variable m_Condition;\n    unsigned int m_MaxThreads{};\n    unsigned int m_AvailableThreads{};\n    unsigned int m_RunningThreads{};\n};\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9platform.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9await.h\"\n\nnamespace p9fs {\n\n// Platform-independent wrapper around socket operations.\nclass ISocket\n{\npublic:\n    virtual ~ISocket() = default;\n\n    virtual Task<std::unique_ptr<ISocket>> AcceptAsync(CancelToken& token) = 0;\n    virtual Task<size_t> RecvAsync(gsl::span<gsl::byte> buffer, CancelToken& token) = 0;\n    virtual Task<size_t> SendAsync(gsl::span<const gsl::byte> buffer, CancelToken& token) = 0;\n};\n\n// Platform-independent wrapper around threadpool work\nclass IWorkItem\n{\npublic:\n    virtual ~IWorkItem() = default;\n\n    virtual void Submit() = 0;\n};\n\nstd::unique_ptr<IWorkItem> CreateWorkItem(std::function<void()> callback);\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9protohelpers.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9defs.h\"\n\nnamespace p9fs {\n\nstruct DirectoryEntry\n{\n    Qid Qid;\n    UINT64 Offset;\n    UINT8 Type;\n    std::string_view Name;\n};\n\ntemplate <typename T>\nstruct ReadResult\n{\n    T Result;\n    bool Success;\n};\n\n// Class to read elements from a 9pfs protocol buffer.\n// TODO: Need ways to read that don't fail fast but return errors.\nclass SpanReader\n{\npublic:\n    SpanReader() = default;\n\n    SpanReader(gsl::span<const gsl::byte> message) : m_Message(message)\n    {\n    }\n\n    gsl::span<const gsl::byte> Read(unsigned int count)\n    {\n        if (m_Message.size() - m_Offset < count)\n        {\n            FAIL_FAST();\n        }\n\n        auto result = m_Message.subspan(m_Offset, count);\n        m_Offset += count;\n        return result;\n    }\n\n    ReadResult<gsl::span<const gsl::byte>> TryRead(unsigned int count)\n    {\n        if (m_Message.size() - m_Offset < count)\n        {\n            return {};\n        }\n\n        auto result = m_Message.subspan(m_Offset, count);\n        m_Offset += count;\n        return {result, true};\n    }\n\n    UINT8 U8()\n    {\n        return ReadFixed<UINT8>();\n    }\n\n    UINT16 U16()\n    {\n        return ReadFixed<UINT16>();\n    }\n\n    UINT32 U32()\n    {\n        return ReadFixed<UINT32>();\n    }\n\n    UINT64 U64()\n    {\n        return ReadFixed<UINT64>();\n    }\n\n    ReadResult<UINT8> TryU8()\n    {\n        return TryReadFixed<UINT8>();\n    }\n\n    ReadResult<UINT16> TryU16()\n    {\n        return TryReadFixed<UINT16>();\n    }\n\n    ReadResult<UINT32> TryU32()\n    {\n        return TryReadFixed<UINT32>();\n    }\n\n    ReadResult<UINT64> TryU64()\n    {\n        return TryReadFixed<UINT64>();\n    }\n\n    Qid Qid()\n    {\n        p9fs::Qid result;\n        result.Type = static_cast<QidType>(U8());\n        result.Version = U32();\n        result.Path = U64();\n        return result;\n    }\n\n    ReadResult<p9fs::Qid> TryQid()\n    {\n        if (m_Message.size() - m_Offset < QidSize)\n        {\n            return {};\n        }\n\n        return {Qid(), true};\n    }\n\n    std::string_view String()\n    {\n        const auto length = U16();\n        auto s = Read(length);\n        return FixString(s);\n    }\n\n    ReadResult<std::string_view> TryString()\n    {\n        const auto length = TryU16();\n        if (!length.Success)\n        {\n            return {};\n        }\n\n        auto s = TryRead(length.Result);\n        if (!s.Success)\n        {\n            return {};\n        }\n\n        return {FixString(s.Result), true};\n    }\n\n#ifndef GSL_KERNEL_MODE\n\n    std::string_view Name()\n    {\n        auto s = String();\n        if (s.size() == 0 || s == \".\" || s == \"..\" || std::find_if(s.begin(), s.end(), [](char c) { return c == '/'; }) != s.end())\n        {\n            THROW_INVALID();\n        }\n\n        return s;\n    }\n\n#endif\n\n    ReadResult<DirectoryEntry> TryDirectoryEntry()\n    {\n        // Check if the data is large enough for the fixed part.\n        if (m_Message.size() - m_Offset < QidSize + sizeof(UINT64) + sizeof(UINT8) + sizeof(UINT16))\n        {\n            return {};\n        }\n\n        DirectoryEntry result;\n        result.Qid = Qid();\n        result.Offset = U64();\n        result.Type = U8();\n        auto name = TryString();\n        if (!name.Success)\n        {\n            return {};\n        }\n\n        result.Name = name.Result;\n        return {result, true};\n    }\n\n    StatResult ReadStatResult()\n    {\n        StatResult attr;\n        attr.Mode = U32();\n        attr.Uid = U32();\n        attr.Gid = U32();\n        attr.NLink = U64();\n        attr.RDev = U64();\n        attr.Size = U64();\n        attr.BlockSize = U64();\n        attr.Blocks = U64();\n        attr.AtimeSec = U64();\n        attr.AtimeNsec = U64();\n        attr.MtimeSec = U64();\n        attr.MtimeNsec = U64();\n        attr.CtimeSec = U64();\n        attr.CtimeNsec = U64();\n        return attr;\n    }\n\n    ReadResult<StatResult> TryStatResult()\n    {\n        // Check if the data is large enough.\n        if (m_Message.size() - m_Offset < StatResultSize)\n        {\n            return {};\n        }\n\n        return {ReadStatResult(), true};\n    }\n\n    size_t Size() const\n    {\n        return m_Message.size();\n    }\n\n    size_t Offset() const\n    {\n        return m_Offset;\n    }\n\n    gsl::span<const gsl::byte> ReadToEnd()\n    {\n        return Read(static_cast<unsigned int>(m_Message.size() - m_Offset));\n    }\n\n    gsl::span<const gsl::byte> Span() const\n    {\n        return m_Message;\n    }\n\nprivate:\n    template <class T>\n    T ReadFixed()\n    {\n        auto s = Read(sizeof(T));\n        return *reinterpret_cast<const T*>(s.data());\n    }\n\n    template <typename T>\n    ReadResult<T> TryReadFixed()\n    {\n        auto s = TryRead(sizeof(T));\n        if (!s.Success)\n        {\n            return {};\n        }\n\n        return {*reinterpret_cast<const T*>(s.Result.data()), true};\n    }\n\n    std::string_view FixString(gsl::span<const gsl::byte> s)\n    {\n        auto string = std::string_view{reinterpret_cast<const char*>(s.data()), static_cast<std::string_view::size_type>(s.size())};\n\n        //\n        // Check for internal nul characters.\n        //\n        std::string_view::size_type strlength = 0;\n        while (strlength < string.size() && string[strlength] != 0)\n        {\n            strlength++;\n        }\n\n        return string.substr(0, strlength);\n    }\n\n    gsl::span<const gsl::byte> m_Message;\n    size_t m_Offset{};\n};\n\n// Class to write elements to a 9pfs protocol buffer.\nclass SpanWriter\n{\npublic:\n    SpanWriter(gsl::span<gsl::byte> message) : Message(message)\n    {\n    }\n\n    void U8(UINT8 value)\n    {\n        WriteFixed(value);\n    }\n\n    void U16(UINT16 value)\n    {\n        WriteFixed(value);\n    }\n\n    void U32(UINT32 value)\n    {\n        WriteFixed(value);\n    }\n\n    void U64(UINT64 value)\n    {\n        WriteFixed(value);\n    }\n\n    void Qid(const Qid& value)\n    {\n        U8(static_cast<UINT8>(value.Type));\n        U32(value.Version);\n        U64(value.Path);\n    }\n\n    void String(std::string_view value)\n    {\n        if (value.size() > UINT16_MAX)\n        {\n            FAIL_FAST();\n        }\n\n        U16(static_cast<UINT16>(value.size()));\n        auto s = Next(value.size());\n        auto bytes = gsl::as_bytes(gsl::make_span(value.data(), value.size()));\n        gsl::copy(bytes, s);\n    }\n\n    gsl::span<gsl::byte> Result()\n    {\n        return Message.subspan(0, Offset);\n    }\n\n    size_t Size() const\n    {\n        return Offset;\n    }\n\n    size_t MaxSize() const\n    {\n        return Message.size();\n    }\n\n    gsl::span<gsl::byte> Peek()\n    {\n        return Message.subspan(Offset);\n    }\n\n    gsl::span<gsl::byte> Peek(size_t Count)\n    {\n        return Message.subspan(Offset, Count);\n    }\n\n    gsl::span<gsl::byte> Next(size_t Count)\n    {\n        auto s = Peek(Count);\n        Offset += Count;\n        return s;\n    }\n\n    void Header(MessageType messageType, UINT16 tag) const\n    {\n        SpanWriter headerWriter{Message.subspan(0, HeaderSize)};\n        headerWriter.U32(static_cast<UINT32>(Offset));\n        headerWriter.U8(static_cast<UINT8>(messageType));\n        headerWriter.U16(tag);\n    }\n\n    void Write(gsl::span<const gsl::byte> buffer)\n    {\n        gsl::copy(buffer, Next(buffer.size()));\n    }\n\nprivate:\n    template <class T>\n    void WriteFixed(T value)\n    {\n        auto s = Next(sizeof(value));\n        *reinterpret_cast<T*>(s.data()) = value;\n    }\n\n    gsl::span<gsl::byte> Message;\n    size_t Offset{};\n};\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9readdir.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9readdir.h\"\n\nnamespace p9fs {\n\n// Creates a new directory enumerator.\n// N.B. If successful, this takes ownership of the specified fd.\nDirectoryEnumerator::DirectoryEnumerator(int fd) : m_Dir{fdopendir(fd)}\n{\n    THROW_LAST_ERROR_IF(m_Dir == nullptr);\n}\n\n// Destructs the directory enumerator, closing the directory object and fd.\nDirectoryEnumerator::~DirectoryEnumerator()\n{\n    if (m_Dir != nullptr)\n    {\n        closedir(m_Dir);\n    }\n}\n\nstruct dirent* DirectoryEnumerator::Next()\n{\n    errno = 0;\n    auto result = readdir(m_Dir);\n    if (result == nullptr)\n    {\n        // If errno is still 0, it means EOF is reached which is not an error.\n        THROW_LAST_ERROR_IF(errno != 0)\n    }\n    else\n    {\n        m_LastOffset = result->d_off;\n    }\n\n    return result;\n}\n\nvoid DirectoryEnumerator::Seek(long offset)\n{\n    // If the offset hasn't changed, continue enumeration and avoid having to\n    // refill the buffer.\n    if (offset != m_LastOffset)\n    {\n        if (offset == 0)\n        {\n            rewinddir(m_Dir);\n        }\n        else\n        {\n            seekdir(m_Dir, offset);\n        }\n\n        m_LastOffset = offset;\n    }\n}\n\nint DirectoryEnumerator::Fd()\n{\n    int fd = dirfd(m_Dir);\n    THROW_LAST_ERROR_IF(fd < 0);\n    return fd;\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9readdir.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nnamespace p9fs {\n\nclass DirectoryEnumerator final\n{\npublic:\n    DirectoryEnumerator(int fd);\n    ~DirectoryEnumerator();\n\n    struct dirent* Next();\n    void Seek(long offset);\n    int Fd();\n\nprivate:\n    DIR* m_Dir{};\n    long m_LastOffset{};\n};\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9scheduler.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9platform.h\"\n#include \"p9scheduler.h\"\n\nnamespace p9fs {\n\nScheduler g_Scheduler;\nthread_local bool Scheduler::tls_Blocked{};\nthread_local bool Scheduler::tls_SchedulerThread{};\n\nScheduler::Scheduler() : m_Work{CreateWorkItem(std::bind(&Scheduler::WorkerCallback, this))}\n{\n}\n\n/// Schedules a coroutine to run. It will run sometime after this coroutine\n/// yields or enters a blocking region.\nvoid Scheduler::Schedule(Coroutine coroutine) noexcept\n{\n    bool kick = false;\n\n    {\n        std::unique_lock<std::shared_mutex> lock(m_Lock);\n\n        // N.B. This could throw in very low memory situations, which would terminate the process.\n        m_Queue.push(coroutine);\n        if (!m_Running && !m_ThreadEnqueued)\n        {\n            m_ThreadEnqueued = true;\n            kick = true;\n        }\n    }\n\n    if (kick)\n    {\n        m_Work->Submit();\n    }\n}\n\n/// Donates the current thread to run coroutines and schedules the specified\n/// coroutine to run.\nvoid Scheduler::DonateThreadAndResume(Coroutine coroutine) noexcept\n{\n    const bool run = Claim(false);\n    Schedule(coroutine);\n    if (run)\n    {\n        RunAndRelease();\n    }\n}\n\n/// Runs coroutines until there are no more in the queue or until this thread\n/// gave up the queue in order to run blocking code.\n///\n/// Must be called on the thread that called Claim().\nvoid Scheduler::RunAndRelease() noexcept\n{\n    WI_ASSERT(!tls_Blocked);\n\n    tls_SchedulerThread = true;\n    std::unique_lock<std::shared_mutex> lock(m_Lock);\n    while (!m_Queue.empty())\n    {\n        auto coroutine = m_Queue.front();\n        m_Queue.pop();\n        lock.unlock();\n        coroutine.resume();\n        if (tls_Blocked)\n        {\n            tls_Blocked = false;\n            tls_SchedulerThread = false;\n            return;\n        }\n\n        lock.lock();\n    }\n\n    WI_ASSERT(m_Queue.empty() || m_Running || m_ThreadEnqueued);\n\n    m_Running = false;\n    tls_SchedulerThread = false;\n}\n\n/// Called when the current thread may block for some time. Gives up queue\n/// ownership, potentially scheduling another thread to resume running\n/// non-blocking code.\nbool Scheduler::Block() noexcept\n{\n    if (!tls_SchedulerThread)\n    {\n        return false;\n    }\n\n    WI_ASSERT(!tls_Blocked);\n\n    tls_Blocked = true;\n\n    bool kick = false;\n\n    {\n        std::unique_lock<std::shared_mutex> lock(m_Lock);\n\n        WI_ASSERT(m_Running);\n\n        m_Running = false;\n        if (!m_Queue.empty() && !m_ThreadEnqueued)\n        {\n            m_ThreadEnqueued = true;\n            kick = true;\n        }\n    }\n\n    if (kick)\n    {\n        m_Work->Submit();\n    }\n\n    return true;\n}\n\n/// Awaitable function called when the current thread is done running blocking\n/// code. Tries to reclaim ownership of the queue and resumes the current\n/// coroutine.\nScheduler::Unblocker Scheduler::Unblock() noexcept\n{\n    WI_ASSERT(tls_Blocked);\n\n    // Try to reuse this thread to run async tasks.\n    const bool run = Claim(false);\n    if (run)\n    {\n        tls_Blocked = false;\n    }\n\n    // Unblocker will either resume the current coroutine or schedule it to run\n    // on the new queue owner.\n    return Unblocker{*this, run};\n}\n\n/// Try to claim queue ownership for the current thread. If this function\n/// returns true, then the caller must call RunAndRelease to process the queue.\n///\n/// If fromKick, then the caller is the thread that was explicitly kicked to\n/// process the queue. Otherwise, this is an IO completion or other\n/// opportunistic thread.\nbool Scheduler::Claim(bool fromKick) noexcept\n{\n    std::unique_lock<std::shared_mutex> lock(m_Lock);\n\n    WI_ASSERT(!fromKick || m_ThreadEnqueued);\n\n    if (fromKick)\n    {\n        m_ThreadEnqueued = false;\n    }\n\n    if (m_Running)\n    {\n        return false;\n    }\n\n    m_Running = true;\n    return true;\n}\n\n/// Threadpool callback called to process the queue.\nvoid Scheduler::WorkerCallback() noexcept\n{\n    if (Claim(true))\n    {\n        RunAndRelease();\n    }\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9scheduler.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nnamespace p9fs {\n\nclass IWorkItem;\n\nclass Scheduler\n{\npublic:\n    using Coroutine = std::coroutine_handle<>;\n\n    struct Unblocker\n    {\n        Scheduler& m_Scheduler;\n        bool m_Run{};\n\n        bool await_ready() const\n        {\n            return m_Run;\n        }\n\n        void await_suspend(Coroutine handle)\n        {\n            m_Scheduler.Schedule(handle);\n        }\n\n        static void await_resume()\n        {\n        }\n    };\n\n    Scheduler();\n    void Schedule(Coroutine coroutine) noexcept;\n    void DonateThreadAndResume(Coroutine coroutine) noexcept;\n    bool Block() noexcept;\n    struct Unblocker Unblock() noexcept;\n\nprivate:\n    void RunAndRelease() noexcept;\n    bool Claim(bool owner) noexcept;\n    void WorkerCallback() noexcept;\n\n    std::shared_mutex m_Lock;\n    std::queue<Coroutine> m_Queue;\n    std::unique_ptr<IWorkItem> m_Work;\n    bool m_Running;\n    bool m_ThreadEnqueued;\n    static thread_local bool tls_Blocked;\n    static thread_local bool tls_SchedulerThread;\n};\n\nextern Scheduler g_Scheduler;\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9tracelogging.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9defs.h\"\n#include \"p9tracelogging.h\"\n#include \"p9tracelogginghelper.h\"\n\nnamespace {\n\nconst char* c_levelLabels[] = {\": CRITICAL: \", \": ERROR:    \", \": WARNING:  \", \": INFO:     \", \": VERBOSE:  \"};\n\nconstexpr int c_levelLabelLength = 12;\n\n} // namespace\n\nnamespace p9fs {\n\nint Plan9TraceLoggingProvider::m_level{TRACE_LEVEL_ERROR};\nint Plan9TraceLoggingProvider::m_log{-1};\n\nconstexpr int c_numberBufferSize = 64;\n\n// Helper to convert unsigned numbers without the use of printf or iostream.\nstd::string_view ConvertNumber(char* buffer, int bufferSize, UINT64 value, int base = 10, int minWidth = 0)\n{\n    if (value == 0)\n    {\n        return \"0\";\n    }\n\n    auto* characters = \"0123456789abcdef\";\n    int index;\n    for (index = bufferSize - 1; index > 0 && value > 0; --index, value /= base)\n    {\n        buffer[index] = characters[value % base];\n    }\n\n    for (; index > 0 && c_numberBufferSize - (index + 1) < minWidth; --index)\n    {\n        buffer[index] = '0';\n    }\n\n    if (base == 16 && index > 1)\n    {\n        buffer[index--] = 'x';\n        buffer[index--] = '0';\n    }\n\n    index += 1;\n    return {&buffer[index], c_numberBufferSize - index};\n}\n\n// Sets the file descriptor to log to.\nvoid Plan9TraceLoggingProvider::SetLogFileDescriptor(int fd)\n{\n    m_log = fd;\n}\n\n// Checks whether logging is enabled for messages of the specified level.\nbool Plan9TraceLoggingProvider::IsEnabled(int level)\n{\n    return m_log >= 0 && level <= m_level;\n}\n\n// Sets the current logging level.\nvoid Plan9TraceLoggingProvider::SetLevel(int level)\n{\n    m_level = level;\n}\n\n// Logs a message at the specified level.\nvoid Plan9TraceLoggingProvider::LogMessage(const char* message, int level)\n{\n    if (!IsEnabled(level))\n    {\n        return;\n    }\n\n    timespec timestamp;\n    clock_gettime(CLOCK_MONOTONIC, &timestamp);\n    char secondsBuffer[c_numberBufferSize];\n    auto seconds = ConvertNumber(secondsBuffer, c_numberBufferSize, timestamp.tv_sec);\n    char nsecondsBuffer[c_numberBufferSize];\n    auto nseconds = ConvertNumber(nsecondsBuffer, c_numberBufferSize, timestamp.tv_nsec, 10, 9);\n    if (level < 1)\n    {\n        level = 1;\n    }\n    else if (level > TRACE_LEVEL_VERBOSE)\n    {\n        level = TRACE_LEVEL_VERBOSE;\n    }\n\n    // Use writev to ensure the message is written atomically with all its parts.\n    iovec buffers[6];\n    buffers[0].iov_base = const_cast<char*>(seconds.data());\n    buffers[0].iov_len = seconds.size();\n    buffers[1].iov_base = const_cast<char*>(\".\");\n    buffers[1].iov_len = 1;\n    buffers[2].iov_base = const_cast<char*>(nseconds.data());\n    buffers[2].iov_len = nseconds.size();\n    buffers[3].iov_base = const_cast<char*>(c_levelLabels[level - 1]);\n    buffers[3].iov_len = c_levelLabelLength;\n    buffers[4].iov_base = const_cast<char*>(message);\n    buffers[4].iov_len = strlen(message);\n    buffers[5].iov_base = const_cast<char*>(\"\\n\");\n    buffers[5].iov_len = 1;\n    writev(m_log, buffers, std::extent<decltype(buffers)>::value);\n}\n\n// Logs a message at the specified level.\nvoid Plan9TraceLoggingProvider::LogMessage(const std::string& message, int level)\n{\n    LogMessage(message.c_str(), level);\n}\n\n// Logs an exception with an optional additional message to the output.\nvoid Plan9TraceLoggingProvider::LogException(const char* message, const char* exceptionDescription, int level)\n{\n    if (!IsEnabled(level))\n    {\n        return;\n    }\n\n    std::string logMessage;\n    if (message != nullptr)\n    {\n        logMessage += message;\n        if (exceptionDescription != nullptr)\n        {\n            logMessage += \" \";\n        }\n    }\n\n    if (exceptionDescription != nullptr)\n    {\n        logMessage += \"Exception: \";\n        logMessage += exceptionDescription;\n    }\n\n    LogMessage(logMessage.c_str(), level);\n}\n\n// Logs a message that the server has started.\nvoid Plan9TraceLoggingProvider::ServerStart()\n{\n    LogMessage(\"Server started.\", TRACE_LEVEL_INFORMATION);\n}\n\n// Logs a message that the server has stopped.\nvoid Plan9TraceLoggingProvider::ServerStop()\n{\n    LogMessage(\"Server stopped.\", TRACE_LEVEL_INFORMATION);\n}\n\n// Logs a message that the server has accepted a connection.\nvoid Plan9TraceLoggingProvider::AcceptedConnection()\n{\n    LogMessage(\"Accepted connection.\", TRACE_LEVEL_INFORMATION);\n}\n\n// Logs a message that a connection was disconnected.\nvoid Plan9TraceLoggingProvider::ConnectionDisconnected()\n{\n    LogMessage(\"Connection disconnected.\", TRACE_LEVEL_INFORMATION);\n}\n\n// Logs a message that the server has rejected a connection attempt because there are too many\n// active connections.\nvoid Plan9TraceLoggingProvider::TooManyConnections()\n{\n    LogMessage(\"Too many connections.\", TRACE_LEVEL_ERROR);\n}\n\n// Logs a message indicating that the buffer provided by the virtio transport for the response is\n// too small.\nvoid Plan9TraceLoggingProvider::InvalidResponseBufferSize()\n{\n    LogMessage(\"Invalid response buffer size.\", TRACE_LEVEL_ERROR);\n}\n\n// A socket has been accepted\nvoid Plan9TraceLoggingProvider::PreAccept()\n{\n    LogMessage(\"PreAccept\", TRACE_LEVEL_VERBOSE);\n}\n\n// A socket has been closed\nvoid Plan9TraceLoggingProvider::PostAccept()\n{\n    LogMessage(\"PostAccept\", TRACE_LEVEL_INFORMATION);\n}\n\n// An accept operation has been aborted\nvoid Plan9TraceLoggingProvider::OperationAborted()\n{\n    LogMessage(\"OperationAborted\", TRACE_LEVEL_VERBOSE);\n}\n\n// A client connected\nvoid Plan9TraceLoggingProvider::ClientConnected(unsigned int connectionCount)\n{\n    LogMessage(std::format(\"ClientConnected, connectionCount={}\", connectionCount), TRACE_LEVEL_VERBOSE);\n}\n\n// A client disconnected\nvoid Plan9TraceLoggingProvider::ClientDisconnected(unsigned int connectionCount)\n{\n    LogMessage(std::format(\"ClientDisconnected, connectionCount={}\", connectionCount), TRACE_LEVEL_VERBOSE);\n}\n\n// Adds the message name to the log message.\n// N.B. This should be the first call on a new LogMessageBuilder.\nvoid LogMessageBuilder::AddName(std::string_view name)\n{\n    m_message += name;\n}\n\n// Adds a string field to the message.\nvoid LogMessageBuilder::AddField(std::string_view name, std::string_view value)\n{\n    AddFieldName(name);\n    AddRawValue(value);\n}\n\n// Adds an unsigned integer field to the message.\nvoid LogMessageBuilder::AddField(std::string_view name, UINT64 value, int base)\n{\n    AddFieldName(name);\n    AddRawValue(value, base);\n}\n\n// Adds a qid field to the message.\nvoid LogMessageBuilder::AddField(std::string_view name, const Qid& value)\n{\n    AddFieldName(name);\n    AddRawValue(value);\n}\n\n// Adds a string value to the message.\nvoid LogMessageBuilder::AddValue(std::string_view value)\n{\n    m_message += \" \";\n    AddRawValue(value);\n}\n\n// Adds a qid value to the message.\nvoid LogMessageBuilder::AddValue(const Qid& value)\n{\n    m_message += \" \";\n    AddRawValue(value);\n}\n\n// Returns the message text as a string.\nconst char* LogMessageBuilder::String() const\n{\n    return m_message.c_str();\n}\n\n// Adds the name of the field, including separators.\nvoid LogMessageBuilder::AddFieldName(std::string_view name)\n{\n    m_message += \" \";\n    m_message += name;\n    m_message += \"=\";\n}\n\n// Adds an unsigned integer value without any separators or prefix.\nvoid LogMessageBuilder::AddRawValue(UINT64 value, int base)\n{\n    char buffer[c_numberBufferSize]{};\n    m_message += ConvertNumber(buffer, c_numberBufferSize, value, base);\n}\n\n// Adds a qid value without any separators or prefix.\nvoid LogMessageBuilder::AddRawValue(const Qid& value)\n{\n    m_message += \"{\";\n    AddRawValue(static_cast<UINT32>(value.Type), 16);\n    m_message += \",\";\n    AddRawValue(value.Version);\n    m_message += \",\";\n    AddRawValue(value.Path);\n    m_message += \"}\";\n}\n\n// Adds a string value without any separators of prefix.\n// N.B. This function does adds quotes surrounding the string.\nvoid LogMessageBuilder::AddRawValue(std::string_view value)\n{\n    m_message += \"\\\"\";\n    m_message.append(value.data(), value.size());\n    m_message += \"\\\"\";\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9tracelogging.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <string>\n\n// Trace-logging levels that match the levels used by Windows levels.\n#define TRACE_LEVEL_NONE 0        // Tracing is not on\n#define TRACE_LEVEL_CRITICAL 1    // Abnormal exit or termination\n#define TRACE_LEVEL_ERROR 2       // Severe errors that need logging\n#define TRACE_LEVEL_WARNING 3     // Warnings such as allocation failure\n#define TRACE_LEVEL_INFORMATION 4 // Includes non-error cases(e.g.,Entry-Exit)\n#define TRACE_LEVEL_VERBOSE 5     // Detailed traces from intermediate steps\n\n#if defined(__cplusplus)\n\nnamespace p9fs {\n\n// Tracelogging class that has similar methods as its Windows counterpart to simple log statements\n// in the cross-platform code will work.\nclass Plan9TraceLoggingProvider\n{\npublic:\n    static void SetLogFileDescriptor(int fd);\n    static bool IsEnabled(int level);\n    static void SetLevel(int level);\n    static void LogMessage(const char* message, int level = TRACE_LEVEL_VERBOSE);\n    static void LogMessage(const std::string& message, int level = TRACE_LEVEL_VERBOSE);\n    static void LogException(const char* message, const char* exceptionDescription, int level = TRACE_LEVEL_ERROR);\n    static void ServerStart();\n    static void ServerStop();\n    static void AcceptedConnection();\n    static void ConnectionDisconnected();\n    static void TooManyConnections();\n    static void InvalidResponseBufferSize();\n    static void PreAccept();\n    static void PostAccept();\n    static void OperationAborted();\n    static void ClientConnected(unsigned int connectionCount);\n    static void ClientDisconnected(unsigned int connectionCount);\n\nprivate:\n    Plan9TraceLoggingProvider() = delete;\n\n    static int m_level;\n    static int m_log;\n};\n\n} // namespace p9fs\n\n#endif\n"
  },
  {
    "path": "src/linux/plan9/p9tracelogginghelper.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\nnamespace p9fs {\n\n// Class to help construct tracelogging messages for verbose logging of server traffic.\n// N.B. To avoid needlessly increasing the size of the statically linked WSL init binary, this\n//      helper allows for constructing log messages without the use of printf or iostream.\nclass LogMessageBuilder\n{\npublic:\n    void AddName(std::string_view name);\n    void AddField(std::string_view name, std::string_view value);\n    void AddField(std::string_view name, UINT64 value, int base = 10);\n    void AddField(std::string_view name, const Qid& value);\n    void AddValue(std::string_view value);\n    void AddValue(const Qid& qid);\n\n    const char* String() const;\n\nprivate:\n    void AddFieldName(std::string_view name);\n    void AddRawValue(UINT64 value, int base = 10);\n    void AddRawValue(const Qid& value);\n    void AddRawValue(std::string_view value);\n\n    std::string m_message;\n};\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9util.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9util.h\"\n#include <pwd.h>\n#include <grp.h>\n#include <syscall.h>\n\n#define _LINUX_CAPABILITY_VERSION_3 0x20080522\n#define CAP_FOWNER 3\n#define CAP_TO_INDEX(Cap) ((Cap) >> 5)\n#define CAP_TO_MASK(Cap) (1 << ((Cap) & 31))\n\nstruct cap_user_header_t\n{\n    std::uint32_t version;\n    pid_t pid;\n};\n\nstruct cap_user_data_t\n{\n    std::uint32_t effective;\n    std::uint32_t permitted;\n    std::uint32_t inheritable;\n};\n\ninline int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)\n{\n    return syscall(SYS_setresuid, ruid, euid, suid);\n}\n\ninline int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)\n{\n    return syscall(SYS_setresgid, rgid, egid, sgid);\n}\n\ninline int sys_faccessat(int dirFd, const char* pathName, int mode, int flags)\n{\n    return syscall(SYS_faccessat, dirFd, pathName, mode, flags);\n}\n\ninline int sys_setgroups(size_t size, const gid_t* list)\n{\n    return syscall(SYS_setgroups, size, list);\n}\n\nconstexpr long c_PasswordFileBufferSize = 1024;\n\nnamespace p9fs::util {\n\nExpected<wil::unique_fd> OpenAt(int dirfd, const std::string& name, int openFlags, mode_t mode)\n{\n    if (name.length() == 0)\n    {\n        return Reopen(dirfd, openFlags);\n    }\n\n    int fd = openat(dirfd, name.c_str(), openFlags | O_CLOEXEC, mode);\n    if (fd < 0)\n    {\n        return LxError{-errno};\n    }\n\n    return wil::unique_fd{fd};\n}\n\nExpected<wil::unique_fd> Reopen(int fd, int openFlags)\n{\n    const char* pathToOpen;\n    std::string targetPath;\n    char fdPath[PATH_MAX];\n\n    // If O_NOFOLLOW is set, open the target of the link in proc directly,\n    // otherwise the call will always fail.\n    if (WI_IsFlagSet(openFlags, O_NOFOLLOW))\n    {\n        targetPath = GetFdPath(fd);\n        pathToOpen = targetPath.c_str();\n    }\n    else\n    {\n        snprintf(fdPath, sizeof(fdPath), \"/proc/self/fd/%d\", fd);\n        pathToOpen = fdPath;\n    }\n\n    int newFd = open(pathToOpen, openFlags | O_CLOEXEC);\n    if (newFd < 0)\n    {\n        return LxError{-errno};\n    }\n\n    return wil::unique_fd{newFd};\n}\n\nstd::string GetFdPath(int fd)\n{\n    char fdPath[PATH_MAX]{};\n    snprintf(fdPath, sizeof(fdPath), \"/proc/self/fd/%d\", fd);\n    char target[PATH_MAX]{};\n    const int result = readlink(fdPath, target, sizeof(target));\n    THROW_LAST_ERROR_IF(result < 0);\n    return {target, static_cast<size_t>(result)};\n}\n\nLX_INT LinuxErrorFromCaughtException()\n{\n    return -wil::ResultFromCaughtException();\n}\n\nLX_INT AccessHelper(int fd, const std::string& path, int mode)\n{\n    const char* pathToCheck = path.c_str();\n    std::string fdPath;\n    if (path.length() == 0)\n    {\n        // AT_EMPTY_PATH is not supported by faccessat, so get the full path\n        // to the target if an access check is to be performed on the specified\n        // directory.\n        fdPath = GetFdPath(fd);\n        pathToCheck = fdPath.c_str();\n    }\n\n    // The musl wrapper incorrectly blocks AT_SYMLINK_NOFOLLOW, so call the syscall directly.\n    int result = sys_faccessat(fd, pathToCheck, mode, AT_SYMLINK_NOFOLLOW | AT_EACCESS);\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    return {};\n}\n\nLX_INT CheckFOwnerCapability()\n{\n    cap_user_header_t header{};\n    cap_user_data_t data[2]{};\n    header.version = _LINUX_CAPABILITY_VERSION_3;\n    int result = syscall(SYS_capget, &header, data);\n    if (result < 0)\n    {\n        return -errno;\n    }\n\n    if (WI_IsFlagSet(data[CAP_TO_INDEX(CAP_FOWNER)].effective, CAP_TO_MASK(CAP_FOWNER)))\n    {\n        return {};\n    }\n\n    return LX_EPERM;\n}\n\ngid_t GetUserGroupId(uid_t uid)\n{\n    long size = sysconf(_SC_GETPW_R_SIZE_MAX);\n    if (size < 0)\n    {\n        size = c_PasswordFileBufferSize;\n    }\n\n    std::vector<char> buffer;\n    struct passwd pwd;\n    struct passwd* result;\n    for (;;)\n    {\n        buffer.resize(size);\n        if (getpwuid_r(uid, &pwd, buffer.data(), size, &result) < 0)\n        {\n            if (errno != ERANGE)\n            {\n                return c_InvalidGid;\n            }\n\n            size += c_PasswordFileBufferSize;\n            continue;\n        }\n\n        break;\n    }\n\n    if (result == nullptr)\n    {\n        return c_InvalidGid;\n    }\n\n    return result->pw_gid;\n}\n\ngid_t GetGroupIdByName(const char* name)\n{\n    long size = sysconf(_SC_GETGR_R_SIZE_MAX);\n    if (size < 0)\n    {\n        size = c_PasswordFileBufferSize;\n    }\n\n    std::vector<char> buffer;\n    struct group grp;\n    struct group* result;\n    for (;;)\n    {\n        buffer.resize(size);\n        if (getgrnam_r(name, &grp, buffer.data(), size, &result) < 0)\n        {\n            if (errno != ERANGE)\n            {\n                return c_InvalidGid;\n            }\n\n            size += c_PasswordFileBufferSize;\n            continue;\n        }\n\n        break;\n    }\n\n    if (result == nullptr)\n    {\n        return c_InvalidGid;\n    }\n\n    return result->gr_gid;\n}\n\n// Sets the effective uid and gid of the thread to the specified values.\nFsUserContext::FsUserContext(uid_t uid, gid_t gid, const std::vector<gid_t>& groups)\n{\n    if (!groups.empty())\n    {\n        THROW_LAST_ERROR_IF(sys_setgroups(groups.size(), groups.data()) < 0);\n        m_restoreGroups = true;\n    }\n\n    if (uid != c_InvalidUid)\n    {\n        m_Restore = true;\n        // Use the syscall directly since the wrappers change the value on all threads.\n        // Set the GID first since the capability to do that is lost once the UID changes to non-root.\n        THROW_LAST_ERROR_IF(sys_setresgid(c_InvalidGid, gid, c_InvalidGid) < 0);\n        THROW_LAST_ERROR_IF(sys_setresuid(c_InvalidUid, uid, c_InvalidUid) < 0);\n    }\n}\n\n// Restores the effective uid and gid to root.\nFsUserContext::~FsUserContext()\n{\n    if (m_Restore)\n    {\n        // Use the syscall directly since the wrappers change the value on all threads.\n        THROW_LAST_ERROR_IF(sys_setresuid(-1, 0, -1) < 0);\n        THROW_LAST_ERROR_IF(sys_setresgid(c_InvalidGid, 0, c_InvalidGid) < 0);\n    }\n\n    if (m_restoreGroups)\n    {\n        THROW_LAST_ERROR_IF(sys_setgroups(0, nullptr) < 0);\n    }\n}\n\n} // namespace p9fs::util\n"
  },
  {
    "path": "src/linux/plan9/p9util.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9errors.h\"\n\nnamespace p9fs::util {\n\nconstexpr uid_t c_InvalidUid = std::numeric_limits<uid_t>::max();\nconstexpr gid_t c_InvalidGid = std::numeric_limits<gid_t>::max();\n\nExpected<wil::unique_fd> Reopen(int fd, int openFlags);\n\nExpected<wil::unique_fd> OpenAt(int dirfd, const std::string& name, int openFlags, mode_t mode = 0600);\n\nstd::string GetFdPath(int fd);\n\nLX_INT AccessHelper(int fd, const std::string& path, int mode);\n\nLX_INT CheckFOwnerCapability();\n\ngid_t GetUserGroupId(uid_t uid);\n\ngid_t GetGroupIdByName(const char* name);\n\n// Changes the effective uid and gid of the current thread for the lifetime of this object.\nclass FsUserContext final\n{\npublic:\n    FsUserContext(uid_t uid, gid_t gid, const std::vector<gid_t>& groups);\n    ~FsUserContext();\n\nprivate:\n    bool m_Restore{};\n    bool m_restoreGroups{};\n};\n\n} // namespace p9fs::util\n"
  },
  {
    "path": "src/linux/plan9/p9xattr.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#include \"precomp.h\"\n#include \"p9file.h\"\n#include \"p9xattr.h\"\n#include \"p9util.h\"\n\nnamespace p9fs {\n\nXAttr::XAttr(const std::shared_ptr<const Root>& root, const std::string& fileName, const std::string& name, Access access, UINT64 size, UINT32 flags) :\n    m_Root{root}, m_FileName{fileName}, m_Name{name}, m_Value{size}, m_Access{access}, m_Flags{flags}\n{\n}\n\nTask<Expected<UINT32>> XAttr::Read(UINT64 offset, gsl::span<gsl::byte> buffer)\n{\n    // In practice, the Linux Plan 9 client never uses a non-zero offset, so\n    // it's not implemented here (otherwise an intermediate buffer would be\n    // needed).\n    if ((m_Access != Access::Read) || (offset != 0))\n    {\n        co_return LxError{LX_EINVAL};\n    }\n\n    auto result = GetValue(buffer);\n    if (!result)\n    {\n        co_return result.Unexpected();\n    }\n\n    co_return static_cast<UINT32>(result.Get());\n}\n\nTask<Expected<UINT32>> XAttr::Write(UINT64 offset, gsl::span<const gsl::byte> buffer)\n{\n    if (m_Access != Access::Write)\n    {\n        co_return LxError{LX_EINVAL};\n    }\n\n    if (offset > m_Value.size())\n    {\n        co_return 0;\n    }\n\n    std::lock_guard<std::shared_mutex> lock{m_Lock};\n    UINT32 length = gsl::narrow_cast<UINT32>(buffer.size());\n    if (length > m_Value.size() - offset)\n    {\n        length = m_Value.size() - offset;\n    }\n\n    if (length > 0)\n    {\n        gsl::copy(buffer.subspan(0, length), gsl::make_span(m_Value).subspan(offset));\n    }\n\n    co_return UINT32{length};\n}\n\nLX_INT XAttr::Clunk()\n{\n    // Nothing to do is this fid is not for write.\n    if (m_Access != Access::Write)\n    {\n        return {};\n    }\n\n    // Make sure in-flight write operations are finished.\n    std::shared_lock<std::shared_mutex> lock{m_Lock};\n\n    // Remove the xattr if its size is 0; otherwise, set the value.\n    // N.B. Plan 9 does not support xattrs with zero-length values.\n    if (m_Value.size() == 0)\n    {\n        const int result = lremovexattr(m_FileName.c_str(), m_Name.c_str());\n        if (result < 0)\n        {\n            return -errno;\n        }\n    }\n    else\n    {\n        int result = lsetxattr(m_FileName.c_str(), m_Name.c_str(), m_Value.data(), m_Value.size(), m_Flags);\n        if (result < 0)\n        {\n            return -errno;\n        }\n    }\n\n    return {};\n}\n\nExpected<UINT64> XAttr::GetSize()\n{\n    return GetValue({});\n}\n\nExpected<UINT64> XAttr::GetValue(gsl::span<gsl::byte> buffer)\n{\n    util::FsUserContext userContext{m_Root->Uid, m_Root->Gid, m_Root->Groups};\n    ssize_t result;\n    if (m_Name.size() > 0)\n    {\n        result = lgetxattr(m_FileName.c_str(), m_Name.c_str(), buffer.data(), buffer.size());\n        if (result < 0)\n        {\n            return LxError{-errno};\n        }\n    }\n    else\n    {\n        result = llistxattr(m_FileName.c_str(), reinterpret_cast<char*>(buffer.data()), buffer.size());\n        if (result < 0)\n        {\n            return LxError{-errno};\n        }\n    }\n\n    return static_cast<UINT64>(result);\n}\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/p9xattr.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include \"p9fid.h\"\n\nnamespace p9fs {\n\nclass XAttr final : public XAttrBase\n{\npublic:\n    enum class Access\n    {\n        Read,\n        Write\n    };\n\n    XAttr(const std::shared_ptr<const Root>& root, const std::string& fileName, const std::string& name, Access access, UINT64 size = 0, UINT32 flags = 0);\n\n    Task<Expected<UINT32>> Read(UINT64 Offset, gsl::span<gsl::byte> Buffer) override;\n    Task<Expected<UINT32>> Write(UINT64 Offset, gsl::span<const gsl::byte> Buffer) override;\n    LX_INT Clunk() override;\n\n    Expected<UINT64> GetSize() override;\n\nprivate:\n    Expected<UINT64> GetValue(gsl::span<gsl::byte> Buffer);\n\n    std::shared_mutex m_Lock;\n    const std::shared_ptr<const Root> m_Root;\n    const std::string m_FileName;\n    const std::string m_Name;\n    std::vector<gsl::byte> m_Value;\n    const Access m_Access;\n    const UINT32 m_Flags;\n};\n\n} // namespace p9fs\n"
  },
  {
    "path": "src/linux/plan9/precomp.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n#pragma once\n\n#include <sys/time.h>\n#include <sys/epoll.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <sys/stat.h>\n#include <sys/statfs.h>\n#include <sys/xattr.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <signal.h>\n#include <aio.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <dirent.h>\n#include <sys/uio.h>\n\n// C standard library\n#include <cstdint>\n#include <cstdio>\n#include <cwctype>\n\n// C++ standard library\n#include <exception>\n#include <vector>\n#include <queue>\n#include <list>\n#include <map>\n#include <variant>\n#include <optional>\n#include <chrono>\n#include <thread>\n#include <mutex>\n#include <shared_mutex>\n#include <atomic>\n#include <string>\n#include <string_view>\n#include <filesystem>\n\n// Guideline Support Library\n#include <gsl/gsl>\n#include <gsl/algorithm>\n\n#include <coroutine>\n#include <cassert>\n#include <dirent.h>\n#include \"lxdef.h\"\n#include \"lxwil.h\"\n"
  },
  {
    "path": "src/linux/plan9/result_macros.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n//\n// Defines macros similar to those in <wil/result_macros.h> for use with kernel mode components.\n// Also defines result macros for use with <unexpected.h> for either kernel or user mode.\n#pragma once\n\n/// Static information for an error site.\nstruct ErrorSite\n{\n    PCSTR file;\n    PCSTR function;\n    int line;\n    PCSTR messageFormat;\n};\n\n// No logging for Linux at the moment.\n#define ON_FAILURE_WITH_SOURCE(str, status)\n#define ON_FAILURE_WITH_SOURCE_MSG(str, status, messageFormat, ...)\n\n#define RETURN_ERROR_IF_UNEXPECTED(expected) \\\n    do \\\n    { \\\n        auto __localError = (expected).OptionalError(); \\\n        if (__localError) \\\n        { \\\n            ON_FAILURE_WITH_SOURCE(#expected, *__localError); \\\n            return *__localError; \\\n        } \\\n    } while (0)\n\n#define RETURN_ERROR_IF_UNEXPECTED_MSG(expected, messageFormat, ...) \\\n    do \\\n    { \\\n        auto __localError = (expected).OptionalError(); \\\n        if (__localError) \\\n        { \\\n            ON_FAILURE_WITH_SOURCE_MSG(#expected, *__localError, messageFormat, __VA_ARGS__); \\\n            return *__localError; \\\n        } \\\n    } while (0)\n\n#define RETURN_IF_UNEXPECTED(expected) \\\n    do \\\n    { \\\n        auto __localError = (expected).OptionalError(); \\\n        if (__localError) \\\n        { \\\n            ON_FAILURE_WITH_SOURCE(#expected, *__localError); \\\n            return ::util::Unexpected{*__localError}; \\\n        } \\\n    } while (0)\n\n#define RETURN_IF_UNEXPECTED_MSG(expected, messageFormat, ...) \\\n    do \\\n    { \\\n        auto __localError = (expected).OptionalError(); \\\n        if (__localError) \\\n        { \\\n            ON_FAILURE_WITH_SOURCE_MSG(#expected, *__localError, messageFormat, __VA_ARGS__); \\\n            return ::util::Unexpected{*__localError}; \\\n        } \\\n    } while (0)\n\n#define RETURN_UNEXPECTED_IF_NTSTATUS_FAILED(status) \\\n    do \\\n    { \\\n        NTSTATUS __localStatus = (status); \\\n        if (!NT_SUCCESS(__localStatus)) \\\n        { \\\n            ON_FAILURE_WITH_SOURCE(#status, __localStatus); \\\n            return ::util::Unexpected{__localStatus}; \\\n        } \\\n    } while (0)\n\n#define RETURN_UNEXPECTED_IF_NTSTATUS_FAILED_MSG(status, message, ...) \\\n    do \\\n    { \\\n        NTSTATUS __localStatus = (status); \\\n        if (!NT_SUCCESS(__localStatus)) \\\n        { \\\n            ON_FAILURE_WITH_SOURCE_MSG(#status, __localStatus, message, __VA_ARGS__); \\\n            return ::util::Unexpected{__localStatus}; \\\n        } \\\n    } while (0)\n"
  },
  {
    "path": "src/shared/configfile/CMakeLists.txt",
    "content": "set(SOURCES ${CMAKE_CURRENT_LIST_DIR}/configfile.cpp)\nset(HEADERS ${CMAKE_CURRENT_LIST_DIR}/configfile.h)\n\nadd_subdirectory(linux)\nadd_subdirectory(windows)"
  },
  {
    "path": "src/shared/configfile/configfile.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft Corporation. All rights reserved\n\nParses .gitconfig-style properties files. This consists of key-value pairs\ndivided into sections.\n\nFor example:\n\n[section1]\nkey1 = value\nkey2 = \" value with leading and trailing spaces \"\nkey3 = value with \\\"embedded quotes\\\"\nboolkey = true\n\n# Comments start with hash\n[section2]\nintkey = 37\nintkey2 = 0x3000    # integers can be in hex\nintkey3 = 0644      # octal is OK too\n\n[section3]\nkey = this key has a line continuation \\\n    so that it can wrap to the next line\n\nkey2 = this key has an \\n embedded newline\nkey3 = \"this key uses quotes to # include a comment prefix\"\n\n--*/\n\n#if defined(_MSC_VER)\n\n#define strcasecmp _stricmp\n#define strncasecmp _strnicmp\n#define strdup _strdup\n#define _WINSOCKAPI_\n\n#include \"precomp.h\"\n\nusing wsl::shared::string::MacAddress;\n\n#else\n\n#include <cassert>\n#include <csignal>\n\n#endif\n\n#include \"configfile.h\"\n#include <ctype.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <algorithm>\n#include <format>\n#include \"stringshared.h\"\n#include \"Localization.h\"\n\nusing wsl::shared::Localization;\n\nbool ConfigKey::ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, bool& result)\n{\n    const auto parsed = wsl::shared::string::ParseBool(value);\n    if (!parsed.has_value())\n    {\n        EMIT_USER_WARNING(Localization::MessageConfigInvalidBoolean(value, name, filePath, fileLine));\n        return false;\n    }\n\n    result = parsed.value();\n    return true;\n}\n\nbool ConfigKey::ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, int& result)\n{\n    char* end{};\n    const long number = strtol(value, &end, 0);\n    if (*value == '\\0' || *end != '\\0' || number < INT_MIN || number > INT_MAX)\n    {\n        EMIT_USER_WARNING(Localization::MessageConfigInvalidInteger(value, name, filePath, fileLine));\n        return false;\n    }\n\n    result = number;\n    return true;\n}\n\nbool ConfigKey::ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, std::string& result)\n{\n    result = value;\n    return true;\n}\n\nbool ConfigKey::ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, MemoryString result)\n{\n    const auto memory = wsl::shared::string::ParseMemorySize(value);\n    if (!memory.has_value())\n    {\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageInvalidNumberString(value, name, filePath, fileLine));\n        return false;\n    }\n\n    result.m_value = memory.value();\n    return true;\n}\n\nbool ConfigKey::ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, std::wstring& result)\n{\n    result = wsl::shared::string::MultiByteToWide(value);\n    return true;\n}\n\n#ifdef WIN32\n\nbool ConfigKey::ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, MacAddress& outValue)\n{\n    if (auto parsed = wsl::shared::string::ParseMacAddressNoThrow<char>(value))\n    {\n        outValue = std::move(parsed.value());\n    }\n    else\n    {\n        THROW_HR_WITH_USER_ERROR(E_INVALIDARG, Localization::MessageConfigMacAddress(value, name, filePath, fileLine));\n    }\n\n    return true;\n}\n\n#endif\n\nbool ConfigKey::ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, std::filesystem::path& result)\n{\n    result = wsl::shared::string::MultiByteToWide(value);\n    return true;\n}\n\nstd::wstring ConfigKey::GetValueImpl(bool result)\n{\n    return result ? L\"true\" : L\"false\";\n}\n\nstd::wstring ConfigKey::GetValueImpl(int result)\n{\n    return std::to_wstring(result);\n}\n\nstd::wstring ConfigKey::GetValueImpl(const std::string& result)\n{\n    return wsl::shared::string::MultiByteToWide(result);\n}\n\nstd::wstring ConfigKey::GetValueImpl(const std::optional<std::string>& result)\n{\n    return result.has_value() ? wsl::shared::string::MultiByteToWide(result.value()) : L\"\";\n}\n\nstd::wstring ConfigKey::GetValueImpl(const MemoryString& result)\n{\n    return std::to_wstring(result.m_value);\n}\n\nstd::wstring ConfigKey::GetValueImpl(const std::wstring& result)\n{\n    return result;\n}\n\n#ifdef WIN32\n\nstd::wstring ConfigKey::GetValueImpl(const MacAddress& result)\n{\n    return wsl::shared::string::FormatMacAddress(result, L':');\n}\n\n#endif\n\nbool ConfigKey::Matches(const char* name) const\n{\n    return std::any_of(m_names.begin(), m_names.end(), [&](const auto& e) { return strcasecmp(e, name) == 0; });\n}\n\nbool ConfigKey::Matches(const char* name, size_t length) const\n{\n    return std::any_of(m_names.begin(), m_names.end(), [&](const auto& e) { return strncasecmp(e, name, length) == 0; });\n}\n\nvoid ConfigKey::Parse(const char* name, const char* value, const wchar_t* fileName, unsigned long line)\n{\n    if (m_parseResult.has_value())\n    {\n        EMIT_USER_WARNING(\n            Localization::MessageConfigKeyDuplicated(name, fileName, line, m_parseResult->first, fileName, m_parseResult->second));\n        return;\n    }\n\n    m_parse(name, value, fileName, line);\n    m_parseResult.emplace(name, line);\n}\n\nconst std::vector<const char*>& ConfigKey::GetNames() const\n{\n    return m_names;\n}\n\nstd::wstring ConfigKey::GetValue() const\n{\n    return m_getValue();\n}\n\n// Updates the configuration with the given value.\nstatic void SetConfig(std::vector<ConfigKey>& keys, const char* keyName, const char* value, bool debug, const wchar_t* filePath, unsigned long fileLine)\n{\n    const auto key = std::find_if(keys.begin(), keys.end(), [keyName](const auto& e) { return e.Matches(keyName); });\n    if (key == keys.end())\n    {\n        EMIT_USER_WARNING(Localization::MessageConfigUnknownKey(keyName, filePath, fileLine));\n        return;\n    }\n\n    key->Parse(keyName, value, filePath, fileLine);\n}\n\n// Returns whether a character is a horizontal space (' ' or '\\t').\nstatic bool IsHSpace(wint_t ch)\n{\n    return ch == ' ' || ch == '\\t';\n}\n\n// Parses a configuration file. If file is NULL, then just set the configuration\n// to the default values.\nint ParseConfigFile(std::vector<ConfigKey>& keys, FILE* file, int flags, const wchar_t* filePath)\n{\n    std::wstring emptyStr;\n    return ParseConfigFile(keys, file, flags, filePath, emptyStr);\n}\n\n// Parses a configuration file. If file is NULL, then just set the configuration\n// to the default values.\nint ParseConfigFile(std::vector<ConfigKey>& keys, FILE* file, int flags, const wchar_t* filePath, std::wstring& configFileOutput, std::optional<ConfigKey> outputKey, bool removeKey)\n{\n    wint_t ch = 0;\n    unsigned long line = 0;\n    bool trailingComment = false;\n    bool inQuote = false;\n    size_t trimmedLength = 0;\n    int result;\n    size_t sectionLength = 0;\n    std::string key = {0};\n    std::string value = {0};\n\n    // Function default is parse mode (updateConfigFile = false).\n    // Otherwise, update mode (though parsing logic is still used).\n    bool updateConfigFile = false;\n    bool outputKeyValueUpdated = false;\n    bool matchedKey = false;\n    bool firstMatchedKey = false;\n\n    if (outputKey.has_value())\n    {\n        updateConfigFile = true;\n    }\n\n    if (file == NULL)\n    {\n        result = 0;\n        if (updateConfigFile && !outputKeyValueUpdated && !removeKey)\n        {\n            goto WriteNewKeyValue;\n        }\n        else\n        {\n            goto Done;\n        }\n    }\n\nNewLine:\n    if (!trailingComment)\n    {\n        line++;\n    }\n\n    // parse [section], key = value, or empty line\n    for (;;)\n    {\n        if (updateConfigFile && ch != 0 && ch != WEOF)\n        {\n            if (trailingComment && matchedKey)\n            {\n                // If we're removing a key and have a trailing comment,\n                // the comment will be preserved. The newline char will have\n                // been removed from the output stream (due to key removal),\n                // so insert it back here.\n                configFileOutput += L'\\n';\n            }\n\n            // Write the current character to output now, since, in\n            // addition to writing the characters read in this loop,\n            // future parsing may jump back to the NewLine label\n            // and we assume the 'ch' has yet to be written.\n            configFileOutput += ch;\n        }\n\n        // Skip any pending comment.\n        if (ch == '#')\n        {\n            do\n            {\n                ch = fgetwc(file);\n\n                if (updateConfigFile && ch != WEOF)\n                {\n                    // Write out the rest of the comment line.\n                    configFileOutput += ch;\n                }\n\n                if (ch == '\\r')\n                {\n                    ch = fgetwc(file);\n                }\n\n                if (ch == '\\n')\n                {\n                    line++;\n                }\n\n            } while (ch != '\\n' && ch != WEOF);\n\n            if (trailingComment)\n            {\n                trailingComment = false;\n            }\n        }\n\n        if (feof(file))\n        {\n            result = 0;\n            if (updateConfigFile && !outputKeyValueUpdated && !removeKey)\n            {\n                goto WriteNewKeyValue;\n            }\n            else\n            {\n                goto Done;\n            }\n        }\n\n        if (ferror(file))\n        {\n            result = -1;\n            goto Done;\n        }\n\n        // Skip leading spaces.\n        while (IsHSpace(ch = fgetwc(file)))\n        {\n            if (updateConfigFile)\n            {\n                configFileOutput += ch;\n            }\n        }\n\n        switch (ch)\n        {\n        case WEOF:\n            break;\n\n        case '\\r':\n        {\n            auto nextc = fgetwc(file);\n            if (nextc == '\\n')\n            {\n                line++;\n            }\n            else\n            {\n                ungetwc(nextc, file);\n            }\n\n            break;\n        }\n\n        case '\\n':\n            line++;\n            break;\n\n        case '#':\n            break;\n\n        case '[':\n            // We're about to parse a new section. If we have an unwritten key-value\n            // and the current section matches, write it now before moving to the new section.\n            if (updateConfigFile && !outputKeyValueUpdated && !removeKey && sectionLength > 0)\n            {\n                const auto& outputConfigKey = outputKey.value();\n                if (outputConfigKey.Matches(key.c_str(), sectionLength))\n                {\n                    const auto& keyNames = outputConfigKey.GetNames();\n                    // Config key without name.\n                    FAIL_FAST_IF(keyNames.empty());\n                    const auto keyNameUtf8 = keyNames.front();\n                    const auto keyName = wsl::shared::string::MultiByteToWide(keyNameUtf8);\n                    const auto sectionKeySeparatorPos = keyName.find('.');\n                    // Config key without separated section/key name\n                    FAIL_FAST_IF(sectionKeySeparatorPos == std::string_view::npos);\n                    // Config key without section name\n                    FAIL_FAST_IF(sectionKeySeparatorPos == 0);\n                    // Config key without key name\n                    FAIL_FAST_IF(sectionKeySeparatorPos == (keyName.length() - 1));\n\n                    // Remove any trailing newlines before inserting the new key-value\n                    while (!configFileOutput.empty() && configFileOutput.back() == L'\\n')\n                    {\n                        configFileOutput.pop_back();\n                    }\n\n                    auto keyValue = std::format(L\"\\n{}={}\\n\\n\", keyName.substr(sectionKeySeparatorPos + 1), outputKey.value().GetValue());\n                    configFileOutput += keyValue;\n                    outputKeyValueUpdated = true;\n                }\n            }\n            goto ParseSection;\n\n        default:\n            if (!isalpha(ch))\n            {\n                if (flags & CFG_DEBUG)\n                {\n                    fputs(\"expected a-z\\n\", stderr);\n                }\n\n                EMIT_USER_WARNING(Localization::MessageConfigInvalidKey(filePath, line));\n\n                if (updateConfigFile)\n                {\n                    // Always write out the invalid character\n                    // prior to jumping to the InvalidLine label.\n                    configFileOutput += ch;\n                    ch = 0;\n                }\n\n                goto InvalidLine;\n            }\n\n            goto ParseKeyValue;\n        }\n    }\n\nParseSection:\n    // parse [section] ([ is already parsed)\n    if (updateConfigFile)\n    {\n        // Write the '[' character to the output.\n        configFileOutput += ch;\n    }\n\n    ch = fgetwc(file);\n\n    if (!isalpha(ch))\n    {\n        if (flags & CFG_DEBUG)\n        {\n            fputs(\"expected a-z\\n\", stderr);\n        }\n\n        EMIT_USER_WARNING(Localization::MessageConfigInvalidSection(filePath, line));\n\n        if (updateConfigFile)\n        {\n            // Always write out the invalid character\n            // prior to jumping to the InvalidLine label.\n            configFileOutput += ch;\n            ch = 0;\n        }\n\n        goto InvalidLine;\n    }\n\n    key.clear();\n\n    do\n    {\n        if (updateConfigFile)\n        {\n            // Write the first alpha character of the section\n            // name followed by the rest of the section name.\n            configFileOutput += ch;\n        }\n\n        key += static_cast<char>(ch);\n\n        ch = fgetwc(file);\n    } while (isalnum(ch));\n\n    if (ch != ']')\n    {\n        if (flags & CFG_DEBUG)\n        {\n            fputs(\"expected ]\\n\", stderr);\n        }\n\n        EMIT_USER_WARNING(Localization::MessageConfigExpected(\"']'\", filePath, line));\n\n        if (updateConfigFile)\n        {\n            // Always write out the invalid character\n            // prior to jumping to the InvalidLine label.\n            configFileOutput += ch;\n            ch = 0;\n        }\n\n        goto InvalidLine;\n    }\n\n    if (updateConfigFile)\n    {\n        // Write the ']' character to the output.\n        configFileOutput += ch;\n    }\n\n    // Skip trailing space.\n    while (IsHSpace(ch = fgetwc(file)))\n    {\n        if (updateConfigFile)\n        {\n            configFileOutput += ch;\n        }\n    }\n\n    switch (ch)\n    {\n    case WEOF:\n    case '\\n':\n    case '\\r':\n        break;\n\n    case '#':\n        trailingComment = true;\n        break;\n\n    default:\n        if (flags & CFG_DEBUG)\n        {\n            fputs(\"expected space or EOL\\n\", stderr);\n        }\n\n        EMIT_USER_WARNING(Localization::MessageConfigExpected(\"' ' or '\\\\n'\", filePath, line));\n\n        if (updateConfigFile)\n        {\n            // Always write out the invalid character\n            // prior to jumping to the InvalidLine label.\n            configFileOutput += ch;\n            ch = 0;\n        }\n\n        goto InvalidLine;\n    }\n\n    sectionLength = key.size();\n\n    goto NewLine;\n\nParseKeyValue:\n    // parse key = value. The first character of the key is in ch.\n    key.resize(sectionLength);\n    if (key.size() > 0)\n    {\n        key += '.';\n    }\n\n    do\n    {\n        if (updateConfigFile)\n        {\n            // Write out the first character of the key to the\n            // output followed by the rest of the key name.\n            configFileOutput += ch;\n        }\n\n        key += static_cast<char>(ch);\n\n        ch = fgetwc(file);\n    } while (isalnum(ch));\n\n    // Skip leading space.\n    while (IsHSpace(ch))\n    {\n        if (updateConfigFile)\n        {\n            configFileOutput += ch;\n        }\n\n        ch = fgetwc(file);\n    }\n\n    if (ch != '=')\n    {\n        if (flags & CFG_DEBUG)\n        {\n            fputs(\"expected =\\n\", stderr);\n        }\n\n        EMIT_USER_WARNING(Localization::MessageConfigExpected(\"'='\", filePath, line));\n\n        if (updateConfigFile)\n        {\n            // Always write out the invalid character\n            // prior to jumping to the InvalidLine label.\n            configFileOutput += ch;\n            ch = 0;\n        }\n\n        goto InvalidLine;\n    }\n\n    if (updateConfigFile)\n    {\n        // Write the '=' character to the output.\n        configFileOutput += ch;\n    }\n\n    // Skip trailing space.\n    while (IsHSpace(ch = fgetwc(file)))\n    {\n        if (updateConfigFile)\n        {\n            configFileOutput += ch;\n        }\n    }\n\n    // Only match the first instance of the key in the input file.\n    // In other words, if we've already updated the matched key value,\n    // then ignore updating any other keys that match.\n    // This is consistent with the behavior of the parsing logic.\n    firstMatchedKey = false;\n    if (updateConfigFile && !outputKeyValueUpdated && !removeKey)\n    {\n        firstMatchedKey = outputKey.value().Matches(key.c_str());\n    }\n\n    // There may be multiple instances of the same key in the input file,\n    // so we need to find and remove all instances of the key.\n    matchedKey = false;\n    if (updateConfigFile && removeKey)\n    {\n        matchedKey = outputKey.value().Matches(key.c_str());\n        if (matchedKey)\n        {\n            auto previousNewLine = configFileOutput.rfind(L'\\n');\n            if (previousNewLine != std::wstring::npos)\n            {\n                configFileOutput = configFileOutput.substr(0, previousNewLine);\n            }\n        }\n    }\n\n    // Parse the value by removing unescaped quotes, handling escaped n, t, \\,\n    // \", and NewLine (line continuation). End parsing on a NewLine, EOF, or\n    // comment (#).\n    value.clear();\n    trimmedLength = 0;\n    inQuote = false;\n    while (ch != WEOF && ch != '\\n' && ch != '\\r')\n    {\n        if (updateConfigFile && !firstMatchedKey && !matchedKey && ch != '#')\n        {\n            // Write out the first character of the value to\n            // the output followed by the rest of the value.\n            // Don't write the '#' as it will be written by the\n            // NewLine label after the ValueDone label. This is\n            // done to ensure consistency with the writing logic.\n            configFileOutput += ch;\n        }\n\n        switch (ch)\n        {\n        case '\"':\n            inQuote = !inQuote;\n            break;\n\n        case '\\\\':\n        {\n            auto ch2 = fgetwc(file);\n\n            if (updateConfigFile && !firstMatchedKey && !matchedKey && ch2 != WEOF)\n            {\n                // Write out the escaped character to the output, also,\n                // handling the case where ch2 is an invalid character.\n                configFileOutput += ch2;\n            }\n\n            switch (ch2)\n            {\n            case '\\\\':\n            case '\"':\n                value += static_cast<char>(ch2);\n\n                break;\n\n            case 'b':\n                value += '\\b';\n                break;\n\n            case 'n':\n                value += '\\n';\n                break;\n\n            case 't':\n                value += '\\t';\n                break;\n\n            case '\\r':\n                break;\n\n            case '\\n':\n                // Line continuation. Skip both characters.\n                line++;\n                break;\n\n            default:\n                if (flags & CFG_DEBUG)\n                {\n                    fprintf(stderr, \"unexpected escaped character %lc\\n\", ch2);\n                }\n\n                EMIT_USER_WARNING(Localization::MessageConfigInvalidEscape(static_cast<wchar_t>(ch2), filePath, line));\n\n                if (firstMatchedKey)\n                {\n                    // This key value will be overwritten, so we can ignore any malformed values,\n                    // since none of the value should/will have been written to the output file.\n                    // However, we can still inform the user of the issue per the above warning.\n                    break;\n                }\n\n                goto InvalidLine;\n            }\n        }\n\n        break;\n\n        case '#':\n            if (!inQuote)\n            {\n                trailingComment = true;\n                goto ValueDone;\n            }\n        default:\n            value += static_cast<char>(ch);\n\n            break;\n        }\n\n        // Track the length without trailing space.\n        if (!IsHSpace(ch))\n        {\n            trimmedLength = value.size();\n        }\n        ch = fgetwc(file);\n    }\n\nValueDone:\n    // If we overwrote an existing key value, where the value is malformed, we can ignore it.\n    if (inQuote)\n    {\n        if (flags & CFG_DEBUG)\n        {\n            fprintf(stderr, \"expected \\\"\\n\");\n        }\n\n        EMIT_USER_WARNING(Localization::MessageConfigExpected(\"'\", filePath, line));\n\n        // This key value will be overwritten, so we can ignore any malformed values.\n        // However, we can still inform the user of the issue per warning above.\n        if (!firstMatchedKey || !matchedKey)\n        {\n            goto InvalidLine;\n        }\n    }\n\n    if (firstMatchedKey)\n    {\n        for (auto outValueCh : outputKey.value().GetValue())\n        {\n            configFileOutput += outValueCh;\n        }\n\n        // Preserve any spacing in the parsed value.\n        // Invalid values are still parsed in the case\n        // of trailing comments.\n        for (size_t spaceIdx = trimmedLength; spaceIdx < value.size(); spaceIdx++)\n        {\n            configFileOutput += value[spaceIdx];\n        }\n\n        outputKeyValueUpdated = true;\n    }\n    else if (!matchedKey)\n    {\n        // Trim any trailing space.\n        value.resize(trimmedLength);\n        SetConfig(keys, key.c_str(), value.c_str(), flags & CFG_DEBUG, filePath, line);\n    }\n\n    goto NewLine;\n\nInvalidLine:\n    if (!(flags & CFG_SKIP_INVALID_LINES))\n    {\n        result = -1;\n        goto Done;\n    }\n\n    while (ch != WEOF && ch != '\\n')\n    {\n        ch = fgetwc(file);\n\n        if (updateConfigFile && ch != WEOF && ch != '\\n' && ch != '\\r')\n        {\n            // Write out the rest of the remaining\n            // invalid line. WEOF and '\\n' will be\n            // handled/written by the NewLine label.\n            configFileOutput += ch;\n        }\n    }\n\n    goto NewLine;\n\nWriteNewKeyValue:\n{\n    const auto& outputConfigKey = outputKey.value();\n    const auto& keyNames = outputConfigKey.GetNames();\n    // Config key without name.\n    FAIL_FAST_IF(keyNames.empty());\n    const auto keyNameUtf8 = keyNames.front();\n    const auto keyName = wsl::shared::string::MultiByteToWide(keyNameUtf8);\n    const auto sectionKeySeparatorPos = keyName.find('.');\n    // Config key without separated section/key name\n    FAIL_FAST_IF(sectionKeySeparatorPos == std::string_view::npos);\n    // Config key without section name\n    FAIL_FAST_IF(sectionKeySeparatorPos == 0);\n    // Config key without key name\n    FAIL_FAST_IF(sectionKeySeparatorPos == (keyName.length() - 1));\n\n    // This is a new key/value pair not present in the input file, so write it out.\n    // No need for newline if this is the first key/value pair.\n    if (file != NULL)\n    {\n        configFileOutput += L'\\n';\n    }\n\n    // Check if we currently parsed a key and the key matches the section name.\n    // In this case, we don't need to write the section name again.\n    if (!(sectionLength > 0 && outputConfigKey.Matches(key.c_str(), sectionLength)))\n    {\n        configFileOutput += std::format(L\"[{}]\\n\", keyName.substr(0, sectionKeySeparatorPos));\n    }\n\n    configFileOutput += std::format(L\"{}={}\", keyName.substr(sectionKeySeparatorPos + 1), outputKey.value().GetValue());\n\n    outputKeyValueUpdated = true;\n    goto Done;\n}\n\nDone:\n    return result;\n}"
  },
  {
    "path": "src/shared/configfile/configfile.h",
    "content": "/*++\n\nCopyright (c) Microsoft Corporation. All rights reserved\n\nParses .gitconfig-style properties files.\n\n--*/\n\n#pragma once\n\n#include <stdio.h>\n#include <string>\n#include <functional>\n#include <map>\n#include <optional>\n#include <vector>\n#include \"stringshared.h\"\n#include \"Localization.h\"\n\n#ifdef WIN32\n\n#include <filesystem>\n#include \"wslservice.h\"\n#include \"ExecutionContext.h\"\n\n#endif\n\nstruct MemoryString\n{\n    MemoryString(uint64_t& value) : m_value(value) {};\n    uint64_t& m_value;\n};\n\nenum class ConfigKeyPresence\n{\n    Absent,\n    Present\n};\n\nclass ConfigKey\n{\npublic:\n    using TParseMethod = std::function<void(const char*, const char*, const wchar_t*, unsigned long)>;\n    using TGetValueMethod = std::function<std::wstring()>;\n\n    template <typename TType>\n    inline ConfigKey(std::vector<const char*>&& names, TType& outValue, ConfigKeyPresence* presence = nullptr) :\n        m_names(std::move(names))\n    {\n        m_parse = [presence = presence, &outValue](const char* name, const char* value, const wchar_t* filename, unsigned long line) {\n            if (ParseImpl(name, value, filename, line, outValue) && presence != nullptr)\n            {\n                *presence = ConfigKeyPresence::Present;\n            }\n        };\n\n        m_getValue = [&outValue]() { return GetValueImpl(outValue); };\n    }\n\n    template <typename TType>\n    inline ConfigKey(const char* name, TType& value, ConfigKeyPresence* presence = nullptr) :\n        ConfigKey(std::vector<const char*>{name}, value, presence)\n    {\n    }\n\n    inline ConfigKey(const char* name, MemoryString outValue, ConfigKeyPresence* presence = nullptr) :\n        m_names(std::vector<const char*>{name})\n    {\n        m_parse = [presence = presence, outValue = outValue](const char* name, const char* value, const wchar_t* filename, unsigned long line) {\n            if (ParseImpl(name, value, filename, line, outValue) && presence != nullptr)\n            {\n                *presence = ConfigKeyPresence::Present;\n            }\n        };\n\n        m_getValue = [outValue = outValue]() { return GetValueImpl(outValue); };\n    }\n\n    inline ConfigKey(const char* name, TParseMethod&& parse) : m_names({name}), m_parse(std::move(parse))\n    {\n    }\n\n    template <typename TEnum>\n    inline ConfigKey(\n        std::vector<const char*>&& names,\n        const std::map<std::string, TEnum, wsl::shared::string::CaseInsensitiveCompare>& values,\n        TEnum& outValue,\n        ConfigKeyPresence* presence = nullptr) :\n        m_names(std::move(names))\n    {\n        m_parse = [presence = presence, values = values, &outValue](\n                      const char* name, const char* value, const wchar_t* filename, unsigned long line) {\n            auto result = ParseEnumString(values, value, name, filename, line);\n            if (result.has_value())\n            {\n                outValue = result.value();\n                if (presence != nullptr)\n                {\n                    *presence = ConfigKeyPresence::Present;\n                }\n            }\n        };\n\n        m_getValue = [&values, &outValue]() { return GetEnumString(values, outValue); };\n    }\n\n    template <typename TEnum>\n    inline ConfigKey(\n        const char* name,\n        const std::map<std::string, TEnum, wsl::shared::string::CaseInsensitiveCompare>& values,\n        TEnum& outValue,\n        ConfigKeyPresence* presence = nullptr) :\n        ConfigKey(std::vector<const char*>{name}, values, outValue, presence)\n    {\n    }\n\n    bool Matches(const char* name) const;\n    bool Matches(const char* name, size_t length) const;\n    void Parse(const char* name, const char* value, const wchar_t* fileName, unsigned long line);\n    const std::vector<const char*>& GetNames() const;\n    std::wstring GetValue() const;\n\n    template <typename TEnum>\n    static inline std::optional<TEnum> ParseEnumString(\n        const std::map<std::string, TEnum, wsl::shared::string::CaseInsensitiveCompare>& mappings,\n        const char* value,\n        const char* name,\n        const wchar_t* fileName,\n        unsigned long line)\n    {\n        auto it = mappings.find(value);\n        if (it == mappings.end())\n        {\n            std::stringstream validValues;\n            bool first = true;\n            for (const auto& e : mappings)\n            {\n                if (!first)\n                {\n                    validValues << \", \";\n                }\n\n                first = false;\n                validValues << e.first;\n            }\n\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageConfigInvalidEnum(value, name, fileName, line, validValues.str().c_str()));\n            return {};\n        }\n\n        return it->second;\n    }\n\n    template <typename TEnum>\n    static std::wstring GetEnumString(const std::map<std::string, TEnum, wsl::shared::string::CaseInsensitiveCompare>& mappings, TEnum& value)\n    {\n        auto it = mappings.begin();\n        for (; it != mappings.end(); ++it)\n        {\n            if (it->second == value)\n            {\n                break;\n            }\n        }\n\n        return it == mappings.end() ? L\"\" : wsl::shared::string::MultiByteToWide(it->first);\n    }\n\nprivate:\n    static bool ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, bool& result);\n    static bool ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, int& result);\n    static bool ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, std::string& result);\n    static bool ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, MemoryString result);\n    static bool ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, std::wstring& result);\n    static bool ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, std::filesystem::path& result);\n\n    static std::wstring GetValueImpl(bool result);\n    static std::wstring GetValueImpl(int result);\n    static std::wstring GetValueImpl(const std::string& result);\n    static std::wstring GetValueImpl(const std::optional<std::string>& result);\n    static std::wstring GetValueImpl(const MemoryString& result);\n    static std::wstring GetValueImpl(const std::wstring& result);\n\n#ifdef WIN32\n    static bool ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, wsl::shared::string::MacAddress& result);\n    static std::wstring GetValueImpl(const wsl::shared::string::MacAddress& result);\n#endif\n\n    template <typename T>\n    static inline bool ParseImpl(const char* name, const char* value, const wchar_t* filePath, unsigned long fileLine, std::optional<T>& result)\n    {\n        T storage{};\n\n        if (ParseImpl(name, value, filePath, fileLine, storage))\n        {\n            result.emplace(std::move(storage));\n            return true;\n        }\n\n        return false;\n    }\n\n    std::vector<const char*> m_names;\n    TParseMethod m_parse;\n    TGetValueMethod m_getValue;\n    std::optional<std::pair<std::string, unsigned long>> m_parseResult;\n};\n\nenum\n{\n    CFG_SKIP_INVALID_LINES = 0x1,\n    CFG_SKIP_UNKNOWN_VALUES = 0x2,\n    CFG_DEBUG = 0x80000000,\n};\n\nint ParseConfigFile(std::vector<ConfigKey>& keys, FILE* file, int flags, const wchar_t* filePath);\nint ParseConfigFile(\n    std::vector<ConfigKey>& keys,\n    FILE* file,\n    int flags,\n    const wchar_t* filePath,\n    std::wstring& configFileOutput,\n    std::optional<ConfigKey> outputKey = std::nullopt,\n    bool removeKey = false);"
  },
  {
    "path": "src/shared/configfile/linux/CMakeLists.txt",
    "content": "add_linux_library_no_sources(libconfigfile \"${SOURCES}\" \"${HEADERS}\")\nset_target_properties(libconfigfile PROPERTIES FOLDER common)\nadd_dependencies(libconfigfile localization)"
  },
  {
    "path": "src/shared/configfile/windows/CMakeLists.txt",
    "content": "add_library(configfile STATIC ${SOURCES} ${HEADERS})\nadd_dependencies(configfile wslserviceidl localization)\nset_target_properties(configfile PROPERTIES FOLDER common)"
  },
  {
    "path": "src/shared/inc/CommandLine.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    CommandLine.h\n\nAbstract:\n\n    This file contains the command line parsing logic.\n\n--*/\n\n#pragma once\n\n#include <string>\n#include <functional>\n#include <type_traits>\n#include \"Localization.h\"\n\nnamespace wsl::shared {\n\n#ifdef WIN32\n\n#define THROW_USER_ERROR(Message) THROW_HR_WITH_USER_ERROR(E_INVALIDARG, (Message))\nusing TChar = wchar_t;\n\n#else\n\nusing TChar = char;\n\n#endif\n\nusing TString = std::basic_string<TChar>;\n\nusing TMatchMethod = std::function<bool(const TChar*, int)>;\nusing TParseMethod = std::function<int(const TChar*)>;\n\nstruct Argument\n{\n    TMatchMethod Matches;\n    TParseMethod Consume;\n    bool Positional;\n};\n\ntemplate <typename T, T Flag>\nstruct SetFlag\n{\n    T& value;\n\n    void operator()() const\n    {\n        WI_SetFlag(value, Flag);\n    }\n};\n\ntemplate <typename T>\nstruct is_optional : std::false_type\n{\n};\n\ntemplate <typename T>\nstruct is_optional<std::optional<T>> : std::true_type\n{\n};\n\ntemplate <typename T>\nstruct Integer\n{\n    T& value;\n\n    int operator()(const TChar* input) const\n    {\n        if (input == nullptr)\n        {\n            return -1;\n        }\n\n        if constexpr (is_optional<T>::value)\n        {\n            value.emplace();\n            return Integer<typename T::value_type>{value.value()}(input);\n        }\n        else\n        {\n\n#ifdef WIN32\n\n            const auto utf8value = wsl::shared::string::WideToMultiByte(input);\n            auto result = std::from_chars(utf8value.c_str(), utf8value.c_str() + utf8value.size(), value);\n\n#else\n            std::from_chars_result result{};\n            if constexpr (std::is_enum_v<T>)\n            {\n                result = std::from_chars(input, input + strlen(input), reinterpret_cast<std::underlying_type_t<T>&>(value));\n            }\n            else\n            {\n                result = std::from_chars(input, input + strlen(input), value);\n            }\n\n#endif\n\n            if (result.ec == std::errc::invalid_argument)\n            {\n                THROW_USER_ERROR(wsl::shared::Localization::MessageInvalidInteger(input));\n            }\n        }\n\n        return 1;\n    }\n};\n\n#ifdef WIN32\ntemplate <typename T>\nstruct UnquotedPath\n{\n    T& value;\n\n    int operator()(const TChar* input) const\n    {\n        if (input == nullptr)\n        {\n            return -1;\n        }\n\n        value = wsl::windows::common::filesystem::UnquotePath(input);\n\n        return 1;\n    }\n};\n\ntemplate <typename T>\nstruct AbsolutePath\n{\n    T& value;\n\n    int operator()(const TChar* input) const\n    {\n        if (input == nullptr)\n        {\n            return -1;\n        }\n\n        if (PathIsRelative(input))\n        {\n            std::error_code error;\n            value = std::filesystem::absolute(input, error);\n            if (error)\n            {\n                THROW_WIN32(error.value());\n            }\n        }\n        else\n        {\n            value = input;\n        }\n\n        return 1;\n    }\n};\n\nstruct Handle\n{\n    wil::unique_handle& output;\n\n    int operator()(const TChar* input) const\n    {\n        if (input == nullptr)\n        {\n            return -1;\n        }\n\n        output.reset(ULongToHandle(wcstoul(input, nullptr, 0)));\n\n        return 1;\n    }\n};\n\n#else\n\nstruct UniqueFd\n{\n    wil::unique_fd& output;\n\n    int operator()(const TChar* input)\n    {\n        if (input == nullptr)\n        {\n            return -1;\n        }\n\n        int fd = -1;\n        auto result = std::from_chars(input, input + strlen(input), fd);\n        if (result.ec == std::errc::invalid_argument || fd < 0)\n        {\n            THROW_USER_ERROR(wsl::shared::Localization::MessageInvalidInteger(input));\n        }\n\n        output.reset(fd);\n\n        return 1;\n    }\n};\n\n#endif\n\ntemplate <typename T>\nstruct ParsedBool\n{\n    T& value;\n\n    int operator()(const TChar* input) const\n    {\n        if (input == nullptr)\n        {\n            return -1;\n        }\n\n        auto result = wsl::shared::string::ParseBool(input);\n        if (!result.has_value())\n        {\n            THROW_USER_ERROR(wsl::shared::Localization::MessageInvalidBoolean(input));\n        }\n\n        value = result.value();\n        return 1;\n    }\n};\n\ntemplate <typename T>\nstruct SizeString\n{\n    T& value;\n\n    int operator()(const TChar* input) const\n    {\n        if (input == nullptr)\n        {\n            return -1;\n        }\n\n        auto parsed = wsl::shared::string::ParseMemorySize(input);\n        if (!parsed.has_value())\n        {\n            THROW_USER_ERROR(wsl::shared::Localization::MessageInvalidSize(input));\n        }\n\n        value = parsed;\n        return 1;\n    }\n};\n\nstruct NoOp\n{\n    void operator()(const TChar*) const\n    {\n    }\n};\n\ntemplate <typename T, T SetValue>\nstruct UniqueSetValue\n{\n    std::optional<T>& value;\n    std::function<TString()> errorMessage;\n\n    void operator()()\n    {\n        if (value.has_value())\n        {\n            THROW_USER_ERROR(errorMessage());\n        }\n\n        value = SetValue;\n    }\n};\n\nclass ArgumentParser\n{\npublic:\n#ifdef WIN32\n\n    ArgumentParser(const std::wstring& CommandLine, LPCWSTR Name, int StartIndex = 1) : m_startIndex(StartIndex), m_name(Name)\n    {\n        m_argv.reset(CommandLineToArgvW(std::wstring(CommandLine).c_str(), &m_argc));\n        THROW_LAST_ERROR_IF(!m_argv);\n    }\n\n#else\n\n    ArgumentParser(int argc, const char* const* argv) : m_argc(argc), m_argv(argv), m_startIndex(1)\n    {\n    }\n\n#endif\n\n    template <typename T>\n    void AddArgument(T&& Output, const TChar* LongName, TChar ShortName = '\\0')\n    {\n        auto match = [LongName, ShortName](const TChar* Name, int Pos) {\n            if (Name == nullptr)\n            {\n                return false;\n            }\n            else if (LongName != nullptr && wsl::shared::string::IsEqual(Name, LongName))\n            {\n                return true;\n            }\n            else\n            {\n                return ShortName != '\\0' && Name[0] == '-' && Name[1] == ShortName && Name[2] == '\\0';\n            }\n        };\n\n        m_arguments.emplace_back(std::move(match), BuildParseMethod(std::forward<T>(Output)), false);\n    }\n\n    template <typename T>\n    void AddPositionalArgument(T&& Output, int Position)\n    {\n        WI_ASSERT(Position >= 0);\n\n        auto match = [Position](const TChar*, int Index) { return Position == Index; };\n\n        m_arguments.emplace_back(std::move(match), BuildParseMethod(std::forward<T>(Output)), true);\n    }\n\n    void Parse() const\n    {\n        int argumentPosition = 0;\n        bool stopParameters = false;\n        for (size_t i = m_startIndex; i < m_argc; i++)\n        {\n            if (!stopParameters && wsl::shared::string::IsEqual(m_argv[i], TEXT(\"--\")))\n            {\n                stopParameters = true;\n                continue;\n            }\n\n            bool foundMatch = false;\n            int offset = 0;\n\n            // Special case for short argument with multiple values like -abc\n            if (!stopParameters && m_argv[i][0] == '-' && m_argv[i][1] != '-' && m_argv[i][1] != '\\0' && m_argv[i][2] != '\\0')\n            {\n                for (const auto* arg = &m_argv[i][1]; *arg != '\\0'; arg++)\n                {\n                    foundMatch = false;\n                    for (const auto& e : m_arguments)\n                    {\n                        const TChar fullArgument[] = {'-', *arg, '\\0'};\n                        if (!e.Positional && e.Matches(fullArgument, -1))\n                        {\n                            e.Consume(nullptr);\n                            foundMatch = true;\n                            break;\n                        }\n                    }\n\n                    if (!foundMatch)\n                    {\n                        break;\n                    }\n                }\n            }\n\n            if (!foundMatch)\n            {\n                for (const auto& e : m_arguments)\n                {\n                    if (e.Matches(stopParameters ? nullptr : m_argv[i], m_argv[i][0] == '-' && m_argv[i][1] != '\\0' && !stopParameters ? -1 : argumentPosition))\n                    {\n                        const TChar* value = nullptr;\n                        if (e.Positional)\n                        {\n                            value = m_argv[i]; // Positional arguments directly receive arvg[i]\n                        }\n                        else if (i + 1 < m_argc)\n                        {\n                            value = m_argv[i + 1];\n                        }\n\n                        offset = e.Consume(value);\n                        if (offset < 0)\n                        {\n                            WI_ASSERT(value == nullptr);\n                            THROW_USER_ERROR(wsl::shared::Localization::MessageMissingArgument(m_argv[i], m_name ? m_name : m_argv[0]));\n                        }\n\n                        if (e.Positional) // Positional arguments can't consume extra arguments.\n                        {\n                            offset = 0;\n                        }\n\n                        i += offset;\n                        foundMatch = true;\n\n                        break;\n                    }\n                }\n            }\n\n            if (!foundMatch)\n            {\n                THROW_USER_ERROR(wsl::shared::Localization::MessageInvalidCommandLine(m_argv[i], m_name ? m_name : m_argv[0]));\n            }\n\n            if (i < m_argc && m_argv[i - offset][0] != '-')\n            {\n                argumentPosition++;\n            }\n        }\n    }\n\nprivate:\n    template <typename T>\n    static std::function<int(const TChar*)> BuildParseMethod(T&& Output)\n    {\n        if constexpr (std::is_rvalue_reference_v<T&&>)\n        {\n            return [Output = std::move(Output)](const TChar* Value) mutable { return ParseArgumentImpl<T>(Output, Value); };\n        }\n        else\n        {\n            return [&Output](const TChar* Value) mutable { return ParseArgumentImpl<T>(Output, Value); };\n        }\n    }\n\n    template <typename T>\n    static int ParseArgumentImpl(T& Output, const TChar* Value)\n    {\n        if constexpr (std::is_invocable_v<T, const TChar*>) // Callable with const TChar*.\n        {\n            if constexpr (std::is_same_v<std::invoke_result_t<T, const TChar*>, void>)\n            {\n                Output(Value);\n                return 0;\n            }\n            else\n            {\n                return Output(Value);\n            }\n        }\n        else if constexpr (std::is_invocable_v<T>) // Callable without argument.\n        {\n            Output();\n            return 0;\n        }\n        else // Simple type.\n        {\n            return ParseSimpleArgument<std::remove_cvref_t<T>>(Output, Value);\n        }\n    }\n\n    template <typename T>\n    static int ParseSimpleArgument(T& Output, const TChar* Value)\n    {\n        // If this is a flag, just set the flag and exit\n        if constexpr (std::is_same_v<T, bool>)\n        {\n            Output = true;\n            return 0;\n        }\n        else\n        {\n            // Otherwise, we need an actual value\n            if (Value == nullptr)\n            {\n                return -1;\n            }\n\n            if constexpr (std::is_same_v<T, GUID> || std::is_same_v<T, std::optional<GUID>>)\n            {\n                auto guid = wsl::shared::string::ToGuid(Value);\n                if (!guid.has_value())\n                {\n                    THROW_USER_ERROR(wsl::shared::Localization::MessageInvalidGuid(Value));\n                }\n\n                Output = guid.value();\n            }\n            else\n            {\n\n                // If this assert is hit, an unsupported type was passed.\n                static_assert(\n                    std::is_same_v<T, TString> || std::is_same_v<T, std::optional<TString>> ||\n                    std::is_same_v<T, std::filesystem::path> || std::is_same_v<T, const TChar*>);\n\n                Output = Value;\n            }\n            return 1;\n        }\n    }\n\n    std::vector<Argument> m_arguments;\n    int m_argc{};\n\n#ifdef WIN32\n\n    wil::unique_hlocal_ptr<LPWSTR[]> m_argv;\n\n#else\n\n    const char* const* m_argv{};\n\n#endif\n\n    int m_startIndex{};\n    const TChar* m_name{};\n};\n} // namespace wsl::shared\n\n#ifdef WIN32\n#undef THROW_USER_ERROR\n#endif"
  },
  {
    "path": "src/shared/inc/JsonUtils.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    JsonUtils.h\n\nAbstract:\n\n    This file contains various JSON helper methods.\n\n--*/\n\n#pragma once\n\n#include <nlohmann/json.hpp>\n#include \"stringshared.h\"\n\n#ifdef WIN32\n#include \"wslservice.h\"\n#include \"ExecutionContext.h\"\n#endif\n\n#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_FROM_ONLY(Type, ...) \\\n    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) \\\n    { \\\n        const Type nlohmann_json_default_obj{}; \\\n        NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) \\\n    }\n\nnamespace wsl::shared {\n\ntemplate <typename T>\nstd::string ToJson(const T& Value)\n{\n    nlohmann::json json;\n    to_json(json, Value);\n\n    return json.dump();\n}\n\ntemplate <typename T>\nstd::wstring ToJsonW(const T& Value)\n{\n    return wsl::shared::string::MultiByteToWide(ToJson(Value));\n}\n\ntemplate <typename T, typename TJson = nlohmann::json>\nT FromJson(const char* Value)\n{\n    try\n    {\n        auto json = TJson::parse(Value);\n        T object{};\n        from_json(json, object);\n\n        return object;\n    }\n    catch (const TJson::exception& e)\n    {\n\n#ifdef WIN32\n\n        THROW_HR_WITH_USER_ERROR(WSL_E_INVALID_JSON, wsl::shared::Localization::MessageInvalidJson(e.what()));\n\n#else\n        LOG_ERROR(\"Failed to deserialize json: '{}'. Error: {}\", Value, e.what());\n        THROW_ERRNO(EINVAL);\n\n#endif\n    }\n}\n\ntemplate <typename T, typename TJson = nlohmann::json>\nT FromJson(const wchar_t* Value)\n{\n    return FromJson<T, TJson>(wsl::shared::string::WideToMultiByte(Value).c_str());\n}\n\ntemplate <typename T>\nstd::string JsonEnumToString(T value)\n{\n    nlohmann::json json;\n    to_json(json, value);\n\n    return json.get<std::string>();\n}\n} // namespace wsl::shared\n\nnamespace nlohmann {\n\ntemplate <>\nstruct adl_serializer<std::wstring>\n{\n    static void to_json(json& j, const std::wstring& str)\n    {\n        j = wsl::shared::string::WideToMultiByte(str);\n    }\n\n    static void from_json(const json& j, std::wstring& str)\n    {\n        str = wsl::shared::string::MultiByteToWide(j.get<std::string>());\n    }\n};\n\ntemplate <typename T>\nstruct adl_serializer<std::optional<T>>\n{\n    static void to_json(json& j, const std::optional<T>& input)\n    {\n        if (input.has_value())\n        {\n            j = input.value();\n        }\n        else\n        {\n            j = nullptr;\n        }\n    }\n\n    static void from_json(const json& j, std::optional<T>& input)\n    {\n        if (!j.is_null())\n        {\n            input.emplace(); // Assumes that object is default constructible.\n            adl_serializer<T>::from_json(j, input.value());\n        }\n    }\n};\n\ntemplate <>\nstruct adl_serializer<GUID>\n{\n    static void to_json(json& j, const GUID& input)\n    {\n        j = wsl::shared::string::GuidToString<char>(input, wsl::shared::string::GuidToStringFlags::None);\n    }\n\n    static void from_json(const json& j, GUID& output)\n    {\n        const auto parsed = wsl::shared::string::ToGuid(j.get<std::string>());\n        if (parsed.has_value())\n        {\n            output = parsed.value();\n        }\n    }\n};\n\ntemplate <>\nstruct adl_serializer<wsl::shared::string::MacAddress>\n{\n    static void to_json(json& j, const wsl::shared::string::MacAddress& input)\n    {\n        j = wsl::shared::string::FormatMacAddress(input, '-');\n    }\n\n    static void from_json(const json& j, wsl::shared::string::MacAddress& output)\n    {\n        if (j.is_string())\n        {\n            auto parsed = wsl::shared::string::ParseMacAddressNoThrow(j.get<std::string>());\n            if (parsed.has_value())\n            {\n                output = std::move(parsed.value());\n            }\n        }\n    }\n};\n\n} // namespace nlohmann"
  },
  {
    "path": "src/shared/inc/SocketChannel.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    SocketChannel.h\n\nAbstract:\n\n    This file contains the SocketChannel helper class implementation.\n\n--*/\n\n#pragma once\n\n#include <mutex>\n#include \"socketshared.h\"\n#include \"lxinitshared.h\"\n\n#ifndef WIN32\n#include <assert.h>\n#include \"lxwil.h\"\n#include \"../../linux/init/util.h\"\nextern std::optional<bool> g_EnableSocketLogging;\n#endif\n\nnamespace wsl::shared {\n#ifdef WIN32\n\nusing TSocket = wil::unique_socket;\nusing TTimeout = DWORD;\n\nconstexpr DWORD DefaultSocketTimeout = INFINITE;\n\n#else\n\nusing TSocket = wil::unique_fd;\nusing TTimeout = const timeval*;\nconstexpr timeval* DefaultSocketTimeout = nullptr;\n\n#endif\n\nclass SocketChannel\n{\n\npublic:\n    SocketChannel() = default;\n\n    SocketChannel(const SocketChannel&) = delete;\n    SocketChannel(SocketChannel&& other)\n    {\n        *this = std::move(other);\n    }\n\n    SocketChannel& operator=(const SocketChannel&) = delete;\n    SocketChannel& operator=(SocketChannel&& other)\n    {\n        m_name = std::move(other.m_name);\n        m_socket = std::move(other.m_socket);\n\n#ifdef WIN32\n        m_exitEvent = std::move(other.m_exitEvent);\n#endif\n        m_ignore_sequence = other.m_ignore_sequence;\n\n        return *this;\n    }\n\n    // Note: 'name' must be a global string, since SocketChannel doesn't make a copy of it.\n    SocketChannel(TSocket&& socket, const char* name) : m_socket(std::move(socket)), m_name(name)\n    {\n    }\n\n#ifdef WIN32\n\n    SocketChannel(TSocket&& socket, const char* name, HANDLE exitEvent) :\n        m_socket(std::move(socket)), m_exitEvent(exitEvent), m_name(name)\n    {\n    }\n\n#endif\n\n    template <typename TMessage>\n    void SendMessage(gsl::span<gsl::byte> span)\n    {\n        // Ensure that no other thread is using this channel.\n        const std::unique_lock<std::mutex> lock{m_sendMutex, std::try_to_lock};\n        if (!lock.owns_lock())\n        {\n\n#ifdef WIN32\n\n            THROW_HR_MSG(E_UNEXPECTED, \"Incorrect channel usage detected on channel: %hs, message type: %hs\", m_name, ToString(TMessage::Type));\n\n#else\n\n            LOG_ERROR(\"Incorrect channel usage detected on channel: {}, message type: {}\", m_name, ToString(TMessage::Type));\n            THROW_ERRNO(EINVAL);\n\n#endif\n        }\n\n        THROW_INVALID_ARG_IF(m_name == nullptr || span.size() < sizeof(TMessage));\n\n        m_sent_messages++;\n\n        auto* header = gslhelpers::try_get_struct<MESSAGE_HEADER>(span);\n        WI_ASSERT(header->MessageSize == span.size());\n\n        header->SequenceNumber = m_sent_messages;\n\n#ifdef WIN32\n\n        auto sentBytes = wsl::windows::common::socket::Send(m_socket.get(), span, m_exitEvent);\n\n        WSL_LOG(\n            \"SentMessage\",\n            TraceLoggingValue(m_name, \"Name\"),\n            TraceLoggingValue(reinterpret_cast<const TMessage*>(span.data())->PrettyPrint().c_str(), \"Content\"),\n            TraceLoggingValue(sentBytes, \"SentBytes\"));\n\n#else\n\n        if (LoggingEnabled())\n        {\n            LOG_INFO(\"SentMessage on channel: {}: '{}'\", m_name, reinterpret_cast<const TMessage*>(span.data())->PrettyPrint().c_str());\n        }\n\n        if (UtilWriteBuffer(m_socket.get(), span.data(), span.size()) < 0)\n        {\n            LOG_ERROR(\"Failed to write message {}. Channel: {}\", header->MessageType, m_name);\n            THROW_LAST_ERROR();\n        }\n\n#endif\n    }\n\n    template <typename TMessage>\n    MESSAGE_HEADER& GetMessageHeader(TMessage& message)\n    {\n        if constexpr (std::is_same_v<TMessage, MESSAGE_HEADER>)\n        {\n            return message;\n        }\n        else\n        {\n            return message.Header;\n        }\n    }\n\n    template <typename TMessage>\n    void SendMessage(TMessage& message)\n    {\n        // Catch situations where the other SendMessage() method should be used\n        const auto& header = GetMessageHeader(message);\n        if (header.MessageSize != sizeof(message))\n        {\n#ifdef WIN32\n            THROW_HR_MSG(E_INVALIDARG, \"Incorrect header size for message type: %u on channel: %hs\", header.MessageType, m_name);\n#else\n            LOG_ERROR(\"Incorrect header size for message type: {} on channel: {}\", header.MessageType, m_name);\n            THROW_ERRNO(EINVAL);\n#endif\n        }\n\n        SendMessage<TMessage>(gslhelpers::struct_as_writeable_bytes(message));\n    }\n\n    template <typename TResult>\n    void SendResultMessage(TResult value)\n    {\n        RESULT_MESSAGE<TResult> Result{};\n        Result.Header.MessageSize = sizeof(Result);\n        Result.Header.MessageType = RESULT_MESSAGE<TResult>::Type;\n        Result.Result = value;\n\n        SendMessage(Result);\n    }\n\n    template <typename TMessage>\n    std::pair<TMessage*, gsl::span<gsl::byte>> ReceiveMessageOrClosed(TTimeout timeout = DefaultSocketTimeout)\n    {\n        WI_ASSERT(m_name != nullptr);\n\n        // Ensure that no other thread is using this channel.\n        const std::unique_lock<std::mutex> lock{m_receiveMutex, std::try_to_lock};\n        if (!lock.owns_lock())\n        {\n\n#ifdef WIN32\n\n            THROW_HR_MSG(E_UNEXPECTED, \"Incorrect channel usage detected on channel: %hs\", m_name);\n#else\n\n            LOG_ERROR(\"Incorrect channel usage detected on channel: {}\", m_name);\n            THROW_ERRNO(EINVAL);\n\n#endif\n        }\n\n        m_received_messages++;\n\n        auto receivedSpan = ReceiveImpl(TMessage::Type, timeout);\n        if (receivedSpan.empty())\n        {\n\n#ifdef WIN32\n            if (errno == HCS_E_CONNECTION_TIMEOUT)\n            {\n                THROW_HR_MSG(HCS_E_CONNECTION_TIMEOUT, \"Timeout: %d, expected type: %hs, channel: %hs\", timeout, ToString(TMessage::Type), m_name);\n            }\n#endif\n\n            return {nullptr, {}};\n        }\n\n        auto* message = gslhelpers::try_get_struct<TMessage>(receivedSpan);\n\n        if (message == nullptr)\n        {\n#ifdef WIN32\n            THROW_HR_MSG(\n                E_UNEXPECTED, \"Message size is too small: %zd, expected type: %hs, channel: %hs\", receivedSpan.size(), ToString(TMessage::Type), m_name);\n#else\n            LOG_ERROR(\"MessageSize is too small: {}, expected type: {}, channel: {}\", receivedSpan.size(), ToString(TMessage::Type), m_name);\n            THROW_ERRNO(EINVAL);\n#endif\n        }\n\n        ValidateMessageHeader(GetMessageHeader(*message), TMessage::Type, m_received_messages);\n\n#ifdef WIN32\n        WSL_LOG(\n            \"ReceivedMessage\", TraceLoggingValue(m_name, \"Name\"), TraceLoggingValue(message->PrettyPrint().c_str(), \"Content\"));\n#else\n        if (LoggingEnabled())\n        {\n            LOG_INFO(\"ReceivedMessage on channel: {}: '{}'\", m_name, message->PrettyPrint().c_str());\n        }\n#endif\n        return {message, receivedSpan};\n    }\n\n    template <typename TMessage>\n    TMessage& ReceiveMessage(gsl::span<gsl::byte>* responseSpan = nullptr, TTimeout timeout = DefaultSocketTimeout)\n    {\n        auto [message, span] = ReceiveMessageOrClosed<TMessage>(timeout);\n        if (message == nullptr)\n        {\n#ifdef WIN32\n            THROW_HR_MSG(E_UNEXPECTED, \"Expected message %hs, but socket %hs was closed\", ToString(TMessage::Type), m_name);\n#else\n            LOG_ERROR(\"ExpectedMessage {}, but socket {} was closed\", ToString(TMessage::Type), m_name);\n            THROW_ERRNO(EINVAL);\n#endif\n        }\n\n        if (responseSpan != nullptr)\n        {\n            *responseSpan = span;\n        }\n\n        return *message;\n    }\n\n    template <typename TSentMessage>\n    TSentMessage::TResponse& Transaction(gsl::span<gsl::byte> message, gsl::span<gsl::byte>* responseSpan = nullptr, TTimeout timeout = DefaultSocketTimeout)\n    {\n        SendMessage<TSentMessage>(message);\n\n        return ReceiveMessage<typename TSentMessage::TResponse>(responseSpan, timeout);\n    }\n\n    template <typename TSentMessage>\n    TSentMessage::TResponse& Transaction(TSentMessage& message, gsl::span<gsl::byte>* responseSpan = nullptr, TTimeout timeout = DefaultSocketTimeout)\n    {\n        WI_ASSERT(message.Header.MessageSize == sizeof(message));\n\n        return Transaction<TSentMessage>(gslhelpers::struct_as_writeable_bytes(message), responseSpan, timeout);\n    }\n\n    void Close()\n    {\n        m_socket.reset();\n    }\n\n    auto Socket() const\n    {\n        return m_socket.get();\n    }\n\n    void IgnoreSequenceNumbers()\n    {\n        m_ignore_sequence = true;\n    }\n\n#ifndef WIN32\n\n    static void EnableSocketLogging(bool enable)\n    {\n        g_EnableSocketLogging = enable;\n    }\n\n#endif\n\nprivate:\n#ifdef WIN32\n\n    gsl::span<gsl::byte> ReceiveImpl(auto expectedMessage, TTimeout timeout)\n    {\n        return wsl::shared::socket::RecvMessage(m_socket.get(), m_buffer, m_exitEvent, timeout);\n    }\n\n#else\n\n    gsl::span<gsl::byte> ReceiveImpl(auto expectedMessage, TTimeout timeout)\n    {\n        return wsl::shared::socket::RecvMessage(m_socket.get(), m_buffer, timeout);\n    }\n\n#endif\n\n    void ValidateMessageHeader(const MESSAGE_HEADER& header, LX_MESSAGE_TYPE expected, unsigned int expectedSequence) const\n    {\n        if (header.MessageSize < sizeof(header) || (expected != LxMiniInitMessageAny && header.MessageType != expected) ||\n            (!m_ignore_sequence && header.SequenceNumber != expectedSequence))\n        {\n#ifdef WIN32\n\n            THROW_HR_MSG(\n                E_UNEXPECTED,\n                \"Protocol error: Received message size: %u, type: %u, sequence: %u. Expected type: %u, expected sequence: %u, \"\n                \"channel: %hs\",\n                header.MessageSize,\n                header.MessageType,\n                header.SequenceNumber,\n                expected,\n                expectedSequence,\n                m_name);\n#else\n\n            LOG_ERROR(\n                \"Protocol error: Received message size: {}, type: {}, sequence: {}. Expected type: {}, expected sequence: {}, \"\n                \"channel: %s\",\n                header.MessageSize,\n                header.MessageType,\n                header.SequenceNumber,\n                expected,\n                expectedSequence,\n                m_name);\n\n            THROW_ERRNO(EINVAL);\n\n#endif\n        }\n    }\n\n#ifndef WIN32\n\n    static bool LoggingEnabled()\n    {\n        static std::once_flag flag;\n        std::call_once(flag, [&]() {\n            try\n            {\n                if (g_EnableSocketLogging.has_value())\n                {\n                    return;\n                }\n\n                auto content = UtilReadFileContent(\"/proc/cmdline\");\n                g_EnableSocketLogging = content.find(\"WSL_SOCKET_LOG\") != std::string::npos;\n            }\n            catch (...)\n            {\n                LOG_CAUGHT_EXCEPTION();\n                g_EnableSocketLogging = false;\n            }\n        });\n\n        return g_EnableSocketLogging.value();\n    }\n\n#endif\n\n    TSocket m_socket{};\n    std::vector<gsl::byte> m_buffer;\n\n#ifdef WIN32\n\n    HANDLE m_exitEvent{};\n\n#endif\n    uint32_t m_sent_messages = 0;\n    uint32_t m_received_messages = 0;\n    bool m_ignore_sequence = false;\n    const char* m_name{};\n    std::mutex m_sendMutex;\n    std::mutex m_receiveMutex;\n};\n} // namespace wsl::shared"
  },
  {
    "path": "src/shared/inc/conncheckshared.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    conncheckshared.h\r\n\r\nAbstract:\r\n\r\n    This file contains a simple network connectivity check shared between Windows and Linux.\r\n--*/\r\n\r\n#pragma once\r\n\r\n#if defined(_MSC_VER)\r\n// Windows\r\n#include <winsock2.h>\r\n#include <windows.h>\r\n#define ConnCheckGetLastError() WSAGetLastError()\r\n#define CONNCHECK_ERROR_PENDING WSAEWOULDBLOCK\r\n#else\r\n// Linux\r\n#include <netdb.h>\r\n#include <errno.h>\r\n#include <fcntl.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/param.h>\r\n#include <sys/select.h>\r\n#include <sys/socket.h>\r\n#include <sys/time.h>\r\n#include <arpa/inet.h>\r\n#include <wchar.h>\r\n#define ConnCheckGetLastError() errno\r\n#define CONNCHECK_ERROR_PENDING EINPROGRESS\r\n#endif\r\n\r\nnamespace wsl::shared::conncheck {\r\n\r\n#if defined(_MSC_VER)\r\nusing unique_socket = wil::unique_socket;\r\n#else\r\nusing unique_socket = wil::unique_fd;\r\n#endif\r\n\r\nenum class ConnCheckStatus\r\n{\r\n    InProgress,\r\n    Success,\r\n    FailureGetAddrInfo,\r\n    FailureConfig,\r\n    FailureSocketConnect,\r\n};\r\n\r\nstruct ConnCheckResult\r\n{\r\n    ConnCheckStatus Ipv4Status{};\r\n    ConnCheckStatus Ipv6Status{};\r\n};\r\n\r\ninline unique_socket ConnCheckConfigureSocket(int family, int socktype, int protocol)\r\n{\r\n    unique_socket sock{::socket(family, socktype, protocol)};\r\n    if (!sock)\r\n    {\r\n        throw std::runtime_error(std::format(\"CheckConnection: socket() failed: {}\", ConnCheckGetLastError()));\r\n    }\r\n\r\n#if defined(_MSC_VER)\r\n    unsigned long value = 1;\r\n    const int status = ::ioctlsocket(sock.get(), FIONBIO, &value);\r\n    if (status != 0)\r\n    {\r\n        throw std::runtime_error(std::format(\"CheckConnection: ioctlsocket(FIONBIO) failed: {}\", ConnCheckGetLastError()));\r\n    }\r\n#else\r\n    int value = fcntl(sock.get(), F_GETFL, 0);\r\n    if (value < 0)\r\n    {\r\n        throw std::runtime_error(std::format(\"CheckConnection: fcntl(F_GETFL) failed: {}\", ConnCheckGetLastError()));\r\n    }\r\n    value = fcntl(sock.get(), F_SETFL, value | O_NONBLOCK);\r\n    if (value < 0)\r\n    {\r\n        throw std::runtime_error(std::format(\"CheckConnection: fcntl(F_SETFL) failed: {}\", ConnCheckGetLastError()));\r\n    }\r\n#endif\r\n\r\n    return sock;\r\n}\r\n\r\ninline unique_socket ConnCheckConnectSocket(int family, const char* hostname, const char* port, ConnCheckResult* resultStatus) noexcept\r\n{\r\n    // update the ConnCheckStatus as we attempt to connect\r\n    ConnCheckStatus* connCheckStatus = (family == AF_INET) ? &(resultStatus->Ipv4Status) : &(resultStatus->Ipv6Status);\r\n    unique_socket sock;\r\n    try\r\n    {\r\n        // first step, try to resolve the name\r\n        *connCheckStatus = ConnCheckStatus::FailureGetAddrInfo;\r\n\r\n        printf(\"CheckConnection: resolving the name %s [%s]\\n\", hostname, (family == AF_INET) ? \"AF_INET\" : \"AF_INET6\");\r\n        addrinfo* servinfo = nullptr;\r\n        const auto freeAddrInfoOnExit = wil::scope_exit([&] {\r\n            if (servinfo)\r\n            {\r\n                freeaddrinfo(servinfo);\r\n            }\r\n        });\r\n\r\n        addrinfo hints{};\r\n        hints.ai_family = family;\r\n        hints.ai_socktype = SOCK_STREAM;\r\n        hints.ai_flags = AI_NUMERICSERV;\r\n        auto status = getaddrinfo(hostname, port, &hints, &servinfo);\r\n        if (status != 0)\r\n        {\r\n            throw std::runtime_error(std::format(\"CheckConnection: getaddrinfo() failed: {}\", status));\r\n        }\r\n\r\n        // next configure the socket\r\n        *connCheckStatus = ConnCheckStatus::FailureConfig;\r\n        sock = ConnCheckConfigureSocket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);\r\n\r\n        const void* pAddr = (AF_INET == family)\r\n                                ? static_cast<const void*>(&(reinterpret_cast<sockaddr_in*>(servinfo->ai_addr))->sin_addr)\r\n                                : static_cast<const void*>(&(reinterpret_cast<sockaddr_in6*>(servinfo->ai_addr))->sin6_addr);\r\n        char dst[INET6_ADDRSTRLEN * 2]{};\r\n        printf(\"CheckConnection: connecting to %s\\n\", inet_ntop(servinfo->ai_family, pAddr, dst, sizeof(dst)));\r\n\r\n        // next connect the socket\r\n        *connCheckStatus = ConnCheckStatus::FailureSocketConnect;\r\n        status = connect(sock.get(), servinfo->ai_addr, static_cast<int>(servinfo->ai_addrlen));\r\n        if (status != 0 && ConnCheckGetLastError() != CONNCHECK_ERROR_PENDING)\r\n        {\r\n            throw std::runtime_error(std::format(\"CheckConnection: connect() failed: {}\", ConnCheckGetLastError()));\r\n        }\r\n\r\n        // success\r\n        *connCheckStatus = ConnCheckStatus::InProgress;\r\n    }\r\n    CATCH_LOG()\r\n\r\n    return sock;\r\n}\r\n\r\n// Attempts to establish a TCPv4 and a TCPv6 connection to a port on a host.\r\n// ipv6hostname is an optional parameter in case the IPv6 equivalent hostname is different.\r\n//     example: www.msftconnecttest.com and ipv6.msftconnecttest.com\r\n// This API is blocking/synchronous.\r\ninline ConnCheckResult CheckConnection(const char* hostname, const char* ipv6hostname, const char* port)\r\n{\r\n    ConnCheckResult result{};\r\n    result.Ipv4Status = ConnCheckStatus::InProgress;\r\n    result.Ipv6Status = ConnCheckStatus::InProgress;\r\n\r\n    if (ipv6hostname == nullptr)\r\n    {\r\n        ipv6hostname = hostname;\r\n    }\r\n\r\n    const auto v4sock = ConnCheckConnectSocket(AF_INET, hostname, port, &result);\r\n    const auto v6sock = ConnCheckConnectSocket(AF_INET6, ipv6hostname, port, &result);\r\n    const auto startTime = std::chrono::steady_clock::now();\r\n\r\n    while (result.Ipv4Status == ConnCheckStatus::InProgress || result.Ipv6Status == ConnCheckStatus::InProgress)\r\n    {\r\n        constexpr long maxMillisecondsElapsed = 5000;\r\n        const auto currentTime = std::chrono::steady_clock::now();\r\n        const auto elapsedTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime);\r\n        if (elapsedTimeMs.count() >= maxMillisecondsElapsed)\r\n        {\r\n            if (result.Ipv4Status == ConnCheckStatus::InProgress)\r\n            {\r\n                wprintf(L\"CheckConnection: result.Ipv4Status = ConnCheckStatus::FailureSocketConnect\\n\");\r\n                result.Ipv4Status = ConnCheckStatus::FailureSocketConnect;\r\n            }\r\n            if (result.Ipv6Status == ConnCheckStatus::InProgress)\r\n            {\r\n                wprintf(L\"CheckConnection: result.Ipv6Status = ConnCheckStatus::FailureSocketConnect\\n\");\r\n                result.Ipv6Status = ConnCheckStatus::FailureSocketConnect;\r\n            }\r\n            // bailing if we have timed out\r\n            break;\r\n        }\r\n\r\n        fd_set writeSocketSet{};\r\n        if (result.Ipv4Status == ConnCheckStatus::InProgress)\r\n        {\r\n            FD_SET(v4sock.get(), &writeSocketSet);\r\n        }\r\n        if (result.Ipv6Status == ConnCheckStatus::InProgress)\r\n        {\r\n            FD_SET(v6sock.get(), &writeSocketSet);\r\n        }\r\n\r\n        const int maxsocket = std::max(\r\n            (result.Ipv4Status == ConnCheckStatus::InProgress) ? (int)v4sock.get() : -1,\r\n            (result.Ipv6Status == ConnCheckStatus::InProgress) ? (int)v6sock.get() : -1);\r\n        if (maxsocket != -1)\r\n        {\r\n            timeval timeout{};\r\n            timeout.tv_sec = static_cast<long>(maxMillisecondsElapsed - elapsedTimeMs.count()) / 1000;\r\n            wprintf(L\"CheckConnection: arming select for %d seconds\\n\", timeout.tv_sec);\r\n            const auto status = select(maxsocket + 1, nullptr, &writeSocketSet, nullptr, &timeout);\r\n            if (status == 0)\r\n            {\r\n                if (result.Ipv4Status == ConnCheckStatus::InProgress)\r\n                {\r\n                    wprintf(L\"CheckConnection: result.Ipv4Status = ConnCheckStatus::FailureSocketConnect (timeout)\\n\");\r\n                    result.Ipv4Status = ConnCheckStatus::FailureSocketConnect;\r\n                }\r\n                if (result.Ipv6Status == ConnCheckStatus::InProgress)\r\n                {\r\n                    wprintf(L\"CheckConnection: result.Ipv6Status = ConnCheckStatus::FailureSocketConnect (timeout)\\n\");\r\n                    result.Ipv6Status = ConnCheckStatus::FailureSocketConnect;\r\n                }\r\n            }\r\n            else if (status < 0)\r\n            {\r\n                const auto error = errno;\r\n                if (result.Ipv4Status == ConnCheckStatus::InProgress)\r\n                {\r\n                    wprintf(L\"CheckConnection: result.Ipv4Status = ConnCheckStatus::FailureSocketConnect (%d)\\n\", error);\r\n                    result.Ipv4Status = ConnCheckStatus::FailureSocketConnect;\r\n                }\r\n                if (result.Ipv6Status == ConnCheckStatus::InProgress)\r\n                {\r\n                    wprintf(L\"CheckConnection: result.Ipv6Status = ConnCheckStatus::FailureSocketConnect (%d)\\n\", error);\r\n                    result.Ipv6Status = ConnCheckStatus::FailureSocketConnect;\r\n                }\r\n            }\r\n            else\r\n            {\r\n                // Success.\r\n                if (v4sock)\r\n                {\r\n                    if (FD_ISSET(v4sock.get(), &writeSocketSet))\r\n                    {\r\n                        wprintf(L\"CheckConnection: v4 succeeded\\n\");\r\n                        result.Ipv4Status = ConnCheckStatus::Success;\r\n                    }\r\n                }\r\n\r\n                if (v6sock)\r\n                {\r\n                    if (FD_ISSET(v6sock.get(), &writeSocketSet))\r\n                    {\r\n                        wprintf(L\"CheckConnection: v6 succeeded\\n\");\r\n                        result.Ipv6Status = ConnCheckStatus::Success;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    wprintf(L\"CheckConnection: returning v4 (%d) v6 (%d)\\n\", result.Ipv4Status, result.Ipv6Status);\r\n    return result;\r\n}\r\n} // namespace wsl::shared::conncheck"
  },
  {
    "path": "src/shared/inc/defs.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    defs.h\n\nAbstract:\n\n    This file contains shared platform definitions.\n\n--*/\n\n#pragma once\n\n#if defined(_MSC_VER)\n#define THROW_INVALID_ARG_IF(condition) THROW_HR_IF(E_INVALIDARG, condition)\n#elif defined(__GNUC__)\n#define THROW_INVALID_ARG_IF(condition) THROW_ERRNO_IF(EINVAL, condition)\n#define _stricmp strcasecmp\n#define _wcsicmp wcscasecmp\n#endif\n\n#define NON_COPYABLE(Type) \\\n    Type(const Type&) = delete; \\\n    Type& operator=(const Type&) = delete;\n\n#define NON_MOVABLE(Type) \\\n    Type(Type&&) = delete; \\\n    Type& operator=(Type&&) = delete;\n\n#define DEFAULT_MOVABLE(Type) \\\n    Type(Type&&) = default; \\\n    Type& operator=(Type&&) = default;\n\nnamespace wsl::shared {\n\ninline constexpr std::uint32_t VersionMajor = WSL_PACKAGE_VERSION_MAJOR;\ninline constexpr std::uint32_t VersionMinor = WSL_PACKAGE_VERSION_MINOR;\ninline constexpr std::uint32_t VersionRevision = WSL_PACKAGE_VERSION_REVISION;\ninline constexpr std::tuple<uint32_t, uint32_t, uint32_t> PackageVersion{VersionMajor, VersionMinor, VersionRevision};\n\n#ifdef WSL_OFFICIAL_BUILD\n\ninline constexpr bool OfficialBuild = true;\n\n#else\n\ninline constexpr bool OfficialBuild = false;\n\n#endif\n\n#ifdef DEBUG\n\ninline constexpr bool Debug = true;\n\n#else\n\ninline constexpr bool Debug = false;\n\n#endif\n\n#ifdef _AMD64_\n\ninline constexpr bool Arm64 = false;\n\n#elif _ARM64_\n\ninline constexpr bool Arm64 = true;\n\n#else\n\n#error Unsupported compiler or build environment\n\n#endif\n\n} // namespace wsl::shared\n"
  },
  {
    "path": "src/shared/inc/gslhelpers.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    gslhelpers.h\n\nAbstract:\n\n    This file contains various GSL helper methods.\n\n--*/\n\n#pragma once\n\n#include <gsl/span>\n\nnamespace gslhelpers {\n\ntemplate <class T>\nconstexpr bool is_standard_layout_v = std::is_standard_layout_v<T>;\n\nnamespace details {\n    //\n    // Helper function, do not use outside of this header.\n    //\n    // A performant bounds check that ensures that it is valid to read\n    // access_size starting from span[offset].\n    //\n\n    template <class SpanType, class OffsetType, class AccessSizeType>\n    __forceinline bool is_range_okay(SpanType s, OffsetType offset, AccessSizeType access_size)\n    {\n        //\n        // Span size_bytes returns an unsigned value.\n        //\n        // If offset is signed, casting to unsigned results in it becoming a massive\n        // unsigned value.\n        //\n\n        return (\n            (s.size_bytes() >= static_cast<size_t>(access_size)) &&\n            (s.size_bytes() - static_cast<size_t>(access_size) >= static_cast<size_t>(offset)));\n    }\n\n    //\n    // Helper function, do not use outside of this header.\n    //\n    // A performant bounds check that ensures that it is valid to dereference type T\n    // from &span[offset].\n    //\n\n    template <class T, class SpanType, class OffsetType>\n    __forceinline bool is_range_okay(SpanType s, OffsetType offset)\n    {\n        return is_range_okay(s, offset, sizeof(T));\n    }\n} // namespace details\n\ntemplate <class T>\nstruct CanAliasStorage\n    : public std::bool_constant<\n          std::is_same<T, gsl::byte>::value || std::is_same<T, const gsl::byte>::value || std::is_same<T, unsigned char>::value ||\n          std::is_same<T, const unsigned char>::value || std::is_same<T, signed char>::value ||\n          std::is_same<T, const signed char>::value || std::is_same<T, char>::value || std::is_same<T, const char>::value>\n{\n};\n\n//\n// get_struct is used to safely retrieve a pointer to a structure contained\n// within a span. This is useful for tasks such as protocol parsing.\n//\n// offset denotes the offset in bytes from the start of the span where the\n// structure begins.\n//\n\ntemplate <class T, class SpanType, class OffsetType>\n__forceinline T* get_struct(SpanType s, OffsetType offset)\n{\n    static_assert(\n        CanAliasStorage<typename SpanType::element_type>::value, \"You can only call get_struct on a span of bytes or chars.\");\n    static_assert(is_standard_layout_v<T>, \"Your destination type should be standard-layout\");\n\n    Expects(details::is_range_okay<T>(s, offset));\n\n    return reinterpret_cast<T*>(s.data() + offset);\n}\n\ntemplate <class T, class SpanType>\n__forceinline T* get_struct(SpanType s)\n{\n    return get_struct<T>(s, 0);\n}\n\n//\n// try_get_struct is used to conditionally retrieve a pointer to a structure\n// contained within a span. This is useful for tasks such as protocol parsing.\n// This helper differs from get_struct in that it does not failfast in the\n// case of a range violation, but rather returns nullptr.\n//\n// offset denotes the offset in bytes from the start of the span where the\n// structure begins.\n//\n\ntemplate <class T, class SpanType, class OffsetType>\n__forceinline T* try_get_struct(SpanType s, OffsetType offset)\n{\n    static_assert(\n        CanAliasStorage<typename SpanType::element_type>::value, \"You can only call try_get_struct on a span of bytes or chars.\");\n    static_assert(is_standard_layout_v<T>, \"Your destination type should be standard-layout\");\n\n    if (details::is_range_okay<T>(s, offset))\n    {\n        return reinterpret_cast<T*>(s.data() + offset);\n    }\n    else\n    {\n        return nullptr;\n    }\n}\n\ntemplate <class T, class SpanType>\n__forceinline T* try_get_struct(SpanType s)\n{\n    return try_get_struct<T>(s, 0);\n}\n\ntemplate <class ByteType = gsl::byte, class T>\n__forceinline gsl::span<const ByteType> struct_as_bytes(const T& structure)\n{\n    static_assert(CanAliasStorage<ByteType>::value, \"struct_as_bytes may only convert to a span of bytes or chars.\");\n    static_assert(is_standard_layout_v<T>, \"Your input structure type should be standard-layout\");\n\n    return {reinterpret_cast<const ByteType*>(&structure), sizeof(structure)};\n}\n\n//\n// Convert a struct to a writeable span of bytes.\n// Accepts alternate single byte types but defaults to gsl::byte.\n//\n\ntemplate <class ByteType = gsl::byte, class T>\n__forceinline gsl::span<ByteType> struct_as_writeable_bytes(T& structure)\n{\n    static_assert(CanAliasStorage<ByteType>::value, \"struct_as_writeable_bytes may only convert to a span of bytes or chars.\");\n    static_assert(is_standard_layout_v<T>, \"Your input structure type should be standard-layout\");\n\n    return {reinterpret_cast<ByteType*>(&structure), sizeof(structure)};\n}\n\ntemplate <class NewClass, class CurrentSpan>\n__forceinline gsl::span<NewClass> convert_span_truncate(CurrentSpan s)\n{\n    NewClass* ptr = reinterpret_cast<NewClass*>(s.data());\n    return {ptr, s.size_bytes() / sizeof(NewClass)};\n}\n\n//\n// Convert a span of one type to another type. Throws if\n// the new type doesn't fit correctly in the existing span.\n//\n\ntemplate <class NewClass, class CurrentSpan>\n__forceinline gsl::span<NewClass> convert_span(CurrentSpan s)\n{\n    Expects(s.size_bytes() % sizeof(NewClass) == 0);\n\n    return convert_span_truncate<NewClass, CurrentSpan>(s);\n}\n} // namespace gslhelpers\n"
  },
  {
    "path": "src/shared/inc/hns_schema.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    hcs_schema.h\n\nAbstract:\n\n    This file contains the host networking service schema definitions.\n\n--*/\n\n#pragma once\n#include <variant>\n#include \"JsonUtils.h\"\n\nnamespace wsl::shared::hns {\n\nenum NetworkFlags\n{\n    None = 0,\n    EnableDns = 1,\n    EnableDhcp = 2,\n    EnableMirroring = 4,\n    EnableNonPersistent = 8,\n    EnablePersistent = 16,\n    IsolateVSwitch = 32,\n    EnableFlowSteering = 64,\n    DisableSharing = 128,\n    EnableFirewall = 256,\n    SuppressMediaDisconnect = 512,\n    DisableHostPort = 1024,\n    WeakHostReceiveAdapter = 2048,\n    WeakHostSendAdapter = 4096,\n    EnableIov = 8192,\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(NetworkFlags);\n\nstruct HostComputeQuery\n{\n    std::uint64_t Flags{};\n    std::string Filter{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(HostComputeQuery, Flags, Filter);\n};\n\nstruct Version\n{\n    uint32_t Major{};\n    uint32_t Minor{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Version, Major, Minor);\n};\n\nenum class EndpointPolicyType\n{\n    PortName = 9,\n    Firewall = 18\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    EndpointPolicyType,\n    {\n        {EndpointPolicyType::PortName, \"PortName\"},\n        {EndpointPolicyType::Firewall, \"Firewall\"},\n    })\n\nstruct PortnameEndpointPolicySetting\n{\n    std::wstring Name;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(PortnameEndpointPolicySetting, Name);\n};\n\nenum class FirewallPolicyFlags\n{\n    None = 0,\n    ConstrainedInterface = 1\n};\n\nstruct FirewallPolicySetting\n{\n    GUID VmCreatorId{};\n    FirewallPolicyFlags PolicyFlags{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(FirewallPolicySetting, VmCreatorId, PolicyFlags);\n};\n\ntemplate <typename T>\nstruct EndpointPolicy\n{\n    EndpointPolicyType Type{};\n    T Settings{};\n};\n\ntemplate <typename T>\ninline void to_json(nlohmann::json& j, const EndpointPolicy<T>& policy)\n{\n    j = nlohmann::json{{\"Type\", policy.Type}, {\"Settings\", policy.Settings}};\n}\n\nstruct IpConfig\n{\n    std::wstring IpAddress;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(IpConfig, IpAddress);\n};\n\ntemplate <typename... Args>\ninline void to_json(nlohmann::json& j, const std::variant<Args...>& variant)\n{\n    std::visit<void>([&j](auto e) { to_json(j, e); }, variant);\n}\n\nstruct HostComputeEndpoint\n{\n    std::vector<std::variant<EndpointPolicy<PortnameEndpointPolicySetting>, EndpointPolicy<FirewallPolicySetting>>> Policies;\n    std::vector<IpConfig> IpConfigurations;\n    Version SchemaVersion{};\n    GUID HostComputeNetwork{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(HostComputeEndpoint, Policies, IpConfigurations, SchemaVersion, HostComputeNetwork);\n};\n\nstruct InterfaceConstraint\n{\n    GUID InterfaceGuid{};\n    uint32_t InterfaceIndex{};\n    uint32_t InterfaceMediaType{};\n    std::wstring InterfaceAlias{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InterfaceConstraint, InterfaceGuid, InterfaceIndex, InterfaceMediaType, InterfaceAlias);\n};\n\nstruct HNSEndpoint\n{\n    std::wstring IPAddress;\n    std::wstring MacAddress;\n    std::wstring GatewayAddress;\n    std::wstring PortFriendlyName;\n    GUID VirtualNetwork{};\n    std::wstring VirtualNetworkName;\n    std::wstring Name;\n    GUID ID{};\n    uint8_t PrefixLength{};\n    InterfaceConstraint InterfaceConstraint{};\n    std::wstring DNSServerList;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(\n        HNSEndpoint, IPAddress, MacAddress, GatewayAddress, PortFriendlyName, VirtualNetwork, VirtualNetworkName, Name, ID, PrefixLength, InterfaceConstraint, DNSServerList);\n};\n\nstruct Route\n{\n    std::wstring NextHop;\n    std::wstring DestinationPrefix;\n    uint8_t SitePrefixLength{};\n    uint32_t Metric{};\n    uint16_t Family{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Route, NextHop, DestinationPrefix, SitePrefixLength, Metric, Family);\n};\n\nenum class ModifyRequestType\n{\n    Add = 0,\n    Remove = 1,\n    Update = 2,\n    Refresh = 3,\n    Reset = 4,\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    ModifyRequestType,\n    {\n        {ModifyRequestType::Add, \"Add\"},\n        {ModifyRequestType::Remove, \"Remove\"},\n        {ModifyRequestType::Update, \"Update\"},\n        {ModifyRequestType::Refresh, \"Refresh\"},\n        {ModifyRequestType::Reset, \"Reset\"},\n    })\n\nenum class GuestEndpointResourceType\n{\n    Interface = 0,\n    Route = 1,\n    IPAddress = 2,\n    DNS = 3,\n    MacAddress = 6,\n    Neighbor = 10,\n    Port = 15\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    GuestEndpointResourceType,\n    {\n        {GuestEndpointResourceType::Interface, \"Interface\"},\n        {GuestEndpointResourceType::Route, \"Route\"},\n        {GuestEndpointResourceType::IPAddress, \"IPAddress\"},\n        {GuestEndpointResourceType::DNS, \"DNS\"},\n        {GuestEndpointResourceType::MacAddress, \"MacAddress\"},\n        {GuestEndpointResourceType::Neighbor, \"Neighbor\"},\n        {GuestEndpointResourceType::Port, \"Port\"},\n    })\n\nstruct DNS\n{\n    std::wstring Domain;\n    std::wstring Search;\n    std::wstring ServerList;\n    std::wstring Options;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(DNS, Domain, Search, ServerList, Options);\n};\n\nstruct IPAddress\n{\n    std::wstring Address;\n    uint16_t Family{};\n    uint8_t OnLinkPrefixLength{};\n    uint8_t PrefixOrigin{};\n    uint8_t SuffixOrigin{};\n    uint32_t PreferredLifetime{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(IPAddress, Address, Family, OnLinkPrefixLength, PrefixOrigin, SuffixOrigin, PreferredLifetime);\n};\n\nenum class OperationType\n{\n    Create,\n    Update,\n    Remove\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    OperationType, {{OperationType::Create, \"Create\"}, {OperationType::Update, \"Update\"}, {OperationType::Remove, \"Remove\"}})\n\nstruct LoopbackRoutesRequest\n{\n    std::wstring targetDeviceName;\n    OperationType operation{};\n    uint32_t family{};\n    std::wstring ipAddress;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(LoopbackRoutesRequest, operation, targetDeviceName, family, ipAddress);\n};\n\nstruct NetworkInterface\n{\n    bool Connected{};\n    uint32_t NlMtu{};\n    uint32_t Metric{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(NetworkInterface, Connected, NlMtu, Metric);\n};\n\nenum class InitialIpConfigurationNotificationFlags\n{\n    None = 0x0,\n    SkipPrimaryRoutingTableUpdate = 0x1,\n    SkipLoopbackRouteReset = 0x2\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(InitialIpConfigurationNotificationFlags);\n\nstruct InitialIpConfigurationNotification\n{\n    std::wstring targetDeviceName;\n    InitialIpConfigurationNotificationFlags flags{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InitialIpConfigurationNotification, targetDeviceName, flags);\n};\n\nstruct VmNicCreatedNotification\n{\n    GUID adapterId{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VmNicCreatedNotification, adapterId);\n};\n\nenum class DeviceType\n{\n    Bond,\n    Loopback,\n    VirtualWifi,\n    VirtualTunnel,\n    VirtualCellular\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    DeviceType,\n    {\n        {DeviceType::Bond, \"Bond\"},\n        {DeviceType::Loopback, \"Loopback\"},\n        {DeviceType::VirtualWifi, \"VirtualWifi\"},\n        {DeviceType::VirtualTunnel, \"VirtualTunnel\"},\n        {DeviceType::VirtualCellular, \"VirtualCellular\"},\n    })\n\nstruct CreateDeviceRequest\n{\n    DeviceType type{};\n    std::wstring deviceName;\n    std::optional<GUID> lowerEdgeAdapterId;\n    std::optional<std::wstring> lowerEdgeDeviceName;\n};\n\nNLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_FROM_ONLY(CreateDeviceRequest, type, deviceName, lowerEdgeAdapterId, lowerEdgeDeviceName);\n\ninline void to_json(nlohmann::json& j, const CreateDeviceRequest& request)\n{\n    j = nlohmann::json{{\"type\", request.type}, {\"deviceName\", request.deviceName}};\n\n    if (request.lowerEdgeAdapterId.has_value())\n    {\n        j[\"lowerEdgeAdapterId\"] = request.lowerEdgeAdapterId.value();\n    }\n\n    if (request.lowerEdgeDeviceName.has_value())\n    {\n        j[\"lowerEdgeDeviceName\"] = request.lowerEdgeDeviceName.value();\n    }\n}\n\nstruct ModifyGuestDeviceSettingRequest\n{\n    std::wstring targetDeviceName;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ModifyGuestDeviceSettingRequest, targetDeviceName);\n};\n\nstruct InterfaceNetFilterRequest\n{\n    std::wstring targetDeviceName;\n    OperationType operation{};\n    uint16_t ephemeralPortRangeStart{};\n    uint16_t ephemeralPortRangeEnd{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InterfaceNetFilterRequest, targetDeviceName, operation, ephemeralPortRangeStart, ephemeralPortRangeEnd);\n};\n\nstruct MacAddress\n{\n    std::string PhysicalAddress;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(MacAddress, PhysicalAddress);\n};\n\ntemplate <typename T>\nstruct ModifyGuestEndpointSettingRequest\n{\n    ModifyRequestType RequestType{};\n    GuestEndpointResourceType ResourceType{};\n    T Settings{};\n\n    std::optional<std::wstring> targetDeviceName;\n};\n\nNLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_FROM_ONLY(\n    ModifyGuestEndpointSettingRequest<NetworkInterface>, RequestType, ResourceType, targetDeviceName, Settings);\n\ntemplate <>\nstruct ModifyGuestEndpointSettingRequest<void>\n{\n    ModifyRequestType RequestType{};\n    GuestEndpointResourceType ResourceType{};\n    std::optional<std::wstring> targetDeviceName;\n};\n\nNLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_FROM_ONLY(ModifyGuestEndpointSettingRequest<void>, RequestType, ResourceType, targetDeviceName);\n\ntemplate <typename T>\ninline void to_json(nlohmann::json& j, const ModifyGuestEndpointSettingRequest<T>& request)\n{\n    j = nlohmann::json{{\"ResourceType\", request.ResourceType}, {\"RequestType\", request.RequestType}};\n\n    if (request.targetDeviceName.has_value())\n    {\n        j[\"targetDeviceName\"] = request.targetDeviceName.value();\n    }\n\n    if constexpr (!std::is_same_v<T, void>)\n    {\n        j[\"Settings\"] = request.Settings;\n    }\n}\n\nstruct IpSubnet\n{\n    std::wstring IpAddressPrefix;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(IpSubnet, IpAddressPrefix);\n};\n\nstruct Subnet\n{\n    std::wstring GatewayAddress;\n    std::wstring AddressPrefix;\n    std::vector<IpSubnet> IpSubnets;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Subnet, GatewayAddress, AddressPrefix, IpSubnets);\n};\n\nstruct HNSNetwork\n{\n    std::wstring ID;\n    std::wstring Name;\n    std::wstring SourceMac;\n    std::wstring DNSSuffix;\n    std::wstring DNSServerList;\n    std::wstring DNSDomain;\n    std::vector<Subnet> Subnets;\n    NetworkFlags Flags{};\n    InterfaceConstraint InterfaceConstraint{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(HNSNetwork, ID, Name, SourceMac, DNSSuffix, DNSServerList, DNSDomain, Subnets, Flags, InterfaceConstraint);\n};\n\nenum class NetworkMode\n{\n    NAT,\n    ICS,\n    ConstrainedICS\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    NetworkMode,\n    {\n        {NetworkMode::NAT, \"NAT\"},\n        {NetworkMode::ICS, \"ICS\"},\n        {NetworkMode::ConstrainedICS, \"ConstrainedICS\"},\n    })\n\nstruct Network\n{\n    std::wstring Name;\n    NetworkMode Type{};\n    bool IsolateSwitch{};\n    NetworkFlags Flags{};\n    std::vector<Subnet> Subnets;\n    InterfaceConstraint InterfaceConstraint{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Network, Name, Type, IsolateSwitch, Subnets, Flags, InterfaceConstraint);\n};\n\nstruct NotificationBase\n{\n    GUID ID{};\n    uint32_t Flags{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(NotificationBase, ID, Flags);\n};\n\nenum class RpcEndpointType\n{\n    LRpc\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    RpcEndpointType,\n    {\n        {RpcEndpointType::LRpc, \"LRpc\"},\n    })\n\nstruct RpcConnectionInformation\n{\n    RpcEndpointType EndpointType{};\n    GUID ObjectUuid{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(RpcConnectionInformation, EndpointType, ObjectUuid);\n};\n\nenum class GuestNetworkServiceFlags\n{\n    IsFlowsteered = 1,\n    IsFlowsteeredSelfManaged = 2,\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(GuestNetworkServiceFlags);\n\nstruct GuestNetworkService\n{\n    GUID VirtualMachineId{};\n    bool MirrorHostNetworking{};\n    Version SchemaVersion{};\n    RpcConnectionInformation GnsRpcServerInformation{};\n    GuestNetworkServiceFlags Flags{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GuestNetworkService, VirtualMachineId, MirrorHostNetworking, SchemaVersion, GnsRpcServerInformation, Flags);\n};\n\nenum class GuestNetworkServiceState\n{\n    None,\n    Created,\n    Bootstrapping,\n    Synchronized,\n    Paused,\n    Desynchronized,\n    Rehydrating,\n    Degraded,\n    Destroyed,\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    GuestNetworkServiceState,\n    {\n        {GuestNetworkServiceState::None, \"None\"},\n        {GuestNetworkServiceState::Created, \"Created\"},\n        {GuestNetworkServiceState::Bootstrapping, \"Bootstrapping\"},\n        {GuestNetworkServiceState::Synchronized, \"Synchronized\"},\n        {GuestNetworkServiceState::Paused, \"Paused\"},\n        {GuestNetworkServiceState::Desynchronized, \"Desynchronized\"},\n        {GuestNetworkServiceState::Rehydrating, \"Rehydrating\"},\n        {GuestNetworkServiceState::Degraded, \"Degraded\"},\n        {GuestNetworkServiceState::Destroyed, \"Destroyed\"},\n    })\n\nstruct GuestNetworkServiceStateRequest\n{\n    GuestNetworkServiceState State{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GuestNetworkServiceStateRequest, State);\n};\n\nenum GuestNetworkServiceResourceType\n{\n    State\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(GuestNetworkServiceResourceType, {{GuestNetworkServiceResourceType::State, \"State\"}})\n\nstruct ModifyGuestNetworkServiceSettingRequest\n{\n    GuestNetworkServiceResourceType ResourceType{};\n    GuestNetworkServiceStateRequest Settings{};\n    ModifyRequestType RequestType{};\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ModifyGuestNetworkServiceSettingRequest, ResourceType, RequestType, Settings);\n};\n\n} // namespace wsl::shared::hns"
  },
  {
    "path": "src/shared/inc/lxfsshares.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    lxfsshares.h\n\nAbstract:\n\n    This file contains share names and paths that are consumed by both init\n    and the WSL service.\n\n--*/\n\n#pragma once\n\ntypedef struct _LXSS_SHARED_DIRECTORY\n{\n    const char* Name;\n    const char* MountPoint;\n} LXSS_SHARED_DIRECTORY, *PLXSS_SHARED_DIRECTORY;\n\n#define LXSS_LIB_PREFIX \"/usr/lib/wsl\"\n#define LXSS_LIB_PATH LXSS_LIB_PREFIX \"/lib\"\n#define LXSS_GPU_DRIVERS_SHARE \"drivers\"\n#define LXSS_GPU_LIB_SHARE \"lib\"\n#define LXSS_GPU_INBOX_LIB_SHARE LXSS_GPU_LIB_SHARE \"_inbox\"\n#define LXSS_GPU_PACKAGED_LIB_SHARE LXSS_GPU_LIB_SHARE \"_packaged\"\n\n//\n// Shared directories for GPU compute support.\n//\n\nconstexpr LXSS_SHARED_DIRECTORY g_gpuShares[] = {{LXSS_GPU_DRIVERS_SHARE, LXSS_LIB_PREFIX \"/drivers\"}, {LXSS_GPU_LIB_SHARE, LXSS_LIB_PATH}};\n"
  },
  {
    "path": "src/shared/inc/lxinitshared.h",
    "content": "\n//\n// Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n\n/*++\n\nModule Name:\n\n    lxinitshared.h\n\nAbstract:\n\n    This is the header file for definitions shared between Windows and init.\n\n--*/\n\n#pragma once\n\n#include <stdint.h>\n#include <type_traits>\n#include \"prettyprintshared.h\"\n\n#if defined(_MSC_VER)\n\n#pragma warning(disable : 4200) // Zero-sized array\n\n#endif\n\n//\n// The path of the init daemon.\n//\n\n#define LX_INIT_PATH \"/init\"\n\n//\n// Command line argument for sending import result message.\n//\n\n#define LX_INIT_IMPORT_MESSAGE_ARG \"--import-result-message\"\n\n//\n// The name of the server.\n//\n\n#define LX_INIT_SERVER_NAME \"wslservice\"\n\n//\n// Max size for a distribution name.\n//\n\n#define LX_INIT_DISTRO_NAME_MAX 256\n\n//\n// The header for autogenerated files.\n//\n\n#define LX_INIT_AUTO_GENERATED_FILE_HEADER \\\n    \"# This file was automatically generated by WSL. To stop automatic \" \\\n    \"generation of this file, add the following entry to /etc/wsl.conf:\\n\"\n\n//\n// The second header for resolv.conf.\n//\n\n#define LX_INIT_RESOLVCONF_PREFIX \"# [network]\\n# generateResolvConf = false\\n\"\n\n//\n// The destination prefix for the default routing rule.\n//\n\n#define LX_INIT_UNSPECIFIED_ADDRESS L\"0.0.0.0\"\n#define LX_INIT_UNSPECIFIED_V6_ADDRESS L\"::\"\n\n#define LX_INIT_DEFAULT_ROUTE_PREFIX LX_INIT_UNSPECIFIED_ADDRESS \"/0\"\n#define LX_INIT_DEFAULT_ROUTE_V6_PREFIX LX_INIT_UNSPECIFIED_V6_ADDRESS \"/0\"\n\n//\n// The hard-coded link-local addresses used for communicating over the loopback to the host\n//\n#define LX_INIT_IPV4_LOOPBACK_GATEWAY_ADDRESS \"169.254.73.152\"\n#define LX_INIT_IPV6_LOOPBACK_GATEWAY_ADDRESS \"fe80::500:4aef:feef:2aa2\"\n\n//\n// Linux device name for the GELNIC used for loopback communication in mirrored networking mode.\n//\n#define LX_INIT_LOOPBACK_DEVICE_NAME \"loopback0\"\n\n//\n// Default IP address used by the DNS tunneling listener.\n// N.B. As of Germanium release, HNS will never assign a subnet from the 10.0.0.0/8 range to the WSL NAT network, so there\n// can't be a conflict between that range and the default IP 10.255.255.254.\n//\n#define LX_INIT_DNS_TUNNELING_IP_ADDRESS \"10.255.255.254\"\n\n//\n// The data for launching a new process. The structure is variable size with the\n// strings starting at the Buffer member in the following order: Filename,\n// CurrentWorkingDirectory, CommandLine, and finally Environment.\n//\n\n#define LX_INIT_CREATE_PROCESS_USE_CONSOLE (LXBUS_IPC_HANDLE_ID_INVALID)\n\n#define LX_INIT_STD_FD_COUNT 3\n\n#define LX_INIT_USER_NOT_FOUND 67\n#define LX_INIT_TTY_LIMIT 87\n\n#define LX_INIT_CREATE_NT_PROCESS_SOCKETS 4\n\n//\n// HvSocket ports used for WSL VM Mode.\n//\n\n#define LX_INIT_UTILITY_VM_INIT_PORT (50000)\n#define LX_INIT_UTILITY_VM_PLAN9_PORT (50001)\n#define LX_INIT_UTILITY_VM_PLAN9_DRVFS_PORT (50002)\n#define LX_INIT_UTILITY_VM_PLAN9_DRVFS_ADMIN_PORT (50003)\n#define LX_INIT_UTILITY_VM_VIRTIOFS_PORT (50004)\n#define LX_INIT_UTILITY_VM_CRASH_DUMP_PORT (50005)\n\n//\n// HvSocket buffer size for 9p connections.\n//\n// N.B. The kernel doubles this value.\n//\n\n#define LX_INIT_UTILITY_VM_PLAN9_BUFFER_SIZE (65536)\n\n//\n// Default buffer size for relaying.\n//\n\n#define LX_RELAY_BUFFER_SIZE 0x1000\n\n//\n// HVC terminal devices.\n//\n\n#define LX_INIT_HVC_DEBUG_SHELL \"hvc2\"\n#define LX_INIT_HVC_TELEMETRY \"hvc1\"\n\n// Since -1 is used to indicate \"any port\", it can't be an actual port number and is used as an\n// invalid value.\n// N.B. Can't use the actual VMADDR_PORT_ANY constant since the header isn't available.\n#define LX_INIT_UTILITY_VM_INVALID_PORT (UINT_MAX)\n\n#define LX_INIT_UTILITY_VM_INIT_SOCKET_FD (100)\n\n#define LX_INIT_UTILITY_VM_DRVFS_SHARE_NAME \"drvfs\"\n\n#define LX_INIT_DRVFS_VIRTIO_TAG \"drvfs\"\n#define LX_INIT_DRVFS_ADMIN_VIRTIO_TAG \"drvfsa\"\n\n//\n// Typical default DrvFs-specific 9p mount options.\n//\n// N.B. These are used to prepopulate virtiofs shares with default mount options.\n//      These will match the default values used by the system distro, and will typically\n//      match the default uid / gid for the user distro. If the values do not match, a new\n//      virtiofs share will be created.\n//\n\n#define LX_INIT_DEFAULT_PLAN9_MOUNT_OPTIONS \";uid=1000;gid=1000;symlinkroot=/mnt/\"\n\n#define LX_INIT_UTILITY_VM_CREATE_PROCESS_SOCKET_COUNT (5)\n\n#define LX_INIT_NO_CONSOLE (LXBUS_IPC_CONSOLE_ID_INVALID)\n\n#define LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION 0x1\n\n//\n// The timeout for hvsocket connect().\n//\n\n#define LX_INIT_HVSOCKET_TIMEOUT_SECONDS (30)\n\n//\n// The data for beginning a port listener.\n//\n\n#define LX_INIT_LOCALHOST_RELAY \"localhost\"\n\n//\n// Environment variable to determine if root init logic, or WSL entrypoint should be used.\n//\n\n#define WSL_ROOT_INIT_ENV \"WSL_ROOT_INIT\"\n\n#define WSL_SOCKET_LOG_ENV \"WSL_SOCKET_LOG\"\n\n#define WSL_ENABLE_CRASH_DUMP_ENV \"WSL_ENABLE_CRASH_DUMP\"\n\n#define WSL_DEBUG_ENV \"WSL_DEBUG\"\n\n#define WSL_DISTRIBUTION_CONF \"/etc/wsl-distribution.conf\"\n\n//\n// Defines needed for GUI support.\n//\n\n#define PULSE_SERVER_ENV \"PULSE_SERVER\"\n#define PULSE_SERVER_NAME \"PulseServer\"\n#define WAYLAND_DISPLAY_ENV \"WAYLAND_DISPLAY\"\n#define WAYLAND_DISPLAY_VALUE \"wayland-0\"\n#define WAYLAND_RUNTIME_DIR \"runtime-dir\"\n#define X11_DISPLAY_ENV \"DISPLAY\"\n#define X11_DISPLAY_VALUE \":0\"\n#define X11_SOCKET_NAME \".X11-unix\"\n#define XDG_RUNTIME_DIR_ENV \"XDG_RUNTIME_DIR\"\n\n//\n// GNS arguments\n//\n\n#define LX_INIT_GNS \"gns\"\n#define LX_INIT_GNS_ADAPTER_ARG \"--adapter\"\n#define LX_INIT_GNS_SOCKET_ARG \"--socket\"\n#define LX_INIT_GNS_DNS_SOCKET_ARG \"--dns_socket\"\n#define LX_INIT_GNS_DNS_TUNNELING_IP \"--dns_tunneling_ip\"\n#define LX_INIT_GNS_MESSAGE_TYPE_ARG \"--msg_type\"\n\n//\n// Plan9 arguments\n//\n\n#define LX_INIT_PLAN9 \"plan9\"\n#define LX_INIT_PLAN9_CONTROL_SOCKET_ARG \"--control-socket\"\n#define LX_INIT_PLAN9_SOCKET_PATH_ARG \"--socket-path\"\n#define LX_INIT_PLAN9_SERVER_FD_ARG \"--server-fd\"\n#define LX_INIT_PLAN9_LOG_FILE_ARG \"--log-file\"\n#define LX_INIT_PLAN9_LOG_LEVEL_ARG \"--log-level\"\n#define LX_INIT_PLAN9_PIPE_FD_ARG \"--pipe-fd\"\n#define LX_INIT_PLAN9_TRUNCATE_LOG_ARG \"--log-truncate\"\n\n//\n// wsl-capture-crash\n//\n\n#define LX_INIT_WSL_CAPTURE_CRASH \"wsl-capture-crash\"\n\n#define LX_INIT_WSL_GENERATOR \"wsl-generator\"\n\n#define LX_INIT_WSL_USER_GENERATOR \"wsl-user-generator\"\n\n//\n// WSL2-specific environment variables.\n//\n\n#define LX_WSL2_CROSS_DISTRO_ENV \"WSL2_CROSS_DISTRO\"\n#define LX_WSL2_DISTRO_NAME_ENV \"WSL2_DISTRO_NAME\"\n#define LX_WSL2_GUI_APP_SUPPORT_ENV \"WSL2_GUI_APPS_ENABLED\"\n#define LX_WSL2_KERNEL_MODULES_MOUNT_ENV \"WSL2_KERNEL_MODULES_MOUNT\"\n#define LX_WSL2_KERNEL_MODULES_PATH_ENV \"WSL2_KERNEL_MODULES_PATH\"\n#define LX_WSL2_SYSTEM_DISTRO_SHARE_ENV \"WSL2_SYSTEM_DISTRO_SHARE\"\n#define LX_WSL2_GPU_SHARE_ENV \"WSL2_GPU_SHARE_ENV_\"\n#define LX_WSL2_SHARED_MEMORY_OB_DIRECTORY \"WSL2_SHARED_MEMORY_OB_DIRECTORY\"\n#define LX_WSL2_INSTALL_PATH \"WSL2_INSTALL_PATH\"\n#define LX_WSL2_SAFE_MODE \"WSL2_SAFE_MODE\"\n#define LX_WSL2_USER_PROFILE \"WSL2_USER_PROFILE\"\n#define LX_WSL2_VM_ID_ENV \"WSL2_VM_ID\"\n#define LX_WSL_PID_ENV \"WSL2_PID\"\n#define LX_INIT_TELEMETRY_AGENT \"telagent\"\n#define LX_WSL2_DISTRO_READ_ONLY_ENV \"WSL_DISTRO_READ_ONLY\"\n#define LX_WSL2_NETWORKING_MODE_ENV \"WSL2_NETWORKING_MODE\"\n#define LX_WSL2_DISTRO_INIT_PID \"WSL2_DISTRO_INIT_PID\"\n\n//\n// Command line arguments shared between init & mini_init\n//\n\n#define INIT_PORT_TRACKER_FD_ARG \"--port-tracker-fd\"\n#define INIT_BPF_FD_ARG \"--bpf-fd\"\n#define INIT_NETLINK_FD_ARG \"--netlink-fd\"\n#define INIT_PORT_TRACKER_LOCALHOST_RELAY \"--localhost-relay\"\n\n//\n// The types of messages that can be sent to init and mini init.\n//\n\ntypedef enum _LX_MESSAGE_TYPE\n{\n    LxMiniInitMessageAny = 0,\n    LxInitMessageCreateProcess,\n    LxInitMessageCreateSession,\n    LxInitMessageCreateSessionResponse,\n    LxInitMessageNetworkInformation,\n    LxInitMessageInitialize,\n    LxInitMessageInitializeResponse,\n    LxInitMessageTimezoneInformation,\n    LxInitMessageCreateProcessUtilityVm,\n    LxInitMessageExitStatus,\n    LxInitMessageWindowSizeChanged,\n    LxInitMessageCreateProcessResponse,\n    LxInitMessageQueryDrvfsElevated,\n    LxInitMessageRemountDrvfs,\n    LxInitMessageTerminateInstance,\n    LxInitMessageStartSocketRelay,\n    LxInitMessageQueryEnvironmentVariable,\n    LxInitMessageQueryFeatureFlags,\n    LxInitMessageKernelVersion,\n    LxInitMessageAddVirtioFsDevice,\n    LxInitMessageAddVirtioFsDeviceResponse,\n    LxInitMessageRemountVirtioFsDevice,\n    LxInitMessageStartDistroInit,\n    LxInitMessageCreateLoginSession,\n    LxInitMessageStopPlan9Server,\n    LxInitMessageQueryNetworkingMode,\n    LxInitMessageQueryVmId,\n    LxInitCreateProcess,\n    LxInitOobeResult,\n    LxMiniInitMessageLaunchInit,\n    LxMiniInitMessageImport,\n    LxMiniInitMessageImportInplace,\n    LxMiniInitMessageExport,\n    LxMiniInitMessageCreateInstanceResult,\n    LxMiniInitMessageImportResult,\n    LxMiniInitMessageEjectVhd,\n    LxMiniInitMessageEarlyConfig,\n    LxMiniInitMessageInitialConfig,\n    LxMiniInitMessageMount,\n    LxMiniInitMessageUnmount,\n    LxMiniInitMessageDetach,\n    LxMiniInitMessageMountStatus,\n    LxMiniInitMessageNetworkInfo,\n    LxMiniInitMessageGuestCapabilities,\n    LxMiniInitMessageWaitForPmemDevice,\n    LxMiniInitMessageChildExit,\n    LxMiniInitMountFolder,\n    LxMiniInitCreateInstancePid,\n    LxMiniInitTelemetryMessage,\n    LxMinitWaitForPmemDeviceResult,\n    LxMiniInitMessageResizeDistribution,\n    LxMiniInitMessageResizeDistributionResponse,\n    LxProcessCrash,\n    LxGnsMessageInterfaceConfiguration,\n    LxGnsMessageResult,\n    LxGnsMessageNotification,\n    LxGnsMessagePortMappingRequest,\n    LxGnsMessagePortMappingResponse,\n    LxGnsMessageSetPortListener,\n    LxGnsMessagePortListenerRelayStart,\n    LxGnsMessagePortListenerRelayStop,\n    LxGnsMessageVmNicCreatedNotification,\n    LxGnsMessageCreateDeviceRequest,\n    LxGnsMessageModifyGuestDeviceSettingRequest,\n    LxGnsMessageLoopbackRoutesRequest,\n    LxGnsMessageDeviceSettingRequest,\n    LxGnsMessageIfStateChangeRequest,\n    LxGnsMessageIfStateChangeResponse,\n    LxGnsMessageInitialIpConfigurationNotification,\n    LxGnsMessageSetupIpv6,\n    LxGnsMessageDnsTunneling,\n    LxGnsMessageNoOp,\n    LxGnsMessageGlobalNetFilter,\n    LxGnsMessageInterfaceNetFilter,\n    LxGnsMessageConnectTestRequest,\n    LxGnsMessageListenerRelay,\n    LxMessageResultBool,\n    LxMessageResultInt32,\n    LxMessageResultUint32,\n    LxMessageResultUint8\n} LX_MESSAGE_TYPE,\n    *PLX_MESSAGE_TYPE;\n\ninline auto ToString(LX_MESSAGE_TYPE messageType)\n{\n#define X(Value) \\\n    case Value: \\\n        return #Value;\n\n    switch (messageType)\n    {\n        X(LxMiniInitMessageAny)\n        X(LxInitMessageCreateProcess)\n        X(LxInitMessageCreateSession)\n        X(LxInitMessageCreateSessionResponse)\n        X(LxInitMessageNetworkInformation)\n        X(LxInitMessageInitialize)\n        X(LxInitMessageInitializeResponse)\n        X(LxInitMessageTimezoneInformation)\n        X(LxInitMessageCreateProcessUtilityVm)\n        X(LxInitMessageExitStatus)\n        X(LxInitMessageWindowSizeChanged)\n        X(LxInitMessageCreateProcessResponse)\n        X(LxInitMessageQueryDrvfsElevated)\n        X(LxInitMessageRemountDrvfs)\n        X(LxInitMessageTerminateInstance)\n        X(LxInitMessageStartSocketRelay)\n        X(LxInitMessageQueryEnvironmentVariable)\n        X(LxInitMessageQueryFeatureFlags)\n        X(LxInitMessageKernelVersion)\n        X(LxInitMessageAddVirtioFsDevice)\n        X(LxInitMessageAddVirtioFsDeviceResponse)\n        X(LxInitMessageRemountVirtioFsDevice)\n        X(LxInitMessageStartDistroInit)\n        X(LxInitMessageCreateLoginSession)\n        X(LxInitMessageStopPlan9Server)\n        X(LxInitMessageQueryNetworkingMode)\n        X(LxInitCreateProcess)\n        X(LxInitOobeResult)\n        X(LxMiniInitMessageLaunchInit)\n        X(LxMiniInitMessageImport)\n        X(LxMiniInitMessageExport)\n        X(LxMiniInitMessageCreateInstanceResult)\n        X(LxMiniInitMessageImportResult)\n        X(LxMiniInitMessageEjectVhd)\n        X(LxMiniInitMessageEarlyConfig)\n        X(LxMiniInitMessageInitialConfig)\n        X(LxMiniInitMessageMount)\n        X(LxMiniInitMessageUnmount)\n        X(LxMiniInitMessageDetach)\n        X(LxMiniInitMessageMountStatus)\n        X(LxMiniInitMessageNetworkInfo)\n        X(LxMiniInitMessageGuestCapabilities)\n        X(LxMiniInitMessageWaitForPmemDevice)\n        X(LxMiniInitMessageChildExit)\n        X(LxMiniInitMessageResizeDistribution)\n        X(LxMiniInitMessageResizeDistributionResponse)\n        X(LxMiniInitMountFolder)\n        X(LxMiniInitCreateInstancePid)\n        X(LxMinitWaitForPmemDeviceResult)\n        X(LxProcessCrash)\n        X(LxGnsMessageInterfaceConfiguration)\n        X(LxGnsMessageResult)\n        X(LxGnsMessageNotification)\n        X(LxGnsMessagePortMappingRequest)\n        X(LxGnsMessagePortMappingResponse)\n        X(LxGnsMessageSetPortListener)\n        X(LxGnsMessagePortListenerRelayStart)\n        X(LxGnsMessagePortListenerRelayStop)\n        X(LxGnsMessageVmNicCreatedNotification)\n        X(LxGnsMessageCreateDeviceRequest)\n        X(LxGnsMessageModifyGuestDeviceSettingRequest)\n        X(LxGnsMessageLoopbackRoutesRequest)\n        X(LxGnsMessageDeviceSettingRequest)\n        X(LxGnsMessageIfStateChangeRequest)\n        X(LxGnsMessageIfStateChangeResponse)\n        X(LxGnsMessageInitialIpConfigurationNotification)\n        X(LxGnsMessageSetupIpv6)\n        X(LxGnsMessageDnsTunneling)\n        X(LxGnsMessageNoOp)\n        X(LxGnsMessageGlobalNetFilter)\n        X(LxGnsMessageInterfaceNetFilter)\n        X(LxGnsMessageConnectTestRequest)\n        X(LxMessageResultBool)\n        X(LxMessageResultInt32)\n        X(LxMessageResultUint32)\n        X(LxMiniInitTelemetryMessage)\n        X(LxMessageResultUint8)\n\n    default:\n        return \"<unexpected LX_MESSAGE_TYPE>\";\n    }\n\n#undef X\n}\n\ntypedef enum _LX_INIT_DRVFS_MOUNT\n{\n    LxInitDrvfsMountNone = 0,\n    LxInitDrvfsMountNonElevated,\n    LxInitDrvfsMountElevated\n} LX_INIT_DRVFS_MOUNT,\n    *PLX_INIT_DRVFS_MOUNT;\n\n#define CONCAT_IMPL(A, B) L##A##B\n\n#define CONCAT(A, B) CONCAT_IMPL(A, B)\n\n#define LX_INIT_RESOLVCONF_FULL_HEADER CONCAT(LX_INIT_AUTO_GENERATED_FILE_HEADER, LX_INIT_RESOLVCONF_PREFIX)\n\ninline void PrettyPrint(std::stringstream& Out, LX_MESSAGE_TYPE Value)\n{\n    Out << ToString(Value);\n}\n\nstruct MESSAGE_HEADER\n{\n    static inline auto Type = LxMiniInitMessageAny; // Setting this allows using MESSAGE_HEADER to receive any type of message\n\n    LX_MESSAGE_TYPE MessageType;\n    unsigned int MessageSize;\n    unsigned int SequenceNumber;\n\n    PRETTY_PRINT(FIELD(MessageType), FIELD(MessageSize), FIELD(SequenceNumber));\n};\n\n//\n// The data for launching a new session.\n//\n\ntypedef struct _LX_INIT_CREATE_SESSION_RESPONSE\n{\n    static inline auto Type = LxInitMessageCreateSessionResponse;\n\n    MESSAGE_HEADER Header;\n    unsigned int Port;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Port));\n\n} LX_INIT_CREATE_SESSION_RESPONSE, *PLX_INIT_CREATE_SESSION_RESPONSE;\n\ntypedef struct _LX_INIT_CREATE_SESSION\n{\n    static inline auto Type = LxInitMessageCreateSession;\n    using TResponse = LX_INIT_CREATE_SESSION_RESPONSE;\n\n    MESSAGE_HEADER Header;\n    int64_t ConsoleId;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(ConsoleId));\n} LX_INIT_CREATE_SESSION, *PLX_INIT_CREATE_SESSION;\n\ntypedef enum _CREATE_PROCESS_SHELL_OPTIONS\n{\n    ShellOptionsLogin = 0x1\n} CREATE_PROCESS_SHELL_OPTIONS,\n    *PCREATE_PROCESS_SHELL_OPTIONS;\n\n//\n// N.B. The common create process structure must be the last element in the\n//      containing struct because it contains an anysize array.\n//\n\ntemplate <typename T>\nconstexpr LX_MESSAGE_TYPE GetResultMessageType()\n{\n    if constexpr (std::is_same_v<T, bool>)\n    {\n        return LxMessageResultBool;\n    }\n    else if constexpr (std::is_same_v<T, int32_t>)\n    {\n        return LxMessageResultInt32;\n    }\n    else if constexpr (std::is_same_v<T, uint32_t>)\n    {\n        return LxMessageResultUint32;\n    }\n    else if constexpr (std::is_same_v<T, uint8_t>)\n    {\n        return LxMessageResultUint8;\n    }\n    else\n    {\n        static_assert(sizeof(T) != sizeof(T));\n    }\n}\n\ntemplate <typename T>\nstruct RESULT_MESSAGE\n{\n    static inline auto Type = GetResultMessageType<T>();\n\n    MESSAGE_HEADER Header;\n    T Result;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Result));\n};\n\ntypedef struct _LX_PROCESS_CRASH\n{\n    static inline auto Type = LxProcessCrash;\n    using TResponse = RESULT_MESSAGE<int32_t>;\n\n    MESSAGE_HEADER Header;\n    std::uint64_t Timestamp;\n    std::uint32_t Signal;\n    std::uint64_t Pid;\n\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Timestamp), FIELD(Signal), FIELD(Pid), FIELD(Buffer));\n\n} LX_PROCESS_CRASH, *PLX_PROCESS_CRASH;\n\ntypedef struct _LX_INIT_CREATE_PROCESS_COMMON\n{\n    unsigned int FilenameOffset;\n    unsigned int CurrentWorkingDirectoryOffset;\n    unsigned int CommandLineOffset;\n    unsigned short CommandLineCount;\n    unsigned int EnvironmentOffset;\n    unsigned short EnvironmentCount;\n    unsigned int NtEnvironmentOffset;\n    unsigned short NtEnvironmentCount;\n    unsigned int NtPathOffset;\n    unsigned int ShellOptions;\n    unsigned int UsernameOffset;\n    unsigned int DefaultUid;\n    int Flags;\n    char Buffer[];\n} LX_INIT_CREATE_PROCESS_COMMON, *PLX_INIT_CREATE_PROCESS_COMMON;\n\ntypedef struct _LX_INIT_CREATE_PROCESS_RESPONSE\n{\n    static inline auto Type = LxInitMessageCreateProcessResponse;\n\n    MESSAGE_HEADER Header;\n    int Result;\n    int64_t SignalPipeId;\n    unsigned int Flags;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Result), FIELD(SignalPipeId), FIELD(Flags));\n} LX_INIT_CREATE_PROCESS_RESPONSE, *PLX_INIT_CREATE_PROCESS_RESPONSE;\n\ntypedef struct _LX_INIT_CREATE_PROCESS\n{\n    static inline auto Type = LxInitMessageCreateProcess;\n    using TResponse = _LX_INIT_CREATE_PROCESS_RESPONSE;\n\n    MESSAGE_HEADER Header;\n    int64_t IpcServerId;\n    int64_t StdFdIds[LX_INIT_STD_FD_COUNT];\n    int64_t ForkTokenId;\n    LX_INIT_CREATE_PROCESS_COMMON Common;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(IpcServerId), FIELD(StdFdIds), FIELD(ForkTokenId));\n} LX_INIT_CREATE_PROCESS, *PLX_INIT_CREATE_PROCESS;\n\ntypedef struct _LX_INIT_CREATE_NT_PROCESS_COMMON\n{\n    int64_t StdFdIds[LX_INIT_STD_FD_COUNT];\n    unsigned int FilenameOffset;\n    unsigned int CurrentWorkingDirectoryOffset;\n    unsigned int CommandLineOffset;\n    unsigned short CommandLineCount;\n    unsigned int EnvironmentOffset;\n    unsigned short Rows;\n    unsigned short Columns;\n    bool CreatePseudoconsole;\n    char Buffer[];\n\n    // Not pretty-printing command line and env since it could contain PII.\n    PRETTY_PRINT(FIELD(StdFdIds), STRING_FIELD(FilenameOffset), STRING_FIELD(CurrentWorkingDirectoryOffset), FIELD(Rows), FIELD(Columns), FIELD(CreatePseudoconsole));\n} LX_INIT_CREATE_NT_PROCESS_COMMON, *PLX_INIT_CREATE_NT_PROCESS_COMMON;\n\nusing PCLX_INIT_CREATE_NT_PROCESS_COMMON = const LX_INIT_CREATE_NT_PROCESS_COMMON*;\n\ntypedef struct _LX_INIT_CREATE_NT_PROCESS\n{\n    static inline auto Type = LxInitMessageCreateProcess;\n\n    MESSAGE_HEADER Header;\n    int64_t StdFdIds[LX_INIT_STD_FD_COUNT];\n    LX_INIT_CREATE_NT_PROCESS_COMMON Common;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(StdFdIds), FIELD(Common));\n\n} LX_INIT_CREATE_NT_PROCESS, *PLX_INIT_CREATE_NT_PROCESS;\n\nusing PCLX_INIT_CREATE_NT_PROCESS = const LX_INIT_CREATE_NT_PROCESS*;\n\ntypedef struct _LX_INIT_CREATE_NT_PROCESS_UTILITY_VM\n{\n    static inline auto Type = LxInitMessageCreateProcessUtilityVm;\n\n    MESSAGE_HEADER Header;\n    unsigned int Port;\n    LX_INIT_CREATE_NT_PROCESS_COMMON Common;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Port), FIELD(Common));\n} LX_INIT_CREATE_NT_PROCESS_UTILITY_VM, *PLX_INIT_CREATE_NT_PROCESS_UTILITY_VM;\n\nusing PCLX_INIT_CREATE_NT_PROCESS_UTILITY_VM = const LX_INIT_CREATE_NT_PROCESS_UTILITY_VM*;\n\n//\n// The data communicating networking information. The structure is variable\n// size and the FileContents member contains the string to write to the\n// /etc/resolv.conf file.\n//\n\ntypedef struct _LX_INIT_NETWORK_INFORMATION\n{\n    static inline auto Type = LxInitMessageNetworkInformation;\n\n    MESSAGE_HEADER Header;\n    unsigned int FileHeaderIndex;\n    unsigned int FileContentsIndex;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), STRING_FIELD(FileHeaderIndex), STRING_FIELD(FileContentsIndex));\n} LX_INIT_NETWORK_INFORMATION, *PLX_INIT_NETWORK_INFORMATION;\n\ntypedef struct _LX_INIT_CREATE_LOGIN_SESSION\n{\n    static inline auto Type = LxInitMessageCreateLoginSession;\n    using TResponse = RESULT_MESSAGE<bool>;\n\n    MESSAGE_HEADER Header;\n    unsigned int Uid;\n    unsigned int Gid;\n    char Buffer[]; // Contains username\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Uid), FIELD(Gid), FIELD(Buffer));\n} LX_INIT_CREATE_LOGIN_SESSION, *PLX_INIT_CREATE_LOGIN_SESSION;\n\n//\n// The buffer of the query environment variable struct contains the name of the variable\n// to query for. On response, it will contain the value of the environment variable.\n//\n\ntypedef struct _LX_INIT_QUERY_ENVIRONMENT_VARIABLE\n{\n    static inline auto Type = LxInitMessageQueryEnvironmentVariable;\n\n    MESSAGE_HEADER Header;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Buffer));\n} LX_INIT_QUERY_ENVIRONMENT_VARIABLE, *PLX_INIT_QUERY_ENVIRONMENT_VARIABLE;\n\ntypedef struct _LX_GNS_RESULT\n{\n    static inline auto Type = LxGnsMessageResult;\n\n    MESSAGE_HEADER Header;\n    int Result;\n    char Buffer[];\n\n    // Note: 'Buffer' doesn't always contain a string, so don't pretty print it.\n    PRETTY_PRINT(FIELD(Header), FIELD(Result));\n} LX_GNS_RESULT, *PLX_GNS_RESULT;\n\ntypedef struct _LX_GNS_INTERFACE_CONFIGURATION\n{\n    static inline auto Type = LxGnsMessageInterfaceConfiguration;\n    using TResponse = LX_GNS_RESULT;\n\n    MESSAGE_HEADER Header;\n    char Content[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Content));\n} LX_GNS_INTERFACE_CONFIGURATION, *PLX_GNS_INTERFACE_CONFIGURATION;\n\ntypedef struct _LX_GNS_NOTIFICATION\n{\n    static inline auto Type = LxGnsMessageNotification;\n    using TResponse = LX_GNS_RESULT;\n\n    MESSAGE_HEADER Header;\n    GUID AdapterId;\n    char Content[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(AdapterId), FIELD(Content));\n} LX_GNS_NOTIFICATION, *PLX_GNS_NOTIFICATION;\n\ntypedef struct _LX_GNS_PORT_ALLOCATION_REQUEST\n{\n    static inline auto Type = LxGnsMessagePortMappingRequest;\n    using TResponse = RESULT_MESSAGE<int32_t>;\n\n    MESSAGE_HEADER Header;\n    uint32_t Address32[4];\n    uint16_t Port;\n    int Af;\n    int Protocol;\n    bool Allocate;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Address32), FIELD(Port), FIELD(Af), FIELD(Protocol), FIELD(Allocate));\n} LX_GNS_PORT_ALLOCATION_REQUEST, *PLX_GNS_PORT_ALLOCATION_REQUEST;\n\ntypedef struct _LX_GNS_SET_PORT_LISTENER\n{\n    static inline auto Type = LxGnsMessageSetPortListener;\n\n    MESSAGE_HEADER Header;\n    int HvSocketPort;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(HvSocketPort));\n} LX_GNS_SET_PORT_LISTENER, *PLX_GNS_SET_PORT_LISTENER;\n\nstatic_assert(sizeof(LX_GNS_SET_PORT_LISTENER) == 16);\n\ntypedef struct _LX_GNS_PORT_LISTENER_RELAY\n{\n    static inline auto Type = LxGnsMessageListenerRelay;\n\n    MESSAGE_HEADER Header;\n    unsigned short Family;\n    unsigned short Port;\n    unsigned int Address[4];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Family), FIELD(Port), FIELD(Address));\n} LX_GNS_PORT_LISTENER_RELAY, *PLX_GNS_START_PORT_LISTENER_RELAY;\n\nusing PCLX_GNS_PORT_LISTENER_RELAY = const LX_GNS_PORT_LISTENER_RELAY*;\n\ntypedef struct _LX_GNS_TUN_BRIDGE_REQUEST\n{\n    static inline auto Type = LxGnsMessageIfStateChangeRequest;\n    using TResponse = RESULT_MESSAGE<int32_t>;\n\n    MESSAGE_HEADER Header;\n    char InterfaceName[16];\n    bool InterfaceUp;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(InterfaceName), FIELD(InterfaceUp));\n} LX_GNS_TUN_BRIDGE_REQUEST, *PLX_GNS_TUN_BRIDGE_REQUEST;\n\ntypedef struct _LX_GNS_DNS_CLIENT_IDENTIFIER\n{\n    // Protocol used by the Linux DNS client. Can be TCP or UDP.\n    uint8_t Protocol;\n    // Unique id that identifies the Linux DNS client. Used by the DNS server on the Linux side to know to which\n    // DNS client to send back a DNS response.\n    uint32_t DnsClientId;\n\n    _LX_GNS_DNS_CLIENT_IDENTIFIER() = default;\n    ~_LX_GNS_DNS_CLIENT_IDENTIFIER() noexcept = default;\n\n    _LX_GNS_DNS_CLIENT_IDENTIFIER(const _LX_GNS_DNS_CLIENT_IDENTIFIER&) = default;\n    _LX_GNS_DNS_CLIENT_IDENTIFIER& operator=(const _LX_GNS_DNS_CLIENT_IDENTIFIER&) = default;\n} LX_GNS_DNS_CLIENT_IDENTIFIER, *PLX_GNS_DNS_CLIENT_IDENTIFIER;\n\ntypedef struct _LX_GNS_DNS_TUNNELING_MESSAGE\n{\n    static inline auto Type = LxGnsMessageDnsTunneling;\n\n    MESSAGE_HEADER Header;\n    LX_GNS_DNS_CLIENT_IDENTIFIER DnsClientIdentifier;\n    // Raw DNS request or response (variable length).\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(DnsClientIdentifier.Protocol), FIELD(DnsClientIdentifier.DnsClientId));\n} LX_GNS_DNS_TUNNELING_MESSAGE, *PLX_GNS_DNS_TUNNELING_MESSAGE;\n\n// Verify there is no padding in the LX_GNS_DNS_TUNNELING_MESSAGE structure before the variable length Buffer field.\nstatic_assert(offsetof(LX_GNS_DNS_TUNNELING_MESSAGE, Buffer) == sizeof(MESSAGE_HEADER) + sizeof(LX_GNS_DNS_CLIENT_IDENTIFIER));\n\ntypedef struct _LX_GNS_JSON_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageAny;\n    using TResponse = LX_GNS_RESULT;\n\n    MESSAGE_HEADER Header;\n    char Content[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Content));\n} LX_GNS_JSON_MESSAGE, *PLX_GNS_JSON_MESSAGE;\n\nusing PCLX_INIT_NETWORK_INFORMATION = const LX_INIT_NETWORK_INFORMATION*;\n\ntypedef struct _LX_INIT_STOP_PLAN9_SERVER\n{\n    static inline auto Type = LxInitMessageStopPlan9Server;\n    using TResponse = RESULT_MESSAGE<bool>;\n\n    MESSAGE_HEADER Header;\n    bool Force;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Force));\n} LX_INIT_STOP_PLAN9_SERVER, *PLX_INIT_STOP_PLAN9_SERVER;\n\n//\n// Feature flags to provide runtime velocity support for Linux code.\n//\n// To use this, define a new flag, and set it as part of the initial\n// configuration message in WslService if the relevant feature is enabled.\n// Call UtilGetFeatureFlags to determine which flags are enabled from the\n// Linux code.\n//\n\ntypedef enum _LX_INIT_FEATURE_FLAGS\n{\n    LxInitFeatureNone = 0,\n    LxInitFeatureVirtIo9p = 0x1,\n    LxInitFeatureVirtIoFs = 0x2,\n    LxInitFeatureDisable9pServer = 0x4,\n    LxInitFeatureRootfsCompressed = 0x8,\n    LxInitFeatureSystemDistro = 0x10,\n    LxInitFeatureDnsTunneling = 0x20,\n} LX_INIT_FEATURE_FLAGS,\n    *PLX_INIT_FEATURE_FLAGS;\n\ntypedef struct _LX_INIT_CONFIGURATION_INFORMATION_RESPONSE\n{\n    static inline auto Type = LxInitMessageInitializeResponse;\n\n    MESSAGE_HEADER Header;\n    unsigned int Plan9Port;\n    unsigned int DefaultUid;\n    unsigned int InteropPort;\n    bool SystemdEnabled;\n    std::uint64_t PidNamespace;\n    unsigned int FlavorIndex;\n    unsigned int VersionIndex;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Plan9Port), FIELD(DefaultUid), FIELD(InteropPort), FIELD(SystemdEnabled), FIELD(PidNamespace), STRING_FIELD(FlavorIndex), STRING_FIELD(VersionIndex));\n} LX_INIT_CONFIGURATION_INFORMATION_RESPONSE, *PLX_INIT_CONFIGURATION_INFORMATION_RESPONSE;\n\nusing PCLX_INIT_CONFIGURATION_INFORMATION_RESPONSE = const LX_INIT_CONFIGURATION_INFORMATION_RESPONSE*;\n\n//\n// The data communicating initial configuration information for the instance.\n// This contains the hostname, domainname, entries from the Windows hosts file,\n// and the DrvFs volumes to mount.\n//\n\ntypedef struct _LX_INIT_CONFIGURATION_INFORMATION\n{\n    static inline auto Type = LxInitMessageInitialize;\n    using TResponse = LX_INIT_CONFIGURATION_INFORMATION_RESPONSE;\n\n    MESSAGE_HEADER Header;\n    unsigned int HostnameOffset;\n    unsigned int DomainnameOffset;\n    unsigned int WindowsHostsOffset;\n    unsigned int DistributionNameOffset;\n    unsigned int Plan9SocketOffset;\n    unsigned int TimezoneOffset;\n    unsigned int DrvFsVolumesBitmap;\n    unsigned int DrvFsDefaultOwner;\n    unsigned int FeatureFlags;\n    LX_INIT_DRVFS_MOUNT DrvfsMount;\n    char Buffer[];\n\n    PRETTY_PRINT(\n        FIELD(Header),\n        STRING_FIELD(HostnameOffset),\n        STRING_FIELD(DomainnameOffset),\n        STRING_FIELD(WindowsHostsOffset),\n        STRING_FIELD(Plan9SocketOffset),\n        STRING_FIELD(TimezoneOffset),\n        FIELD(DrvFsVolumesBitmap),\n        FIELD(DrvFsDefaultOwner),\n        FIELD(FeatureFlags),\n        FIELD(DrvfsMount));\n\n} LX_INIT_CONFIGURATION_INFORMATION, *PLX_INIT_CONFIGURATION_INFORMATION;\n\nusing PCLX_INIT_CONFIGURATION_INFORMATION = const LX_INIT_CONFIGURATION_INFORMATION*;\n\n//\n// The data communicating timezone information. The structure contains the IANA\n// timezone identifier.\n//\n\ntypedef struct _LX_INIT_TIMEZONE_INFORMATION\n{\n    static inline auto Type = LxInitMessageTimezoneInformation;\n\n    MESSAGE_HEADER Header;\n    unsigned int TimezoneOffset;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), STRING_FIELD(TimezoneOffset));\n} LX_INIT_TIMEZONE_INFORMATION, *PLX_INIT_TIMEZONE_INFORMATION;\n\nusing PCLX_INIT_TIMEZONE_INFORMATION = const LX_INIT_TIMEZONE_INFORMATION*;\n\n//\n// Flags to provide runtime behavior for create process.\n//\n\ntypedef enum _LX_INIT_CREATE_PROCESS_FLAGS\n{\n    LxInitCreateProcessFlagsNone = 0,\n    LxInitCreateProcessFlagsStdInConsole = 0x1,\n    LxInitCreateProcessFlagsStdOutConsole = 0x2,\n    LxInitCreateProcessFlagsStdErrConsole = 0x4,\n    LxInitCreateProcessFlagsElevated = 0x8,\n    LxInitCreateProcessFlagsInteropEnabled = 0x10,\n    LxInitCreateProcessFlagAllowOOBE = 0x20,\n} LX_INIT_CREATE_PROCESS_FLAGS,\n    *PLX_INIT_CREATE_PROCESS_FLAGS;\n\ntypedef struct _LX_INIT_CREATE_PROCESS_UTILITY_VM\n{\n    static inline auto Type = LxInitMessageCreateProcessUtilityVm;\n    using TResponse = RESULT_MESSAGE<uint32_t>;\n\n    MESSAGE_HEADER Header;\n    unsigned short Rows;\n    unsigned short Columns;\n    LX_INIT_CREATE_PROCESS_COMMON Common;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Rows), FIELD(Columns));\n} LX_INIT_CREATE_PROCESS_UTILITY_VM, *PLX_INIT_CREATE_PROCESS_UTILITY_VM;\n\nusing PCLX_INIT_CREATE_PROCESS_UTILITY_VM = const LX_INIT_CREATE_PROCESS_UTILITY_VM*;\n\n//\n// The data for communicating process exit status.\n//\n\ntypedef struct _LX_INIT_PROCESS_EXIT_STATUS\n{\n    static inline auto Type = LxInitMessageExitStatus;\n\n    MESSAGE_HEADER Header;\n    int ExitCode;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(ExitCode));\n} LX_INIT_PROCESS_EXIT_STATUS, *PLX_INIT_PROCESS_EXIT_STATUS;\n\nusing PCLX_INIT_PROCESS_EXIT_STATUS = const LX_INIT_PROCESS_EXIT_STATUS*;\n\n//\n// The data for communicating window size changes.\n//\n\ntypedef struct _LX_INIT_WINDOW_SIZE_CHANGED\n{\n    static inline auto Type = LxInitMessageWindowSizeChanged;\n\n    MESSAGE_HEADER Header;\n    unsigned short Rows;\n    unsigned short Columns;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Rows), FIELD(Columns));\n} LX_INIT_WINDOW_SIZE_CHANGED, *PLX_INIT_WINDOW_SIZE_CHANGED;\n\nusing PCLX_INIT_WINDOW_SIZE_CHANGED = const LX_INIT_WINDOW_SIZE_CHANGED*;\n\n//\n// The data for terminating an instance.\n//\n\ntypedef struct _LX_INIT_TERMINATE_INSTANCE\n{\n    static inline auto Type = LxInitMessageTerminateInstance;\n    using TResponse = RESULT_MESSAGE<bool>;\n\n    MESSAGE_HEADER Header;\n    bool Force;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Force));\n} LX_INIT_TERMINATE_INSTANCE, *PLX_INIT_TERMINATE_INSTANCE;\n\nusing PCLX_INIT_TERMINATE_INSTANCE = const LX_INIT_TERMINATE_INSTANCE*;\n\n//\n// The data for beginning a socket relay.\n//\n\ntypedef struct _LX_INIT_START_SOCKET_RELAY\n{\n    static inline auto Type = LxInitMessageStartSocketRelay;\n\n    MESSAGE_HEADER Header;\n    unsigned short Family;\n    unsigned short Port;\n    int HvSocketPort;\n    size_t BufferSize;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Family), FIELD(Port), FIELD(HvSocketPort), FIELD(BufferSize));\n} LX_INIT_START_SOCKET_RELAY, *PLX_INIT_START_SOCKET_RELAY;\n\nusing PCLX_INIT_START_SOCKET_RELAY = const LX_INIT_START_SOCKET_RELAY*;\n\ntypedef struct _LX_INIT_MOUNT_DRVFS\n{\n    static inline auto Type = LxInitMessageRemountDrvfs;\n    using TResponse = RESULT_MESSAGE<int32_t>;\n\n    MESSAGE_HEADER Header;\n    bool Admin;\n    unsigned int VolumesToMount;\n    unsigned int UnreadableVolumes;\n    int DefaultOwnerUid;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Admin), FIELD(VolumesToMount), FIELD(UnreadableVolumes), FIELD(DefaultOwnerUid));\n} LX_INIT_MOUNT_DRVFS, PLX_INIT_MOUNT_DRVFS;\n\ntypedef struct _LX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE\n{\n    static inline auto Type = LxInitMessageAddVirtioFsDeviceResponse;\n\n    MESSAGE_HEADER Header;\n    int Result;\n    unsigned int TagOffset;\n    unsigned int SourceOffset;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Result), STRING_FIELD(TagOffset), STRING_FIELD(SourceOffset));\n} LX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE, *PLX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE;\n\ntypedef struct _LX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE\n{\n    static inline auto Type = LxInitMessageAddVirtioFsDevice;\n    using TResponse = LX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE;\n\n    MESSAGE_HEADER Header;\n    bool Admin;\n    unsigned int PathOffset;\n    unsigned int OptionsOffset;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Admin), STRING_FIELD(PathOffset), STRING_FIELD(OptionsOffset));\n} LX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE, *PLX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE;\n\ntypedef struct _LX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE\n{\n    static inline auto Type = LxInitMessageRemountVirtioFsDevice;\n    using TResponse = LX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE;\n\n    MESSAGE_HEADER Header;\n    bool Admin;\n    unsigned int TagOffset;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Admin), STRING_FIELD(TagOffset));\n} LX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE, *PLX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE;\n\n//\n// The messages that can be sent to mini_init.\n//\n\ntypedef enum _LX_MINI_INIT_MESSAGE_FLAGS\n{\n    LxMiniInitMessageFlagNone = 0,\n    LxMiniInitMessageFlagMountReadOnly = 0x1,\n    LxMiniInitMessageFlagCreateOverlayFs = 0x2,\n    LxMiniInitMessageFlagLaunchSystemDistro = 0x4,\n    LxMiniInitMessageFlagExportCompressGzip = 0x8,\n    LxMiniInitMessageFlagExportCompressXzip = 0x10,\n    LxMiniInitMessageFlagVerbose = 0x20,\n} LX_MINI_INIT_MESSAGE_FLAGS,\n    *PLX_MINI_INIT_MESSAGE_FLAGS;\n\ntypedef enum _LX_MINI_INIT_MOUNT_DEVICE_TYPE\n{\n    LxMiniInitMountDeviceTypeInvalid = 0,\n    LxMiniInitMountDeviceTypeLun = 0x1,\n    LxMiniInitMountDeviceTypePmem = 0x2,\n} LX_MINI_INIT_MOUNT_DEVICE_TYPE,\n    *PLX_MINI_INIT_MOUNT_DEVICE_TYPE;\n\ntypedef struct _LX_MINI_INIT_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageLaunchInit;\n\n    MESSAGE_HEADER Header;\n    LX_MINI_INIT_MOUNT_DEVICE_TYPE MountDeviceType;\n    unsigned int DeviceId;\n    unsigned int FsTypeOffset;\n    unsigned int MountOptionsOffset;\n    unsigned int VmIdOffset;\n    unsigned int DistributionNameOffset;\n    unsigned int SharedMemoryRootOffset;\n    unsigned int InstallPathOffset;\n    unsigned int UserProfileOffset;\n    unsigned int Flags;\n    unsigned int ConnectPort;\n    char Buffer[];\n\n    PRETTY_PRINT(\n        FIELD(Header),\n        FIELD(MountDeviceType),\n        FIELD(DeviceId),\n        STRING_FIELD(FsTypeOffset),\n        STRING_FIELD(MountOptionsOffset),\n        STRING_FIELD(DistributionNameOffset),\n        STRING_FIELD(SharedMemoryRootOffset),\n        STRING_FIELD(InstallPathOffset),\n        STRING_FIELD(UserProfileOffset),\n        FIELD(Flags),\n        FIELD(ConnectPort));\n\n} LX_MINI_INIT_MESSAGE, *PLX_MINI_INIT_MESSAGE;\n\nusing PCLX_MINI_INIT_MESSAGE = const LX_MINI_INIT_MESSAGE*;\n\ntypedef enum _LX_MINI_INIT_MEMORY_RECLAIM_MODE\n{\n    LxMiniInitMemoryReclaimModeDisabled,\n    LxMiniInitMemoryReclaimModeGradual,\n    LxMiniInitMemoryReclaimModeDropCache\n} LX_MINI_INIT_MEMORY_RECLAIM_MODE,\n    *PLX_MINI_INIT_MEMORY_RECLAIM_MODE;\n\ntypedef struct _LX_MINI_INIT_EARLY_CONFIG_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageEarlyConfig;\n\n    MESSAGE_HEADER Header;\n    unsigned int SwapLun;\n    LX_MINI_INIT_MOUNT_DEVICE_TYPE SystemDistroDeviceType;\n    unsigned int SystemDistroDeviceId;\n    int PageReportingOrder;\n    LX_MINI_INIT_MEMORY_RECLAIM_MODE MemoryReclaimMode;\n    // IPv4 address stored in network byte order\n    uint32_t DnsTunnelingIpAddress = 0;\n    bool EnableDebugShell;\n    bool EnableDnsTunneling;\n    bool EnableSafeMode;\n    bool DefaultKernel;\n    unsigned int KernelModulesDeviceId;\n    unsigned int HostnameOffset;\n    unsigned int KernelModulesListOffset;\n    char Buffer[];\n\n    PRETTY_PRINT(\n        FIELD(Header),\n        FIELD(SwapLun),\n        FIELD(SystemDistroDeviceType),\n        FIELD(SystemDistroDeviceId),\n        FIELD(PageReportingOrder),\n        FIELD(MemoryReclaimMode),\n        FIELD(DnsTunnelingIpAddress),\n        FIELD(EnableDebugShell),\n        FIELD(EnableDnsTunneling),\n        FIELD(EnableSafeMode),\n        FIELD(DefaultKernel),\n        FIELD(KernelModulesDeviceId),\n        STRING_FIELD(HostnameOffset),\n        STRING_FIELD(KernelModulesListOffset));\n} LX_MINI_INIT_EARLY_CONFIG_MESSAGE, *PLX_MINI_INIT_EARLY_CONFIG_MESSAGE;\n\nusing PCLX_MINI_INIT_EARLY_CONFIG_MESSAGE = const LX_MINI_INIT_EARLY_CONFIG_MESSAGE*;\n\ntypedef enum _LX_MINI_INIT_PORT_TRACKER_TYPE\n{\n    LxMiniInitPortTrackerTypeNone,\n    LxMiniInitPortTrackerTypeMirrored,\n    LxMiniInitPortTrackerTypeRelay,\n} LX_MINI_INIT_PORT_TRACKER_TYPE,\n    *PLX_MINI_INIT_PORT_TRACKER_TYPE;\n\ntypedef enum _LX_MINI_INIT_NETWORKING_MODE\n{\n    LxMiniInitNetworkingModeNone = 0,\n    LxMiniInitNetworkingModeNat = 1,\n    LxMiniInitNetworkingModeBridged = 2,\n    LxMiniInitNetworkingModeMirrored = 3,\n    LxMiniInitNetworkingModeVirtioProxy = 4\n} LX_MINI_INIT_NETWORKING_MODE,\n    *PLX_MINI_INIT_NETWORKING_MODE;\n\ntypedef struct _LX_MINI_INIT_NETWORKING_CONFIGURATION\n{\n    LX_MINI_INIT_NETWORKING_MODE NetworkingMode;\n    LX_MINI_INIT_PORT_TRACKER_TYPE PortTrackerType;\n    uint16_t EphemeralPortRangeStart; // Both Start and End are inclusive\n    uint16_t EphemeralPortRangeEnd;\n    bool EnableDhcpClient;\n    bool DisableIpv6;\n    int DhcpTimeout;\n} LX_MINI_INIT_NETWORKING_CONFIGURATION, *PLX_MINI_INIT_NETWORKING_CONFIGURATION;\n\ntypedef struct _LX_MINI_INIT_CONFIG_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageInitialConfig;\n\n    MESSAGE_HEADER Header;\n    int EntropySize;\n    unsigned int EntropyOffset;\n    bool EnableGuiApps;\n    bool MountGpuShares;\n    bool EnableInboxGpuLibs;\n    LX_MINI_INIT_NETWORKING_CONFIGURATION NetworkingConfiguration;\n    char Buffer[];\n\n    PRETTY_PRINT(\n        FIELD(Header),\n        FIELD(EntropySize),\n        FIELD(MountGpuShares),\n        FIELD(EntropyOffset),\n        FIELD(EnableInboxGpuLibs),\n        FIELD(NetworkingConfiguration.NetworkingMode),\n        FIELD(NetworkingConfiguration.PortTrackerType),\n        FIELD(NetworkingConfiguration.EphemeralPortRangeStart),\n        FIELD(NetworkingConfiguration.EphemeralPortRangeEnd),\n        FIELD(NetworkingConfiguration.EnableDhcpClient),\n        FIELD(NetworkingConfiguration.DisableIpv6),\n        FIELD(NetworkingConfiguration.DhcpTimeout));\n\n} LX_MINI_INIT_CONFIG_MESSAGE, *PLX_MINI_INIT_CONFIG_MESSAGE;\n\nusing PCLX_MINI_INIT_CONFIG_MESSAGE = const LX_MINI_INIT_CONFIG_MESSAGE*;\n\ntypedef struct _LX_MINI_INIT_TELEMETRY_MESSAGE\n{\n    static inline auto Type = LxMiniInitTelemetryMessage;\n\n    MESSAGE_HEADER Header;\n    bool ShowDrvFsNotification;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(ShowDrvFsNotification), FIELD(Buffer));\n\n} LX_MINI_INIT_TELEMETRY_MESSAGE, *PLX_MINI_INIT_TELEMETRY_MESSAGE;\n\nusing PCLX_MINI_INIT_TELEMETRY_MESSAGE = const LX_MINI_INIT_TELEMETRY_MESSAGE*;\n\ntypedef struct _LX_MINI_INIT_MOUNT_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageMount;\n\n    MESSAGE_HEADER Header;\n    unsigned int PartitionIndex; // 0 means the disk directly\n    unsigned int ScsiLun;\n    unsigned int TypeOffset;\n    unsigned int TargetNameOffset;\n    unsigned int OptionsOffset;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(PartitionIndex), FIELD(ScsiLun), STRING_FIELD(TypeOffset), STRING_FIELD(TargetNameOffset), STRING_FIELD(OptionsOffset));\n\n} LX_MINI_INIT_MOUNT_MESSAGE, *PLX_MINI_INIT_MOUNT_MESSAGE;\n\nusing PCLX_MINI_INIT_MOUNT_MESSAGE = const LX_MINI_INIT_MOUNT_MESSAGE*;\n\ntypedef struct _LX_MINI_INIT_UNMOUNT_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageUnmount;\n\n    MESSAGE_HEADER Header;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Buffer));\n} LX_MINI_INIT_UNMOUNT_MESSAGE, *PLX_MINI_INIT_UNMOUNT_MESSAGE;\n\ntypedef struct _LX_MINI_INIT_DETACH_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageDetach;\n\n    MESSAGE_HEADER Header;\n    unsigned int ScsiLun;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(ScsiLun));\n} LX_MINI_INIT_DETACH_MESSAGE, *PLX_MINI_INIT_DETACH_MESSAGE;\n\nusing PCLX_MINI_INIT_UNMOUNT_MESSAGE = const LX_MINI_INIT_UNMOUNT_MESSAGE*;\n\ntypedef enum _LX_MINI_MOUNT_STEP\n{\n    LxMiniInitMountStepNone = 0,\n    LxMiniInitMountStepFindDevice = 0x1,\n    LxMiniInitMountStepFindPartition = 0x2,\n    LxMiniInitMountStepMount = 0x3,\n    LxMiniInitMountStepUnmount = 0x4,\n    LxMiniInitMountStepRmDir = 0x5,\n    LxMiniInitMountStepDetectFilesystem = 0x6,\n} LX_MINI_MOUNT_STEP,\n    *PLX_MINI_MOUNT_STEP;\n\ntypedef struct _LX_MINI_INIT_MOUNT_RESULT_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageMountStatus;\n\n    MESSAGE_HEADER Header;\n    int Result;\n    LX_MINI_MOUNT_STEP FailureStep;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Result), FIELD(FailureStep));\n\n} LX_MINI_INIT_MOUNT_RESULT_MESSAGE, *PLX_MINI_INIT_MOUNT_RESULT_MESSAGE;\n\nusing PCLX_MINI_INIT_MOUNT_RESULT_MESSAGE = const LX_MINI_INIT_MOUNT_RESULT_MESSAGE*;\n\ntypedef struct _LX_INIT_GUEST_CAPABILITIES\n{\n    static inline auto Type = LxMiniInitMessageGuestCapabilities;\n\n    MESSAGE_HEADER Header;\n    bool SeccompAvailable;\n    char Buffer[]; // Contains the kernel version string\n\n    PRETTY_PRINT(FIELD(Header), FIELD(SeccompAvailable), FIELD(Buffer));\n} LX_INIT_GUEST_CAPABILITIES, *PLX_INIT_GUEST_CAPABILITIES;\n\ntypedef struct _LX_MINI_INIT_WAIT_FOR_PMEM_DEVICE_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageWaitForPmemDevice;\n    using TResponse = RESULT_MESSAGE<int32_t>;\n\n    MESSAGE_HEADER Header;\n    unsigned int PmemId;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(PmemId));\n} LX_MINI_INIT_WAIT_FOR_PMEM_DEVICE_MESSAGE, *PLX_MINI_INIT_WAIT_FOR_PMEM_DEVICE_MESSAGE;\n\ntypedef struct _LX_MINI_INIT_CHILD_EXIT_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageChildExit;\n\n    MESSAGE_HEADER Header;\n    unsigned int ChildPid;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(ChildPid));\n} LX_MINI_INIT_CHILD_EXIT_MESSAGE, *PLX_MINI_INIT_CHILD_EXIT_MESSAGE;\n\ntypedef struct _LX_MINI_INIT_MOUNT_FOLDER_MESSAGE\n{\n    static inline auto Type = LxMiniInitMountFolder;\n    using TResponse = RESULT_MESSAGE<int32_t>;\n\n    MESSAGE_HEADER Header;\n    bool ReadOnly;\n    unsigned int PathIndex;\n    unsigned int NameIndex;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(ReadOnly), STRING_FIELD(PathIndex), STRING_FIELD(NameIndex));\n} LX_MINI_INIT_MOUNT_FOLDER_MESSAGE, *PLX_MINI_INIT_MOUNT_FOLDER_MESSAGE;\n\ntypedef struct _LX_MINI_INIT_RESIZE_DISTRIBUTION_RESPONSE\n{\n    static inline auto Type = LxMiniInitMessageResizeDistributionResponse;\n\n    MESSAGE_HEADER Header;\n    uint32_t ResponseCode;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(ResponseCode));\n} LX_MINI_INIT_RESIZE_DISTRIBUTION_RESPONSE, *PLX_MINI_INIT_RESIZE_DISTRIBUTION_RESPONSE;\n\ntypedef struct _LX_MINI_INIT_RESIZE_DISTRIBUTION_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageResizeDistribution;\n\n    MESSAGE_HEADER Header;\n    unsigned int ScsiLun;\n    uint64_t NewSize;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(ScsiLun), FIELD(NewSize));\n} LX_MINI_INIT_RESIZE_DISTRIBUTION_MESSAGE, *PLX_MINI_INIT_RESIZE_DISTRIBUTION_MESSAGE;\n\nstruct CREATE_PROCESS_MESSAGE\n{\n    static inline auto Type = LxInitCreateProcess;\n\n    MESSAGE_HEADER Header;\n    unsigned int PathIndex;\n    unsigned int CommandLineIndex;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), STRING_FIELD(PathIndex), STRING_FIELD(CommandLineIndex));\n};\n\nstruct EJECT_VHD_MESSAGE\n{\n    static inline auto Type = LxMiniInitMessageEjectVhd;\n    using TResponse = RESULT_MESSAGE<int32_t>;\n\n    MESSAGE_HEADER Header;\n    uint32_t Lun;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Lun));\n};\n\ntypedef enum _LX_MINI_CREATE_INSTANCE_STEP\n{\n    LxInitCreateInstanceStepNone = 0,\n    LxInitCreateInstanceStepFormatDisk = 0x1,\n    LxInitCreateInstanceStepMountDisk = 0x2,\n    LxInitCreateInstanceStepLaunchSystemDistro = 0x3,\n    LxInitCreateInstanceStepLaunchInit = 0x3,\n    LxInitCreateInstanceStepRunTar = 0x4\n} LX_MINI_CREATE_INSTANCE_STEP,\n    *PLX_MINI_CREATE_INSTANCE_STEP;\n\ntypedef struct _LX_MINI_INIT_CREATE_INSTANCE_RESULT\n{\n    static inline auto Type = LxMiniInitMessageCreateInstanceResult;\n\n    MESSAGE_HEADER Header;\n    int Result;\n    _LX_MINI_CREATE_INSTANCE_STEP FailureStep;\n    uint64_t Pid;\n    uint32_t ConnectPort;\n    unsigned int WarningsOffset;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Result), FIELD(FailureStep), FIELD(Pid), FIELD(ConnectPort), STRING_FIELD(WarningsOffset));\n} LX_MINI_INIT_CREATE_INSTANCE_RESULT, *P_LX_MINI_INIT_CREATE_INSTANCE_RESULT;\n\ntypedef struct _LX_MINI_INIT_IMPORT_RESULT\n{\n    static inline auto Type = LxMiniInitMessageImportResult;\n\n    MESSAGE_HEADER Header;\n    int Result;\n    bool ValidDistribution;\n    unsigned int FlavorIndex;\n    unsigned int VersionIndex;\n    unsigned int ShortcutIconIndex;\n    unsigned int ShortcutIconSize;\n    unsigned int DefaultNameIndex;\n    unsigned int TerminalProfileIndex;\n    unsigned int TerminalProfileSize;\n    bool GenerateTerminalProfile;\n    bool GenerateShortcut;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Result), STRING_FIELD(FlavorIndex), STRING_FIELD(VersionIndex), STRING_FIELD(DefaultNameIndex), FIELD(ShortcutIconIndex), FIELD(TerminalProfileIndex));\n} LX_MINI_INIT_IMPORT_RESULT, *PLX_MINI_INIT_IMPORT_RESULT;\n\ntypedef struct _LX_INIT_OOBE_RESULT\n{\n    static inline auto Type = LxInitOobeResult;\n\n    MESSAGE_HEADER Header;\n    uint32_t Result;\n    int64_t DefaultUid;\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Result), FIELD(DefaultUid));\n} LX_INIT_OOBE_RESULT, *PLX_INIT_OOBE_RESULT;\n\ntypedef struct _LX_INIT_QUERY_VM_ID\n{\n    static inline auto Type = LxInitMessageQueryVmId;\n\n    MESSAGE_HEADER Header;\n    char Buffer[];\n\n    PRETTY_PRINT(FIELD(Header), FIELD(Buffer));\n} LX_INIT_QUERY_VM_ID, *PLX_INIT_QUERY_VM_ID;\n\ntemplate <>\nstruct std::formatter<LX_MESSAGE_TYPE, char>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(LX_MESSAGE_TYPE str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", ToString(str));\n    }\n};\n\ntemplate <>\nstruct std::formatter<LX_MINI_INIT_MOUNT_DEVICE_TYPE, char>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(LX_MINI_INIT_MOUNT_DEVICE_TYPE str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", static_cast<int>(str));\n    }\n};\n"
  },
  {
    "path": "src/shared/inc/message.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    message.h\n\nAbstract:\n\n    This file contains a utility class to write serialized messages.\n\n--*/\n\n#pragma once\n\n#include <vector>\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <gsl/gsl>\n#include <cassert>\n\nnamespace wsl::shared {\n\ntemplate <typename TMessage>\nclass MessageWriter\n{\n    using THeader = decltype(TMessage::Header);\n    using TMessageType = decltype(THeader::MessageType);\n\npublic:\n    MessageWriter(TMessageType type)\n    {\n        // Note: this is required because the structure might be padded. For instance:\n        // struct a\n        // {\n        //    char c;\n        //    char buffer[0];\n        // };\n        // Would have a.buffer at byte 1, but sizeof(a) can be > 1 depending on padding.\n\n        const auto bufferOffset = reinterpret_cast<size_t>(&reinterpret_cast<TMessage*>(0)->Buffer);\n        m_buffer.resize(bufferOffset);\n\n        (*this)->Header.MessageType = type;\n        Size() = static_cast<unsigned long>(bufferOffset);\n\n        // Validate that 'Buffer' has a char type.\n        static_assert(std::is_same_v<std::remove_reference_t<decltype((*this)->Buffer[0])>, char>);\n    }\n\n    MessageWriter() : MessageWriter(TMessage::Type)\n    {\n    }\n\n    TMessage* operator->()\n    {\n        return reinterpret_cast<TMessage*>(m_buffer.data());\n    }\n\n    void WriteString(std::string_view String)\n    {\n        std::transform(String.begin(), String.end(), std::back_inserter(m_buffer), [](auto c) { return static_cast<std::byte>(c); });\n        m_buffer.push_back(std::byte{0}); // zero terminator for the string\n\n        Size() = static_cast<unsigned int>(m_buffer.size());\n    }\n\n    void WriteString(const char* String)\n    {\n        WriteString(String ? std::string_view{String} : std::string_view{});\n    }\n\n    void WriteString(unsigned int& Index, std::string_view String)\n    {\n        // Don't write directly to Index since resizing the buffer might invalidate it.\n        // Instead save its relative offset to the buffer so we can write there after the resize.\n        const auto IndexOffset = GetRelativeIndex(Index);\n        const auto IndexValue = Size();\n\n        WriteString(String);\n        WriteRelativeIndex(IndexOffset, IndexValue);\n    }\n\n    void WriteString(unsigned int& Index, const char* String)\n    {\n        WriteString(Index, String ? std::string_view{String} : std::string_view{});\n    }\n\n    void WriteSpan(const gsl::span<gsl::byte>& Span)\n    {\n        gsl::copy(Span, InsertBuffer(Span.size()));\n    }\n\n    gsl::span<std::byte> InsertBuffer(unsigned int& Index, size_t BufferSize, unsigned int& Size)\n    {\n        Size = BufferSize;\n        return InsertBuffer(Index, BufferSize);\n    }\n\n    gsl::span<std::byte> InsertBuffer(unsigned int& Index, size_t BufferSize)\n    {\n        const auto IndexOffset = GetRelativeIndex(Index);\n        const auto IndexValue = Size();\n\n        m_buffer.resize(m_buffer.size() + BufferSize);\n        WriteRelativeIndex(IndexOffset, IndexValue);\n        Size() = static_cast<unsigned long>(m_buffer.size());\n\n        return Span().subspan(IndexValue, BufferSize);\n    }\n\n    gsl::span<std::byte> InsertBuffer(size_t BufferSize)\n    {\n        m_buffer.resize(m_buffer.size() + BufferSize);\n        const auto Index = Size();\n        Size() = static_cast<unsigned long>(m_buffer.size());\n\n        return Span().subspan(Index, BufferSize);\n    }\n\n    void WriteString(const std::wstring& String)\n    {\n        WriteString(wsl::shared::string::WideToMultiByte(String));\n    }\n\n    void WriteString(const wchar_t* String)\n    {\n        WriteString(wsl::shared::string::WideToMultiByte(String));\n    }\n\n    void WriteString(unsigned int& Index, const std::wstring& String)\n    {\n        WriteString(Index, wsl::shared::string::WideToMultiByte(String));\n    }\n\n    void WriteString(unsigned int& Index, const wchar_t* String)\n    {\n        WriteString(Index, wsl::shared::string::WideToMultiByte(String));\n    }\n\n    gsl::span<std::byte> Span()\n    {\n        // In case the structure is padded,\n        // make sure that the message is at least the size of the structure.\n\n        const int64_t diff = sizeof(TMessage) - m_buffer.size();\n        if (diff > 0)\n        {\n            InsertBuffer(diff);\n        }\n\n        return gsl::make_span(m_buffer);\n    }\n\n    std::vector<std::byte> MoveBuffer()\n    {\n        return std::vector<std::byte>(std::move(m_buffer));\n    }\n\nprivate:\n    unsigned int& Size()\n    {\n        return (*this)->Header.MessageSize;\n    }\n\n    size_t GetRelativeIndex(unsigned int& Index)\n    {\n        const size_t Offset = reinterpret_cast<char*>(&Index) - reinterpret_cast<char*>(m_buffer.data());\n\n        // Validate that 'Index' is actually within the bounds of our buffer\n        assert(Offset >= 0 && Offset < m_buffer.size());\n\n        return Offset;\n    }\n\n    void WriteRelativeIndex(size_t Offset, unsigned int Value)\n    {\n        *reinterpret_cast<unsigned int*>(&m_buffer[Offset]) = Value;\n    }\n\n    std::vector<std::byte> m_buffer;\n    size_t m_offset = 0;\n};\n} // namespace wsl::shared"
  },
  {
    "path": "src/shared/inc/prettyprintshared.h",
    "content": "//\n// Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n\n/*++\n\nModule Name:\n\n    prettyprintshared.h\n\nAbstract:\n\n    This is the header file for message pretty printing.\n\n--*/\n\n#pragma once\n\n#include <sstream>\n\n#include \"defs.h\"\n#include \"stringshared.h\"\n\n#ifdef WIN32\n#include <guiddef.h>\n#else\n#include \"lxdef.h\"\n#endif\n\n#define FIELD(Name) #Name, Name\n\n#define STRING_FIELD(Name) #Name, (Name <= 0 ? \"<empty>\" : ((char*)(this)) + Name)\n\n#define PRETTY_PRINT(...) \\\n    void PrettyPrintImpl(std::stringstream& Out) const \\\n    { \\\n        PrettyPrintField(Out, __VA_ARGS__); \\\n    } \\\n\\\n    std::string PrettyPrint() const \\\n    { \\\n        std::stringstream Out; \\\n        PrettyPrintImpl(Out); \\\n        return Out.str(); \\\n    }\n\ntemplate <typename T>\ninline void PrettyPrint(std::stringstream& Out, const T& Value)\n{\n    if constexpr (std::is_same_v<T, const char*> || std::is_same_v<T, char[]>)\n    {\n        if (Value == nullptr)\n        {\n            Out << \"<null>\";\n        }\n        else\n        {\n            Out << Value;\n        }\n    }\n    else if constexpr (std::is_same_v<T, GUID>)\n    {\n        Out << wsl::shared::string::GuidToString<char>(Value);\n    }\n    else if constexpr (std::is_same_v<T, char>)\n    {\n        Out << Value;\n    }\n    else if constexpr (std::is_fundamental_v<T> || std::is_enum_v<T>)\n    {\n        // N.B. Enum can be specialized by creating an overload for this method.\n        Out << std::to_string(Value);\n    }\n    else\n    {\n        Out << \"{\";\n        Value.PrettyPrintImpl(Out);\n        Out << \"}\";\n    }\n}\n\ntemplate <typename T, int Size>\ninline void PrettyPrint(std::stringstream& Out, const T (&Value)[Size])\n{\n    Out << \"[\";\n    for (auto i = 0; i < Size; i++)\n    {\n        if (i > 0 && i < Size - 1)\n        {\n            Out << \",\";\n        }\n\n        PrettyPrint(Out, Value[i]);\n    }\n\n    Out << \"]\";\n}\n\ntemplate <typename TArg>\ninline void PrettyPrintField(std::stringstream& Out, const char* FieldName, const TArg& FieldValue)\n{\n    Out << FieldName << \" = \";\n    PrettyPrint(Out, FieldValue);\n    Out << \"\\n\";\n}\n\ntemplate <typename TFirst, typename... TArgs>\ninline void PrettyPrintField(std::stringstream& Out, const char* FieldName, const TFirst& FieldValue, TArgs&&... Args)\n{\n    PrettyPrintField(Out, FieldName, FieldValue);\n    PrettyPrintField(Out, std::forward<TArgs>(Args)...);\n}"
  },
  {
    "path": "src/shared/inc/retryshared.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    retryshared.h\n\nAbstract:\n\n    This file contains shared retry helper functions.\n\n--*/\n\n#pragma once\n\nnamespace wsl::shared::retry {\n\nconstexpr auto AlwaysRetry = []() { return true; };\n\ntemplate <typename T, typename TPeriod, typename TTimeout>\nT RetryWithTimeout(const std::function<T()>& routine, TPeriod retryPeriod, TTimeout timeout, const std::function<bool()>& retryPred = AlwaysRetry)\n{\n    auto stop = std::chrono::steady_clock::now() + timeout;\n    for (;;)\n    {\n        try\n        {\n            return routine();\n        }\n        catch (...)\n        {\n            if (!retryPred() || std::chrono::steady_clock::now() > stop)\n            {\n                throw;\n            }\n\n            std::this_thread::sleep_for(retryPeriod);\n        }\n    }\n}\n\n} // namespace wsl::shared::retry\n"
  },
  {
    "path": "src/shared/inc/socketshared.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    socketshared.h\n\nAbstract:\n\n    This file contains shared socket helper functions.\n\n--*/\n\n#pragma once\n#include <cassert>\n\nnamespace wsl::shared::socket {\n\n#if defined(_MSC_VER)\ninline gsl::span<gsl::byte> RecvMessage(SOCKET Socket, std::vector<gsl::byte>& Buffer, std::optional<HANDLE> ExitHandle = {}, DWORD Timeout = INFINITE)\n#elif defined(__GNUC__)\ninline gsl::span<gsl::byte> RecvMessage(int Socket, std::vector<gsl::byte>& Buffer, const timeval* Timeout = nullptr)\n#endif\ntry\n{\n    auto MessageSize = sizeof(MESSAGE_HEADER);\n    if (Buffer.size() < MessageSize)\n    {\n        Buffer.resize(MessageSize);\n    }\n\n    auto Message = gsl::make_span(Buffer.data(), MessageSize);\n#if defined(_MSC_VER)\n    auto BytesRead = wsl::windows::common::socket::Receive(Socket, Message, ExitHandle.value_or(nullptr), MSG_WAITALL, Timeout);\n#elif defined(__GNUC__)\n    // 'Timeout' is not implemented on Linux.\n    assert(Timeout == nullptr);\n\n    auto BytesRead = TEMP_FAILURE_RETRY(recv(Socket, Message.data(), Message.size(), MSG_WAITALL));\n    THROW_LAST_ERROR_IF(BytesRead < 0);\n#endif\n    if (BytesRead == 0)\n    {\n        return {};\n    }\n    else if (BytesRead < MessageSize)\n    {\n#if defined(_MSC_VER)\n        THROW_HR(E_UNEXPECTED);\n#elif defined(__GNUC__)\n        THROW_UNEXCEPTED();\n#endif\n    }\n\n    // Grow the message buffer if needed and read the rest of the message.\n    MessageSize = gslhelpers::get_struct<MESSAGE_HEADER>(Message)->MessageSize;\n    if (MessageSize < sizeof(MESSAGE_HEADER))\n    {\n#if defined(_MSC_VER)\n        THROW_HR_MSG(E_UNEXPECTED, \"Unexpected message size: %llu\", MessageSize);\n#elif defined(__GNUC__)\n        THROW_UNEXCEPTED();\n#endif\n    }\n\n    if (Buffer.size() < MessageSize)\n    {\n        Buffer.resize(MessageSize);\n    }\n\n    Message = gsl::make_span(Buffer.data(), MessageSize).subspan(sizeof(MESSAGE_HEADER));\n    while (Message.size() > 0)\n    {\n#if defined(_MSC_VER)\n        BytesRead = wsl::windows::common::socket::Receive(Socket, Message, ExitHandle.value_or(nullptr), 0);\n#elif defined(__GNUC__)\n        BytesRead = TEMP_FAILURE_RETRY(recv(Socket, Message.data(), Message.size(), 0));\n        THROW_LAST_ERROR_IF(BytesRead < 0);\n#endif\n        if (BytesRead <= 0)\n        {\n            const auto* Header = reinterpret_cast<const MESSAGE_HEADER*>(Buffer.data());\n\n#if defined(_MSC_VER)\n\n            LOG_HR_MSG(\n                E_UNEXPECTED,\n                \"Socket closed while reading message. Size: %u, type: %i, sequence: %u\",\n                Header->MessageSize,\n                Header->MessageType,\n                Header->SequenceNumber);\n\n#elif defined(__GNUC__)\n\n            LOG_ERROR(\n                \"Socket closed while reading message. Size: {}, type: {}, sequence: {}\",\n                Header->MessageSize,\n                Header->MessageType,\n                Header->SequenceNumber);\n\n#endif\n\n            return {};\n        }\n\n        Message = Message.subspan(BytesRead);\n    }\n\n    return gsl::make_span(Buffer.data(), MessageSize);\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    errno = wil::ResultFromCaughtException();\n    return {};\n}\n\n} // namespace wsl::shared::socket\n"
  },
  {
    "path": "src/shared/inc/stringshared.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    stringshared.h\n\nAbstract:\n\n    This file contains shared string helper functions.\n\n--*/\n\n#pragma once\n#include <set>\n#include <vector>\n#include <string>\n#include <sstream>\n#include <fstream>\n#include <gsl/gsl>\n#include <format>\n#include <source_location>\n\n#ifndef WIN32\n#include <string.h>\n#include \"lxdef.h\"\n#include \"lxwil.h\"\n#include \"defs.h\"\n#else\n#include \"string.hpp\"\n#endif\n\n#define STRING_TO_WIDE_STRING_INNER(_str) L##_str\n#define STRING_TO_WIDE_STRING(_str) STRING_TO_WIDE_STRING_INNER(_str)\n\n#define GUID_FORMAT_STRING \"{%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x}\"\n#define GUID_SSCANF_STRING \"%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx\"\n#define GUID_BRACES_SSCANF_STRING \"{\" GUID_SSCANF_STRING \"}\"\n\n#define MAC_ADDRESS_FORMAT_STRING \"%02X%c%02X%c%02X%c%02X%c%02X%c%02X\"\n\nnamespace wsl::shared::string {\n\nusing MacAddress = std::array<std::uint8_t, 6>;\n\ninline unsigned int CopyToSpan(const std::string_view String, const gsl::span<gsl::byte> Span, size_t& Offset)\n{\n    gsl::copy(as_bytes(gsl::make_span(String.data(), String.size())), Span.subspan(Offset));\n    const auto PreviousOffset = gsl::narrow_cast<unsigned int>(Offset);\n    Offset += String.size() + 1;\n    return PreviousOffset;\n}\n\ntemplate <class T>\ninline bool EndsWith(const std::basic_string<T>& String, const std::basic_string_view<T> Suffix)\n{\n    if (Suffix.size() > String.size())\n    {\n        return false;\n    }\n\n    return std::equal(Suffix.rbegin(), Suffix.rend(), String.rbegin());\n}\n\ntemplate <class T, class TInput>\ninline std::basic_string<T> Join(const std::vector<TInput>& Input, T Separator)\n{\n    std::basic_stringstream<T> Out;\n    for (size_t Index = 0; Index < Input.size(); Index += 1)\n    {\n        if (Index != 0)\n        {\n            Out << Separator;\n        }\n\n        Out << Input[Index];\n    }\n\n    return Out.str();\n}\n\ntemplate <class T>\ninline std::vector<std::basic_string<T>> Split(const std::basic_string<T>& String, T Separator)\n{\n    std::vector<std::basic_string<T>> Output;\n    std::basic_istringstream<T> Input(String);\n    std::basic_string<T> Entry;\n    while (std::getline(Input, Entry, Separator))\n    {\n        if (!Entry.empty())\n        {\n            Output.emplace_back(std::move(Entry));\n        }\n    }\n\n    return Output;\n}\n\ntemplate <class T>\ninline std::vector<std::basic_string<T>> SplitByMultipleSeparators(const std::basic_string<T>& String, const std::basic_string<T>& Separators)\n{\n    std::vector<std::basic_string<T>> Output;\n    size_t CurrentIndex = 0;\n\n    while (true)\n    {\n        CurrentIndex = String.find_first_not_of(Separators, CurrentIndex);\n        if (CurrentIndex == std::string::npos)\n        {\n            break;\n        }\n\n        const size_t NextSeparator = String.find_first_of(Separators, CurrentIndex);\n\n        if (NextSeparator == std::string::npos)\n        {\n            Output.emplace_back(std::move(String.substr(CurrentIndex)));\n            break;\n        }\n        else\n        {\n            Output.emplace_back(std::move(String.substr(CurrentIndex, NextSeparator - CurrentIndex)));\n            CurrentIndex = NextSeparator;\n        }\n    }\n\n    return Output;\n}\n\ninline const char* FromSpan(gsl::span<gsl::byte> Span, size_t Offset = 0)\n{\n    THROW_INVALID_ARG_IF(Span.size() < Offset);\n\n    Span = Span.subspan(Offset);\n    const std::string_view String{reinterpret_cast<const char*>(Span.data()), Span.size()};\n    const auto End = String.find('\\0');\n    THROW_INVALID_ARG_IF(End == String.npos);\n\n    return String.data();\n}\n\nconstexpr auto c_defaultHostName = \"localhost\";\n\ninline std::string CleanHostname(const std::string_view Hostname)\n{\n    // A valid Linux hostname:\n    //  - is composed of alphanumeric characters, hyphens, and up to one dot\n    //  - cannot start or end with a hyphen or a dot\n    //  - cannot have a hyphen follow a dot or another hyphen\n    //  - cannot be empty\n    //  - cannot be longer than 64 chars\n    bool dot = false;\n    std::string result;\n    for (const auto e : Hostname)\n    {\n        if (e == '.')\n        {\n            // There can be only one '.', it cannot be the first character, and it cannot follow a '-'.\n            if (dot || result.empty() || result.back() == '-')\n            {\n                continue;\n            }\n\n            dot = true;\n            result += e;\n        }\n        else if (e == '-')\n        {\n            // A '-' cannot be the first character, or follow another '-' or a '.'.\n            if (result.empty() || result.back() == '-' || result.back() == '.')\n            {\n                continue;\n            }\n\n            result += e;\n        }\n        else if (isalnum(e))\n        {\n            result += e;\n        }\n    }\n\n    while (!result.empty() && (result.back() == '.' || result.back() == '-'))\n    {\n        result.pop_back();\n    }\n\n    if (result.empty())\n    {\n        result = c_defaultHostName;\n    }\n    else if (result.size() > 64)\n    {\n        result.resize(64);\n    }\n\n    return result;\n}\n\ntemplate <typename T>\ninline size_t Compare(const std::basic_string_view<T> String1, const std::basic_string_view<T> String2, bool CaseInsensitive = false)\n{\n    // This method counts the number of matching characters at the beginning of two strings.\n    std::basic_string_view<T> firstString;\n    std::basic_string_view<T> secondString;\n    if (String1.size() <= String2.size())\n    {\n        firstString = String1;\n        secondString = String2;\n    }\n    else\n    {\n        firstString = String2;\n        secondString = String1;\n    }\n\n    if (CaseInsensitive)\n    {\n        std::locale loc{\"C\"};\n        auto result = std::mismatch(firstString.begin(), firstString.end(), secondString.begin(), [loc](T a, T b) {\n            return (std::tolower(a, loc) == std::tolower(b, loc));\n        });\n\n        return (result.first - firstString.begin());\n    }\n    else\n    {\n        auto result = std::mismatch(firstString.begin(), firstString.end(), secondString.begin());\n        return (result.first - firstString.begin());\n    }\n}\n\ninline bool IsEqual(const std::string_view String1, const std::string_view String2, bool CaseInsensitive = false)\n{\n    if (String1.size() != String2.size())\n    {\n        return false;\n    }\n\n    return (Compare(String1, String2, CaseInsensitive) == String1.size());\n}\n\ninline bool IsEqual(const std::wstring_view String1, const std::wstring_view String2, bool CaseInsensitive = false)\n{\n    if (String1.size() != String2.size())\n    {\n        return false;\n    }\n\n    return (Compare(String1, String2, CaseInsensitive) == String1.size());\n}\n\ntemplate <typename T>\ninline std::optional<bool> ParseBool(const T* String)\n{\n    if (!String)\n    {\n        return {};\n    }\n\n    const std::basic_string_view<T> StringView(String);\n    constexpr T One[] = {T('1'), T('\\0')};\n    constexpr T True[] = {T('t'), T('r'), T('u'), T('e'), T('\\0')};\n    if (IsEqual(StringView, One) || IsEqual(StringView, True, true))\n    {\n        return true;\n    }\n\n    constexpr T Zero[] = {T('0'), T('\\0')};\n    constexpr T False[] = {T('f'), T('a'), T('l'), T('s'), T('e'), T('\\0')};\n    if (IsEqual(StringView, Zero) || IsEqual(StringView, False, true))\n    {\n        return false;\n    }\n\n    return {};\n}\n\ntemplate <typename T>\ninline uint64_t ToUInt64(const T* String, T** End = nullptr, int Base = 10);\n\ntemplate <>\ninline uint64_t ToUInt64<char>(const char* String, char** End, int Base)\n{\n    return std::strtoull(String, End, Base);\n}\n\ntemplate <>\ninline uint64_t ToUInt64<wchar_t>(const wchar_t* String, wchar_t** End, int Base)\n{\n    return std::wcstoull(String, End, Base);\n}\n\ntemplate <typename T>\ninline std::optional<uint64_t> ParseMemorySize(const T* String)\n{\n    if (!String)\n    {\n        return {};\n    }\n\n    T* End{};\n    uint64_t Value = ToUInt64(String, &End, 10);\n    if (Value == 0)\n    {\n        if (String[0] != T('0') || End != String + 1)\n        {\n            return {};\n        }\n    }\n\n    const std::basic_string_view<T> Remainder(End);\n    if (Remainder.empty())\n    {\n        return Value;\n    }\n    else if (Remainder.size() > 2)\n    {\n        return {};\n    }\n\n    constexpr T Bytes[] = {T('B'), T('\\0')};\n    constexpr T Kilobytes[] = {T('K'), T('B'), T('\\0')};\n    constexpr T Megabytes[] = {T('M'), T('B'), T('\\0')};\n    constexpr T Gigabytes[] = {T('G'), T('B'), T('\\0')};\n    constexpr T Terabytes[] = {T('T'), T('B'), T('\\0')};\n    const std::array<std::pair<std::basic_string_view<T>, uint64_t>, 5> Units{\n        std::make_pair(Bytes, 1ULL),\n        std::make_pair(Kilobytes, 1ULL << 10),\n        std::make_pair(Megabytes, 1ULL << 20),\n        std::make_pair(Gigabytes, 1ULL << 30),\n        std::make_pair(Terabytes, 1ULL << 40)};\n\n    for (const auto& [Suffix, Factor] : Units)\n    {\n        if ((Remainder == Suffix.substr(0, 1)) || (Remainder == Suffix))\n        {\n            return Value * Factor;\n        }\n    }\n\n    return {};\n}\n\ninline bool StartsWith(const std::string_view String, const std::string_view Prefix, bool CaseInsensitive = false)\n{\n    if (String.size() < Prefix.size())\n    {\n        return false;\n    }\n\n    return (Compare(String.substr(0, Prefix.size()), Prefix, CaseInsensitive) == Prefix.size());\n}\n\ninline bool StartsWith(const std::wstring_view String, const std::wstring_view Prefix, bool CaseInsensitive = false)\n{\n    if (String.size() < Prefix.size())\n    {\n        return false;\n    }\n\n    return (Compare(String.substr(0, Prefix.size()), Prefix, CaseInsensitive) == Prefix.size());\n}\n\nenum GuidToStringFlags\n{\n    None = 0,\n    AddBraces = 1,\n    Uppercase = 2\n};\n\ntemplate <typename TChar>\ninline std::basic_string<TChar> GuidToString(const GUID& guid, GuidToStringFlags flags = GuidToStringFlags::AddBraces)\n{\n    // N.B. std::string guarantees that the null terminator is always allocated:\n    //      https://en.cppreference.com/w/cpp/string/basic_string/data\n    std::basic_string<TChar> output(38, '\\0');\n\n    if constexpr (std::is_same_v<TChar, char>)\n    {\n        snprintf(\n            output.data(),\n            output.size() + 1,\n            GUID_FORMAT_STRING,\n            static_cast<unsigned int>(guid.Data1),\n            guid.Data2,\n            guid.Data3,\n            guid.Data4[0],\n            guid.Data4[1],\n            guid.Data4[2],\n            guid.Data4[3],\n            guid.Data4[4],\n            guid.Data4[5],\n            guid.Data4[6],\n            guid.Data4[7]);\n    }\n    else if constexpr (std::is_same_v<TChar, wchar_t>)\n    {\n        swprintf(\n            output.data(),\n            output.size() + 1,\n            STRING_TO_WIDE_STRING(GUID_FORMAT_STRING),\n            static_cast<unsigned int>(guid.Data1),\n            guid.Data2,\n            guid.Data3,\n            guid.Data4[0],\n            guid.Data4[1],\n            guid.Data4[2],\n            guid.Data4[3],\n            guid.Data4[4],\n            guid.Data4[5],\n            guid.Data4[6],\n            guid.Data4[7]);\n    }\n    else\n    {\n        static_assert(sizeof(TChar) != sizeof(TChar), \"Unsupported character type\");\n    }\n\n    if (WI_IsFlagClear(flags, GuidToStringFlags::AddBraces))\n    {\n        output.erase(output.begin());\n        output.pop_back();\n    }\n\n    if (WI_IsFlagSet(flags, GuidToStringFlags::Uppercase))\n    {\n        std::transform(output.begin(), output.end(), output.begin(), toupper);\n    }\n\n    return output;\n}\n\ntemplate <typename TChar>\ninline std::optional<GUID> ToGuid(const TChar* string, std::optional<size_t> length = {})\n{\n    if (!string)\n    {\n        return {};\n    }\n\n    if (!length.has_value())\n    {\n        length = std::basic_string<TChar>{string}.size();\n    }\n\n    GUID guid;\n    int result{};\n    if constexpr (std::is_same_v<TChar, char>)\n    {\n        if (length.value() == 38 && string[0] == '{' && string[37] == '}')\n        {\n            result = sscanf(\n                string,\n                GUID_BRACES_SSCANF_STRING,\n                &guid.Data1,\n                &guid.Data2,\n                &guid.Data3,\n                &guid.Data4[0],\n                &guid.Data4[1],\n                &guid.Data4[2],\n                &guid.Data4[3],\n                &guid.Data4[4],\n                &guid.Data4[5],\n                &guid.Data4[6],\n                &guid.Data4[7]);\n        }\n        else if (length.value() == 36)\n        {\n            result = sscanf(\n                string,\n                GUID_SSCANF_STRING,\n                &guid.Data1,\n                &guid.Data2,\n                &guid.Data3,\n                &guid.Data4[0],\n                &guid.Data4[1],\n                &guid.Data4[2],\n                &guid.Data4[3],\n                &guid.Data4[4],\n                &guid.Data4[5],\n                &guid.Data4[6],\n                &guid.Data4[7]);\n        }\n    }\n    else if constexpr (std::is_same_v<TChar, wchar_t>)\n    {\n        if (length.value() == 38 && string[0] == '{' && string[37] == '}')\n        {\n            result = swscanf(\n                string,\n                STRING_TO_WIDE_STRING(GUID_BRACES_SSCANF_STRING),\n                &guid.Data1,\n                &guid.Data2,\n                &guid.Data3,\n                &guid.Data4[0],\n                &guid.Data4[1],\n                &guid.Data4[2],\n                &guid.Data4[3],\n                &guid.Data4[4],\n                &guid.Data4[5],\n                &guid.Data4[6],\n                &guid.Data4[7]);\n        }\n        else if (length.value() == 36)\n        {\n            result = swscanf(\n                string,\n                STRING_TO_WIDE_STRING(GUID_SSCANF_STRING),\n                &guid.Data1,\n                &guid.Data2,\n                &guid.Data3,\n                &guid.Data4[0],\n                &guid.Data4[1],\n                &guid.Data4[2],\n                &guid.Data4[3],\n                &guid.Data4[4],\n                &guid.Data4[5],\n                &guid.Data4[6],\n                &guid.Data4[7]);\n        }\n    }\n    else\n    {\n        static_assert(sizeof(TChar) != sizeof(TChar), \"Unsupported character type\");\n    }\n\n    if (result != 11)\n    {\n        return {};\n    }\n\n    return guid;\n}\n\ntemplate <typename TChar>\ninline std::optional<GUID> ToGuid(const std::basic_string_view<TChar> string)\n{\n    return ToGuid(string.data(), string.size());\n}\n\ntemplate <typename TChar>\ninline std::optional<GUID> ToGuid(const std::basic_string<TChar>& string)\n{\n    return ToGuid(string.data(), string.size());\n}\n\ntemplate <typename TChar, typename TPath>\ninline std::basic_string<TChar> ReadFile(const TPath* path)\n{\n    std::basic_ifstream<TChar> file;\n    file.exceptions(std::ios::badbit | std::ios::failbit);\n\n    try\n    {\n        file.open(path);\n        return std::basic_string<TChar>{std::istreambuf_iterator<TChar>(file), {}};\n    }\n    catch (...)\n    {\n        THROW_LAST_ERROR();\n    }\n}\n\ninline std::wstring MultiByteToWide(const char* string)\n{\n\n#ifdef WIN32\n\n    // This uses MultiByteToWideChar which gets the desired CP_UTF8 behavior\n    return wsl::windows::common::string::MultiByteToWide(string);\n\n#else\n\n    if (!string)\n    {\n        return {};\n    }\n\n    std::mbstate_t state{};\n    size_t size = std::mbsrtowcs(nullptr, &string, 0, &state);\n    THROW_LAST_ERROR_IF(size == -1);\n\n    if (size == 0)\n    {\n        return {};\n    }\n\n    std::wstring buffer(size, L'\\0');\n    std::mbsrtowcs(buffer.data(), &string, size, &state);\n    return buffer;\n\n#endif // WIN32\n}\n\ninline std::wstring MultiByteToWide(const std::string& string)\n{\n    return MultiByteToWide(string.c_str());\n}\n\ninline std::string WideToMultiByte(const wchar_t* string)\n{\n\n#ifdef WIN32\n\n    // This uses WideCharToMultiByte which gets the desired CP_UTF8 behavior\n    return wsl::windows::common::string::WideToMultiByte(string);\n\n#else\n\n    if (!string)\n    {\n        return {};\n    }\n\n    std::mbstate_t state{};\n    size_t size = std::wcsrtombs(nullptr, &string, 0, &state);\n    THROW_LAST_ERROR_IF(size == -1);\n\n    if (size == 0)\n    {\n        return {};\n    }\n\n    std::string buffer(size, '\\0');\n    std::wcsrtombs(buffer.data(), &string, size, &state);\n    return buffer;\n\n#endif // WIN32\n}\n\ninline std::string WideToMultiByte(const std::wstring& string)\n{\n    return WideToMultiByte(string.c_str());\n}\n\ntemplate <typename T>\ninline uint8_t ParseNibble(T HexDigit)\n{\n    // Clearing bit 0x20 will turn a-f to A-F.\n    return (HexDigit >= '0' && HexDigit <= '9') ? (HexDigit - '0') : ((HexDigit & ~0x20) - 'A' + 10);\n}\n\ntemplate <typename T>\ninline std::optional<MacAddress> ParseMacAddressNoThrow(const std::basic_string<T>& Input, T Separator = '\\0')\n{\n    if (Input.size() != 17)\n    {\n        return {};\n    }\n\n    if (Separator == '\\0')\n    {\n        Separator = Input[2];\n        if (Separator != '-' && Separator != ':')\n        {\n            return {};\n        }\n    }\n\n    MacAddress result;\n    for (auto octet = 0; octet < 6; octet++)\n    {\n        size_t index = octet * 3;\n        if (!std::iswxdigit(Input[index]) || !std::iswxdigit(Input[index + 1]))\n        {\n            return {};\n        }\n\n        if (octet < 5 && Input[index + 2] != Separator)\n        {\n            return {};\n        }\n\n        result[octet] = ParseNibble(Input[index]) * 16 + ParseNibble(Input[index + 1]);\n    }\n\n    return result;\n}\n\ntemplate <typename T>\ninline MacAddress ParseMacAddress(const std::basic_string<T>& Input, T Separator = '\\0')\n{\n    auto result = ParseMacAddressNoThrow(Input, Separator);\n\n#ifdef WIN32\n    THROW_HR_IF(E_INVALIDARG, !result.has_value());\n#else\n    THROW_ERRNO_IF(EINVAL, !result.has_value());\n#endif\n\n    return result.value();\n}\n\ntemplate <typename TChar>\ninline std::basic_string<TChar> FormatMacAddress(const MacAddress& input, TChar separator)\n{\n    std::basic_string<TChar> output(17, '\\0');\n\n    if constexpr (std::is_same_v<TChar, char>)\n    {\n        snprintf(\n            output.data(),\n            output.size() + 1,\n            MAC_ADDRESS_FORMAT_STRING,\n            input[0],\n            separator,\n            input[1],\n            separator,\n            input[2],\n            separator,\n            input[3],\n            separator,\n            input[4],\n            separator,\n            input[5]);\n    }\n    else if constexpr (std::is_same_v<TChar, wchar_t>)\n    {\n        swprintf(\n            output.data(),\n            output.size() + 1,\n            STRING_TO_WIDE_STRING(MAC_ADDRESS_FORMAT_STRING),\n            input[0],\n            separator,\n            input[1],\n            separator,\n            input[2],\n            separator,\n            input[3],\n            separator,\n            input[4],\n            separator,\n            input[5]);\n    }\n    else\n    {\n        static_assert(sizeof(TChar) != sizeof(TChar), \"Unsupported character type\");\n    }\n\n    return output;\n}\n\nstruct CaseInsensitiveCompare\n{\n    bool operator()(const std::string& left, const std::string& right) const\n    {\n        return _stricmp(left.c_str(), right.c_str()) < 0;\n    }\n\n    bool operator()(const char* left, const char* right) const\n    {\n        return _stricmp(left, right) < 0;\n    }\n\n    bool operator()(const wchar_t* left, const wchar_t* right) const\n    {\n        return _wcsicmp(left, right) < 0;\n    }\n\n    bool operator()(const std::wstring& left, const std::wstring& right) const\n    {\n        return _wcsicmp(left.c_str(), right.c_str()) < 0;\n    }\n};\n\n} // namespace wsl::shared::string\n\ntemplate <>\nstruct std::formatter<std::wstring, char>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const std::wstring& str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", wsl::shared::string::WideToMultiByte(str));\n    }\n};\n\ntemplate <>\nstruct std::formatter<const wchar_t*, char>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const wchar_t* str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", wsl::shared::string::WideToMultiByte(str));\n    }\n};\n\ntemplate <std::size_t N>\nstruct std::formatter<wchar_t[N], char>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const wchar_t str[N], TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", wsl::shared::string::WideToMultiByte(str));\n    }\n};\n\ntemplate <>\nstruct std::formatter<std::source_location, char>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const std::source_location& location, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}[{}:{}]\", location.function_name(), location.file_name(), location.line());\n    }\n};\n\ntemplate <>\nstruct std::formatter<char*, wchar_t>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const char* str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", wsl::shared::string::MultiByteToWide(str));\n    }\n};\n\ntemplate <>\nstruct std::formatter<const char*, wchar_t>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const char* str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", wsl::shared::string::MultiByteToWide(str));\n    }\n};\n\ntemplate <std::size_t N>\nstruct std::formatter<char[N], wchar_t>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const char str[N], TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", wsl::shared::string::MultiByteToWide(str));\n    }\n};\n\ntemplate <class Traits, class Allocator>\nstruct std::formatter<std::basic_string<char, Traits, Allocator>, wchar_t>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const std::basic_string<char, Traits, Allocator>& str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", wsl::shared::string::MultiByteToWide(str));\n    }\n};\n\ntemplate <>\nstruct std::formatter<std::filesystem::path, wchar_t>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const std::filesystem::path& str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", str.wstring());\n    }\n};\n\ntemplate <>\nstruct std::formatter<GUID, wchar_t>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(const GUID& Guid, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", wsl::shared::string::GuidToString<wchar_t>(Guid));\n    }\n};\n\ntemplate <>\nstruct std::formatter<wchar_t, char>\n{\n    template <typename TCtx>\n    static constexpr auto parse(TCtx& ctx)\n    {\n        return ctx.begin();\n    }\n\n    template <typename TCtx>\n    auto format(wchar_t str, TCtx& ctx) const\n    {\n        return std::format_to(ctx.out(), \"{}\", wsl::shared::string::WideToMultiByte(std::wstring{&str, 1}));\n    }\n};"
  },
  {
    "path": "src/windows/common/CMakeLists.txt",
    "content": "set(SOURCES\n    ConsoleProgressBar.cpp\n    ConsoleProgressIndicator.cpp\n    ConsoleState.cpp\n    DeviceHostProxy.cpp\n    disk.cpp\n    Distribution.cpp\n    Dmesg.cpp\n    DnsResolver.cpp\n    DnsTunnelingChannel.cpp\n    filesystem.cpp\n    GnsChannel.cpp\n    GnsPortTrackerChannel.cpp\n    GuestDeviceManager.cpp\n    HandleConsoleProgressBar.cpp\n    hcs.cpp\n    helpers.cpp\n    interop.cpp\n    ExecutionContext.cpp\n    socket.cpp\n    hvsocket.cpp\n    Localization.cpp\n    lxssbusclient.cpp\n    lxssclient.cpp\n    LxssMessagePort.cpp\n    LxssSecurity.cpp\n    LxssServerPort.cpp\n    NatNetworking.cpp\n    Redirector.cpp\n    registry.cpp\n    relay.cpp\n    RingBuffer.cpp\n    string.cpp\n    SubProcess.cpp\n    svccomm.cpp\n    WslClient.cpp\n    WslCoreConfig.cpp\n    WslCoreFilesystem.cpp\n    WslCoreFirewallSupport.cpp\n    WslCoreHostDnsInfo.cpp\n    WslCoreNetworkEndpointSettings.cpp\n    WslCoreNetworkingSupport.cpp\n    WslInstall.cpp\n    WslSecurity.cpp\n    WslTelemetry.cpp\n    VirtioNetworking.cpp\n    wslutil.cpp\n    install.cpp\n    notifications.cpp)\n\nset(HEADERS\n    ../../../generated/Localization.h\n    ../../shared/inc/CommandLine.h\n    ../../shared/inc/defs.h\n    ../../shared/inc/lxfsshares.h\n    ../../shared/inc/lxinitshared.h\n    ../../shared/inc/SocketChannel.h\n    ../../shared/inc/socketshared.h\n    ../../shared/inc/hns_schema.h\n    ../../shared/inc/JsonUtils.h\n    ../../shared/inc/stringshared.h\n    ../../shared/inc/retryshared.h\n    ../../shared/inc/message.h\n    ../../shared/inc/prettyprintshared.h\n    ../inc/WslPluginApi.h\n    ../inc/wslpolicies.h\n    ../inc/lxssbusclient.h\n    ../inc/lxssclient.h\n    ../inc/LxssDynamicFunction.h\n    ../inc/traceloggingconfig.h\n    ../inc/wdk.h\n    ../inc/wsl.h\n    ../inc/wslconfig.h\n    ../inc/wslhost.h\n    ../inc/wslrelay.h\n    ConsoleProgressBar.h\n    ConsoleProgressIndicator.h\n    ConsoleState.h\n    DeviceHostProxy.h\n    disk.hpp\n    Distribution.h\n    Dmesg.h\n    DnsResolver.h\n    DnsTunnelingChannel.h\n    filesystem.hpp\n    GnsChannel.h\n    GnsPortTrackerChannel.h\n    GuestDeviceManager.h\n    HandleConsoleProgressBar.h\n    hcs.hpp\n    hcs_schema.h\n    helpers.hpp\n    interop.hpp\n    ExecutionContext.h\n    socket.hpp\n    hvsocket.hpp\n    INetworkingEngine.h\n    LxssMessagePort.h\n    LxssPort.h\n    LxssSecurity.h\n    LxssServerPort.h\n    NatNetworking.h\n    precomp.h\n    Redirector.h\n    registry.hpp\n    relay.hpp\n    RingBuffer.h\n    string.hpp\n    Stringify.h\n    SubProcess.h\n    svccomm.hpp\n    WslClient.h\n    WslCoreConfig.h\n    WslCoreFilesystem.h\n    WslCoreFirewallSupport.h\n    WslCoreHostDnsInfo.h\n    WslCoreMessageQueue.h\n    WslCoreNetworkEndpointSettings.h\n    WslCoreNetworkingSupport.h\n    WslInstall.h\n    WslSecurity.h\n    WslTelemetry.h\n    VirtioNetworking.h\n    wslutil.h\n    notifications.h)\n\nadd_library(common STATIC ${SOURCES} ${HEADERS})\nadd_dependencies(common wslserviceidl localization wslservicemc wslinstalleridl)\n\ntarget_precompile_headers(common PRIVATE precomp.h)\nset_target_properties(common PROPERTIES FOLDER windows)\ntarget_include_directories(common PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/../service/mc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE})\n"
  },
  {
    "path": "src/windows/common/ConsoleProgressBar.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ConsoleProgressBar.cpp\n\nAbstract:\n\n    This file contains the ConsoleProgressBar implementation\n\n--*/\n\n#include \"precomp.h\"\n#include \"ConsoleProgressBar.h\"\n\nconstexpr size_t c_progressBarWidth = 58;\nconstexpr size_t c_lineWidth = c_progressBarWidth + 2;\nconstexpr size_t c_progressBarBufferSize = c_lineWidth + 1;\nconstexpr LPCWSTR c_progressBarFormatString = L\"[%58s]\";\nconstexpr LPCWSTR c_clearFormatString = L\"%60s\";\n\nwsl::windows::common::ConsoleProgressBar::ConsoleProgressBar()\n{\n    m_outputHandle = GetStdHandle(STD_ERROR_HANDLE);\n    m_isOutputConsole = HandleIsConsole(m_outputHandle);\n    m_progressString = wil::make_hlocal_string_nothrow(nullptr, c_lineWidth);\n    THROW_IF_NULL_ALLOC(m_progressString);\n}\n\n// Print\n//\n// formats and prints the progress bar to the console with the given progress indicated\nHRESULT\nwsl::windows::common::ConsoleProgressBar::Print(_In_ uint64_t progress, _In_ uint64_t total)\n{\n    if (!m_isOutputConsole)\n    {\n        return S_FALSE;\n    }\n\n    total = std::max<uint64_t>(total, 1);\n    progress = std::min<uint64_t>(progress, total);\n    if ((progress == m_previousProgress) && (total == m_previousTotal))\n    {\n        return S_OK;\n    }\n\n    const float percent = progress / static_cast<float>(total);\n    const size_t filledBarCount = static_cast<size_t>(c_progressBarWidth * percent);\n    RETURN_IF_FAILED(StringCchPrintfW(m_progressString.get(), c_progressBarBufferSize, c_progressBarFormatString, L\"\"));\n\n    wil::unique_hlocal_string percentString;\n    RETURN_IF_FAILED(wil::str_printf_nothrow(percentString, L\"%2.1f%%\", percent * 100.0f));\n\n    RETURN_HR_IF(E_INVALIDARG, (wcslen(percentString.get()) > wcslen(m_progressString.get())));\n\n    // Write the 'filled' progress symbol '=' into the bar\n    for (size_t i = 0; i < filledBarCount; i++)\n    {\n        m_progressString.get()[i + 1] = L'=';\n    }\n\n    // Insert the percentage-formatted string into the middle of the progress bar\n    size_t percentOffset = (c_lineWidth - wcslen(percentString.get())) / 2;\n    for (LPWSTR currentPercentCharacter = percentString.get(); L'\\0' != *currentPercentCharacter; currentPercentCharacter++)\n    {\n        m_progressString.get()[percentOffset++] = *currentPercentCharacter;\n    }\n\n    RETURN_IF_FAILED(PrintAndResetPosition(m_progressString.get()));\n\n    m_previousProgress = progress;\n    m_previousTotal = total;\n    return S_OK;\n}\n\n// Clear\n//\n// removes the progress bar from the console\nHRESULT\nwsl::windows::common::ConsoleProgressBar::Clear()\n{\n    if (!m_isOutputConsole)\n    {\n        return S_FALSE;\n    }\n\n    RETURN_IF_FAILED(StringCchPrintfW(m_progressString.get(), c_progressBarBufferSize, c_clearFormatString, L\"\"));\n    return PrintAndResetPosition(m_progressString.get());\n}\n\n// PrintAndResetPosition\n//\n// This function writes a given wide character string to the given output handle\n// and moves the cursor back to the start position\nHRESULT\nwsl::windows::common::ConsoleProgressBar::PrintAndResetPosition(_In_ LPCWSTR string) const\n{\n    RETURN_HR_IF_NULL(E_INVALIDARG, string);\n\n    CONSOLE_SCREEN_BUFFER_INFO consoleBufferInfo;\n    RETURN_LAST_ERROR_IF(!GetConsoleScreenBufferInfo(m_outputHandle, &consoleBufferInfo));\n\n    DWORD stringLength = static_cast<DWORD>(wcslen(string));\n    RETURN_LAST_ERROR_IF(!WriteConsoleW(m_outputHandle, string, stringLength, &stringLength, nullptr));\n\n    RETURN_LAST_ERROR_IF(!SetConsoleCursorPosition(m_outputHandle, consoleBufferInfo.dwCursorPosition));\n    return S_OK;\n}\n\n// HandleIsConsole\n//\n// Determines if the given handle is a console\nbool wsl::windows::common::ConsoleProgressBar::HandleIsConsole(_In_ HANDLE handle)\n{\n    DWORD mode;\n    return (GetFileType(handle) == FILE_TYPE_CHAR) && GetConsoleMode(handle, &mode);\n}\n"
  },
  {
    "path": "src/windows/common/ConsoleProgressBar.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ConsoleProgressBar.h\n\nAbstract:\n\n    This file contains the ConsoleProgressBar definition\n\n--*/\n\n#pragma once\n#include \"windows.h\"\n\nnamespace wsl::windows::common {\nclass ConsoleProgressBar\n{\npublic:\n    ConsoleProgressBar();\n\n    HRESULT\n    Print(_In_ uint64_t progress, _In_ uint64_t total);\n\n    HRESULT\n    Clear();\n\nprivate:\n    HRESULT\n    PrintAndResetPosition(_In_ LPCWSTR string) const;\n\n    static bool HandleIsConsole(_In_ HANDLE handle);\n\n    bool m_isOutputConsole;\n    HANDLE m_outputHandle;\n    wil::unique_hlocal_string m_progressString;\n    uint64_t m_previousProgress = 0;\n    uint64_t m_previousTotal = 0;\n};\n} // namespace wsl::windows::common\n"
  },
  {
    "path": "src/windows/common/ConsoleProgressIndicator.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ConsoleProgressIndicator.cpp\n\nAbstract:\n\n    This file contains the ConsoleProgressIndicator implementation\n\n--*/\n\n#include \"precomp.h\"\n#include \"ConsoleProgressIndicator.h\"\n#include \"wslutil.h\"\n\nwsl::windows::common::ConsoleProgressIndicator::ConsoleProgressIndicator(std::wstring&& inputMessage, bool animatedDots)\n{\n    m_waitMessage = std::move(inputMessage);\n\n    // Start the thread to update progress bar only if it's a tty\n    if (_isatty(_fileno(stderr)))\n    {\n        m_interactive = true;\n\n        if (animatedDots)\n        {\n            m_event.create(wil::EventOptions::ManualReset);\n            m_thread = std::thread([&] { IndicateProgress(); });\n        }\n        else\n        {\n            fwprintf(stderr, L\"%ls\", m_waitMessage.c_str());\n        }\n    }\n}\n\nwsl::windows::common::ConsoleProgressIndicator::~ConsoleProgressIndicator()\n{\n    End();\n}\n\nvoid wsl::windows::common::ConsoleProgressIndicator::UpdateProgress(std::wstring&& Progress)\n{\n    if (!m_interactive)\n    {\n        return;\n    }\n\n    // Only update if the new progress message differs from the previous one\n    if (Progress == m_progressMessage)\n    {\n        return;\n    }\n\n    for (auto i = 0; i < m_progressMessage.size(); i++)\n    {\n        fwprintf(stderr, L\"\\b \\b\");\n    }\n\n    fwprintf(stderr, L\"%ls\", Progress.c_str());\n    m_progressMessage = std::move(Progress);\n}\n\nvoid wsl::windows::common::ConsoleProgressIndicator::IndicateProgress() const\n{\n    fwprintf(stderr, L\"%ls\", m_waitMessage.c_str());\n\n    // Print status until the exit event is signaled.\n    int currentDots = 0;\n    constexpr int c_maxDots = 3;\n    while (!m_event.wait(500))\n    {\n        if (currentDots < c_maxDots)\n        {\n            fwprintf(stderr, L\".\");\n            currentDots++;\n        }\n        else\n        {\n            for (int i = 0; i < c_maxDots; i++)\n            {\n                fwprintf(stderr, L\"\\b \\b\");\n            }\n\n            currentDots = 0;\n        }\n    }\n\n    // Clear any dots that remain.\n    for (int i = 0; i < currentDots; i++)\n    {\n        fwprintf(stderr, L\"\\b \\b\");\n    }\n}\n\nvoid wsl::windows::common::ConsoleProgressIndicator::End()\n{\n    // If the thread started, trigger exit event and join thread.\n    if (m_thread.joinable())\n    {\n        m_event.SetEvent();\n        m_thread.join();\n    }\n\n    if (m_interactive)\n    {\n        fwprintf(stderr, L\"\\n\");\n    }\n}\n"
  },
  {
    "path": "src/windows/common/ConsoleProgressIndicator.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ConsoleProgressIndicator.h\n\nAbstract:\n\n    This file contains console function declarations.\n\n--*/\n\n#pragma once\n\n#include <thread>\n#include \"wrl.h\"\n\nnamespace wsl::windows::common {\nclass ConsoleProgressIndicator\n{\nprivate:\n    std::thread m_thread;\n    wil::unique_event m_event;\n    std::wstring m_waitMessage;\n    std::wstring m_progressMessage;\n    bool m_interactive{};\n\n    void IndicateProgress() const;\n\npublic:\n    ConsoleProgressIndicator(std::wstring&& inputMessage, bool animatedDots = false);\n    ~ConsoleProgressIndicator();\n\n    void End();\n\n    void UpdateProgress(std::wstring&& progress);\n};\n} // namespace wsl::windows::common\n"
  },
  {
    "path": "src/windows/common/ConsoleState.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ConsoleState.cpp\n\nAbstract:\n\n    This file contains function definitions for the ConsoleState helper class.\n\n--*/\n\n#include \"precomp.h\"\n#include \"svccomm.hpp\"\n#include \"ConsoleState.h\"\n#pragma hdrstop\n\nnamespace {\n\nvoid ChangeConsoleMode(_In_ HANDLE Handle, _In_ DWORD Mode)\n{\n    // Use the invalid parameter error code to detect the v1 console that does not support the provided mode.\n    // This can be improved in the future when a more elegant solution exists.\n    //\n    // N.B. Ignore failures setting the mode if the console has already disconnected.\n    if (!SetConsoleMode(Handle, Mode))\n    {\n        // DISABLE_NEWLINE_AUTO_RETURN is not supported everywhere, if the flag was present fall back and try again.\n        if (WI_IsFlagSet(Mode, DISABLE_NEWLINE_AUTO_RETURN))\n        {\n            Mode = WI_ClearFlag(Mode, DISABLE_NEWLINE_AUTO_RETURN);\n            if (SetConsoleMode(Handle, Mode))\n            {\n                return;\n            }\n        }\n\n        switch (GetLastError())\n        {\n        case ERROR_PIPE_NOT_CONNECTED:\n            break;\n\n        case ERROR_INVALID_PARAMETER:\n            THROW_HR_MSG(WSL_E_CONSOLE, \"SetConsoleMode(0x%x) failed\", Mode);\n\n        default:\n            THROW_LAST_ERROR_MSG(\"SetConsoleMode(0x%x) failed\", Mode);\n        }\n    }\n}\n\nvoid TrySetConsoleMode(_In_ HANDLE Handle, _In_ DWORD Mode)\ntry\n{\n    ChangeConsoleMode(Handle, Mode);\n}\nCATCH_LOG()\n\n} // namespace\n\nnamespace wsl::windows::common {\n\nConsoleState::ConsoleState()\n{\n    // Ensure console state is restored if the constructor throws.\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { RestoreConsoleState(); });\n\n    m_InputHandle.reset(\n        CreateFileW(L\"CONIN$\", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));\n\n    if (m_InputHandle)\n    {\n        m_SavedInputCodePage = GetConsoleCP();\n        LOG_IF_WIN32_BOOL_FALSE(SetConsoleCP(CP_UTF8));\n\n        // Configure for raw input with VT support.\n        DWORD mode;\n        THROW_LAST_ERROR_IF(!GetConsoleMode(m_InputHandle.get(), &mode));\n\n        DWORD NewMode = mode;\n        WI_SetAllFlags(NewMode, ENABLE_WINDOW_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT);\n        WI_ClearAllFlags(NewMode, ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);\n        ChangeConsoleMode(m_InputHandle.get(), NewMode);\n        m_SavedInputMode = mode;\n    }\n    else\n    {\n        LOG_LAST_ERROR_MSG(\"CreateFileW(CONIN$) failed\");\n    }\n\n    m_OutputHandle.reset(\n        CreateFileW(L\"CONOUT$\", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));\n\n    if (m_OutputHandle)\n    {\n        m_SavedOutputCodePage = GetConsoleOutputCP();\n        LOG_IF_WIN32_BOOL_FALSE(SetConsoleOutputCP(CP_UTF8));\n\n        // Configure for VT output.\n        DWORD mode;\n        THROW_LAST_ERROR_IF(!GetConsoleMode(m_OutputHandle.get(), &mode));\n\n        DWORD NewMode = mode;\n        WI_SetAllFlags(NewMode, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN);\n        ChangeConsoleMode(m_OutputHandle.get(), NewMode);\n        m_SavedOutputMode = mode;\n    }\n    else\n    {\n        LOG_LAST_ERROR_MSG(\"CreateFileW(CONOUT$) failed\");\n    }\n\n    cleanup.release();\n}\n\nConsoleState::~ConsoleState()\n{\n    RestoreConsoleState();\n}\n\nvoid ConsoleState::RestoreConsoleState()\n{\n    if (m_InputHandle)\n    {\n        if (m_SavedInputCodePage.has_value())\n        {\n            LOG_IF_WIN32_BOOL_FALSE(SetConsoleCP(m_SavedInputCodePage.value()));\n        }\n\n        if (m_SavedInputMode.has_value())\n        {\n            TrySetConsoleMode(m_InputHandle.get(), m_SavedInputMode.value());\n        }\n    }\n\n    if (m_OutputHandle)\n    {\n        if (m_SavedOutputCodePage.has_value())\n        {\n            LOG_IF_WIN32_BOOL_FALSE(SetConsoleOutputCP(m_SavedOutputCodePage.value()));\n        }\n\n        if (m_SavedOutputMode.has_value())\n        {\n            TrySetConsoleMode(m_OutputHandle.get(), m_SavedOutputMode.value());\n        }\n    }\n}\n\nCOORD ConsoleState::GetWindowSize() const\n{\n    if (m_OutputHandle)\n    {\n        CONSOLE_SCREEN_BUFFER_INFOEX Info{};\n        Info.cbSize = sizeof(Info);\n        THROW_IF_WIN32_BOOL_FALSE(GetConsoleScreenBufferInfoEx(m_OutputHandle.get(), &Info));\n        return {\n            static_cast<short>(Info.srWindow.Right - Info.srWindow.Left + 1),\n            static_cast<short>(Info.srWindow.Bottom - Info.srWindow.Top + 1)};\n    }\n\n    LOG_HR_MSG(E_UNEXPECTED, \"No console handle available for GetWindowSize\");\n    return {80, 24};\n}\n\n} // namespace wsl::windows::common\n"
  },
  {
    "path": "src/windows/common/ConsoleState.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ConsoleState.h\n\nAbstract:\n\n    This file contains function declarations for the ConsoleState helper class.\n\n--*/\n\n#pragma once\n\n#include <wil/filesystem.h>\n#include <wil/result.h>\n#include \"wslservice.h\"\n\nnamespace wsl::windows::common {\n\n// RAII wrapper for console state configuration and restoration\nclass ConsoleState\n{\npublic:\n    ConsoleState();\n    ~ConsoleState();\n    ConsoleState(const ConsoleState&) = delete;\n    ConsoleState& operator=(const ConsoleState&) = delete;\n    ConsoleState(ConsoleState&&) = delete;\n    ConsoleState& operator=(ConsoleState&&) = delete;\n\n    COORD GetWindowSize() const;\n\nprivate:\n    void RestoreConsoleState();\n\n    wil::unique_hfile m_InputHandle;\n    wil::unique_hfile m_OutputHandle;\n    std::optional<DWORD> m_SavedInputMode{};\n    std::optional<UINT> m_SavedInputCodePage{};\n    std::optional<DWORD> m_SavedOutputMode{};\n    std::optional<UINT> m_SavedOutputCodePage{};\n};\n} // namespace wsl::windows::common\n"
  },
  {
    "path": "src/windows/common/DeviceHostProxy.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include \"precomp.h\"\r\n#include \"DeviceHostProxy.h\"\r\n\r\n// This template works around a limitation with decltype on overloaded functions. It will be able\r\n// to get the correct version of GetVmWorkerProcess based on the provided type arguments. By\r\n// doing it this way, a compiler error will be generated if someone changes the signature of\r\n// GetVmWorkerProcess.\r\n//\r\n// The way this works: decltype(GetVmWorkerProcess) does not work because it's overloaded.\r\n// decltype(GetVmWorkerProcess(arg1, ...)) works to select an overload if you have values of the\r\n// correct type (std::declval<T>() generates a value of the specified type), however the result\r\n// of that is the function's return type, not the function's type, so the argument types must\r\n// be repeated to reconstruct the function type.\r\ntemplate <typename... Args>\r\nusing GetVmWorkerProcessType = decltype(GetVmWorkerProcess(std::declval<Args>()...))(Args...);\r\n\r\n// Limit the number of allowed doorbells registered by an external HDV vdev. Currently virtio-9p only uses\r\n// one doorbell and wsldevicehost uses only two.\r\n#define DEVICE_HOST_PROXY_DOORBELL_LIMIT 8\r\n\r\nusing namespace wsl::windows::common::hcs;\r\n\r\nDeviceHostProxy::DeviceHostProxy(const std::wstring& VmId, const GUID& RuntimeId) :\r\n    m_systemId{VmId}, m_runtimeId{RuntimeId}, m_system{wsl::windows::common::hcs::OpenComputeSystem(VmId.c_str(), GENERIC_ALL)}, m_shutdown{false}\r\n{\r\n    m_devicesShutdown = false;\r\n}\r\n\r\nGUID DeviceHostProxy::AddNewDevice(const GUID& Type, const wil::com_ptr<IPlan9FileSystem>& Plan9Fs, const std::wstring& VirtIoTag)\r\n{\r\n    const wrl::ComPtr<IUnknown> thisUnknown{CastToUnknown()};\r\n    GUID instanceId{};\r\n    THROW_IF_FAILED(UuidCreate(&instanceId));\r\n    // Tell the device host to create the device.\r\n    THROW_IF_FAILED(Plan9Fs->CreateVirtioDevice(m_systemId.c_str(), thisUnknown.Get(), VirtIoTag.c_str(), &instanceId));\r\n\r\n    // Add the instance ID to the list of known devices. This must be done before the device is\r\n    // added to the system, because doing that can cause the register doorbell function to be\r\n    // called.\r\n    // N.B. It will be removed if there is a failure.\r\n    {\r\n        auto lock = m_devicesLock.lock_exclusive();\r\n        THROW_HR_IF(E_CHANGED_STATE, m_devicesShutdown);\r\n\r\n        m_devices.emplace(instanceId, DeviceHostProxyEntry{});\r\n    }\r\n\r\n    auto removeOnFailure = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n        auto lock = m_devicesLock.lock_exclusive();\r\n        m_devices.erase(instanceId);\r\n    });\r\n\r\n    // Add the device to the compute system on behalf of the device host.\r\n    ModifySettingRequest<FlexibleIoDevice> request;\r\n    request.RequestType = ModifyRequestType::Add;\r\n    request.ResourcePath = L\"VirtualMachine/Devices/FlexibleIov/\";\r\n    request.ResourcePath += wsl::shared::string::GuidToString<wchar_t>(instanceId, wsl::shared::string::GuidToStringFlags::None);\r\n    request.Settings.EmulatorId = Type;\r\n    request.Settings.HostingModel = FlexibleIoDeviceHostingModel::ExternalRestricted;\r\n    wsl::windows::common::hcs::ModifyComputeSystem(m_system.get(), wsl::shared::ToJsonW(request).c_str());\r\n    removeOnFailure.release();\r\n    return instanceId;\r\n}\r\n\r\nvoid DeviceHostProxy::AddRemoteFileSystem(const GUID& ImplementationClsid, const std::wstring& Tag, const wil::com_ptr<IPlan9FileSystem>& Plan9Fs)\r\n{\r\n    auto lock = m_lock.lock_exclusive();\r\n    THROW_HR_IF(E_CHANGED_STATE, m_shutdown);\r\n\r\n    // Make sure there are no duplicate tags.\r\n    for (auto& entry : m_fileSystems)\r\n    {\r\n        THROW_HR_IF(E_INVALIDARG, entry.ImplementationClsid == ImplementationClsid && entry.Tag == Tag);\r\n    }\r\n\r\n    m_fileSystems.emplace_back(ImplementationClsid, Tag, Plan9Fs);\r\n}\r\n\r\nwil::com_ptr<IPlan9FileSystem> DeviceHostProxy::GetRemoteFileSystem(const GUID& ImplementationClsid, std::wstring_view Tag)\r\n{\r\n    auto lock = m_lock.lock_shared();\r\n    THROW_HR_IF(E_CHANGED_STATE, m_shutdown);\r\n\r\n    for (auto& entry : m_fileSystems)\r\n    {\r\n        if (entry.ImplementationClsid == ImplementationClsid && entry.Tag == Tag)\r\n        {\r\n            return entry.Instance;\r\n        }\r\n    }\r\n\r\n    return {};\r\n}\r\n\r\nvoid DeviceHostProxy::Shutdown()\r\n{\r\n    {\r\n        auto lock = m_lock.lock_exclusive();\r\n        m_fileSystems.clear();\r\n        m_shutdown = true;\r\n    }\r\n\r\n    {\r\n        auto lock = m_devicesLock.lock_exclusive();\r\n        m_devices.clear();\r\n        m_devicesShutdown = true;\r\n    }\r\n}\r\n\r\nHRESULT\r\nDeviceHostProxy::RegisterDeviceHost(_In_ IVmDeviceHost* DeviceHost, _In_ DWORD ProcessId, _Out_ UINT64* IpcSectionHandle)\r\ntry\r\n{\r\n    //\r\n    // Because HdvProxyDeviceHost is not part of the API set, it is loaded here dynamically.\r\n    //\r\n\r\n    static LxssDynamicFunction<decltype(HdvProxyDeviceHost)> proxyDeviceHost{c_hdvModuleName, \"HdvProxyDeviceHost\"};\r\n    const wil::com_ptr<IVmDeviceHost> remoteHost = DeviceHost;\r\n    const wil::com_ptr<IUnknown> unknown = remoteHost.query<IUnknown>();\r\n    THROW_IF_FAILED(proxyDeviceHost(m_system.get(), unknown.get(), ProcessId, IpcSectionHandle));\r\n    return S_OK;\r\n}\r\nCATCH_RETURN()\r\n\r\nHRESULT\r\nDeviceHostProxy::NotifyAllDevicesInUse(_In_ LPCWSTR Tag)\r\ntry\r\n{\r\n    //\r\n    // Add another Plan9 virtio device to the guest so additional mount commands will be possible.\r\n    // This callback should be unused by virtiofs devices because a device is created for every\r\n    // AddSharePath call.\r\n    //\r\n    auto p9fs = GetRemoteFileSystem(__uuidof(p9fs::Plan9FileSystem), Tag);\r\n    THROW_HR_IF(E_NOT_SET, !p9fs);\r\n    (void)AddNewDevice(VIRTIO_PLAN9_DEVICE_ID, p9fs, Tag);\r\n    return S_OK;\r\n}\r\nCATCH_RETURN()\r\n\r\nHRESULT\r\nDeviceHostProxy::RegisterDoorbell(const GUID& InstanceId, UINT8 BarIndex, UINT64 Offset, UINT64 TriggerValue, UINT64 Flags, HANDLE Event)\r\ntry\r\n{\r\n    auto lock = m_devicesLock.lock_exclusive();\r\n    RETURN_HR_IF(E_CHANGED_STATE, m_devicesShutdown);\r\n\r\n    // Check if the device is one of the known devices that doorbells can be registered for, and\r\n    // if the device has not already registered a doorbell.\r\n    // N.B. For security it is enforced that each device can only register a small number of doorbells.\r\n    //      Currently virtio-9p only uses one and the external virtio device uses two.\r\n    const auto knownDevice = m_devices.find(InstanceId);\r\n    RETURN_HR_IF(E_ACCESSDENIED, knownDevice == m_devices.end() || knownDevice->second.DoorbellCount == DEVICE_HOST_PROXY_DOORBELL_LIMIT);\r\n\r\n    if (!knownDevice->second.MemoryNotification)\r\n    {\r\n        // Get an interface to the worker process to query devices.\r\n        if (!m_deviceAccess)\r\n        {\r\n            static LxssDynamicFunction<GetVmWorkerProcessType<REFGUID, REFIID, IUnknown**>> getVmWorker{\r\n                c_vmwpctrlModuleName, \"GetVmWorkerProcess\"};\r\n\r\n            RETURN_IF_FAILED(getVmWorker(m_runtimeId, __uuidof(*m_deviceAccess), reinterpret_cast<IUnknown**>(&m_deviceAccess)));\r\n        }\r\n\r\n        RETURN_HR_IF(E_NOINTERFACE, !m_deviceAccess);\r\n\r\n        // Retrieve the device's memory notification interface to register the doorbell, and store it\r\n        // to be used during unregistration.\r\n        wil::com_ptr<IUnknown> device;\r\n        RETURN_IF_FAILED(m_deviceAccess->GetDevice(FLEXIO_DEVICE_ID, InstanceId, &device));\r\n        knownDevice->second.MemoryNotification = device.query<IVmFiovGuestMemoryFastNotification>();\r\n    }\r\n\r\n    const auto result = knownDevice->second.MemoryNotification->RegisterDoorbell(\r\n        static_cast<FIOV_BAR_SELECTOR>(BarIndex), Offset, TriggerValue, Flags, Event);\r\n\r\n    if (SUCCEEDED(result))\r\n    {\r\n        ++knownDevice->second.DoorbellCount;\r\n    }\r\n\r\n    return result;\r\n}\r\nCATCH_RETURN()\r\n\r\nHRESULT\r\nDeviceHostProxy::UnregisterDoorbell(const GUID& InstanceId, UINT8 BarIndex, UINT64 Offset, UINT64 TriggerValue, UINT64 Flags)\r\ntry\r\n{\r\n    auto lock = m_devicesLock.lock_exclusive();\r\n    RETURN_HR_IF(E_CHANGED_STATE, m_devicesShutdown);\r\n\r\n    // Check if the device is a known device and has registered a doorbell.\r\n    // N.B. If the device is being removed, the device can't be retrieved from the worker process\r\n    //      so it's necessary to use the stored COM pointer.\r\n    const auto device = m_devices.find(InstanceId);\r\n    RETURN_HR_IF(E_ACCESSDENIED, device == m_devices.end() || device->second.DoorbellCount == 0);\r\n    RETURN_IF_FAILED(device->second.MemoryNotification->UnregisterDoorbell(static_cast<FIOV_BAR_SELECTOR>(BarIndex), Offset, TriggerValue, Flags));\r\n\r\n    if (--device->second.DoorbellCount == 0)\r\n    {\r\n        device->second.MemoryNotification.reset();\r\n    }\r\n\r\n    return S_OK;\r\n}\r\nCATCH_RETURN()\r\n\r\nHRESULT\r\nDeviceHostProxy::CreateSectionBackedMmioRange(\r\n    const GUID& InstanceId, UINT8 BarIndex, UINT64 BarOffsetInPages, UINT64 PageCount, UINT64 MappingFlags, HANDLE SectionHandle, UINT64 SectionOffsetInPages)\r\ntry\r\n{\r\n    auto lock = m_devicesLock.lock_exclusive();\r\n    RETURN_HR_IF(E_CHANGED_STATE, m_devicesShutdown);\r\n\r\n    // Check if the device is one of the known devices.\r\n    const auto knownDevice = m_devices.find(InstanceId);\r\n    THROW_HR_IF(E_ACCESSDENIED, knownDevice == m_devices.end());\r\n\r\n    if (!knownDevice->second.MemoryMapping)\r\n    {\r\n        // Get an interface to the worker process to query devices.\r\n        if (!m_deviceAccess)\r\n        {\r\n            static LxssDynamicFunction<GetVmWorkerProcessType<REFGUID, REFIID, IUnknown**>> getVmWorker{\r\n                c_vmwpctrlModuleName, \"GetVmWorkerProcess\"};\r\n            THROW_IF_FAILED(getVmWorker(m_runtimeId, __uuidof(*m_deviceAccess), reinterpret_cast<IUnknown**>(&m_deviceAccess)));\r\n        }\r\n\r\n        THROW_HR_IF(E_NOINTERFACE, !m_deviceAccess);\r\n\r\n        // Retrieve the device specific interface to manage mapped sections.\r\n        wil::com_ptr<IUnknown> device;\r\n        THROW_IF_FAILED(m_deviceAccess->GetDevice(FLEXIO_DEVICE_ID, InstanceId, &device));\r\n        knownDevice->second.MemoryMapping = device.query<IVmFiovGuestMmioMappings>();\r\n    }\r\n\r\n    THROW_IF_FAILED(knownDevice->second.MemoryMapping->CreateSectionBackedMmioRange(\r\n        static_cast<FIOV_BAR_SELECTOR>(BarIndex), BarOffsetInPages, PageCount, static_cast<FiovMmioMappingFlags>(MappingFlags), SectionHandle, SectionOffsetInPages));\r\n\r\n    return S_OK;\r\n}\r\nCATCH_RETURN()\r\n\r\nHRESULT\r\nDeviceHostProxy::DestroySectionBackedMmioRange(const GUID& InstanceId, UINT8 BarIndex, UINT64 BarOffsetInPages)\r\ntry\r\n{\r\n    auto lock = m_devicesLock.lock_exclusive();\r\n    RETURN_HR_IF(E_CHANGED_STATE, m_devicesShutdown);\r\n    const auto device = m_devices.find(InstanceId);\r\n    RETURN_HR_IF(E_ACCESSDENIED, device == m_devices.end() || !device->second.MemoryMapping);\r\n    RETURN_IF_FAILED(device->second.MemoryMapping->DestroySectionBackedMmioRange(static_cast<FIOV_BAR_SELECTOR>(BarIndex), BarOffsetInPages));\r\n    return S_OK;\r\n}\r\nCATCH_RETURN()"
  },
  {
    "path": "src/windows/common/DeviceHostProxy.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include <windowsdefs.h>\r\n#include \"hcs.hpp\"\r\n\r\nnamespace wrl = Microsoft::WRL;\r\n\r\nclass DeviceHostProxy : public wrl::RuntimeClass<wrl::RuntimeClassFlags<wrl::RuntimeClassType::ClassicCom>, IVmDeviceHostSupport, IPlan9FileSystemHost>\r\n{\r\npublic:\r\n    DeviceHostProxy(const std::wstring& VmId, const GUID& RuntimeId);\r\n\r\n    GUID AddNewDevice(const GUID& Type, const wil::com_ptr<IPlan9FileSystem>& Plan9Fs, const std::wstring& VirtIoTag);\r\n\r\n    void AddRemoteFileSystem(const GUID& ImplementationClsid, const std::wstring& Tag, const wil::com_ptr<IPlan9FileSystem>& Plan9Fs);\r\n\r\n    wil::com_ptr<IPlan9FileSystem> GetRemoteFileSystem(const GUID& ImplementationClsid, std::wstring_view Tag);\r\n\r\n    void Shutdown();\r\n\r\n    //\r\n    // IVmDeviceHostSupport\r\n    //\r\n    IFACEMETHOD(RegisterDeviceHost)(_In_ IVmDeviceHost* DeviceHost, _In_ DWORD ProcessId, _Out_ UINT64* IpcSectionHandle) override;\r\n\r\n    //\r\n    // IPlan9FileSystemHost\r\n    //\r\n    IFACEMETHOD(NotifyAllDevicesInUse)(_In_ LPCWSTR Tag) override;\r\n\r\n    IFACEMETHOD(RegisterDoorbell)(const GUID& InstanceId, UINT8 BarIndex, UINT64 Offset, UINT64 TriggerValue, UINT64 Flags, HANDLE Event) override;\r\n\r\n    IFACEMETHOD(UnregisterDoorbell)(const GUID& InstanceId, UINT8 BarIndex, UINT64 Offset, UINT64 TriggerValue, UINT64 Flags) override;\r\n\r\n    IFACEMETHOD(CreateSectionBackedMmioRange)(\r\n        const GUID& InstanceId, UINT8 BarIndex, UINT64 BarOffsetInPages, UINT64 PageCount, UINT64 MappingFlags, HANDLE SectionHandle, UINT64 SectionOffsetInPages) override;\r\n\r\n    IFACEMETHOD(DestroySectionBackedMmioRange)(const GUID& InstanceId, UINT8 BarIndex, UINT64 BarOffsetInPages) override;\r\n\r\nprivate:\r\n    struct RemoteFileSystemInfo\r\n    {\r\n        RemoteFileSystemInfo(GUID ImplementationClsid, const std::wstring& Tag, const wil::com_ptr<IPlan9FileSystem>& Instance) :\r\n            ImplementationClsid{ImplementationClsid}, Tag{Tag}, Instance{Instance}\r\n        {\r\n        }\r\n\r\n        GUID ImplementationClsid;\r\n        std::wstring Tag;\r\n        wil::com_ptr<IPlan9FileSystem> Instance;\r\n    };\r\n\r\n    std::wstring m_systemId;\r\n    GUID m_runtimeId;\r\n    wsl::windows::common::hcs::unique_hcs_system m_system;\r\n    wil::srwlock m_lock;\r\n    std::vector<RemoteFileSystemInfo> m_fileSystems;\r\n    bool m_shutdown;\r\n\r\n    struct DeviceHostProxyEntry\r\n    {\r\n        wil::com_ptr<IVmFiovGuestMemoryFastNotification> MemoryNotification;\r\n        wil::com_ptr<IVmFiovGuestMmioMappings> MemoryMapping;\r\n        size_t DoorbellCount = 0;\r\n    };\r\n\r\n    wil::com_ptr<IVmVirtualDeviceAccess> m_deviceAccess;\r\n    wil::srwlock m_devicesLock;\r\n    std::map<GUID, DeviceHostProxyEntry, wsl::windows::common::helpers::GuidLess> m_devices;\r\n    bool m_devicesShutdown;\r\n\r\n    static constexpr LPCWSTR c_hdvModuleName = L\"vmdevicehost.dll\";\r\n    static constexpr LPCWSTR c_vmwpctrlModuleName = L\"vmwpctrl.dll\";\r\n};"
  },
  {
    "path": "src/windows/common/Distribution.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Distribution.cpp\r\n\r\nAbstract:\r\n\r\n    This file contains implementations for distribution app download, install and launch.\r\n\r\n--*/\r\n\r\n#include \"precomp.h\"\r\n#include \"Distribution.h\"\r\n#include \"ConsoleProgressBar.h\"\r\n#include \"registry.hpp\"\r\n\r\nconstexpr auto c_defaultDistroListUrl =\r\n    L\"https://raw.githubusercontent.com/microsoft/WSL/master/distributions/DistributionInfo.json\";\r\nconstexpr auto StoreClientId = L\"wsl-install-lifted\";\r\n\r\nusing namespace winrt::Windows::ApplicationModel::Store::Preview::InstallControl;\r\nusing namespace winrt::Windows::System;\r\nusing namespace wsl::windows::common::distribution;\r\nusing wsl::shared::Localization;\r\n\r\nnamespace {\r\nstd::wstring GetFamilyNameFromStorePackage(const winrt::Windows::Services::Store::StoreProduct& Package)\r\n{\r\n    const auto extendedJson = Package.ExtendedJsonData();\r\n\r\n    const auto json = nlohmann::json::parse(wsl::shared::string::WideToMultiByte(extendedJson.c_str()));\r\n    THROW_HR_IF_MSG(E_UNEXPECTED, !json.contains(\"Properties\"), \"Failed to deserialize store json : '%ls'\", extendedJson.c_str());\r\n\r\n    const auto properties = json.at(\"Properties\");\r\n    THROW_HR_IF_MSG(\r\n        E_UNEXPECTED,\r\n        !properties.is_object() || !properties.contains(\"PackageFamilyName\"),\r\n        \"Failed to deserialize store json : '%ls'\",\r\n        extendedJson.c_str());\r\n\r\n    return properties.at(\"PackageFamilyName\").get<std::wstring>();\r\n}\r\n\r\nwinrt::Windows::Services::Store::StoreProduct GetStorePackage(LPCWSTR AppId)\r\n{\r\n    const auto storeContext = winrt::Windows::Services::Store::StoreContext::GetDefault();\r\n\r\n    const auto productKinds = winrt::single_threaded_vector<winrt::hstring>({L\"Application\"});\r\n    const auto productIds = winrt::single_threaded_vector<winrt::hstring>({AppId});\r\n    const auto packages = storeContext.GetStoreProductsAsync(productKinds, productIds).get().Products();\r\n    THROW_HR_IF_MSG(\r\n        E_UNEXPECTED,\r\n        packages.Size() != 1,\r\n        \"Unexpected store package count AppId=%ls, Count=%zu\",\r\n        AppId,\r\n        static_cast<size_t>(packages.Size()));\r\n\r\n    return packages.First().Current().Value();\r\n}\r\n\r\nstd::optional<winrt::Windows::ApplicationModel::Package> GetInstalledPackage(LPCWSTR PackageFamilyName)\r\n{\r\n    const winrt::Windows::Management::Deployment::PackageManager packageManager;\r\n    const auto familyCollection = packageManager.FindPackagesForUser(L\"\", PackageFamilyName);\r\n    const auto iter = familyCollection.First();\r\n    if (!iter.HasCurrent())\r\n    {\r\n        return {};\r\n    }\r\n\r\n    auto package = iter.Current();\r\n    LOG_HR_IF_MSG(E_UNEXPECTED, iter.MoveNext(), \"More than one package found for packageFamily=%ls\", PackageFamilyName);\r\n\r\n    return package;\r\n}\r\n\r\nstd::wstring GetFamilyName(const Distribution& distro, bool directDownload)\r\n{\r\n    if (directDownload)\r\n    {\r\n        THROW_HR_IF(E_UNEXPECTED, !distro.PackageFamilyName.has_value());\r\n        return *distro.PackageFamilyName;\r\n    }\r\n\r\n    return GetFamilyNameFromStorePackage(GetStorePackage(distro.StoreAppId.c_str()));\r\n}\r\n\r\nDistributionList ReadFromManifest(const std::wstring& url)\r\n{\r\n    using namespace wsl::windows::common::distribution;\r\n\r\n    try\r\n    {\r\n        std::wstring content;\r\n        if (const auto localFile = wsl::windows::common::filesystem::TryGetPathFromFileUrl(url))\r\n        {\r\n            content = wsl::shared::string::ReadFile<wchar_t, wchar_t>(localFile->c_str());\r\n        }\r\n        else\r\n        {\r\n            const winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter filter;\r\n            filter.CacheControl().WriteBehavior(winrt::Windows::Web::Http::Filters::HttpCacheWriteBehavior::NoCache);\r\n            filter.CacheControl().ReadBehavior(winrt::Windows::Web::Http::Filters::HttpCacheReadBehavior::NoCache);\r\n\r\n            const winrt::Windows::Web::Http::HttpClient client(filter);\r\n            const auto response = client.GetAsync(winrt::Windows::Foundation::Uri(url)).get();\r\n            response.EnsureSuccessStatusCode();\r\n\r\n            content = response.Content().ReadAsStringAsync().get();\r\n        }\r\n\r\n        auto distros = wsl::shared::FromJson<DistributionList, nlohmann::ordered_json>(content.c_str());\r\n\r\n        if (distros.Distributions.has_value())\r\n        {\r\n            std::erase_if(*distros.Distributions, [](const auto& e) {\r\n                if constexpr (wsl::shared::Arm64)\r\n                {\r\n                    return !e.Arm64;\r\n                }\r\n                else\r\n                {\r\n                    return !e.Amd64;\r\n                }\r\n            });\r\n        }\r\n\r\n        if (distros.ModernDistributions.has_value())\r\n        {\r\n            for (auto& [_, versions] : *distros.ModernDistributions)\r\n            {\r\n                std::erase_if(versions, [](const auto& e) {\r\n                    if constexpr (wsl::shared::Arm64)\r\n                    {\r\n                        return !e.Arm64Url.has_value();\r\n                    }\r\n                    else\r\n                    {\r\n                        return !e.Amd64Url.has_value();\r\n                    }\r\n                });\r\n            }\r\n        }\r\n\r\n        // The \"Default\" string takes precedence. If not present, use the first legacy distro entry.\r\n        if (!distros.Default.has_value() && distros.Distributions.has_value() && distros.Distributions->size() > 0)\r\n        {\r\n            distros.Default = (*distros.Distributions)[0].Name;\r\n        }\r\n\r\n        return distros;\r\n    }\r\n    catch (...)\r\n    {\r\n        const auto hr = wil::ResultFromCaughtException();\r\n        THROW_HR_WITH_USER_ERROR(\r\n            hr, wsl::shared::Localization::MessageCouldFetchDistributionList(url.c_str(), wsl::windows::common::wslutil::GetSystemErrorString(hr)));\r\n    }\r\n}\r\n\r\nstd::optional<TDistribution> LookupDistributionInManifest(const DistributionList& manifest, LPCWSTR name, bool legacy)\r\n{\r\n    // First check if the name matches a distribution, or a distribution version in the modern entries\r\n\r\n    const auto utf8name = wsl::shared::string::WideToMultiByte(name);\r\n    if (!legacy && manifest.ModernDistributions.has_value())\r\n    {\r\n        for (const auto& [distributionName, versions] : *manifest.ModernDistributions)\r\n        {\r\n            bool useDefault = false;\r\n            if (wsl::shared::string::IsEqual(distributionName, utf8name, true))\r\n            {\r\n                useDefault = true;\r\n            }\r\n\r\n            for (const auto& e : versions)\r\n            {\r\n                if (useDefault && e.Default.value_or(false))\r\n                {\r\n                    return e;\r\n                }\r\n\r\n                if (wsl::shared::string::IsEqual(e.Name, name, true))\r\n                {\r\n                    return e;\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    // If no modern distribution is found, or --legacy is passed, look for a legacy registration\r\n\r\n    if (!manifest.Distributions.has_value())\r\n    {\r\n        return {};\r\n    }\r\n\r\n    const auto it = std::find_if(manifest.Distributions->begin(), manifest.Distributions->end(), [&](const auto e) {\r\n        return wsl::shared::string::IsEqual(e.Name, name, true);\r\n    });\r\n\r\n    if (it == manifest.Distributions->end())\r\n    {\r\n        return {};\r\n    }\r\n\r\n    return *it;\r\n}\r\n\r\n} // namespace\r\n\r\nAvailableDistributions wsl::windows::common::distribution::GetAvailable()\r\n{\r\n    AvailableDistributions distributions{};\r\n\r\n    std::wstring url = c_defaultDistroListUrl;\r\n    std::optional<std::wstring> appendUrl;\r\n    try\r\n    {\r\n        const auto registryKey = registry::OpenLxssMachineKey();\r\n        url = registry::ReadString(registryKey.get(), nullptr, c_distroUrlRegistryValue, c_defaultDistroListUrl);\r\n        if (url != c_defaultDistroListUrl)\r\n        {\r\n            WSL_LOG(\"Found custom URL for distribution list\", TraceLoggingValue(url.c_str(), \"url\"));\r\n        }\r\n\r\n        appendUrl = registry::ReadOptionalString(registryKey.get(), nullptr, c_distroUrlAppendRegistryValue);\r\n    }\r\n    CATCH_LOG()\r\n\r\n    distributions.Manifest = ReadFromManifest(url);\r\n\r\n    if (appendUrl.has_value())\r\n    {\r\n        WSL_LOG(\"Found append URL for distribution list\", TraceLoggingValue(appendUrl->c_str(), \"url\"));\r\n\r\n        distributions.OverrideManifest = ReadFromManifest(appendUrl.value());\r\n    }\r\n\r\n    return distributions;\r\n}\r\n\r\nstd::variant<Distribution, ModernDistributionVersion> wsl::windows::common::distribution::LookupByName(\r\n    const AvailableDistributions& manifest, LPCWSTR name, bool legacy)\r\n{\r\n    if (manifest.OverrideManifest.has_value())\r\n    {\r\n        auto distribution = LookupDistributionInManifest(manifest.OverrideManifest.value(), name, legacy);\r\n        if (distribution.has_value())\r\n        {\r\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageDistributionOverridden(name));\r\n            return distribution.value();\r\n        }\r\n    }\r\n\r\n    auto distribution = LookupDistributionInManifest(manifest.Manifest, name, legacy);\r\n\r\n    if (!distribution.has_value())\r\n    {\r\n        THROW_HR_WITH_USER_ERROR(WSL_E_DISTRO_NOT_FOUND, Localization::MessageInvalidDistributionName(name));\r\n    }\r\n\r\n    return distribution.value();\r\n}\r\n\r\nbool wsl::windows::common::distribution::IsInstalled(const Distribution& distro, bool directDownload)\r\n{\r\n    const auto familyName = GetFamilyName(distro, directDownload);\r\n    return GetInstalledPackage(familyName.c_str()).has_value();\r\n}\r\n\r\nvoid wsl::windows::common::distribution::LegacyInstallViaGithub(const Distribution& distro)\r\n{\r\n    decltype(distro.Amd64PackageUrl) downloadUrl;\r\n\r\n    if constexpr (wsl::shared::Arm64)\r\n    {\r\n        downloadUrl = distro.Arm64PackageUrl;\r\n    }\r\n    else\r\n    {\r\n        downloadUrl = distro.Amd64PackageUrl;\r\n    }\r\n\r\n    THROW_HR_IF(WSL_E_DISTRO_ONLY_AVAILABLE_FROM_STORE, !downloadUrl.has_value());\r\n\r\n    wslutil::PrintMessage(Localization::MessageDownloading(distro.FriendlyName.c_str()), stdout);\r\n\r\n    // Note: The appx extensions is required for the installation to succeed.\r\n    const auto downloadPath = wslutil::DownloadFile(*downloadUrl, distro.Name + L\".appx\");\r\n    auto deleteFile =\r\n        wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { THROW_IF_WIN32_BOOL_FALSE(DeleteFileW(downloadPath.c_str())); });\r\n\r\n    wslutil::PrintMessage(Localization::MessageInstalling(distro.FriendlyName), stdout);\r\n\r\n    const winrt::Windows::Management::Deployment::PackageManager packageManager;\r\n    auto installedPackage = packageManager\r\n                                .AddPackageAsync(\r\n                                    winrt::Windows::Foundation::Uri{downloadPath},\r\n                                    nullptr,\r\n                                    winrt::Windows::Management::Deployment::DeploymentOptions::None,\r\n                                    wsl::windows::common::wslutil::GetSystemVolume())\r\n                                .get();\r\n\r\n    wslutil::PrintMessage(Localization::MessageDownloadComplete(distro.FriendlyName), stdout);\r\n}\r\n\r\nvoid wsl::windows::common::distribution::LegacyInstallViaStore(const Distribution& distro)\r\n{\r\n    const AppInstallOptions options;\r\n    options.CompletedInstallToastNotificationMode(AppInstallationToastNotificationMode::NoToast);\r\n\r\n    const AppInstallManager manager;\r\n    const auto entries =\r\n        manager.StartProductInstallAsync(distro.StoreAppId.c_str(), winrt::hstring{}, StoreClientId, winrt::hstring{}, options).get();\r\n\r\n    // Cancel the app deployment if something goes wrong\r\n    auto cancel = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n        for (const auto& e : entries)\r\n        {\r\n            e.Cancel();\r\n        }\r\n    });\r\n\r\n    wslutil::PrintMessage(Localization::MessageDownloading(distro.FriendlyName), stdout);\r\n\r\n    // Print install progress.\r\n    auto complete = [&]() {\r\n        for (uint32_t i = 0; i < entries.Size(); i++)\r\n        {\r\n            if (entries.GetAt(i).GetCurrentStatus().InstallState() != AppInstallState::Completed)\r\n            {\r\n                return false;\r\n            }\r\n        }\r\n        return true;\r\n    };\r\n\r\n    ConsoleProgressBar progressBar;\r\n    const auto total = std::lround(100 * entries.Size());\r\n    while (!complete())\r\n    {\r\n        double percentComplete = 0;\r\n        for (const auto& e : entries)\r\n        {\r\n            const auto& status = e.GetCurrentStatus();\r\n            THROW_IF_FAILED(static_cast<HRESULT>(status.ErrorCode()));\r\n\r\n            percentComplete += status.PercentComplete();\r\n        }\r\n\r\n        progressBar.Print(static_cast<UINT>(percentComplete), total);\r\n        Sleep(100);\r\n    }\r\n\r\n    progressBar.Clear();\r\n    cancel.release();\r\n\r\n    wslutil::PrintMessage(Localization::MessageDownloadComplete(distro.FriendlyName), stdout);\r\n\r\n    // Sanity check\r\n    THROW_HR_IF(E_UNEXPECTED, !IsInstalled(distro, false));\r\n}\r\n\r\nvoid wsl::windows::common::distribution::Launch(const Distribution& distro, bool directDownload, bool throwOnError)\r\n{\r\n    const std::wstring familyName = GetFamilyName(distro, directDownload);\r\n\r\n    try\r\n    {\r\n        wil::unique_cotaskmem_string appsPath;\r\n        THROW_IF_FAILED(::SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_APPCONTAINER_REDIRECTION, nullptr, &appsPath));\r\n\r\n        const std::filesystem::path appsFolder{appsPath.get()};\r\n\r\n        std::optional<std::filesystem::path> entryPoint;\r\n        for (const auto& e : std::filesystem::directory_iterator(appsFolder / L\"Microsoft\" / \"WindowsApps\" / familyName))\r\n        {\r\n            if (e.path().has_extension() &&\r\n                wsl::windows::common::string::IsPathComponentEqual(e.path().extension().native(), L\".exe\"))\r\n            {\r\n                if (entryPoint.has_value())\r\n                {\r\n                    // Note: Can't use THROW_HR_IF* here because entryPoint.value() should only be called if entryPoint has a value.\r\n                    THROW_HR_MSG(\r\n                        E_UNEXPECTED,\r\n                        \"Found multiple entrypoints for app: %ls (%ls, %ls), falling back to LaunchAsync()\",\r\n                        familyName.c_str(),\r\n                        entryPoint.value().c_str(),\r\n                        e.path().c_str());\r\n                }\r\n\r\n                entryPoint = e.path();\r\n            }\r\n        }\r\n\r\n        THROW_HR_IF_MSG(\r\n            E_UNEXPECTED,\r\n            !entryPoint.has_value(),\r\n            \"No entrypoint found for app: %ls, path: %ls\",\r\n            familyName.c_str(),\r\n            (appsFolder / L\"Microsoft\" / \"WindowsApps\" / familyName).c_str());\r\n\r\n        auto commandLine = entryPoint->wstring();\r\n        const auto exitCode = wsl::windows::common::helpers::RunProcess(commandLine);\r\n        if (throwOnError && exitCode != 0)\r\n        {\r\n            THROW_HR_WITH_USER_ERROR(WSL_E_INSTALL_PROCESS_FAILED, wsl::shared::Localization::MessageInstallProcessFailed(distro.Name, exitCode));\r\n        }\r\n        return;\r\n    }\r\n    catch (...)\r\n    {\r\n        if (wil::ResultFromCaughtException() == WSL_E_INSTALL_PROCESS_FAILED)\r\n        {\r\n            throw;\r\n        }\r\n        else\r\n        {\r\n            LOG_CAUGHT_EXCEPTION();\r\n        }\r\n    }\r\n\r\n    // Fallback to the old launch logic in case something went wrong looking up the app execution alias.\r\n\r\n    const auto package = GetInstalledPackage(familyName.c_str());\r\n    THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !package.has_value());\r\n\r\n    const auto entryPoints = package->GetAppListEntries();\r\n    THROW_HR_IF_MSG(\r\n        E_UNEXPECTED,\r\n        entryPoints.Size() != 1,\r\n        \"Unexpected number of entry points for app: %ls, %i\",\r\n        distro.StoreAppId.c_str(),\r\n        !entryPoints.Size());\r\n\r\n    entryPoints.GetAt(0).LaunchAsync().get();\r\n}\r\n"
  },
  {
    "path": "src/windows/common/Distribution.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Distribution.cpp\n\nAbstract:\n\n    This file contains definitions for distribution app download, install and launch.\n\n--*/\n\n#pragma once\n\n#include <optional>\n#include <vector>\n#include <functional>\n#include <nlohmann/json.hpp>\n#include \"JsonUtils.h\"\n\nnamespace wsl::windows::common::distribution {\n\nstruct DistributionArchive\n{\n    std::wstring Url;\n    std::wstring Sha256;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(DistributionArchive, Url, Sha256);\n};\n\nstruct ModernDistributionVersion\n{\n    std::wstring Name;\n    std::wstring FriendlyName;\n    std::optional<bool> Default;\n    std::optional<DistributionArchive> Amd64Url;\n    std::optional<DistributionArchive> Arm64Url;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ModernDistributionVersion, Name, FriendlyName, Default, Amd64Url, Arm64Url);\n};\n\nstruct Distribution\n{\n    std::wstring Name;\n    std::wstring FriendlyName;\n    std::wstring StoreAppId;\n    bool Amd64;\n    bool Arm64;\n    std::optional<std::wstring> Amd64PackageUrl;\n    std::optional<std::wstring> Arm64PackageUrl;\n    std::optional<std::wstring> PackageFamilyName;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Distribution, Name, FriendlyName, StoreAppId, Amd64, Arm64, Amd64PackageUrl, Arm64PackageUrl, PackageFamilyName);\n};\n\nstruct DistributionList\n{\n    std::optional<std::vector<Distribution>> Distributions;\n    std::optional<nlohmann::ordered_map<std::string, std::vector<ModernDistributionVersion>>> ModernDistributions;\n    std::optional<std::wstring> Default;\n\n    friend void from_json(const nlohmann::ordered_json& nlohmann_json_j, DistributionList& nlohmann_json_t)\n    {\n        const DistributionList nlohmann_json_default_obj{};\n        NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, Distributions, Default));\n\n        auto modernDistributions = nlohmann_json_j.find(\"ModernDistributions\");\n        if (modernDistributions != nlohmann_json_j.end())\n        {\n            nlohmann_json_t.ModernDistributions.emplace();\n\n            for (const auto& e : modernDistributions->items())\n            {\n                std::vector<ModernDistributionVersion> distros;\n                from_json(e.value(), distros);\n\n                nlohmann_json_t.ModernDistributions->emplace_back(e.key(), std::move(distros));\n            }\n        }\n    }\n};\n\nconstexpr inline auto c_distroUrlRegistryValue = L\"DistributionListUrl\";\nconstexpr inline auto c_distroUrlAppendRegistryValue = L\"DistributionListUrlAppend\";\nconstexpr inline auto fileUrlPrefix = L\"file://\";\n\nusing TDistribution = std::variant<Distribution, ModernDistributionVersion>;\n\nstruct AvailableDistributions\n{\n    DistributionList Manifest;\n    std::optional<DistributionList> OverrideManifest;\n};\n\nAvailableDistributions GetAvailable();\nTDistribution LookupByName(const AvailableDistributions& distributions, LPCWSTR name, bool legacy);\n\nbool IsInstalled(const Distribution& distro, bool directDownload);\n\nvoid LegacyInstallViaStore(const Distribution& distro);\n\nvoid LegacyInstallViaGithub(const Distribution& distro);\n\nvoid Launch(const Distribution& distro, bool directDownload, bool throwOnError);\n\n} // namespace wsl::windows::common::distribution\n"
  },
  {
    "path": "src/windows/common/Dmesg.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Dmesg.cpp\n\nAbstract:\n\n    This file contains logic to collect dmesg output.\n\n--*/\n\n#include \"precomp.h\"\n#include \"Dmesg.h\"\n\nDmesgCollector::DmesgCollector(GUID VmId, const wil::unique_event& ExitEvent, bool EnableTelemetry, bool EnableDebugConsole, const std::wstring& Com1PipeName) :\n    m_com1PipeName(Com1PipeName), m_runtimeId(VmId), m_debugConsole(EnableDebugConsole), m_telemetry(EnableTelemetry)\n{\n    m_exitEvent.reset(wsl::windows::common::wslutil::DuplicateHandle(ExitEvent.get()));\n    m_overlappedEvent.create(wil::EventOptions::ManualReset);\n    m_overlapped.hEvent = m_overlappedEvent.get();\n    m_threadExit.create(wil::EventOptions::ManualReset);\n    m_exitEvents = {m_threadExit.get(), m_exitEvent.get()};\n}\n\nDmesgCollector::~DmesgCollector()\n{\n    m_threadExit.SetEvent();\n    if (m_earlyConsoleWorker.joinable())\n    {\n        m_earlyConsoleWorker.join();\n    }\n\n    if (m_virtioWorker.joinable())\n    {\n        m_virtioWorker.join();\n    }\n}\n\nstd::shared_ptr<DmesgCollector> DmesgCollector::Create(\n    GUID VmId, const wil::unique_event& ExitEvent, bool EnableTelemetry, bool EnableDebugConsole, const std::wstring& Com1PipeName, bool EnableEarlyBootConsole)\n{\n    auto dmesgCollector =\n        std::shared_ptr<DmesgCollector>(new DmesgCollector(VmId, ExitEvent, EnableTelemetry, EnableDebugConsole, Com1PipeName));\n\n    if (FAILED(dmesgCollector->Start(EnableEarlyBootConsole)))\n    {\n        return {};\n    }\n\n    return dmesgCollector;\n}\n\nstd::pair<std::wstring, std::thread> DmesgCollector::StartDmesgThread(InputSource Source)\n{\n    std::wstring pipeName = wsl::windows::common::helpers::GetUniquePipeName();\n    wil::unique_hfile pipe(CreateNamedPipeW(\n        pipeName.c_str(), (PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED), (PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT), 1, LX_RELAY_BUFFER_SIZE, LX_RELAY_BUFFER_SIZE, 0, nullptr));\n\n    THROW_LAST_ERROR_IF(!pipe);\n\n    auto workerThread = std::thread([Self = shared_from_this(), Source, Pipe = std::move(pipe)]() {\n        try\n        {\n            wsl::windows::common::wslutil::SetThreadDescription(L\"Dmesg\");\n\n            // When the pipe connects, start reading data.\n            wsl::windows::common::helpers::ConnectPipe(Pipe.get(), INFINITE, Self->m_exitEvents);\n\n            std::vector<char> buffer(LX_RELAY_BUFFER_SIZE);\n            const auto allBuffer = gsl::make_span(buffer);\n            OVERLAPPED overlapped = {};\n            const wil::unique_event overlappedEvent(wil::EventOptions::ManualReset);\n            overlapped.hEvent = overlappedEvent.get();\n            for (;;)\n            {\n                overlappedEvent.ResetEvent();\n                const auto bytesRead = wsl::windows::common::relay::InterruptableRead(\n                    Pipe.get(), gslhelpers::convert_span<gsl::byte>(allBuffer), Self->m_exitEvents, &overlapped);\n\n                if (bytesRead == 0)\n                {\n                    break;\n                }\n\n                auto validBuffer = allBuffer.subspan(0, bytesRead);\n                Self->ProcessInput(Source, validBuffer);\n            }\n        }\n        CATCH_LOG()\n    });\n\n    return std::pair{std::move(pipeName), std::move(workerThread)};\n}\n\nvoid DmesgCollector::ProcessInput(InputSource Source, const gsl::span<char>& Input)\n{\n    RingBuffer* ringBuffer = nullptr;\n    bool sendToComPipe = m_debugConsole;\n    if (Source == DmesgCollectorEarlyConsole)\n    {\n        if (!m_earlyConsoleTransition)\n        {\n            ringBuffer = &m_dmesgEarlyBuffer;\n        }\n        else\n        {\n            sendToComPipe = !m_debugConsole && m_com1Pipe;\n        }\n    }\n    else\n    {\n        WI_ASSERT(Source == DmesgCollectorConsole);\n        ringBuffer = &m_dmesgBuffer;\n        if (!m_earlyConsoleTransition)\n        {\n            // The transition is because COM1 may have some other purpose after boot, and that data should not be\n            // captured into the dmesg log. Ideally we would flush all bytes for a clean transition, but since the\n            // legacy serial device is essentially one byte at a time, there isn't a clean way to detect this.\n            m_earlyConsoleTransition = true;\n        }\n    }\n\n    if (ringBuffer != nullptr)\n    {\n        std::string_view inputString{Input.data(), Input.size()};\n        ringBuffer->Insert(inputString);\n        if (m_telemetry)\n        {\n            const auto delimiterCount = std::count(inputString.begin(), inputString.end(), '\\n');\n            const auto newStrings = ringBuffer->GetLastDelimitedStrings('\\n', delimiterCount);\n            for (const auto& logLine : newStrings)\n            {\n                WSL_LOG(\"GuestLog\", TraceLoggingValue(logLine.c_str(), \"text\"), TraceLoggingValue(m_runtimeId, \"vmId\"));\n            }\n        }\n    }\n\n    if (sendToComPipe)\n    {\n        WriteToCom1(Input);\n    }\n}\n\nvoid DmesgCollector::WriteToCom1(const gsl::span<char>& Input)\n{\n    auto lock = m_lock.lock_exclusive();\n    if (!m_com1Pipe)\n    {\n        return;\n    }\n\n    // If this is not writing to the debug console, emulate the normal\n    // serial pipe behavior of waiting for a pipe connection.\n    if (m_waitForConnection)\n    {\n        if (FAILED(wil::ResultFromException(\n                [&]() { wsl::windows::common::helpers::ConnectPipe(m_com1Pipe.get(), INFINITE, m_exitEvents); })))\n        {\n            return;\n        }\n\n        m_waitForConnection = false;\n    }\n\n    m_overlappedEvent.ResetEvent();\n    const auto buffer = gslhelpers::convert_span<gsl::byte>(Input);\n    if (wsl::windows::common::relay::InterruptableWrite(m_com1Pipe.get(), buffer, m_exitEvents, &m_overlapped) == 0)\n    {\n        if (m_debugConsole || !m_pipeServer)\n        {\n            // A disconnect from the debug console, or from a pipe that was acting as the server, doesn't have any\n            // reconnect mechanism, so don't try to write anymore bytes.\n            m_com1Pipe.reset();\n        }\n        else\n        {\n            // Emulate the normal serial behavior of waiting for a pipe connection to write.\n            m_waitForConnection = true;\n        }\n    }\n}\n\nHRESULT DmesgCollector::Start(bool EnableEarlyBootConsole)\ntry\n{\n    if (!m_com1PipeName.empty())\n    {\n        // Check if the named pipe has already been created\n        m_com1Pipe.reset(CreateFileW(\n            m_com1PipeName.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, nullptr));\n\n        if (!m_com1Pipe)\n        {\n            m_com1Pipe.reset(CreateNamedPipeW(\n                m_com1PipeName.c_str(),\n                (PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED),\n                (PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT),\n                1,\n                LX_RELAY_BUFFER_SIZE,\n                LX_RELAY_BUFFER_SIZE,\n                0,\n                nullptr));\n\n            if (m_com1Pipe)\n            {\n                m_pipeServer = true;\n                // If the debug console is not active, may have to wait for a connection.\n                m_waitForConnection = !m_debugConsole;\n            }\n        }\n\n        THROW_LAST_ERROR_IF(!m_com1Pipe);\n    }\n\n    if (EnableEarlyBootConsole)\n    {\n        std::tie(m_earlyConsoleName, m_earlyConsoleWorker) = StartDmesgThread(DmesgCollectorEarlyConsole);\n    }\n\n    std::tie(m_virtioConsoleName, m_virtioWorker) = StartDmesgThread(DmesgCollectorConsole);\n    return S_OK;\n}\nCATCH_RETURN()\n"
  },
  {
    "path": "src/windows/common/Dmesg.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Dmesg.h\n\nAbstract:\n\n    This file contains declarations used to collect dmesg output\n\n--*/\n\n#pragma once\n\n#include \"relay.hpp\"\n#include \"RingBuffer.h\"\n\nclass DmesgCollector : public std::enable_shared_from_this<DmesgCollector>\n{\npublic:\n    DmesgCollector() = delete;\n    ~DmesgCollector();\n\n    std::wstring EarlyConsoleName() const\n    {\n        return m_earlyConsoleName;\n    }\n\n    std::wstring VirtioConsoleName() const\n    {\n        return m_virtioConsoleName;\n    }\n\n    static std::shared_ptr<DmesgCollector> Create(\n        GUID VmId, const wil::unique_event& ExitEvent, bool EnableTelemetry, bool EnableDebugConsole, const std::wstring& Com1PipeName, bool EnableEarlyBootConsole);\n\nprivate:\n    enum InputSource\n    {\n        DmesgCollectorEarlyConsole,\n        DmesgCollectorConsole\n    };\n\n    DmesgCollector(GUID VmId, const wil::unique_event& ExitEvent, bool EnableTelemetry, bool EnableDebugConsole, const std::wstring& Com1PipeName);\n\n    HRESULT Start(bool EnableEarlyBootConsole);\n    std::pair<std::wstring, std::thread> StartDmesgThread(InputSource Source);\n    void ProcessInput(InputSource Source, const gsl::span<char>& Input);\n    void WriteToCom1(const gsl::span<char>& Input);\n\n    wil::srwlock m_lock;\n    std::wstring m_com1PipeName;\n    std::wstring m_earlyConsoleName;\n    std::wstring m_virtioConsoleName;\n    wil::unique_event m_exitEvent;\n    wil::unique_event m_threadExit;\n    std::vector<HANDLE> m_exitEvents;\n    wil::unique_hfile m_com1Pipe;\n    GUID m_runtimeId{};\n    wil::unique_event m_overlappedEvent;\n    _Guarded_by_(m_lock) OVERLAPPED m_overlapped {};\n    RingBuffer m_dmesgBuffer{LX_RELAY_BUFFER_SIZE};\n    RingBuffer m_dmesgEarlyBuffer{LX_RELAY_BUFFER_SIZE};\n    bool m_debugConsole;\n    bool m_telemetry;\n    std::atomic<bool> m_earlyConsoleTransition = false;\n    bool m_pipeServer;\n    bool m_waitForConnection;\n    std::thread m_earlyConsoleWorker;\n    std::thread m_virtioWorker;\n};\n"
  },
  {
    "path": "src/windows/common/DnsResolver.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include <LxssDynamicFunction.h>\r\n#include \"precomp.h\"\r\n#include \"DnsResolver.h\"\r\n\r\nusing wsl::core::networking::DnsResolver;\r\n\r\nstatic constexpr auto c_dnsModuleName = L\"dnsapi.dll\";\r\n\r\nstd::optional<LxssDynamicFunction<decltype(DnsQueryRaw)>> DnsResolver::s_dnsQueryRaw;\r\nstd::optional<LxssDynamicFunction<decltype(DnsCancelQueryRaw)>> DnsResolver::s_dnsCancelQueryRaw;\r\nstd::optional<LxssDynamicFunction<decltype(DnsQueryRawResultFree)>> DnsResolver::s_dnsQueryRawResultFree;\r\n\r\nHRESULT DnsResolver::LoadDnsResolverMethods() noexcept\r\n{\r\n    static wil::shared_hmodule dnsModule;\r\n    static DWORD loadError = ERROR_SUCCESS;\r\n    static std::once_flag dnsLoadFlag;\r\n\r\n    // Load DNS dll only once\r\n    std::call_once(dnsLoadFlag, [&]() {\r\n        dnsModule.reset(LoadLibraryEx(c_dnsModuleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));\r\n        if (!dnsModule)\r\n        {\r\n            loadError = GetLastError();\r\n        }\r\n    });\r\n\r\n    RETURN_IF_WIN32_ERROR_MSG(loadError, \"LoadLibraryEx %ls\", c_dnsModuleName);\r\n\r\n    // Initialize dynamic functions for the DNS tunneling Windows APIs.\r\n    // using the non-throwing instance of LxssDynamicFunction as to not end up in the Error telemetry\r\n    LxssDynamicFunction<decltype(DnsQueryRaw)> local_dnsQueryRaw{DynamicFunctionErrorLogs::None};\r\n    RETURN_IF_FAILED_EXPECTED(local_dnsQueryRaw.load(dnsModule, \"DnsQueryRaw\"));\r\n    LxssDynamicFunction<decltype(DnsCancelQueryRaw)> local_dnsCancelQueryRaw{DynamicFunctionErrorLogs::None};\r\n    RETURN_IF_FAILED_EXPECTED(local_dnsCancelQueryRaw.load(dnsModule, \"DnsCancelQueryRaw\"));\r\n    LxssDynamicFunction<decltype(DnsQueryRawResultFree)> local_dnsQueryRawResultFree{DynamicFunctionErrorLogs::None};\r\n    RETURN_IF_FAILED_EXPECTED(local_dnsQueryRawResultFree.load(dnsModule, \"DnsQueryRawResultFree\"));\r\n\r\n    // Make a dummy call to the DNS APIs to verify if they are working. The APIs are going to be present\r\n    // on older Windows versions, where they can be turned on/off. If turned off, the APIs\r\n    // will be unusable and will return ERROR_CALL_NOT_IMPLEMENTED.\r\n    if (local_dnsQueryRaw(nullptr, nullptr) == ERROR_CALL_NOT_IMPLEMENTED)\r\n    {\r\n        RETURN_IF_WIN32_ERROR_EXPECTED(ERROR_CALL_NOT_IMPLEMENTED);\r\n    }\r\n\r\n    s_dnsQueryRaw.emplace(std::move(local_dnsQueryRaw));\r\n    s_dnsCancelQueryRaw.emplace(std::move(local_dnsCancelQueryRaw));\r\n    s_dnsQueryRawResultFree.emplace(std::move(local_dnsQueryRawResultFree));\r\n    return S_OK;\r\n}\r\n\r\nDnsResolver::DnsResolver(wil::unique_socket&& dnsHvsocket, DnsResolverFlags flags) :\r\n    m_dnsChannel(\r\n        std::move(dnsHvsocket),\r\n        [this](const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) {\r\n            ProcessDnsRequest(dnsBuffer, dnsClientIdentifier);\r\n        }),\r\n    m_flags(flags)\r\n{\r\n    // Initialize as signaled, as there are no requests yet\r\n    m_allRequestsFinished.SetEvent();\r\n\r\n    // Read external interface constraint regkey\r\n    const auto lxssKey = windows::common::registry::OpenLxssMachineKey(KEY_READ);\r\n    m_externalInterfaceConstraintName =\r\n        windows::common::registry::ReadString(lxssKey.get(), nullptr, c_interfaceConstraintKey, L\"\");\r\n\r\n    if (!m_externalInterfaceConstraintName.empty())\r\n    {\r\n        ResolveExternalInterfaceConstraintIndex();\r\n\r\n        WSL_LOG(\r\n            \"DnsResolver::DnsResolver\",\r\n            TraceLoggingValue(m_externalInterfaceConstraintName.c_str(), \"m_externalInterfaceConstraintName\"),\r\n            TraceLoggingValue(m_externalInterfaceConstraintIndex, \"m_externalInterfaceConstraintIndex\"));\r\n\r\n        // Register for interface change notifications. Notifications are used to determine if the external interface constraint setting is applicable.\r\n        THROW_IF_WIN32_ERROR(NotifyIpInterfaceChange(AF_UNSPEC, &DnsResolver::InterfaceChangeCallback, this, FALSE, &m_interfaceNotificationHandle));\r\n    }\r\n}\r\n\r\nDnsResolver::~DnsResolver() noexcept\r\n{\r\n    Stop();\r\n}\r\n\r\nvoid DnsResolver::GenerateTelemetry() noexcept\r\ntry\r\n{\r\n    // Find the 3 most common DNS API failures\r\n    uint32_t mostCommonDnsStatusError = 0;\r\n    uint32_t mostCommonDnsStatusErrorCount = 0;\r\n    uint32_t secondCommonDnsStatusError = 0;\r\n    uint32_t secondCommonDnsStatusErrorCount = 0;\r\n    uint32_t thirdCommonDnsStatusError = 0;\r\n    uint32_t thirdCommonDnsStatusErrorCount = 0;\r\n\r\n    std::vector<std::pair<uint32_t, uint32_t>> failures(m_dnsApiFailures.size());\r\n    std::copy(m_dnsApiFailures.begin(), m_dnsApiFailures.end(), failures.begin());\r\n\r\n    // Sort in descending order based on failure count\r\n    std::sort(failures.begin(), failures.end(), [](const auto& lhs, const auto& rhs) { return lhs.second > rhs.second; });\r\n\r\n    if (failures.size() >= 1)\r\n    {\r\n        mostCommonDnsStatusError = failures[0].first;\r\n        mostCommonDnsStatusErrorCount = failures[0].second;\r\n    }\r\n    if (failures.size() >= 2)\r\n    {\r\n        secondCommonDnsStatusError = failures[1].first;\r\n        secondCommonDnsStatusErrorCount = failures[1].second;\r\n    }\r\n    if (failures.size() >= 3)\r\n    {\r\n        thirdCommonDnsStatusError = failures[2].first;\r\n        thirdCommonDnsStatusErrorCount = failures[2].second;\r\n    }\r\n\r\n    // Add telemetry with DNS tunneling statistics, before shutting down\r\n    WSL_LOG(\r\n        \"DnsTunnelingStatistics\",\r\n        TraceLoggingValue(m_totalUdpQueries.load(), \"totalUdpQueries\"),\r\n        TraceLoggingValue(m_successfulUdpQueries.load(), \"successfulUdpQueries\"),\r\n        TraceLoggingValue(m_totalTcpQueries.load(), \"totalTcpQueries\"),\r\n        TraceLoggingValue(m_successfulTcpQueries.load(), \"successfulTcpQueries\"),\r\n        TraceLoggingValue(m_queriesWithNullResult.load(), \"queriesWithNullResult\"),\r\n        TraceLoggingValue(m_failedDnsQueryRawCalls.load(), \"FailedDnsQueryRawCalls\"),\r\n        TraceLoggingValue(m_dnsApiFailures.size(), \"totalDnsStatusErrorInstances\"),\r\n        TraceLoggingValue(mostCommonDnsStatusError, \"mostCommonDnsStatusError\"),\r\n        TraceLoggingValue(mostCommonDnsStatusErrorCount, \"mostCommonDnsStatusErrorCount\"),\r\n        TraceLoggingValue(secondCommonDnsStatusError, \"secondCommonDnsStatusError\"),\r\n        TraceLoggingValue(secondCommonDnsStatusErrorCount, \"secondCommonDnsStatusErrorCount\"),\r\n        TraceLoggingValue(thirdCommonDnsStatusError, \"thirdCommonDnsStatusError\"),\r\n        TraceLoggingValue(thirdCommonDnsStatusErrorCount, \"thirdCommonDnsStatusErrorCount\"));\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsResolver::Stop() noexcept\r\ntry\r\n{\r\n    WSL_LOG(\"DnsResolver::Stop\");\r\n\r\n    // Scoped m_dnsLock\r\n    {\r\n        const std::lock_guard lock(m_dnsLock);\r\n\r\n        m_stopped = true;\r\n\r\n        // Cancel existing requests. Cancel is complete when DnsQueryRawCallback is\r\n        // invoked with status == ERROR_CANCELLED\r\n        // N.B. Cancelling can end up calling the DnsQueryRawCallback directly on this same thread. i.e., while this\r\n        // lock is held. Which is fine because m_dnsLock is a recursive mutex.\r\n        // N.B. Cancelling a query will synchronously remove the query from m_dnsRequests, which invalidates iterators.\r\n\r\n        std::vector<DNS_QUERY_RAW_CANCEL*> cancelHandles;\r\n        cancelHandles.reserve(m_dnsRequests.size());\r\n\r\n        for (auto& [_, context] : m_dnsRequests)\r\n        {\r\n            cancelHandles.emplace_back(&context->m_cancelHandle);\r\n        }\r\n\r\n        for (const auto e : cancelHandles)\r\n        {\r\n            LOG_IF_WIN32_ERROR(s_dnsCancelQueryRaw.value()(e));\r\n        }\r\n    }\r\n\r\n    // Wait for all requests to complete. At this point no new requests can be started since the object is stopped.\r\n    // We are only waiting for existing requests to finish.\r\n    m_allRequestsFinished.wait();\r\n\r\n    // Stop the response queue first as it can make calls in m_dnsChannel\r\n    m_dnsResponseQueue.cancel();\r\n\r\n    m_dnsChannel.Stop();\r\n\r\n    // Stop interface change notifications\r\n    m_interfaceNotificationHandle.reset();\r\n\r\n    GenerateTelemetry();\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsResolver::ProcessDnsRequest(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept\r\ntry\r\n{\r\n    const std::lock_guard lock(m_dnsLock);\r\n    if (m_stopped)\r\n    {\r\n        return;\r\n    }\r\n\r\n    WSL_LOG_DEBUG(\r\n        \"DnsResolver::ProcessDnsRequest - received new DNS request\",\r\n        TraceLoggingValue(dnsBuffer.size(), \"DNS buffer size\"),\r\n        TraceLoggingValue(dnsClientIdentifier.Protocol == IPPROTO_UDP ? \"UDP\" : \"TCP\", \"Protocol\"),\r\n        TraceLoggingValue(dnsClientIdentifier.DnsClientId, \"DNS client id\"),\r\n        TraceLoggingValue(!m_externalInterfaceConstraintName.empty(), \"Is ExternalInterfaceConstraint configured\"),\r\n        TraceLoggingValue(m_externalInterfaceConstraintIndex, \"m_externalInterfaceConstraintIndex\"));\r\n\r\n    // If the external interface constraint is configured but it is *not* present/up, WSL should be net-blind, so we avoid making DNS requests.\r\n    if (!m_externalInterfaceConstraintName.empty() && m_externalInterfaceConstraintIndex == 0)\r\n    {\r\n        return;\r\n    }\r\n\r\n    dnsClientIdentifier.Protocol == IPPROTO_UDP ? m_totalUdpQueries++ : m_totalTcpQueries++;\r\n\r\n    // Get next request id. If value reaches UINT_MAX + 1 it will be automatically reset to 0\r\n    const auto requestId = m_currentRequestId++;\r\n\r\n    // Create the DNS request context\r\n    auto context = std::make_unique<DnsResolver::DnsQueryContext>(\r\n        requestId, dnsClientIdentifier, [this](_Inout_ DnsResolver::DnsQueryContext* context, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) {\r\n            HandleDnsQueryCompletion(context, queryResults);\r\n        });\r\n\r\n    auto [it, _] = m_dnsRequests.emplace(requestId, std::move(context));\r\n    const auto localContext = it->second.get();\r\n\r\n    auto removeContextOnError = wil::scope_exit([&] { WI_VERIFY(m_dnsRequests.erase(requestId) == 1); });\r\n\r\n    // Fill DNS request structure\r\n    DNS_QUERY_RAW_REQUEST request{};\r\n\r\n    request.version = DNS_QUERY_RAW_REQUEST_VERSION1;\r\n    request.resultsVersion = DNS_QUERY_RAW_RESULTS_VERSION1;\r\n    request.dnsQueryRawSize = static_cast<ULONG>(dnsBuffer.size());\r\n    request.dnsQueryRaw = (PBYTE)dnsBuffer.data();\r\n    request.protocol = (dnsClientIdentifier.Protocol == IPPROTO_TCP) ? DNS_PROTOCOL_TCP : DNS_PROTOCOL_UDP;\r\n    request.queryCompletionCallback = DnsResolver::DnsQueryRawCallback;\r\n    request.queryContext = localContext;\r\n    // Only unicast UDP & TCP queries are tunneled. Pass this flag to tell Windows DNS client to *not* resolve using multicast.\r\n    request.queryOptions |= DNS_QUERY_NO_MULTICAST;\r\n\r\n    // In a DNS request from Linux there might be DNS records that Windows DNS client does not know how to parse.\r\n    // By default in this case Windows will fail the request. When the flag is enabled, Windows will extract the\r\n    // question from the DNS request and attempt to resolve it, ignoring the unknown records.\r\n    if (WI_IsFlagSet(m_flags, DnsResolverFlags::BestEffortDnsParsing))\r\n    {\r\n        request.queryRawOptions |= DNS_QUERY_RAW_OPTION_BEST_EFFORT_PARSE;\r\n    }\r\n\r\n    // If the external interface constraint is configured and present on the host, only send DNS requests on that interface.\r\n    if (m_externalInterfaceConstraintIndex != 0)\r\n    {\r\n        request.interfaceIndex = m_externalInterfaceConstraintIndex;\r\n    }\r\n\r\n    // Start the DNS request\r\n    // N.B. All DNS requests will bypass the Windows DNS cache\r\n    const auto result = s_dnsQueryRaw.value()(&request, &localContext->m_cancelHandle);\r\n    if (result != DNS_REQUEST_PENDING)\r\n    {\r\n        m_failedDnsQueryRawCalls++;\r\n\r\n        WSL_LOG(\r\n            \"ProcessDnsRequestFailed\",\r\n            TraceLoggingValue(requestId, \"requestId\"),\r\n            TraceLoggingValue(result, \"result\"),\r\n            TraceLoggingValue(\"DnsQueryRaw\", \"executionStep\"));\r\n        return;\r\n    }\r\n\r\n    removeContextOnError.release();\r\n\r\n    m_allRequestsFinished.ResetEvent();\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsResolver::HandleDnsQueryCompletion(_Inout_ DnsResolver::DnsQueryContext* queryContext, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) noexcept\r\ntry\r\n{\r\n    // Always free the query result structure\r\n    const auto freeQueryResults = wil::scope_exit([&] {\r\n        if (queryResults != nullptr)\r\n        {\r\n            s_dnsQueryRawResultFree.value()(queryResults);\r\n        }\r\n    });\r\n\r\n    const std::lock_guard lock(m_dnsLock);\r\n\r\n    if (queryResults != nullptr)\r\n    {\r\n        WSL_LOG(\r\n            \"DnsResolver::HandleDnsQueryCompletion\",\r\n            TraceLoggingValue(queryContext->m_id, \"queryContext->m_id\"),\r\n            TraceLoggingValue(queryResults->queryStatus, \"queryResults->queryStatus\"),\r\n            TraceLoggingValue(queryResults->queryRawResponse != nullptr, \"validResponse\"));\r\n\r\n        // Note: The response may be valid even if queryResults->queryStatus is not 0, for example when the DNS server returns a negative response.\r\n        if (queryResults->queryRawResponse != nullptr)\r\n        {\r\n            queryContext->m_dnsClientIdentifier.Protocol == IPPROTO_UDP ? m_successfulUdpQueries++ : m_successfulTcpQueries++;\r\n        }\r\n        // the Windows DNS API returned failure\r\n        else\r\n        {\r\n            if (m_dnsApiFailures.find(queryResults->queryStatus) == m_dnsApiFailures.end())\r\n            {\r\n                m_dnsApiFailures[queryResults->queryStatus] = 1;\r\n            }\r\n            else\r\n            {\r\n                m_dnsApiFailures[queryResults->queryStatus]++;\r\n            }\r\n        }\r\n    }\r\n    else\r\n    {\r\n        WSL_LOG(\r\n            \"DnsResolver::HandleDnsQueryCompletion - received a NULL queryResults\",\r\n            TraceLoggingValue(queryContext->m_id, \"queryContext->m_id\"));\r\n        m_queriesWithNullResult++;\r\n    }\r\n\r\n    if (!m_stopped && queryResults != nullptr && queryResults->queryRawResponse != nullptr)\r\n    {\r\n        // Copy DNS response buffer\r\n        std::vector<gsl::byte> dnsResponse(queryResults->queryRawResponseSize);\r\n        CopyMemory(dnsResponse.data(), queryResults->queryRawResponse, queryResults->queryRawResponseSize);\r\n\r\n        WSL_LOG_DEBUG(\r\n            \"DnsResolver::HandleDnsQueryCompletion - received new DNS response\",\r\n            TraceLoggingValue(dnsResponse.size(), \"DNS buffer size\"),\r\n            TraceLoggingValue(queryContext->m_dnsClientIdentifier.Protocol == IPPROTO_UDP ? \"UDP\" : \"TCP\", \"Protocol\"),\r\n            TraceLoggingValue(queryContext->m_dnsClientIdentifier.DnsClientId, \"DNS client id\"));\r\n\r\n        // Schedule the DNS response to be sent to Linux\r\n        m_dnsResponseQueue.submit([this, dnsResponse = std::move(dnsResponse), dnsClientIdentifier = queryContext->m_dnsClientIdentifier]() mutable {\r\n            m_dnsChannel.SendDnsMessage(gsl::make_span(dnsResponse), dnsClientIdentifier);\r\n        });\r\n    }\r\n\r\n    // Stop tracking this DNS request and delete the request context\r\n    WI_VERIFY(m_dnsRequests.erase(queryContext->m_id) == 1);\r\n\r\n    // Set event if all tracked requests have finished\r\n    if (m_dnsRequests.empty())\r\n    {\r\n        m_allRequestsFinished.SetEvent();\r\n    }\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsResolver::ResolveExternalInterfaceConstraintIndex() noexcept\r\ntry\r\n{\r\n    const std::lock_guard lock(m_dnsLock);\r\n    if (m_stopped)\r\n    {\r\n        return;\r\n    }\r\n\r\n    if (m_externalInterfaceConstraintName.empty())\r\n    {\r\n        return;\r\n    }\r\n\r\n    NET_LUID interfaceLuid{};\r\n    ULONG interfaceIndex = 0;\r\n\r\n    // Update the interface index on every exit path.\r\n    // The calls below to convert interface name to index will fail if the interface does not exist anymore,\r\n    // in which case we still need to reset the interface index to its default value of 0.\r\n    const auto setInterfaceIndex = wil::scope_exit([&] {\r\n        if (interfaceIndex != m_externalInterfaceConstraintIndex)\r\n        {\r\n            WSL_LOG(\r\n                \"DnsResolver::ResolveExternalInterfaceConstraintIndex - setting m_externalInterfaceConstraintIndex to new value\",\r\n                TraceLoggingValue(m_externalInterfaceConstraintIndex, \"old interface index\"),\r\n                TraceLoggingValue(interfaceIndex, \"new interface index\"));\r\n\r\n            m_externalInterfaceConstraintIndex = interfaceIndex;\r\n        }\r\n    });\r\n\r\n    // If external interface constraint is configured, query to see if it's present on the host.\r\n    auto errorCode = ConvertInterfaceAliasToLuid(m_externalInterfaceConstraintName.c_str(), &interfaceLuid);\r\n    if (FAILED_WIN32_LOG(errorCode))\r\n    {\r\n        return;\r\n    }\r\n\r\n    errorCode = ConvertInterfaceLuidToIndex(&interfaceLuid, reinterpret_cast<PNET_IFINDEX>(&interfaceIndex));\r\n    if (FAILED_WIN32_LOG(errorCode))\r\n    {\r\n        return;\r\n    }\r\n}\r\nCATCH_LOG()\r\n\r\nVOID CALLBACK DnsResolver::DnsQueryRawCallback(_In_ VOID* queryContext, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) noexcept\r\ntry\r\n{\r\n    assert(queryContext != nullptr);\r\n\r\n    const auto context = static_cast<DnsQueryContext*>(queryContext);\r\n\r\n    // Call into DnsResolver parent object to process the query result\r\n    context->m_handleQueryCompletion(context, queryResults);\r\n}\r\nCATCH_LOG()\r\n\r\nVOID CALLBACK DnsResolver::InterfaceChangeCallback(_In_ PVOID context, PMIB_IPINTERFACE_ROW, MIB_NOTIFICATION_TYPE) noexcept\r\ntry\r\n{\r\n    const auto dnsResolver = static_cast<DnsResolver*>(context);\r\n    dnsResolver->ResolveExternalInterfaceConstraintIndex();\r\n}\r\nCATCH_LOG()\r\n"
  },
  {
    "path": "src/windows/common/DnsResolver.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include \"DnsTunnelingChannel.h\"\r\n#include \"WslCoreMessageQueue.h\"\r\n#include \"WslCoreNetworkingSupport.h\"\r\n\r\nnamespace wsl::core::networking {\r\n\r\nenum class DnsResolverFlags\r\n{\r\n    None = 0x0,\r\n    BestEffortDnsParsing = 0x1\r\n};\r\nDEFINE_ENUM_FLAG_OPERATORS(DnsResolverFlags);\r\n\r\nclass DnsResolver\r\n{\r\npublic:\r\n    DnsResolver(wil::unique_socket&& dnsHvsocket, DnsResolverFlags flags);\r\n    ~DnsResolver() noexcept;\r\n\r\n    DnsResolver(const DnsResolver&) = delete;\r\n    DnsResolver& operator=(const DnsResolver&) = delete;\r\n\r\n    DnsResolver(DnsResolver&&) = delete;\r\n    DnsResolver& operator=(DnsResolver&&) = delete;\r\n\r\n    void Stop() noexcept;\r\n\r\n    static HRESULT LoadDnsResolverMethods() noexcept;\r\n\r\nprivate:\r\n    struct DnsQueryContext\r\n    {\r\n        // Struct containing protocol (TCP/UDP) and unique id of the Linux DNS client making the request.\r\n        LX_GNS_DNS_CLIENT_IDENTIFIER m_dnsClientIdentifier{};\r\n\r\n        // Handle used to cancel the request.\r\n        DNS_QUERY_RAW_CANCEL m_cancelHandle{};\r\n\r\n        // Unique query id.\r\n        uint32_t m_id{};\r\n\r\n        // Callback to the parent object to notify about the DNS query completion.\r\n        std::function<void(DnsQueryContext*, DNS_QUERY_RAW_RESULT*)> m_handleQueryCompletion;\r\n\r\n        DnsQueryContext(\r\n            uint32_t id,\r\n            const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier,\r\n            std::function<void(DnsQueryContext*, DNS_QUERY_RAW_RESULT*)>&& handleQueryCompletion) :\r\n            m_dnsClientIdentifier(dnsClientIdentifier), m_id(id), m_handleQueryCompletion(std::move(handleQueryCompletion))\r\n        {\r\n        }\r\n\r\n        ~DnsQueryContext() noexcept = default;\r\n\r\n        DnsQueryContext(const DnsQueryContext&) = delete;\r\n        DnsQueryContext& operator=(const DnsQueryContext&) = delete;\r\n        DnsQueryContext(DnsQueryContext&&) = delete;\r\n        DnsQueryContext& operator=(DnsQueryContext&&) = delete;\r\n    };\r\n\r\n    void GenerateTelemetry() noexcept;\r\n\r\n    // Process DNS request received from Linux.\r\n    //\r\n    // Arguments:\r\n    // dnsBuffer - buffer containing DNS request.\r\n    // dnsClientIdentifier - struct containing protocol (TCP/UDP) and unique id of the Linux DNS client making the request.\r\n    void ProcessDnsRequest(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept;\r\n\r\n    // Handle completion of DNS query.\r\n    //\r\n    // Arguments:\r\n    // dnsQueryContext - context structure for the DNS request.\r\n    // queryResults - structure containing result of the DNS request.\r\n    void HandleDnsQueryCompletion(_Inout_ DnsQueryContext* dnsQueryContext, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) noexcept;\r\n\r\n    void ResolveExternalInterfaceConstraintIndex() noexcept;\r\n\r\n    // Callback that will be invoked by the DNS API whenever a request finishes. The callback is invoked on success, error or when request is cancelled.\r\n    //\r\n    // Arguments:\r\n    // queryContext - pointer to context structure, will be a structure of type DnsQueryContext.\r\n    // queryResults - pointer to structure containing the result of the DNS request.\r\n    static VOID CALLBACK DnsQueryRawCallback(_In_ VOID* queryContext, _Inout_opt_ DNS_QUERY_RAW_RESULT* queryResults) noexcept;\r\n\r\n    static VOID CALLBACK InterfaceChangeCallback(_In_ PVOID context, PMIB_IPINTERFACE_ROW, MIB_NOTIFICATION_TYPE) noexcept;\r\n\r\n    std::recursive_mutex m_dnsLock;\r\n\r\n    // Flag used when shutting down the object.\r\n    _Guarded_by_(m_dnsLock) bool m_stopped = false;\r\n\r\n    // Hvsocket channel used to exchange DNS messages with Linux.\r\n    DnsTunnelingChannel m_dnsChannel;\r\n\r\n    // Queue used to send DNS responses to Linux.\r\n    WslCoreMessageQueue m_dnsResponseQueue;\r\n\r\n    // Unique id that is incremented for each request. In case the value reaches MAX_UINT and is reset to 0,\r\n    // it's assumed previous requests with id's 0, 1, ... finished in the meantime and the id can be reused.\r\n    _Guarded_by_(m_dnsLock) uint32_t m_currentRequestId = 0;\r\n\r\n    // Mapping request id to the request context structure.\r\n    _Guarded_by_(m_dnsLock) std::unordered_map<uint32_t, std::unique_ptr<DnsQueryContext>> m_dnsRequests {};\r\n\r\n    // Event that is set when all tracked DNS requests have completed.\r\n    wil::unique_event m_allRequestsFinished{wil::EventOptions::ManualReset};\r\n\r\n    // Used for handling of external interface constraint setting.\r\n    unique_notify_handle m_interfaceNotificationHandle{};\r\n\r\n    std::wstring m_externalInterfaceConstraintName;\r\n    _Guarded_by_(m_dnsLock) ULONG m_externalInterfaceConstraintIndex = 0;\r\n\r\n    const DnsResolverFlags m_flags{};\r\n\r\n    // Statistics used for telemetry.\r\n    std::atomic<uint32_t> m_totalUdpQueries{0};\r\n    std::atomic<uint32_t> m_successfulUdpQueries{0};\r\n    std::atomic<uint32_t> m_totalTcpQueries{0};\r\n    std::atomic<uint32_t> m_successfulTcpQueries{0};\r\n    std::atomic<uint32_t> m_queriesWithNullResult{0};\r\n    std::atomic<uint32_t> m_failedDnsQueryRawCalls{0};\r\n\r\n    _Guarded_by_(m_dnsLock) std::map<uint32_t, uint32_t> m_dnsApiFailures;\r\n\r\n    // Dynamic functions used for calling the DNS APIs.\r\n\r\n    // Function to start a raw DNS request.\r\n    static std::optional<LxssDynamicFunction<decltype(DnsQueryRaw)>> s_dnsQueryRaw;\r\n    // Function to cancel a raw DNS request.\r\n    static std::optional<LxssDynamicFunction<decltype(DnsCancelQueryRaw)>> s_dnsCancelQueryRaw;\r\n    // Function to free the structure containing the result of a raw DNS request.\r\n    static std::optional<LxssDynamicFunction<decltype(DnsQueryRawResultFree)>> s_dnsQueryRawResultFree;\r\n};\r\n\r\n} // namespace wsl::core::networking\r\n"
  },
  {
    "path": "src/windows/common/DnsTunnelingChannel.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include \"precomp.h\"\r\n#include \"DnsTunnelingChannel.h\"\r\n\r\nusing wsl::core::networking::DnsTunnelingChannel;\r\n\r\nDnsTunnelingChannel::DnsTunnelingChannel(wil::unique_socket&& socket, DnsTunnelingCallback&& reportDnsRequest) :\r\n    m_channel{std::move(socket), \"DnsTunneling\", m_stopEvent.get()}, m_reportDnsRequest(std::move(reportDnsRequest))\r\n{\r\n    WSL_LOG(\"DnsTunnelingChannel::DnsTunnelingChannel [Windows]\", TraceLoggingValue(m_channel.Socket(), \"socket\"));\r\n\r\n    // Start thread waiting for incoming messages from Linux side\r\n    m_receiveWorkerThread = std::thread([this]() { ReceiveLoop(); });\r\n}\r\n\r\nDnsTunnelingChannel::~DnsTunnelingChannel()\r\n{\r\n    Stop();\r\n}\r\n\r\nvoid DnsTunnelingChannel::SendDnsMessage(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept\r\ntry\r\n{\r\n    // Exit if channel was stopped\r\n    if (m_stopEvent.is_signaled())\r\n    {\r\n        return;\r\n    }\r\n\r\n    wsl::shared::MessageWriter<LX_GNS_DNS_TUNNELING_MESSAGE> message(LxGnsMessageDnsTunneling);\r\n    message->DnsClientIdentifier = dnsClientIdentifier;\r\n    message.WriteSpan(dnsBuffer);\r\n\r\n    m_channel.SendMessage<LX_GNS_DNS_TUNNELING_MESSAGE>(message.Span());\r\n}\r\nCATCH_LOG()\r\n\r\nvoid DnsTunnelingChannel::ReceiveLoop() noexcept\r\n{\r\n    std::vector<gsl::byte> receiveBuffer;\r\n\r\n    for (;;)\r\n    {\r\n        try\r\n        {\r\n            if (m_stopEvent.is_signaled())\r\n            {\r\n                return;\r\n            }\r\n\r\n            WSL_LOG_DEBUG(\"DnsTunnelingChannel::ReceiveLoop [Windows] - waiting for next message from Linux\");\r\n\r\n            // Read next message. wsl::shared::socket::RecvMessage() first reads the message header, then uses it to determine the\r\n            // total size of the message and read the rest of the message, resizing the buffer if needed.\r\n            auto [message, span] = m_channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\r\n            if (message == nullptr)\r\n            {\r\n                WSL_LOG(\"DnsTunnelingChannel::ReceiveLoop [Windows] - failed to read message\");\r\n                return;\r\n            }\r\n\r\n            // Get the message type from the message header\r\n            switch (message->MessageType)\r\n            {\r\n            case LxGnsMessageDnsTunneling:\r\n            {\r\n                // Cast message to a LX_GNS_DNS_TUNNELING_MESSAGE struct\r\n                auto* dnsMessage = gslhelpers::try_get_struct<LX_GNS_DNS_TUNNELING_MESSAGE>(span);\r\n                if (!dnsMessage)\r\n                {\r\n                    WSL_LOG(\r\n                        \"DnsTunnelingChannel::ReceiveLoop [Windows] - failed to convert message to LX_GNS_DNS_TUNNELING_MESSAGE\");\r\n                    return;\r\n                }\r\n\r\n                // Extract DNS buffer from message\r\n                auto dnsBuffer = span.subspan(offsetof(LX_GNS_DNS_TUNNELING_MESSAGE, Buffer));\r\n\r\n                WSL_LOG_DEBUG(\r\n                    \"DnsTunnelingChannel::ReceiveLoop [Windows] - received DNS message\",\r\n                    TraceLoggingValue(dnsBuffer.size(), \"DNS buffer size\"),\r\n                    TraceLoggingValue(dnsMessage->DnsClientIdentifier.Protocol == IPPROTO_UDP ? \"UDP\" : \"TCP\", \"Protocol\"),\r\n                    TraceLoggingValue(dnsMessage->DnsClientIdentifier.DnsClientId, \"DNS client id\"));\r\n\r\n                // Invoke callback to notify about the new DNS request\r\n                m_reportDnsRequest(dnsBuffer, dnsMessage->DnsClientIdentifier);\r\n\r\n                break;\r\n            }\r\n\r\n            default:\r\n            {\r\n                THROW_HR_MSG(E_UNEXPECTED, \"Unexpected LX_MESSAGE_TYPE : %i\", message->MessageType);\r\n            }\r\n            }\r\n        }\r\n        CATCH_LOG()\r\n    }\r\n}\r\n\r\nvoid DnsTunnelingChannel::Stop() noexcept\r\ntry\r\n{\r\n    WSL_LOG(\"DnsTunnelingChannel::Stop [Windows]\");\r\n\r\n    m_stopEvent.SetEvent();\r\n\r\n    // Stop receive loop\r\n    if (m_receiveWorkerThread.joinable())\r\n    {\r\n        m_receiveWorkerThread.join();\r\n    }\r\n}\r\nCATCH_LOG()\r\n"
  },
  {
    "path": "src/windows/common/DnsTunnelingChannel.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include <wil/resource.h>\r\n#include \"lxinitshared.h\"\r\n#include \"SocketChannel.h\"\r\n\r\nnamespace wsl::core::networking {\r\n\r\nusing DnsTunnelingCallback = std::function<void(const gsl::span<gsl::byte>, const LX_GNS_DNS_CLIENT_IDENTIFIER&)>;\r\n\r\nclass DnsTunnelingChannel\r\n{\r\npublic:\r\n    DnsTunnelingChannel(wil::unique_socket&& socket, DnsTunnelingCallback&& reportDnsRequest);\r\n    ~DnsTunnelingChannel();\r\n\r\n    DnsTunnelingChannel(const DnsTunnelingChannel&) = delete;\r\n    DnsTunnelingChannel& operator=(const DnsTunnelingChannel&) = delete;\r\n\r\n    DnsTunnelingChannel(DnsTunnelingChannel&&) = delete;\r\n    DnsTunnelingChannel& operator=(DnsTunnelingChannel&&) = delete;\r\n\r\n    // Construct and send a LX_GNS_DNS_TUNNELING_MESSAGE message on the channel.\r\n    // Note: Callers are responsible for sequencing calls to this method.\r\n    //\r\n    // Arguments:\r\n    // dnsBuffer - buffer containing DNS response.\r\n    // dnsClientIdentifier - struct containing protocol (TCP/UDP) and unique id of the Linux DNS client making the request.\r\n    void SendDnsMessage(const gsl::span<gsl::byte> dnsBuffer, const LX_GNS_DNS_CLIENT_IDENTIFIER& dnsClientIdentifier) noexcept;\r\n\r\n    // Stop the channel.\r\n    void Stop() noexcept;\r\n\r\nprivate:\r\n    // Wait for messages on the channel from Linux side.\r\n    void ReceiveLoop() noexcept;\r\n\r\n    wil::unique_event m_stopEvent{wil::EventOptions::ManualReset};\r\n\r\n    wsl::shared::SocketChannel m_channel;\r\n\r\n    std::thread m_receiveWorkerThread;\r\n\r\n    // Callback used to notify when there is a new DNS request message on the channel.\r\n    DnsTunnelingCallback m_reportDnsRequest;\r\n};\r\n\r\n} // namespace wsl::core::networking\r\n"
  },
  {
    "path": "src/windows/common/ExecutionContext.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"precomp.h\"\n#include \"ExecutionContext.h\"\n#include \"wsleventschema.h\"\n\nusing wsl::windows::common::ClientExecutionContext;\nusing wsl::windows::common::Context;\nusing wsl::windows::common::Error;\nusing wsl::windows::common::ExecutionContext;\nusing wsl::windows::common::ServiceExecutionContext;\n\nthread_local ExecutionContext* g_currentContext = nullptr;\nstatic bool g_enabled = false;\nbool g_runningInService = false;\nstatic HANDLE g_eventLog = nullptr;\n\nvoid wsl::windows::common::EnableContextualizedErrors(bool service)\n{\n    WI_ASSERT(!g_enabled);\n    g_enabled = true;\n    g_runningInService = service;\n}\n\nExecutionContext::ExecutionContext(Context context, FILE* warningsFile) noexcept :\n    m_warningsFile(warningsFile), m_parent(g_currentContext), m_context(context)\n{\n    WI_ASSERT(g_currentContext == nullptr || g_currentContext->m_context < m_context);\n\n    if (!g_enabled)\n    {\n        return;\n    }\n\n    g_currentContext = this;\n}\n\nExecutionContext::~ExecutionContext()\n{\n    g_currentContext = m_parent;\n    WI_ASSERT(!m_errorString.has_value());\n}\n\nExecutionContext* ExecutionContext::Current()\n{\n    return g_currentContext;\n}\n\nvoid ExecutionContext::SetErrorStringImpl(std::wstring&& string)\n{\n    WI_ASSERT(!m_errorString.has_value());\n    m_errorString = std::move(string);\n}\n\nbool ExecutionContext::CanCollectUserErrorMessage()\n{\n    if (g_runningInService)\n    {\n        if (m_parent != nullptr)\n        {\n            return m_parent->CanCollectUserErrorMessage();\n        }\n        else\n        {\n            // If we're running in a service and the root context isn't a service context,\n            // then error messages cannot be reported.\n            return false;\n        }\n    }\n    else\n    {\n        return true;\n    }\n}\n\nULONGLONG ExecutionContext::CurrentContext() const noexcept\n{\n    ULONGLONG errorContext = m_context;\n    for (const auto* e = m_parent; e != nullptr; e = e->m_parent)\n    {\n        errorContext |= static_cast<ULONGLONG>(e->m_context);\n    }\n\n    return errorContext;\n}\n\nvoid ExecutionContext::CollectErrorImpl(HRESULT result, ULONGLONG context, std::optional<std::wstring>&& message)\n{\n    WI_ASSERT(m_parent == nullptr);\n\n    // Special case for an error being rethrown from a parent context.\n    if (m_error.has_value() && m_error->Code == result && (context & m_error->Context) == context)\n    {\n        // This error has the same HRESULT that the one we already have and comes from a less specific context, drop.\n        if (!m_error->Message.has_value() && message.has_value())\n        {\n            /* This is for the scenario where a specialized error message is sent after catching and rethrowing an error.\n             * Example:\n             * try\n             * {\n             *    something();\n             * }\n             * catch (...)\n             * {\n             *   THROW_HR_WITH_USER_ERROR(..., \"Something failed: [...]\");\n             * }\n             */\n\n            m_error->Message = std::move(message);\n        }\n\n        return;\n    }\n\n    m_error.emplace(result, context, std::move(message));\n}\n\nvoid ExecutionContext::CollectError(HRESULT result)\n{\n    if (g_currentContext == nullptr)\n    {\n        return;\n    }\n\n    g_currentContext->CollectErrorImpl(result);\n}\n\nvoid ExecutionContext::CollectErrorImpl(HRESULT result)\n{\n    RootContext().CollectErrorImpl(result, CurrentContext(), std::move(m_errorString));\n\n    m_errorString.reset();\n}\n\nvoid ExecutionContext::EmitUserWarning(const std::wstring& warning, const std::source_location& location)\n{\n    WSL_LOG(\n        \"UserWarning\",\n        TraceLoggingValue(location.file_name(), \"FileName\"),\n        TraceLoggingValue(location.line(), \"Line\"),\n        TraceLoggingValue(warning.c_str(), \"Content\"));\n\n    if (!g_enabled)\n    {\n        return;\n    }\n\n    if (!CollectUserWarning(std::format(L\"wsl: {}\\n\", warning)))\n    {\n        static std::atomic<bool> displayed = false;\n        if (!displayed.exchange(true))\n        {\n            wsl::windows::common::notifications::DisplayWarningsNotification();\n        }\n    }\n\n    if (g_eventLog)\n    {\n        auto* warningPtr = warning.c_str();\n        LOG_IF_WIN32_BOOL_FALSE(ReportEventW(g_eventLog, EVENTLOG_WARNING_TYPE, 0, MSG_WARNING, nullptr, 1, 0, &warningPtr, nullptr));\n    }\n}\n\nExecutionContext& ExecutionContext::RootContext()\n{\n    if (m_parent == nullptr)\n    {\n        return *this;\n    }\n    else\n    {\n        return m_parent->RootContext();\n    }\n}\n\nconst std::optional<wsl::windows::common::Error>& ExecutionContext::ReportedError() const noexcept\n{\n    if (!m_error.has_value() && m_parent != nullptr)\n    {\n        return m_parent->ReportedError();\n    }\n\n    return m_error;\n}\n\nbool ExecutionContext::ShouldCollectErrorMessage()\n{\n    return g_currentContext != nullptr && g_currentContext->CanCollectUserErrorMessage();\n}\n\nbool ExecutionContext::CanCollectUserWarnings() const\n{\n    return m_warningsFile != nullptr;\n}\n\nbool ExecutionContext::CollectUserWarning(const std::wstring& warning)\n{\n    if (m_warningsFile != nullptr)\n    {\n        fputws(warning.c_str(), m_warningsFile);\n        return true;\n    }\n    else if (m_parent != nullptr)\n    {\n        return m_parent->CollectUserWarning(warning);\n    }\n    else\n    {\n        return false;\n    }\n}\n\nClientExecutionContext::~ClientExecutionContext()\n{\n    if (m_outError.Message != nullptr)\n    {\n        CoTaskMemFree(m_outError.Message);\n    }\n\n    if (m_outError.Warnings != nullptr)\n    {\n        CollectUserWarning(m_outError.Warnings);\n        CoTaskMemFree(m_outError.Warnings);\n    }\n\n    if (m_interactiveWarningsThread.joinable())\n    {\n        m_warningsPipeWrite.reset();\n        m_interactiveWarningsThread.join();\n    }\n}\n\nClientExecutionContext::ClientExecutionContext(bool enableContextualizedErrors) : ExecutionContext(Service)\n{\n    WI_SetFlagIf(m_outError.Flags, LxssExecutionContextFlagsEnableContextualizedErrors, enableContextualizedErrors);\n    WI_SetFlagIf(m_outError.Flags, LxssExecutionContextFlagsEnableUserWarnings, RootContext().CanCollectUserWarnings());\n}\n\nvoid ClientExecutionContext::CollectErrorImpl(HRESULT result)\n{\n    const auto errorContext = CurrentContext() | m_outError.Context;\n    std::optional<std::wstring> message;\n    if (m_outError.Message != nullptr)\n    {\n        WI_ASSERT(WI_IsFlagSet(m_outError.Flags, LxssExecutionContextFlagsEnableContextualizedErrors));\n        message = std::wstring(m_outError.Message);\n    }\n\n    RootContext().CollectErrorImpl(result, errorContext, std::move(message));\n}\n\nvoid ClientExecutionContext::FlushWarnings()\n{\n    if (m_interactiveWarningsThread.joinable())\n    {\n        m_warningsPipeWrite.reset();\n        m_interactiveWarningsThread.join();\n    }\n\n    if (m_outError.Warnings && CollectUserWarning(m_outError.Warnings))\n    {\n        CoTaskMemFree(m_outError.Warnings);\n        m_outError.Warnings = nullptr;\n    }\n}\n\nvoid ClientExecutionContext::EnableInteractiveWarnings()\n{\n    WI_ASSERT(!m_interactiveWarningsThread.joinable());\n\n    wil::unique_handle read;\n    THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&read, &m_warningsPipeWrite, nullptr, 0));\n\n    m_outError.WarningsPipe = HandleToULong(m_warningsPipeWrite.get());\n\n    m_interactiveWarningsThread = std::thread([read = std::move(read)]() {\n        try\n        {\n            wchar_t buffer[1024] = {0};\n\n            DWORD bytesRead{};\n            while (ReadFile(read.get(), buffer, sizeof(buffer) - sizeof(wchar_t), &bytesRead, nullptr) && bytesRead > 0)\n            {\n                const auto endIndex = bytesRead / sizeof(wchar_t);\n                buffer[endIndex] = UNICODE_NULL;\n\n                fwprintf(stderr, L\"%ls\", buffer);\n            }\n        }\n        CATCH_LOG();\n    });\n}\n\nServiceExecutionContext::ServiceExecutionContext(LXSS_ERROR_INFO* outError) noexcept : ExecutionContext(Empty)\n{\n    if (outError != nullptr)\n    {\n        if (WI_IsFlagSet(outError->Flags, LxssExecutionContextFlagsEnableContextualizedErrors))\n        {\n            m_outError = outError;\n\n            if (WI_IsFlagSet(outError->Flags, LxssExecutionContextFlagsEnableUserWarnings))\n            {\n                if (outError->WarningsPipe != 0)\n                {\n                    m_warningsPipe.reset(wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(outError->WarningsPipe)));\n                }\n\n                if (!m_warningsPipe)\n                {\n                    m_warningsString.emplace();\n                }\n            }\n        }\n    }\n}\n\nbool ServiceExecutionContext::CanCollectUserErrorMessage()\n{\n    return m_outError.has_value();\n}\n\nbool ServiceExecutionContext::CollectUserWarning(const std::wstring& warning)\n{\n    if (m_warningsPipe)\n    {\n        LOG_IF_WIN32_BOOL_FALSE(WriteFile(\n            m_warningsPipe.get(), warning.c_str(), gsl::narrow_cast<DWORD>(warning.size() * sizeof(wchar_t)), nullptr, nullptr));\n\n        return true;\n    }\n\n    if (m_warningsString.has_value())\n    {\n        *m_warningsString += warning;\n        return true;\n    }\n    else\n    {\n        return false;\n    }\n}\n\nServiceExecutionContext::~ServiceExecutionContext()\n{\n    if (m_outError.has_value())\n    {\n        if (m_error.has_value())\n        {\n            m_outError.value()->Context = m_error->Context;\n\n            if (m_error->Message.has_value())\n            {\n                m_outError.value()->Message = wil::make_unique_string<wil::unique_cotaskmem_string>(m_error->Message->c_str()).release();\n            }\n        }\n\n        if (m_warningsString.has_value())\n        {\n            m_outError.value()->Warnings = wil::make_unique_string<wil::unique_cotaskmem_string>(m_warningsString->c_str()).release();\n        }\n    }\n}\n\nLXSS_ERROR_INFO* ClientExecutionContext::OutError() noexcept\n{\n    return &m_outError;\n}\n\nvoid wsl::windows::common::SetErrorMessage(std::wstring&& message)\n{\n    if (g_currentContext == nullptr || message.empty())\n    {\n        return; // no context to save the error to or empty message, ignore\n    }\n\n    g_currentContext->SetErrorStringImpl(std::move(message));\n}\n\nvoid wsl::windows::common::SetEventLog(HANDLE eventLog)\n{\n    WI_ASSERT(!g_eventLog);\n\n    g_eventLog = eventLog;\n}\n"
  },
  {
    "path": "src/windows/common/ExecutionContext.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\nnamespace wsl::windows::common {\n\n#define THROW_HR_WITH_USER_ERROR(Result, Message) \\\n    if (wsl::windows::common::ExecutionContext::ShouldCollectErrorMessage()) \\\n    { \\\n        ::wsl::windows::common::SetErrorMessage(Message); \\\n    } \\\n    THROW_HR(Result)\n\n#define EMIT_USER_WARNING(Warning) \\\n    if (::wsl::windows::common::ExecutionContext* context = ::wsl::windows::common::ExecutionContext::Current(); context != nullptr) \\\n    { \\\n        context->EmitUserWarning(Warning); \\\n    }\n\n/* List of ExecutionContext that can be passed to ExecutionContext().\n * Note: ExecutionContext makes the assumption that the parent context always has\n * a lower value than its child context.\n * (for instance RegisterDistro must be smaller than CreateInstance\n * because RegisterDistro is always CreateInstance's parent).\n */\n\nenum Context : ULONGLONG\n{\n    Empty = 0x0,\n    Wsl = 0x1,\n    Wslg = 0x2,\n    Bash = 0x4,\n    WslConfig = 0x8,\n    InstallDistro = 0x10,\n    EnumerateDistros = 0x20,\n    Service = 0x40,\n    RegisterDistro = 0x80,\n    CreateInstance = 0x100,\n    AttachDisk = 0x200,\n    DetachDisk = 0x400,\n    CreateVm = 0x800,\n    ParseConfig = 0x1000,\n    ConfigureNetworking = 0x2000,\n    ConfigureGpu = 0x4000,\n    LaunchProcess = 0x8000,\n    ConfigureDistro = 0x10000,\n    CreateLxProcess = 0x20000,\n    UnregisterDistro = 0x40000,\n    ExportDistro = 0x80000,\n    GetDistroConfiguration = 0x100000,\n    GetDistroId = 0x200000,\n    SetDefaultDistro = 0x400000,\n    SetVersion = 0x800000,\n    TerminateDistro = 0x1000000,\n    RegisterLxBus = 0x2000000,\n    MountDisk = 0x4000000,\n    Plugin = 0x8000000,\n    MoveDistro = 0x10000000,\n    GetDefaultDistro = 0x20000000,\n    DebugShell = 0x40000000,\n    HCS = 0x80000000,\n    HNS = 0x100000000,\n    CallMsi = 0x200000000,\n    Install = 0x4000000000,\n    ReadDistroConfig = 0x8000000000,\n    UpdatePackage = 0x10000000000,\n    QueryLatestGitHubRelease = 0x20000000000,\n    VerifyChecksum = 0x40000000000,\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(Context)\n\nstruct Error\n{\n    HRESULT Code = E_UNEXPECTED;\n    ULONGLONG Context = 0;\n    std::optional<std::wstring> Message;\n};\n\n/*\n * The ExecutionContext class is a tool to automatically contextualize the errors\n * so they are returned to the user (and optionally with a specialized error message).\n *\n * When an ExecutionContext is declared in a scope, it will override g_currentContext (thread-local)\n * and keep a pointer to its parent scope (caller), if any.\n *\n * When an error is reported via wil (THROW_X, or RETURN_X), wil calls ExecutionContext::CollectError()\n * which will save a record of this error with its current scope so it can be properly reported to the user.\n */\n\nclass ExecutionContext\n{\npublic:\n    ExecutionContext(Context context, FILE* warningsFile = nullptr) noexcept;\n    virtual ~ExecutionContext();\n\n    ExecutionContext(const ExecutionContext&) = delete;\n    ExecutionContext(ExecutionContext&&) = delete;\n\n    ExecutionContext& operator=(const ExecutionContext&) = delete;\n    ExecutionContext& operator=(ExecutionContext&&) = delete;\n\n    virtual void CollectErrorImpl(HRESULT result);\n    virtual bool CanCollectUserErrorMessage();\n    bool CanCollectUserWarnings() const;\n    void EmitUserWarning(const std::wstring& warning, const std::source_location& location = std::source_location::current());\n\n    void CollectErrorImpl(HRESULT result, ULONGLONG context, std::optional<std::wstring>&& message);\n\n    const std::optional<Error>& ReportedError() const noexcept;\n\n    void SetErrorStringImpl(std::wstring&& string);\n\n    ULONGLONG CurrentContext() const noexcept;\n\n    static void CollectError(HRESULT error);\n    static bool ShouldCollectErrorMessage();\n    static ExecutionContext* Current();\n\nprotected:\n    ExecutionContext& RootContext();\n    virtual bool CollectUserWarning(const std::wstring& warning);\n    std::optional<Error> m_error;\n    FILE* m_warningsFile = nullptr;\n\nprivate:\n    ExecutionContext* m_parent = nullptr;\n    Context m_context = Context::Empty;\n    std::optional<std::wstring> m_errorString;\n};\n\nclass ClientExecutionContext : public ExecutionContext\n{\npublic:\n    ClientExecutionContext(bool enableContextualizedErrors = true);\n    ~ClientExecutionContext() override;\n\n    ClientExecutionContext(const ClientExecutionContext&) = delete;\n    ClientExecutionContext(ClientExecutionContext&&) = delete;\n\n    ClientExecutionContext& operator=(const ClientExecutionContext&) = delete;\n    ClientExecutionContext& operator=(ClientExecutionContext&&) = delete;\n\n    void CollectErrorImpl(HRESULT result) override;\n\n    void FlushWarnings();\n    void EnableInteractiveWarnings();\n\n    LXSS_ERROR_INFO* OutError() noexcept;\n\nprivate:\n    LXSS_ERROR_INFO m_outError = {};\n\n    wil::unique_handle m_warningsPipeWrite;\n    std::thread m_interactiveWarningsThread;\n};\n\nclass ServiceExecutionContext : public ExecutionContext\n{\npublic:\n    ServiceExecutionContext(LXSS_ERROR_INFO* outError) noexcept;\n    ~ServiceExecutionContext() override;\n\n    ServiceExecutionContext(const ServiceExecutionContext&) = delete;\n    ServiceExecutionContext(ServiceExecutionContext&&) = delete;\n\n    ServiceExecutionContext& operator=(const ServiceExecutionContext&) = delete;\n    ServiceExecutionContext& operator=(ServiceExecutionContext&&) = delete;\n\n    bool CanCollectUserErrorMessage() override;\n\nprotected:\n    virtual bool CollectUserWarning(const std::wstring& warning) override;\n\nprivate:\n    std::optional<LXSS_ERROR_INFO*> m_outError;\n    std::optional<std::wstring> m_warningsString;\n    wil::unique_handle m_warningsPipe;\n};\n\nvoid EnableContextualizedErrors(bool service);\n\nvoid SetErrorMessage(std::wstring&& message);\n\nvoid SetEventLog(HANDLE eventLog);\n\n} // namespace wsl::windows::common"
  },
  {
    "path": "src/windows/common/GnsChannel.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"precomp.h\"\n#include \"lxinitshared.h\"\n#include \"GnsChannel.h\"\n\nusing namespace wsl::shared;\nusing wsl::core::GnsChannel;\n\nGnsChannel::GnsChannel(wil::unique_socket&& socket) : m_channel(std::move(socket), \"GNS\", m_stopEvent.get())\n{\n    WSL_LOG(\"GnsChannel::GnsChannel\", TraceLoggingValue(m_channel.Socket(), \"socket\"));\n}\n\nvoid GnsChannel::SendEndpointState(const hns::HNSEndpoint& Notification)\n{\n    // if we have signaled to stop, block anyone making new calls\n    if (m_stopEvent.is_signaled())\n    {\n        return;\n    }\n\n    Message<LX_GNS_INTERFACE_CONFIGURATION>(LxGnsMessageInterfaceConfiguration, ToJson(Notification));\n}\n\ntemplate <typename TMessage>\nint GnsChannel::MessageReturnResult(LX_MESSAGE_TYPE Type, const std::string& Content, const std::function<void(TMessage&)>& BuildMessage)\n{\n    size_t messageSize;\n    THROW_IF_FAILED(SizeTAdd(offsetof(TMessage, Content), Content.size() + 1, &messageSize));\n\n    // Populate the message that will be sent to gns.\n    std::vector<gsl::byte> buffer(messageSize);\n    const auto messageSpan = gsl::make_span(buffer);\n    auto* message = gslhelpers::get_struct<TMessage>(messageSpan);\n    message->Header.MessageType = Type;\n    message->Header.MessageSize = gsl::narrow_cast<ULONG>(messageSize);\n    if (BuildMessage)\n    {\n        BuildMessage(*message);\n    }\n\n    auto offset = offsetof(TMessage, Content);\n    wsl::shared::string::CopyToSpan(Content, messageSpan, offset);\n    WI_ASSERT(messageSize == offset);\n\n    return m_channel.Transaction<TMessage>(messageSpan).Result;\n}\n\ntemplate <typename TMessage>\nvoid GnsChannel::Message(LX_MESSAGE_TYPE Type, const std::string& Content, const std::function<void(TMessage&)>& BuildMessage)\n{\n    const auto result = MessageReturnResult(Type, Content, BuildMessage);\n\n    THROW_HR_IF_MSG(\n        E_UNEXPECTED,\n        result == ERROR_FATAL_APP_EXIT,\n        \"Did not receive a LX_GNS_RESULT after sending message %hs, type %u\",\n        Content.c_str(),\n        static_cast<uint32_t>(Type));\n\n    THROW_HR_IF_MSG(\n        E_UNEXPECTED,\n        (result != 0),\n        \"Error returned from GNS after sending message %hs, type %u. Result=%i\",\n        Content.c_str(),\n        static_cast<uint32_t>(Type),\n        result);\n}\n\n// the payload is expected to be of type ModifyGuestEndpointSettingRequest\nvoid GnsChannel::SendHnsNotification(_In_ LPCWSTR Notification, const GUID& AdapterId)\n{\n    // if we have signaled to stop, block anyone making new calls\n    if (m_stopEvent.is_signaled())\n    {\n        return;\n    }\n\n    auto AddAdapterId = [&](LX_GNS_NOTIFICATION& Message) { Message.AdapterId = AdapterId; };\n    Message<LX_GNS_NOTIFICATION>(LxGnsMessageNotification, wsl::shared::string::WideToMultiByte(Notification), AddAdapterId);\n}\n\n// Network device messages built from the corresponding serialization functions\n// throws on error\nvoid GnsChannel::SendNetworkDeviceMessage(LX_MESSAGE_TYPE MessageType, LPCWSTR MessageContent)\n{\n    // if we have signaled to stop, block anyone making new calls\n    if (m_stopEvent.is_signaled())\n    {\n        return;\n    }\n\n    WI_ASSERT(\n        MessageType == LxGnsMessageVmNicCreatedNotification || MessageType == LxGnsMessageCreateDeviceRequest ||\n        MessageType == LxGnsMessageModifyGuestDeviceSettingRequest || MessageType == LxGnsMessageLoopbackRoutesRequest ||\n        MessageType == LxGnsMessageDeviceSettingRequest || MessageType == LxGnsMessageInitialIpConfigurationNotification ||\n        MessageType == LxGnsMessageSetupIpv6 || MessageType == LxGnsMessageInterfaceConfiguration ||\n        MessageType == LxGnsMessageNoOp || MessageType == LxGnsMessageGlobalNetFilter ||\n        MessageType == LxGnsMessageInterfaceNetFilter || MessageType == LxGnsMessageConnectTestRequest);\n\n    Message<LX_GNS_JSON_MESSAGE>(MessageType, wsl::shared::string::WideToMultiByte(MessageContent));\n}\n\n// Network device messages built from the corresponding serialization functions\n// throws on error, returns the integer value that was returned from Linux\nint GnsChannel::SendNetworkDeviceMessageReturnResult(LX_MESSAGE_TYPE MessageType, LPCWSTR MessageContent)\n{\n    // if we have signaled to stop, block anyone making new calls\n    if (m_stopEvent.is_signaled())\n    {\n        return ERROR_SHUTDOWN_IN_PROGRESS;\n    }\n\n    WI_ASSERT(\n        MessageType == LxGnsMessageVmNicCreatedNotification || MessageType == LxGnsMessageCreateDeviceRequest ||\n        MessageType == LxGnsMessageModifyGuestDeviceSettingRequest || MessageType == LxGnsMessageLoopbackRoutesRequest ||\n        MessageType == LxGnsMessageDeviceSettingRequest || MessageType == LxGnsMessageInitialIpConfigurationNotification ||\n        MessageType == LxGnsMessageSetupIpv6 || MessageType == LxGnsMessageInterfaceConfiguration ||\n        MessageType == LxGnsMessageNoOp || MessageType == LxGnsMessageGlobalNetFilter ||\n        MessageType == LxGnsMessageInterfaceNetFilter || MessageType == LxGnsMessageConnectTestRequest);\n\n    return MessageReturnResult<LX_GNS_JSON_MESSAGE>(MessageType, wsl::shared::string::WideToMultiByte(MessageContent));\n}\n\nvoid GnsChannel::Stop() const\n{\n    WSL_LOG(\"GnsChannel::Stop\");\n    m_stopEvent.SetEvent();\n}\n"
  },
  {
    "path": "src/windows/common/GnsChannel.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include <wil/resource.h>\n#include \"SocketChannel.h\"\n#include \"hns_schema.h\"\n\nnamespace wsl::core {\n\n// This class manages the hvsocket channel between wslservice and the gns process inside the VM.\n// This channel is used for network configuration inside the guest.\nclass GnsChannel\n{\npublic:\n    GnsChannel(wil::unique_socket&& Socket);\n\n    GnsChannel(const GnsChannel&) = delete;\n\n    GnsChannel& operator=(const GnsChannel&) = delete;\n\n    GnsChannel(GnsChannel&&) = default;\n\n    GnsChannel& operator=(GnsChannel&&) = default;\n\n    void SendEndpointState(const wsl::shared::hns::HNSEndpoint& Notification);\n\n    void SendHnsNotification(LPCWSTR Notification, const GUID& AdapterId);\n\n    void SendNetworkDeviceMessage(LX_MESSAGE_TYPE MessageType, LPCWSTR MessageContent);\n    int SendNetworkDeviceMessageReturnResult(LX_MESSAGE_TYPE MessageType, LPCWSTR MessageContent);\n\n    void Stop() const;\n\nprivate:\n    template <typename T>\n    int MessageReturnResult(LX_MESSAGE_TYPE Type, const std::string& Content, const std::function<void(T&)>& BuildMessage = [](auto&) {});\n\n    template <typename T>\n    void Message(LX_MESSAGE_TYPE Type, const std::string& Content, const std::function<void(T&)>& BuildMessage = [](auto&) {});\n\n    // m_channel depends on m_stopEvent, so m_channel needs to be destroyed first.\n    wil::unique_event m_stopEvent{wil::EventOptions::ManualReset};\n    wsl::shared::SocketChannel m_channel;\n};\n} // namespace wsl::core\n"
  },
  {
    "path": "src/windows/common/GnsPortTrackerChannel.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"precomp.h\"\n#include \"socket.hpp\"\n#include \"GnsPortTrackerChannel.h\"\n\nusing wsl::core::GnsPortTrackerChannel;\n\nGnsPortTrackerChannel::GnsPortTrackerChannel(\n    wil::unique_socket&& Socket,\n    const std::function<int(const SOCKADDR_INET&, int, bool)>& Callback,\n    const std::function<void(const std::string&, bool)>& InterfaceStateCallback) :\n    m_callback(Callback),\n    m_interfaceStateCallback(InterfaceStateCallback),\n    m_channel(std::move(Socket), \"GNSPortTracker\", m_stopEvent.get())\n{\n    m_thread = std::thread{std::bind(&GnsPortTrackerChannel::Run, this)};\n}\n\nGnsPortTrackerChannel::~GnsPortTrackerChannel()\n{\n    LOG_IF_WIN32_BOOL_FALSE(SetEvent(m_stopEvent.get()));\n\n    if (m_thread.joinable())\n    {\n        m_thread.join();\n    }\n}\n\nvoid GnsPortTrackerChannel::Run()\n{\n    try\n    {\n        for (;;)\n        {\n            auto [header, range] = m_channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\n            if (header == nullptr)\n            {\n                return;\n            }\n\n            switch (header->MessageType)\n            {\n            case LxGnsMessagePortMappingRequest:\n            {\n                const auto* message = gslhelpers::try_get_struct<LX_GNS_PORT_ALLOCATION_REQUEST>(range);\n                THROW_HR_IF_MSG(E_UNEXPECTED, !message, \"Unexpected message size: %i\", header->MessageSize);\n\n                m_channel.SendResultMessage<int32_t>(m_callback(ConvertPortRequestToSockAddr(message), message->Protocol, message->Allocate));\n            }\n            break;\n            case LxGnsMessageIfStateChangeRequest:\n            {\n                const auto* message = gslhelpers::try_get_struct<LX_GNS_TUN_BRIDGE_REQUEST>(range);\n                THROW_HR_IF_MSG(E_UNEXPECTED, !message, \"Unexpected message size: %i\", header->MessageSize);\n\n                m_interfaceStateCallback(message->InterfaceName, message->InterfaceUp);\n                m_channel.SendResultMessage<int32_t>(0);\n            }\n            break;\n            default:\n                THROW_HR_MSG(E_UNEXPECTED, \"Unexpected message type: %i\", header->MessageType);\n            }\n        }\n    }\n    CATCH_LOG()\n}\n\nSOCKADDR_INET GnsPortTrackerChannel::ConvertPortRequestToSockAddr(_In_ const LX_GNS_PORT_ALLOCATION_REQUEST* portAllocationRequest)\n{\n    SOCKADDR_INET address{};\n\n    address.si_family = static_cast<uint16_t>(portAllocationRequest->Af);\n\n    if (portAllocationRequest->Af == AF_INET)\n    {\n        IN_ADDR ipv4Addr{};\n        ipv4Addr.S_un.S_addr = portAllocationRequest->Address32[0];\n        IN4ADDR_SETSOCKADDR(&address.Ipv4, &ipv4Addr, portAllocationRequest->Port);\n    }\n    else\n    {\n        IN6_ADDR ipv6Addr{};\n        // Copy 16 bytes that represent IPv6 address\n        memcpy(&ipv6Addr.u, portAllocationRequest->Address32, sizeof(portAllocationRequest->Address32));\n        IN6ADDR_SETSOCKADDR(&address.Ipv6, &ipv6Addr, SCOPEID_UNSPECIFIED_INIT, portAllocationRequest->Port);\n    }\n\n    return address;\n}\n"
  },
  {
    "path": "src/windows/common/GnsPortTrackerChannel.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include <thread>\r\n#include <functional>\r\n#include <wil/resource.h>\r\n#include <lxinitshared.h>\r\n#include \"SocketChannel.h\"\r\n\r\nnamespace wsl::core {\r\nclass GnsPortTrackerChannel\r\n{\r\npublic:\r\n    GnsPortTrackerChannel(\r\n        wil::unique_socket&& Socket,\r\n        const std::function<int(const SOCKADDR_INET&, int, bool)>& Callback,\r\n        const std::function<void(const std::string&, bool)>& InterfaceStateCallback);\r\n\r\n    ~GnsPortTrackerChannel();\r\n\r\n    GnsPortTrackerChannel(const GnsPortTrackerChannel&) = delete;\r\n    GnsPortTrackerChannel(GnsPortTrackerChannel&&) = delete;\r\n\r\n    GnsPortTrackerChannel& operator=(const GnsPortTrackerChannel&) = delete;\r\n    GnsPortTrackerChannel& operator=(GnsPortTrackerChannel&&) = delete;\r\n\r\n    void Run();\r\n\r\nprivate:\r\n    static SOCKADDR_INET ConvertPortRequestToSockAddr(_In_ const LX_GNS_PORT_ALLOCATION_REQUEST* portAllocationRequest);\r\n\r\n    const std::function<int(const SOCKADDR_INET&, int, bool)> m_callback;\r\n    const std::function<void(const std::string&, bool)> m_interfaceStateCallback;\r\n    wil::unique_event m_stopEvent = wil::unique_event{wil::EventOptions::ManualReset};\r\n    wsl::shared::SocketChannel m_channel;\r\n    std::thread m_thread;\r\n};\r\n} // namespace wsl::core\r\n"
  },
  {
    "path": "src/windows/common/GuestDeviceManager.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"precomp.h\"\n#include \"GuestDeviceManager.h\"\n#include \"DeviceHostProxy.h\"\n\nGuestDeviceManager::GuestDeviceManager(_In_ const std::wstring& machineId, _In_ const GUID& runtimeId) :\n    m_machineId(machineId), m_deviceHostSupport(wil::MakeOrThrow<DeviceHostProxy>(machineId, runtimeId))\n{\n}\n\n_Requires_lock_not_held_(m_lock)\nGUID GuestDeviceManager::AddGuestDevice(\n    _In_ const GUID& DeviceId, _In_ const GUID& ImplementationClsid, _In_ PCWSTR AccessName, _In_opt_ PCWSTR Options, _In_ PCWSTR Path, _In_ UINT32 Flags, _In_ HANDLE UserToken)\n{\n    auto guestDeviceLock = m_lock.lock_exclusive();\n    return AddHdvShareWithOptions(DeviceId, ImplementationClsid, AccessName, Options, Path, Flags, UserToken);\n}\n\n_Requires_lock_held_(m_lock)\nGUID GuestDeviceManager::AddHdvShareWithOptions(\n    _In_ const GUID& DeviceId, _In_ const GUID& ImplementationClsid, _In_ PCWSTR AccessName, _In_opt_ PCWSTR Options, _In_ PCWSTR Path, _In_ UINT32 Flags, _In_ HANDLE UserToken)\n{\n    wil::com_ptr<IPlan9FileSystem> server;\n\n    // Options are appended to the name with a semi-colon separator.\n    //  \"name;key1=value1;key2=value2\"\n    // The AddSharePath implementation is responsible for separating them out and interpreting them.\n    std::wstring nameWithOptions{AccessName};\n    if (ARGUMENT_PRESENT(Options))\n    {\n        nameWithOptions += L\";\";\n        nameWithOptions += Options;\n    }\n\n    {\n        auto revert = wil::impersonate_token(UserToken);\n\n        server = GetRemoteFileSystem(ImplementationClsid, c_defaultDeviceTag);\n        if (!server)\n        {\n            server = wil::CoCreateInstance<IPlan9FileSystem>(ImplementationClsid, (CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA));\n            AddRemoteFileSystem(ImplementationClsid, c_defaultDeviceTag.c_str(), server);\n        }\n\n        THROW_IF_FAILED(server->AddSharePath(nameWithOptions.c_str(), Path, Flags));\n    }\n\n    // This requires more privileges than the user may have, so impersonation is disabled.\n    return AddNewDevice(DeviceId, server, AccessName);\n}\n\nGUID GuestDeviceManager::AddNewDevice(_In_ const GUID& deviceId, _In_ const wil::com_ptr<IPlan9FileSystem>& server, _In_ PCWSTR tag)\n{\n    return m_deviceHostSupport->AddNewDevice(deviceId, server, tag);\n}\n\nvoid GuestDeviceManager::AddRemoteFileSystem(_In_ REFCLSID clsid, _In_ PCWSTR tag, _In_ const wil::com_ptr<IPlan9FileSystem>& server)\n{\n    m_deviceHostSupport->AddRemoteFileSystem(clsid, tag, server);\n}\n\nvoid GuestDeviceManager::AddSharedMemoryDevice(_In_ const GUID& ImplementationClsid, _In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb, _In_ HANDLE UserToken)\n{\n    auto guestDeviceLock = m_lock.lock_exclusive();\n    auto objectLifetime = CreateSectionObjectRoot(Path, UserToken);\n\n    // For virtiofs hdv, the flags parameter has been overloaded. Flags are placed in the lower\n    // 16 bits, while the shared memory size in megabytes are placed in the upper 16 bits.\n    static constexpr auto VIRTIO_FS_FLAGS_SHMEM_SIZE_SHIFT = 16;\n    UINT32 flags = (SizeMb << VIRTIO_FS_FLAGS_SHMEM_SIZE_SHIFT);\n    WI_SetFlag(flags, VIRTIO_FS_FLAGS_TYPE_SECTIONS);\n    (void)AddHdvShareWithOptions(VIRTIO_FS_DEVICE_ID, ImplementationClsid, Tag, {}, objectLifetime.Path.c_str(), flags, UserToken);\n    m_objectDirectories.emplace_back(std::move(objectLifetime));\n}\n\nGuestDeviceManager::DirectoryObjectLifetime GuestDeviceManager::CreateSectionObjectRoot(_In_ std::wstring_view RelativeRootPath, _In_ HANDLE UserToken) const\n{\n    auto revert = wil::impersonate_token(UserToken);\n    DWORD sessionId;\n    DWORD bytesWritten;\n    THROW_LAST_ERROR_IF(!GetTokenInformation(GetCurrentThreadToken(), TokenSessionId, &sessionId, sizeof(sessionId), &bytesWritten));\n\n    // /Sessions/1/BaseNamedObjects/WSL/<VM ID>/<Relative Path>\n    std::wstringstream sectionPathBuilder;\n    sectionPathBuilder << L\"\\\\Sessions\\\\\" << sessionId << L\"\\\\BaseNamedObjects\" << L\"\\\\WSL\\\\\" << m_machineId << L\"\\\\\" << RelativeRootPath;\n    auto sectionPath = sectionPathBuilder.str();\n\n    UNICODE_STRING ntPath{};\n    OBJECT_ATTRIBUTES attributes{};\n    attributes.Length = sizeof(OBJECT_ATTRIBUTES);\n    attributes.ObjectName = &ntPath;\n    std::vector<wil::unique_handle> directoryHierarchy;\n    auto remainingPath = std::wstring_view(sectionPath.data(), sectionPath.length());\n    while (remainingPath.length() > 0)\n    {\n        // Find the next path substring, ignoring the root path backslash.\n        auto nextDir = remainingPath;\n        const auto separatorPos = nextDir.find(L\"\\\\\", remainingPath[0] == L'\\\\' ? 1 : 0);\n        if (separatorPos != std::wstring_view::npos)\n        {\n            nextDir = nextDir.substr(0, separatorPos);\n            remainingPath = remainingPath.substr(separatorPos + 1, std::wstring_view::npos);\n\n            // Skip concurrent backslashes.\n            while (remainingPath.length() > 0 && remainingPath[0] == L'\\\\')\n            {\n                remainingPath = remainingPath.substr(1, std::wstring_view::npos);\n            }\n        }\n        else\n        {\n            remainingPath = remainingPath.substr(remainingPath.length(), std::wstring_view::npos);\n        }\n\n        attributes.RootDirectory = directoryHierarchy.size() > 0 ? directoryHierarchy.back().get() : nullptr;\n        ntPath.Buffer = const_cast<PWCH>(nextDir.data());\n        ntPath.Length = sizeof(WCHAR) * gsl::narrow_cast<USHORT>(nextDir.length());\n        ntPath.MaximumLength = ntPath.Length;\n        wil::unique_handle nextHandle;\n        NTSTATUS status = ZwCreateDirectoryObject(&nextHandle, DIRECTORY_ALL_ACCESS, &attributes);\n        if (status == STATUS_OBJECT_NAME_COLLISION)\n        {\n            status = NtOpenDirectoryObject(&nextHandle, MAXIMUM_ALLOWED, &attributes);\n        }\n        THROW_IF_NTSTATUS_FAILED(status);\n        directoryHierarchy.emplace_back(std::move(nextHandle));\n    }\n\n    return {std::move(sectionPath), std::move(directoryHierarchy)};\n}\n\nwil::com_ptr<IPlan9FileSystem> GuestDeviceManager::GetRemoteFileSystem(_In_ REFCLSID clsid, _In_ std::wstring_view tag)\n{\n    return m_deviceHostSupport->GetRemoteFileSystem(clsid, tag);\n}\n\nvoid GuestDeviceManager::Shutdown()\ntry\n{\n    m_deviceHostSupport->Shutdown();\n}\nCATCH_LOG()\n"
  },
  {
    "path": "src/windows/common/GuestDeviceManager.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include \"DeviceHostProxy.h\"\n\n// Flags for virtiofs vdev device creation.\n#define VIRTIO_FS_FLAGS_TYPE_FILES 0x8000\n#define VIRTIO_FS_FLAGS_TYPE_SECTIONS 0x4000\n\ninline const std::wstring c_defaultDeviceTag = L\"default\";\n\n// These device types and class IDs are implemented by the external wsldevicehost vdev.\nDEFINE_GUID(VIRTIO_FS_DEVICE_ID, 0x872270E1, 0xA899, 0x4AF6, 0xB4, 0x54, 0x71, 0x93, 0x63, 0x44, 0x35, 0xAD); // {872270E1-A899-4AF6-B454-7193634435AD}\nDEFINE_GUID(VIRTIO_FS_ADMIN_CLASS_ID, 0x7E6AD219, 0xD1B3, 0x42D5, 0xB8, 0xEE, 0xD9, 0x63, 0x24, 0xE6, 0x4F, 0xF6); // {7E6AD219-D1B3-42D5-B8EE-D96324E64FF6}\nDEFINE_GUID(VIRTIO_FS_CLASS_ID, 0x60285AE6, 0xAAF3, 0x4456, 0xB4, 0x44, 0xA6, 0xC2, 0xD0, 0xDE, 0xDA, 0x38); // {60285AE6-AAF3-4456-B444-A6C2D0DEDA38}\n\nDEFINE_GUID(VIRTIO_NET_DEVICE_ID, 0xF07010D0, 0x0EA9, 0x447F, 0x88, 0xEF, 0xBD, 0x95, 0x2A, 0x4D, 0x2F, 0x14); // {F07010D0-0EA9-447F-88EF-BD952A4D2F14}\nDEFINE_GUID(VIRTIO_NET_CLASS_ID, 0x16479D2E, 0xF0C3, 0x4DBA, 0xBF, 0x7A, 0x04, 0xFF, 0xF0, 0x89, 0x2B, 0x07); // {16479D2E-F0C3-4DBA-BF7A-04FFF0892B07}\n\nDEFINE_GUID(VIRTIO_PMEM_DEVICE_ID, 0xEDBB24BB, 0x5E19, 0x40F4, 0x8A, 0x0F, 0x82, 0x24, 0x31, 0x30, 0x64, 0xFD); // {EDBB24BB-5E19-40F4-8A0F-8224313064FD}\nDEFINE_GUID(VIRTIO_PMEM_CLASS_ID, 0xABB755FC, 0x1B86, 0x4255, 0x83, 0xE2, 0xE5, 0x78, 0x7A, 0xBC, 0xF6, 0xC2); // {ABB755FC-1B86-4255-83E2-E5787ABCF6C2}\n\n//\n// Provides synchronized access to guest device operations.\n//\nclass GuestDeviceManager\n{\npublic:\n    GuestDeviceManager(_In_ const std::wstring& machineId, _In_ const GUID& runtimeId);\n\n    _Requires_lock_not_held_(m_lock)\n    GUID AddGuestDevice(\n        _In_ const GUID& DeviceId,\n        _In_ const GUID& ImplementationClsid,\n        _In_ PCWSTR AccessName,\n        _In_opt_ PCWSTR Options,\n        _In_ PCWSTR Path,\n        _In_ UINT32 Flags,\n        _In_ HANDLE UserToken);\n\n    GUID AddNewDevice(_In_ const GUID& deviceId, _In_ const wil::com_ptr<IPlan9FileSystem>& server, _In_ PCWSTR tag);\n\n    void AddRemoteFileSystem(_In_ REFCLSID clsid, _In_ PCWSTR tag, _In_ const wil::com_ptr<IPlan9FileSystem>& server);\n\n    void AddSharedMemoryDevice(_In_ const GUID& ImplementationClsid, _In_ PCWSTR Tag, _In_ PCWSTR Path, _In_ UINT32 SizeMb, _In_ HANDLE UserToken);\n\n    wil::com_ptr<IPlan9FileSystem> GetRemoteFileSystem(_In_ REFCLSID clsid, _In_ std::wstring_view tag);\n\n    void Shutdown();\n\nprivate:\n    _Requires_lock_held_(m_lock)\n    GUID AddHdvShareWithOptions(\n        _In_ const GUID& DeviceId,\n        _In_ const GUID& ImplementationClsid,\n        _In_ PCWSTR AccessName,\n        _In_opt_ PCWSTR Options,\n        _In_ PCWSTR Path,\n        _In_ UINT32 Flags,\n        _In_ HANDLE UserToken);\n\n    struct DirectoryObjectLifetime\n    {\n        std::wstring Path;\n        // Directory objects are temporary, even if they have children, so need to keep\n        // any created handles open in order for the directory to remain accessible.\n        std::vector<wil::unique_handle> HierarchyLifetimes;\n    };\n\n    DirectoryObjectLifetime CreateSectionObjectRoot(_In_ std::wstring_view RelativeRootPath, _In_ HANDLE UserToken) const;\n\n    wil::srwlock m_lock;\n    std::wstring m_machineId;\n    wil::com_ptr<DeviceHostProxy> m_deviceHostSupport;\n    _Guarded_by_(m_lock) std::vector<DirectoryObjectLifetime> m_objectDirectories;\n};\n"
  },
  {
    "path": "src/windows/common/HandleConsoleProgressBar.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    HandleConsoleProgressBar.cpp\n\nAbstract:\n\n    This file contains the HandleConsoleProgressBar class implementation.\n\n--*/\n\n#include \"precomp.h\"\n\n#include \"HandleConsoleProgressBar.h\"\n\nusing wsl::windows::common::HandleConsoleProgressBar;\n\nHandleConsoleProgressBar::HandleConsoleProgressBar(HANDLE handle, std::wstring&& message, Format format)\n{\n    // If this file isn't a disk file, we can't show actual progress. Just show an indicator in that case\n    LARGE_INTEGER fileSize{};\n    if (GetFileType(handle) != FILE_TYPE_DISK || FAILED(GetFileSizeEx(handle, &fileSize)))\n    {\n        m_progressBar.emplace<ConsoleProgressIndicator>(std::move(message));\n    }\n    else if (format == FileSize)\n    {\n        auto& progressBar = m_progressBar.emplace<ConsoleProgressIndicator>(std::move(message), false);\n\n        m_thread = std::thread([handle, &progressBar, this]() {\n            UpdateFileSize(handle, progressBar);\n            progressBar.End();\n        });\n    }\n    else\n    {\n        WI_ASSERT(format == FilePointer);\n\n        auto& progressBar = m_progressBar.emplace<ConsoleProgressBar>();\n\n        m_thread = std::thread([handle, fileSize, &progressBar, this]() {\n            UpdateProgress(handle, fileSize, progressBar);\n            progressBar.Clear();\n        });\n    }\n}\n\nvoid HandleConsoleProgressBar::UpdateProgress(HANDLE handle, LARGE_INTEGER fileSize, ConsoleProgressBar& progressBar) const\ntry\n{\n    while (!m_event.is_signaled())\n    {\n        LARGE_INTEGER position{};\n        THROW_IF_WIN32_BOOL_FALSE(SetFilePointerEx(handle, LARGE_INTEGER{}, &position, FILE_CURRENT));\n\n        constexpr auto progressResolution = 1000;\n        const auto progressRatio = (position.QuadPart / (double)fileSize.QuadPart) * progressResolution;\n\n        progressBar.Print(static_cast<UINT>(progressRatio), progressResolution);\n        std::this_thread::sleep_for(std::chrono::milliseconds(100));\n    }\n}\nCATCH_LOG();\n\nvoid HandleConsoleProgressBar::UpdateFileSize(HANDLE handle, ConsoleProgressIndicator& progressBar) const\n{\n    try\n    {\n        while (!m_event.is_signaled())\n        {\n            LARGE_INTEGER size{};\n            THROW_IF_WIN32_BOOL_FALSE(GetFileSizeEx(handle, &size));\n\n            progressBar.UpdateProgress(std::format(L\" ({} MB)\", size.QuadPart / _1MB));\n\n            std::this_thread::sleep_for(std::chrono::milliseconds(100));\n        }\n    }\n    CATCH_LOG();\n}\n\nHandleConsoleProgressBar::~HandleConsoleProgressBar()\n{\n    if (m_thread.joinable())\n    {\n        m_event.SetEvent();\n        m_thread.join();\n    }\n}"
  },
  {
    "path": "src/windows/common/HandleConsoleProgressBar.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    HandleConsoleProgressBar.h\n\nAbstract:\n\n    This file contains the definition of the HandleConsoleProgressBar class.\n\n--*/\n\n#pragma once\n\n#include \"ConsoleProgressBar.h\"\n#include \"ConsoleProgressIndicator.h\"\n\nnamespace wsl::windows::common {\nclass HandleConsoleProgressBar\n{\npublic:\n    enum Format\n    {\n        FilePointer,\n        FileSize\n    };\n\n    HandleConsoleProgressBar(HANDLE handle, std::wstring&& message, Format format = FilePointer);\n    HandleConsoleProgressBar(const HandleConsoleProgressBar&) = delete;\n    HandleConsoleProgressBar(HandleConsoleProgressBar&&) = delete;\n\n    ~HandleConsoleProgressBar();\n\n    HandleConsoleProgressBar& operator=(const HandleConsoleProgressBar&) = delete;\n    HandleConsoleProgressBar& operator=(HandleConsoleProgressBar&&) = delete;\n\nprivate:\n    void UpdateProgress(HANDLE handle, LARGE_INTEGER totalBytes, ConsoleProgressBar& progressBar) const;\n    void UpdateFileSize(HANDLE handle, ConsoleProgressIndicator& progressBar) const;\n\n    wil::unique_event m_event{wil::EventOptions::ManualReset};\n    std::thread m_thread;\n    std::variant<ConsoleProgressBar, ConsoleProgressIndicator> m_progressBar;\n};\n\n} // namespace wsl::windows::common"
  },
  {
    "path": "src/windows/common/INetworkingEngine.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n#include \"WslCoreNetworkEndpointSettings.h\"\n\nnamespace wsl::core {\n\nclass INetworkingEngine\n{\npublic:\n    virtual ~INetworkingEngine() = default;\n    virtual void Initialize() = 0;\n    virtual void TraceLoggingRundown() noexcept = 0;\n    virtual void FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message) = 0;\n    virtual void StartPortTracker(wil::unique_socket&& socket) = 0;\n};\n} // namespace wsl::core\n"
  },
  {
    "path": "src/windows/common/Localization.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Localization.cpp\n\nAbstract:\n\n    This file contains the class to format localized strings.\n\n--*/\n\n#include \"precomp.h\"\n#include \"Localization.h\"\n\nextern bool g_runningInService;\n\nnamespace {\n\nstd::vector<std::wstring> GetUserLanguagesImpl()\n{\n    DWORD count{};\n    DWORD bufferSize{};\n    GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, nullptr, &bufferSize);\n\n    std::vector<wchar_t> buffer(bufferSize, '\\0');\n    if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, buffer.data(), &bufferSize))\n    {\n        LOG_LAST_ERROR_MSG(\"GetUserDefaultLocaleName failed\");\n        return {L\"\"};\n    }\n\n    std::vector<std::wstring> languages;\n    for (size_t i = 0; buffer[i] != '\\0'; i += wcslen(&buffer[i]) + 1)\n    {\n        languages.emplace_back(&buffer[i]);\n    }\n\n    return languages;\n}\n\nstd::vector<std::wstring> GetUserLanguages(bool impersonate)\n{\n    if (g_runningInService)\n    {\n        // N.B. If we're in the service, the locale needs to be queried every time since different users\n        // can have different language configurations.\n        std::optional<wil::unique_coreverttoself_call> revert;\n        if (impersonate)\n        {\n            // If we're running in wslservice.exe, impersonation is needed to get the correct locale\n            try\n            {\n                revert = wil::CoImpersonateClient();\n            }\n            catch (...)\n            {\n                // Continue if this failed so we fall back to the machine's locale\n                LOG_CAUGHT_EXCEPTION();\n            }\n        }\n\n        return GetUserLanguagesImpl();\n    }\n    else\n    {\n        static std::vector<std::wstring> languages;\n        static std::once_flag flag;\n        std::call_once(flag, [&]() { languages = GetUserLanguagesImpl(); });\n\n        return languages;\n    }\n}\n} // namespace\n\nLPCWSTR wsl::shared::Localization::LookupString(const std::vector<std::pair<std::wstring, LPCWSTR>>& strings, Options options)\n{\n    WI_ASSERT(!strings.empty());\n\n    try\n    {\n        for (const auto& language : GetUserLanguages(options != Options::DontImpersonate && g_runningInService))\n        {\n            for (const auto& e : strings)\n            {\n                if (e.first == language)\n                {\n                    return e.second;\n                }\n            }\n        }\n    }\n    catch (...)\n    {\n        LOG_CAUGHT_EXCEPTION();\n    }\n\n    // Default to English is string is not found (English is always the first entry)\n    return strings[0].second;\n}\n"
  },
  {
    "path": "src/windows/common/LxssMessagePort.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssMessagePort.cpp\n\nAbstract:\n\n    This file contains a wrapper class for LxBus message ports.\n\n--*/\n\n#include \"precomp.h\"\n#include \"LxssMessagePort.h\"\n#include \"LxssServerPort.h\"\n\n// Defines.\n\n#define LAUNCH_PROCESS_DEFAULT_BUFFER_SIZE 1024\n\nLxssMessagePort::LxssMessagePort(_In_ HANDLE MessagePort) : m_messagePort(MessagePort), m_messageEvent(wil::EventOptions::None)\n{\n    //\n    // N.B. The class takes ownership of the handle.\n    //\n}\n\nLxssMessagePort::LxssMessagePort(_In_ LxssMessagePort&& Source) :\n    m_messagePort(std::move(Source.m_messagePort)),\n    m_messageEvent(std::move(Source.m_messageEvent)),\n    m_serverPort(std::move(Source.m_serverPort))\n{\n}\n\nLxssMessagePort::LxssMessagePort(_In_ std::unique_ptr<LxssMessagePort>&& SourcePointer) :\n    LxssMessagePort(std::move(*SourcePointer.get()))\n{\n}\n\nstd::shared_ptr<LxssPort> LxssMessagePort::CreateSessionLeader(_In_ HANDLE ClientProcess)\n{\n    THROW_HR_IF(E_UNEXPECTED, (!m_serverPort));\n\n    const LXBUS_IPC_MESSAGE_MARSHAL_CONSOLE_DATA Data{HandleToUlong(ClientProcess)};\n\n    const LXBUS_IPC_CONSOLE_ID MarshalId = this->MarshalConsole(&Data);\n    auto ReleaseConsole = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { this->ReleaseConsole(MarshalId); });\n\n    LX_INIT_CREATE_SESSION Message{{LxInitMessageCreateSession, sizeof(Message)}, MarshalId};\n\n    Send(&Message, sizeof(Message));\n    auto LocalMessagePort = m_serverPort->WaitForConnection(c_defaultMessageTimeout);\n    ReleaseConsole.release();\n    return LocalMessagePort;\n}\n\nwil::unique_handle LxssMessagePort::CreateUnnamedServer(_Out_ PLXBUS_SERVER_ID ServerId) const\n{\n    LXBUS_IPC_MESSAGE_CREATE_UNNAMED_SERVER_PARAMETERS Parameters;\n    THROW_IF_NTSTATUS_FAILED(LxBusClientCreateUnnamedServer(m_messagePort.get(), &Parameters));\n\n    *ServerId = Parameters.Output.ServerId;\n    return wil::unique_handle(ULongToHandle(Parameters.Output.ServerPort));\n}\n\nvoid LxssMessagePort::DisconnectConsole(_In_ HANDLE ClientProcess)\n{\n    LXBUS_IPC_MESSAGE_DISCONNECT_CONSOLE_PARAMETERS Parameters;\n    NTSTATUS Status;\n    Parameters.Input.ConsoleData.ClientProcess = HandleToUlong(ClientProcess);\n    Status = LxBusClientDisconnectConsole(m_messagePort.get(), &Parameters);\n\n    // Console disconnect is expected to fail in two cases:\n    //     1. The instance has been torn down: STATUS_NOT_FOUND\n    //     2. The tty device that had the console reference has already been\n    //        closed: STATUS_NO_SUCH_DEVICE\n    if ((Status != STATUS_NOT_FOUND) && (Status != STATUS_NO_SUCH_DEVICE))\n    {\n        THROW_IF_NTSTATUS_FAILED(Status);\n    }\n}\n\nwil::cs_leave_scope_exit LxssMessagePort::Lock()\n{\n    return m_lock.lock();\n}\n\nLXBUS_IPC_CONSOLE_ID\nLxssMessagePort::MarshalConsole(_In_ PCLXBUS_IPC_MESSAGE_MARSHAL_CONSOLE_DATA ConsoleData) const\n{\n    LXBUS_IPC_MESSAGE_MARSHAL_CONSOLE_PARAMETERS Parameters;\n    Parameters.Input.ConsoleData = *ConsoleData;\n    THROW_IF_NTSTATUS_FAILED(LxBusClientMarshalConsole(m_messagePort.get(), &Parameters));\n\n    return Parameters.Output.ConsoleId;\n}\n\nLXBUS_IPC_FORK_TOKEN_ID\nLxssMessagePort::MarshalForkToken(_In_ HANDLE TokenHandle) const\n{\n    LXBUS_IPC_MESSAGE_MARSHAL_FORK_TOKEN_PARAMETERS Parameters;\n    Parameters.Input.TokenHandle = HandleToULong(TokenHandle);\n    THROW_IF_NTSTATUS_FAILED(LxBusClientMarshalForkToken(m_messagePort.get(), &Parameters));\n\n    return Parameters.Output.ForkTokenId;\n}\n\nLXBUS_IPC_HANDLE_ID\nLxssMessagePort::MarshalHandle(_In_ PCLXBUS_IPC_MESSAGE_MARSHAL_HANDLE_DATA HandleData) const\n{\n    LXBUS_IPC_MESSAGE_MARSHAL_HANDLE_PARAMETERS Parameters;\n    Parameters.Input.HandleData = *HandleData;\n    THROW_IF_NTSTATUS_FAILED(LxBusClientMarshalHandle(m_messagePort.get(), &Parameters));\n\n    return Parameters.Output.HandleId;\n}\n\nLXBUS_IPC_PROCESS_ID\nLxssMessagePort::MarshalProcess(_In_ HANDLE ProcessHandle, _In_ bool TerminateOnClose) const\n{\n    LXBUS_IPC_MESSAGE_MARSHAL_PROCESS_PARAMETERS Parameters;\n    Parameters.Input.Process = HandleToULong(ProcessHandle);\n    if (TerminateOnClose)\n    {\n        Parameters.Input.Flags = LXBUS_IPC_MARSHAL_PROCESS_FLAG_TERMINATE_ON_CLOSE;\n    }\n\n    THROW_IF_NTSTATUS_FAILED(LxBusClientMarshalProcess(m_messagePort.get(), &Parameters));\n\n    return Parameters.Output.ProcessId;\n}\n\nvoid LxssMessagePort::Receive(_Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_opt_ HANDLE, _In_ DWORD Timeout)\n{\n    IO_STATUS_BLOCK IoStatus;\n    ULONG SizeReceived;\n    const NTSTATUS Status =\n        LxBusClientReceiveMessageAsync(m_messagePort.get(), Buffer, Length, &SizeReceived, &IoStatus, m_messageEvent.get());\n    THROW_IF_NTSTATUS_FAILED(Status);\n\n    if (Status == STATUS_PENDING)\n    {\n        WaitForMessage(&IoStatus, Timeout);\n    }\n    else\n    {\n        WI_ASSERT(Status == STATUS_SUCCESS);\n    }\n\n    THROW_IF_NTSTATUS_FAILED(IoStatus.Status);\n    THROW_HR_IF(E_UNEXPECTED, ((NT_SUCCESS(IoStatus.Status)) && (Length != static_cast<ULONG>(IoStatus.Information))));\n\n    return;\n}\n\nstd::vector<gsl::byte> LxssMessagePort::Receive(DWORD Timeout)\n{\n    IO_STATUS_BLOCK IoStatus;\n    std::vector<gsl::byte> Message;\n    ULONG SizeReceived;\n    NTSTATUS Status;\n    Message.resize(LAUNCH_PROCESS_DEFAULT_BUFFER_SIZE);\n    for (;;)\n    {\n        Status = LxBusClientReceiveMessageAsync(\n            m_messagePort.get(), Message.data(), static_cast<ULONG>(Message.size()), &SizeReceived, &IoStatus, m_messageEvent.get());\n\n        if (Status == STATUS_PENDING)\n        {\n            WaitForMessage(&IoStatus, Timeout);\n            Status = IoStatus.Status;\n            SizeReceived = static_cast<ULONG>(IoStatus.Information);\n        }\n\n        //\n        // Grow the buffer if it was not large enough.\n        //\n        // N.B. When a provided buffer is too small, LxBus will write the\n        //      required size of the buffer as a SIZE_T into the beginning of\n        //      the buffer.\n        //\n\n        if (Status == STATUS_BUFFER_TOO_SMALL)\n        {\n            Message.resize(*((PSIZE_T)Message.data()));\n        }\n        else\n        {\n            break;\n        }\n    }\n\n    THROW_IF_NTSTATUS_FAILED(Status);\n\n    //\n    // Resize the buffer to be the size of the received message.\n    //\n\n    Message.resize(SizeReceived);\n    return Message;\n}\n\nvoid LxssMessagePort::ReleaseConsole(_In_ LXBUS_IPC_CONSOLE_ID ConsoleId) const\n{\n    LXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters;\n    Parameters.Input.Id.Console = ConsoleId;\n    Parameters.Input.Type = LxBusIpcReleaseTypeConsole;\n    THROW_IF_NTSTATUS_FAILED(LxBusClientReleaseConsole(m_messagePort.get(), &Parameters));\n}\n\nvoid LxssMessagePort::ReleaseForkToken(_In_ LXBUS_IPC_FORK_TOKEN_ID ForkTokenId) const\n{\n    LXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters;\n    Parameters.Input.Id.Token = ForkTokenId;\n    Parameters.Input.Type = LxBusIpcReleaseTypeForkToken;\n    THROW_IF_NTSTATUS_FAILED(LxBusClientReleaseHandle(m_messagePort.get(), &Parameters));\n}\n\nvoid LxssMessagePort::ReleaseHandle(_In_ LXBUS_IPC_HANDLE_ID HandleId) const\n{\n    LXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters;\n    Parameters.Input.Id.Handle = HandleId;\n    Parameters.Input.Type = LxBusIpcReleaseTypeHandle;\n    THROW_IF_NTSTATUS_FAILED(LxBusClientReleaseHandle(m_messagePort.get(), &Parameters));\n}\n\nvoid LxssMessagePort::Send(_In_reads_bytes_(Length) PVOID Buffer, _In_ ULONG Length)\n{\n    IO_STATUS_BLOCK IoStatus;\n    const NTSTATUS Status = LxBusClientSendMessageAsync(m_messagePort.get(), Buffer, Length, &IoStatus, m_messageEvent.get());\n    THROW_IF_NTSTATUS_FAILED(Status);\n\n    if (Status == STATUS_PENDING)\n    {\n        WaitForMessage(&IoStatus);\n    }\n    else\n    {\n        WI_ASSERT(Status == STATUS_SUCCESS);\n    }\n\n    THROW_IF_NTSTATUS_FAILED(IoStatus.Status);\n\n    WI_ASSERT((Status != STATUS_SUCCESS) || (Length == IoStatus.Information));\n}\n\nvoid LxssMessagePort::SetServerPort(_In_ const std::shared_ptr<LxssServerPort>& ServerPort)\n{\n    m_serverPort = ServerPort;\n}\n\nwil::unique_handle LxssMessagePort::UnmarshalProcess(_In_ LXBUS_IPC_PROCESS_ID ProcessId) const\n{\n    LXBUS_IPC_MESSAGE_UNMARSHAL_PROCESS_PARAMETERS Parameters;\n    Parameters.Input.ProcessId = ProcessId;\n    THROW_IF_NTSTATUS_FAILED(LxBusClientUnmarshalProcess(m_messagePort.get(), &Parameters));\n\n    wil::unique_handle ProcessHandle(ULongToHandle(Parameters.Output.ProcessHandle));\n    return ProcessHandle;\n}\n\nwil::unique_handle LxssMessagePort::UnmarshalVfsFile(_In_ LXBUS_IPC_HANDLE_ID VfsFileId) const\n{\n    LXBUS_IPC_MESSAGE_UNMARSHAL_VFS_FILE_PARAMETERS Parameters;\n    Parameters.Input.VfsFileId = VfsFileId;\n    THROW_IF_NTSTATUS_FAILED(LxBusClientUnmarshalVfsFile(m_messagePort.get(), &Parameters));\n\n    wil::unique_handle ProcessHandle(ULongToHandle(Parameters.Output.Handle));\n    return ProcessHandle;\n}\n\nvoid LxssMessagePort::WaitForMessage(_In_ PIO_STATUS_BLOCK IoStatus, _In_ DWORD Timeout) const\n{\n    const DWORD WaitStatus = WaitForSingleObject(m_messageEvent.get(), Timeout);\n    if (WaitStatus == WAIT_TIMEOUT)\n    {\n        IO_STATUS_BLOCK IoStatusCancel;\n        const NTSTATUS Status = NtCancelIoFileEx(m_messagePort.get(), IoStatus, &IoStatusCancel);\n\n        WI_ASSERT((Status == STATUS_SUCCESS) || (Status == STATUS_NOT_FOUND));\n\n        WI_VERIFY(WaitForSingleObject(m_messageEvent.get(), Timeout) == WAIT_OBJECT_0);\n    }\n    else\n    {\n        WI_ASSERT(WaitStatus == WAIT_OBJECT_0);\n    }\n}\n"
  },
  {
    "path": "src/windows/common/LxssMessagePort.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssMessagePort.h\n\nAbstract:\n\n    This file contains declarations for the message port wrapper class.\n\n--*/\n\n#pragma once\n\n#include \"LxssPort.h\"\n\nclass LxssServerPort;\n\nclass LxssMessagePort : public LxssPort\n{\npublic:\n    static inline DWORD c_defaultMessageTimeout = 30000;\n\n    LxssMessagePort(_In_ HANDLE MessagePort);\n    LxssMessagePort(_In_ LxssMessagePort&& Source);\n    LxssMessagePort(_In_ std::unique_ptr<LxssMessagePort>&& SourcePointer);\n    virtual ~LxssMessagePort() = default;\n\n    std::shared_ptr<LxssPort> CreateSessionLeader(_In_ HANDLE ClientProcess) override;\n    void DisconnectConsole(_In_ HANDLE ClientProcess) override;\n    wil::cs_leave_scope_exit Lock() override;\n    void Receive(_Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_opt_ HANDLE ClientProcess = nullptr, _In_ DWORD Timeout = INFINITE) override;\n    void Send(_In_reads_bytes_(Length) PVOID Buffer, _In_ ULONG Length) override;\n\n    wil::unique_handle CreateUnnamedServer(_Out_ PLXBUS_SERVER_ID ServerId) const;\n\n    LXBUS_IPC_CONSOLE_ID\n    MarshalConsole(_In_ PCLXBUS_IPC_MESSAGE_MARSHAL_CONSOLE_DATA ConsoleData) const;\n\n    LXBUS_IPC_FORK_TOKEN_ID\n    MarshalForkToken(_In_ HANDLE TokenHandle) const;\n\n    LXBUS_IPC_HANDLE_ID\n    MarshalHandle(_In_ PCLXBUS_IPC_MESSAGE_MARSHAL_HANDLE_DATA HandleData) const;\n\n    LXBUS_IPC_PROCESS_ID\n    MarshalProcess(_In_ HANDLE ProcessHandle, _In_ bool TerminateOnClose) const;\n\n    std::vector<gsl::byte> Receive(DWORD Timeout = c_defaultMessageTimeout);\n\n    void ReleaseConsole(_In_ LXBUS_IPC_CONSOLE_ID ConsoleId) const;\n\n    void ReleaseForkToken(_In_ LXBUS_IPC_FORK_TOKEN_ID HandleId) const;\n\n    void ReleaseHandle(_In_ LXBUS_IPC_HANDLE_ID HandleId) const;\n\n    void SetServerPort(_In_ const std::shared_ptr<LxssServerPort>& ServerPort);\n\n    wil::unique_handle UnmarshalProcess(_In_ LXBUS_IPC_PROCESS_ID ProcessId) const;\n\n    wil::unique_handle UnmarshalVfsFile(_In_ LXBUS_IPC_HANDLE_ID VfsFileId) const;\n\nprivate:\n    void WaitForMessage(_In_ PIO_STATUS_BLOCK IoStatus, _In_ DWORD Timeout = INFINITE) const;\n\n    wil::critical_section m_lock;\n    wil::unique_handle m_messagePort;\n    wil::unique_event m_messageEvent;\n    std::shared_ptr<LxssServerPort> m_serverPort;\n};\n"
  },
  {
    "path": "src/windows/common/LxssPort.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssPort.h\n\nAbstract:\n\n    This file contains declarations for the port abstract base class.\n\n--*/\n\n#pragma once\n\nclass LxssPort\n{\npublic:\n    virtual std::shared_ptr<LxssPort> CreateSessionLeader(_In_ HANDLE ClientProcess) = 0;\n    virtual void DisconnectConsole(_In_ HANDLE ClientProcess) = 0;\n    virtual wil::cs_leave_scope_exit Lock() = 0;\n    virtual void Receive(_Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_opt_ HANDLE ClientProcess = nullptr, _In_ DWORD Timeout = INFINITE) = 0;\n    virtual void Send(_In_reads_bytes_(Length) PVOID Buffer, _In_ ULONG Length) = 0;\n};\n"
  },
  {
    "path": "src/windows/common/LxssSecurity.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Security.cpp\n\nAbstract:\n\n    This file contains user security function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"LxssSecurity.h\"\n\nusing namespace Microsoft::WRL;\n\nvoid Security::InitializeInstanceJob(_In_ HANDLE jobHandle)\n{\n    // Set the job limit flags.\n    //\n    // N.B. The kill on close flag is required to convert the job to a silo.\n    JOBOBJECT_EXTENDED_LIMIT_INFORMATION limitInfo = {};\n    limitInfo.BasicLimitInformation.LimitFlags = (JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE);\n    THROW_IF_WIN32_BOOL_FALSE(SetInformationJobObject(jobHandle, JobObjectExtendedLimitInformation, &limitInfo, sizeof(limitInfo)));\n\n    // Turn on timer-virtualization for this job.\n    BOOLEAN enableTimerVirtualization = TRUE;\n    THROW_IF_WIN32_BOOL_FALSE(SetInformationJobObject(\n        jobHandle, JobObjectTimerVirtualizationInformation, &enableTimerVirtualization, sizeof(enableTimerVirtualization)));\n\n    // Convert the job to a silo. This allows processes from multiple sessions\n    // in the same job object.\n    THROW_IF_WIN32_BOOL_FALSE(SetInformationJobObject(jobHandle, JobObjectCreateSilo, nullptr, 0));\n}\n\nbool Security::IsTokenLocalAdministrator(_In_ HANDLE token)\n{\n    auto [sid, buffer] =\n        wsl::windows::common::security::CreateSid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);\n\n    BOOL member{};\n    THROW_IF_WIN32_BOOL_FALSE(::CheckTokenMembership(token, sid, &member));\n\n    return member;\n}\n"
  },
  {
    "path": "src/windows/common/LxssSecurity.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Security.h\n\nAbstract:\n\n    This file contains user security function declarations.\n\n--*/\n\n#pragma once\n\n#include <xstring>\n#include \"wrl/client.h\"\n#include \"wil/resource.h\"\n\nnamespace Security {\n\n/// <summary>\n/// Initializes the job object for a instance.\n/// </summary>\nvoid InitializeInstanceJob(_In_ HANDLE jobHandle);\n\n/// <summary>\n/// Returns true if the provided token is a member of the local administrators group\n/// </summary>\nbool IsTokenLocalAdministrator(_In_ HANDLE token);\n} // namespace Security\n"
  },
  {
    "path": "src/windows/common/LxssServerPort.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssServerPort.cpp\n\nAbstract:\n\n    This file contains a wrapper class for LxBus server ports.\n\n--*/\n\n#include \"precomp.h\"\n#include \"LxssServerPort.h\"\n#include \"LxssMessagePort.h\"\n\nLxssServerPort::LxssServerPort()\n{\n}\n\nLxssServerPort::LxssServerPort(_In_ wil::unique_handle&& ServerPortHandle) : m_serverPort(std::move(ServerPortHandle))\n{\n}\n\nvoid LxssServerPort::RegisterLxBusServer(_In_ const wil::unique_handle& InstanceHandle, _In_ LPCSTR ServerName)\n{\n    WI_ASSERT(!m_serverPort);\n\n    LXBUS_REGISTER_SERVER_PARAMETERS RegisterServer = {};\n    RegisterServer.Input.ServerName = ServerName;\n    THROW_IF_NTSTATUS_FAILED(LxBusClientRegisterServer(InstanceHandle.get(), &RegisterServer));\n\n    m_serverPort.reset(UlongToHandle(RegisterServer.Output.ServerPort));\n}\n\nHANDLE\nLxssServerPort::ReleaseServerPort()\n{\n    return m_serverPort.release();\n}\n\nstd::unique_ptr<LxssMessagePort> LxssServerPort::WaitForConnection(_In_ ULONG TimeoutMs)\n{\n    std::unique_ptr<LxssMessagePort> MessagePort;\n    THROW_IF_NTSTATUS_FAILED(WaitForConnectionNoThrow(&MessagePort, TimeoutMs));\n\n    return MessagePort;\n}\n\nNTSTATUS\nLxssServerPort::WaitForConnectionNoThrow(_Out_ std::unique_ptr<LxssMessagePort>* MessagePort, _In_ ULONG TimeoutMs) const\n{\n    LXBUS_IPC_SERVER_WAIT_FOR_CONNECTION_PARAMETERS Params = {};\n    Params.Input.TimeoutMs = TimeoutMs;\n    const NTSTATUS Status = LxBusClientWaitForConnection(m_serverPort.get(), &Params);\n    if (NT_SUCCESS(Status))\n    {\n        *MessagePort = std::make_unique<LxssMessagePort>(UlongToHandle(Params.Output.MessagePort));\n    }\n\n    return Status;\n}\n"
  },
  {
    "path": "src/windows/common/LxssServerPort.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssServerPort.h\n\nAbstract:\n\n    This file contains declarations for the server port wrapper class.\n\n--*/\n\n#pragma once\n\nclass LxssMessagePort;\n\nclass LxssServerPort\n{\npublic:\n    LxssServerPort();\n\n    LxssServerPort(_In_ wil::unique_handle&& ServerPortHandle);\n\n    void RegisterLxBusServer(_In_ const wil::unique_handle& InstanceHandle, _In_ LPCSTR ServerName);\n\n    HANDLE\n    ReleaseServerPort();\n\n    std::unique_ptr<LxssMessagePort> WaitForConnection(_In_ ULONG TimeoutMs = LXBUS_IPC_INFINITE_TIMEOUT);\n\n    NTSTATUS\n    WaitForConnectionNoThrow(_Out_ std::unique_ptr<LxssMessagePort>* MessagePort, _In_ ULONG TimeoutMs = LXBUS_IPC_INFINITE_TIMEOUT) const;\n\nprivate:\n    wil::unique_handle m_serverPort;\n};\n"
  },
  {
    "path": "src/windows/common/NatNetworking.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"precomp.h\"\n#include \"NatNetworking.h\"\n#include \"WslCoreNetworkEndpointSettings.h\"\n#include \"WslCoreHostDnsInfo.h\"\n#include \"Stringify.h\"\n#include \"WslCoreFirewallSupport.h\"\n#include \"hcs.hpp\"\n\nusing namespace wsl::core::networking;\nusing namespace wsl::windows::common::stringify;\nusing namespace wsl::windows::common::string;\nusing namespace wsl::windows::common::hcs;\nusing namespace wsl::shared;\nusing wsl::core::NatNetworking;\nusing wsl::windows::common::Context;\nusing wsl::windows::common::ExecutionContext;\nusing wsl::windows::common::hcs::unique_hcn_endpoint;\n\n// This static list is used to keep of which endpoints are in use by other users.\n// It's needed because when we see an endpoint with the same ip address we want,\n// we have no way to differentiate between an endpoint that we previously used\n// that didn't get deleted, and an endpoint actively in use by another user.\nstatic wil::srwlock g_endpointsInUseLock;\nstatic std::vector<GUID> g_endpointsInUse;\n\nNatNetworking::NatNetworking(\n    HCS_SYSTEM system,\n    wsl::windows::common::hcs::unique_hcn_network&& network,\n    GnsChannel&& gnsChannel,\n    Config& config,\n    wil::unique_socket&& dnsHvsocket,\n    LPCWSTR dnsOptions) :\n    m_system(system), m_config(config), m_network(std::move(network)), m_dnsOptions(dnsOptions), m_gnsChannel(std::move(gnsChannel))\n{\n    m_connectivityTelemetryEnabled = config.EnableTelemetry && !WslTraceLoggingShouldDisableTelemetry();\n\n    if (dnsHvsocket)\n    {\n        // Create the DNS resolver used for DNS tunneling.\n        networking::DnsResolverFlags resolverFlags{};\n        WI_SetFlagIf(resolverFlags, networking::DnsResolverFlags::BestEffortDnsParsing, m_config.BestEffortDnsParsing);\n\n        m_dnsTunnelingResolver.emplace(std::move(dnsHvsocket), resolverFlags);\n\n        m_dnsTunnelingIpAddress = wsl::windows::common::string::IntegerIpv4ToWstring(config.DnsTunnelingIpAddress.value());\n    }\n    else if (!config.EnableDnsProxy)\n    {\n        // EnableDnsProxy indicates to use the DNS/NAT shared access service to proxy DNS requests\n        // If this is false then wsl will assign a prioritized set of DNS servers into the Linux container\n        // prioritized means:\n        // - can only set 3 DNS servers (Linux limitation)\n        // - when there are multiple host connected interfaces, we need to use the DNS servers from the most-likely-to-be-used interface on the host\n        m_useMirrorDnsSettings = true;\n    }\n}\n\nNatNetworking::~NatNetworking()\n{\n    // Stop DNS suffix change notifications first, as those can call into the GNS channel.\n    m_dnsSuffixRegistryWatcher.reset();\n\n    // Stop the GNS channel to unblock any stuck communications with the guest\n    // calling this before m_connectivityTelemetry.Reset() to unblock that callback if it's attempting a connectivity request in Linux\n    m_gnsChannel.Stop();\n\n    // Stop the telemetry timer which could queue work to linux (through m_gnsChannel)\n    m_connectivityTelemetry.Reset();\n\n    // Unregister the network notification callback to prevent notifications from running while the remainder of the object is destroyed.\n    m_networkNotifyHandle.reset();\n\n    auto lock = g_endpointsInUseLock.lock_exclusive();\n    auto eraseRange = std::ranges::remove(g_endpointsInUse, m_endpoint.Id);\n    g_endpointsInUse.erase(eraseRange.begin(), eraseRange.end());\n}\n\nvoid NatNetworking::TelemetryConnectionCallback(NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) noexcept\ntry\n{\n    WSL_LOG(\"NatNetworking::TelemetryConnectionCallback\");\n\n    // if this is the inital callback for checking container connectivity, push this through as telemetry, so we can observe the time-to-connect\n    if ((telemetryCounter == 1) || (hostConnectivity & NLM_CONNECTIVITY_IPV4_INTERNET))\n    {\n        int returnedIPv4Value{};\n        const auto requestStatus = wil::ResultFromException([&] {\n            const auto lock = m_lock.lock_exclusive();\n            returnedIPv4Value = m_gnsChannel.SendNetworkDeviceMessageReturnResult(LxGnsMessageConnectTestRequest, c_ipv4TestRequestTarget);\n        });\n\n        // make the same connect requests as we just requested from the container\n        const auto hostConnCheckResult = wsl::shared::conncheck::CheckConnection(c_ipv4TestRequestTargetA, nullptr, \"80\");\n        const auto WindowsIpv4ConnCheckStatus = static_cast<uint32_t>(hostConnCheckResult.Ipv4Status);\n        const auto WindowsIpv6ConnCheckStatus = static_cast<uint32_t>(hostConnCheckResult.Ipv6Status);\n\n        const auto WindowsIPv4NlmConnectivityLevel = ConnectivityTelemetry::WindowsIPv4NlmConnectivityLevel(hostConnectivity);\n        const auto WindowsIPv6NlmConnectivityLevel = ConnectivityTelemetry::WindowsIPv6NlmConnectivityLevel(hostConnectivity);\n        const auto LinuxIPv4ConnCheckStatus = ConnectivityTelemetry::LinuxIPv4ConnCheckResult(returnedIPv4Value);\n        // NAT doesn't have an IPv6 result because NAT is only IPv4 -- 2 == failed to connect\n        constexpr auto LinuxIPv6ConnCheckStatus = 2;\n\n        const auto timeFromObjectCreation = std::chrono::steady_clock::now() - m_objectCreationTime;\n        WSL_LOG_TELEMETRY(\n            \"TelemetryConnectionCallback\",\n            PDT_ProductAndServicePerformance,\n            TraceLoggingValue(\"NAT\", \"networkingMode\"),\n            TraceLoggingValue(telemetryCounter, \"telemetryCounter\"),\n            TraceLoggingValue(\n                (std::chrono::duration_cast<std::chrono::milliseconds>(timeFromObjectCreation)).count(),\n                \"timeFromObjectCreationMs\"),\n            TraceLoggingValue(wsl::core::networking::ToString(hostConnectivity).c_str(), \"HostConnectivityLevel\"),\n            TraceLoggingValue(WindowsIPv4NlmConnectivityLevel, \"WindowsIPv4ConnectivityLevel\"),\n            TraceLoggingValue(WindowsIPv6NlmConnectivityLevel, \"WindowsIPv6ConnectivityLevel\"),\n            TraceLoggingValue(LinuxIPv4ConnCheckStatus, \"LinuxIPv4ConnCheckStatus\"),\n            TraceLoggingValue(LinuxIPv6ConnCheckStatus, \"LinuxIPv6ConnCheckStatus\"),\n            TraceLoggingValue(WindowsIpv4ConnCheckStatus, \"WindowsIpv4ConnCheckStatus\"),\n            TraceLoggingValue(WindowsIpv6ConnCheckStatus, \"WindowsIpv6ConnCheckStatus\"),\n            TraceLoggingHResult(requestStatus, \"statusSendingMessageToLinux\"),\n            TraceLoggingValue(m_config.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n            TraceLoggingValue(m_dnsTunnelingIpAddress.c_str(), \"DnsTunnelingIpAddress\"),\n            TraceLoggingValue(m_config.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n            TraceLoggingValue(m_config.EnableAutoProxy, \"AutoProxyFeatureEnabled\")); // the feature is enabled, but we don't know if proxy settings are actually configured\n    }\n    else\n    {\n        WSL_LOG(\n            \"NatNetworking::TelemetryConnectionCallback - not testing connectivity - host is not connected\",\n            TraceLoggingValue(wsl::core::networking::ToString(hostConnectivity).c_str(), \"HostConnectivityLevel\"));\n    }\n}\nCATCH_LOG()\n\nbool NatNetworking::IsHyperVFirewallSupported(const wsl::core::Config& vmConfig) noexcept\n{\n    const auto hyperVFirewallSupport = wsl::core::networking::GetHyperVFirewallSupportVersion(vmConfig.FirewallConfig);\n\n    switch (hyperVFirewallSupport)\n    {\n    case HyperVFirewallSupport::None:\n        WSL_LOG(\"IsHyperVFirewallSupported returning false: No Hyper-V Firewall API present\");\n        return false;\n\n    case HyperVFirewallSupport::Version1:\n        // we don't support using a NAT *and* Hyper-V Firewall when Windows only has the V1 APIs\n        WSL_LOG(\n            \"IsHyperVFirewallSupported returning false: Hyper-V Firewall not supported with a NAT-network and v1 Hyper-V \"\n            \"Firewall APIs\");\n        return false;\n\n    case HyperVFirewallSupport::Version2:\n    {\n        return true;\n    }\n\n    default:\n        WI_ASSERT(false);\n        return false;\n    }\n}\n\nstd::pair<wsl::core::networking::EphemeralHcnEndpoint, wsl::shared::hns::HNSEndpoint> NatNetworking::CreateEndpoint(const std::wstring& IpAddress) const\n{\n    hns::HostComputeEndpoint hnsEndpoint{};\n    hnsEndpoint.SchemaVersion.Major = 2;\n    hnsEndpoint.SchemaVersion.Minor = 16;\n\n    // Network Id\n    hnsEndpoint.HostComputeNetwork = m_config.NatNetworkId();\n\n    // Port name policy\n    hns::EndpointPolicy<hns::PortnameEndpointPolicySetting> endpointPortNamePolicy{};\n    endpointPortNamePolicy.Type = hns::EndpointPolicyType::PortName;\n    hnsEndpoint.Policies.emplace_back(std::move(endpointPortNamePolicy));\n\n    // IP Address\n    if (!IpAddress.empty())\n    {\n        wsl::shared::hns::IpConfig endpointIpConfig{};\n        endpointIpConfig.IpAddress = IpAddress;\n        hnsEndpoint.IpConfigurations.emplace_back(endpointIpConfig);\n    }\n\n    // Firewall policy\n    if (m_config.FirewallConfig.Enabled())\n    {\n        hns::EndpointPolicy<hns::FirewallPolicySetting> endpointFirewallPolicy{};\n        endpointFirewallPolicy.Settings.VmCreatorId = m_config.FirewallConfig.VmCreatorId.value();\n        endpointFirewallPolicy.Settings.PolicyFlags = hns::FirewallPolicyFlags::None;\n        endpointFirewallPolicy.Type = hns::EndpointPolicyType::Firewall;\n        hnsEndpoint.Policies.emplace_back(std::move(endpointFirewallPolicy));\n    }\n\n    auto endpoint = wsl::core::networking::CreateEphemeralHcnEndpoint(m_network.get(), hnsEndpoint);\n\n    return {std::move(endpoint), wsl::windows::common::hcs::GetEndpointProperties(endpoint.Endpoint.get())};\n}\n\nvoid NatNetworking::Initialize()\n{\n    auto lock = m_lock.lock_exclusive();\n    wil::unique_cotaskmem_string error;\n    wsl::shared::hns::HNSEndpoint endpointProperties{};\n\n    // First try to find an existing endpoint that we can use.\n    if (!m_config.NatIpAddress.empty())\n    {\n        PCSTR executionStep = \"\";\n        try\n        {\n            // Enumerating and attaching the endpoints need to be an atomic operation between different users.\n            // Keep the lock until endpoint is created.\n            // Its currently safe to take this lock while holding the member m_lock\n            // because g_endpointsInUseLock is only ever locked during the d'tor\n            auto endpointLock = g_endpointsInUseLock.lock_exclusive();\n\n            wil::unique_cotaskmem_string endpointsJson;\n            wil::unique_cotaskmem_string endpointsError;\n\n            // Unfortunately it's not possible to filter endpoints by IP address\n            // (since internally HNS will convert the IP address field to an array of objects, and the objects themselves won't be equal\n            // because the query will only have on field set), so we need to manually iterate through the endpoints on the network.\n            for (const auto& id : EnumerateEndpointsByNetworkId(m_config.NatNetworkId()))\n            {\n                wil::unique_cotaskmem_string openEndpointError;\n                wsl::windows::common::hcs::unique_hcn_endpoint openEndpoint;\n                executionStep = \"HcnOpenEndpoint\";\n                auto result = HcnOpenEndpoint(id, &openEndpoint, &openEndpointError);\n                THROW_HR_IF_MSG(result, FAILED(result), \"HcnOpenEndpoint %ls\", openEndpointError.get());\n\n                executionStep = \"HcnQueryEndpointProperties\";\n                auto properties = wsl::windows::common::hcs::GetEndpointProperties(openEndpoint.get());\n                if (properties.IPAddress == m_config.NatIpAddress)\n                {\n                    THROW_HR_IF_MSG(\n                        E_UNEXPECTED,\n                        std::ranges::find(g_endpointsInUse, id) != g_endpointsInUse.end(),\n                        \"Endpoint is in use by another address. Refusing to delete it.\");\n\n                    // TODO: this means WSL just whacked a different container's NAT address\n                    //   e.g., this just broke MDAG or Sandbox if they happened to use this same address range\n                    //   this sounds like a really bad idea\n\n                    // Found an endpoint on the same network with the IP address we want: delete it so it doesn't conflict\n                    // with ours.\n                    LOG_HR_MSG(E_UNEXPECTED, \"Found a conflicting endpoint. Deleting it\");\n                    openEndpoint.reset();\n                    executionStep = \"HcnDeleteEndpoint\";\n                    result = HcnDeleteEndpoint(id, &openEndpointError);\n                    THROW_HR_IF_MSG(result, FAILED(result), \"HcnDeleteEndpoint %ls\", openEndpointError.get());\n                }\n            }\n\n            // Create and attach the endpoint.\n            wsl::core::networking::EphemeralHcnEndpoint endpoint;\n            executionStep = \"HcnCreateEndpoint\";\n            std::tie(endpoint, endpointProperties) = CreateEndpoint(m_config.NatIpAddress);\n            executionStep = \"AttachEndpoint\";\n            AttachEndpoint(std::move(endpoint), endpointProperties);\n            g_endpointsInUse.emplace_back(endpointProperties.ID);\n        }\n        catch (...)\n        {\n            WSL_LOG(\n                \"ConstrainedNetworkEndpointCreationFailed\",\n                TraceLoggingValue(executionStep, \"executionStep\"),\n                TraceLoggingValue(\"NAT\", \"networkingMode\"),\n                TraceLoggingValue(m_config.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                TraceLoggingValue(m_config.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                TraceLoggingValue(m_config.EnableAutoProxy, \"AutoProxyFeatureEnabled\"), // the feature is enabled, but we don't know if proxy settings are actually configured\n                TraceLoggingHexUInt32(wil::ResultFromCaughtException(), \"result\"));\n        }\n    }\n\n    if (!m_endpoint.Endpoint)\n    {\n        PCSTR executionStep = \"\";\n        try\n        {\n            // If no IP address was passed or if the endpoint couldn't be created / attached, create a new one without the IP address requirement.\n            networking::EphemeralHcnEndpoint endpoint;\n            executionStep = \"HcnCreateEndpoint\";\n            std::tie(endpoint, endpointProperties) = CreateEndpoint(L\"\");\n            executionStep = \"AttachEndpoint\";\n            AttachEndpoint(std::move(endpoint), endpointProperties);\n        }\n        catch (...)\n        {\n            const auto hr = wil::ResultFromCaughtException();\n            WSL_LOG(\n                \"NewEndpointCreationFailed\",\n                TraceLoggingValue(executionStep, \"executionStep\"),\n                TraceLoggingValue(\"NAT\", \"networkingMode\"),\n                TraceLoggingValue(m_config.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                TraceLoggingValue(m_config.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                TraceLoggingValue(m_config.EnableAutoProxy, \"AutoProxyFeatureEnabled\"), // the feature is enabled, but we don't know if proxy settings are actually configured\n                TraceLoggingHexUInt32(hr, \"result\"));\n            throw;\n        }\n\n        if (!m_config.NatIpAddress.empty())\n        {\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageFailedToCreateNetworkEndpoint(\n                m_config.NatIpAddress.c_str(), endpointProperties.IPAddress.c_str()));\n        }\n\n        // Record the new IP address associated to the endpoint.\n        m_config.NatIpAddress = endpointProperties.IPAddress;\n    }\n\n    WI_ASSERT(m_endpoint.Endpoint);\n\n    // Send the endpoint state (ip address & link) to gns\n    m_gnsChannel.SendEndpointState(endpointProperties);\n\n    // Send the default route to gns\n\n    hns::ModifyGuestEndpointSettingRequest<hns::Route> request;\n    request.RequestType = hns::ModifyRequestType::Add;\n    request.ResourceType = hns::GuestEndpointResourceType::Route;\n    request.Settings.NextHop = endpointProperties.GatewayAddress;\n    request.Settings.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_PREFIX;\n    request.Settings.Family = AF_INET;\n\n    m_gnsChannel.SendHnsNotification(ToJsonW(request).c_str(), m_endpoint.Id);\n\n    if (m_dnsTunnelingResolver)\n    {\n        // Register notifications for DNS suffix changes after we create the Endpoint\n        //\n        // Note: DNS suffix change notifications are used only if DNS tunneling is enabled. DNS behavior for NAT mode\n        // without DNS tunneling remains unchanged\n        m_dnsSuffixRegistryWatcher.emplace([this] {\n            const auto watcher_lock = m_lock.lock_exclusive();\n            UpdateDns();\n        });\n    }\n\n    // Update DNS information.\n    UpdateDns(endpointProperties.GatewayAddress.c_str());\n\n    // if using the shared access DNS proxy, ensure that the shared access service is allowed inbound UDP access.\n    if (!m_useMirrorDnsSettings && !m_dnsTunnelingResolver)\n    {\n        // N.B. This rule works around a host OS issue that prevents the DNS proxy from working on older versions of Windows.\n        ConfigureSharedAccessFirewallRule();\n    }\n\n    THROW_IF_WIN32_ERROR(NotifyNetworkConnectivityHintChange(&NatNetworking::OnNetworkConnectivityChange, this, true, &m_networkNotifyHandle));\n\n    // once the VM is created, start the telemetry timer\n    if (m_connectivityTelemetryEnabled)\n    {\n        m_connectivityTelemetry.StartTimer([&](NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) {\n            TelemetryConnectionCallback(hostConnectivity, telemetryCounter);\n        });\n    }\n}\n\nvoid NatNetworking::AttachEndpoint(wsl::core::networking::EphemeralHcnEndpoint&& endpoint, const wsl::shared::hns::HNSEndpoint& properties)\n{\n\n    // for mirrored endpoints, we will set the InstanceId to the InterfaceGuid of the host interface we mirror - as we add &\n    // remove them dynamically for NAT endpoints, we will just set the InstanceId to the EndpointId\n\n    ModifySettingRequest<NetworkAdapter> networkRequest{};\n    networkRequest.ResourcePath = networking::c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(properties.ID);\n    networkRequest.RequestType = ModifyRequestType::Add;\n    networkRequest.Settings.EndpointId = properties.ID;\n    networkRequest.Settings.InstanceId = properties.ID;\n\n    networkRequest.Settings.MacAddress = wsl::shared::string::ParseMacAddress(properties.MacAddress);\n    auto retryCount = 0ul;\n    const auto hr = wsl::shared::retry::RetryWithTimeout<HRESULT>(\n        [&] {\n            HRESULT exceptionHr = wil::ResultFromException(\n                [&] { wsl::windows::common::hcs::ModifyComputeSystem(m_system, wsl::shared::ToJsonW(networkRequest).c_str()); });\n\n            WSL_LOG(\n                \"NatNetworking::AttachEndpoint [ModifyComputeSystem(ModifyRequestType::Add)]\",\n                TraceLoggingValue(properties.ID, \"endpointId\"),\n                TraceLoggingValue(exceptionHr, \"hr\"),\n                TraceLoggingValue(retryCount, \"retryCount\"));\n\n            ++retryCount;\n            return THROW_IF_FAILED(exceptionHr);\n        },\n        wsl::core::networking::AddEndpointRetryPeriod,\n        wsl::core::networking::AddEndpointRetryTimeout,\n        wsl::core::networking::AddEndpointRetryPredicate);\n\n    if (hr == HCN_E_ENDPOINT_ALREADY_ATTACHED)\n    {\n        WSL_LOG(\n            \"NatNetworking::AttachEndpoint [Adding the endpoint returned HCN_E_ENDPOINT_ALREADY_ATTACHED - continuing]\",\n            TraceLoggingValue(properties.ID, \"endpointId\"));\n    }\n    else if (FAILED(hr))\n    {\n        THROW_HR(hr);\n    }\n\n    m_endpoint = std::move(endpoint);\n    m_networkSettings = GetEndpointSettings(properties);\n}\n\nvoid NatNetworking::StartPortTracker(wil::unique_socket&& socket)\n{\n    WI_ASSERT(false);\n}\n\nvoid NETIOAPI_API_ NatNetworking::OnNetworkConnectivityChange(PVOID context, NL_NETWORK_CONNECTIVITY_HINT hint)\n{\n    auto* thisPtr = static_cast<NatNetworking*>(context);\n\n    thisPtr->RefreshGuestConnection(hint);\n    thisPtr->m_connectivityTelemetry.UpdateTimer();\n}\n\nvoid NatNetworking::RefreshGuestConnection(NL_NETWORK_CONNECTIVITY_HINT connectivityHint) noexcept\ntry\n{\n    auto lock = m_lock.lock_exclusive();\n\n    WSL_LOG(\n        \"NatNetworking::RefreshGuestConnection\",\n        TraceLoggingValue(wsl::windows::common::stringify::ToString(connectivityHint.ConnectivityLevel), \"ConnectivityLevel\"),\n        TraceLoggingValue(wsl::windows::common::stringify::ToString(connectivityHint.ConnectivityCost), \"ConnectivityCost\"));\n\n    UpdateMtu();\n    UpdateDns();\n}\nCATCH_LOG()\n\n_Requires_lock_held_(m_lock)\nvoid NatNetworking::UpdateDns(std::optional<PCWSTR> gatewayAddress) noexcept\ntry\n{\n    if (!m_dnsTunnelingResolver && !m_useMirrorDnsSettings && !gatewayAddress)\n    {\n        return;\n    }\n\n    networking::DnsInfo latestDnsSettings{};\n\n    // NAT mode with DNS tunneling\n    if (m_dnsTunnelingResolver)\n    {\n        latestDnsSettings = HostDnsInfo::GetDnsTunnelingSettings(m_dnsTunnelingIpAddress);\n    }\n    // NAT mode without Shared Access DNS proxy\n    else if (m_useMirrorDnsSettings)\n    {\n        latestDnsSettings = HostDnsInfo::GetDnsSettings(DnsSettingsFlags::IncludeVpn);\n    }\n    // NAT mode with Shared Access DNS proxy\n    else if (gatewayAddress)\n    {\n        // set the NAT gateway address when using the NAT IPv4 DNS proxy\n        latestDnsSettings.Servers.emplace_back(wsl::shared::string::WideToMultiByte(gatewayAddress.value()));\n    }\n\n    if (latestDnsSettings != m_trackedDnsSettings)\n    {\n        auto dnsNotification = BuildDnsNotification(latestDnsSettings, m_dnsOptions);\n\n        WSL_LOG(\n            \"NatNetworking::UpdateDns\",\n            TraceLoggingValue(dnsNotification.Options.c_str(), \"options\"),\n            TraceLoggingValue(dnsNotification.Search.c_str(), \"search\"),\n            TraceLoggingValue(dnsNotification.ServerList.c_str(), \"serverList\"));\n\n        hns::ModifyGuestEndpointSettingRequest<hns::DNS> notification{};\n        notification.RequestType = hns::ModifyRequestType::Update;\n        notification.ResourceType = hns::GuestEndpointResourceType::DNS;\n        notification.Settings = std::move(dnsNotification);\n        m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_endpoint.Id);\n\n        m_trackedDnsSettings = std::move(latestDnsSettings);\n    }\n}\nCATCH_LOG()\n\nvoid NatNetworking::UpdateMtu()\n{\n    const auto minMtu = GetMinimumConnectedInterfaceMtu();\n\n    // Only send the update if the MTU changed.\n    if (minMtu && minMtu.value() != m_networkMtu)\n    {\n        m_networkMtu = minMtu.value();\n\n        hns::ModifyGuestEndpointSettingRequest<hns::NetworkInterface> notification{};\n        notification.ResourceType = hns::GuestEndpointResourceType::Interface;\n        notification.RequestType = hns::ModifyRequestType::Update;\n        notification.Settings.Connected = true;\n        notification.Settings.NlMtu = m_networkMtu;\n\n        WSL_LOG(\n            \"NatNetworking::UpdateMtu\", TraceLoggingValue(m_endpoint.Id, \"endpointId\"), TraceLoggingValue(m_networkMtu, \"natMtu\"));\n\n        m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_endpoint.Id);\n    }\n}\n\nvoid NatNetworking::TraceLoggingRundown() noexcept\n{\n    auto lock = m_lock.lock_exclusive();\n\n    WSL_LOG(\n        \"NatNetworking::TraceLoggingRundown\",\n        TraceLoggingValue(m_config.NatNetworkId(), \"networkId\"),\n        TraceLoggingValue(m_endpoint.Id, \"endpointId\"),\n        TRACE_NETWORKSETTINGS_OBJECT(m_networkSettings));\n}\n\nvoid NatNetworking::FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message)\n{\n    message.NetworkingMode = LxMiniInitNetworkingModeNat;\n    message.DisableIpv6 = false;\n    message.EnableDhcpClient = false;\n    message.PortTrackerType = m_config.EnableLocalhostRelay ? LxMiniInitPortTrackerTypeRelay : LxMiniInitPortTrackerTypeNone;\n}\n\n// before sending anything to the container, we must wait for the NAT IP Interfaces on the host to be connected.\n// there's a possible race here if the physical adapter gets connected but the NAT vNIC interface take a bit longer\nstd::optional<ULONGLONG> NatNetworking::FindNatInterfaceLuid(const SOCKADDR_INET& natAddress, const NL_NETWORK_CONNECTIVITY_HINT& currentConnectivityHint)\n{\n    constexpr ULONGLONG maxTimeToWaitMs = 10ull * 1000ull;\n    constexpr ULONG timeToSleepMs = 100ul;\n    const auto startTickCount = GetTickCount64();\n\n    NET_LUID natLuid{};\n    for (;;)\n    {\n        // HNS does not give us the interface guid/luid/index of the vNIC that is used for this NAT configuration\n        // because we don't constrain our NAT interface to any one host NIC\n        // we only have the assigned IPAddress - we'll have to use that to find the interface to check its state\n        // this is NAT - so it's an IPv4 address\n        unique_address_table addressTable;\n        THROW_IF_WIN32_ERROR(GetUnicastIpAddressTable(AF_INET, &addressTable));\n        for (const auto& address : wil::make_range(addressTable.get()->Table, addressTable.get()->NumEntries))\n        {\n            if (natAddress == address.Address)\n            {\n                natLuid.Value = address.InterfaceLuid.Value;\n                break;\n            }\n\n            WSL_LOG(\n                \"NatNetworking::FindNatInterfaceLuid [IP Address comparison mismatch]\",\n                TraceLoggingValue(wsl::windows::common::string::SockAddrInetToString(natAddress).c_str(), \"natAddress\"),\n                TraceLoggingValue(\n                    wsl::windows::common::string::SockAddrInetToString(address.Address).c_str(), \"enumeratedAddress\"));\n        }\n\n        if (natLuid.Value != 0)\n        {\n            break;\n        }\n\n        // give up if something is just broken and taking too long\n        if (GetTickCount64() - startTickCount >= maxTimeToWaitMs)\n        {\n            break;\n        }\n        // else sleep and try again shortly\n        Sleep(timeToSleepMs);\n        // bail if connectivity on the host has completely changed\n        NL_NETWORK_CONNECTIVITY_HINT latestConnectivityHint{};\n        GetNetworkConnectivityHint(&latestConnectivityHint);\n        if (latestConnectivityHint != currentConnectivityHint)\n        {\n            WSL_LOG(\"NatNetworking::FindNatInterfaceLuid [connectivity changed while waiting for the NAT interface]\");\n            THROW_WIN32_MSG(ERROR_RETRY, \"connectivity changed while waiting for the NAT interface\");\n        }\n    }\n\n    if (natLuid.Value == 0)\n    {\n        WSL_LOG(\n            \"NatNetworking::FindNatInterfaceLuid [IP address not found]\",\n            TraceLoggingValue(natLuid.Value, \"natInterfaceLuid\"),\n            TraceLoggingValue(wsl::windows::common::string::SockAddrInetToString(natAddress).c_str(), \"natIPAddress\"));\n        return {};\n    }\n\n    WSL_LOG(\n        \"NatNetworking::FindNatInterfaceLuid [waiting for NAT interface to be connected]\",\n        TraceLoggingValue(natLuid.Value, \"natInterfaceLuid\"),\n        TraceLoggingValue(wsl::windows::common::string::SockAddrInetToString(natAddress).c_str(), \"natIPAddress\"));\n\n    bool ipv4Connected = false;\n    for (;;)\n    {\n        unique_interface_table interfaceTable{};\n        THROW_IF_WIN32_ERROR(::GetIpInterfaceTable(AF_UNSPEC, &interfaceTable));\n        // we only track the IPv4 interface because we only NAT IPv4 to the container\n        for (auto index = 0ul; index < interfaceTable.get()->NumEntries; ++index)\n        {\n            const auto& ipInterface = interfaceTable.get()->Table[index];\n            if (ipInterface.Family == AF_INET && !!ipInterface.Connected && ipInterface.InterfaceLuid.Value == natLuid.Value)\n            {\n                ipv4Connected = true;\n                break;\n            }\n        }\n        if (ipv4Connected)\n        {\n            break;\n        }\n\n        // give up if something is just broken and taking too long\n        if (GetTickCount64() - startTickCount >= maxTimeToWaitMs)\n        {\n            break;\n        }\n        // else sleep and try again shortly\n        Sleep(timeToSleepMs);\n        // bail if connectivity on the host has completely changed\n        NL_NETWORK_CONNECTIVITY_HINT latestConnectivityHint{};\n        GetNetworkConnectivityHint(&latestConnectivityHint);\n        if (latestConnectivityHint != currentConnectivityHint)\n        {\n            WSL_LOG(\"NatNetworking::FindNatInterfaceLuid [connectivity changed while waiting for the NAT interface]\");\n            THROW_WIN32_MSG(ERROR_RETRY, \"connectivity changed while waiting for the NAT interface\");\n        }\n    }\n\n    // return zero if it's not connected yet so we can retry the next cycle\n    return ipv4Connected ? natLuid.Value : std::optional<ULONGLONG>();\n}\n\nwsl::windows::common::hcs::unique_hcn_network NatNetworking::CreateNetwork(wsl::core::Config& config)\n{\n    wsl::windows::common::hcs::unique_hcn_network natNetwork;\n    wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&] {\n        try\n        {\n            wsl::core::networking::ConfigureHyperVFirewall(config.FirewallConfig, wsl::windows::common::wslutil::c_vmOwner);\n            natNetwork = CreateNetworkInternal(config);\n        }\n        catch (...)\n        {\n            // Don't retry if no constraints were set.\n            if (config.NatNetwork.empty() && config.NatGateway.empty())\n            {\n                LOG_CAUGHT_EXCEPTION();\n                throw;\n            }\n\n            LOG_CAUGHT_EXCEPTION_MSG(\n                \"Failed to create network: '%ls' with gateway: '%ls', retrying without constraints\",\n                config.NatNetwork.c_str(),\n                config.NatGateway.c_str());\n\n            const auto error = wil::ResultFromCaughtException();\n            WSL_LOG(\n                \"ConstrainedNetworkCreationFailed\",\n                TraceLoggingHexUInt32(error, \"result\"),\n                TraceLoggingValue(\"NAT\", \"networkingMode\"),\n                TraceLoggingValue(config.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                TraceLoggingValue(config.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                TraceLoggingValue(config.EnableAutoProxy, \"AutoProxyFeatureEnabled\") // the feature is enabled, but we don't know if proxy settings are actually configured\n            );\n\n            const auto previousRange = std::move(config.NatNetwork);\n            config.NatGateway = {};\n            // Note that the firewall config is NOT cleared here as we MUST always configure firewall if it has been requested\n            natNetwork = CreateNetworkInternal(config);\n\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageFailedToCreateNetwork(\n                previousRange.c_str(), config.NatNetwork.c_str(), wsl::windows::common::wslutil::GetSystemErrorString(error).c_str()));\n        }\n    });\n\n    return natNetwork;\n}\n\nwsl::windows::common::hcs::unique_hcn_network NatNetworking::CreateNetworkInternal(wsl::core::Config& config)\n{\n    HRESULT hr = S_OK;\n    PCSTR executionStep = \"\";\n\n    // Log telemetry to determine how long it takes to create the network.\n    const auto startTimeMs = GetTickCount64();\n\n    // Log how long it takes for networking to be created\n    WSL_LOG_TELEMETRY(\n        \"CreateNetworkBegin\",\n        PDT_ProductAndServicePerformance,\n        TraceLoggingValue(config.NatNetworkName(), \"NetworkName\"),\n        TraceLoggingGuid(config.NatNetworkId(), \"NetworkGuid\"),\n        TraceLoggingValue(\"NAT\", \"networkingMode\"),\n        TraceLoggingValue(config.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n        TraceLoggingValue(config.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n        TraceLoggingValue(config.EnableAutoProxy, \"AutoProxyFeatureEnabled\")); // the feature is enabled, but we don't know if proxy settings are actually configured\n\n    auto createEnd = wil::scope_exit([&] {\n        const auto TimeToCreateNetworkMs = GetTickCount64() - startTimeMs;\n        WSL_LOG_TELEMETRY(\n            \"CreateNetworkEnd\",\n            PDT_ProductAndServicePerformance,\n            TraceLoggingValue(config.NatNetworkName(), \"NetworkName\"),\n            TraceLoggingGuid(config.NatNetworkId(), \"NetworkGuid\"),\n            TraceLoggingValue(TimeToCreateNetworkMs, \"TimeToCreateNetworkMs\"),\n            TraceLoggingHResult(hr, \"hr\"),\n            TraceLoggingValue(executionStep, \"executionStep\"),\n            TraceLoggingValue(\"NAT\", \"networkingMode\"),\n            TraceLoggingValue(config.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n            TraceLoggingValue(config.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n            TraceLoggingValue(config.EnableAutoProxy, \"AutoProxyFeatureEnabled\")); // the feature is enabled, but we don't know if proxy settings are actually configured\n    });\n\n    auto runAsSelf = wil::run_as_self();\n\n    // Send a HNS request to create the network.\n    hns::Network settings{};\n    settings.Name = config.NatNetworkName();\n    settings.Type = hns::NetworkMode::ICS;\n    settings.IsolateSwitch = true;\n    settings.Flags = hns::NetworkFlags::EnableDns | hns::NetworkFlags::EnableNonPersistent;\n    WI_SetFlagIf(settings.Flags, hns::NetworkFlags::EnableFirewall, config.FirewallConfig.Enabled());\n\n    if (!config.NatNetwork.empty())\n    {\n        hns::IpSubnet netIpSubnet{};\n        netIpSubnet.IpAddressPrefix = config.NatNetwork;\n\n        hns::Subnet subnet{};\n        subnet.AddressPrefix = config.NatNetwork;\n        subnet.GatewayAddress = config.NatGateway;\n        subnet.IpSubnets.emplace_back(std::move(netIpSubnet));\n        settings.Subnets.emplace_back(std::move(subnet));\n    }\n\n    // Determine if the virtual network should be constrained by an external interface on the host.\n    // For example, if the user only wants traffic to be routed if a VPN is connected.\n    try\n    {\n        const auto lxssKey = windows::common::registry::OpenKey(HKEY_LOCAL_MACHINE, LXSS_REGISTRY_PATH, KEY_READ);\n        const auto interfaceConstraint =\n            windows::common::registry::ReadString(lxssKey.get(), nullptr, c_interfaceConstraintKey, L\"\");\n\n        if (!interfaceConstraint.empty())\n        {\n            settings.Type = hns::NetworkMode::ConstrainedICS;\n            settings.InterfaceConstraint.InterfaceAlias = interfaceConstraint;\n        }\n    }\n    CATCH_LOG()\n\n    wsl::windows::common::hcs::unique_hcn_network network{};\n    try\n    {\n        auto retryCount = 0ul;\n        wsl::shared::retry::RetryWithTimeout<void>(\n            [&] {\n                executionStep = \"HcnCreateNetwork\";\n                ExecutionContext context(Context::HNS);\n                wil::unique_cotaskmem_string error;\n                HRESULT hns_hr = HcnCreateNetwork(config.NatNetworkId(), ToJsonW(settings).c_str(), &network, &error);\n                WSL_LOG(\n                    \"NatNetworking::CreateNetwork [HcnCreateNetwork]\",\n                    TraceLoggingValue(config.NatNetworkId(), \"networkGuid\"),\n                    TraceLoggingValue(settings.Name.c_str(), \"settingsName\"),\n                    TraceLoggingValue(JsonEnumToString(settings.Type).c_str(), \"settingsType\"),\n                    TraceLoggingValue(\n                        settings.InterfaceConstraint.InterfaceAlias.c_str(), \"settingsInterfaceConstraintInterfaceAlias\"),\n                    TraceLoggingValue(settings.IsolateSwitch, \"settingsIsolateSwitch\"),\n                    TraceLoggingValue(static_cast<uint32_t>(settings.Flags), \"settingsFlags\"),\n                    TraceLoggingValue(hns_hr, \"hr\"),\n                    TraceLoggingValue(retryCount, \"retryCount\"));\n\n                ++retryCount;\n\n                // Open the existing network if it already exists.\n                if (hns_hr == HCN_E_NETWORK_ALREADY_EXISTS)\n                {\n                    executionStep = \"HcnOpenNetwork\";\n                    network = wsl::core::networking::OpenNetwork(config.NatNetworkId());\n                }\n                else\n                {\n                    // Throw other errors to allow for retries\n                    THROW_IF_FAILED_MSG(hns_hr, \"HcnCreateNetwork %ls\", error.get());\n                }\n\n                executionStep = \"HcnQueryNetworkProperties\";\n                // Save the networks settings in the configuration (used for WSL to save the NAT network configuration)\n                auto [properties, propertiesString] = wsl::core::networking::QueryNetworkProperties(network.get());\n                THROW_HR_IF_MSG(\n                    E_UNEXPECTED, properties.Subnets.size() != 1, \"Unexpected number of subnets in network: %ls\", propertiesString.get());\n\n                config.NatGateway = properties.Subnets[0].GatewayAddress;\n                config.NatNetwork = properties.Subnets[0].AddressPrefix;\n            },\n            std::chrono::milliseconds(100),\n            std::chrono::seconds(3));\n    }\n    catch (...)\n    {\n        hr = wil::ResultFromCaughtException();\n        throw;\n    }\n\n    return network;\n}\n"
  },
  {
    "path": "src/windows/common/NatNetworking.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include <netlistmgr.h>\n\n#include \"INetworkingEngine.h\"\n#include \"GnsChannel.h\"\n#include \"DnsResolver.h\"\n#include \"WslCoreConfig.h\"\n#include \"WslCoreNetworkEndpointSettings.h\"\n#include \"WslCoreHostDnsInfo.h\"\n#include \"WslCoreNetworkingSupport.h\"\n#include \"hns_schema.h\"\n\nnamespace wsl::core {\n\nclass NatNetworking final : public INetworkingEngine\n{\npublic:\n    NatNetworking(\n        HCS_SYSTEM system,\n        wsl::windows::common::hcs::unique_hcn_network&& network,\n        GnsChannel&& gnsChannel,\n        Config& config,\n        wil::unique_socket&& dnsHvsocket,\n        LPCWSTR dnsOptions = LX_INIT_RESOLVCONF_FULL_HEADER);\n    ~NatNetworking() override;\n\n    // Note: This class cannot be moved because m_networkNotifyHandle captures a 'this' pointer.\n    NatNetworking(const NatNetworking&) = delete;\n    NatNetworking(NatNetworking&&) = delete;\n    NatNetworking& operator=(const NatNetworking&) = delete;\n    NatNetworking& operator=(NatNetworking&&) = delete;\n\n    void Initialize() override;\n    void TraceLoggingRundown() noexcept override;\n    void FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message) override;\n    void StartPortTracker(wil::unique_socket&& socket) override;\n\n    static wsl::windows::common::hcs::unique_hcn_network CreateNetwork(wsl::core::Config& config);\n    static bool IsHyperVFirewallSupported(const wsl::core::Config& vmConfig) noexcept;\n\nprivate:\n    static void NETIOAPI_API_ OnNetworkConnectivityChange(PVOID context, NL_NETWORK_CONNECTIVITY_HINT hint);\n    static std::optional<ULONGLONG> FindNatInterfaceLuid(const SOCKADDR_INET& natAddress, const NL_NETWORK_CONNECTIVITY_HINT& currentConnectivityHint);\n    std::pair<networking::EphemeralHcnEndpoint, wsl::shared::hns::HNSEndpoint> CreateEndpoint(const std::wstring& IpAddress) const;\n    static wsl::windows::common::hcs::unique_hcn_network CreateNetworkInternal(wsl::core::Config& config);\n\n    void RefreshGuestConnection(NL_NETWORK_CONNECTIVITY_HINT connectivityHint) noexcept;\n    _Requires_lock_held_(m_lock)\n    void UpdateDns(std::optional<PCWSTR> gatewayAddress = std::nullopt) noexcept;\n    void UpdateMtu();\n    void AttachEndpoint(networking::EphemeralHcnEndpoint&& endpoint, const wsl::shared::hns::HNSEndpoint& properties);\n    void TelemetryConnectionCallback(NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) noexcept;\n\n    mutable wil::srwlock m_lock;\n\n    // Handle for the Hcn* Api. Owned by the caller (WslCoreVm), this is a non-owning copy\n    const HCS_SYSTEM m_system{};\n    Config& m_config;\n    wsl::windows::common::hcs::unique_hcn_network m_network{};\n\n    bool m_connectivityTelemetryEnabled{false};\n    wsl::core::networking::ConnectivityTelemetry m_connectivityTelemetry;\n\n    // Optional DNS resolver used for DNS tunneling\n    std::optional<networking::DnsResolver> m_dnsTunnelingResolver;\n\n    std::wstring m_dnsTunnelingIpAddress;\n\n    std::chrono::time_point<std::chrono::steady_clock> m_objectCreationTime = std::chrono::steady_clock::now();\n\n    std::optional<networking::DnsSuffixRegistryWatcher> m_dnsSuffixRegistryWatcher;\n    // The latest DNS settings configured in Linux\n    _Guarded_by_(m_lock) networking::DnsInfo m_trackedDnsSettings {};\n\n    // If true, DNS settings are retrieved from host adapters (mirrored mode)\n    // rather than using the shared access DNS proxy\n    bool m_useMirrorDnsSettings = false;\n\n    // Options/header for /etc/resolv.conf\n    LPCWSTR m_dnsOptions = nullptr;\n\n    GnsChannel m_gnsChannel;\n    std::shared_ptr<networking::NetworkSettings> m_networkSettings;\n    networking::EphemeralHcnEndpoint m_endpoint;\n    ULONG m_networkMtu = 0;\n\n    networking::unique_notify_handle m_networkNotifyHandle{};\n};\n\n} // namespace wsl::core\n"
  },
  {
    "path": "src/windows/common/Redirector.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Redirector.cpp\n\nAbstract:\n\n    This file contains helpers for controlling the Plan 9 Redirector.\n\n--*/\n\n#include \"precomp.h\"\n#include <p9rdr.h>\n#include <afunix.h>\n#include \"Redirector.h\"\n\nnamespace {\nstruct ConnectionSecurityContext\n{\n    LUID LogonId;\n    LUID LinkedLogonId;\n};\n\nConnectionSecurityContext GetUserLogonIds(_In_ HANDLE token)\n{\n    const auto tokenGroups = wil::get_token_information<TOKEN_GROUPS_AND_PRIVILEGES>(token);\n\n    // Try to get the linked token. If that fails, just use the one token.\n    wil::unique_token_linked_token tokenInfo{};\n    if (FAILED(wil::get_token_information_nothrow(tokenInfo, token)))\n    {\n        return {tokenGroups->AuthenticationId, tokenGroups->AuthenticationId};\n    }\n\n    const auto linkedTokenGroups = wil::get_token_information<TOKEN_GROUPS_AND_PRIVILEGES>(tokenInfo.LinkedToken);\n    return {tokenGroups->AuthenticationId, linkedTokenGroups->AuthenticationId};\n}\n} // namespace\n\nnamespace wsl::windows::common::redirector {\n\nconstexpr wchar_t c_RedirectorServiceName[] = L\"P9Rdr\";\n\n// Removes all connection targets.\nvoid ClearConnectionTargets(HANDLE device)\n{\n    filesystem::DeviceIoControl(device, IOCTL_P9RDR_CLEAR_CONNECTION_TARGETS);\n}\n\n// Opens the device object for the Plan 9 redirector.\nwil::unique_hfile OpenRedirector()\n{\n    UNICODE_STRING name{};\n    RtlInitUnicodeString(&name, P9RDR_DEVICE_NAME);\n    return filesystem::OpenRelativeFile(nullptr, &name, GENERIC_READ, FILE_OPEN, 0);\n}\n\n// Starts the Plan 9 mini-redirector.\nbool StartRedirector(HANDLE device)\n{\n    const NTSTATUS status = filesystem::DeviceIoControlNoThrow(device, IOCTL_P9RDR_START);\n    if (!NT_SUCCESS(status))\n    {\n        if (status != STATUS_REDIRECTOR_STARTED)\n        {\n            THROW_NTSTATUS(status);\n        }\n\n        return false;\n    }\n\n    return true;\n}\n\n// Starts the Plan 9 mini-redirector.\nbool StartRedirector()\n{\n    const auto rdr = OpenRedirector();\n    return StartRedirector(rdr.get());\n}\n\n// Starts the Plan 9 redirector system service.\nbool StartRedirectorService()\n{\n    const wil::unique_schandle manager{OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT)};\n    THROW_LAST_ERROR_IF(!manager);\n\n    const wil::unique_schandle service{OpenService(manager.get(), c_RedirectorServiceName, SERVICE_START)};\n    THROW_LAST_ERROR_IF(!service);\n    if (!StartService(service.get(), 0, nullptr))\n    {\n        THROW_LAST_ERROR_IF(GetLastError() != ERROR_SERVICE_ALREADY_RUNNING);\n        return false;\n    }\n\n    return true;\n}\n\n// Make sure the Plan 9 Redirector device is present, the mini-redirector is started, and is in a\n// clean state.\nvoid EnsureRedirectorStarted()\n{\n    const bool serviceStarted = StartRedirectorService();\n    const auto rdr = OpenRedirector();\n\n    // Clear any connection targets that may be left over e.g. if the WSL service crashed\n    // before.\n    // N.B. This isn't necessary if the redirector service was just started.\n    if (!serviceStarted)\n    {\n        ClearConnectionTargets(rdr.get());\n    }\n\n    // Always send the start ioctl, because even if the service was running this might not have been\n    // sent before.\n    StartRedirector(rdr.get());\n}\n\n// Adds a connection target to the Plan 9 Redirector.\nvoid AddConnectionTarget(std::wstring_view name, LUID logonId, std::string_view aname, LX_UID_T uid, std::wstring_view unixSocketPath, const GUID& instanceId, ULONG port)\n{\n    const auto nameBytes = as_bytes(gsl::make_span(name.data(), name.size()));\n    const auto anameBytes = as_bytes(gsl::make_span(aname.data(), aname.size()));\n    const auto unixSocketPathBytes = as_bytes(gsl::make_span(unixSocketPath.data(), unixSocketPath.size()));\n\n    const auto size = sizeof(P9RDR_ADD_CONNECTION_TARGET_INPUT) + nameBytes.size() + anameBytes.size() + unixSocketPathBytes.size();\n    std::vector<gsl::byte> buffer(size);\n\n    const auto addConnection = gslhelpers::get_struct<P9RDR_ADD_CONNECTION_TARGET_INPUT>(gsl::make_span(buffer));\n    if (!unixSocketPathBytes.empty())\n    {\n        // This is regular WSL, which uses a Unix socket.\n        const auto unixAddress = reinterpret_cast<PSOCKADDR_UN>(&addConnection->Address);\n\n        // The path in the sockaddr_un is not used, but it should not be empty. Just put the\n        // unqualified file name in there.\n        unixAddress->sun_family = AF_UNIX;\n        strcpy_s(unixAddress->sun_path, LXSS_PLAN9_UNIX_SOCKET_A);\n    }\n    else\n    {\n        // This is a VM mode instance, so use a Hyper-V socket.\n        const auto hvAddress = reinterpret_cast<PSOCKADDR_HV>(&addConnection->Address);\n        hvAddress->Family = AF_HYPERV;\n        hvAddress->VmId = instanceId;\n        hvAddress->ServiceId = HV_GUID_VSOCK_TEMPLATE;\n        hvAddress->ServiceId.Data1 = port;\n    }\n\n    addConnection->Uid = uid;\n    addConnection->LogonId = logonId;\n    addConnection->ShareNameLength = gsl::narrow_cast<USHORT>(name.length() * sizeof(wchar_t));\n    addConnection->ANameLength = gsl::narrow_cast<USHORT>(aname.length());\n\n    // Copy over the share name.\n    auto stringSpan = gsl::make_span(buffer).subspan(sizeof(P9RDR_ADD_CONNECTION_TARGET_INPUT));\n    gsl::copy(nameBytes, stringSpan);\n    stringSpan = stringSpan.subspan(nameBytes.size());\n\n    // Copy over the aname.\n    if (aname.size() > 0)\n    {\n        gsl::copy(anameBytes, stringSpan);\n        stringSpan = stringSpan.subspan(anameBytes.size());\n    }\n\n    // Copy over the unix socket path.\n    if (unixSocketPathBytes.size() > 0)\n    {\n        gsl::copy(unixSocketPathBytes, stringSpan);\n    }\n\n    // Send the command to the driver.\n    const auto rdr = OpenRedirector();\n    filesystem::DeviceIoControl(rdr.get(), IOCTL_P9RDR_ADD_CONNECTION_TARGET, buffer);\n}\n\n// Removes a connection target from the Plan 9 Redirector.\nvoid RemoveConnectionTarget(std::wstring_view name, LUID logonId)\n{\n    const auto nameBytes = as_bytes(gsl::make_span(name.data(), name.length()));\n    std::vector<gsl::byte> buffer(sizeof(P9RDR_REMOVE_CONNECTION_TARGET_INPUT) + nameBytes.size());\n\n    const auto removeConnection = gslhelpers::get_struct<P9RDR_REMOVE_CONNECTION_TARGET_INPUT>(gsl::make_span(buffer));\n    removeConnection->LogonId = logonId;\n\n    // Copy over the share name.\n    const auto stringSpan = gsl::make_span(buffer).subspan(sizeof(P9RDR_REMOVE_CONNECTION_TARGET_INPUT));\n    gsl::copy(nameBytes, stringSpan);\n\n    // Send the command to the driver.\n    const auto rdr = OpenRedirector();\n    const NTSTATUS status = filesystem::DeviceIoControlNoThrow(rdr.get(), IOCTL_P9RDR_REMOVE_CONNECTION_TARGET, buffer);\n\n    // If the share didn't exist, that's weird but not a failure.\n    if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND)\n    {\n        THROW_NTSTATUS(status);\n    }\n}\n\n// Registers a user-mode callback with the Plan 9 Redirector.\nvoid RegisterUserCallback(HANDLE handle, gsl::span<gsl::byte> outputBuffer, LPOVERLAPPED overlapped)\n{\n    if (!DeviceIoControl(\n            handle, IOCTL_P9RDR_REGISTER_USER_CALLBACK, nullptr, 0, outputBuffer.data(), gsl::narrow_cast<DWORD>(outputBuffer.size()), nullptr, overlapped))\n    {\n        THROW_LAST_ERROR_IF(GetLastError() != ERROR_IO_PENDING);\n    }\n}\n\nConnectionTargetManager::ConnectionTargetManager(std::wstring_view name) : m_name{name}\n{\n}\n\n// Registers connection targets for the specified logon ID and linked logon ID, if they're not\n// already registered.\nvoid ConnectionTargetManager::AddConnectionTarget(\n    HANDLE userToken, std::string_view aname, LX_UID_T uid, std::wstring_view unixSocketPath, const GUID& instanceId, ULONG port)\n{\n    const auto security = GetUserLogonIds(userToken);\n    auto lock = m_lock.lock_exclusive();\n    if (!Contains(security.LogonId))\n    {\n        redirector::AddConnectionTarget(m_name, security.LogonId, aname, uid, unixSocketPath, instanceId, port);\n        m_logonIds.push_back(security.LogonId);\n    }\n\n    // Checking the list also catches the case where the logon ID and linked logon ID are equal.\n    if (!Contains(security.LinkedLogonId))\n    {\n        redirector::AddConnectionTarget(m_name, security.LinkedLogonId, aname, uid, unixSocketPath, instanceId, port);\n        m_logonIds.push_back(security.LinkedLogonId);\n    }\n}\n\n// Removes all connection targets associated with the instance.\nvoid ConnectionTargetManager::RemoveAll()\n{\n    auto lock = m_lock.lock_exclusive();\n    for (const auto logonId : m_logonIds)\n    {\n        RemoveConnectionTarget(m_name, logonId);\n    }\n\n    m_logonIds.clear();\n}\n\n// Checks whether the list of logon IDs contains the specified ID.\nbool ConnectionTargetManager::Contains(LUID luid) const\n{\n    const auto it = std::find_if(m_logonIds.begin(), m_logonIds.end(), [&luid](auto& item) { return RtlEqualLuid(&luid, &item); });\n\n    return it != m_logonIds.end();\n}\n\n} // namespace wsl::windows::common::redirector\n"
  },
  {
    "path": "src/windows/common/Redirector.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Redirector.h\n\nAbstract:\n\n    This file contains declarations for helpers for controlling the Plan 9 Redirector.\n\n--*/\n\n#pragma once\n\nnamespace wsl::windows::common::redirector {\n\nclass ConnectionTargetManager final\n{\npublic:\n    ConnectionTargetManager(std::wstring_view name);\n\n    void AddConnectionTarget(\n        HANDLE userToken, std::string_view aname, LX_UID_T uid, std::wstring_view unixSocketPath = {}, const GUID& instanceId = {}, ULONG port = 0);\n\n    void RemoveAll();\n\nprivate:\n    bool Contains(LUID luid) const;\n\n    std::wstring_view m_name;\n    std::vector<LUID> m_logonIds;\n    wil::srwlock m_lock;\n};\n\nwil::unique_hfile OpenRedirector();\n\nvoid EnsureRedirectorStarted();\n\nbool StartRedirector();\n\nvoid AddConnectionTarget(\n    std::wstring_view name, LUID logonId, std::string_view aname, LX_UID_T uid, std::wstring_view unixSocketPath, const GUID& instanceId = {}, ULONG port = 0);\n\nvoid RemoveConnectionTarget(std::wstring_view name, LUID logonId);\n\nvoid RegisterUserCallback(HANDLE handle, gsl::span<gsl::byte> outputBuffer, LPOVERLAPPED overlapped);\n\n} // namespace wsl::windows::common::redirector\n"
  },
  {
    "path": "src/windows/common/RingBuffer.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    RingBuffer.cpp\r\n\r\nAbstract:\r\n\r\n    This file contains definitions for the RingBuffer class.\r\n\r\n--*/\r\n\r\n#include \"precomp.h\"\r\n#include \"RingBuffer.h\"\r\n\r\nRingBuffer::RingBuffer(size_t size) : m_maxSize(size), m_offset(0)\r\n{\r\n    m_buffer.reserve(size);\r\n}\r\n\r\nvoid RingBuffer::Insert(std::string_view data)\r\n{\r\n    auto lock = m_lock.lock_exclusive();\r\n    auto remainingData = gsl::make_span(data.data(), data.size());\r\n    if (remainingData.size() > m_maxSize)\r\n    {\r\n        remainingData = remainingData.subspan(remainingData.size() - m_maxSize);\r\n    }\r\n\r\n    const auto bytesAtEnd = std::min(m_maxSize - m_offset, remainingData.size());\r\n    if (m_offset + bytesAtEnd > m_buffer.size())\r\n    {\r\n        m_buffer.resize(m_offset + bytesAtEnd);\r\n        WI_ASSERT(m_buffer.size() <= m_maxSize);\r\n    }\r\n\r\n    const auto allBuffer = gsl::make_span(m_buffer);\r\n    const auto beginCopyBuffer = allBuffer.subspan(m_offset, bytesAtEnd);\r\n    copy(remainingData.subspan(0, bytesAtEnd), beginCopyBuffer);\r\n    remainingData = remainingData.subspan(bytesAtEnd);\r\n    if (!remainingData.empty())\r\n    {\r\n        copy(remainingData, allBuffer);\r\n        m_offset = remainingData.size();\r\n    }\r\n    else\r\n    {\r\n        m_offset += bytesAtEnd;\r\n    }\r\n}\r\n\r\nstd::vector<std::string> RingBuffer::GetLastDelimitedStrings(char Delimiter, size_t Count) const\r\n{\r\n    auto lock = m_lock.lock_shared();\r\n    auto [begin, end] = Contents();\r\n    std::vector<std::string> results;\r\n    std::optional<size_t> endIndex;\r\n    for (size_t i = end.size(); i > 0; i--)\r\n    {\r\n        if (results.size() == Count)\r\n        {\r\n            break;\r\n        }\r\n\r\n        if (Delimiter == end[i - 1])\r\n        {\r\n            if (endIndex.has_value())\r\n            {\r\n                results.emplace(results.begin(), &end[i], endIndex.value() - i);\r\n                endIndex.reset();\r\n            }\r\n            else\r\n            {\r\n                endIndex = i - 1;\r\n            }\r\n        }\r\n    }\r\n\r\n    if (results.size() == Count)\r\n    {\r\n        return results;\r\n    }\r\n\r\n    std::string partial;\r\n    if (endIndex.has_value())\r\n    {\r\n        partial = std::string{&end[0], endIndex.value()};\r\n        endIndex.reset();\r\n    }\r\n\r\n    for (size_t i = begin.size(); i > 0; i--)\r\n    {\r\n        if (results.size() == Count)\r\n        {\r\n            break;\r\n        }\r\n\r\n        if (Delimiter == begin[i - 1])\r\n        {\r\n            if (!partial.empty())\r\n            {\r\n                // The debug CRT will fastfail if begin[size] is accessed\r\n                // But in this case it's not a problem because begin.size() - i would be == 0\r\n                std::string partial_begin{&begin.data()[i], begin.size() - i};\r\n                results.emplace(results.begin(), partial_begin + partial);\r\n                partial.clear();\r\n            }\r\n            else if (endIndex.has_value())\r\n            {\r\n                results.emplace(results.begin(), &begin.data()[i], endIndex.value() - i);\r\n                endIndex.reset();\r\n            }\r\n            else\r\n            {\r\n                endIndex = i - 1;\r\n            }\r\n        }\r\n    }\r\n\r\n    if (results.size() < Count)\r\n    {\r\n        // May have lost some data, or this could be the very first line logged.\r\n        if (!partial.empty())\r\n        {\r\n            results.emplace(results.begin(), partial);\r\n        }\r\n        else if (endIndex.has_value())\r\n        {\r\n            results.emplace(results.begin(), &begin[0], endIndex.value());\r\n        }\r\n    }\r\n\r\n    return results;\r\n}\r\n\r\nstd::string RingBuffer::Get() const\r\n{\r\n    auto lock = m_lock.lock_shared();\r\n    auto [begin, end] = Contents();\r\n    std::string data;\r\n    data.reserve(begin.size() + end.size());\r\n    data.append(begin.data(), begin.size());\r\n    data.append(end.data(), end.size());\r\n    return data;\r\n}\r\n\r\nstd::pair<std::string_view, std::string_view> RingBuffer::Contents() const\r\n{\r\n    std::string_view beginView(m_buffer.data() + m_offset, m_buffer.size() - m_offset);\r\n    std::string_view endView(m_buffer.data(), m_offset);\r\n    return {beginView, endView};\r\n}"
  },
  {
    "path": "src/windows/common/RingBuffer.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    RingBuffer.h\r\n\r\nAbstract:\r\n\r\n    This file contains declarations for the RingBuffer class.\r\n\r\n--*/\r\n\r\n#pragma once\r\n\r\nclass RingBuffer\r\n{\r\npublic:\r\n    RingBuffer() = delete;\r\n    RingBuffer(size_t size);\r\n\r\n    void Insert(std::string_view data);\r\n    std::vector<std::string> GetLastDelimitedStrings(char Delimiter, size_t Count) const;\r\n    std::string Get() const;\r\n\r\nprivate:\r\n    std::pair<std::string_view, std::string_view> Contents() const;\r\n\r\n    mutable wil::srwlock m_lock;\r\n    std::vector<char> m_buffer;\r\n    size_t m_maxSize;\r\n    size_t m_offset;\r\n};"
  },
  {
    "path": "src/windows/common/Stringify.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include <computenetwork.h>\n\nnamespace wsl::windows::common::stringify {\n\nconstexpr auto ToString(NL_NETWORK_CONNECTIVITY_LEVEL_HINT level) noexcept\n{\n    switch (level)\n    {\n    case NetworkConnectivityLevelHintNone:;\n        return \"None\";\n    case NetworkConnectivityLevelHintLocalAccess:\n        return \"LocalAccess\";\n    case NetworkConnectivityLevelHintInternetAccess:\n        return \"InternetAccess\";\n    case NetworkConnectivityLevelHintConstrainedInternetAccess:\n        return \"ConstrainedInternetAccess\";\n    case NetworkConnectivityLevelHintHidden:\n        return \"Hidden\";\n    default:\n        return \"Unknown\";\n    }\n}\n\nconstexpr auto ToString(NL_NETWORK_CONNECTIVITY_COST_HINT cost) noexcept\n{\n    switch (cost)\n    {\n    case NetworkConnectivityCostHintUnrestricted:\n        return \"Unrestricted\";\n    case NetworkConnectivityCostHintFixed:\n        return \"Fixed\";\n    case NetworkConnectivityCostHintVariable:\n        return \"Variable\";\n    default:\n        return \"Unknown\";\n    }\n}\n\nconstexpr auto ToString(ABI::Windows::Networking::Connectivity::NetworkConnectivityLevel connectivityLevel) noexcept\n{\n    using ABI::Windows::Networking::Connectivity::NetworkConnectivityLevel;\n\n    switch (connectivityLevel)\n    {\n    case NetworkConnectivityLevel::NetworkConnectivityLevel_ConstrainedInternetAccess:\n        return \"ConstrainedInternetAccess\";\n    case NetworkConnectivityLevel::NetworkConnectivityLevel_InternetAccess:\n        return \"InternetAccess\";\n    case NetworkConnectivityLevel::NetworkConnectivityLevel_LocalAccess:\n        return \"LocalAccess\";\n    case NetworkConnectivityLevel::NetworkConnectivityLevel_None:\n        return \"None\";\n    default:\n        return \"<unknown NetworkConnectivityLevel>\";\n    }\n}\n\nconstexpr auto HcnNotificationsToString(DWORD notification) noexcept\n{\n    switch (notification)\n    {\n    case HcnNotificationNetworkPreCreate:\n        return \"HcnNotificationNetworkPreCreate\";\n    case HcnNotificationNetworkCreate:\n        return \"HcnNotificationNetworkCreate\";\n    case HcnNotificationNetworkPreDelete:\n        return \"HcnNotificationNetworkPreDelete\";\n    case HcnNotificationNetworkDelete:\n        return \"HcnNotificationNetworkDelete\";\n    case HcnNotificationNamespaceCreate:\n        return \"HcnNotificationNamespaceCreate\";\n    case HcnNotificationNamespaceDelete:\n        return \"HcnNotificationNamespaceDelete\";\n    /// Notifications for HCN_SERVICE handles\n    case 0x00000007:\n        return \"HcnNotificationGuestNetworkServiceCreate\";\n    case 0x00000008:\n        return \"HcnNotificationGuestNetworkServiceDelete\";\n    /// Notifications for HCN_NETWORK handles\n    case 0x00000009:\n        return \"HcnNotificationNetworkEndpointAttached\";\n    case 0x00000010:\n        return \"HcnNotificationNetworkEndpointDetached\";\n    /// Notifications for HCN_GUESTNETWORKSERVICE handles\n    case 0x00000011:\n        return \"HcnNotificationGuestNetworkServiceStateChanged\";\n    case 0x00000012:\n        return \"HcnNotificationGuestNetworkServiceInterfaceStateChanged\";\n    case HcnNotificationServiceDisconnect:\n        return \"HcnNotificationServiceDisconnect\";\n    default:\n        return \"<unknown>\";\n    }\n}\n} // namespace wsl::windows::common::stringify\n"
  },
  {
    "path": "src/windows/common/SubProcess.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    SubProcess.cpp\n\nAbstract:\n\n    This file contains the subprocess helper class implementation.\n\n--*/\n\n#include \"precomp.h\"\n\n#include \"SubProcess.h\"\n\nusing wsl::windows::common::SubProcess;\n\nnamespace {\nwil::unique_file FileFromHandle(wil::unique_hfile& Handle, const char* Mode)\n{\n    using UniqueFd = wil::unique_any<int, decltype(_close), _close, wil::details::pointer_access_all, int, int, -1>;\n\n    UniqueFd Fd(_open_osfhandle(reinterpret_cast<intptr_t>(Handle.get()), 0));\n    THROW_LAST_ERROR_IF(Fd.get() < 0);\n\n    Handle.release();\n\n    wil::unique_file File(_fdopen(Fd.get(), Mode));\n    THROW_LAST_ERROR_IF(!File);\n    Fd.release();\n\n    return File;\n}\n\nstd::wstring ReadFileContent(wil::unique_hfile& Handle)\n{\n    THROW_LAST_ERROR_IF(SetFilePointer(Handle.get(), 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER);\n\n    const auto File = FileFromHandle(Handle, \"r\");\n\n    std::ifstream Stdout(File.get());\n    return wsl::shared::string::MultiByteToWide(std::string(std::istreambuf_iterator<char>(Stdout), {}));\n}\n} // namespace\n\nSubProcess::SubProcess(LPCWSTR ApplicationName, LPCWSTR CommandLine, DWORD Flags, DWORD StartupFlags) :\n    m_applicationName(ApplicationName), m_commandLine(CommandLine), m_flags(Flags), m_startupFlags(StartupFlags)\n{\n}\n\nvoid SubProcess::SetStdHandles(HANDLE Stdin, HANDLE Stdout, HANDLE Stderr)\n{\n    m_stdIn = Stdin;\n    m_stdOut = Stdout;\n    m_stdErr = Stderr;\n}\n\nvoid SubProcess::InheritHandle(HANDLE Handle)\n{\n    // N.B. Trying to inherit the same handle twice will cause CreateProcess to fail with INVALID_ARG.\n    if (std::find(m_inheritHandles.begin(), m_inheritHandles.end(), Handle) == m_inheritHandles.end())\n    {\n        m_inheritHandles.emplace_back(Handle);\n    }\n}\n\nvoid SubProcess::SetPseudoConsole(HPCON Console)\n{\n    m_pseudoConsole = Console;\n}\n\nvoid SubProcess::SetDesktopAppPolicy(DWORD Policy)\n{\n    m_desktopAppPolicy = Policy;\n}\n\nvoid SubProcess::SetEnvironment(LPVOID Environment)\n{\n    m_environment = Environment;\n}\n\nvoid SubProcess::SetWorkingDirectory(LPCWSTR Directory)\n{\n    m_workingDirectory = Directory;\n}\n\nvoid SubProcess::SetFlags(DWORD Flag)\n{\n    WI_SetAllFlags(m_flags, Flag);\n}\n\nvoid SubProcess::SetToken(HANDLE Token)\n{\n    m_token = Token;\n}\n\nvoid SubProcess::SetShowWindow(WORD ShowWindow)\n{\n    m_showWindow = ShowWindow;\n}\n\nwsl::windows::common::helpers::unique_proc_attribute_list SubProcess::BuildProcessAttributes()\n{\n    DWORD attributes = 0;\n    if (!m_inheritHandles.empty())\n    {\n        attributes++;\n    }\n\n    if (m_desktopAppPolicy.has_value())\n    {\n        attributes++;\n    }\n\n    if (m_pseudoConsole != nullptr)\n    {\n        attributes++;\n    }\n\n    if (attributes == 0)\n    {\n        return {};\n    }\n\n    auto list = helpers::CreateProcThreadAttributeList(attributes);\n\n    // Handles to inherit\n    // N.B. Pseudoconsoles can't be passed to PROC_THREAD_ATTRIBUTE_HANDLE_LIST\n    // so if a pseudoconsole is passed, all handles need to be inherited.\n    if (!m_inheritHandles.empty())\n    {\n        THROW_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(\n            list.get(), 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, m_inheritHandles.data(), m_inheritHandles.size() * sizeof(HANDLE), nullptr, nullptr));\n    }\n\n    // Desktop app policy\n    if (m_desktopAppPolicy.has_value())\n    {\n        THROW_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(\n            list.get(), 0, PROC_THREAD_ATTRIBUTE_DESKTOP_APP_POLICY, &m_desktopAppPolicy.value(), sizeof(m_desktopAppPolicy.value()), nullptr, nullptr));\n    }\n\n    // Pseudoconsole\n    if (m_pseudoConsole != nullptr)\n    {\n        THROW_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(\n            list.get(), 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, m_pseudoConsole, sizeof(m_pseudoConsole), nullptr, nullptr));\n    }\n\n    return list;\n}\n\nwil::unique_handle SubProcess::Start()\n{\n    WI_SetFlag(m_flags, EXTENDED_STARTUPINFO_PRESENT);\n\n    STARTUPINFOEX StartupInfo{};\n    StartupInfo.StartupInfo.cb = sizeof(StartupInfo);\n    StartupInfo.StartupInfo.dwFlags = STARTF_USESTDHANDLES | m_startupFlags;\n\n    // N.B. Passing a pseudoconsole requires all standard handles to be null\n    if (m_pseudoConsole == nullptr)\n    {\n        StartupInfo.StartupInfo.hStdInput = ARGUMENT_PRESENT(m_stdIn) ? m_stdIn : GetStdHandle(STD_INPUT_HANDLE);\n        StartupInfo.StartupInfo.hStdOutput = ARGUMENT_PRESENT(m_stdOut) ? m_stdOut : GetStdHandle(STD_OUTPUT_HANDLE);\n        StartupInfo.StartupInfo.hStdError = ARGUMENT_PRESENT(m_stdErr) ? m_stdErr : GetStdHandle(STD_ERROR_HANDLE);\n\n        if (StartupInfo.StartupInfo.hStdInput != nullptr)\n        {\n            InheritHandle(StartupInfo.StartupInfo.hStdInput);\n        }\n\n        if (StartupInfo.StartupInfo.hStdOutput != nullptr)\n        {\n            InheritHandle(StartupInfo.StartupInfo.hStdOutput);\n        }\n\n        if (StartupInfo.StartupInfo.hStdError != nullptr)\n        {\n            InheritHandle(StartupInfo.StartupInfo.hStdError);\n        }\n    }\n\n    StartupInfo.StartupInfo.lpDesktop = const_cast<LPWSTR>(m_desktop);\n\n    if (m_showWindow.has_value())\n    {\n        WI_SetFlag(StartupInfo.StartupInfo.dwFlags, STARTF_USESHOWWINDOW);\n        StartupInfo.StartupInfo.wShowWindow = m_showWindow.value();\n    }\n\n    const auto attributes = BuildProcessAttributes();\n    StartupInfo.lpAttributeList = attributes.get();\n\n    wil::unique_process_information processInfo;\n    THROW_IF_WIN32_BOOL_FALSE_MSG(\n        CreateProcessAsUserW(\n            m_token,\n            m_applicationName,\n            m_commandLine.data(),\n            nullptr,\n            nullptr,\n            !m_inheritHandles.empty(),\n            m_flags,\n            m_environment,\n            m_workingDirectory,\n            &StartupInfo.StartupInfo,\n            &processInfo),\n        \"ApplicationName: %ls, CommandLine: %ls, WorkingDirectory: %ls\",\n        m_applicationName,\n        m_commandLine.c_str(),\n        m_workingDirectory != nullptr ? m_workingDirectory : L\"<null>\");\n\n    wil::unique_handle createdProcess{processInfo.hProcess};\n\n    // Make sure that the process handle doesn't get closed on return\n    processInfo.hProcess = nullptr;\n\n    return createdProcess;\n}\n\nDWORD SubProcess::GetExitCode(HANDLE Process, DWORD Timeout)\n{\n    const auto status = WaitForSingleObject(Process, Timeout);\n    THROW_HR_IF(HRESULT_FROM_NT(ERROR_TIMEOUT), status == WAIT_TIMEOUT);\n    THROW_LAST_ERROR_IF(status != WAIT_OBJECT_0);\n\n    DWORD exitCode{};\n    THROW_IF_WIN32_BOOL_FALSE(GetExitCodeProcess(Process, &exitCode));\n    return exitCode;\n}\n\nDWORD SubProcess::Run(DWORD Timeout)\n{\n    return GetExitCode(Start().get(), Timeout);\n}\n\nSubProcess::ProcessOutput SubProcess::RunAndCaptureOutput(DWORD Timeout, HANDLE StdErr)\n{\n    //\n    // Using pipes could cause a deadlock if the process writes more bytes\n    // than the size of the pipe buffer. Using two files to prevent that.\n    //\n\n    using wsl::windows::common::filesystem::TempFile;\n    const auto flags = filesystem::TempFileFlags::DeleteOnClose | filesystem::TempFileFlags::InheritHandle;\n    auto stdoutFile = filesystem::TempFile(GENERIC_ALL, 0, OPEN_EXISTING, flags);\n    m_stdOut = stdoutFile.Handle.get();\n\n    std::optional<filesystem::TempFile> stderrFile;\n    if (StdErr == nullptr)\n    {\n        stderrFile = filesystem::TempFile(GENERIC_ALL, 0, OPEN_EXISTING, flags);\n    }\n\n    m_stdErr = stderrFile ? stderrFile->Handle.get() : StdErr;\n\n    const DWORD ExitCode = GetExitCode(Start().get(), Timeout);\n    ProcessOutput output{ExitCode, ReadFileContent(stdoutFile.Handle), stderrFile ? ReadFileContent(stderrFile->Handle) : L\"\"};\n\n    // Clear out references to stdout and stderr temp files.\n    m_stdOut = nullptr;\n    m_stdErr = nullptr;\n    return output;\n}"
  },
  {
    "path": "src/windows/common/SubProcess.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    SubProcess.h\n\nAbstract:\n\n    This file contains the SubProcess helper class definition.\n\n--*/\n\n#pragma once\n\n#include <wil/resource.h>\n#include <string>\n\nnamespace wsl::windows::common {\n\nclass SubProcess\n{\npublic:\n    struct ProcessOutput\n    {\n        DWORD ExitCode{};\n        std::wstring Stdout;\n        std::wstring Stderr;\n    };\n\n    SubProcess(LPCWSTR ApplicationName, LPCWSTR CommandLine, DWORD Flags = CREATE_UNICODE_ENVIRONMENT, DWORD StartupFlags = STARTF_FORCEOFFFEEDBACK);\n\n    void SetStdHandles(HANDLE Stdin, HANDLE Stdout, HANDLE Stderr);\n    void SetPseudoConsole(HPCON Console);\n    void SetDesktopAppPolicy(DWORD Policy);\n    void InheritHandle(HANDLE Handle);\n    void SetEnvironment(LPVOID Environment);\n    void SetWorkingDirectory(LPCWSTR Directory);\n    void SetToken(HANDLE Token);\n    void SetShowWindow(WORD Show);\n    void SetFlags(DWORD Flag);\n\n    wil::unique_handle Start();\n    DWORD Run(DWORD Timeout = INFINITE);\n\n    ProcessOutput RunAndCaptureOutput(DWORD Timeout = INFINITE, HANDLE StdErr = nullptr);\n\n    static DWORD GetExitCode(HANDLE Process, DWORD Timeout = INFINITE);\n\nprivate:\n    helpers::unique_proc_attribute_list BuildProcessAttributes();\n\n    LPCWSTR m_applicationName = nullptr;\n    std::wstring m_commandLine;\n    LPVOID m_environment = nullptr;\n    LPCWSTR m_workingDirectory = nullptr;\n    LPCWSTR m_desktop = nullptr;\n    HANDLE m_token = nullptr;\n    DWORD m_flags = 0;\n    DWORD m_startupFlags = 0;\n\n    HANDLE m_stdIn = nullptr;\n    HANDLE m_stdOut = nullptr;\n    HANDLE m_stdErr = nullptr;\n    HPCON m_pseudoConsole = nullptr;\n    std::optional<DWORD> m_desktopAppPolicy;\n    std::optional<WORD> m_showWindow;\n    std::vector<HANDLE> m_inheritHandles;\n};\n\n} // namespace wsl::windows::common"
  },
  {
    "path": "src/windows/common/VirtioNetworking.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"precomp.h\"\n#include \"VirtioNetworking.h\"\n#include \"GuestDeviceManager.h\"\n#include \"Stringify.h\"\n#include \"stringshared.h\"\n\nusing namespace wsl::core::networking;\nusing namespace wsl::shared;\nusing namespace wsl::windows::common::stringify;\nusing wsl::core::VirtioNetworking;\n\nstatic constexpr auto c_eth0DeviceName = L\"eth0\";\nstatic constexpr auto c_loopbackDeviceName = TEXT(LX_INIT_LOOPBACK_DEVICE_NAME);\n\nVirtioNetworking::VirtioNetworking(\n    GnsChannel&& gnsChannel, VirtioNetworkingFlags flags, LPCWSTR dnsOptions, std::shared_ptr<GuestDeviceManager> guestDeviceManager, wil::shared_handle userToken) :\n    m_guestDeviceManager(std::move(guestDeviceManager)),\n    m_userToken(std::move(userToken)),\n    m_gnsChannel(std::move(gnsChannel)),\n    m_flags(flags),\n    m_dnsOptions(dnsOptions)\n{\n}\n\nVirtioNetworking::~VirtioNetworking()\n{\n    // Unregister the network notification callback to prevent it from using the GNS channel.\n    m_networkNotifyHandle.reset();\n\n    // Stop the GNS channel to unblock any stuck communications with the guest.\n    m_gnsChannel.Stop();\n}\n\nvoid VirtioNetworking::Initialize()\n{\n    // Initialize adapter state.\n    RefreshGuestConnection();\n\n    if (WI_IsFlagSet(m_flags, VirtioNetworkingFlags::LocalhostRelay))\n    {\n        SetupLoopbackDevice();\n    }\n\n    THROW_IF_WIN32_ERROR(NotifyNetworkConnectivityHintChange(&VirtioNetworking::OnNetworkConnectivityChange, this, TRUE, &m_networkNotifyHandle));\n}\n\nvoid VirtioNetworking::TraceLoggingRundown() noexcept\n{\n    auto lock = m_lock.lock_exclusive();\n\n    WSL_LOG(\"VirtioNetworking::TraceLoggingRundown\", TRACE_NETWORKSETTINGS_OBJECT(m_networkSettings));\n}\n\nvoid VirtioNetworking::FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message)\n{\n    message.NetworkingMode = LxMiniInitNetworkingModeVirtioProxy;\n    message.DisableIpv6 = WI_IsFlagClear(m_flags, VirtioNetworkingFlags::Ipv6);\n    message.EnableDhcpClient = false;\n    message.PortTrackerType = LX_MINI_INIT_PORT_TRACKER_TYPE::LxMiniInitPortTrackerTypeMirrored;\n}\n\nvoid VirtioNetworking::StartPortTracker(wil::unique_socket&& socket)\n{\n    WI_ASSERT(!m_gnsPortTrackerChannel.has_value());\n\n    m_gnsPortTrackerChannel.emplace(\n        std::move(socket),\n        [&](const SOCKADDR_INET& addr, int protocol, bool allocate) { return HandlePortNotification(addr, protocol, allocate); },\n        [](const std::string&, bool) {}); // TODO: reconsider if InterfaceStateCallback is needed.\n}\n\nvoid NETIOAPI_API_ VirtioNetworking::OnNetworkConnectivityChange(PVOID context, NL_NETWORK_CONNECTIVITY_HINT hint)\n{\n    static_cast<VirtioNetworking*>(context)->RefreshGuestConnection();\n}\n\nHRESULT VirtioNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int protocol, bool allocate) const noexcept\n{\n    if (addr.si_family == AF_INET6 && WI_IsFlagClear(m_flags, VirtioNetworkingFlags::Ipv6))\n    {\n        return S_OK;\n    }\n\n    int result = 0;\n    const auto ipAddress = (addr.si_family == AF_INET) ? reinterpret_cast<const void*>(&addr.Ipv4.sin_addr)\n                                                       : reinterpret_cast<const void*>(&addr.Ipv6.sin6_addr);\n    const bool loopback = INET_IS_ADDR_LOOPBACK(addr.si_family, ipAddress);\n    const bool unspecified = INET_IS_ADDR_UNSPECIFIED(addr.si_family, ipAddress);\n    if (addr.si_family == AF_INET && loopback)\n    {\n        // Only intercepting 127.0.0.1; any other loopback address will remain on 'lo'.\n        if (addr.Ipv4.sin_addr.s_addr != htonl(INADDR_LOOPBACK))\n        {\n            return result;\n        }\n    }\n\n    if (WI_IsFlagSet(m_flags, VirtioNetworkingFlags::LocalhostRelay) && (unspecified || loopback))\n    {\n        SOCKADDR_INET localAddr = addr;\n        if (!loopback)\n        {\n            INETADDR_SETLOOPBACK(reinterpret_cast<PSOCKADDR>(&localAddr));\n            if (addr.si_family == AF_INET)\n            {\n                localAddr.Ipv4.sin_port = addr.Ipv4.sin_port;\n            }\n            else\n            {\n                localAddr.Ipv6.sin6_port = addr.Ipv6.sin6_port;\n            }\n        }\n        result = ModifyOpenPorts(c_loopbackDeviceName, localAddr, protocol, allocate);\n        LOG_HR_IF_MSG(\n            E_FAIL, result != S_OK, \"Failure adding localhost relay port %d\", INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&localAddr)));\n    }\n\n    if (!loopback)\n    {\n        const int localResult = ModifyOpenPorts(c_eth0DeviceName, addr, protocol, allocate);\n        LOG_HR_IF_MSG(E_FAIL, localResult != S_OK, \"Failure adding relay port %d\", INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&addr)));\n        if (result == 0)\n        {\n            result = localResult;\n        }\n    }\n\n    return result;\n}\n\nint VirtioNetworking::ModifyOpenPorts(_In_ PCWSTR tag, _In_ const SOCKADDR_INET& addr, _In_ int protocol, _In_ bool isOpen) const\n{\n    if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)\n    {\n        LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), \"Unsupported bind protocol %d\", protocol);\n        return 0;\n    }\n\n    auto lock = m_lock.lock_exclusive();\n    const auto server = m_guestDeviceManager->GetRemoteFileSystem(VIRTIO_NET_CLASS_ID, c_defaultDeviceTag);\n    if (server)\n    {\n        std::wstring portString = std::format(L\"tag={};port_number={}\", tag, INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&addr)));\n        if (protocol == IPPROTO_UDP)\n        {\n            portString += L\";udp\";\n        }\n\n        if (!isOpen)\n        {\n            portString += L\";allocate=false\";\n        }\n        else\n        {\n            const auto addrStr = wsl::windows::common::string::SockAddrInetToWstring(addr);\n            portString += std::format(L\";listen_addr={}\", addrStr);\n        }\n\n        LOG_IF_FAILED(server->AddShare(portString.c_str(), nullptr, 0));\n    }\n\n    return 0;\n}\n\nvoid VirtioNetworking::RefreshGuestConnection() noexcept\ntry\n{\n    // Query current networking information before acquiring the lock.\n    auto networkSettings = GetHostEndpointSettings();\n\n    std::wstring device_options;\n    auto appendOption = [&device_options](std::wstring_view key, std::wstring_view value) {\n        if (!value.empty())\n        {\n            std::format_to(std::back_inserter(device_options), L\"{}{}={}\", device_options.empty() ? L\"\" : L\";\", key, value);\n        }\n    };\n\n    appendOption(L\"client_ip\", networkSettings->PreferredIpAddress.AddressString);\n    appendOption(L\"client_mac\", networkSettings->MacAddress);\n\n    std::wstring default_route = networkSettings->GetBestGatewayAddressString();\n    appendOption(L\"gateway_ip\", default_route);\n    appendOption(L\"gateway_mac\", networkSettings->GetBestGatewayMacAddress(AF_INET));\n\n    if (WI_IsFlagSet(m_flags, VirtioNetworkingFlags::Ipv6))\n    {\n        appendOption(L\"client_ip_ipv6\", networkSettings->PreferredIpv6Address.AddressString);\n        appendOption(L\"gateway_mac_ipv6\", networkSettings->GetBestGatewayMacAddress(AF_INET6));\n    }\n\n    networking::DnsInfo currentDns{};\n    if (WI_IsFlagSet(m_flags, VirtioNetworkingFlags::DnsTunneling))\n    {\n        currentDns = networking::HostDnsInfo::GetDnsTunnelingSettings(default_route);\n    }\n    else\n    {\n        wsl::core::networking::DnsSettingsFlags dnsFlags = networking::DnsSettingsFlags::IncludeVpn;\n        WI_SetFlagIf(dnsFlags, networking::DnsSettingsFlags::IncludeIpv6Servers, WI_IsFlagSet(m_flags, VirtioNetworkingFlags::Ipv6));\n        currentDns = networking::HostDnsInfo::GetDnsSettings(dnsFlags);\n    }\n\n    const auto minMtu = GetMinimumConnectedInterfaceMtu();\n\n    // Acquire the lock and perform device updates.\n    auto lock = m_lock.lock_exclusive();\n\n    // Add virtio net adapter to guest. If the adapter already exists update adapter state.\n    if (device_options != m_trackedDeviceOptions)\n    {\n        m_trackedDeviceOptions = device_options;\n        if (!m_adapterId.has_value())\n        {\n            m_adapterId = m_guestDeviceManager->AddGuestDevice(\n                VIRTIO_NET_DEVICE_ID, VIRTIO_NET_CLASS_ID, c_eth0DeviceName, nullptr, device_options.c_str(), 0, m_userToken.get());\n        }\n        else\n        {\n            const auto server = m_guestDeviceManager->GetRemoteFileSystem(VIRTIO_NET_CLASS_ID, c_defaultDeviceTag);\n            if (server)\n            {\n                LOG_IF_FAILED(server->AddSharePath(c_eth0DeviceName, device_options.c_str(), 0));\n            }\n        }\n    }\n\n    UpdateIpv4Address(networkSettings->PreferredIpAddress);\n    if (WI_IsFlagSet(m_flags, VirtioNetworkingFlags::Ipv6))\n    {\n        UpdateIpv6Address(networkSettings->PreferredIpv6Address);\n    }\n\n    UpdateDefaultRoute(default_route);\n\n    UpdateDnsSettings(currentDns);\n    UpdateMtu(minMtu);\n\n    m_networkSettings = std::move(networkSettings);\n}\nCATCH_LOG();\n\nvoid VirtioNetworking::SetupLoopbackDevice()\n{\n    m_localhostAdapterId = m_guestDeviceManager->AddGuestDevice(\n        VIRTIO_NET_DEVICE_ID,\n        VIRTIO_NET_CLASS_ID,\n        c_loopbackDeviceName,\n        nullptr,\n        L\"client_ip=127.0.0.1;client_mac=00:11:22:33:44:55\",\n        0,\n        m_userToken.get());\n\n    // The loopback gateway (see LX_INIT_IPV4_LOOPBACK_GATEWAY_ADDRESS) is 169.254.73.152, so assign loopback0 an\n    // address of 169.254.73.153 with a netmask of 30 so that the only addresses associated with this adapter are\n    // itself and the gateway.\n    // N.B. The MAC address is advertised with the virtio device so doesn't need to be explicitly set.\n    hns::HNSEndpoint endpointProperties;\n    endpointProperties.ID = m_localhostAdapterId.value();\n    endpointProperties.IPAddress = L\"169.254.73.153\";\n    endpointProperties.PrefixLength = 30;\n    endpointProperties.PortFriendlyName = c_loopbackDeviceName;\n    m_gnsChannel.SendEndpointState(endpointProperties);\n\n    hns::CreateDeviceRequest createLoopbackDevice;\n    createLoopbackDevice.deviceName = c_loopbackDeviceName;\n    createLoopbackDevice.type = hns::DeviceType::Loopback;\n    createLoopbackDevice.lowerEdgeAdapterId = m_localhostAdapterId.value();\n    constexpr auto loopbackType = GnsMessageType(createLoopbackDevice);\n    m_gnsChannel.SendNetworkDeviceMessage(loopbackType, ToJsonW(createLoopbackDevice).c_str());\n}\n\nvoid VirtioNetworking::SendDefaultRoute(const std::wstring& gateway, hns::ModifyRequestType requestType)\n{\n    if (gateway.empty() || !m_adapterId.has_value())\n    {\n        return;\n    }\n\n    wsl::shared::hns::Route route;\n    route.NextHop = gateway;\n    route.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_PREFIX;\n    route.Family = AF_INET;\n\n    hns::ModifyGuestEndpointSettingRequest<hns::Route> request;\n    request.RequestType = requestType;\n    request.ResourceType = hns::GuestEndpointResourceType::Route;\n    request.Settings = route;\n    m_gnsChannel.SendHnsNotification(ToJsonW(request).c_str(), m_adapterId.value());\n}\n\nvoid VirtioNetworking::UpdateDefaultRoute(const std::wstring& gateway)\n{\n    if (gateway == m_trackedDefaultRoute || !m_adapterId.has_value())\n    {\n        return;\n    }\n\n    SendDefaultRoute(m_trackedDefaultRoute, hns::ModifyRequestType::Remove);\n    m_trackedDefaultRoute = gateway;\n    SendDefaultRoute(gateway, hns::ModifyRequestType::Add);\n}\n\nvoid VirtioNetworking::UpdateDnsSettings(const networking::DnsInfo& dns)\n{\n    if (dns == m_trackedDnsSettings || !m_adapterId.has_value())\n    {\n        return;\n    }\n\n    m_trackedDnsSettings = dns;\n\n    hns::ModifyGuestEndpointSettingRequest<hns::DNS> notification{};\n    notification.RequestType = hns::ModifyRequestType::Update;\n    notification.ResourceType = hns::GuestEndpointResourceType::DNS;\n    notification.Settings = networking::BuildDnsNotification(dns, m_dnsOptions);\n    m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_adapterId.value());\n}\n\nvoid VirtioNetworking::UpdateIpv4Address(const networking::EndpointIpAddress& ipAddress)\n{\n    if (ipAddress == m_trackedIpv4Address || ipAddress.AddressString.empty() || !m_adapterId.has_value())\n    {\n        return;\n    }\n\n    m_trackedIpv4Address = ipAddress;\n\n    // N.B. SendEndpointState triggers SetAdapterConfiguration on the Linux side\n    // which brings the interface UP and configures the full adapter state.\n    hns::HNSEndpoint endpointProperties;\n    endpointProperties.ID = m_adapterId.value();\n    endpointProperties.IPAddress = ipAddress.AddressString;\n    endpointProperties.PrefixLength = ipAddress.PrefixLength;\n    m_gnsChannel.SendEndpointState(endpointProperties);\n}\n\nvoid VirtioNetworking::SendIpv6Address(const networking::EndpointIpAddress& ipAddress, hns::ModifyRequestType requestType)\n{\n    WI_ASSERT(WI_IsFlagSet(m_flags, VirtioNetworkingFlags::Ipv6));\n\n    if (ipAddress.AddressString.empty() || !m_adapterId.has_value())\n    {\n        return;\n    }\n\n    // The HNSEndpoint schema doesn't support IPv6 addresses, so use ModifyGuestEndpointSettingRequest.\n    hns::ModifyGuestEndpointSettingRequest<hns::IPAddress> request;\n    request.RequestType = requestType;\n    request.ResourceType = hns::GuestEndpointResourceType::IPAddress;\n    request.Settings.Address = ipAddress.AddressString;\n    request.Settings.Family = ipAddress.Address.si_family;\n    request.Settings.OnLinkPrefixLength = ipAddress.PrefixLength;\n    request.Settings.PreferredLifetime = ULONG_MAX;\n    m_gnsChannel.SendHnsNotification(ToJsonW(request).c_str(), m_adapterId.value());\n}\n\nvoid VirtioNetworking::UpdateIpv6Address(const networking::EndpointIpAddress& ipAddress)\n{\n    WI_ASSERT(WI_IsFlagSet(m_flags, VirtioNetworkingFlags::Ipv6));\n\n    if (ipAddress == m_trackedIpv6Address || !m_adapterId.has_value())\n    {\n        return;\n    }\n\n    SendIpv6Address(m_trackedIpv6Address, hns::ModifyRequestType::Remove);\n    m_trackedIpv6Address = ipAddress;\n    SendIpv6Address(ipAddress, hns::ModifyRequestType::Add);\n}\n\nvoid VirtioNetworking::UpdateMtu(std::optional<ULONG> mtu)\n{\n    if (!mtu || mtu.value() == m_networkMtu || !m_adapterId.has_value())\n    {\n        return;\n    }\n\n    m_networkMtu = mtu.value();\n\n    hns::ModifyGuestEndpointSettingRequest<hns::NetworkInterface> notification{};\n    notification.ResourceType = hns::GuestEndpointResourceType::Interface;\n    notification.RequestType = hns::ModifyRequestType::Update;\n    notification.Settings.Connected = true;\n    notification.Settings.NlMtu = m_networkMtu;\n    m_gnsChannel.SendHnsNotification(ToJsonW(notification).c_str(), m_adapterId.value());\n}\n"
  },
  {
    "path": "src/windows/common/VirtioNetworking.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include \"INetworkingEngine.h\"\n#include \"GnsChannel.h\"\n#include \"WslCoreHostDnsInfo.h\"\n#include \"GnsPortTrackerChannel.h\"\n#include \"GuestDeviceManager.h\"\n\nnamespace wsl::core {\n\nenum class VirtioNetworkingFlags\n{\n    None = 0x0,\n    LocalhostRelay = 0x1,\n    DnsTunneling = 0x2,\n    Ipv6 = 0x4,\n};\nDEFINE_ENUM_FLAG_OPERATORS(VirtioNetworkingFlags);\n\nclass VirtioNetworking : public INetworkingEngine\n{\npublic:\n    VirtioNetworking(GnsChannel&& gnsChannel, VirtioNetworkingFlags flags, LPCWSTR dnsOptions, std::shared_ptr<GuestDeviceManager> guestDeviceManager, wil::shared_handle userToken);\n    ~VirtioNetworking();\n\n    // Note: This class cannot be moved because m_networkNotifyHandle captures a 'this' pointer.\n    VirtioNetworking(const VirtioNetworking&) = delete;\n    VirtioNetworking(VirtioNetworking&&) = delete;\n    VirtioNetworking& operator=(const VirtioNetworking&) = delete;\n    VirtioNetworking& operator=(VirtioNetworking&&) = delete;\n\n    // INetworkingEngine\n    void Initialize() override;\n    void TraceLoggingRundown() noexcept override;\n    void FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message) override;\n    void StartPortTracker(wil::unique_socket&& socket) override;\n\nprivate:\n    static void NETIOAPI_API_ OnNetworkConnectivityChange(PVOID context, NL_NETWORK_CONNECTIVITY_HINT hint);\n\n    HRESULT HandlePortNotification(const SOCKADDR_INET& addr, int protocol, bool allocate) const noexcept;\n    int ModifyOpenPorts(_In_ PCWSTR tag, _In_ const SOCKADDR_INET& addr, _In_ int protocol, _In_ bool isOpen) const;\n    void RefreshGuestConnection() noexcept;\n    void SetupLoopbackDevice();\n    void SendDefaultRoute(const std::wstring& gateway, wsl::shared::hns::ModifyRequestType requestType);\n    void SendIpv6Address(const networking::EndpointIpAddress& ipAddress, wsl::shared::hns::ModifyRequestType requestType);\n    void UpdateDefaultRoute(const std::wstring& gateway);\n    void UpdateDnsSettings(const networking::DnsInfo& dns);\n    void UpdateIpv4Address(const networking::EndpointIpAddress& ipAddress);\n    void UpdateIpv6Address(const networking::EndpointIpAddress& ipAddress);\n    void UpdateMtu(std::optional<ULONG> mtu);\n\n    mutable wil::srwlock m_lock;\n\n    std::shared_ptr<GuestDeviceManager> m_guestDeviceManager;\n    wil::shared_handle m_userToken;\n    GnsChannel m_gnsChannel;\n    std::optional<GnsPortTrackerChannel> m_gnsPortTrackerChannel;\n    std::shared_ptr<networking::NetworkSettings> m_networkSettings;\n    VirtioNetworkingFlags m_flags = VirtioNetworkingFlags::None;\n    LPCWSTR m_dnsOptions = nullptr;\n    std::optional<GUID> m_localhostAdapterId;\n    std::optional<GUID> m_adapterId;\n\n    ULONG m_networkMtu = 0;\n    std::wstring m_trackedDeviceOptions;\n    networking::EndpointIpAddress m_trackedIpv4Address{};\n    networking::EndpointIpAddress m_trackedIpv6Address{};\n    std::wstring m_trackedDefaultRoute;\n    networking::DnsInfo m_trackedDnsSettings{};\n\n    // Note: this field must be destroyed first to stop the callbacks before any other field is destroyed.\n    networking::unique_notify_handle m_networkNotifyHandle;\n};\n\n} // namespace wsl::core\n"
  },
  {
    "path": "src/windows/common/WslClient.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslClient.cpp\n\nAbstract:\n\n    This file contains the logic for WSL client entry points.\n\n--*/\n\n#include \"precomp.h\"\n#include \"install.h\"\n#include \"WslInstall.h\"\n#include \"HandleConsoleProgressBar.h\"\n#include \"Distribution.h\"\n#include \"CommandLine.h\"\n#include <conio.h>\n\n#define BASH_PATH L\"/bin/bash\"\n\nusing winrt::Windows::Foundation::Uri;\nusing winrt::Windows::Management::Deployment::DeploymentOptions;\nusing wsl::shared::Localization;\nusing wsl::windows::common::ClientExecutionContext;\nusing wsl::windows::common::Context;\nusing namespace wsl::windows::common;\nusing namespace wsl::shared;\nusing namespace wsl::windows::common::distribution;\n\nstatic bool g_promptBeforeExit = false;\n\nnamespace {\n\nenum Entrypoint\n{\n    Bash,\n    Wsl,\n    Wslconfig,\n    Wslg\n};\n\nstruct LaunchProcessOptions\n{\n    std::wstring CurrentWorkingDirectory;\n    std::optional<GUID> DistroGuid;\n    std::wstring Username;\n    ULONG LaunchFlags = LXSS_LAUNCH_FLAG_ENABLE_INTEROP | LXSS_LAUNCH_FLAG_TRANSLATE_ENVIRONMENT;\n};\n\nstruct ListOptions\n{\n    bool verbose;\n    bool quiet;\n    bool running;\n    bool all;\n    bool online;\n};\n\nstruct ShellExecOptions\n{\n    std::optional<bool> UseShell;\n    std::optional<bool> Login;\n\n    bool DefaultUseShell = true;\n    bool DefaultLogin = false;\n\n    bool IsLogin() const\n    {\n        return Login.value_or(DefaultLogin);\n    }\n\n    bool IsUseShell() const\n    {\n        return UseShell.value_or(DefaultUseShell);\n    }\n\n    void SetExecMode()\n    {\n        UseShell = false;\n        Login = false;\n    }\n\n    void ParseShellOptionArg(std::wstring_view Argument)\n    {\n        if (Argument == WSL_SHELL_OPTION_ARG_LOGIN_OPTION)\n        {\n            UseShell = true;\n            Login = true;\n        }\n        else if (Argument == WSL_SHELL_OPTION_ARG_NOSHELL_OPTION)\n        {\n            SetExecMode();\n        }\n        else if (Argument == WSL_SHELL_OPTION_ARG_STANDARD_OPTION)\n        {\n            UseShell = true;\n            Login = false;\n        }\n        else\n        {\n            THROW_HR(E_INVALIDARG);\n        }\n    }\n};\n\nbool IsInteractiveConsole()\n{\n    const HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE);\n    DWORD mode{};\n\n    return GetFileType(stdinHandle) == FILE_TYPE_CHAR && GetConsoleMode(stdinHandle, &mode);\n}\n\nvoid PromptForKeyPress()\n{\n    if (IsInteractiveConsole())\n    {\n        wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessagePressAnyKeyToExit());\n        LOG_IF_WIN32_BOOL_FALSE(FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)));\n        _getch();\n    }\n}\n\n// Forward function declarations.\nbool InstallPrerequisites(_In_ bool installWslOptionalComponent);\nint LaunchProcess(_In_opt_ LPCWSTR filename, _In_ int argc, _In_reads_(argc) LPCWSTR argv[], _In_ const LaunchProcessOptions& options);\nint ListDistributionsHelper(_In_ ListOptions options);\nLaunchProcessOptions ParseLegacyArguments(_Inout_ std::wstring_view& commandLine);\nDWORD ParseVersionString(_In_ const std::wstring_view& versionString);\nint SetSparse(GUID& distroGuid, bool sparse, bool allowUnsafe);\nint Version();\n\ntemplate <typename T>\nstruct WslVersion\n{\n    T& value;\n\n    int operator()(LPCWSTR Input) const\n    {\n        if (Input == nullptr)\n        {\n            return -1;\n        }\n\n        value = ParseVersionString(Input);\n        return 1;\n    }\n};\n\n// Function definitions.\nint BashMain(_In_ std::wstring_view commandLine)\n{\n    // Call the MSI package if we're in an MSIX context\n    if (wsl::windows::common::wslutil::IsRunningInMsix())\n    {\n        return wsl::windows::common::install::CallMsiPackage();\n    }\n\n    const auto options = ParseLegacyArguments(commandLine);\n\n    // If the command line is empty, construct the arguments in the following\n    // format to launch bash as a login shell:\n    //\n    //     filename = /bin/bash\n    //     argv[0]  = -bash\n    //\n    // N.B. This is the same logic that login uses to launch the shell.\n    //\n    // For non-empty command lines, construct the arguments in the following\n    // format:\n    //\n    //     filename = /bin/bash\n    //     argv[0]  = /bin/bash\n    //     argv[1]  = -c\n    //     argv[2]  = /bin/bash -c \"commandLine\"\n    //\n    // N.B. The arguments are set up this way to leave /bin/bash in charge of\n    //      all argument parsing.\n    int argc = 1;\n    LPCWSTR argv[3];\n    std::wstring arguments;\n    LPCWSTR filename;\n    if (commandLine.empty())\n    {\n        argv[0] = L\"-bash\";\n        filename = BASH_PATH;\n    }\n    else\n    {\n        argc = RTL_NUMBER_OF(argv);\n        arguments = BASH_PATH L\" \";\n        arguments.append(commandLine);\n        argv[0] = BASH_PATH;\n        argv[1] = L\"-c\";\n        argv[2] = arguments.c_str();\n        filename = argv[0];\n    }\n\n    return LaunchProcess(filename, argc, argv, options);\n}\n\nvoid ChangeDirectory(_In_ std::wstring_view argument, _Inout_ LaunchProcessOptions& options)\n{\n    std::wstring directory(wsl::windows::common::string::StripQuotes(argument));\n    THROW_HR_IF(E_INVALIDARG, directory.empty());\n\n    // There are two supported directory arguments:\n    // 1. Any path that begins with a '/' or `~` is assumed to be a Linux path.\n    //    If the path does not exist an error is logged to /dev/kmsg.\n    // 2. Everything else is assumed to be a valid absolute Windows path.\n    if ((directory[0] == L'/') || (directory[0] == L'~'))\n    {\n        options.CurrentWorkingDirectory = std::move(directory);\n    }\n    else\n    {\n        THROW_HR_IF(E_INVALIDARG, !std::filesystem::path(directory).is_absolute());\n\n        THROW_IF_WIN32_BOOL_FALSE(SetCurrentDirectoryW(directory.c_str()));\n    }\n}\n\nint ExportDistribution(_In_ std::wstring_view commandLine)\n{\n    ULONG flags = 0;\n    ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);\n    std::filesystem::path filePath;\n    LPCWSTR name{};\n\n    auto parseFormat = [&flags](LPCWSTR Value) {\n        if (Value == nullptr)\n        {\n            return -1;\n        }\n\n        if (wsl::shared::string::IsEqual(L\"tar.gz\", Value))\n        {\n            WI_SetFlag(flags, LXSS_EXPORT_DISTRO_FLAGS_GZIP);\n        }\n        else if (wsl::shared::string::IsEqual(L\"tar.xz\", Value))\n        {\n            WI_SetFlag(flags, LXSS_EXPORT_DISTRO_FLAGS_XZIP);\n        }\n        else if (wsl::shared::string::IsEqual(L\"vhd\", Value))\n        {\n            WI_SetFlag(flags, LXSS_EXPORT_DISTRO_FLAGS_VHD);\n        }\n        else if (!wsl::shared::string::IsEqual(L\"tar\", Value))\n        {\n            THROW_HR(E_INVALIDARG);\n        }\n\n        return 1;\n    };\n\n    parser.AddPositionalArgument(name, 0);\n    parser.AddPositionalArgument(filePath, 1);\n    parser.AddArgument(SetFlag<ULONG, LXSS_EXPORT_DISTRO_FLAGS_VHD>(flags), WSL_EXPORT_ARG_VHD_OPTION);\n    parser.AddArgument(parseFormat, WSL_EXPORT_ARG_FORMAT_OPTION);\n    parser.Parse();\n\n    THROW_HR_IF(\n        WSL_E_INVALID_USAGE,\n        filePath.empty() || (WI_IsFlagSet(flags, LXSS_EXPORT_DISTRO_FLAGS_GZIP) && WI_IsFlagSet(flags, LXSS_EXPORT_DISTRO_FLAGS_VHD)));\n\n    // Determine if the target is stdout, or an on-disk file.\n    wil::unique_hfile file;\n    HANDLE fileHandle;\n    if (filePath.wstring() == WSL_EXPORT_ARG_STDOUT)\n    {\n        fileHandle = GetStdHandle(STD_OUTPUT_HANDLE);\n    }\n    else\n    {\n        file.reset(CreateFileW(\n            filePath.c_str(), GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_DELETE), nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));\n\n        THROW_LAST_ERROR_IF(!file);\n\n        fileHandle = file.get();\n    }\n\n    // Delete the target if export was unsuccessful.\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\n        if (file)\n        {\n            LOG_IF_WIN32_BOOL_FALSE(DeleteFileW(filePath.c_str()));\n        }\n    });\n\n    // Export the distribution.\n    wsl::windows::common::SvcComm service;\n    const GUID distroId = service.GetDistributionId(name);\n\n    {\n        using wsl::windows::common::HandleConsoleProgressBar;\n\n        HandleConsoleProgressBar exportProgress(fileHandle, Localization::MessageExportProgress(), HandleConsoleProgressBar::Format::FileSize);\n        THROW_IF_FAILED(service.ExportDistribution(&distroId, fileHandle, flags));\n    }\n\n    if (file)\n    {\n        wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    }\n\n    cleanup.release();\n    return 0;\n}\n\nint ImportDistribution(_In_ std::wstring_view commandLine)\n{\n    ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);\n    LPCWSTR name{};\n    std::optional<std::wstring> installPath{};\n    std::filesystem::path filePath;\n    ULONG flags = LXSS_IMPORT_DISTRO_FLAGS_NO_OOBE;\n    DWORD version = LXSS_WSL_VERSION_DEFAULT;\n\n    parser.AddPositionalArgument(name, 0);\n    parser.AddPositionalArgument(AbsolutePath(installPath), 1);\n    parser.AddPositionalArgument(filePath, 2);\n    parser.AddArgument(WslVersion(version), WSL_IMPORT_ARG_VERSION);\n    parser.AddArgument(SetFlag<ULONG, LXSS_IMPORT_DISTRO_FLAGS_VHD>{flags}, WSL_IMPORT_ARG_VHD);\n\n    parser.Parse();\n\n    if (name == nullptr || !installPath.has_value() || filePath.empty())\n    {\n        THROW_HR(E_INVALIDARG);\n    }\n\n    // Ensure that the install path exists.\n    bool directoryCreated = true;\n    if (!CreateDirectoryW(installPath->c_str(), nullptr))\n    {\n        if (GetLastError() == ERROR_ALREADY_EXISTS)\n        {\n            directoryCreated = false;\n        }\n        else\n        {\n            THROW_LAST_ERROR_MSG(\"CreateDirectoryW\");\n        }\n    }\n\n    auto directory_cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [directoryCreated, &installPath]() {\n        if (directoryCreated)\n        {\n            LOG_IF_WIN32_BOOL_FALSE(RemoveDirectory(installPath->c_str()));\n        }\n    });\n\n    // Determine if the source of the tar file is stdin, or an on-disk file.\n    wil::unique_hfile file;\n    HANDLE fileHandle;\n    if (filePath.wstring() == WSL_IMPORT_ARG_STDIN)\n    {\n        fileHandle = GetStdHandle(STD_INPUT_HANDLE);\n    }\n    else\n    {\n        if (WI_IsFlagClear(flags, LXSS_IMPORT_DISTRO_FLAGS_VHD))\n        {\n            // Fail if expecting a tar, but the file name has the .vhd or .vhdx extension.\n            if (wsl::windows::common::wslutil::IsVhdFile(filePath))\n            {\n                wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessagePassVhdFlag());\n                return -1;\n            }\n        }\n\n        file.reset(CreateFileW(\n            filePath.c_str(), GENERIC_READ, (FILE_SHARE_READ | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));\n\n        THROW_LAST_ERROR_IF(!file);\n\n        fileHandle = file.get();\n    }\n\n    // Register the distribution.\n    {\n        wsl::windows::common::HandleConsoleProgressBar progressBar(fileHandle, Localization::MessageImportProgress());\n        wsl::windows::common::SvcComm service;\n        service.RegisterDistribution(name, version, fileHandle, installPath->c_str(), flags);\n    }\n\n    directory_cleanup.release();\n    wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    return 0;\n}\n\nint ImportDistributionInplace(_In_ std::wstring_view commandLine)\n{\n    // Parse the command line.\n    int argc = 0;\n    const wil::unique_hlocal_ptr<LPWSTR[]> argv{CommandLineToArgvW(std::wstring(commandLine).c_str(), &argc)};\n    THROW_LAST_ERROR_IF(!argv);\n\n    THROW_HR_IF(WSL_E_INVALID_USAGE, argc != 2);\n\n    const auto name(argv[0]);\n    const auto filePath = wsl::windows::common::filesystem::GetFullPath(argv[1]);\n\n    wsl::windows::common::SvcComm service;\n    service.ImportDistributionInplace(name, filePath.c_str());\n    wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    return 0;\n}\n\nint LaunchElevated(_In_ LPCWSTR commandLine)\n{\n    wsl::windows::common::wslutil::PrintMessage(\n        wsl::windows::common::wslutil::GetSystemErrorString(HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED)));\n\n    // Add the attach parent process argument to the command line and shell execute an elevated version of wsl.exe.\n    std::wstring arguments;\n    arguments += WSL_PARENT_CONSOLE_ARG L\" \";\n    arguments += std::to_wstring(GetCurrentProcessId());\n    arguments += L\" \";\n    arguments += commandLine;\n\n    const auto path = wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle());\n    SHELLEXECUTEINFOW execInfo{};\n    execInfo.cbSize = sizeof(execInfo);\n    execInfo.fMask = (SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE | SEE_MASK_FLAG_NO_UI);\n    execInfo.lpFile = path.c_str();\n    execInfo.lpVerb = L\"runas\";\n    execInfo.nShow = SW_HIDE;\n    execInfo.lpParameters = arguments.c_str();\n    THROW_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&execInfo));\n    const wil::unique_handle process{execInfo.hProcess};\n\n    // Get the process exit code.\n    WI_VERIFY(WaitForSingleObject(process.get(), INFINITE) == WAIT_OBJECT_0);\n\n    DWORD exitCode;\n    THROW_IF_WIN32_BOOL_FALSE(GetExitCodeProcess(process.get(), &exitCode));\n    return static_cast<int>(exitCode);\n}\n\nint Install(_In_ std::wstring_view commandLine)\n{\n\n    // Parse options.\n    std::optional<std::wstring> distroArgument;\n    std::optional<std::wstring> fromFile;\n    std::optional<std::wstring> name;\n    std::optional<std::filesystem::path> location;\n    std::optional<ULONG> version;\n    std::optional<uint64_t> vhdSize;\n    bool fixedVhd = false;\n    bool installWslOptionalComponent = false;\n    bool noLaunchAfterInstall = false;\n    bool noDistribution = false;\n    bool legacy = false;\n    bool webDownload = IsWindowsServer();\n\n    ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);\n    parser.AddPositionalArgument(distroArgument, 0);\n    parser.AddArgument(distroArgument, WSL_INSTALL_ARG_DIST_OPTION_LONG, WSL_INSTALL_ARG_DIST_OPTION);\n    parser.AddArgument(noLaunchAfterInstall, WSL_INSTALL_ARG_NO_LAUNCH_OPTION_LONG, WSL_INSTALL_ARG_NO_LAUNCH_OPTION);\n    parser.AddArgument(webDownload, WSL_INSTALL_ARG_WEB_DOWNLOAD_LONG);\n    parser.AddArgument(noDistribution, WSL_INSTALL_ARG_NO_DISTRIBUTION_OPTION);\n    parser.AddArgument(installWslOptionalComponent, WSL_INSTALL_ARG_ENABLE_WSL1_LONG);\n    parser.AddArgument(NoOp{}, WSL_INSTALL_ARG_PRERELEASE_LONG); // Unused but handled because argument may be present when invoked from inbox.\n    parser.AddArgument(fromFile, WSL_INSTALL_ARG_FROM_FILE_LONG, WSL_INSTALL_ARG_FROM_FILE_OPTION);\n    parser.AddArgument(name, WSL_INSTALL_ARG_NAME_LONG);\n    parser.AddArgument(AbsolutePath(location), WSL_INSTALL_ARG_LOCATION_LONG, WSL_INSTALL_ARG_LOCATION_OPTION);\n    parser.AddArgument(legacy, WSL_INSTALL_ARG_LEGACY_LONG);\n    parser.AddArgument(WslVersion(version), WSL_INSTALL_ARG_VERSION);\n    parser.AddArgument(g_promptBeforeExit, WSL_INSTALL_ARG_PROMPT_BEFORE_EXIT_OPTION);\n    parser.AddArgument(SizeString(vhdSize), WSL_INSTALL_ARG_VHD_SIZE);\n    parser.AddArgument(fixedVhd, WSL_INSTALL_ARG_FIXED_VHD);\n\n    parser.Parse();\n\n    if (noDistribution && distroArgument.has_value())\n    {\n        THROW_HR_WITH_USER_ERROR(\n            E_INVALIDARG, Localization::MessageArgumentsNotValidTogether(WSL_INSTALL_ARG_NO_DISTRIBUTION_OPTION, WSL_INSTALL_ARG_DIST_OPTION_LONG));\n    }\n\n    if (fixedVhd && !vhdSize.has_value())\n    {\n        THROW_HR_WITH_USER_ERROR(E_INVALIDARG, Localization::MessageArgumentNotValidWithout(WSL_INSTALL_ARG_FIXED_VHD, WSL_INSTALL_ARG_VHD_SIZE));\n    }\n\n    // A distribution to be installed can be specified in three ways:\n    // wsl.exe --install --distribution Ubuntu\n    // wsl.exe --install Ubuntu\n    // wsl.exe --install\n    //\n    // N.B. The legacy method (specifying --distribution) is no longer documented,\n    // but is still supported to avoid breaking existing scripts.\n    if (fromFile.has_value())\n    {\n        if (distroArgument.has_value())\n        {\n            THROW_HR_WITH_USER_ERROR(\n                E_INVALIDARG, Localization::MessageArgumentsNotValidTogether(WSL_INSTALL_ARG_FROM_FILE_LONG, WSL_INSTALL_ARG_DIST_OPTION_LONG));\n        }\n\n        wil::unique_hfile diskFile;\n        HANDLE file{};\n        if (fromFile.value() == WSL_IMPORT_ARG_STDIN)\n        {\n            file = GetStdHandle(STD_INPUT_HANDLE);\n            fromFile = L\"<stdin>\";\n        }\n        else\n        {\n            diskFile.reset(CreateFileW(\n                fromFile->c_str(), GENERIC_READ, (FILE_SHARE_READ | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));\n\n            THROW_LAST_ERROR_IF(!diskFile);\n\n            file = diskFile.get();\n        }\n\n        wsl::windows::common::wslutil::PrintMessage(Localization::MessageInstalling(fromFile->c_str()));\n        wsl::windows::common::HandleConsoleProgressBar progressBar(file, Localization::MessageImportProgress());\n\n        SvcComm service;\n        auto [id, installedName] = service.RegisterDistribution(\n            name.has_value() ? name->c_str() : nullptr,\n            version.value_or(LXSS_WSL_VERSION_DEFAULT),\n            file,\n            location.has_value() ? location->c_str() : nullptr,\n            fixedVhd ? LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD : 0,\n            vhdSize);\n\n        wsl::windows::common::wslutil::PrintMessage(Localization::MessageDistributionInstalled(installedName.get()), stdout);\n\n        if (!noLaunchAfterInstall)\n        {\n            wsl::windows::common::wslutil::PrintMessage(Localization::MessageLaunchingDistro(installedName.get()), stdout);\n\n            LaunchProcessOptions options{};\n            options.DistroGuid = id;\n            return LaunchProcess(nullptr, 0, nullptr, options);\n        }\n\n        return 0;\n    }\n\n    bool rebootRequired = InstallPrerequisites(installWslOptionalComponent);\n    noLaunchAfterInstall |= rebootRequired;\n\n    // Install a distribution only if no reboot is required, or if we're on the --legacy path (to maintain old behavior).\n    const Distribution* legacyDistro = nullptr;\n\n    WslInstall::InstallResult installResult{};\n    if (!noDistribution && (legacy || !rebootRequired))\n    {\n        auto result = WslInstall::InstallDistribution(\n            installResult, distroArgument, version, !noLaunchAfterInstall, webDownload, legacy, fixedVhd, name, location, vhdSize);\n\n        std::optional<std::wstring> flavor;\n        if (installResult.Distribution.has_value())\n        {\n            if (const auto* distro = std::get_if<ModernDistributionVersion>(&*installResult.Distribution))\n            {\n                flavor = distro->Name;\n            }\n            else\n            {\n                legacyDistro = std::get_if<Distribution>(&*installResult.Distribution);\n                WI_ASSERT(legacyDistro != nullptr);\n\n                flavor = legacyDistro->Name;\n            }\n        }\n\n        // Logs when a specific distribution is installed, and whether that was successful. Used to report distro usage to distro maintainers\n        WSL_LOG_TELEMETRY(\n            \"InstallDistribution\",\n            PDT_ProductAndServiceUsage,\n            TraceLoggingValue(result, \"result\"),\n            TraceLoggingValue(legacyDistro == nullptr, \"modern\"),\n            TraceLoggingValue(flavor.value_or(L\"<none>\").c_str(), \"flavor\"));\n\n        THROW_IF_FAILED(result);\n    }\n\n    if (rebootRequired)\n    {\n        wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS_REBOOT_REQUIRED);\n    }\n    else if (noDistribution)\n    {\n        wsl::windows::common::wslutil::PrintSystemError(NO_ERROR);\n    }\n    else\n    {\n        if (!installResult.Alreadyinstalled)\n        {\n            wsl::windows::common::wslutil::PrintMessage(Localization::MessageDistributionInstalled(installResult.Name));\n        }\n\n        if (!noLaunchAfterInstall)\n        {\n            wsl::windows::common::wslutil::PrintMessage(Localization::MessageLaunchingDistro(installResult.Name), stdout);\n\n            if (legacyDistro != nullptr)\n            {\n                wsl::windows::common::distribution::Launch(*legacyDistro, installResult.InstalledViaGitHub, !installResult.Alreadyinstalled);\n            }\n            else\n            {\n                LaunchProcessOptions options{};\n                options.DistroGuid = installResult.Id.value();\n\n                return LaunchProcess(nullptr, 0, nullptr, options);\n            }\n        }\n    }\n\n    return 0;\n}\n\nbool InstallPrerequisites(_In_ bool installWslOptionalComponent)\n{\n    const auto [rebootRequired, missingComponents] = WslInstall::CheckForMissingOptionalComponents(installWslOptionalComponent);\n    if (missingComponents.empty())\n    {\n        return rebootRequired;\n    }\n\n    // Install any optional components that have not yet been installed.\n    const auto token = wil::open_current_access_token();\n    if (!wsl::windows::common::security::IsTokenElevated(token.get()))\n    {\n        const auto elevatedCommand = std::format(\n            L\"{} {} {}\", WSL_INSTALL_ARG, WSL_INSTALL_ARG_NO_DISTRIBUTION_OPTION, installWslOptionalComponent ? WSL_INSTALL_ARG_ENABLE_WSL1_LONG : L\"\");\n\n        const auto exitCode = LaunchElevated(elevatedCommand.c_str());\n        if (exitCode != 0)\n        {\n            return exitCode;\n        }\n    }\n    else\n    {\n        WslInstall::InstallOptionalComponents(missingComponents);\n    }\n\n    return rebootRequired;\n}\n\nint LaunchProcess(_In_opt_ LPCWSTR filename, _In_ int argc, _In_reads_(argc) LPCWSTR argv[], _In_ const LaunchProcessOptions& options)\n{\n    // Create an instance of the specified distribution.\n    //\n    // N.B. If creating the instance fails because the file system needs to\n    //      be upgraded, the appropriate message is displayed before\n    //      re-attempting the create while allowing the upgrade. This is\n    //      only done if running in interactive mode.\n    const LPCGUID distribution = options.DistroGuid.has_value() ? &options.DistroGuid.value() : nullptr;\n    wsl::windows::common::SvcComm service;\n    if (argc == 0)\n    {\n        ClientExecutionContext context;\n        const auto result = service.CreateInstanceNoThrow(distribution, 0, context.OutError());\n        if (FAILED(result))\n        {\n            if (result == WSL_E_FS_UPGRADE_NEEDED)\n            {\n                wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageFsUpgradeNeeded(), stderr);\n            }\n            else\n            {\n                THROW_HR(result);\n            }\n        }\n    }\n\n    const int exitCode = service.LaunchProcess(\n        distribution,\n        filename,\n        argc,\n        argv,\n        options.LaunchFlags,\n        options.Username.empty() ? nullptr : options.Username.c_str(),\n        options.CurrentWorkingDirectory.empty() ? nullptr : options.CurrentWorkingDirectory.c_str());\n\n    THROW_HR_IF(WSL_E_USER_NOT_FOUND, (exitCode == LX_INIT_USER_NOT_FOUND));\n    THROW_HR_IF(WSL_E_TTY_LIMIT, (exitCode == LX_INIT_TTY_LIMIT));\n\n    return exitCode;\n}\n\nint ListDistributions(_In_ std::wstring_view commandLine)\n{\n    ListOptions options{};\n    ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);\n    parser.AddArgument(options.all, WSL_LIST_ARG_ALL_OPTION);\n    parser.AddArgument(options.running, WSL_LIST_ARG_RUNNING_OPTION);\n    parser.AddArgument(options.quiet, WSL_LIST_ARG_QUIET_OPTION_LONG, WSL_LIST_ARG_QUIET_OPTION);\n    parser.AddArgument(options.verbose, WSL_LIST_ARG_VERBOSE_OPTION_LONG, WSL_LIST_ARG_VERBOSE_OPTION);\n    parser.AddArgument(options.online, WSL_LIST_ARG_ONLINE_OPTION_LONG, WSL_LIST_ARG_ONLINE_OPTION);\n\n    parser.Parse();\n\n    return ListDistributionsHelper(options);\n}\n\nint ListDistributionsHelper(_In_ ListOptions options)\n{\n    // Handle invalid options.\n    THROW_HR_IF(\n        WSL_E_INVALID_USAGE,\n        ((options.quiet && options.verbose) || (options.all && options.running)) || ((options.verbose || options.all) && options.online));\n\n    // Query all registered distributions and sort the list so the default\n    // (if present) is first.\n    wsl::windows::common::SvcComm service;\n    auto distros = service.EnumerateDistributions();\n    std::sort(distros.begin(), distros.end(), [](const auto& Left, const auto&) {\n        return (WI_IsFlagSet(Left.Flags, LXSS_ENUMERATE_FLAGS_DEFAULT));\n    });\n\n    if (options.verbose)\n    {\n        THROW_HR_IF(WSL_E_DEFAULT_DISTRO_NOT_FOUND, distros.empty());\n\n        // Determine max length of a distro name and construct the format string.\n        size_t maxLength = wcslen(WSL_LIST_HEADER_NAME);\n        std::for_each(distros.begin(), distros.end(), [&](const auto& entry) {\n            const size_t length = wcslen(entry.DistroName);\n            if (length > maxLength)\n            {\n                maxLength = length;\n            }\n        });\n\n        std::wstring formatString(L\"%s %-\");\n        formatString += std::to_wstring(maxLength + 4);\n        formatString += L\"s%-16s%s\\n\";\n\n        // Print distribution information.\n        wprintf(formatString.c_str(), L\" \", WSL_LIST_HEADER_NAME, WSL_LIST_HEADER_STATE, WSL_LIST_HEADER_VERSION);\n        std::for_each(distros.begin(), distros.end(), [&](const auto& entry) {\n            const LPCWSTR defaultDistro = WI_IsFlagSet(entry.Flags, LXSS_ENUMERATE_FLAGS_DEFAULT) ? L\"*\" : L\" \";\n            const std::wstring version(std::to_wstring(entry.Version));\n            auto state = L\"Stopped\";\n            switch (entry.State)\n            {\n            case LxssDistributionStateRunning:\n                state = L\"Running\";\n                break;\n\n            case LxssDistributionStateInstalling:\n                state = L\"Installing\";\n                break;\n\n            case LxssDistributionStateUninstalling:\n                state = L\"Uninstalling\";\n                break;\n\n            case LxssDistributionStateConverting:\n                state = L\"Converting\";\n                break;\n\n            case LxssDistributionStateExporting:\n                state = L\"Exporting\";\n                break;\n\n            default:\n                break;\n            }\n\n            wprintf(formatString.c_str(), defaultDistro, entry.DistroName, state, version.c_str());\n        });\n    }\n    else if (!options.online)\n    {\n        if (options.running)\n        {\n            std::erase_if(distros, [&](const auto& entry) { return (entry.State != LxssDistributionStateRunning); });\n\n            if ((!options.quiet) && (distros.empty()))\n            {\n                wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageNoRunningDistro());\n                return -1;\n            }\n        }\n\n        if (!options.all)\n        {\n            std::erase_if(distros, [&](const auto& entry) {\n                return (\n                    (entry.State == LxssDistributionStateInstalling) || (entry.State == LxssDistributionStateUninstalling) ||\n                    (entry.State == LxssDistributionStateConverting) || (entry.State == LxssDistributionStateExporting));\n            });\n        }\n\n        if (!options.quiet)\n        {\n            THROW_HR_IF(WSL_E_DEFAULT_DISTRO_NOT_FOUND, distros.empty());\n\n            wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageRegisteredDistrosHeader());\n        }\n\n        std::for_each(distros.begin(), distros.end(), [&](const auto& entry) {\n            if ((!options.quiet) && WI_IsFlagSet(entry.Flags, LXSS_ENUMERATE_FLAGS_DEFAULT))\n            {\n                wsl::windows::common::wslutil::PrintMessage(Localization::MessagePrintDistroDefault(entry.DistroName), stdout);\n            }\n            else\n            {\n                wprintf(L\"%s\\n\", entry.DistroName);\n            }\n        });\n    }\n    else\n    {\n        std::vector<std::pair<std::wstring, std::wstring>> names;\n\n        size_t maxLength = wcslen(WSL_LIST_HEADER_NAME);\n\n        auto appendIfNotPresent = [&](const std::wstring& name, const std::wstring& friendlyName) {\n            auto pred = [&name](const auto& e) { return e.first == name; };\n\n            if (std::find_if(names.begin(), names.end(), pred) == names.end())\n            {\n                names.emplace_back(name, friendlyName);\n\n                if (name.size() > maxLength)\n                {\n                    maxLength = name.size();\n                }\n            }\n        };\n\n        auto readNames = [&](const DistributionList& distributions) {\n            if (distributions.ModernDistributions.has_value())\n            {\n                for (const auto& [name, versions] : *distributions.ModernDistributions)\n                {\n                    for (auto i = 0; i < versions.size(); i++)\n                    {\n                        if (!options.all && i > 3)\n                        {\n                            break; // Only show 3 entries per distro unless --all is passed.\n                        }\n\n                        appendIfNotPresent(versions[i].Name, versions[i].FriendlyName);\n                    }\n                }\n            }\n\n            if (distributions.Distributions.has_value())\n            {\n                for (const auto& e : *distributions.Distributions)\n                {\n                    appendIfNotPresent(e.Name, e.FriendlyName);\n                }\n            }\n        };\n\n        const auto manifest = wsl::windows::common::distribution::GetAvailable();\n        if (manifest.OverrideManifest.has_value())\n        {\n            readNames(*manifest.OverrideManifest);\n        }\n\n        readNames(manifest.Manifest);\n\n        std::wstring formatString(L\"%-\");\n        formatString += std::to_wstring(maxLength + 4);\n        formatString += L\"s%s\\n\";\n\n        wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageDistributionListOnline(WSL_INSTALL_ARG));\n        wprintf(formatString.c_str(), WSL_LIST_HEADER_NAME, WSL_LIST_HEADER_FRIENDLY_NAME);\n        std::for_each(names.begin(), names.end(), [&](const auto& entry) {\n            wprintf(formatString.c_str(), entry.first.c_str(), entry.second.c_str());\n        });\n    }\n\n    return 0;\n}\n\nint Manage(_In_ std::wstring_view commandLine)\n{\n    LPCWSTR distribution{};\n    std::optional<bool> sparse;\n    std::optional<std::wstring> move;\n    std::optional<std::wstring> defaultUser;\n    std::optional<uint64_t> resize;\n    bool allowUnsafe = false;\n\n    ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME, 0);\n    parser.AddPositionalArgument(distribution, 0);\n    parser.AddArgument(ParsedBool(sparse), WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG, WSL_MANAGE_ARG_SET_SPARSE_OPTION);\n    parser.AddArgument(AbsolutePath(move), WSL_MANAGE_ARG_MOVE_OPTION_LONG, WSL_MANAGE_ARG_MOVE_OPTION);\n    parser.AddArgument(defaultUser, WSL_MANAGE_ARG_SET_DEFAULT_USER_OPTION_LONG);\n    parser.AddArgument(SizeString(resize), WSL_MANAGE_ARG_RESIZE_OPTION_LONG, WSL_MANAGE_ARG_RESIZE_OPTION);\n    parser.AddArgument(allowUnsafe, WSL_MANAGE_ARG_ALLOW_UNSAFE);\n    parser.Parse();\n\n    THROW_HR_IF(WSL_E_INVALID_USAGE, distribution == nullptr);\n\n    wsl::windows::common::SvcComm service;\n    auto distroGuid = service.GetDistributionId(distribution);\n\n    if (sparse.has_value() + move.has_value() + defaultUser.has_value() + resize.has_value() != 1)\n    {\n        THROW_HR(WSL_E_INVALID_USAGE);\n    }\n\n    if (sparse)\n    {\n        SetSparse(distroGuid, sparse.value(), allowUnsafe);\n    }\n    else if (move)\n    {\n        service.MoveDistribution(distroGuid, move->c_str());\n    }\n    else if (defaultUser)\n    {\n        auto wslExe = wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle());\n\n        auto commandLine = std::format(\n            L\"\\\"{}\\\" {} -u root /usr/bin/id -u -- '{}'\",\n            wslExe,\n            wsl::shared::string::GuidToString<wchar_t>(distroGuid),\n            defaultUser.value());\n\n        wsl::windows::common::SubProcess process{wslExe.c_str(), commandLine.c_str()};\n\n        auto result = process.RunAndCaptureOutput(INFINITE, GetStdHandle(STD_ERROR_HANDLE));\n        if (result.ExitCode != 0)\n        {\n            return result.ExitCode;\n        }\n\n        while (!result.Stdout.empty() && (result.Stdout.back() == '\\r' || result.Stdout.back() == '\\n'))\n        {\n            result.Stdout.pop_back();\n        }\n\n        wchar_t* endPtr{};\n        auto newUid = std::wcstoul(result.Stdout.c_str(), &endPtr, 10);\n\n        THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), endPtr != result.Stdout.c_str() + result.Stdout.size());\n\n        service.ConfigureDistribution(&distroGuid, newUid, LXSS_DISTRO_FLAGS_UNCHANGED);\n    }\n    else if (resize)\n    {\n        THROW_IF_FAILED(service.ResizeDistribution(&distroGuid, resize.value()));\n    }\n\n    wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    return 0;\n}\n\nint Mount(_In_ std::wstring_view commandLine)\n{\n    bool vhd = false;\n    bool bare = false;\n    std::optional<std::wstring> options;\n    ULONG partition = 0;\n    std::optional<std::wstring> type;\n    std::optional<std::wstring> name;\n    std::wstring disk;\n\n    ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);\n    parser.AddArgument(bare, WSL_MOUNT_ARG_BARE_OPTION_LONG);\n    parser.AddArgument(vhd, WSL_MOUNT_ARG_VHD_OPTION_LONG);\n    parser.AddArgument(options, WSL_MOUNT_ARG_OPTIONS_OPTION_LONG, WSL_MOUNT_ARG_OPTIONS_OPTION);\n    parser.AddArgument(Integer(partition), WSL_MOUNT_ARG_PARTITION_OPTION_LONG, WSL_MOUNT_ARG_PARTITION_OPTION);\n    parser.AddArgument(type, WSL_MOUNT_ARG_TYPE_OPTION_LONG, WSL_MOUNT_ARG_TYPE_OPTION);\n    parser.AddArgument(name, WSL_MOUNT_ARG_NAME_OPTION_LONG, WSL_MOUNT_ARG_NAME_OPTION);\n    parser.AddPositionalArgument(UnquotedPath(disk), 0);\n    parser.Parse();\n\n    THROW_HR_IF(WSL_E_INVALID_USAGE, disk.empty());\n\n    ULONG flags = 0;\n    if (vhd)\n    {\n        WI_SetFlag(flags, LXSS_ATTACH_MOUNT_FLAGS_VHD);\n        disk = wsl::windows::common::filesystem::GetFullPath(disk.c_str()).wstring();\n    }\n    else\n    {\n        WI_SetFlag(flags, LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH);\n    }\n\n    // First attach the disk to the vm\n    wsl::windows::common::SvcComm service;\n    const auto result = service.AttachDisk(disk.c_str(), flags);\n    if (FAILED(result))\n    {\n        THROW_HR_IF(result, bare);\n\n        // In the case of a non-bare mount, WSL_E_DISK_ALREADY_ATTACHED and LXSS_E_USER_VHD_ALREADY_ATTACHED are\n        // ok to ignore because the user can mount more than one partition on the same disk\n        // (so that disk might be already attached).\n        THROW_HR_IF(result, result != WSL_E_DISK_ALREADY_ATTACHED && result != WSL_E_USER_VHD_ALREADY_ATTACHED);\n    }\n\n    // Perform the mount\n    if (!bare)\n    {\n        const auto mountResult = service.MountDisk(\n            disk.c_str(),\n            flags,\n            partition,\n            name.has_value() ? name->c_str() : nullptr,\n            type.has_value() ? type->c_str() : nullptr,\n            options.has_value() ? options->c_str() : nullptr);\n\n        if (mountResult.Result != 0)\n        {\n            wsl::windows::common::wslutil::PrintMessage(\n                Localization::MessageDiskMountFailed(strerror(-mountResult.Result), WSL_UNMOUNT_ARG, disk), stdout);\n            return 1;\n        }\n        else\n        {\n            wsl::windows::common::wslutil::PrintMessage(\n                Localization::MessageDiskMounted(mountResult.MountName.get(), WSL_UNMOUNT_ARG, disk), stdout);\n        }\n    }\n    else\n    {\n        wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    }\n\n    return 0;\n}\n\nLaunchProcessOptions ParseLegacyArguments(_Inout_ std::wstring_view& commandLine)\n{\n    // Strip the executable name. Because this has to be a legal file name, quoted parts cannot contain escaped quotes.\n    BOOLEAN inQuotes = FALSE;\n    while ((!commandLine.empty()) && ((inQuotes != FALSE) || (!LXSS_IS_WHITESPACE(commandLine[0]))))\n    {\n        if (commandLine[0] == L'\"')\n        {\n            inQuotes = !inQuotes;\n        }\n\n        commandLine = commandLine.substr(1);\n    }\n\n    // Strip any leading whitespace.\n    commandLine = wsl::windows::common::string::StripLeadingWhitespace(commandLine);\n\n    // Check for a distribution GUID as the first parameter and strip it out if present.\n    auto argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n    auto distroGuid = wsl::shared::string::ToGuid(argument);\n    if (distroGuid.has_value())\n    {\n        commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n    }\n\n    // Check for the home directory parameter and strip it out if present.\n    std::wstring currentWorkingDirectory;\n    argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n    if (argument == WSL_CWD_HOME)\n    {\n        currentWorkingDirectory = WSL_CWD_HOME;\n        commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n    }\n\n    return {std::move(currentWorkingDirectory), std::move(distroGuid)};\n}\n\nDWORD\nParseVersionString(_In_ const std::wstring_view& versionString)\n{\n    DWORD version;\n    const auto result = wil::ResultFromException([&]() { version = std::stoi(std::wstring(versionString)); });\n    THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR), (FAILED(result) || ((version != LXSS_WSL_VERSION_1) && (version != LXSS_WSL_VERSION_2))));\n\n    return version;\n}\n\nint SetDefaultDistribution(_In_ LPCWSTR distributionName)\n{\n    wsl::windows::common::SvcComm service;\n    const GUID distroGuid = service.GetDistributionId(distributionName);\n    service.SetDefaultDistribution(&distroGuid);\n    wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    return 0;\n}\n\nint SetDefaultVersion(_In_ std::wstring_view commandLine)\n{\n    const auto argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n    const auto version = ParseVersionString(argument);\n    if (version == LXSS_WSL_VERSION_1)\n    {\n        THROW_HR_IF(WSL_E_WSL1_NOT_SUPPORTED, !wsl::windows::common::helpers::IsWslOptionalComponentPresent());\n    }\n    else\n    {\n        WI_ASSERT(version == LXSS_WSL_VERSION_2);\n\n        wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageVmModeConversionInfo());\n    }\n\n    const wil::unique_hkey lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\n    wsl::windows::common::registry::WriteDword(lxssKey.get(), nullptr, LXSS_WSL_DEFAULT_VERSION, version);\n    wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    return 0;\n}\n\nint Shutdown(_In_ std::wstring_view commandLine)\n{\n    bool force = false;\n    ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);\n    parser.AddArgument(force, WSL_SHUTDOWN_OPTION_FORCE);\n\n    parser.Parse();\n\n    wsl::windows::common::SvcComm service;\n    service.Shutdown(force);\n\n    return 0;\n}\n\nint SetSparse(GUID& distroGuid, bool sparse, bool allowUnsafe)\n{\n    wsl::windows::common::SvcComm service;\n\n    auto setProgress = wsl::windows::common::ConsoleProgressIndicator(wsl::shared::Localization::MessageConversionStart());\n    THROW_IF_FAILED(service.SetSparse(&distroGuid, sparse, allowUnsafe));\n\n    return 0;\n}\n\nint SetVersion(_In_ std::wstring_view commandLine)\n{\n    auto argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n    if (argument.empty())\n    {\n        wsl::windows::common::wslutil::PrintMessage(Localization::MessageRequiredParameterMissing(WSL_SET_VERSION_ARG), stdout);\n        return -1;\n    }\n\n    const std::wstring distributionName(argument);\n    wsl::windows::common::SvcComm service;\n    const auto distroGuid = service.GetDistributionId(distributionName.c_str());\n\n    commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n    argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n    const auto version = ParseVersionString(argument);\n    if (version == LXSS_WSL_VERSION_2)\n    {\n        wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageVmModeConversionInfo());\n    }\n\n    auto progress = wsl::windows::common::ConsoleProgressIndicator(wsl::shared::Localization::MessageConversionStart(), true);\n    const auto result = service.SetVersion(&distroGuid, version);\n    progress.End();\n    THROW_IF_FAILED(result);\n\n    wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    return 0;\n}\n\nint Status()\n{\n    // Print the default distro.\n    wsl::windows::common::SvcComm service;\n    const auto distros = service.EnumerateDistributions();\n    for (const auto& entry : distros)\n    {\n        if (WI_IsFlagSet(entry.Flags, LXSS_ENUMERATE_FLAGS_DEFAULT))\n        {\n            wsl::windows::common::wslutil::PrintMessage(Localization::MessageStatusDefaultDistro(entry.DistroName), stdout);\n            break;\n        }\n    }\n\n    // Print the default version.\n    const DWORD version = wsl::windows::common::wslutil::GetDefaultVersion();\n    wsl::windows::common::wslutil::PrintMessage(Localization::MessageStatusDefaultVersion(version), stdout);\n\n    // Print a message if the WSL optional component is not present for WSL1 support.\n    if (!wsl::windows::common::helpers::IsWslOptionalComponentPresent())\n    {\n        wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageWsl1NotSupported());\n    }\n\n    // Print a message if the vmcompute service is present for WSL2 support.\n    if (!wsl::windows::common::helpers::IsServicePresent(L\"vmcompute\"))\n    {\n        wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageEnableVirtualization());\n    }\n\n    return 0;\n}\n\nint TerminateDistribution(_In_ LPCWSTR distributionName)\n{\n    wsl::windows::common::SvcComm service;\n    const GUID distroGuid = service.GetDistributionId(distributionName);\n    service.TerminateInstance(&distroGuid);\n    wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    return 0;\n}\n\nint Unmount(_In_ const std::wstring& arg)\n{\n    const auto* disk = arg.empty() ? nullptr : arg.c_str();\n\n    std::pair<int, int> value;\n    wsl::windows::common::SvcComm service;\n    const HRESULT result = wil::ResultFromException([&] { value = service.DetachDisk(disk); });\n\n    // support relative paths in unmount\n    // check is the result is the error code for \"file not found\" and the path is relative\n    if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && PathIsRelative(disk))\n    {\n        // retry dismounting with the absolute path\n        const auto absoluteDisk = wsl::windows::common::filesystem::GetFullPath(filesystem::UnquotePath(disk).c_str());\n        value = service.DetachDisk(absoluteDisk.c_str());\n    }\n    else if (FAILED(result))\n    {\n        THROW_HR(result);\n    }\n\n    if (value.first != 0)\n    {\n        wsl::windows::common::wslutil::PrintMessage(Localization::MessageDetachFailed(strerror(-value.first), WSL_SHUTDOWN_ARG), stdout);\n        return -1;\n    }\n\n    wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    return 0;\n}\n\nint UnregisterDistribution(_In_ LPCWSTR distributionName)\n{\n    auto progress = wsl::windows::common::ConsoleProgressIndicator(wsl::shared::Localization::MessageStatusUnregistering(), true);\n    wsl::windows::common::SvcComm service;\n    const GUID distroGuid = service.GetDistributionId(distributionName, LXSS_GET_DISTRO_ID_LIST_ALL);\n    service.UnregisterDistribution(&distroGuid);\n    progress.End();\n    wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS);\n    return 0;\n}\n\nint UpdatePackage(std::wstring_view commandLine)\n{\n    ExecutionContext context(wsl::windows::common::UpdatePackage);\n\n    bool preRelease{};\n    ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);\n    parser.AddArgument(preRelease, WSL_UPDATE_ARG_PRE_RELEASE_OPTION_LONG);\n\n    // Options kept for compatibility with inbox WSL.\n    parser.AddArgument(NoOp(), WSL_UPDATE_ARG_WEB_DOWNLOAD_OPTION_LONG);\n    parser.AddArgument(NoOp(), WSL_UPDATE_ARG_CONFIRM_OPTION_LONG);\n    parser.AddArgument(NoOp(), WSL_UPDATE_ARG_PROMPT_OPTION_LONG);\n    parser.Parse();\n\n    return wsl::windows::common::install::UpdatePackage(preRelease, false);\n}\n\nint Uninstall()\n{\n    auto logFile = std::filesystem::temp_directory_path() / L\"wsl-uninstall-logs.txt\";\n    auto clearLogs =\n        wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&logFile]() { LOG_IF_WIN32_BOOL_FALSE(DeleteFile(logFile.c_str())); });\n\n    const auto exitCode = wsl::windows::common::install::UninstallViaMsi(logFile.c_str(), &wsl::windows::common::install::MsiMessageCallback);\n\n    if (exitCode != 0)\n    {\n        clearLogs.release();\n        THROW_HR_WITH_USER_ERROR(\n            HRESULT_FROM_WIN32(exitCode),\n            wsl::shared::Localization::MessageUninstallFailed(exitCode) + L\"\\r\\n\" +\n                wsl::shared::Localization::MessageSeeLogFile(logFile.c_str()));\n    }\n\n    return exitCode;\n}\n\nint Version()\n{\n    // Query the Windows version.\n    const auto windowsVersion = wsl::windows::common::helpers::GetWindowsVersionString();\n    wsl::windows::common::wslutil::PrintMessage(\n        Localization::MessagePackageVersions(\n            WSL_PACKAGE_VERSION, KERNEL_VERSION, WSLG_VERSION, MSRDC_VERSION, DIRECT3D_VERSION, DXCORE_VERSION, windowsVersion),\n        stdout);\n\n    if constexpr (!wsl::shared::OfficialBuild)\n    {\n        // Print additional information if running a debug build.\n        wsl::windows::common::wslutil::PrintMessage(Localization::MessageBuildInfo(_MSC_VER, COMMIT_HASH, __TIME__ \" \" __DATE__), stdout);\n    }\n\n    return 0;\n}\n\nint WslconfigMain(_In_ int argc, _In_reads_(argc) LPWSTR* argv)\n{\n    // Call the MSI package if we're in an MSIX context\n    if (wsl::windows::common::wslutil::IsRunningInMsix())\n    {\n        return wsl::windows::common::install::CallMsiPackage();\n    }\n\n    using wsl::shared::string::IsEqual;\n\n    // Use exit code -1 on generic failures. This was the original exit code and shouldn't be changed, especially since wslconfig.exe is deprecated.\n    int exitCode = -1;\n    if ((argc >= 2) && ((IsEqual(argv[1], WSLCONFIG_COMMAND_LIST, true)) || (IsEqual(argv[1], WSLCONFIG_COMMAND_LIST_SHORT, true))))\n    {\n        ListOptions options{};\n        for (int index = 2; index < argc; index += 1)\n        {\n            std::wstring_view argument = argv[index];\n            if (argument.empty())\n            {\n                break;\n            }\n            if (IsEqual(argument, WSLCONFIG_COMMAND_LIST_ALL, true))\n            {\n                options.all = true;\n            }\n            else if (IsEqual(argument, WSLCONFIG_COMMAND_LIST_RUNNING, true))\n            {\n                options.running = true;\n            }\n            else\n            {\n                THROW_HR(WSL_E_INVALID_USAGE);\n            }\n        }\n\n        exitCode = ListDistributionsHelper(options);\n    }\n    else if ((argc >= 3) && ((IsEqual(argv[1], WSLCONFIG_COMMAND_SET_DEFAULT, true)) || (IsEqual(argv[1], WSLCONFIG_COMMAND_SET_DEFAULT_SHORT, true))))\n    {\n        exitCode = SetDefaultDistribution(argv[2]);\n    }\n    else if ((argc >= 3) && ((IsEqual(argv[1], WSLCONFIG_COMMAND_TERMINATE, true)) || (IsEqual(argv[1], WSLCONFIG_COMMAND_TERMINATE_SHORT, true))))\n    {\n        exitCode = TerminateDistribution(argv[2]);\n    }\n    else if ((argc >= 3) && ((IsEqual(argv[1], WSLCONFIG_COMMAND_UNREGISTER_DISTRIBUTION, true)) || (IsEqual(argv[1], WSLCONFIG_COMMAND_UNREGISTER_DISTRIBUTION_SHORT, true))))\n    {\n        exitCode = UnregisterDistribution(argv[2]);\n    }\n    else\n    {\n        THROW_HR(WSL_E_INVALID_USAGE);\n    }\n\n    return exitCode;\n}\n\nint WslgMain(_In_ std::wstring_view commandLine)\n{\n    // N.B. There is no app execution alias for wslg, so it cannot run in an MSIX context.\n    WI_ASSERT(!wsl::windows::common::wslutil::IsRunningInMsix());\n\n    auto options = ParseLegacyArguments(commandLine);\n\n    // Parse additional arguments.\n    std::wstring_view argument;\n    ShellExecOptions shellExecOptions{};\n    wsl::windows::common::SvcComm service;\n    for (;;)\n    {\n        argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n        if (argument.empty())\n        {\n            break;\n        }\n\n        if ((argument == WSL_DISTRO_ARG) || (argument == WSL_DISTRO_ARG_LONG))\n        {\n            THROW_HR_IF(WSL_E_INVALID_USAGE, options.DistroGuid.has_value());\n\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            THROW_HR_IF(WSL_E_INVALID_USAGE, argument.empty());\n\n            // Query the service for the distribution id.\n            options.DistroGuid = service.GetDistributionId(std::wstring(argument).c_str());\n        }\n        else if (argument == WSL_SHELL_OPTION_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            THROW_HR_IF(E_INVALIDARG, argument.empty());\n\n            shellExecOptions.ParseShellOptionArg(argument);\n        }\n        else if ((argument == WSL_USER_ARG) || (argument == WSL_USER_ARG_LONG))\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            THROW_HR_IF(WSL_E_INVALID_USAGE, argument.empty());\n\n            options.Username = argument;\n        }\n        else if (argument == WSL_CHANGE_DIRECTORY_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine, true);\n            ChangeDirectory(argument, options);\n        }\n        else if (argument == WSL_STOP_PARSING_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            break;\n        }\n        else\n        {\n            THROW_HR_IF(WSL_E_INVALID_USAGE, ((argument.size() > 0) && (argument[0] == L'-')));\n\n            break;\n        }\n\n        commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n    }\n\n    // Launching a graphical application requires a non-empty command line.\n    THROW_HR_IF(WSL_E_INVALID_USAGE, commandLine.empty());\n\n    std::vector<const wchar_t*> arguments;\n    const std::wstring commandLineString{commandLine};\n    wil::unique_hlocal_ptr<LPWSTR[]> execArguments{};\n    LPCWSTR filename{};\n    if (!shellExecOptions.IsUseShell())\n    {\n        int argc;\n        execArguments.reset(CommandLineToArgvW(commandLineString.c_str(), &argc));\n        THROW_HR_IF(E_INVALIDARG, (!execArguments || (argc == 0)));\n\n        arguments.reserve(argc);\n        arguments.insert(arguments.begin(), &execArguments.get()[0], &execArguments.get()[argc]);\n        filename = arguments[0];\n    }\n    else\n    {\n        arguments.push_back(commandLineString.c_str());\n    }\n\n    // Graphical applications by default will use a login shell so that users can modify behavior.\n    shellExecOptions.DefaultUseShell = true;\n    shellExecOptions.DefaultLogin = shellExecOptions.IsUseShell();\n    if (shellExecOptions.IsLogin())\n    {\n        // Launch via the user's default shell in login mode to parse files like /etc/profile.\n        WI_SetFlag(options.LaunchFlags, LXSS_LAUNCH_FLAG_SHELL_LOGIN);\n    }\n\n    return LaunchProcess(filename, gsl::narrow_cast<int>(arguments.size()), arguments.data(), options);\n}\n\nint RunDebugShell()\n{\n    ExecutionContext context(Context::DebugShell);\n\n    auto token = wil::open_current_access_token();\n    auto tokenInfo = wil::get_token_information<TOKEN_USER>(token.get());\n    auto pipePath = wsl::windows::common::wslutil::GetDebugShellPipeName(tokenInfo->User.Sid);\n    wil::unique_hfile pipe{CreateFileW(pipePath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr)};\n\n    if (!pipe)\n    {\n        auto error = GetLastError();\n        if (error == ERROR_ACCESS_DENIED && !wsl::windows::common::security::IsTokenElevated(token.get()))\n        {\n            wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageAdministratorAccessRequiredForDebugShell());\n            return 1;\n        }\n        else if (\n            error == ERROR_FILE_NOT_FOUND &&\n            !wsl::windows::policies::IsFeatureAllowed(wsl::windows::policies::OpenPoliciesKey().get(), wsl::windows::policies::c_allowDebugShellUserSetting))\n        {\n            wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageDebugShellDisabled());\n            return 1;\n        }\n        else\n        {\n            THROW_WIN32(error);\n        }\n    }\n\n    // agetty waits for a LF before printing the prompt, so write it immediately after the pipe is opened.\n    // This is needed because without the '-w' flag, agetty doesn't wait and prints the shell prompt before\n    // a pipe is connected, so it's lost.\n    THROW_IF_WIN32_BOOL_FALSE(WriteFile(pipe.get(), \"\\n\", 1, nullptr, nullptr));\n\n    // Create a thread to relay stdin to the pipe.\n    wsl::windows::common::ConsoleState Io;\n    auto exitEvent = wil::unique_event(wil::EventOptions::ManualReset);\n    std::thread inputThread(\n        [&]() { wsl::windows::common::RelayStandardInput(GetStdHandle(STD_INPUT_HANDLE), pipe.get(), {}, exitEvent.get(), &Io); });\n\n    auto joinThread = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\n        exitEvent.SetEvent();\n        inputThread.join();\n    });\n\n    // Relay the contents of the pipe to stdout.\n    wsl::windows::common::relay::InterruptableRelay(pipe.get(), GetStdHandle(STD_OUTPUT_HANDLE));\n\n    // Print a message that the VM has exited and signal the input thread to exit.\n    fputws(L\"\\n\", stdout);\n    THROW_HR(HCS_E_CONNECTION_CLOSED);\n}\n\nint WslMain(_In_ std::wstring_view commandLine)\n{\n    // Call the MSI package if we're in an MSIX context\n    if (wsl::windows::common::wslutil::IsRunningInMsix())\n    {\n        return wsl::windows::common::install::CallMsiPackage();\n    }\n\n    // Use exit code -1 so invokers of wsl.exe can distinguish between a Linux\n    // process failure and a wsl.exe failure. The distro launcher sample depends\n    // on this specific code.\n    int exitCode = -1;\n\n    // Parse the command line to determine if the legacy distro GUID or the '~' argument were specified.\n    auto options = ParseLegacyArguments(commandLine);\n\n    // Parse additional arguments.\n    std::wstring_view argument;\n    ShellExecOptions shellExecOptions{};\n    for (;;)\n    {\n        argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n        if (argument.empty())\n        {\n            break;\n        }\n\n        if (argument == WSL_DEBUG_SHELL_ARG_LONG)\n        {\n            return RunDebugShell();\n        }\n        else if ((argument == WSL_DISTRO_ARG) || (argument == WSL_DISTRO_ARG_LONG))\n        {\n            // Ensure the distribution has not already been set.\n            if (options.DistroGuid.has_value())\n            {\n                wsl::windows::common::wslutil::PrintMessage(wsl::shared::Localization::MessageDistroAlreadySet());\n                return exitCode;\n            }\n\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            if (argument.empty())\n            {\n                wsl::windows::common::wslutil::PrintMessage(Localization::MessageRequiredParameterMissing(WSL_DISTRO_ARG_LONG), stdout);\n                return exitCode;\n            }\n\n            // Query the service for the distribution id.\n            wsl::windows::common::SvcComm service;\n            options.DistroGuid = service.GetDistributionId(std::wstring(argument).c_str());\n        }\n        else if (argument == WSL_CHANGE_DIRECTORY_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine, true);\n            ChangeDirectory(argument, options);\n        }\n        else if (argument == WSL_DISTRIBUTION_ID_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n\n            if (argument.empty())\n            {\n                wsl::windows::common::wslutil::PrintMessage(Localization::MessageRequiredParameterMissing(WSL_DISTRIBUTION_ID_ARG), stdout);\n                return exitCode;\n            }\n\n            options.DistroGuid = wsl::shared::string::ToGuid(argument);\n            THROW_HR_IF(E_INVALIDARG, !options.DistroGuid.has_value());\n        }\n        else if ((argument == WSL_USER_ARG) || (argument == WSL_USER_ARG_LONG))\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            if (argument.empty())\n            {\n                wsl::windows::common::wslutil::PrintMessage(Localization::MessageRequiredParameterMissing(WSL_USER_ARG_LONG), stdout);\n                return exitCode;\n            }\n\n            options.Username = argument;\n        }\n        else if (argument == WSL_UPDATE_ARG)\n        {\n            return UpdatePackage(commandLine);\n        }\n        else if (argument == WSL_HELP_ARG)\n        {\n            wsl::windows::common::wslutil::PrintMessage(Localization::MessageWslUsage());\n            return exitCode;\n        }\n        else if (argument == WSL_STOP_PARSING_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            break;\n        }\n        else if ((argument == WSL_EXEC_ARG) || (argument == WSL_EXEC_ARG_LONG))\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            shellExecOptions.SetExecMode();\n            break;\n        }\n        else if (argument == WSL_SHELL_OPTION_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            if (argument.empty())\n            {\n                wsl::windows::common::wslutil::PrintMessage(Localization::MessageRequiredParameterMissing(WSL_SHELL_OPTION_ARG), stdout);\n                return exitCode;\n            }\n\n            shellExecOptions.ParseShellOptionArg(argument);\n        }\n        else if (argument == WSL_EXPORT_ARG)\n        {\n            return ExportDistribution(commandLine);\n        }\n        else if (argument == WSL_IMPORT_ARG)\n        {\n            return ImportDistribution(commandLine);\n        }\n        else if (argument == WSL_IMPORT_INPLACE_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            return ImportDistributionInplace(commandLine);\n        }\n        else if ((argument == WSL_LIST_ARG) || (argument == WSL_LIST_ARG_LONG))\n        {\n            return ListDistributions(commandLine);\n        }\n        else if ((argument == WSL_SET_DEFAULT_DISTRO_ARG) || (argument == WSL_SET_DEFAULT_DISTRO_ARG_LEGACY) || (argument == WSL_SET_DEFAULT_DISTRO_ARG_LONG))\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            if (argument.empty())\n            {\n                wsl::windows::common::wslutil::PrintMessage(\n                    Localization::MessageRequiredParameterMissing(WSL_SET_DEFAULT_DISTRO_ARG_LONG), stdout);\n                return exitCode;\n            }\n\n            return SetDefaultDistribution(std::wstring(argument).c_str());\n        }\n        else if (argument == WSL_PARENT_CONSOLE_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            if (argument.empty())\n            {\n                wsl::windows::common::wslutil::PrintMessage(Localization::MessageRequiredParameterMissing(WSL_PARENT_CONSOLE_ARG), stdout);\n                return exitCode;\n            }\n\n            const auto parentProcessId = std::stoi(std::wstring(argument));\n\n            FreeConsole();\n            THROW_IF_WIN32_BOOL_FALSE(AttachConsole(parentProcessId));\n        }\n        else if ((argument == WSL_TERMINATE_ARG) || (argument == WSL_TERMINATE_ARG_LONG))\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            if (argument.empty())\n            {\n                wsl::windows::common::wslutil::PrintMessage(Localization::MessageRequiredParameterMissing(WSL_TERMINATE_ARG_LONG), stdout);\n                return exitCode;\n            }\n\n            return TerminateDistribution(std::wstring(argument).c_str());\n        }\n        else if (argument == WSL_UNREGISTER_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            argument = wsl::windows::common::helpers::ParseArgument(commandLine);\n            if (argument.empty())\n            {\n                wsl::windows::common::wslutil::PrintMessage(Localization::MessageRequiredParameterMissing(WSL_UNREGISTER_ARG), stdout);\n                return exitCode;\n            }\n\n            return UnregisterDistribution(std::wstring(argument).c_str());\n        }\n        else if (argument == WSL_SET_DEFAULT_VERSION_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            return SetDefaultVersion(commandLine);\n        }\n        else if (argument == WSL_SHUTDOWN_ARG)\n        {\n            return Shutdown(commandLine);\n        }\n        else if (argument == WSL_MANAGE_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            return Manage(commandLine);\n        }\n        else if (argument == WSL_SET_VERSION_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            return SetVersion(commandLine);\n        }\n        else if (argument == WSL_MOUNT_ARG)\n        {\n            return Mount(commandLine);\n        }\n        else if (argument == WSL_UNMOUNT_ARG)\n        {\n            commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n            return Unmount(std::wstring(commandLine));\n        }\n        else if (argument == WSL_INSTALL_ARG)\n        {\n            return Install(commandLine);\n        }\n        else if (argument == WSL_SYSTEM_DISTRO_ARG)\n        {\n            WI_SetFlag(options.LaunchFlags, LXSS_LAUNCH_FLAG_USE_SYSTEM_DISTRO);\n        }\n        else if (argument == WSL_STATUS_ARG)\n        {\n            return Status();\n        }\n        else if ((argument == WSL_VERSION_ARG) || (argument == WSL_VERSION_ARG_LONG))\n        {\n            return Version();\n        }\n        else if (argument == WSL_UNINSTALL_ARG)\n        {\n            return Uninstall();\n        }\n        else\n        {\n            if ((argument.size() > 0) && (argument[0] == L'-'))\n            {\n                std::wstring InvalidArgument(argument);\n                wsl::windows::common::wslutil::PrintMessage(Localization::MessageInvalidCommandLine(InvalidArgument, WSL_BINARY_NAME), stdout);\n                return exitCode;\n            }\n\n            break;\n        }\n\n        commandLine = wsl::windows::common::helpers::ConsumeArgument(commandLine, argument);\n    }\n\n    // There are three possible cases:\n    //     1. Empty command line - Launch the default user's default shell.\n    //     2. Exec mode - Call CommandLineToArgvW on the remaining command\n    //        line and pass it along to the create process call.\n    //     3. Non-empty command line - The command is invoked through the\n    //        default user's default shell via '$SHELL -c commandLine'.\n    int argc = 0;\n    LPCWSTR* arguments{};\n    LPCWSTR argv[1];\n    std::wstring commandLineString{commandLine};\n    wil::unique_hlocal_ptr<LPWSTR[]> execArguments{};\n    LPCWSTR filename{};\n    if (!commandLine.empty())\n    {\n        if (!shellExecOptions.IsUseShell())\n        {\n            execArguments.reset(CommandLineToArgvW(commandLineString.c_str(), &argc));\n            THROW_HR_IF(E_INVALIDARG, (!execArguments || (argc == 0)));\n\n            arguments = const_cast<LPCWSTR*>(execArguments.get());\n            filename = arguments[0];\n        }\n        else\n        {\n            argv[0] = commandLineString.c_str();\n            arguments = argv;\n            argc = RTL_NUMBER_OF(argv);\n        }\n    }\n    else\n    {\n        THROW_HR_IF(E_INVALIDARG, !shellExecOptions.IsUseShell());\n    }\n\n    shellExecOptions.DefaultLogin = shellExecOptions.IsUseShell() && commandLine.empty();\n    WI_SetFlagIf(options.LaunchFlags, LXSS_LAUNCH_FLAG_SHELL_LOGIN, shellExecOptions.IsLogin());\n\n    // Launch the process.\n    return LaunchProcess(filename, argc, arguments, options);\n}\n\n} // namespace\n\nint wsl::windows::common::WslClient::Main(_In_ LPCWSTR commandLine)\n{\n    wsl::windows::common::EnableContextualizedErrors(false);\n\n    // Note WslTraceLoggingUninitialize() is a no-op if WslTraceLoggingInitialize was not called.\n    auto cleanupTelemetry = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { WslTraceLoggingUninitialize(); });\n\n    std::optional<wsl::windows::common::ExecutionContext> context;\n    auto entryPoint = Entrypoint::Wsl;\n    DWORD exitCode;\n    HRESULT result = S_OK;\n    try\n    {\n        wsl::windows::common::wslutil::ConfigureCrt();\n        wsl::windows::common::wslutil::InitializeWil();\n        WslTraceLoggingInitialize(LxssTelemetryProvider, !wsl::shared::OfficialBuild);\n\n        // Set CRT encoding.\n        const char* encoding = getenv(\"WSL_UTF8\");\n        if (encoding != nullptr && strcmp(encoding, \"1\") == 0)\n        {\n            wsl::windows::common::wslutil::SetCrtEncoding(_O_U8TEXT);\n        }\n        else\n        {\n            wsl::windows::common::wslutil::SetCrtEncoding(_O_U16TEXT);\n        }\n\n        // Initialize COM.\n        auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);\n        wsl::windows::common::wslutil::CoInitializeSecurity();\n\n        auto cleanupWinrt = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { winrt::clear_factory_cache(); });\n\n        // Initialize winsock.\n        WSADATA data;\n        THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &data));\n\n        // Determine which entrypoint to use.\n        int argc = 0;\n        wil::unique_hlocal_ptr<LPWSTR[]> argv{CommandLineToArgvW(commandLine, &argc)};\n        THROW_HR_IF(E_INVALIDARG, (!argv || (argc == 0)));\n\n        auto fileName = std::filesystem::path(argv[0]).stem().wstring();\n        std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower);\n\n        FILE* warningsFile = nullptr;\n        const char* disableWarnings = getenv(\"WSL_DISABLE_WARNINGS\");\n        if (disableWarnings == nullptr || strcmp(disableWarnings, \"1\") != 0)\n        {\n            warningsFile = stderr;\n        }\n\n        if (fileName == L\"bash\")\n        {\n            entryPoint = Entrypoint::Bash;\n            context.emplace(Context::Bash, warningsFile);\n            exitCode = BashMain(commandLine);\n        }\n        else if (fileName == L\"wslconfig\")\n        {\n            entryPoint = Entrypoint::Wslconfig;\n            context.emplace(Context::WslConfig, warningsFile);\n            exitCode = WslconfigMain(argc, argv.get());\n        }\n        else if (fileName == L\"wslg\")\n        {\n            entryPoint = Entrypoint::Wslg;\n            context.emplace(Context::Wslg, warningsFile);\n            exitCode = WslgMain(commandLine);\n        }\n        else\n        {\n            context.emplace(Context::Wsl, warningsFile);\n            exitCode = WslMain(commandLine);\n        }\n    }\n    catch (...)\n    {\n        // N.B. bash.exe historically has used 1 instead of -1 to indicate failure.\n        exitCode = (entryPoint == Entrypoint::Bash) ? 1 : -1;\n        result = wil::ResultFromCaughtException();\n    }\n\n    // Print error messages for failures.\n    if (FAILED(result))\n    {\n        try\n        {\n            std::wstring errorString{};\n            if (context.has_value() && context->ReportedError().has_value())\n            {\n                auto strings = wsl::windows::common::wslutil::ErrorToString(context->ReportedError().value());\n\n                // Don't print the error code for WSL_E_DEFAULT_DISTRO_NOT_FOUND and WSL_E_INVALID_USAGE to make the error message easier to read.\n                if (context->ReportedError()->Code != WSL_E_DEFAULT_DISTRO_NOT_FOUND && context->ReportedError()->Code != WSL_E_INVALID_USAGE)\n                {\n                    errorString = Localization::MessageErrorCode(strings.Message, strings.Code);\n                }\n                else\n                {\n                    errorString = strings.Message.c_str();\n                }\n\n                // Logs when an error is shown to the user, and what that error is\n                WSL_LOG_TELEMETRY(\n                    \"UserVisibleError\",\n                    PDT_ProductAndServicePerformance,\n                    TraceLoggingLevel(WINEVENT_LEVEL_ERROR),\n                    TraceLoggingValue(strings.Code.c_str(), \"ErrorCode\"));\n            }\n            else\n            {\n                errorString = wsl::windows::common::wslutil::GetErrorString(result);\n            }\n\n            // For wslg.exe, attempt to print the error message to the parent console, if that fails display a messagebox.\n            if ((entryPoint == Entrypoint::Wslg) && (!wsl::windows::common::helpers::TryAttachConsole()))\n            {\n                auto caption = wsl::shared::Localization::AppName();\n                LOG_LAST_ERROR_IF(MessageBoxW(nullptr, errorString.c_str(), caption.c_str(), (MB_OK | MB_ICONEXCLAMATION)) == 0);\n\n                g_promptBeforeExit = false;\n            }\n            else\n            {\n                wsl::windows::common::wslutil::PrintMessage(errorString);\n\n                //\n                // If the app was launched via the start menu tile, prompt for input so the\n                // message does not disappear.\n                // TODO: This should be replaced with launching the WSL Settings app when that is created.\n                //\n\n                if (entryPoint == Entrypoint::Wsl && winrt::Windows::ApplicationModel::AppInstance::GetActivatedEventArgs() != nullptr)\n                {\n                    g_promptBeforeExit = true;\n                }\n            }\n        }\n        CATCH_LOG()\n    }\n\n    if (g_promptBeforeExit)\n    {\n        g_promptBeforeExit = false;\n        PromptForKeyPress();\n    }\n\n    return exitCode;\n}\n"
  },
  {
    "path": "src/windows/common/WslClient.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslClient.h\n\nAbstract:\n\n    This file contains the declaration for WSL client entry points.\n\n--*/\n\n#pragma once\n\nnamespace wsl::windows::common {\nclass WslClient\n{\npublic:\n    static int Main(_In_ LPCWSTR commandLine);\n};\n} // namespace wsl::windows::common\n"
  },
  {
    "path": "src/windows/common/WslCoreConfig.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreConfig.cpp\n\nAbstract:\n\n    This file contains the WSL Core VM configuration helper class definition.\n\n--*/\n\n#include \"precomp.h\"\n#include \"WslCoreConfig.h\"\n#include \"Localization.h\"\n#include \"WslCoreFirewallSupport.h\"\n#include \"WslCoreNetworkingSupport.h\"\n\nconstexpr auto c_natGatewayAddress = L\"NatGatewayIpAddress\";\nconstexpr auto c_natNetwork = L\"NatNetwork\";\nconstexpr auto c_natIpAddress = L\"NatIpAddress\";\n\nwsl::core::Config::Config(_In_opt_ LPCWSTR Path, _In_opt_ HANDLE UserToken)\n{\n    ParseConfigFile(Path, UserToken);\n    Initialize(UserToken);\n}\n\nvoid wsl::core::Config::ParseConfigFile(_In_opt_ LPCWSTR ConfigFilePath, _In_opt_ HANDLE UserToken)\n{\n    windows::common::ExecutionContext context(windows::common::ParseConfig);\n\n    auto parseIgnoredPorts = [&](const char* name, const char* value, const wchar_t* fileName, unsigned long fileLine) {\n        const auto ignoredPortsVector = wsl::shared::string::Split(std::string{value}, ',');\n        for (const auto& portString : ignoredPortsVector)\n        {\n            int number = 0;\n            if (FAILED(wil::ResultFromException([&]() { number = std::stoi(portString); })) || (number <= 0 || number > USHRT_MAX))\n            {\n                EMIT_USER_WARNING(shared::Localization::MessageConfigInvalidInteger(value, name, fileName, fileLine));\n            }\n            else\n            {\n                IgnoredPorts.insert(static_cast<uint16_t>(number));\n            }\n        }\n    };\n\n    auto parseDnsTunnelingIp = [&](const char* name, const char* value, const wchar_t* fileName, unsigned long fileLine) {\n        // If the IP is invalid, DNS tunneling is disabled.\n        in_addr address{};\n\n        if (inet_pton(AF_INET, value, &address) != 1)\n        {\n            EMIT_USER_WARNING(shared::Localization::MessageConfigInvalidIp(value, name, fileName, fileLine));\n            EnableDnsTunneling = false;\n        }\n        else\n        {\n            DnsTunnelingIpAddress = address.S_un.S_addr;\n        }\n    };\n\n    ConfigKeyPresence earlyBootLoggingPresent{};\n    ConfigKeyPresence macAddressPresent{};\n    bool enableFirewall = true;\n    std::wstring userKernelModules;\n\n    std::vector<ConfigKey> keys{\n        ConfigKey(ConfigSetting::Kernel, KernelPath),\n        ConfigKey(ConfigSetting::KernelCommandLine, KernelCommandLine),\n        ConfigKey(ConfigSetting::KernelModules, KernelModulesPath),\n        ConfigKey(ConfigSetting::Memory, MemoryString(MemorySizeBytes)),\n        ConfigKey(ConfigSetting::Processors, ProcessorCount),\n        ConfigKey(ConfigSetting::DebugConsole, EnableDebugConsole),\n        ConfigKey(ConfigSetting::EarlyBootLogging, EnableEarlyBootLogging, &earlyBootLoggingPresent),\n        ConfigKey(ConfigSetting::Swap, MemoryString(SwapSizeBytes)),\n        ConfigKey(ConfigSetting::SwapFile, SwapFilePath),\n        ConfigKey(ConfigSetting::LocalhostForwarding, EnableLocalhostRelay, &LocalhostRelayConfigPresence),\n        ConfigKey(ConfigSetting::NestedVirtualization, EnableNestedVirtualization),\n        ConfigKey(ConfigSetting::Virtio9p, EnableVirtio9p),\n        ConfigKey(ConfigSetting::Virtiofs, EnableVirtioFs),\n        ConfigKey(ConfigSetting::KernelDebugPort, KernelDebugPort),\n        ConfigKey(ConfigSetting::GpuSupport, EnableGpuSupport),\n        ConfigKey(ConfigSetting::GuiApplications, EnableGuiApps),\n        ConfigKey(ConfigSetting::SystemDistro, SystemDistroPath),\n        ConfigKey(ConfigSetting::Telemetry, EnableTelemetry),\n        ConfigKey(ConfigSetting::VmIdleTimeout, VmIdleTimeout),\n        ConfigKey(ConfigSetting::DebugConsoleLogFile, DebugConsoleLogFile),\n        ConfigKey(ConfigSetting::KernelBootTimeout, KernelBootTimeout),\n        ConfigKey(ConfigSetting::DistributionStartTimeout, DistributionStartTimeout),\n        ConfigKey(ConfigSetting::Virtio, EnableVirtio),\n        ConfigKey(ConfigSetting::HostFileSystemAccess, EnableHostFileSystemAccess),\n        ConfigKey(ConfigSetting::MountDeviceTimeout, MountDeviceTimeout),\n        ConfigKey(ConfigSetting::HardwarePerformanceCounters, EnableHardwarePerformanceCounters),\n        ConfigKey(ConfigSetting::VmSwitch, VmSwitch),\n        ConfigKey(ConfigSetting::MacAddress, MacAddress, &macAddressPresent),\n        ConfigKey(ConfigSetting::Dhcp, EnableDhcp),\n        ConfigKey(ConfigSetting::DhcpTimeout, DhcpTimeout),\n        ConfigKey(ConfigSetting::Ipv6, EnableIpv6),\n        ConfigKey(ConfigSetting::DnsProxy, EnableDnsProxy),\n        ConfigKey(ConfigSetting::SafeMode, EnableSafeMode),\n        ConfigKey(ConfigSetting::DefaultVhdSize, MemoryString(VhdSizeBytes)),\n        ConfigKey(ConfigSetting::CrashDumpFolder, CrashDumpFolder),\n        ConfigKey(ConfigSetting::MaxCrashDumpCount, MaxCrashDumpCount),\n        ConfigKey(ConfigSetting::DistributionInstallPath, DefaultDistributionLocation),\n        ConfigKey(ConfigSetting::InstanceIdleTimeout, InstanceIdleTimeout),\n        ConfigKey(ConfigSetting::LoadDefaultKernelModules, LoadDefaultKernelModules, &LoadKernelModulesPresence),\n        ConfigKey(ConfigSetting::LoadKernelModules, userKernelModules, &LoadKernelModulesPresence),\n\n        // Features that were previously experimental (the old header is maintained for compatibility).\n        ConfigKey({ConfigSetting::NetworkingMode, ConfigSetting::Experimental::NetworkingMode}, wsl::core::NetworkingModes, NetworkingMode, &NetworkingModePresence),\n        ConfigKey({ConfigSetting::DnsTunneling, ConfigSetting::Experimental::DnsTunneling}, EnableDnsTunneling, &DnsTunnelingConfigPresence),\n        ConfigKey({ConfigSetting::Firewall, ConfigSetting::Experimental::Firewall}, enableFirewall, &FirewallConfigPresence),\n        ConfigKey({ConfigSetting::AutoProxy, ConfigSetting::Experimental::AutoProxy}, EnableAutoProxy),\n\n        // Experimental features.\n        ConfigKey(ConfigSetting::Experimental::AutoMemoryReclaim, wsl::core::MemoryReclaimModes, MemoryReclaim),\n        ConfigKey(ConfigSetting::Experimental::SparseVhd, EnableSparseVhd),\n        ConfigKey(ConfigSetting::Experimental::BestEffortDnsParsing, BestEffortDnsParsing),\n        ConfigKey(ConfigSetting::Experimental::DnsTunnelingIpAddress, std::move(parseDnsTunnelingIp)),\n        ConfigKey(ConfigSetting::Experimental::InitialAutoProxyTimeout, InitialAutoProxyTimeout),\n        ConfigKey(ConfigSetting::Experimental::IgnoredPorts, std::move(parseIgnoredPorts)),\n        ConfigKey(ConfigSetting::Experimental::HostAddressLoopback, EnableHostAddressLoopback),\n        ConfigKey(ConfigSetting::Experimental::SetVersionDebug, SetVersionDebug)};\n\n    wil::unique_file ConfigFile;\n    if (ConfigFilePath != nullptr)\n    {\n        ConfigFile.reset(_wfopen(ConfigFilePath, L\"rt,ccs=UTF-8\"));\n        if (!ConfigFile)\n        {\n            const auto error = _doserrno;\n            LOG_WIN32_MSG(error, \"opening config file failed\");\n            if (error != ERROR_FILE_NOT_FOUND)\n            {\n                EMIT_USER_WARNING(wsl::shared::Localization::MessageFailedToOpenConfigFile(\n                    ConfigFilePath, wsl::windows::common::wslutil::GetErrorString(HRESULT_FROM_WIN32(error))));\n            }\n        }\n    }\n\n    // Parse the configuration keys.\n    WI_VERIFY(::ParseConfigFile(keys, ConfigFile.get(), (CFG_SKIP_INVALID_LINES | CFG_SKIP_UNKNOWN_VALUES), ConfigFilePath) == 0);\n\n    // Hyper-V firewall must always be configured for Mirrored Mode.\n    // For NAT mode, we use the experimental config to determine if Hyper-V firewall should be enabled\n    if ((NetworkingMode::Mirrored == NetworkingMode) || enableFirewall)\n    {\n        FirewallConfig.Enable();\n    }\n\n    if (EnableDnsTunneling && !DnsTunnelingIpAddress.has_value())\n    {\n        in_addr address{};\n        WI_VERIFY(inet_pton(AF_INET, LX_INIT_DNS_TUNNELING_IP_ADDRESS, &address) == 1);\n\n        DnsTunnelingIpAddress = address.S_un.S_addr;\n    }\n\n    if (macAddressPresent == ConfigKeyPresence::Absent && NetworkingMode == NetworkingMode::Bridged)\n    {\n        // Generate a random mac address if unspecified, so that the VM retains the same if restarted\n        const std::independent_bits_engine<std::default_random_engine, 16, unsigned short> random;\n\n        // independent_bits_engine doesn't support unsigned char on MSVC\n        auto* macAddressShort = reinterpret_cast<unsigned short*>(MacAddress.data());\n        std::generate(macAddressShort, macAddressShort + 3, random);\n\n        // Clear the multicast bit\n        MacAddress[0] &= ~1;\n        // Set the locally generated bit.\n        MacAddress[0] |= 2;\n    }\n\n    // Enable early boot logging if the debug console is enabled, unless explicitly disabled\n    if (EnableDebugConsole || !DebugConsoleLogFile.empty())\n    {\n        if (earlyBootLoggingPresent == ConfigKeyPresence::Absent)\n        {\n            EnableEarlyBootLogging = true;\n        }\n    }\n\n    if (CrashDumpFolder.empty() && MaxCrashDumpCount >= 0)\n    {\n        CrashDumpFolder = wsl::windows::common::filesystem::GetTempFolderPath(UserToken) / \"wsl-crashes\";\n    }\n\n    if (DefaultDistributionLocation.empty())\n    {\n        DefaultDistributionLocation = wsl::windows::common::filesystem::GetLocalAppDataPath(UserToken) / \"wsl\";\n    }\n\n    auto kernelModules =\n        LoadDefaultKernelModules ? std::vector<std::wstring>{L\"tun\", L\"ip_tables\", L\"br_netfilter\"} : std::vector<std::wstring>{};\n\n    if (!userKernelModules.empty())\n    {\n        for (const auto& e : wsl::shared::string::Split(userKernelModules, L','))\n        {\n            kernelModules.emplace_back(std::move(e));\n        }\n    }\n\n    KernelModulesList = wsl::shared::string::Join(kernelModules, L',');\n}\n\nvoid wsl::core::Config::SaveNetworkingSettings(_In_opt_ HANDLE UserToken) const\ntry\n{\n    if (NetworkingMode != NetworkingMode::Nat)\n    {\n        return;\n    }\n\n    const auto machineKey = wsl::windows::common::registry::OpenLxssMachineKey(KEY_SET_VALUE);\n    wsl::windows::common::registry::WriteString(machineKey.get(), nullptr, c_natGatewayAddress, NatGateway.c_str());\n    wsl::windows::common::registry::WriteString(machineKey.get(), nullptr, c_natNetwork, NatNetwork.c_str());\n\n    auto runAsUser = wil::impersonate_token(UserToken);\n    const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();\n    wsl::windows::common::registry::WriteString(userKey.get(), nullptr, c_natIpAddress, NatIpAddress.c_str());\n}\nCATCH_LOG();\n\nunsigned long wsl::core::Config::WriteConfigFile(_In_ LPCWSTR ConfigFilePath, _In_ ConfigKey KeyToWrite, _In_ bool RemoveKey)\n{\n    windows::common::ExecutionContext context(windows::common::ParseConfig);\n\n    if (!ConfigFilePath)\n    {\n        return ERROR_INVALID_PARAMETER;\n    }\n\n    // Open file for reading & writing. This assumes the file exists.\n    wil::unique_file ConfigFile(_wfopen(ConfigFilePath, L\"r+t,ccs=UTF-8\"));\n    const auto win32Error = _doserrno;\n    if (!ConfigFile && win32Error != ERROR_FILE_NOT_FOUND)\n    {\n        return win32Error;\n    }\n\n    // Since we aren't parsing in the config file, we don't need to pass in the known keys.\n    std::vector<ConfigKey> keys{};\n    std::wstring configFileOutput{};\n    auto result = ::ParseConfigFile(\n        keys, ConfigFile.get(), (CFG_SKIP_INVALID_LINES | CFG_SKIP_UNKNOWN_VALUES), ConfigFilePath, configFileOutput, KeyToWrite, RemoveKey);\n    if (result != 0)\n    {\n        return ERROR_READ_FAULT;\n    }\n\n    // If the config file didn't exist/wasn't opened, open it for writing.\n    if (!ConfigFile)\n    {\n        ConfigFile.reset(_wfopen(ConfigFilePath, L\"wt,ccs=UTF-8\"));\n        if (!ConfigFile)\n        {\n            return _doserrno;\n        }\n    }\n\n    // Move file pointer to beginning of file, write out the new config file, and truncate the file.\n    rewind(ConfigFile.get());\n    result = fputws(configFileOutput.c_str(), ConfigFile.get());\n    if (result == WEOF)\n    {\n        return ERROR_WRITE_FAULT;\n    }\n\n    const auto fileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(ConfigFile.get())));\n    if (SetEndOfFile(fileHandle) != TRUE)\n    {\n        return GetLastError();\n    }\n\n    return ERROR_SUCCESS;\n}\n\n#define VALIDATE_CONFIG_OPTION(_dependency, _setting, _value) \\\n    { \\\n        LOG_HR_IF(E_INVALIDARG, _dependency && (_setting != _value)); \\\n        _setting = _value; \\\n    }\n\nvoid wsl::core::Config::Initialize(_In_opt_ HANDLE UserToken)\n{\n    // Determine the maximum number of processors that can be added to the VM.\n    // If the user did not supply a processor count, use the maximum.\n    MaximumProcessorCount = wsl::windows::common::wslutil::GetLogicalProcessorCount();\n    if (ProcessorCount <= 0)\n    {\n        ProcessorCount = MaximumProcessorCount;\n    }\n    else if (ProcessorCount > MaximumProcessorCount)\n    {\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageTooManyProcessors(ProcessorCount, MaximumProcessorCount));\n        ProcessorCount = MaximumProcessorCount;\n    }\n\n    // Determine how much memory to add to the VM. If the user did not specify a value,\n    // use 50% of host memory. Otherwise, ensure the value falls within 256MB and the total system memory.\n    MEMORYSTATUSEX memInfo{sizeof(MEMORYSTATUSEX)};\n    THROW_IF_WIN32_BOOL_FALSE(GlobalMemoryStatusEx(&memInfo));\n\n    MaximumMemorySizeBytes = memInfo.ullTotalPhys;\n    if (MemorySizeBytes == 0)\n    {\n        MemorySizeBytes = (MaximumMemorySizeBytes / 2);\n    }\n    else\n    {\n        MemorySizeBytes = std::max<UINT64>(MemorySizeBytes, (256 * _1MB));\n        MemorySizeBytes = std::min<UINT64>(MemorySizeBytes, MaximumMemorySizeBytes);\n    }\n\n    // Use the user-defined swap size if one was specified; otherwise, set to 25%\n    // the memory size rounded up to the nearest GB.\n    //\n    // N.B. This heuristic is modeled after Red Hat and Ubuntu's recommended swap size.\n    if (SwapSizeBytes == UINT64_MAX)\n    {\n        SwapSizeBytes = ((MemorySizeBytes / 4 + _1GB - 1) & ~(_1GB - 1));\n    }\n\n    // Apply machine-wide policies to the configuration.\n    auto key = wsl::windows::policies::OpenPoliciesKey();\n    auto applyOverride = [&key](LPCWSTR ValueName, LPCWSTR SettingName, auto& value) {\n        if (value != std::remove_reference_t<decltype(value)>{} && !wsl::windows::policies::IsFeatureAllowed(key.get(), ValueName))\n        {\n            value = std::remove_reference_t<decltype(value)>{};\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageSettingOverriddenByPolicy(SettingName));\n        }\n    };\n\n    applyOverride(wsl::windows::policies::c_allowCustomKernelUserSetting, L\"wsl2.kernel\", KernelPath);\n    applyOverride(wsl::windows::policies::c_allowCustomKernelUserSetting, L\"wsl2.kernelModules\", KernelModulesPath);\n    applyOverride(wsl::windows::policies::c_allowCustomSystemDistroUserSetting, L\"wsl2.systemDistro\", SystemDistroPath);\n    applyOverride(wsl::windows::policies::c_allowCustomKernelCommandLineUserSetting, L\"wsl2.kernelCommandLine\", KernelCommandLine);\n    applyOverride(wsl::windows::policies::c_allowKernelDebuggingUserSetting, L\"wsl2.kernelDebugPort\", KernelDebugPort);\n    applyOverride(wsl::windows::policies::c_allowNestedVirtualizationUserSetting, L\"wsl2.nestedVirtualization\", EnableNestedVirtualization);\n\n    if (!wsl::windows::policies::IsFeatureAllowed(key.get(), wsl::windows::policies::c_allowDebugShellUserSetting))\n    {\n        // N.B. The warning for debug shell is handled in wsl.exe.\n        EnableDebugShell = false;\n    }\n\n    // Read the policy key for default networking mode.\n    auto defaultNetworkingMode = wsl::core::NetworkingMode::Nat;\n    const auto setting = wsl::windows::policies::GetPolicyValue(key.get(), wsl::windows::policies::c_defaultNetworkingMode);\n    if (setting.has_value())\n    {\n        switch (setting.value())\n        {\n        case wsl::core::NetworkingMode::None:\n        case wsl::core::NetworkingMode::Nat:\n        case wsl::core::NetworkingMode::Mirrored:\n        case wsl::core::NetworkingMode::VirtioProxy:\n            defaultNetworkingMode = static_cast<wsl::core::NetworkingMode>(setting.value());\n            break;\n\n        case wsl::core::NetworkingMode::Bridged: // Bridged requires additional configuration.\n        default:\n            LOG_HR_MSG(E_UNEXPECTED, \"Invalid default networking mode: %d\", setting.value());\n            break;\n        }\n    }\n\n    // Determine if the user is allowed to override the networking mode.\n    //\n    // N.B. User can always disable networking entirely.\n    if (NetworkingModePresence == ConfigKeyPresence::Present)\n    {\n        if ((!wsl::windows::policies::IsFeatureAllowed(key.get(), wsl::windows::policies::c_allowCustomNetworkingModeUserSetting)) &&\n            (NetworkingMode != wsl::core::NetworkingMode::None) && (NetworkingMode != defaultNetworkingMode))\n        {\n            NetworkingMode = defaultNetworkingMode;\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageSettingOverriddenByPolicy(L\"wsl2.networkingMode\"));\n        }\n    }\n    else\n    {\n        NetworkingMode = defaultNetworkingMode;\n    }\n\n    // Mirrored mode has Hyper-V Firewall always on - we ignore the local setting regardless in this case.\n    if (NetworkingMode != wsl::core::NetworkingMode::Mirrored)\n    {\n        if (!FirewallConfig.Enabled() &&\n            !wsl::windows::policies::IsFeatureAllowed(key.get(), wsl::windows::policies::c_allowCustomFirewallUserSetting))\n        {\n            FirewallConfig.Enable();\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageSettingOverriddenByPolicy(L\"wsl2.firewall\"));\n        }\n    }\n\n    // Load NAT configuration from the registry.\n    if (NetworkingMode == wsl::core::NetworkingMode::Nat)\n    {\n        try\n        {\n            const auto machineKey = wsl::windows::common::registry::OpenLxssMachineKey();\n            NatGateway = wsl::windows::common::registry::ReadString(machineKey.get(), nullptr, c_natGatewayAddress, L\"\");\n            NatNetwork = wsl::windows::common::registry::ReadString(machineKey.get(), nullptr, c_natNetwork, L\"\");\n\n            auto runAsUser = wil::impersonate_token(UserToken);\n            const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();\n            NatIpAddress = wsl::windows::common::registry::ReadString(userKey.get(), nullptr, c_natIpAddress, L\"\");\n        }\n        CATCH_LOG()\n    }\n\n    // Due to an issue with Global Secure Access Client, do not use DNS tunneling if the service is present.\n    if (EnableDnsTunneling)\n    {\n        try\n        {\n            // Open a handle to the service control manager and check if the inbox service is registered.\n            const wil::unique_schandle manager{OpenSCManager(nullptr, nullptr, SC_MANAGER_ENUMERATE_SERVICE)};\n            THROW_LAST_ERROR_IF(!manager);\n\n            // Check if the service is running.\n            const wil::unique_schandle service{OpenServiceW(manager.get(), L\"GlobalSecureAccessTunnelingService\", SERVICE_QUERY_STATUS)};\n            if (service)\n            {\n                SERVICE_STATUS status;\n                THROW_IF_WIN32_BOOL_FALSE(QueryServiceStatus(service.get(), &status));\n\n                if (status.dwCurrentState != SERVICE_STOPPED)\n                {\n                    if (DnsTunnelingConfigPresence == ConfigKeyPresence::Present)\n                    {\n                        EMIT_USER_WARNING(wsl::shared::Localization::MessageDnsTunnelingDisabled());\n                    }\n\n                    EnableDnsTunneling = false;\n                }\n            }\n        }\n        CATCH_LOG()\n    }\n\n    // Ensure that settings are consistent (disable features that require other features that are not present).\n    if (EnableSafeMode)\n    {\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageSafeModeEnabled());\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, EnableHostFileSystemAccess, false);\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, EnableNestedVirtualization, false);\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, EnableHardwarePerformanceCounters, false);\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, EnableGpuSupport, false);\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, EnableVirtio, false);\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, EnableGuiApps, false);\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, SwapSizeBytes, 0);\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, KernelPath, std::filesystem::path{});\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, KernelModulesPath, std::filesystem::path{});\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, NetworkingMode, NetworkingMode::None);\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, EnableDnsTunneling, false);\n        VALIDATE_CONFIG_OPTION(EnableSafeMode, EnableAutoProxy, false);\n    }\n\n    if (!EnableVirtio)\n    {\n        VALIDATE_CONFIG_OPTION(!EnableVirtio, EnableVirtio9p, false);\n        VALIDATE_CONFIG_OPTION(!EnableVirtio, EnableVirtioFs, false);\n    }\n\n    if (EnableVirtio9p)\n    {\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageConfigVirtio9pDisabled());\n        EnableVirtio9p = false;\n    }\n\n    if (NetworkingMode != NetworkingMode::Nat && NetworkingMode != NetworkingMode::Mirrored && NetworkingMode != NetworkingMode::VirtioProxy)\n    {\n        VALIDATE_CONFIG_OPTION(\n            (NetworkingMode != NetworkingMode::Nat && NetworkingMode != NetworkingMode::Mirrored && NetworkingMode != NetworkingMode::VirtioProxy),\n            EnableDnsTunneling,\n            false);\n    }\n\n    if (!EnableDnsTunneling || NetworkingMode == NetworkingMode::VirtioProxy)\n    {\n        VALIDATE_CONFIG_OPTION(!EnableDnsTunneling || NetworkingMode == NetworkingMode::VirtioProxy, BestEffortDnsParsing, false);\n        VALIDATE_CONFIG_OPTION(\n            !EnableDnsTunneling || NetworkingMode == NetworkingMode::VirtioProxy, DnsTunnelingIpAddress, std::optional<uint32_t>{});\n    }\n\n    if (NetworkingMode != NetworkingMode::Mirrored)\n    {\n        VALIDATE_CONFIG_OPTION((NetworkingMode != NetworkingMode::Mirrored), IgnoredPorts, std::set<uint16_t>{});\n        VALIDATE_CONFIG_OPTION((NetworkingMode != NetworkingMode::Mirrored), EnableHostAddressLoopback, false);\n    }\n}\n\nGUID wsl::core::Config::NatNetworkId() const noexcept\n{\n    // Identifier for the WSL virtual network: {b95d0c5e-57d4-412b-b571-18a81a16e005}\n    static constexpr GUID c_networkId = {0xb95d0c5e, 0x57d4, 0x412b, {0xb5, 0x71, 0x18, 0xa8, 0x1a, 0x16, 0xe0, 0x05}};\n\n    // Identifier for the WSL virtual network with Hyper-v firewall enabled: {790e58b4-7939-4434-9358-89ae7ddbe87e}\n    static constexpr GUID c_networkWithFirewallId = {0x790e58b4, 0x7939, 0x4434, {0x93, 0x58, 0x89, 0xae, 0x7d, 0xdb, 0xe8, 0x7e}};\n\n    return FirewallConfig.Enabled() ? c_networkWithFirewallId : c_networkId;\n}\n\nLPCWSTR wsl::core::Config::NatNetworkName() const noexcept\n{\n    static constexpr auto c_networkName = L\"WSL\";\n    static constexpr auto c_networkWithFirewallName = L\"WSL (Hyper-V firewall)\";\n    return FirewallConfig.Enabled() ? c_networkWithFirewallName : c_networkName;\n}\n\nvoid wsl::core::FirewallConfiguration::Enable() noexcept\n{\n    VmCreatorId = wsl::core::networking::c_wslFirewallVmCreatorId;\n    DefaultLoopbackPolicy = FirewallAction::Allow;\n    Rules = wsl::core::networking::MakeDefaultFirewallRuleConfiguration(networking::c_wslFirewallVmCreatorId);\n}\n\nvoid wsl::core::FirewallConfiguration::reset() noexcept\n{\n    VmCreatorId.reset();\n    Rules.clear();\n    DefaultLoopbackPolicy = FirewallAction::Invalid;\n}\n\nbool wsl::core::FirewallConfiguration::Enabled() const noexcept\n{\n    return VmCreatorId.has_value();\n}\n"
  },
  {
    "path": "src/windows/common/WslCoreConfig.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreConfig.h\n\nAbstract:\n\n    This file contains the WSL Core VM configuration helper class declaration.\n\n--*/\n\n#pragma once\n\n#define T_ENUM(c, n) TraceLoggingValue(wsl::core::ToString((c).n), #n)\n#define T_PRESENT(c, n) TraceLoggingValue((c).n == ConfigKeyPresence::Present, #n)\n#define T_SET(c, n) TraceLoggingValue(!(c).n.empty(), #n \"Set\")\n#define T_STRING(c, n) TraceLoggingValue((c).n.c_str(), #n)\n#define T_VALUE(c, n) TraceLoggingValue((c).n, #n)\n\n#define CONFIG_TELEMETRY(c) \\\n    T_VALUE(c, BestEffortDnsParsing), T_VALUE(c, DhcpTimeout), T_VALUE(c, EnableAutoProxy), T_VALUE(c, EnableDebugConsole), \\\n        T_VALUE(c, EnableDebugShell), T_VALUE(c, EnableDhcp), T_VALUE(c, EnableDnsProxy), T_VALUE(c, EnableDnsTunneling), \\\n        T_VALUE(c, EnableGpuSupport), T_VALUE(c, EnableGuiApps), T_VALUE(c, EnableHardwarePerformanceCounters), \\\n        T_VALUE(c, EnableHostAddressLoopback), T_VALUE(c, EnableHostFileSystemAccess), T_VALUE(c, EnableIpv6), \\\n        T_VALUE(c, EnableLocalhostRelay), T_VALUE(c, EnableNestedVirtualization), T_VALUE(c, EnableSafeMode), \\\n        T_VALUE(c, EnableSparseVhd), T_VALUE(c, EnableVirtio), T_VALUE(c, EnableVirtio9p), T_VALUE(c, EnableVirtioFs), \\\n        T_ENUM(c, FirewallConfigPresence), T_VALUE(c, KernelBootTimeout), T_SET(c, KernelCommandLine), \\\n        T_VALUE(c, KernelDebugPort), T_SET(c, KernelModulesPath), T_STRING(c, KernelModulesList), T_SET(c, KernelPath), \\\n        T_VALUE(c, LoadDefaultKernelModules), T_PRESENT(c, LoadKernelModulesPresence), T_VALUE(c, MaximumMemorySizeBytes), \\\n        T_VALUE(c, MaximumProcessorCount), T_ENUM(c, MemoryReclaim), T_VALUE(c, MemorySizeBytes), T_VALUE(c, MountDeviceTimeout), \\\n        T_ENUM(c, NetworkingMode), T_VALUE(c, ProcessorCount), T_SET(c, SwapFilePath), T_VALUE(c, SwapSizeBytes), \\\n        T_SET(c, SystemDistroPath), T_VALUE(c, VhdSizeBytes), T_VALUE(c, VmIdleTimeout), T_SET(c, VmSwitch)\n\nnamespace wsl::core {\nconstexpr auto ToString(ConfigKeyPresence key)\n{\n    switch (key)\n    {\n    case ConfigKeyPresence::Absent:\n        return \"Absent\";\n    case ConfigKeyPresence::Present:\n        return \"Present\";\n    default:\n        return \"Invalid\";\n    }\n}\n\nenum class MemoryReclaimMode\n{\n    Disabled,\n    Gradual,\n    DropCache\n};\n\n// Ensure the WslCoreConfig versions of the enum match the version that's used in mini init.\nstatic_assert(static_cast<ULONG>(MemoryReclaimMode::Disabled) == LxMiniInitMemoryReclaimModeDisabled);\nstatic_assert(static_cast<ULONG>(MemoryReclaimMode::Gradual) == LxMiniInitMemoryReclaimModeGradual);\nstatic_assert(static_cast<ULONG>(MemoryReclaimMode::DropCache) == LxMiniInitMemoryReclaimModeDropCache);\n\nconstexpr auto ToString(MemoryReclaimMode mode)\n{\n    switch (mode)\n    {\n    case MemoryReclaimMode::Disabled:\n        return \"Disabled\";\n    case MemoryReclaimMode::Gradual:\n        return \"Gradual\";\n    case MemoryReclaimMode::DropCache:\n        return \"DropCache\";\n    default:\n        return \"Invalid\";\n    }\n}\n\nconst std::map<std::string, MemoryReclaimMode, shared::string::CaseInsensitiveCompare> MemoryReclaimModes = {\n    {ToString(MemoryReclaimMode::Gradual), MemoryReclaimMode::Gradual},\n    {ToString(MemoryReclaimMode::DropCache), MemoryReclaimMode::DropCache},\n    {ToString(MemoryReclaimMode::Disabled), MemoryReclaimMode::Disabled}};\n\n// N.B. These enum values are also used in InTune ADMX templates, if entries are added or removed ensure that existing\n//      values are not changed.\nenum NetworkingMode\n{\n    None = 0,\n    Nat = 1,\n    Bridged = 2,\n    Mirrored = 3,\n    VirtioProxy = 4\n};\n\n// Ensure the WslCoreConfig versions of the enum match the version that's used in mini init.\nstatic_assert(static_cast<ULONG>(NetworkingMode::None) == LxMiniInitNetworkingModeNone);\nstatic_assert(static_cast<ULONG>(NetworkingMode::Nat) == LxMiniInitNetworkingModeNat);\nstatic_assert(static_cast<ULONG>(NetworkingMode::Bridged) == LxMiniInitNetworkingModeBridged);\nstatic_assert(static_cast<ULONG>(NetworkingMode::Mirrored) == LxMiniInitNetworkingModeMirrored);\nstatic_assert(static_cast<ULONG>(NetworkingMode::VirtioProxy) == LxMiniInitNetworkingModeVirtioProxy);\n\nconstexpr auto ToString(NetworkingMode config) noexcept\n{\n    switch (config)\n    {\n    case NetworkingMode::None:\n        return \"None\";\n    case NetworkingMode::Nat:\n        return \"Nat\";\n    case NetworkingMode::Bridged:\n        return \"Bridged\";\n    case NetworkingMode::Mirrored:\n        return \"Mirrored\";\n    case NetworkingMode::VirtioProxy:\n        return \"VirtioProxy\";\n    default:\n        return \"Invalid\";\n    }\n}\n\nconst std::map<std::string, wsl::core::NetworkingMode, shared::string::CaseInsensitiveCompare> NetworkingModes{\n    {ToString(NetworkingMode::None), NetworkingMode::None},\n    {ToString(NetworkingMode::Nat), NetworkingMode::Nat},\n    {ToString(NetworkingMode::Bridged), NetworkingMode::Bridged},\n    {ToString(NetworkingMode::Mirrored), NetworkingMode::Mirrored},\n    {ToString(NetworkingMode::VirtioProxy), NetworkingMode::VirtioProxy}};\n\nenum class FirewallAction\n{\n    Invalid,\n    Allow,\n    Block\n};\n\nconstexpr auto ToString(const FirewallAction Action) noexcept\n{\n    switch (Action)\n    {\n    case FirewallAction::Allow:\n        return \"Allow\";\n    case FirewallAction::Block:\n        return \"Block\";\n    default:\n        return \"Invalid\";\n    }\n}\n\nenum class FirewallRuleOperation\n{\n    Invalid,\n    Add,\n    Delete\n};\n\nstruct FirewallRuleConfiguration\n{\n    // These values are shared_bstr because we make temporary copies (for example in ConfigureHyperVFirewall)\n    wil::shared_bstr RuleId;\n    wil::shared_bstr RuleName;\n    wil::shared_bstr Protocol;\n    std::vector<wil::shared_bstr> LocalPorts;\n    std::vector<wil::shared_bstr> LocalAddresses;\n    std::vector<wil::shared_bstr> RemoteAddresses;\n    FirewallRuleOperation RuleOperation;\n    // NOTE these are only applicable for HOST firewall rules\n    wil::shared_bstr LocalService;\n    wil::shared_bstr LocalApplication;\n\n    FirewallRuleConfiguration& operator=(const FirewallRuleConfiguration&) = default;\n    FirewallRuleConfiguration& operator=(FirewallRuleConfiguration&&) = default;\n    FirewallRuleConfiguration(FirewallRuleConfiguration&&) = default;\n    FirewallRuleConfiguration(const FirewallRuleConfiguration&) = default;\n\n    FirewallRuleConfiguration(\n        _In_ LPCWSTR RuleIdParam,\n        _In_opt_ LPCWSTR RuleNameParam = nullptr,\n        _In_opt_ LPCWSTR ProtocolParam = nullptr,\n        _In_ DWORD LocalPortsCountParam = 0,\n        _In_reads_opt_(LocalPortsCountParam) LPCWSTR* LocalPortsParam = nullptr,\n        _In_ DWORD LocalAddressesCountParam = 0,\n        _In_reads_opt_(LocalAddressesCountParam) LPCWSTR* LocalAddressesParam = nullptr,\n        _In_ DWORD RemoteAddressesCountParam = 0,\n        _In_reads_opt_(RemoteAddressesCountParam) LPCWSTR* RemoteAddressesParam = nullptr,\n        _In_opt_ LPCWSTR LocalServiceParam = nullptr,\n        _In_opt_ LPCWSTR LocalApplicationParam = nullptr,\n        _In_ FirewallRuleOperation RuleOperationParam = FirewallRuleOperation::Add)\n    {\n        RuleId = wil::make_bstr(RuleIdParam);\n        if (RuleNameParam)\n        {\n            RuleName = wil::make_bstr(RuleNameParam);\n        }\n        if (ProtocolParam)\n        {\n            Protocol = wil::make_bstr(ProtocolParam);\n        }\n        for (ULONG i = 0; i < LocalPortsCountParam; ++i)\n        {\n            LocalPorts.emplace_back(wil::make_bstr(LocalPortsParam[i]));\n        }\n        for (ULONG i = 0; i < LocalAddressesCountParam; ++i)\n        {\n            LocalAddresses.emplace_back(wil::make_bstr(LocalAddressesParam[i]));\n        }\n        for (ULONG i = 0; i < RemoteAddressesCountParam; ++i)\n        {\n            RemoteAddresses.emplace_back(wil::make_bstr(RemoteAddressesParam[i]));\n        }\n        if (LocalServiceParam)\n        {\n            LocalService = wil::make_bstr(LocalServiceParam);\n        }\n        if (LocalApplicationParam)\n        {\n            LocalApplication = wil::make_bstr(LocalApplicationParam);\n        }\n        RuleOperation = RuleOperationParam;\n    }\n};\n\nstruct FirewallConfiguration\n{\n    std::optional<GUID> VmCreatorId{};\n    std::vector<FirewallRuleConfiguration> Rules{};\n    FirewallAction DefaultLoopbackPolicy{FirewallAction::Invalid};\n\n    bool Enabled() const noexcept;\n\n    void reset() noexcept;\n\n    void Enable() noexcept;\n};\n\nnamespace ConfigSetting {\n    static constexpr auto Kernel = \"wsl2.kernel\";\n    static constexpr auto KernelCommandLine = \"wsl2.kernelCommandLine\";\n    static constexpr auto KernelModules = \"wsl2.kernelModules\";\n    static constexpr auto Memory = \"wsl2.memory\";\n    static constexpr auto Processors = \"wsl2.processors\";\n    static constexpr auto DebugConsole = \"wsl2.debugConsole\";\n    static constexpr auto EarlyBootLogging = \"wsl2.earlyBootLogging\";\n    static constexpr auto Swap = \"wsl2.swap\";\n    static constexpr auto SwapFile = \"wsl2.swapFile\";\n    static constexpr auto LocalhostForwarding = \"wsl2.localhostForwarding\";\n    static constexpr auto NestedVirtualization = \"wsl2.nestedVirtualization\";\n    static constexpr auto Virtio9p = \"wsl2.virtio9p\";\n    static constexpr auto Virtiofs = \"wsl2.virtiofs\";\n    static constexpr auto KernelDebugPort = \"wsl2.kernelDebugPort\";\n    static constexpr auto GpuSupport = \"wsl2.gpuSupport\";\n    static constexpr auto GuiApplications = \"wsl2.guiApplications\";\n    static constexpr auto SystemDistro = \"wsl2.systemDistro\";\n    static constexpr auto Telemetry = \"wsl2.telemetry\";\n    static constexpr auto VmIdleTimeout = \"wsl2.vmIdleTimeout\";\n    static constexpr auto DebugConsoleLogFile = \"wsl2.debugConsoleLogFile\";\n    static constexpr auto KernelBootTimeout = \"wsl2.kernelBootTimeout\";\n    static constexpr auto DistributionStartTimeout = \"wsl2.distributionStartTimeout\";\n    static constexpr auto Virtio = \"wsl2.virtio\";\n    static constexpr auto HostFileSystemAccess = \"wsl2.hostFileSystemAccess\";\n    static constexpr auto MountDeviceTimeout = \"wsl2.mountDeviceTimeout\";\n    static constexpr auto HardwarePerformanceCounters = \"wsl2.hardwarePerformanceCounters\";\n    static constexpr auto NetworkingMode = \"wsl2.networkingMode\";\n    static constexpr auto VmSwitch = \"wsl2.vmSwitch\";\n    static constexpr auto MacAddress = \"wsl2.macAddress\";\n    static constexpr auto Dhcp = \"wsl2.dhcp\";\n    static constexpr auto DhcpTimeout = \"wsl2.dhcpTimeout\";\n    static constexpr auto Ipv6 = \"wsl2.ipv6\";\n    static constexpr auto DnsProxy = \"wsl2.dnsProxy\";\n    static constexpr auto SafeMode = \"wsl2.safeMode\";\n    static constexpr auto DefaultVhdSize = \"wsl2.defaultVhdSize\";\n    static constexpr auto CrashDumpFolder = \"wsl2.crashDumpFolder\";\n    static constexpr auto MaxCrashDumpCount = \"wsl2.maxCrashDumpCount\";\n    static constexpr auto DistributionInstallPath = \"general.distributionInstallPath\";\n    static constexpr auto InstanceIdleTimeout = \"general.instanceIdleTimeout\";\n    static constexpr auto DnsTunneling = \"wsl2.dnsTunneling\";\n    static constexpr auto Firewall = \"wsl2.firewall\";\n    static constexpr auto AutoProxy = \"wsl2.autoProxy\";\n    static constexpr auto LoadKernelModules = \"wsl2.loadKernelModules\";\n    static constexpr auto LoadDefaultKernelModules = \"wsl2.loadDefaultKernelModules\";\n\n    namespace Experimental {\n        static constexpr auto NetworkingMode = \"experimental.networkingMode\";\n        static constexpr auto AutoMemoryReclaim = \"experimental.autoMemoryReclaim\";\n        static constexpr auto SparseVhd = \"experimental.sparseVhd\";\n        static constexpr auto DnsTunneling = \"experimental.dnsTunneling\";\n        static constexpr auto BestEffortDnsParsing = \"experimental.bestEffortDnsParsing\";\n        static constexpr auto DnsTunnelingIpAddress = \"experimental.dnsTunnelingIpAddress\";\n        static constexpr auto Firewall = \"experimental.firewall\";\n        static constexpr auto AutoProxy = \"experimental.autoProxy\";\n        static constexpr auto InitialAutoProxyTimeout = \"experimental.initialAutoProxyTimeout\";\n        static constexpr auto IgnoredPorts = \"experimental.ignoredPorts\";\n        static constexpr auto HostAddressLoopback = \"experimental.hostAddressLoopback\";\n        static constexpr auto SetVersionDebug = \"experimental.setVersionDebug\";\n\n    } // namespace Experimental\n} // namespace ConfigSetting\n\nstruct Config\n{\n    Config() = delete;\n    Config(_In_opt_ LPCWSTR Path = nullptr, _In_opt_ HANDLE UserToken = nullptr);\n    ~Config() = default;\n\n    void Initialize(_In_opt_ HANDLE UserToken = nullptr);\n    void ParseConfigFile(_In_opt_ LPCWSTR ConfigFilePath, _In_opt_ HANDLE UserToken);\n    void SaveNetworkingSettings(_In_opt_ HANDLE UserToken) const;\n    static unsigned long WriteConfigFile(_In_ LPCWSTR ConfigFilePath, _In_ ConfigKey KeyToWrite, _In_ bool RemoveKey = false);\n\n    std::filesystem::path KernelPath;\n    std::wstring KernelCommandLine;\n    std::wstring KernelModulesList;\n    std::filesystem::path KernelModulesPath;\n    UINT64 MemorySizeBytes = 0;\n    UINT64 MaximumMemorySizeBytes = 0;\n    int ProcessorCount = 0;\n    int MaximumProcessorCount = 0;\n    bool EnableDebugConsole = false;\n    bool EnableEarlyBootLogging = false;\n    UINT64 SwapSizeBytes = UINT64_MAX;\n    std::filesystem::path SwapFilePath;\n    bool EnableLocalhostRelay = true;\n    ConfigKeyPresence LocalhostRelayConfigPresence = ConfigKeyPresence::Absent;\n    ConfigKeyPresence LoadKernelModulesPresence = ConfigKeyPresence::Absent;\n    bool LoadDefaultKernelModules = true;\n    bool EnableNestedVirtualization = !shared::Arm64 && windows::common::helpers::IsWindows11OrAbove();\n    bool EnableVirtio9p = false;\n    bool EnableVirtio = !shared::Arm64 || windows::common::helpers::IsWindows11OrAbove();\n    bool EnableVirtioFs = false;\n    int KernelDebugPort = 0;\n    bool EnableGpuSupport = true;\n    bool EnableGuiApps = true;\n    std::filesystem::path SystemDistroPath;\n    bool EnableTelemetry = shared::OfficialBuild;\n    int VmIdleTimeout = (60 * 1000);\n    int InstanceIdleTimeout = (15 * 1000);\n    std::filesystem::path DebugConsoleLogFile;\n    std::wstring VmSwitch;\n    int KernelBootTimeout = (30 * 1000);\n    int DistributionStartTimeout = (60 * 1000);\n    int MountDeviceTimeout = (5 * 1000);\n    bool EnableHostFileSystemAccess = true;\n    bool EnableDhcp = true;\n    bool EnableIpv6 = false;\n    int DhcpTimeout = (5 * 1000);\n    NetworkingMode NetworkingMode = NetworkingMode::Nat;\n    ConfigKeyPresence NetworkingModePresence = ConfigKeyPresence::Absent;\n    bool EnableDnsProxy = true;\n    bool EnableSafeMode = false;\n    bool EnableDnsTunneling = true;\n    std::filesystem::path DefaultDistributionLocation;\n    ConfigKeyPresence DnsTunnelingConfigPresence = ConfigKeyPresence::Absent;\n    // Only applicable when DNS tunneling is enabled\n    //\n    // In a DNS request from Linux there might be DNS records that Windows DNS client does not know how to parse.\n    // By default in this case Windows will fail the request. When the flag is enabled, Windows will extract the\n    // question from the DNS request and attempt to resolve it, ignoring the unknown records\n    bool BestEffortDnsParsing = false;\n    // Only applicable when DNS tunneling is enabled\n    // IP address that will be used by the DNS listener/proxy used for DNS tunneling. Some scenarios (such as native Docker)\n    // require Linux nameserver to be an IP that is not in the range 127.0.0.0/8. This config is intended for those scenarios.\n    std::optional<uint32_t> DnsTunnelingIpAddress;\n    bool EnableHardwarePerformanceCounters = !shared::Arm64;\n    bool EnableAutoProxy = true;\n    int InitialAutoProxyTimeout = 1000;\n    MemoryReclaimMode MemoryReclaim = MemoryReclaimMode::DropCache;\n    bool EnableSparseVhd = false;\n    UINT64 VhdSizeBytes = 0x10000000000; // 1TB\n\n    wsl::shared::string::MacAddress MacAddress;\n    std::wstring NatIpAddress;\n    std::wstring NatGateway;\n    std::wstring NatNetwork;\n    bool EnableDebugShell = true;\n    FirewallConfiguration FirewallConfig;\n    ConfigKeyPresence FirewallConfigPresence = ConfigKeyPresence::Absent;\n    std::set<uint16_t> IgnoredPorts;\n    bool EnableHostAddressLoopback = false;\n    std::filesystem::path CrashDumpFolder;\n    int MaxCrashDumpCount = 10;\n\n    // Temporary config value to help root cause the truncated archive errors in SetVersion()\n    bool SetVersionDebug = false;\n\n    GUID NatNetworkId() const noexcept;\n    LPCWSTR NatNetworkName() const noexcept;\n};\n} // namespace wsl::core\n"
  },
  {
    "path": "src/windows/common/WslCoreFilesystem.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreFilesystem.cpp\n\nAbstract:\n\n    This file contains filesystem helper function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"WslCoreFilesystem.h\"\n#include \"WslSecurity.h\"\n\nwil::unique_hfile wsl::core::filesystem::CreateFile(\n    _In_ LPCWSTR fileName, _In_ DWORD desiredAccess, _In_ DWORD shareMode, _In_ DWORD creationDisposition, _In_ DWORD flagsAndAttributes, _In_ PSID userSid)\n{\n    auto sd = windows::common::security::CreateSecurityDescriptor(userSid);\n    SECURITY_ATTRIBUTES sa{sizeof(SECURITY_ATTRIBUTES), &sd, false};\n    wil::unique_hfile file{CreateFileW(fileName, desiredAccess, shareMode, &sa, creationDisposition, flagsAndAttributes, nullptr)};\n    THROW_LAST_ERROR_IF(!file);\n\n    return file;\n}\n\nvoid wsl::core::filesystem::CreateVhd(_In_ LPCWSTR target, _In_ ULONGLONG maximumSize, _In_ PSID userSid, _In_ BOOL sparse, _In_ BOOL fixed)\n{\n    WI_ASSERT(wsl::windows::common::string::IsPathComponentEqual(\n        std::filesystem::path{target}.extension().native(), windows::common::wslutil::c_vhdxFileExtension));\n\n    // Disable creation of sparse VHDs while data corruption is being debugged.\n    if (sparse)\n    {\n        sparse = false;\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageSparseVhdDisabled());\n    }\n\n    VIRTUAL_STORAGE_TYPE storageType{};\n    storageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_VHDX;\n    storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT;\n\n    // Create a VHDX with the specified maximum size.\n    //\n    // N.B. The block size was chosen based on the best practices for Linux VHDs:\n    //      https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/best-practices-for-running-linux-on-hyper-v\n    CREATE_VIRTUAL_DISK_PARAMETERS createVhdParameters{};\n    createVhdParameters.Version = CREATE_VIRTUAL_DISK_VERSION_2;\n    createVhdParameters.Version2.BlockSizeInBytes = _1MB;\n    createVhdParameters.Version2.MaximumSize = maximumSize;\n\n    CREATE_VIRTUAL_DISK_FLAG flags = CREATE_VIRTUAL_DISK_FLAG_SUPPORT_COMPRESSED_VOLUMES;\n    WI_SetFlagIf(flags, CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION, fixed);\n    if (sparse)\n    {\n        WI_SetAllFlags(flags, CREATE_VIRTUAL_DISK_FLAG_SPARSE_FILE | CREATE_VIRTUAL_DISK_FLAG_SUPPORT_SPARSE_FILES_ANY_FS);\n    }\n\n    // Explicitly set the owner of the file so the default is not used.\n    //\n    // N.B. This ensures that HcsGrantVmAccess is able to add the required ACL\n    //      to the VHD because the operation is done while impersonating the user.\n    auto sd = windows::common::security::CreateSecurityDescriptor(userSid);\n    wil::unique_hfile vhd{};\n    THROW_IF_WIN32_ERROR(\n        ::CreateVirtualDisk(&storageType, target, VIRTUAL_DISK_ACCESS_NONE, &sd, flags, 0, &createVhdParameters, nullptr, &vhd));\n}\n\nwil::unique_handle wsl::core::filesystem::OpenVhd(_In_ LPCWSTR Path, _In_ VIRTUAL_DISK_ACCESS_MASK Mask)\n{\n    WI_ASSERT(wsl::windows::common::wslutil::IsVhdFile(std::filesystem::path{Path}));\n\n    // N.B. Specifying unknown for device and vendor means the system will determine the type of VHD.\n    VIRTUAL_STORAGE_TYPE storageType{};\n    storageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;\n    storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN;\n\n    wil::unique_handle disk;\n    THROW_IF_WIN32_ERROR(OpenVirtualDisk(&storageType, Path, Mask, OPEN_VIRTUAL_DISK_FLAG_NONE, nullptr, &disk));\n\n    return disk;\n}\n\nvoid wsl::core::filesystem::ResizeExistingVhd(_In_ HANDLE diskHandle, _In_ ULONGLONG maximumSize, _In_ RESIZE_VIRTUAL_DISK_FLAG resizeFlag)\n{\n    RESIZE_VIRTUAL_DISK_PARAMETERS resize{};\n    resize.Version1.NewSize = maximumSize;\n    resize.Version = RESIZE_VIRTUAL_DISK_VERSION_1;\n    THROW_IF_WIN32_ERROR(ResizeVirtualDisk(diskHandle, resizeFlag, &resize, nullptr));\n}\n\nULONGLONG wsl::core::filesystem::GetDiskSize(_In_ HANDLE diskHandle)\n{\n    GET_VIRTUAL_DISK_INFO virtualDiskInfo{};\n    virtualDiskInfo.Version = GET_VIRTUAL_DISK_INFO_SIZE;\n    ULONG size = sizeof(virtualDiskInfo);\n\n    THROW_IF_WIN32_ERROR(GetVirtualDiskInformation(diskHandle, &size, &virtualDiskInfo, nullptr));\n\n    return virtualDiskInfo.Size.VirtualSize;\n}\n"
  },
  {
    "path": "src/windows/common/WslCoreFilesystem.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreFilesystem.h\n\nAbstract:\n\n    This file contains WSL Core filesystem helper function declarations.\n\n--*/\n\n#pragma once\n#include \"precomp.h\"\n\n#define MAX_VHD_COUNT 254\n\n// Each virtiofs device uses the DAX cache, which is controlled by wslcore's caller, plus a couple\n// of extra pages for configuration. MMIO space needs to be large page aligned (2MB), so request an\n// additional 2MB to cover the couple of extra pages needed.\n#define EXTRA_MMIO_SIZE_PER_VIRTIOFS_DEVICE_IN_MB 2\n\nnamespace wsl::core::filesystem {\n\n/// <summary>\n/// Create a file owned by the specified user.\n/// </summary>\nwil::unique_hfile CreateFile(\n    _In_ LPCWSTR fileName, _In_ DWORD desiredAccess, _In_ DWORD shareMode, _In_ DWORD creationDisposition, _In_ DWORD flagsAndAttributes, _In_ PSID userSid);\n\n/// <summary>\n/// Create a VHD of the specified size.\n/// </summary>\nvoid CreateVhd(_In_ LPCWSTR target, _In_ ULONGLONG maximumSize, _In_ PSID userSid, _In_ BOOL sparse, _In_ BOOL fixed);\n\nwil::unique_handle OpenVhd(_In_ LPCWSTR Path, _In_ VIRTUAL_DISK_ACCESS_MASK Mask);\n\nvoid ResizeExistingVhd(_In_ HANDLE diskHandle, _In_ ULONGLONG maximumSize, _In_ RESIZE_VIRTUAL_DISK_FLAG resizeFlag);\n\nULONGLONG GetDiskSize(_In_ HANDLE diskHandle);\n\n} // namespace wsl::core::filesystem\n"
  },
  {
    "path": "src/windows/common/WslCoreFirewallSupport.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include \"WslCoreFirewallSupport.h\"\r\n\r\n#include <ComputeNetwork.h>\r\n#include <wil/com.h>\r\n\r\n#include \"string.hpp\"\r\n#include \"WslCoreNetworkingSupport.h\"\r\n\r\nstatic constexpr auto c_hyperVFirewallLoopbackRuleIdPrefix_Old = L\"WSA-IP-Loopback-Allow-Rule-1-\";\r\nstatic constexpr auto c_hyperVFirewallLoopbackRuleIdPrefix = L\"WslCore-IP-Loopback-Allow-Rule-1-\";\r\nstatic constexpr auto c_hyperVFirewallLoopbackRuleName = L\"WslCore Loopback Allow Rule\";\r\n\r\nstatic constexpr auto c_hyperVFirewallLocalSubnetRuleIdPrefix = L\"WslCore-LocalSubnet-Allow-Rule-1-\";\r\nstatic constexpr auto c_hyperVFirewallLocalSubnetRuleName = L\"WslCore LocalSubnet Allow Rule\";\r\n\r\nstatic constexpr auto c_hyperVFirewallIcmpV6RuleIdPrefix = L\"WslCore-Allow-Inbound-ICMPv6-1-\";\r\nstatic constexpr auto c_hyperVFirewallIcmpV6RuleName = L\"WslCore Inbound ICMPv6 Default Allow Rule\";\r\n\r\nstatic constexpr auto c_hyperVFirewallIcmpV4RuleIdPrefix = L\"WslCore-Allow-Inbound-ICMPv4-1-\";\r\nstatic constexpr auto c_hyperVFirewallIcmpV4RuleName = L\"WslCore Inbound ICMPv4 Default Allow Rule\";\r\n\r\n// Host Firewall rule to allow traffic to SharedAccess service.\r\nstatic constexpr auto c_sharedAccessRuleId = L\"WSLCore-SharedAccess-Allow-Rule\";\r\nstatic constexpr auto c_sharedAccessRuleName = L\"WSLCore SharedAccess Allow Rule\";\r\nstatic constexpr auto c_sharedAccessService = L\"SharedAccess\";\r\n\r\nstatic constexpr auto c_protocolUDP = L\"UDP\";\r\nstatic constexpr auto c_svchostApplication = L\"%SYSTEMROOT%\\\\System32\\\\svchost.exe\";\r\n\r\n// Regkey to control hyper-v firewall being disabled\r\nstatic constexpr auto c_mpssvcRegPath = L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\MpsSvc\\\\Parameters\";\r\nstatic constexpr auto c_mpssvcRegDisableKey = L\"HyperVFirewallDisable\";\r\n\r\n// Constants corresponding to firewall WMI values\r\nstatic constexpr auto c_directionInbound = 1;\r\nstatic constexpr auto c_actionAllow = 2;\r\nstatic constexpr auto c_ruleEnabled = 1;\r\nstatic constexpr auto c_ruleDisabled = 0;\r\nstatic constexpr auto c_true = 1;\r\n\r\n// ICMP \"port\" constants\r\nstatic constexpr auto c_icmpv6NeighborSolicitation = L\"135\";\r\nstatic constexpr auto c_icmpv6NeighborAdvertisement = L\"136\";\r\nstatic constexpr auto c_icmpv6PortDestinationUnreachable = L\"1\";\r\nstatic constexpr auto c_icmpv6PortTimeExceeded = L\"3\";\r\nstatic constexpr auto c_icmpv4PortDestinationUnreachable = L\"3\";\r\nstatic constexpr auto c_icmpv4PortTimeExceeded = L\"11\";\r\n\r\n// mDNS related constants\r\nstatic constexpr auto c_hyperVFirewallMdnsIpv4RuleIdPrefix = L\"WslCore-Allow-Inbound-mDNS-IPv4-1-\";\r\nstatic constexpr auto c_hyperVFirewallMdnsIpv4RuleName = L\"WslCore Inbound IPv4 mDNS Default Allow Rule\";\r\n\r\nstatic constexpr auto c_hyperVFirewallMdnsIpv6RuleIdPrefix = L\"WslCore-Allow-Inbound-mDNS-IPv6-1-\";\r\nstatic constexpr auto c_hyperVFirewallMdnsIpv6RuleName = L\"WslCore Inbound IPv6 mDNS Default Allow Rule\";\r\n\r\nstatic constexpr auto c_mdnsPort = L\"5353\";\r\nstatic constexpr auto c_mdnsIpv4Address = L\"224.0.0.251\";\r\nstatic constexpr auto c_mdnsIpv6Address = L\"ff02::fb\";\r\n\r\nnamespace wsl::core::networking {\r\nstd::wstring MakeLoopbackFirewallRuleId(const GUID& guid)\r\n{\r\n    return c_hyperVFirewallLoopbackRuleIdPrefix +\r\n           wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::None);\r\n}\r\n\r\nstd::wstring MakeLocalSubnetFirewallRuleId(const GUID& guid)\r\n{\r\n    return c_hyperVFirewallLocalSubnetRuleIdPrefix +\r\n           wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::None);\r\n}\r\n\r\nstd::wstring MakeICMPv6FirewallRuleId(const GUID& guid)\r\n{\r\n    return c_hyperVFirewallIcmpV6RuleIdPrefix + wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::None);\r\n}\r\n\r\nstd::wstring MakeICMPv4FirewallRuleId(const GUID& guid)\r\n{\r\n    return c_hyperVFirewallIcmpV4RuleIdPrefix + wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::None);\r\n}\r\n\r\nstd::wstring MakeMdnsIpv4FirewallRuleId(const GUID& guid)\r\n{\r\n    return c_hyperVFirewallMdnsIpv4RuleIdPrefix +\r\n           wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::None);\r\n}\r\n\r\nstd::wstring MakeMdnsIpv6FirewallRuleId(const GUID& guid)\r\n{\r\n    return c_hyperVFirewallMdnsIpv6RuleIdPrefix +\r\n           wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::None);\r\n}\r\n\r\n// if enabling Hyper-V Firewall, ensure the following rules are always added:\r\n// a) ICMP rules for inbound responses, without these we risk breaking basic connectivity and/or app compat\r\n// b) inbound rules to allow mDNS traffic. Note: Host firewall also has rules to allow inbound mDNS traffic but those\r\n//    are scoped to the Windows dnscache service so they can't be automatically translated to Hyper-V firewall\r\nstd::vector<FirewallRuleConfiguration> MakeDefaultFirewallRuleConfiguration(const GUID& guid)\r\n{\r\n    std::vector<FirewallRuleConfiguration> firewallConfiguration;\r\n\r\n    FirewallRuleConfiguration icmpV6AllowRule{MakeICMPv6FirewallRuleId(guid).c_str()};\r\n    icmpV6AllowRule.RuleName = wil::make_bstr(c_hyperVFirewallIcmpV6RuleName);\r\n    icmpV6AllowRule.Protocol = wil::make_bstr(L\"ICMPv6\");\r\n    icmpV6AllowRule.LocalPorts.emplace_back(wil::make_bstr(c_icmpv6NeighborSolicitation));\r\n    icmpV6AllowRule.LocalPorts.emplace_back(wil::make_bstr(c_icmpv6NeighborAdvertisement));\r\n    icmpV6AllowRule.LocalPorts.emplace_back(wil::make_bstr(c_icmpv6PortDestinationUnreachable));\r\n    icmpV6AllowRule.LocalPorts.emplace_back(wil::make_bstr(c_icmpv6PortTimeExceeded));\r\n    icmpV6AllowRule.RemoteAddresses.clear(); // all remote addresses\r\n    icmpV6AllowRule.RuleOperation = FirewallRuleOperation::Add;\r\n    firewallConfiguration.emplace_back(icmpV6AllowRule);\r\n\r\n    FirewallRuleConfiguration icmpV4AllowRule{MakeICMPv4FirewallRuleId(guid).c_str()};\r\n    icmpV4AllowRule.RuleName = wil::make_bstr(c_hyperVFirewallIcmpV4RuleName);\r\n    icmpV4AllowRule.Protocol = wil::make_bstr(L\"ICMPv4\");\r\n    icmpV4AllowRule.LocalPorts.emplace_back(wil::make_bstr(c_icmpv4PortDestinationUnreachable));\r\n    icmpV4AllowRule.LocalPorts.emplace_back(wil::make_bstr(c_icmpv4PortTimeExceeded));\r\n    icmpV4AllowRule.RemoteAddresses.clear(); // all remote addresses\r\n    icmpV4AllowRule.RuleOperation = FirewallRuleOperation::Add;\r\n    firewallConfiguration.emplace_back(icmpV4AllowRule);\r\n\r\n    FirewallRuleConfiguration mdnsIPv4AllowRule{MakeMdnsIpv4FirewallRuleId(guid).c_str()};\r\n    mdnsIPv4AllowRule.RuleName = wil::make_bstr(c_hyperVFirewallMdnsIpv4RuleName);\r\n    mdnsIPv4AllowRule.Protocol = wil::make_bstr(c_protocolUDP);\r\n    mdnsIPv4AllowRule.LocalPorts.emplace_back(wil::make_bstr(c_mdnsPort));\r\n    mdnsIPv4AllowRule.LocalAddresses.emplace_back(wil::make_bstr(c_mdnsIpv4Address));\r\n    mdnsIPv4AllowRule.RemoteAddresses.clear(); // all remote addresses\r\n    mdnsIPv4AllowRule.RuleOperation = FirewallRuleOperation::Add;\r\n    firewallConfiguration.emplace_back(mdnsIPv4AllowRule);\r\n\r\n    FirewallRuleConfiguration mdnsIPv6AllowRule{MakeMdnsIpv6FirewallRuleId(guid).c_str()};\r\n    mdnsIPv6AllowRule.RuleName = wil::make_bstr(c_hyperVFirewallMdnsIpv6RuleName);\r\n    mdnsIPv6AllowRule.Protocol = wil::make_bstr(c_protocolUDP);\r\n    mdnsIPv6AllowRule.LocalPorts.emplace_back(wil::make_bstr(c_mdnsPort));\r\n    mdnsIPv6AllowRule.LocalAddresses.emplace_back(wil::make_bstr(c_mdnsIpv6Address));\r\n    mdnsIPv6AllowRule.RemoteAddresses.clear(); // all remote addresses\r\n    mdnsIPv6AllowRule.RuleOperation = FirewallRuleOperation::Add;\r\n    firewallConfiguration.emplace_back(mdnsIPv6AllowRule);\r\n\r\n    return firewallConfiguration;\r\n}\r\n\r\nFirewallRuleConfiguration MakeLoopbackFirewallRuleConfiguration(const std::wstring& ruleId)\r\n{\r\n    return {ruleId.c_str(), c_hyperVFirewallLoopbackRuleName};\r\n}\r\n\r\nFirewallRuleConfiguration MakeLocalSubnetFirewallRuleConfiguration(const std::wstring& ruleId)\r\n{\r\n    return {ruleId.c_str(), c_hyperVFirewallLocalSubnetRuleName};\r\n}\r\n\r\n// We can require the updated Firewall API be available (on all OS's that get the update)\r\n// Thus we must indicate to the caller what version of Hyper-V Firewall is currently running.\r\nHyperVFirewallSupport GetHyperVFirewallSupportVersion(const FirewallConfiguration& firewallConfig) noexcept\r\ntry\r\n{\r\n    // Check to see if Hyper-V firewall is disabled via the registry.\r\n    DWORD localFirewallDisabled = 0;\r\n    wil::ResultFromException([&] {\r\n        localFirewallDisabled = windows::common::registry::ReadDword(HKEY_LOCAL_MACHINE, c_mpssvcRegPath, c_mpssvcRegDisableKey, 0);\r\n    });\r\n    if (localFirewallDisabled == 1)\r\n    {\r\n        WSL_LOG(\"GetHyperVFirewallSupportVersion: disabled by registry [HyperVFirewallSupport::None]\");\r\n        return HyperVFirewallSupport::None;\r\n    }\r\n\r\n    // There are no APIs to directly query which level of Hyper-V firewall support we have.\r\n    // Instead, we check for availability of specific firewall objects/fields present to\r\n    // determine if the requested functionality is supported or not.\r\n    //\r\n    // Currently, there are 3 possible levels of Hyper-V firewall OS support:\r\n    // 1 - No Hyper-V firewall OS support.\r\n    // 2 - Initial Hyper-V firewall support (Support for mirrored mode only).\r\n    //     To check for this support, we query for the 'MSFT_NetFirewallHyperVVMCreator' object.\r\n    // 3 - Enterprise Hyper-V firewall support (Support for NAT mode, configuring default settings values, and configuring per-profile configs).\r\n    //     To check for this support, we query for the 'MSFT_NetFirewallHyperVProfile' object.\r\n\r\n    // Connect to the root\\standardcimv2 namespace with the current user and obtain pointer to make IWbemServices calls.\r\n    const auto locator = wil::CoCreateInstance<WbemLocator, IWbemLocator>();\r\n    wil::com_ptr<IWbemServices> wbemService;\r\n    THROW_IF_FAILED(locator->ConnectServer(\r\n        wil::make_bstr(L\"ROOT\\\\standardcimv2\").get(), nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemService));\r\n\r\n    // Set the IWbemServices proxy so that impersonation of the user (client) occurs.\r\n    THROW_IF_FAILED(CoSetProxyBlanket(\r\n        wbemService.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE));\r\n\r\n    HRESULT hr{};\r\n    wil::com_ptr<IWbemClassObject> baseObject;\r\n\r\n    // Query for initial Hyper-V firewall OS support.\r\n    hr = wbemService->GetObjectW(\r\n        wil::make_bstr(L\"MSFT_NetFirewallHyperVVMCreator\").get(), WBEM_FLAG_RETURN_WBEM_COMPLETE, nullptr, &baseObject, nullptr);\r\n    if (FAILED(hr))\r\n    {\r\n        WSL_LOG(\r\n            \"GetHyperVFirewallSupportVersion: MSFT_NetFirewallHyperVVMCreator failed to be instantiated \"\r\n            \"[HyperVFirewallSupport::None]\",\r\n            TraceLoggingValue(hr));\r\n        return HyperVFirewallSupport::None;\r\n    }\r\n\r\n    // Query for version 2 of the Hyper-V Firewall\r\n    // We query for object instances instead of only getting the object class as this will return an error\r\n    // if the OS changes are present but the Hyper-V Firewall feature is disabled.\r\n    wil::com_ptr<IEnumWbemClassObject> enumObjects;\r\n    hr = wbemService->ExecQuery(\r\n        wil::make_bstr(L\"WQL\").get(), wil::make_bstr(L\"SELECT * FROM MSFT_NetFirewallHyperVProfile\").get(), WBEM_FLAG_RETURN_WBEM_COMPLETE, nullptr, &enumObjects);\r\n    if (FAILED(hr))\r\n    {\r\n        WSL_LOG(\r\n            \"GetHyperVFirewallSupportVersion: Query MSFT_NetFirewallHyperVProfile instances failed \"\r\n            \"[HyperVFirewallSupport::Version1]\",\r\n            TraceLoggingValue(hr));\r\n        return HyperVFirewallSupport::Version1;\r\n    }\r\n\r\n    // If we reached here, we were able to query the Version2 objects\r\n    WSL_LOG(\"GetHyperVFirewallSupportVersion [HyperVFirewallSupport::Version2]\");\r\n    return HyperVFirewallSupport::Version2;\r\n}\r\ncatch (...)\r\n{\r\n    LOG_CAUGHT_EXCEPTION();\r\n    WSL_LOG(\r\n        \"wsl::core::networking::GetHyperVFirewallSupportVersion [HyperVFirewallSupport::None]\",\r\n        TraceLoggingValue(ToString(firewallConfig.DefaultLoopbackPolicy), \"defaultLoopbackPolicy\"));\r\n\r\n    return HyperVFirewallSupport::None;\r\n}\r\n\r\nwil::com_ptr<IWbemClassObject> SpawnWbemObjectInstance(\r\n    _In_ PCWSTR className, const wil::shared_bstr& instanceId, _In_opt_ IWbemContext* wbemContext, const wil::com_ptr<IWbemServices>& wbemService)\r\n{\r\n    // Fetch the class definition\r\n    wil::com_ptr<IWbemClassObject> baseObject;\r\n    THROW_IF_FAILED(wbemService->GetObject(wil::make_bstr(className).get(), WBEM_FLAG_RETURN_WBEM_COMPLETE, wbemContext, &baseObject, nullptr));\r\n\r\n    // Create the new object instance\r\n    wil::com_ptr<IWbemClassObject> newObject;\r\n    THROW_IF_FAILED(baseObject->SpawnInstance(0, &newObject));\r\n\r\n    // Non-RAII variant as we are not owning the resource here\r\n    VARIANT v{};\r\n    v.vt = VT_BSTR;\r\n    v.bstrVal = instanceId.get();\r\n    THROW_IF_FAILED(newObject->Put(L\"InstanceID\", 0, &v, 0));\r\n\r\n    return newObject;\r\n}\r\n\r\nvoid WriteWMIInstance(_In_opt_ IWbemContext* wbemContext, const wil::com_ptr<IWbemServices>& wbemService, const wil::com_ptr<IWbemClassObject>& newObject)\r\n{\r\n    constexpr WBEM_CHANGE_FLAG_TYPE changeType = WBEM_FLAG_CREATE_OR_UPDATE;\r\n    wil::com_ptr<IWbemCallResult> wmiResult;\r\n    THROW_IF_FAILED_MSG(\r\n        wbemService->PutInstance(newObject.get(), changeType, wbemContext, &wmiResult), \"Failed to execute PutInstance WMI call\");\r\n\r\n    long callStatus;\r\n    THROW_IF_FAILED_MSG(wmiResult->GetCallStatus(WBEM_INFINITE, &callStatus), \"Failed to retrieve the WMI call status\");\r\n    THROW_IF_FAILED_MSG(callStatus, \"Failed to create object instance\");\r\n}\r\n\r\nHRESULT RegisterHyperVFirewallVmCreator(const GUID& vmCreatorId, const std::wstring& vmCreatorFriendlyName) noexcept\r\n{\r\n    PCSTR executionStep = \"\";\r\n    try\r\n    {\r\n        executionStep = \"CoCreateInstance\";\r\n        auto locator = wil::CoCreateInstance<WbemLocator, IWbemLocator>();\r\n\r\n        executionStep = \"ConnectServer\";\r\n        // Connect to the root\\standardcimv2 namespace with the current user and obtain pointer to make IWbemServices calls.\r\n        wil::com_ptr<IWbemServices> wbemService;\r\n        THROW_IF_FAILED(locator->ConnectServer(\r\n            wil::make_bstr(L\"ROOT\\\\standardcimv2\").get(), nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemService));\r\n\r\n        executionStep = \"CoSetProxyBlanket\";\r\n        // Set the IWbemServices proxy so that impersonation of the user (client) occurs.\r\n        THROW_IF_FAILED(CoSetProxyBlanket(\r\n            wbemService.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE));\r\n\r\n        executionStep = \"GetNetFirewallHyperVVMCreator\";\r\n        // Fetch the class definition\r\n        wil::com_ptr<IWbemClassObject> baseObject;\r\n        THROW_IF_FAILED(wbemService->GetObject(\r\n            wil::make_bstr(L\"MSFT_NetFirewallHyperVVMCreator\").get(), WBEM_FLAG_RETURN_WBEM_COMPLETE, nullptr, &baseObject, nullptr));\r\n\r\n        executionStep = \"GetRegisterHyperVVMCreator\";\r\n        // Get the parameter object\r\n        wil::com_ptr<IWbemClassObject> paramsObject;\r\n        THROW_IF_FAILED(baseObject->GetMethod(wil::make_bstr(L\"RegisterHyperVVMCreator\").get(), 0, &paramsObject, nullptr));\r\n\r\n        executionStep = \"SpawnInstance\";\r\n        // Spawn instance for the parameter object\r\n        wil::com_ptr<IWbemClassObject> paramsInstance;\r\n        THROW_IF_FAILED(paramsObject->SpawnInstance(0, &paramsInstance));\r\n\r\n        // Fill the parameter object\r\n        wil::unique_variant v;\r\n\r\n        executionStep = \"PutVMCreatorId\";\r\n        // VM Creator Id\r\n        std::wstring vmCreatorIdString =\r\n            wsl::shared::string::GuidToString<wchar_t>(vmCreatorId, wsl::shared::string::GuidToStringFlags::AddBraces);\r\n        v.vt = VT_BSTR;\r\n        v.bstrVal = wil::make_bstr(vmCreatorIdString.c_str()).release();\r\n        THROW_IF_FAILED(paramsInstance->Put(L\"VMCreatorId\", 0, &v, 0));\r\n        v.reset();\r\n\r\n        executionStep = \"PutFriendlyName\";\r\n        // Friendly name\r\n        v.vt = VT_BSTR;\r\n        v.bstrVal = wil::make_bstr(vmCreatorFriendlyName.c_str()).release();\r\n        THROW_IF_FAILED(paramsInstance->Put(L\"FriendlyName\", 0, &v, 0));\r\n        v.reset();\r\n\r\n        executionStep = \"NetFirewallHyperVVMCreator::RegisterHyperVVMCreator\";\r\n        // making the recommended semi-synchronous call into WMI\r\n        // which requires waiting for the completion with the resultObject\r\n        wil::com_ptr<IWbemCallResult> resultObject;\r\n        THROW_IF_FAILED(wbemService->ExecMethod(\r\n            wil::make_bstr(L\"MSFT_NetFirewallHyperVVMCreator\").get(),\r\n            wil::make_bstr(L\"RegisterHyperVVMCreator\").get(),\r\n            WBEM_FLAG_RETURN_IMMEDIATELY,\r\n            nullptr,\r\n            paramsInstance.get(),\r\n            nullptr,\r\n            &resultObject));\r\n\r\n        executionStep = \"GetResultObject\";\r\n        wil::com_ptr<IWbemClassObject> outParams;\r\n        auto result = resultObject->GetResultObject(WBEM_INFINITE, &outParams);\r\n        WSL_LOG(\r\n            \"RegisterHyperVFirewallVmCreator [GetResultObject]\",\r\n            TraceLoggingValue(\r\n                result == WBEM_E_ALREADY_EXISTS ? \"WBEM_E_ALREADY_EXISTS\" : std::to_string(result).c_str(), \"result\"));\r\n\r\n        if (result == WBEM_E_ALREADY_EXISTS)\r\n        {\r\n            // the method immediately returned already exists - we're fine to return now.\r\n            result = S_OK;\r\n        }\r\n        THROW_IF_FAILED_MSG(result, \"Failed to register hyper-v firewall vm creator\");\r\n        return S_OK;\r\n    }\r\n    catch (...)\r\n    {\r\n        auto hr = wil::ResultFromCaughtException();\r\n        WSL_LOG(\r\n            \"RegisterHyperVFirewallVmCreatorFailed\",\r\n            TraceLoggingValue(hr, \"result\"),\r\n            TraceLoggingValue(executionStep, \"executionStep\"));\r\n\r\n        return hr;\r\n    }\r\n}\r\n\r\nHRESULT ConfigureHyperVFirewallLoopbackAllow(const GUID& vmCreatorId) noexcept\r\n{\r\n    PCSTR executionStep = \"\";\r\n    try\r\n    {\r\n        executionStep = \"CoCreateInstance\";\r\n        auto locator = wil::CoCreateInstance<WbemLocator, IWbemLocator>();\r\n\r\n        executionStep = \"CoCreateInstanceWbemContext\";\r\n        // Create WbemContext for SystemDefaults\r\n        // SystemDefaults are configured with lowest priority, so admin configuration can overwrite it\r\n        auto wbemContext = wil::CoCreateInstance<WbemContext, IWbemContext>();\r\n        wil::unique_variant v;\r\n        v.vt = VT_BSTR;\r\n        v.bstrVal = wil::make_bstr(L\"SystemDefaults\").release();\r\n        executionStep = \"SetPolicyStore\";\r\n        THROW_IF_FAILED(wbemContext->SetValue(L\"PolicyStore\", 0, &v));\r\n        v.reset();\r\n\r\n        executionStep = \"ConnectServer\";\r\n        // Connect to the root\\standardcimv2 namespace with the current user and obtain pointer to make IWbemServices calls.\r\n        wil::com_ptr<IWbemServices> wbemService;\r\n        THROW_IF_FAILED(locator->ConnectServer(\r\n            wil::make_bstr(L\"ROOT\\\\standardcimv2\").get(), nullptr, nullptr, nullptr, 0, nullptr, wbemContext.get(), &wbemService));\r\n\r\n        executionStep = \"CoSetProxyBlanket\";\r\n        // Set the IWbemServices proxy so that impersonation of the user (client) occurs.\r\n        THROW_IF_FAILED(CoSetProxyBlanket(\r\n            wbemService.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE));\r\n\r\n        executionStep = \"SpawnNetFirewallHyperVVMSetting\";\r\n        // Spawn instance\r\n        wil::shared_bstr vmCreatorIdString = wil::make_bstr(\r\n            wsl::shared::string::GuidToString<wchar_t>(vmCreatorId, wsl::shared::string::GuidToStringFlags::AddBraces).c_str());\r\n        wil::com_ptr<IWbemClassObject> settingsObject =\r\n            SpawnWbemObjectInstance(L\"MSFT_NetFirewallHyperVVMSetting\", vmCreatorIdString, wbemContext.get(), wbemService);\r\n\r\n        executionStep = \"PutName\";\r\n        v.vt = VT_BSTR;\r\n        v.bstrVal = vmCreatorIdString.get();\r\n        const auto hr = settingsObject->Put(L\"Name\", 0, &v, 0);\r\n        v.release(); // the variant should not free the bstr, it's owned by the wil::shared_bstr\r\n        THROW_IF_FAILED(hr);\r\n\r\n        executionStep = \"PutLoopbackEnabled\";\r\n        v.vt = VT_I4;\r\n        v.lVal = c_true;\r\n        THROW_IF_FAILED(settingsObject->Put(L\"LoopbackEnabled\", 0, &v, 0));\r\n        v.reset();\r\n\r\n        executionStep = \"WriteWMIInstance\";\r\n        WriteWMIInstance(wbemContext.get(), wbemService, settingsObject);\r\n\r\n        return S_OK;\r\n    }\r\n    catch (...)\r\n    {\r\n        auto hr = wil::ResultFromCaughtException();\r\n        WSL_LOG(\r\n            \"ConfigureHyperVFirewallLoopbackAllowFailed\",\r\n            TraceLoggingValue(hr, \"result\"),\r\n            TraceLoggingValue(executionStep, \"executionStep\"));\r\n\r\n        return hr;\r\n    }\r\n}\r\n\r\nvoid ConfigureHyperVFirewall(const FirewallConfiguration& firewallConfig, const std::wstring& vmCreatorFriendlyName) noexcept\r\ntry\r\n{\r\n    if (!firewallConfig.Enabled())\r\n    {\r\n        return;\r\n    }\r\n    const auto coInit = InitializeCOMState();\r\n\r\n    // Register the input ID with the firewall service.\r\n    // If this fails, still proceed with rule creation, as the rules\r\n    // will still be enforced without the vm creator registered\r\n    LOG_IF_FAILED_MSG(\r\n        RegisterHyperVFirewallVmCreator(firewallConfig.VmCreatorId.value(), vmCreatorFriendlyName),\r\n        \"RegisterHyperVFirewallVmCreator\");\r\n\r\n    // Configure firewall settings\r\n    // The OS default is to block loopback. Configure the loopback setting only if the client requests a configuration different than OS default.\r\n    if (FirewallAction::Allow == firewallConfig.DefaultLoopbackPolicy)\r\n    {\r\n        LOG_IF_FAILED_MSG(\r\n            ConfigureHyperVFirewallLoopbackAllow(firewallConfig.VmCreatorId.value()), \"ConfigureHyperVFirewallLoopbackAllow\");\r\n    }\r\n\r\n    // Configure firewall rules\r\n    for (const auto& firewallRule : firewallConfig.Rules)\r\n    {\r\n        if (firewallRule.RuleOperation == wsl::core::FirewallRuleOperation::Add)\r\n        {\r\n            if (FAILED_LOG(AddHyperVFirewallRule(firewallConfig.VmCreatorId.value(), firewallRule)))\r\n            {\r\n                // Due to a Windows bug, certain rules do not accept local ports.\r\n                // If this error is encountered here, we try to instead add a less scoped\r\n                // version of the rule to ensure necessary traffic is still allowed\r\n                FirewallRuleConfiguration currFirewallRule = firewallRule;\r\n                currFirewallRule.LocalPorts.clear();\r\n                LOG_IF_FAILED_MSG(\r\n                    AddHyperVFirewallRule(firewallConfig.VmCreatorId.value(), currFirewallRule),\r\n                    \"AddHyperVFirewallRule for %ls\",\r\n                    currFirewallRule.RuleId.get());\r\n            }\r\n        }\r\n        else if (firewallRule.RuleOperation == wsl::core::FirewallRuleOperation::Delete)\r\n        {\r\n            LOG_IF_FAILED_MSG(\r\n                RemoveHyperVFirewallRule(firewallRule.RuleId.get()), \"RemoveHyperVFirewallRule for %ls\", firewallRule.RuleId.get());\r\n        }\r\n        else\r\n        {\r\n            // Unexpected rule operation type\r\n            WI_ASSERT(false);\r\n        }\r\n    }\r\n\r\n    // WSL may have previously added this rule (which has since been renamed). Remove it if it is present.\r\n    const auto oldLoopbackRuleId =\r\n        c_hyperVFirewallLoopbackRuleIdPrefix_Old +\r\n        wsl::shared::string::GuidToString<wchar_t>(firewallConfig.VmCreatorId.value(), wsl::shared::string::GuidToStringFlags::None);\r\n\r\n    LOG_IF_FAILED_MSG(RemoveHyperVFirewallRule(oldLoopbackRuleId), \"RemoveHyperVFirewallRule for %ls\", oldLoopbackRuleId.c_str());\r\n}\r\nCATCH_LOG()\r\n\r\nHRESULT AddHyperVFirewallRule(const GUID& vmCreatorId, const wsl::core::FirewallRuleConfiguration& firewallRule) noexcept\r\n{\r\n    PCSTR executionStep = \"\";\r\n    try\r\n    {\r\n        executionStep = \"CoCreateInstance\";\r\n        auto locator = wil::CoCreateInstance<WbemLocator, IWbemLocator>();\r\n\r\n        executionStep = \"ConnectServer\";\r\n        // Connect to the root\\standardcimv2 namespace with the current user and obtain pointer to make IWbemServices calls.\r\n        wil::com_ptr<IWbemServices> wbemService;\r\n        THROW_IF_FAILED(locator->ConnectServer(\r\n            wil::make_bstr(L\"ROOT\\\\standardcimv2\").get(), nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemService));\r\n\r\n        executionStep = \"CoSetProxyBlanket\";\r\n        // Set the IWbemServices proxy so that impersonation of the user (client) occurs.\r\n        THROW_IF_FAILED(CoSetProxyBlanket(\r\n            wbemService.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE));\r\n\r\n        executionStep = \"SpawnNetFirewallHyperVRule\";\r\n        wil::com_ptr<IWbemClassObject> ruleObject =\r\n            SpawnWbemObjectInstance(L\"MSFT_NetFirewallHyperVRule\", firewallRule.RuleId, nullptr, wbemService);\r\n\r\n        executionStep = \"PutInstanceID\";\r\n        // Fill the object\r\n        wil::unique_variant v;\r\n        v.vt = VT_BSTR;\r\n        v.bstrVal = firewallRule.RuleId.get();\r\n        HRESULT hr = ruleObject->Put(L\"InstanceID\", 0, &v, 0);\r\n        v.release(); // the variant should not free the bstr, it's owned by the wil::shared_bstr\r\n        THROW_IF_FAILED(hr);\r\n\r\n        executionStep = \"PutElementName\";\r\n        v.vt = VT_BSTR;\r\n        v.bstrVal = firewallRule.RuleName.get();\r\n        hr = ruleObject->Put(L\"ElementName\", 0, &v, 0);\r\n        v.release(); // the variant should not free the bstr, it's owned by the wil::shared_bstr\r\n        THROW_IF_FAILED(hr);\r\n\r\n        executionStep = \"PutDirection\";\r\n        v.vt = VT_I4;\r\n        v.lVal = c_directionInbound;\r\n        THROW_IF_FAILED(ruleObject->Put(L\"Direction\", 0, &v, 0));\r\n        v.reset();\r\n\r\n        executionStep = \"PutVMCreatorId\";\r\n        std::wstring vmCreatorIdString =\r\n            wsl::shared::string::GuidToString<wchar_t>(vmCreatorId, wsl::shared::string::GuidToStringFlags::AddBraces);\r\n        v.vt = VT_BSTR;\r\n        v.bstrVal = wil::make_bstr(vmCreatorIdString.c_str()).release();\r\n        THROW_IF_FAILED(ruleObject->Put(L\"VMCreatorId\", 0, &v, 0));\r\n        v.reset();\r\n\r\n        executionStep = \"PutAction\";\r\n        v.vt = VT_I4;\r\n        v.lVal = c_actionAllow;\r\n        THROW_IF_FAILED(ruleObject->Put(L\"Action\", 0, &v, 0));\r\n        v.reset();\r\n\r\n        executionStep = \"PutEnabled\";\r\n        v.vt = VT_I4;\r\n        v.lVal = c_ruleEnabled;\r\n        THROW_IF_FAILED(ruleObject->Put(L\"Enabled\", 0, &v, 0));\r\n        v.reset();\r\n\r\n        executionStep = \"PutProtocol\";\r\n        if (firewallRule.Protocol.is_valid())\r\n        {\r\n            v.vt = VT_BSTR;\r\n            v.bstrVal = firewallRule.Protocol.get();\r\n            hr = ruleObject->Put(L\"Protocol\", 0, &v, 0);\r\n            v.release(); // the variant should not free the bstr, it's owned by the wil::shared_bstr\r\n            THROW_IF_FAILED(hr);\r\n        }\r\n\r\n        executionStep = \"PutLocalPorts\";\r\n        if (!firewallRule.LocalPorts.empty())\r\n        {\r\n            // Convert to a safe array for usage in WMI\r\n            CComSafeArray<BSTR> localPortsArray;\r\n            THROW_IF_FAILED(localPortsArray.Create());\r\n            for (const auto& localPort : firewallRule.LocalPorts)\r\n            {\r\n                THROW_IF_FAILED(localPortsArray.Add(localPort.get()));\r\n            }\r\n            v.vt = (VT_BSTR | VT_ARRAY);\r\n            v.parray = localPortsArray.Detach();\r\n            THROW_IF_FAILED(ruleObject->Put(L\"LocalPorts\", 0, &v, 0));\r\n            v.reset();\r\n        }\r\n\r\n        executionStep = \"PutLocalAddresses\";\r\n        if (!firewallRule.LocalAddresses.empty())\r\n        {\r\n            // Convert to a safe array for usage in WMI\r\n            CComSafeArray<BSTR> localAddressesArray;\r\n            THROW_IF_FAILED(localAddressesArray.Create());\r\n            for (const auto& localAddress : firewallRule.LocalAddresses)\r\n            {\r\n                THROW_IF_FAILED(localAddressesArray.Add(localAddress.get()));\r\n            }\r\n            v.vt = (VT_BSTR | VT_ARRAY);\r\n            v.parray = localAddressesArray.Detach();\r\n            THROW_IF_FAILED(ruleObject->Put(L\"LocalAddresses\", 0, &v, 0));\r\n            v.reset();\r\n        }\r\n\r\n        executionStep = \"PutRemoteAddresses\";\r\n        if (!firewallRule.RemoteAddresses.empty())\r\n        {\r\n            // Convert to a safe array for usage in WMI\r\n            CComSafeArray<BSTR> remoteAddressesArray;\r\n            THROW_IF_FAILED(remoteAddressesArray.Create());\r\n            for (const auto& remoteAddress : firewallRule.RemoteAddresses)\r\n            {\r\n                THROW_IF_FAILED(remoteAddressesArray.Add(remoteAddress.get()));\r\n            }\r\n            v.vt = (VT_BSTR | VT_ARRAY);\r\n            v.parray = remoteAddressesArray.Detach();\r\n            THROW_IF_FAILED(ruleObject->Put(L\"RemoteAddresses\", 0, &v, 0));\r\n            v.reset();\r\n        }\r\n\r\n        executionStep = \"WriteWMIInstance\";\r\n        WriteWMIInstance(nullptr, wbemService, ruleObject);\r\n\r\n        return S_OK;\r\n    }\r\n    catch (...)\r\n    {\r\n        auto hr = wil::ResultFromCaughtException();\r\n        WSL_LOG(\"AddHyperVFirewallRuleFailed\", TraceLoggingValue(hr, \"result\"), TraceLoggingValue(executionStep, \"executionStep\"));\r\n        return hr;\r\n    }\r\n}\r\n\r\nHRESULT RemoveHyperVFirewallRule(const std::wstring& ruleId) noexcept\r\n{\r\n    PCSTR executionStep = \"\";\r\n    try\r\n    {\r\n        executionStep = \"CoCreateInstance\";\r\n        const auto locator = wil::CoCreateInstance<WbemLocator, IWbemLocator>();\r\n\r\n        executionStep = \"ConnectServer\";\r\n        // Connect to the root\\standardcimv2 namespace with the current user and obtain pointer to make IWbemServices calls.\r\n        wil::com_ptr<IWbemServices> wbemService;\r\n        THROW_IF_FAILED(locator->ConnectServer(\r\n            wil::make_bstr(L\"ROOT\\\\standardcimv2\").get(), nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemService));\r\n\r\n        executionStep = \"CoSetProxyBlanket\";\r\n        // Set the IWbemServices proxy so that impersonation of the user (client) occurs.\r\n        THROW_IF_FAILED(CoSetProxyBlanket(\r\n            wbemService.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE));\r\n\r\n        // Create the rule deletion query string\r\n        const std::wstring ruleDeletionString = std::format(L\"MSFT_NetFirewallHyperVRule.InstanceId=\\\"{}\\\"\", ruleId);\r\n        const wil::unique_bstr ruleDeletionBstr = wil::make_bstr(ruleDeletionString.c_str());\r\n\r\n        executionStep = \"DeleteInstance\";\r\n        // Delete the instance to WMI\r\n        wil::com_ptr<IWbemCallResult> wmiResult;\r\n        THROW_IF_FAILED_MSG(\r\n            wbemService->DeleteInstance(ruleDeletionBstr.get(), 0, nullptr, &wmiResult),\r\n            \"Failed to execute the WMI call for deleting the hyper-v firewall ruleId=%ws\",\r\n            ruleId.c_str());\r\n\r\n        executionStep = \"GetCallStatus\";\r\n        long callStatus;\r\n        THROW_IF_FAILED_MSG(\r\n            wmiResult->GetCallStatus(WBEM_INFINITE, &callStatus),\r\n            \"Failed to retrieve the WMI call status for deleting the hyper-v firewall ruleId=%ws\",\r\n            ruleId.c_str());\r\n\r\n        // Ignore error not found, as this indicates the rule is already deleted\r\n        if (callStatus == WBEM_E_NOT_FOUND)\r\n        {\r\n            callStatus = S_OK;\r\n        }\r\n        THROW_IF_FAILED_MSG(callStatus, \"Failed to delete hyper-v firewall rule with ruleId=%ws\", ruleId.c_str());\r\n        return S_OK;\r\n    }\r\n    catch (...)\r\n    {\r\n        auto hr = wil::ResultFromCaughtException();\r\n        WSL_LOG(\r\n            \"RemoveHyperVFirewallRuleFailed\", TraceLoggingValue(hr, \"result\"), TraceLoggingValue(executionStep, \"executionStep\"));\r\n\r\n        return hr;\r\n    }\r\n}\r\n\r\nHRESULT AddHostFirewallRule(const wsl::core::FirewallRuleConfiguration& firewallRule) noexcept\r\ntry\r\n{\r\n    auto wbemLocator = wil::CoCreateInstance<WbemLocator, IWbemLocator>();\r\n\r\n    // Create WbemContext for ActiveStore\r\n    // ActiveStore is used so that the rules are not persisted and therefore not leaked upon uninstall\r\n    auto wbemContext = wil::CoCreateInstance<WbemContext, IWbemContext>();\r\n    wil::unique_variant v;\r\n    v.vt = VT_BSTR;\r\n    v.bstrVal = wil::make_bstr(L\"ActiveStore\").release();\r\n    THROW_IF_FAILED(wbemContext->SetValue(L\"PolicyStore\", 0, &v));\r\n    v.reset();\r\n\r\n    // Connect to the root\\standardcimv2 namespace with the current user and obtain pointer to make IWbemServices calls.\r\n    wil::com_ptr<IWbemServices> wbemService;\r\n    THROW_IF_FAILED(wbemLocator->ConnectServer(\r\n        wil::make_bstr(L\"ROOT\\\\standardcimv2\").get(), nullptr, nullptr, nullptr, 0, nullptr, wbemContext.get(), &wbemService));\r\n\r\n    // Set the IWbemServices proxy so that impersonation of the user (client) occurs.\r\n    THROW_IF_FAILED(CoSetProxyBlanket(\r\n        wbemService.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE));\r\n\r\n    wil::com_ptr<IWbemClassObject> ruleObject =\r\n        SpawnWbemObjectInstance(L\"MSFT_NetFirewallRule\", firewallRule.RuleId, wbemContext.get(), wbemService);\r\n\r\n    v.vt = VT_BSTR;\r\n    v.bstrVal = firewallRule.RuleName.get();\r\n    auto hr = ruleObject->Put(L\"ElementName\", 0, &v, 0);\r\n    v.release(); // the variant should not free the bstr, it's owned by the wil::shared_bstr\r\n    THROW_IF_FAILED(hr);\r\n\r\n    v.vt = VT_I4;\r\n    v.lVal = c_directionInbound;\r\n    THROW_IF_FAILED(ruleObject->Put(L\"Direction\", 0, &v, 0));\r\n    v.reset();\r\n\r\n    v.vt = VT_I4;\r\n    v.lVal = c_actionAllow;\r\n    THROW_IF_FAILED(ruleObject->Put(L\"Action\", 0, &v, 0));\r\n    v.reset();\r\n\r\n    // Create the rule initially in the disabled state so that we can add the proper associated objects with the correct scoping of the rule\r\n    v.vt = VT_I4;\r\n    v.lVal = c_ruleDisabled;\r\n    THROW_IF_FAILED(ruleObject->Put(L\"Enabled\", 0, &v, 0));\r\n    v.reset();\r\n\r\n    v.vt = VT_BSTR;\r\n    v.bstrVal = wil::make_bstr(L\"ActiveStore\").release();\r\n    THROW_IF_FAILED(ruleObject->Put(L\"PolicyStoreSource\", 0, &v, 0));\r\n    v.reset();\r\n\r\n    WriteWMIInstance(wbemContext.get(), wbemService, ruleObject);\r\n\r\n    // Firewall WMI uses associated instances for many rule conditions. These are created as separate objects (if the input parameter uses the fields)\r\n\r\n    if (firewallRule.Protocol.is_valid() || !firewallRule.LocalPorts.empty())\r\n    {\r\n        wil::com_ptr<IWbemClassObject> protocolPortObject =\r\n            SpawnWbemObjectInstance(L\"MSFT_NetProtocolPortFilter\", firewallRule.RuleId, wbemContext.get(), wbemService);\r\n\r\n        if (firewallRule.Protocol.is_valid())\r\n        {\r\n            v.vt = VT_BSTR;\r\n            v.bstrVal = firewallRule.Protocol.get();\r\n            hr = protocolPortObject->Put(L\"Protocol\", 0, &v, 0);\r\n            v.release(); // the variant should not free the bstr, it's owned by the wil::shared_bstr\r\n            THROW_IF_FAILED(hr);\r\n        }\r\n\r\n        if (!firewallRule.LocalPorts.empty())\r\n        {\r\n            // Convert to a safe array for usage in WMI\r\n            CComSafeArray<BSTR> localPortsArray;\r\n            THROW_IF_FAILED(localPortsArray.Create());\r\n            for (const auto& localPort : firewallRule.LocalPorts)\r\n            {\r\n                THROW_IF_FAILED(localPortsArray.Add(localPort.get()));\r\n            }\r\n            v.vt = (VT_BSTR | VT_ARRAY);\r\n            v.parray = localPortsArray.Detach();\r\n            THROW_IF_FAILED(protocolPortObject->Put(L\"LocalPort\", 0, &v, 0));\r\n            v.reset();\r\n        }\r\n\r\n        WriteWMIInstance(wbemContext.get(), wbemService, protocolPortObject);\r\n    }\r\n\r\n    if (firewallRule.LocalApplication.is_valid())\r\n    {\r\n        wil::com_ptr<IWbemClassObject> applicationObject =\r\n            SpawnWbemObjectInstance(L\"MSFT_NetApplicationFilter\", firewallRule.RuleId, wbemContext.get(), wbemService);\r\n\r\n        v.vt = VT_BSTR;\r\n        v.bstrVal = firewallRule.LocalApplication.get();\r\n        hr = applicationObject->Put(L\"AppPath\", 0, &v, 0);\r\n        v.release(); // the variant should not free the bstr, it's owned by the wil::shared_bstr\r\n        THROW_IF_FAILED(hr);\r\n\r\n        WriteWMIInstance(wbemContext.get(), wbemService, applicationObject);\r\n    }\r\n\r\n    if (firewallRule.LocalService.is_valid())\r\n    {\r\n        wil::com_ptr<IWbemClassObject> serviceObject =\r\n            SpawnWbemObjectInstance(L\"MSFT_NetServiceFilter\", firewallRule.RuleId, wbemContext.get(), wbemService);\r\n\r\n        v.vt = VT_BSTR;\r\n        v.bstrVal = firewallRule.LocalService.get();\r\n        hr = serviceObject->Put(L\"ServiceName\", 0, &v, 0);\r\n        v.release(); // the variant should not free the bstr, it's owned by the wil::shared_bstr\r\n        THROW_IF_FAILED(hr);\r\n\r\n        WriteWMIInstance(wbemContext.get(), wbemService, serviceObject);\r\n    }\r\n\r\n    if (!firewallRule.RemoteAddresses.empty())\r\n    {\r\n        wil::com_ptr<IWbemClassObject> addressObject =\r\n            SpawnWbemObjectInstance(L\"MSFT_NetAddressFilter\", firewallRule.RuleId, wbemContext.get(), wbemService);\r\n\r\n        // Convert to a safe array for usage in WMI\r\n        CComSafeArray<BSTR> remoteAddressesArray;\r\n        THROW_IF_FAILED(remoteAddressesArray.Create());\r\n        for (const auto& remoteAddress : firewallRule.RemoteAddresses)\r\n        {\r\n            THROW_IF_FAILED(remoteAddressesArray.Add(remoteAddress.get()));\r\n        }\r\n        v.vt = (VT_BSTR | VT_ARRAY);\r\n        v.parray = remoteAddressesArray.Detach();\r\n        THROW_IF_FAILED(addressObject->Put(L\"RemoteAddress\", 0, &v, 0));\r\n        v.reset();\r\n\r\n        WriteWMIInstance(wbemContext.get(), wbemService, addressObject);\r\n    }\r\n\r\n    // After necessary associated objects are created, we can now enable the rule\r\n    ruleObject = SpawnWbemObjectInstance(L\"MSFT_NetFirewallRule\", firewallRule.RuleId, wbemContext.get(), wbemService);\r\n\r\n    v.vt = VT_I4;\r\n    v.lVal = c_ruleEnabled;\r\n    THROW_IF_FAILED(ruleObject->Put(L\"Enabled\", 0, &v, 0));\r\n    v.reset();\r\n\r\n    WriteWMIInstance(wbemContext.get(), wbemService, ruleObject);\r\n    return S_OK;\r\n}\r\nCATCH_RETURN()\r\n\r\nvoid ConfigureSharedAccessFirewallRule() noexcept\r\n{\r\n    // Configures necessary host firewall rules:\r\n    //    -Inbound rule to allow UDP traffic to port 53 for the SharedAccess service. This allows the proxied DNS requests to the host.\r\n    LPCWSTR sharedAccessRulePorts[] = {L\"53\"};\r\n    const wsl::core::FirewallRuleConfiguration sharedAccessRule(\r\n        c_sharedAccessRuleId, c_sharedAccessRuleName, c_protocolUDP, 1, sharedAccessRulePorts, 0, nullptr, 0, nullptr, c_sharedAccessService, c_svchostApplication);\r\n    LOG_IF_FAILED_MSG(AddHostFirewallRule(sharedAccessRule), \"AddHostFirewallRule::sharedAccessRule\");\r\n}\r\n\r\n} // namespace wsl::core::networking"
  },
  {
    "path": "src/windows/common/WslCoreFirewallSupport.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n#include <optional>\r\n#include <string>\r\n#include <vector>\r\n#include <wbemcli.h>\r\n#include <windows.h>\r\n#include \"WslCoreConfig.h\"\r\n\r\nnamespace wsl::core::networking {\r\n\r\nenum class HyperVFirewallSupport\r\n{\r\n    None,\r\n    Version1, // initially shipped with SV2\r\n    Version2  // updated from Version1 and backported down to Windows 11 22H2\r\n};\r\nHyperVFirewallSupport GetHyperVFirewallSupportVersion(const FirewallConfiguration& firewallConfig) noexcept;\r\n\r\nstd::wstring MakeLoopbackFirewallRuleId(const GUID& guid);\r\nstd::wstring MakeLocalSubnetFirewallRuleId(const GUID& guid);\r\n\r\nstd::vector<FirewallRuleConfiguration> MakeDefaultFirewallRuleConfiguration(const GUID& guid);\r\nFirewallRuleConfiguration MakeLoopbackFirewallRuleConfiguration(const std::wstring& ruleId);\r\nFirewallRuleConfiguration MakeLocalSubnetFirewallRuleConfiguration(const std::wstring& ruleId);\r\n\r\nvoid ConfigureHyperVFirewall(const FirewallConfiguration& firewallConfig, const std::wstring& vmCreatorFriendlyName) noexcept;\r\n\r\nvoid ConfigureSharedAccessFirewallRule() noexcept;\r\n\r\nHRESULT AddHyperVFirewallRule(const GUID& vmCreatorId, const wsl::core::FirewallRuleConfiguration& firewallRule) noexcept;\r\nHRESULT RemoveHyperVFirewallRule(const std::wstring& ruleId) noexcept;\r\n\r\n} // namespace wsl::core::networking\r\n"
  },
  {
    "path": "src/windows/common/WslCoreHostDnsInfo.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include <LxssDynamicFunction.h>\n#include \"precomp.h\"\n\n#include <algorithm>\n\n#include <iphlpapi.h>\n#include <ipifcons.h>\n\n#include \"WmiService.h\"\n#include \"WslCoreHostDnsInfo.h\"\n\nstatic constexpr auto c_asciiNewLine = \"\\xa\";\n\n// Used for querying suffixes via WMI\nstatic constexpr auto c_suffixSearchList = L\"SuffixSearchList\";\nstatic constexpr auto c_connectionSpecificSuffix = L\"ConnectionSpecificSuffix\";\nstatic constexpr auto c_connectionSpecificSuffixSearchList = L\"ConnectionSpecificSuffixSearchList\";\nstatic constexpr auto c_interfaceIndex = L\"InterfaceIndex\";\n\nstatic constexpr auto c_ipHelperModuleName = L\"Iphlpapi.dll\";\n\nstatic std::optional<LxssDynamicFunction<decltype(GetInterfaceDnsSettings)>> g_getInterfaceDnsSettings;\nstatic std::optional<LxssDynamicFunction<decltype(FreeInterfaceDnsSettings)>> g_freeInterfaceDnsSettings;\n\nstruct DnsRegistryPath\n{\n    const wchar_t* registryPath;\n    const bool isRecursive;\n};\n\n// Registry paths that need to be monitored for DNS suffix changes\nconstexpr DnsRegistryPath c_dnsSuffixesRegistryPaths[] = {\n    {L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\Dnscache\\\\InterfaceSpecificParameters\", true},\n    {L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip\\\\Parameters\\\\Interfaces\", true},\n    {L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip6\\\\Parameters\\\\Interfaces\", true},\n    {L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip\\\\Parameters\", false},\n    {L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip6\\\\Parameters\", false},\n    {L\"SOFTWARE\\\\Policies\\\\Microsoft\\\\Windows NT\\\\DNSClient\", false},\n    {L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\Dnscache\\\\Parameters\", false}};\n\n// Load GetInterfaceDnsSettings and FreeInterfaceDnsSettings, if available\nstatic bool LoadIpHelperMethods() noexcept\ntry\n{\n    static wil::shared_hmodule ipHelperModule;\n    static std::once_flag loadFlag;\n\n    std::call_once(loadFlag, [&]() {\n        ipHelperModule.reset(LoadLibraryEx(c_ipHelperModuleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));\n        RETURN_LAST_ERROR_IF_EXPECTED(!ipHelperModule);\n\n        LxssDynamicFunction<decltype(GetInterfaceDnsSettings)> local_getInterfaceDnsSettings{DynamicFunctionErrorLogs::None};\n        RETURN_IF_FAILED_EXPECTED(local_getInterfaceDnsSettings.load(ipHelperModule, \"GetInterfaceDnsSettings\"));\n        LxssDynamicFunction<decltype(FreeInterfaceDnsSettings)> local_freeInterfaceDnsSettings{DynamicFunctionErrorLogs::None};\n        RETURN_IF_FAILED_EXPECTED(local_freeInterfaceDnsSettings.load(ipHelperModule, \"FreeInterfaceDnsSettings\"));\n\n        g_getInterfaceDnsSettings.emplace(std::move(local_getInterfaceDnsSettings));\n        g_freeInterfaceDnsSettings.emplace(std::move(local_freeInterfaceDnsSettings));\n        return S_OK;\n    });\n\n    if (g_getInterfaceDnsSettings.has_value() && g_freeInterfaceDnsSettings.has_value())\n    {\n        return true;\n    }\n\n    WSL_LOG(\"LoadIpHelperMethods (false): GetInterfaceDnsSettings is not present\");\n    return false;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return false;\n}\n\nDWORD wsl::core::networking::GetBestInterface()\n{\n    DWORD bestInterface = 0;\n    SOCKADDR_STORAGE address{};\n    IN4ADDR_SETANY((SOCKADDR_IN*)&address);\n    if (GetBestInterfaceEx((SOCKADDR*)&address, &bestInterface) != NO_ERROR)\n    {\n        IN6ADDR_SETANY((SOCKADDR_IN6*)&address);\n        if (GetBestInterfaceEx((SOCKADDR*)&address, &bestInterface) != NO_ERROR)\n        {\n            bestInterface = 0;\n        }\n    }\n\n    WSL_LOG(\"wsl::core::networking::GetBestInterface [GetBestInterfaceEx]\", TraceLoggingValue(bestInterface, \"bestInterface\"));\n\n    return bestInterface;\n}\n\nwsl::core::networking::DnsInfo wsl::core::networking::HostDnsInfo::GetDnsTunnelingSettings(const std::wstring& dnsTunnelingNameserver)\n{\n    DnsInfo dnsInfo;\n    dnsInfo.Servers.push_back(wsl::shared::string::WideToMultiByte(dnsTunnelingNameserver));\n\n    // All Windows DNS suffixes are configured in Linux when DNS tunneling is enabled\n    dnsInfo.Domains = GetAllDnsSuffixes(AdapterAddresses::GetCurrent());\n\n    return dnsInfo;\n}\n\nstd::vector<std::string> wsl::core::networking::HostDnsInfo::GetDnsServerStrings(\n    _In_ const PIP_ADAPTER_DNS_SERVER_ADDRESS& FirstDnsServer, _In_ USHORT IpFamilyFilter, _In_ USHORT MaxValues)\n{\n    std::vector<std::string> DnsServerStrings;\n    CHAR IpBuffer[46];\n\n    PIP_ADAPTER_DNS_SERVER_ADDRESS DnsServer = FirstDnsServer;\n    while ((DnsServer != nullptr) && (DnsServerStrings.size() < MaxValues))\n    {\n        PVOID IpAddress = nullptr;\n        const USHORT IpFamily = DnsServer->Address.lpSockaddr->sa_family;\n        if (IpFamily == AF_INET)\n        {\n            IpAddress = &((sockaddr_in*)DnsServer->Address.lpSockaddr)->sin_addr;\n        }\n        else if (IpFamily == AF_INET6)\n        {\n            IpAddress = &((sockaddr_in6*)DnsServer->Address.lpSockaddr)->sin6_addr;\n        }\n\n        DnsServer = DnsServer->Next;\n        if (IpFamily != IpFamilyFilter)\n        {\n            continue;\n        }\n\n        THROW_LAST_ERROR_IF_MSG(\n            (InetNtopA(IpFamily, IpAddress, IpBuffer, ARRAYSIZE(IpBuffer)) == NULL), \"Failed to convert IP address\");\n\n        DnsServerStrings.push_back(IpBuffer);\n    }\n\n    return DnsServerStrings;\n}\n\nstd::vector<std::string> wsl::core::networking::HostDnsInfo::GetInterfaceDnsServers(const std::vector<IpAdapterAddress>& AdapterAddresses, _In_ DnsSettingsFlags Flags)\n{\n    std::vector<std::string> DnsServers;\n\n    constexpr size_t MaxResolvConfDnsServers = 3;\n    for (const IpAdapterAddress& NextAddress : AdapterAddresses)\n    {\n        WI_ASSERT(DnsServers.size() < MaxResolvConfDnsServers);\n\n        USHORT MaxDnsServers = static_cast<USHORT>(MaxResolvConfDnsServers - DnsServers.size());\n\n        // Include only primary DNS VPN server.\n        if ((MaxDnsServers > 1) && (wsl::core::networking::IsInterfaceTypeVpn(NextAddress->IfType)))\n        {\n            MaxDnsServers = 1;\n        }\n\n        // Add DNS nameservers from the interface, with the IPv4 addresses first.\n        std::vector<std::string> ipv4Servers = GetDnsServerStrings(NextAddress->FirstDnsServerAddress, AF_INET, MaxDnsServers);\n        DnsServers.insert(DnsServers.end(), ipv4Servers.begin(), ipv4Servers.end());\n\n        WI_ASSERT(DnsServers.size() <= MaxResolvConfDnsServers);\n\n        if (WI_IsFlagSet(Flags, DnsSettingsFlags::IncludeIpv6Servers))\n        {\n            std::vector<std::string> ipv6Servers = GetDnsServerStrings(\n                NextAddress->FirstDnsServerAddress, AF_INET6, static_cast<USHORT>(MaxResolvConfDnsServers - DnsServers.size()));\n\n            DnsServers.insert(DnsServers.end(), ipv6Servers.begin(), ipv6Servers.end());\n        }\n\n        WI_ASSERT(DnsServers.size() <= MaxResolvConfDnsServers);\n\n        // Only the first three nameserver entries are used.\n        if (DnsServers.size() >= MaxResolvConfDnsServers)\n        {\n            break;\n        }\n    }\n\n    return DnsServers;\n}\n\nstd::vector<std::string> wsl::core::networking::HostDnsInfo::GetInterfaceDnsSuffixes(const std::vector<IpAdapterAddress>& AdapterAddresses)\n{\n    std::vector<std::string> DnsSuffixes;\n    std::set<std::wstring> UniqueDnsSuffixes;\n\n    PIP_ADAPTER_DNS_SUFFIX DnsSuffix;\n\n    auto AppendSuffix = [&](const std::wstring& NewSuffix) {\n        if (NewSuffix.empty())\n        {\n            return;\n        }\n\n        if (std::ranges::find_if(UniqueDnsSuffixes, [&](const std::wstring& Suffix) {\n                return wsl::shared::string::IsEqual(Suffix, NewSuffix, true);\n            }) == UniqueDnsSuffixes.end())\n        {\n            DnsSuffixes.emplace_back(wsl::shared::string::WideToMultiByte(NewSuffix));\n            UniqueDnsSuffixes.insert(NewSuffix);\n        }\n    };\n\n    for (const IpAdapterAddress& NextAddress : AdapterAddresses)\n    {\n        // Add any domain suffix information from the interface.\n        if ((NextAddress->DnsSuffix != nullptr) && (wcslen(NextAddress->DnsSuffix) > 0))\n        {\n            AppendSuffix(NextAddress->DnsSuffix);\n        }\n\n        DnsSuffix = NextAddress->FirstDnsSuffix;\n        while (DnsSuffix != nullptr)\n        {\n            AppendSuffix(DnsSuffix->String);\n\n            DnsSuffix = DnsSuffix->Next;\n        }\n    }\n\n    return DnsSuffixes;\n}\n\nwsl::core::networking::DnsInfo wsl::core::networking::HostDnsInfo::GetDnsSettings(_In_ DnsSettingsFlags Flags)\n{\n    std::vector<IpAdapterAddress> Addresses = AdapterAddresses::GetCurrent();\n\n    auto RemoveFilter = [&](const IpAdapterAddress& Address) {\n        // Ignore interfaces that are not currently \"up\".\n        // Ignore loopback and tunneling interfaces.\n        // Ignore interfaces that have no IP address or no DNS addresses.\n        // Ignore hidden interfaces\n        if ((Address->OperStatus != IfOperStatusUp) || (Address->IfType == IF_TYPE_SOFTWARE_LOOPBACK) || (Address->IfType == IF_TYPE_TUNNEL) ||\n            (!WI_IsFlagSet(Flags, DnsSettingsFlags::IncludeVpn) && wsl::core::networking::IsInterfaceTypeVpn(Address->IfType)) ||\n            (Address->FirstUnicastAddress == nullptr) || (Address->FirstDnsServerAddress == nullptr) || IsInterfaceHidden(Address->IfIndex))\n        {\n            return true;\n        }\n        return false;\n    };\n\n    std::erase_if(Addresses, RemoveFilter);\n\n    // Find the recommended internet interface if one exists.\n    // First try VPN interface, then regular IPv4 and then IPv6.\n    const auto BestInterface = GetBestInterface();\n\n    // Sort the remaining network interfaces, with the most preferable at index 0.\n    std::sort(Addresses.begin(), Addresses.end(), [&](const IpAdapterAddress& First, const IpAdapterAddress& Second) {\n        // VPN interface takes precedence.\n        const bool FirstIsVpn = wsl::core::networking::IsInterfaceTypeVpn(First->IfType);\n        const bool SecondIsVpn = wsl::core::networking::IsInterfaceTypeVpn(Second->IfType);\n        if (FirstIsVpn ^ SecondIsVpn)\n        {\n            // Give precedence to the first VPN interface.\n            return FirstIsVpn;\n        }\n\n        // The identified 'best' internet connection interface should go right\n        // after VPN. Or if both networking interfaces are VPN interfaces,\n        // give preference to the one considered 'best'.\n        if (First->IfIndex == BestInterface)\n        {\n            return true;\n        }\n        if (Second->IfIndex == BestInterface)\n        {\n            return false;\n        }\n\n        // Check the first interface for IPv4 DNS servers.\n        bool FirstHasIpv4 = false;\n        auto DnsServer = First->FirstDnsServerAddress;\n        while (DnsServer != nullptr)\n        {\n            const USHORT IpFamily = DnsServer->Address.lpSockaddr->sa_family;\n            DnsServer = DnsServer->Next;\n            if (IpFamily == AF_INET)\n            {\n                FirstHasIpv4 = true;\n                break;\n            }\n        }\n\n        // Check the second interface for IPv4 DNS servers.\n        bool SecondHasIpv4 = false;\n        DnsServer = Second->FirstDnsServerAddress;\n        while (DnsServer != nullptr)\n        {\n            const USHORT IpFamily = DnsServer->Address.lpSockaddr->sa_family;\n            DnsServer = DnsServer->Next;\n            if (IpFamily == AF_INET)\n            {\n                SecondHasIpv4 = true;\n                break;\n            }\n        }\n\n        // Give precedence to interfaces that have IPv4 DNS servers; otherwise, give precedence to the lower interface index.\n        return (FirstHasIpv4 ^ SecondHasIpv4) ? FirstHasIpv4 : (First->IfIndex < Second->IfIndex);\n    });\n\n    DnsInfo DnsSettings{};\n\n    DnsSettings.Servers = GetInterfaceDnsServers(Addresses, Flags);\n\n    if (WI_IsFlagSet(Flags, DnsSettingsFlags::IncludeAllSuffixes))\n    {\n        DnsSettings.Domains = GetAllDnsSuffixes(Addresses);\n    }\n    else\n    {\n        DnsSettings.Domains = GetInterfaceDnsSuffixes(Addresses);\n    }\n\n    return DnsSettings;\n}\n\nstd::string wsl::core::networking::GenerateResolvConf(_In_ const DnsInfo& Info)\n{\n    std::string contents{};\n    if (!Info.Servers.empty())\n    {\n        // Add IP addresses of the DNS name servers.\n        for (const std::string& ip : Info.Servers)\n        {\n            contents += \"nameserver \";\n            contents += ip;\n            contents += c_asciiNewLine;\n        }\n\n        // Add DNS suffix information using 'search' directive.\n        // Per resolv.conf(5): \"The domain directive is an obsolete name for the search directive\n        // that handles one search list entry only.\"\n        // See: https://man7.org/linux/man-pages/man5/resolv.conf.5.html\n        if (!Info.Domains.empty())\n        {\n            contents += \"search \";\n            std::for_each(Info.Domains.begin(), (Info.Domains.end() - 1), [&contents](const std::string& NextDomain) {\n                contents += NextDomain;\n                contents += \" \";\n            });\n\n            contents += Info.Domains.back();\n            contents += c_asciiNewLine;\n        }\n    }\n\n    WSL_LOG(\"wsl::core::networking::GenerateResolvConf\", TraceLoggingValue(contents.c_str(), \"resolvConf\"));\n\n    return contents;\n}\n\nstd::vector<std::string> wsl::core::networking::GetAllDnsSuffixes(const std::vector<IpAdapterAddress>& AdapterAddresses)\n{\n    const auto com = wil::CoInitializeEx();\n    wsl::core::WmiService service(L\"ROOT\\\\StandardCimv2\");\n\n    // DNS suffixes will be configured in Linux in the following order, *similar* (not 100% the same) to the order in which Windows tries suffixes.\n    //\n    // 1) Global suffixes (can be configured manually or via group policy) - queried using WMI call equivalent with Get-DnsClientGlobalSetting\n    // 2) Supplemental search list, queried using GetInterfaceDnsSettings()\n    // 3) Per-interface suffixes, queried using WMI call equivalent with Get-DnsClient\n    std::vector<std::string> dnsSuffixes;\n    std::set<std::wstring> uniqueDnsSuffixes;\n\n    auto AppendSuffix = [&](const std::wstring& newSuffix) {\n        if (newSuffix.empty())\n        {\n            return;\n        }\n\n        if (std::ranges::find_if(uniqueDnsSuffixes, [&](const std::wstring& suffix) {\n                return wsl::shared::string::IsEqual(suffix, newSuffix, true);\n            }) == uniqueDnsSuffixes.end())\n        {\n            dnsSuffixes.emplace_back(wsl::shared::string::WideToMultiByte(newSuffix));\n            uniqueDnsSuffixes.insert(newSuffix);\n        }\n    };\n\n    // 1) Query global suffixes\n    wsl::core::WmiEnumerate enumDnsClientGlobalSetting(service);\n\n    for (const auto& instance : enumDnsClientGlobalSetting.query(L\"SELECT * FROM MSFT_DnsClientGlobalSetting\"))\n    {\n        std::vector<std::wstring> suffixSearchList;\n\n        instance.get(c_suffixSearchList, &suffixSearchList);\n\n        for (const auto& suffix : suffixSearchList)\n        {\n            AppendSuffix(suffix);\n        }\n    }\n\n    // 2) Query supplemental search list. Skip this step if the OS does not support the required APIs\n    if (LoadIpHelperMethods())\n    {\n        for (const auto& address : AdapterAddresses)\n        {\n            if (IsInterfaceHidden(address->IfIndex))\n            {\n                continue;\n            }\n\n            GUID interfaceGuid{};\n            if (FAILED_WIN32_LOG(ConvertInterfaceLuidToGuid(&address->Luid, &interfaceGuid)))\n            {\n                continue;\n            }\n\n            DNS_INTERFACE_SETTINGS_EX settings{};\n            settings.SettingsV1.Version = DNS_INTERFACE_SETTINGS_VERSION2;\n            settings.SettingsV1.Flags = DNS_SETTING_SUPPLEMENTAL_SEARCH_LIST;\n\n            if (FAILED_WIN32_LOG(g_getInterfaceDnsSettings.value()(interfaceGuid, reinterpret_cast<DNS_INTERFACE_SETTINGS*>(&settings))))\n            {\n                continue;\n            }\n\n            const auto freeSettings =\n                wil::scope_exit([&] { g_freeInterfaceDnsSettings.value()(reinterpret_cast<DNS_INTERFACE_SETTINGS*>(&settings)); });\n\n            if (settings.SupplementalSearchList != nullptr)\n            {\n                // The suffix list can be delimited by comma, space, tab\n                std::wstring separators = L\", \\t\";\n\n                for (const auto& suffix :\n                     wsl::shared::string::SplitByMultipleSeparators(std::wstring{settings.SupplementalSearchList}, separators))\n                {\n                    AppendSuffix(suffix);\n                }\n            }\n        }\n    }\n\n    // 3) Query per-interface suffixes\n    wsl::core::WmiEnumerate enumDnsClient(service);\n\n    for (const auto& instance : enumDnsClient.query(L\"SELECT * FROM MSFT_DnsClient\"))\n    {\n        std::wstring connectionSuffix;\n        std::vector<std::wstring> connectionSuffixSearchList;\n        IF_INDEX interfaceIndex{};\n\n        instance.get(c_interfaceIndex, &interfaceIndex);\n        if (IsInterfaceHidden(interfaceIndex))\n        {\n            continue;\n        }\n\n        instance.get(c_connectionSpecificSuffix, &connectionSuffix);\n        instance.get(c_connectionSpecificSuffixSearchList, &connectionSuffixSearchList);\n\n        AppendSuffix(connectionSuffix);\n\n        for (const auto& suffix : connectionSuffixSearchList)\n        {\n            AppendSuffix(suffix);\n        }\n    }\n\n    return dnsSuffixes;\n}\n\nwsl::core::networking::DnsSuffixRegistryWatcher::DnsSuffixRegistryWatcher(RegistryChangeCallback&& reportRegistryChange) :\n    m_reportRegistryChange(std::move(reportRegistryChange))\n{\n    std::vector<wistd::unique_ptr<wsl::windows::common::slim_registry_watcher>> localRegistryWatchers;\n\n    for (const auto& path : c_dnsSuffixesRegistryPaths)\n    {\n        auto watcher = wil::make_unique_nothrow<wsl::windows::common::slim_registry_watcher>();\n        THROW_HR_IF(E_OUTOFMEMORY, !watcher);\n\n        THROW_IF_FAILED(watcher->create(HKEY_LOCAL_MACHINE, path.registryPath, path.isRecursive, [this](wil::RegistryChangeKind changeKind) {\n            m_reportRegistryChange();\n        }));\n        localRegistryWatchers.emplace_back(std::move(watcher));\n    }\n\n    m_registryWatchers.swap(localRegistryWatchers);\n}\n\nwsl::shared::hns::DNS wsl::core::networking::BuildDnsNotification(const DnsInfo& settings, PCWSTR options)\n{\n    wsl::shared::hns::DNS dnsNotification{};\n    if (options)\n    {\n        dnsNotification.Options = options;\n    }\n\n    dnsNotification.ServerList = wsl::shared::string::MultiByteToWide(wsl::shared::string::Join(settings.Servers, ','));\n\n    // Use 'search' entry for DNS suffix list.\n    // Per resolv.conf(5): \"The domain directive is an obsolete name for the search directive\n    // that handles one search list entry only.\"\n    // See: https://man7.org/linux/man-pages/man5/resolv.conf.5.html\n    dnsNotification.Search = wsl::shared::string::MultiByteToWide(wsl::shared::string::Join(settings.Domains, ','));\n\n    return dnsNotification;\n}\n"
  },
  {
    "path": "src/windows/common/WslCoreHostDnsInfo.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n#include <string>\r\n#include <vector>\r\n\r\n#include <iptypes.h>\r\n#include <wil/registry.h>\r\n\r\n#include \"WslCoreNetworkingSupport.h\"\r\n#include \"RegistryWatcher.h\"\r\n\r\nnamespace wsl::core::networking {\r\nstruct DnsInfo\r\n{\r\n    std::vector<std::string> Servers;\r\n    std::vector<std::string> Domains;\r\n};\r\n\r\nenum class DnsSettingsFlags\r\n{\r\n    None = 0x0,\r\n    IncludeVpn = 0x1,\r\n    IncludeIpv6Servers = 0x2,\r\n    IncludeAllSuffixes = 0x4\r\n};\r\nDEFINE_ENUM_FLAG_OPERATORS(DnsSettingsFlags);\r\n\r\ninline bool operator==(const DnsInfo& lhs, const DnsInfo& rhs) noexcept\r\n{\r\n    return lhs.Servers == rhs.Servers && lhs.Domains == rhs.Domains;\r\n}\r\ninline bool operator!=(const DnsInfo& lhs, const DnsInfo& rhs) noexcept\r\n{\r\n    return !(lhs == rhs);\r\n}\r\n\r\nstd::string GenerateResolvConf(_In_ const DnsInfo& Info);\r\n\r\n/// <summary>\r\n/// Builds an hns::DNS notification from DnsInfo settings.\r\n/// </summary>\r\n/// <param name=\"settings\">The DNS settings to convert</param>\r\n/// <param name=\"options\">The resolv.conf header options (defaults to LX_INIT_RESOLVCONF_FULL_HEADER)</param>\r\n/// <returns>The hns::DNS notification ready to send via GNS channel</returns>\r\nwsl::shared::hns::DNS BuildDnsNotification(const DnsInfo& settings, PCWSTR options = LX_INIT_RESOLVCONF_FULL_HEADER);\r\n\r\nstd::vector<std::string> GetAllDnsSuffixes(const std::vector<IpAdapterAddress>& AdapterAddresses);\r\n\r\nDWORD GetBestInterface();\r\n\r\nclass HostDnsInfo\r\n{\r\npublic:\r\n    static DnsInfo GetDnsSettings(_In_ DnsSettingsFlags Flags);\r\n\r\n    static DnsInfo GetDnsTunnelingSettings(const std::wstring& dnsTunnelingNameserver);\r\n\r\nprivate:\r\n    /// <summary>\r\n    /// Internal function to retrieve interface DNS servers.\r\n    /// </summary>\r\n    static std::vector<std::string> GetInterfaceDnsServers(const std::vector<IpAdapterAddress>& AdapterAddresses, _In_ DnsSettingsFlags Flags);\r\n\r\n    /// <summary>\r\n    /// Internal function to retrieve all Windows DNS suffixes.\r\n    /// </summary>\r\n    static std::vector<std::string> GetInterfaceDnsSuffixes(const std::vector<IpAdapterAddress>& AdapterAddresses);\r\n\r\n    /// <summary>\r\n    /// Internal function to convert DNS server addresses into strings.\r\n    /// </summary>\r\n    static std::vector<std::string> GetDnsServerStrings(_In_ const PIP_ADAPTER_DNS_SERVER_ADDRESS& DnsServer, _In_ USHORT IpFamilyFilter, _In_ USHORT MaxValues);\r\n};\r\n\r\nusing RegistryChangeCallback = std::function<void()>;\r\n\r\n/// <summary>\r\n/// Class used to get notifications when Windows DNS suffixes are updated in registry.\r\n/// </summary>\r\nclass DnsSuffixRegistryWatcher\r\n{\r\npublic:\r\n    DnsSuffixRegistryWatcher(RegistryChangeCallback&& reportRegistryChange);\r\n    ~DnsSuffixRegistryWatcher() noexcept = default;\r\n\r\nprivate:\r\n    RegistryChangeCallback m_reportRegistryChange;\r\n\r\n    std::vector<wistd::unique_ptr<wsl::windows::common::slim_registry_watcher>> m_registryWatchers;\r\n};\r\n\r\n} // namespace wsl::core::networking"
  },
  {
    "path": "src/windows/common/WslCoreMessageQueue.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    WslCoreMessageQueue.h\r\n\r\nAbstract:\r\n\r\n    This file contains a queuing implementation, guaranteeing running function objects\r\n    with guaranteed serialization in a threadpool thread\r\n\r\n--*/\r\n\r\n#pragma once\r\n#include <deque>\r\n#include <functional>\r\n#include <memory>\r\n#include <variant>\r\n#include <windows.h>\r\n#include <wil/resource.h>\r\n\r\nnamespace wsl::core {\r\n// forward-declare classes that can instantiate a WslThreadPoolWaitableResult object\r\nclass WslCoreMessageQueue;\r\n\r\nclass WslBaseThreadPoolWaitableResult\r\n{\r\npublic:\r\n    virtual ~WslBaseThreadPoolWaitableResult() noexcept = default;\r\n\r\nprivate:\r\n    // limit who can run() and abort()\r\n    friend class WslCoreMessageQueue;\r\n\r\n    virtual void run() noexcept = 0;\r\n    virtual void abort() noexcept = 0;\r\n};\r\n\r\ntemplate <typename TReturn>\r\nclass WslThreadPoolWaitableResult : public WslBaseThreadPoolWaitableResult\r\n{\r\npublic:\r\n    // throws a wil exception on failure\r\n    template <typename FunctorType>\r\n    explicit WslThreadPoolWaitableResult(FunctorType&& functor) : m_function(std::forward<FunctorType>(functor))\r\n    {\r\n    }\r\n\r\n    ~WslThreadPoolWaitableResult() noexcept override = default;\r\n\r\n    // returns ERROR_SUCCESS if the callback ran to completion\r\n    // returns ERROR_TIMEOUT if this wait timed out\r\n    // - this can be called multiple times if needing to probe\r\n    // any other error code resulted from attempting to run the callback\r\n    // - meaning it did *not* run to completion\r\n    DWORD wait(DWORD timeout) const noexcept\r\n    {\r\n        if (!m_completionSignal.wait(timeout))\r\n        {\r\n            // not setting m_internalError to timeout\r\n            // since the caller is allowed to try to wait() again later\r\n            return ERROR_TIMEOUT;\r\n        }\r\n        const auto lock = m_lock.lock_shared();\r\n        return m_internalError;\r\n    }\r\n\r\n    // waitable event handle, signaled when the callback has run to completion (or failed)\r\n    HANDLE notification_event() const noexcept\r\n    {\r\n        return m_completionSignal.get();\r\n    }\r\n\r\n    const TReturn& read_result() const noexcept\r\n    {\r\n        return result;\r\n    }\r\n\r\n    // move the result out of the object for move-only types\r\n    TReturn move_result() noexcept\r\n    {\r\n        TReturn move_out(std::move(result));\r\n        return move_out;\r\n    }\r\n\r\n    // non-copyable\r\n    WslThreadPoolWaitableResult(const WslThreadPoolWaitableResult&) = delete;\r\n    WslThreadPoolWaitableResult& operator=(const WslThreadPoolWaitableResult&) = delete;\r\n\r\nprivate:\r\n    void run() noexcept override\r\n    {\r\n        // we are now running in the TP callback\r\n        {\r\n            const auto lock = m_lock.lock_exclusive();\r\n            if (m_runStatus != RunStatus::NotYetRun)\r\n            {\r\n                // return early - the caller has already canceled this\r\n                return;\r\n            }\r\n            m_runStatus = RunStatus::Running;\r\n        }\r\n\r\n        DWORD error = NO_ERROR;\r\n        try\r\n        {\r\n            result = std::move(m_function());\r\n        }\r\n        catch (...)\r\n        {\r\n            const HRESULT hr = wil::ResultFromCaughtException();\r\n            // HRESULT_TO_WIN32\r\n            error = (HRESULT_FACILITY(hr) == FACILITY_WIN32) ? HRESULT_CODE(hr) : hr;\r\n        }\r\n\r\n        const auto lock = m_lock.lock_exclusive();\r\n        WI_ASSERT(m_runStatus == RunStatus::Running);\r\n        m_runStatus = RunStatus::RanToCompletion;\r\n        m_internalError = error;\r\n        m_completionSignal.SetEvent();\r\n    }\r\n\r\n    void abort() noexcept override\r\n    {\r\n        const auto lock = m_lock.lock_exclusive();\r\n        // only override the error if we know we haven't started running their functor\r\n        if (m_runStatus == RunStatus::NotYetRun)\r\n        {\r\n            m_runStatus = RunStatus::Canceled;\r\n            m_internalError = ERROR_CANCELLED;\r\n            m_completionSignal.SetEvent();\r\n        }\r\n    }\r\n\r\n    std::function<TReturn(void)> m_function;\r\n    // a notification event\r\n    wil::unique_event m_completionSignal{wil::EventOptions::ManualReset};\r\n    mutable wil::srwlock m_lock;\r\n    TReturn result{};\r\n    DWORD m_internalError = NO_ERROR;\r\n\r\n    enum class RunStatus\r\n    {\r\n        NotYetRun,\r\n        Running,\r\n        RanToCompletion,\r\n        Canceled\r\n    } m_runStatus{RunStatus::NotYetRun};\r\n};\r\n\r\nclass WslCoreMessageQueue\r\n{\r\npublic:\r\n    WslCoreMessageQueue() : m_tpEnvironment(0, 1)\r\n    {\r\n        // create a single-threaded threadpool\r\n        m_tpHandle = m_tpEnvironment.create_tp(WorkCallback, this);\r\n    }\r\n\r\n    template <typename TReturn, typename FunctorType>\r\n    std::shared_ptr<WslThreadPoolWaitableResult<TReturn>> submit_with_results(FunctorType&& functor) noexcept\r\n    try\r\n    {\r\n        FAIL_FAST_IF(m_tpHandle.get() == nullptr);\r\n\r\n        const auto new_result = std::make_shared<WslThreadPoolWaitableResult<TReturn>>(std::forward<FunctorType>(functor));\r\n        // scope to the queue lock\r\n        {\r\n            const auto queueLock = m_lock.lock_exclusive();\r\n            THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANCELLED), m_isCanceled);\r\n            m_workItems.emplace_back(new_result);\r\n        }\r\n\r\n        // always maintain a 1:1 ratio for calls to SubmitWorkWithResults() and ::SubmitThreadpoolWork\r\n        SubmitThreadpoolWork(m_tpHandle.get());\r\n        return new_result;\r\n    }\r\n    catch (...)\r\n    {\r\n        LOG_CAUGHT_EXCEPTION();\r\n        return nullptr;\r\n    }\r\n\r\n    template <typename FunctorType>\r\n    bool submit(FunctorType&& functor) noexcept\r\n    try\r\n    {\r\n        FAIL_FAST_IF(m_tpHandle.get() == nullptr);\r\n\r\n        // scope to the queue lock\r\n        {\r\n            const auto queueLock = m_lock.lock_exclusive();\r\n            THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANCELLED), m_isCanceled);\r\n            m_workItems.emplace_back(std::forward<SimpleFunction_t>(functor));\r\n        }\r\n\r\n        // always maintain a 1:1 ratio for calls to SubmitWork() and ::SubmitThreadpoolWork\r\n        SubmitThreadpoolWork(m_tpHandle.get());\r\n        return true;\r\n    }\r\n    catch (...)\r\n    {\r\n        LOG_CAUGHT_EXCEPTION();\r\n        return false;\r\n    }\r\n\r\n    // functors must return type HRESULT\r\n    template <typename FunctorType>\r\n    HRESULT submit_and_wait(FunctorType&& functor) noexcept\r\n    try\r\n    {\r\n        HRESULT hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);\r\n        if (const auto waitableResult = submit_with_results<HRESULT>(std::forward<FunctorType>(functor)))\r\n        {\r\n            hr = HRESULT_FROM_WIN32(waitableResult->wait(INFINITE));\r\n            if (SUCCEEDED(hr))\r\n            {\r\n                hr = waitableResult->read_result();\r\n            }\r\n        }\r\n        return hr;\r\n    }\r\n    CATCH_RETURN()\r\n\r\n    // cancels anything queued to the TP - this WslCoreMessageQueue instance can no longer be used\r\n    void cancel() noexcept\r\n    try\r\n    {\r\n        if (m_tpHandle)\r\n        {\r\n            // immediately release anyone waiting for these workitems not yet run\r\n            {\r\n                const auto queueLock = m_lock.lock_exclusive();\r\n                m_isCanceled = true;\r\n\r\n                for (const auto& work : m_workItems)\r\n                {\r\n                    // signal that these are canceled before we shutdown the TP which they could be scheduled\r\n                    if (const auto* pWaitableWorkitem = std::get_if<WaitableFunction_t>(&work))\r\n                    {\r\n                        (*pWaitableWorkitem)->abort();\r\n                    }\r\n                }\r\n\r\n                m_workItems.clear();\r\n            }\r\n\r\n            // force the m_tpHandle to wait and close the TP\r\n            m_tpHandle.reset();\r\n            m_tpEnvironment.reset();\r\n        }\r\n    }\r\n    CATCH_LOG()\r\n\r\n    bool isRunningInQueue() const noexcept\r\n    {\r\n        const auto currentThreadId = GetThreadId(GetCurrentThread());\r\n        return currentThreadId == static_cast<DWORD>(InterlockedCompareExchange64(&m_threadpoolThreadId, 0ll, 0ll));\r\n    }\r\n\r\n    ~WslCoreMessageQueue() noexcept\r\n    {\r\n        cancel();\r\n    }\r\n\r\n    WslCoreMessageQueue(const WslCoreMessageQueue&) = delete;\r\n    WslCoreMessageQueue& operator=(const WslCoreMessageQueue&) = delete;\r\n    WslCoreMessageQueue(WslCoreMessageQueue&&) = delete;\r\n    WslCoreMessageQueue& operator=(WslCoreMessageQueue&&) = delete;\r\n\r\nprivate:\r\n    struct TPEnvironment\r\n    {\r\n        using unique_tp_env = wil::unique_struct<TP_CALLBACK_ENVIRON, decltype(&DestroyThreadpoolEnvironment), DestroyThreadpoolEnvironment>;\r\n        unique_tp_env m_tpEnvironment;\r\n\r\n        using unique_tp_pool = wil::unique_any<PTP_POOL, decltype(&CloseThreadpool), CloseThreadpool>;\r\n        unique_tp_pool m_threadPool;\r\n\r\n        TPEnvironment(DWORD countMinThread, DWORD countMaxThread)\r\n        {\r\n            InitializeThreadpoolEnvironment(&m_tpEnvironment);\r\n\r\n            m_threadPool.reset(CreateThreadpool(nullptr));\r\n            THROW_LAST_ERROR_IF_NULL(m_threadPool.get());\r\n\r\n            // Set min and max thread counts for custom thread pool\r\n            THROW_LAST_ERROR_IF(!::SetThreadpoolThreadMinimum(m_threadPool.get(), countMinThread));\r\n            SetThreadpoolThreadMaximum(m_threadPool.get(), countMaxThread);\r\n            SetThreadpoolCallbackPool(&m_tpEnvironment, m_threadPool.get());\r\n        }\r\n\r\n        wil::unique_threadpool_work create_tp(PTP_WORK_CALLBACK callback, void* pv)\r\n        {\r\n            wil::unique_threadpool_work newThreadpool(CreateThreadpoolWork(callback, pv, (m_threadPool) ? &m_tpEnvironment : nullptr));\r\n            THROW_LAST_ERROR_IF_NULL(newThreadpool.get());\r\n            return newThreadpool;\r\n        }\r\n\r\n        void reset()\r\n        {\r\n            m_threadPool.reset();\r\n            m_tpEnvironment.reset();\r\n        }\r\n    };\r\n\r\n    using SimpleFunction_t = std::function<void()>;\r\n    using WaitableFunction_t = std::shared_ptr<WslBaseThreadPoolWaitableResult>;\r\n    using FunctionVariant_t = std::variant<SimpleFunction_t, WaitableFunction_t>;\r\n\r\n    // the lock must be destroyed *after* the TP object (thus must be declared first)\r\n    // since the lock is used in the TP callback\r\n    // the lock is mutable to allow us to acquire the lock in const methods\r\n    mutable wil::srwlock m_lock;\r\n    TPEnvironment m_tpEnvironment;\r\n    wil::unique_threadpool_work m_tpHandle;\r\n    std::deque<FunctionVariant_t> m_workItems;\r\n    mutable LONG64 m_threadpoolThreadId{0}; // useful for callers to assert they are running within the queue\r\n    bool m_isCanceled{false};\r\n\r\n    static void CALLBACK WorkCallback(PTP_CALLBACK_INSTANCE, void* Context, PTP_WORK) noexcept\r\n    try\r\n    {\r\n        auto* pThis = static_cast<WslCoreMessageQueue*>(Context);\r\n\r\n        FunctionVariant_t work;\r\n        {\r\n            const auto queueLock = pThis->m_lock.lock_exclusive();\r\n\r\n            if (pThis->m_workItems.empty())\r\n            {\r\n                // pThis object is being destroyed and the queue was cleared\r\n                return;\r\n            }\r\n\r\n            std::swap(work, pThis->m_workItems.front());\r\n            pThis->m_workItems.pop_front();\r\n\r\n            InterlockedExchange64(&pThis->m_threadpoolThreadId, GetThreadId(GetCurrentThread()));\r\n        }\r\n\r\n        // run the tasks outside the WslCoreMessageQueue lock\r\n        const auto resetThreadIdOnExit = wil::scope_exit([pThis] { InterlockedExchange64(&pThis->m_threadpoolThreadId, 0ll); });\r\n        if (work.index() == 0)\r\n        {\r\n            const auto& workItem = std::get<SimpleFunction_t>(work);\r\n            workItem();\r\n        }\r\n        else\r\n        {\r\n            const auto& waitableWorkItem = std::get<WaitableFunction_t>(work);\r\n            waitableWorkItem->run();\r\n        }\r\n    }\r\n    CATCH_LOG()\r\n};\r\n} // namespace wsl::core\r\n"
  },
  {
    "path": "src/windows/common/WslCoreNetworkEndpointSettings.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include \"precomp.h\"\r\n#include \"hns_schema.h\"\r\n#include \"WslCoreNetworkEndpointSettings.h\"\r\n#include \"WslCoreHostDnsInfo.h\"\r\n\r\nusing namespace wsl::shared;\r\n\r\nstd::shared_ptr<wsl::core::networking::NetworkSettings> wsl::core::networking::GetEndpointSettings(const hns::HNSEndpoint& properties)\r\n{\r\n    EndpointIpAddress address{};\r\n    address.Address = windows::common::string::StringToSockAddrInet(properties.IPAddress);\r\n    address.AddressString = properties.IPAddress;\r\n    address.PrefixLength = properties.PrefixLength;\r\n\r\n    EndpointRoute route{};\r\n    route.DestinationPrefix.PrefixLength = 0;\r\n    IN4ADDR_SETANY(&route.DestinationPrefix.Prefix.Ipv4);\r\n    route.DestinationPrefixString = LX_INIT_UNSPECIFIED_ADDRESS;\r\n    route.NextHop = windows::common::string::StringToSockAddrInet(properties.GatewayAddress);\r\n    route.NextHopString = properties.GatewayAddress;\r\n\r\n    return std::make_shared<wsl::core::networking::NetworkSettings>(\r\n        properties.InterfaceConstraint.InterfaceGuid,\r\n        address,\r\n        EndpointIpAddress{},\r\n        route,\r\n        EndpointRoute{},\r\n        properties.MacAddress,\r\n        properties.InterfaceConstraint.InterfaceIndex,\r\n        properties.InterfaceConstraint.InterfaceMediaType);\r\n}\r\n\r\nstd::shared_ptr<wsl::core::networking::NetworkSettings> wsl::core::networking::GetHostEndpointSettings()\r\n{\r\n    auto addresses = AdapterAddresses::GetCurrent();\r\n    auto bestIndex = GetBestInterface();\r\n    auto bestInterfacePtr =\r\n        std::find_if(addresses.cbegin(), addresses.cend(), [&](const auto& address) { return address->IfIndex == bestIndex; });\r\n    if (bestInterfacePtr == addresses.end())\r\n    {\r\n        return std::make_shared<NetworkSettings>();\r\n    }\r\n\r\n    const auto& bestInterface = *bestInterfacePtr;\r\n\r\n    std::wstring macAddress = wsl::shared::string::FormatMacAddress(\r\n        wsl::shared::string::MacAddress{\r\n            bestInterface->PhysicalAddress[0],\r\n            bestInterface->PhysicalAddress[1],\r\n            bestInterface->PhysicalAddress[2],\r\n            bestInterface->PhysicalAddress[3],\r\n            bestInterface->PhysicalAddress[4],\r\n            bestInterface->PhysicalAddress[5]},\r\n        L'-');\r\n\r\n    EndpointIpAddress address{};\r\n    auto firstIpv4Address = bestInterface->FirstUnicastAddress;\r\n    while (firstIpv4Address && firstIpv4Address->Address.lpSockaddr->sa_family != AF_INET)\r\n    {\r\n        firstIpv4Address = firstIpv4Address->Next;\r\n    }\r\n    if (firstIpv4Address)\r\n    {\r\n        address.Address.Ipv4 = *reinterpret_cast<SOCKADDR_IN*>(firstIpv4Address->Address.lpSockaddr);\r\n        address.AddressString = windows::common::string::SockAddrInetToWstring(address.Address);\r\n        address.PrefixLength = firstIpv4Address->OnLinkPrefixLength;\r\n    }\r\n\r\n    // Find the first global-scope (non-link-local) IPv6 unicast address.\r\n    EndpointIpAddress ipv6Address{};\r\n    auto nextUnicastAddress = bestInterface->FirstUnicastAddress;\r\n    while (nextUnicastAddress)\r\n    {\r\n        if (nextUnicastAddress->Address.lpSockaddr->sa_family == AF_INET6)\r\n        {\r\n            const auto& sin6 = *reinterpret_cast<SOCKADDR_IN6*>(nextUnicastAddress->Address.lpSockaddr);\r\n            if (!IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) && !IN6_IS_ADDR_LOOPBACK(&sin6.sin6_addr))\r\n            {\r\n                ipv6Address.Address.Ipv6 = sin6;\r\n                ipv6Address.AddressString = windows::common::string::SockAddrInetToWstring(ipv6Address.Address);\r\n                ipv6Address.PrefixLength = nextUnicastAddress->OnLinkPrefixLength;\r\n                break;\r\n            }\r\n        }\r\n        nextUnicastAddress = nextUnicastAddress->Next;\r\n    }\r\n\r\n    // Helper to find the first gateway address of a given family.\r\n    auto findGatewayAddress = [](PIP_ADAPTER_GATEWAY_ADDRESS list, ADDRESS_FAMILY family) -> PIP_ADAPTER_GATEWAY_ADDRESS {\r\n        while (list && list->Address.lpSockaddr->sa_family != family)\r\n        {\r\n            list = list->Next;\r\n        }\r\n        return list;\r\n    };\r\n\r\n    // Build IPv4 default route.\r\n    EndpointRoute route{};\r\n    const auto v4Gateway = findGatewayAddress(bestInterface->FirstGatewayAddress, AF_INET);\r\n    if (v4Gateway)\r\n    {\r\n        SOCKADDR_INET v4NextHop{};\r\n        v4NextHop.Ipv4 = *reinterpret_cast<SOCKADDR_IN*>(v4Gateway->Address.lpSockaddr);\r\n        route = EndpointRoute::DefaultRoute(AF_INET, v4NextHop);\r\n    }\r\n    else if (address.Address.si_family == AF_INET)\r\n    {\r\n        // Synthesize a gateway from the first host address in the subnet.\r\n        SOCKADDR_INET gatewayAddr{};\r\n        gatewayAddr.si_family = AF_INET;\r\n        const uint32_t hostAddr = ntohl(address.Address.Ipv4.sin_addr.s_addr);\r\n        const uint32_t mask = (address.PrefixLength == 0) ? 0u : ~((1u << (32u - address.PrefixLength)) - 1u);\r\n        gatewayAddr.Ipv4.sin_addr.s_addr = htonl((hostAddr & mask) | 1u);\r\n        route = EndpointRoute::DefaultRoute(AF_INET, gatewayAddr);\r\n    }\r\n\r\n    // Build IPv6 default route.\r\n    EndpointRoute v6Route{};\r\n    const auto v6Gateway = findGatewayAddress(bestInterface->FirstGatewayAddress, AF_INET6);\r\n    if (v6Gateway)\r\n    {\r\n        SOCKADDR_INET v6NextHop{};\r\n        v6NextHop.Ipv6 = *reinterpret_cast<SOCKADDR_IN6*>(v6Gateway->Address.lpSockaddr);\r\n        v6Route = EndpointRoute::DefaultRoute(AF_INET6, v6NextHop);\r\n    }\r\n\r\n    return std::make_shared<NetworkSettings>(\r\n        bestInterface->NetworkGuid,\r\n        std::move(address),\r\n        std::move(ipv6Address),\r\n        std::move(route),\r\n        std::move(v6Route),\r\n        std::move(macAddress),\r\n        bestInterface->IfIndex,\r\n        bestInterface->IfType);\r\n}\r\n\r\nstd::wstring wsl::core::networking::NetworkSettings::GetBestGatewayMacAddress(ADDRESS_FAMILY addressFamily) const\r\n{\r\n    auto gatewayAddress = GetBestGatewayAddress(addressFamily);\r\n    if (gatewayAddress.si_family != addressFamily)\r\n    {\r\n        return {};\r\n    }\r\n\r\n    MIB_IPNET_ROW2 ipNetRow{};\r\n    ipNetRow.Address = gatewayAddress;\r\n    ipNetRow.InterfaceIndex = InterfaceIndex;\r\n\r\n    const auto result = ResolveIpNetEntry2(&ipNetRow, nullptr);\r\n    if (result != NO_ERROR)\r\n    {\r\n        LOG_HR_MSG(HRESULT_FROM_WIN32(result), \"Failed to resolve gateway MAC address\");\r\n        return {};\r\n    }\r\n\r\n    if (ipNetRow.PhysicalAddressLength != 6)\r\n    {\r\n        return {};\r\n    }\r\n\r\n    return wsl::shared::string::FormatMacAddress(\r\n        wsl::shared::string::MacAddress{\r\n            ipNetRow.PhysicalAddress[0],\r\n            ipNetRow.PhysicalAddress[1],\r\n            ipNetRow.PhysicalAddress[2],\r\n            ipNetRow.PhysicalAddress[3],\r\n            ipNetRow.PhysicalAddress[4],\r\n            ipNetRow.PhysicalAddress[5]},\r\n        L'-');\r\n}\r\n"
  },
  {
    "path": "src/windows/common/WslCoreNetworkEndpointSettings.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n#include <algorithm>\r\n#include <set>\r\n#include <string>\r\n\r\n#include <windows.h>\r\n#include <mstcpip.h>\r\n#include <ws2ipdef.h>\r\n#include <netioapi.h>\r\n\r\n#include \"hcs.hpp\"\r\n#include \"lxinitshared.h\"\r\n#include \"Stringify.h\"\r\n#include \"stringshared.h\"\r\n#include \"WslCoreNetworkingSupport.h\"\r\n#include \"hns_schema.h\"\r\n\r\nnamespace wsl::core::networking {\r\n\r\nconstexpr auto AddEndpointRetryPeriod = std::chrono::milliseconds(100);\r\nconstexpr auto AddEndpointRetryTimeout = std::chrono::seconds(3);\r\nconstexpr auto AddEndpointRetryPredicate = [] {\r\n    // Don't retry if ModifyComputeSystem fails with:\r\n    //     HCN_E_ENDPOINT_NOT_FOUND - indicates that the underlying network object was deleted.\r\n    //     HCN_E_ENDPOINT_ALREADY_ATTACHED - occurs when HNS was restarted before the endpoints were removed.\r\n    //     VM_E_INVALID_STATE - occurs when the VM has been terminated.\r\n    const auto result = wil::ResultFromCaughtException();\r\n    return result != HCN_E_ENDPOINT_NOT_FOUND && result != HCN_E_ENDPOINT_ALREADY_ATTACHED && result != VM_E_INVALID_STATE;\r\n};\r\n\r\nstruct EndpointIpAddress\r\n{\r\n    SOCKADDR_INET Address{};\r\n    std::wstring AddressString{};\r\n    unsigned char PrefixLength = 0;\r\n    unsigned int PrefixOrigin = 0;\r\n    unsigned int SuffixOrigin = 0;\r\n\r\n    // The following field can be changed from a const iterator in SyncIpStateWithLinux - that's why it's marked mutable.\r\n    mutable unsigned int PreferredLifetime = 0;\r\n\r\n    EndpointIpAddress() = default;\r\n    ~EndpointIpAddress() noexcept = default;\r\n\r\n    EndpointIpAddress(EndpointIpAddress&&) = default;\r\n    EndpointIpAddress& operator=(EndpointIpAddress&&) = default;\r\n    EndpointIpAddress(const EndpointIpAddress&) = default;\r\n    EndpointIpAddress& operator=(const EndpointIpAddress&) = default;\r\n\r\n    explicit EndpointIpAddress(const MIB_UNICASTIPADDRESS_ROW& AddressRow) :\r\n        Address(AddressRow.Address),\r\n        AddressString(windows::common::string::SockAddrInetToWstring(AddressRow.Address)),\r\n        PrefixLength(AddressRow.OnLinkPrefixLength),\r\n        PrefixOrigin(AddressRow.PrefixOrigin),\r\n        SuffixOrigin(AddressRow.SuffixOrigin),\r\n        // We treat the preferred lifetime field as effective DAD state - 0 is not preferred, anything else is preferred.\r\n        // We do this for convenience, as we can't directly set the DAD state of an address into the guest, but we\r\n        // we can set an address's preferred lifetime (in Linux, at least).\r\n        PreferredLifetime(AddressRow.DadState == IpDadStatePreferred ? 0xFFFFFFFF : 0)\r\n    {\r\n    }\r\n\r\n    // operator== is deliberately not comparing PreferredLifetime (DAD state) for equality - only the address portion\r\n    bool operator==(const EndpointIpAddress& rhs) const noexcept\r\n    {\r\n        return Address == rhs.Address && PrefixLength == rhs.PrefixLength;\r\n    }\r\n\r\n    bool operator<(const EndpointIpAddress& rhs) const noexcept\r\n    {\r\n        if (Address == rhs.Address)\r\n        {\r\n            return PrefixLength < rhs.PrefixLength;\r\n        }\r\n        return Address < rhs.Address;\r\n    }\r\n\r\n    void Clear() noexcept\r\n    {\r\n        Address = {};\r\n        AddressString.clear();\r\n        PrefixLength = 0;\r\n        PrefixOrigin = 0;\r\n        SuffixOrigin = 0;\r\n    }\r\n\r\n    std::wstring GetPrefix() const\r\n    {\r\n        SOCKADDR_INET address{Address};\r\n        unsigned char* addressPointer{nullptr};\r\n\r\n        if (Address.si_family == AF_INET)\r\n        {\r\n            addressPointer = reinterpret_cast<unsigned char*>(&address.Ipv4.sin_addr);\r\n        }\r\n        else if (Address.si_family == AF_INET6)\r\n        {\r\n            addressPointer = address.Ipv6.sin6_addr.u.Byte;\r\n        }\r\n        else\r\n        {\r\n            return L\"\";\r\n        }\r\n\r\n        constexpr int c_numBitsPerByte = 8;\r\n        for (int i = 0, currPrefixLength = PrefixLength; i < INET_ADDR_LENGTH(Address.si_family); i++, currPrefixLength -= c_numBitsPerByte)\r\n        {\r\n            if (currPrefixLength < c_numBitsPerByte)\r\n            {\r\n                const int bitShiftAmt = c_numBitsPerByte - std::max(currPrefixLength, 0);\r\n                addressPointer[i] &= (0xFF >> bitShiftAmt) << bitShiftAmt;\r\n            }\r\n        }\r\n\r\n        const auto addressString = windows::common::string::SockAddrInetToWstring(address);\r\n        WI_ASSERT(!addressString.empty());\r\n        if (addressString.empty())\r\n        {\r\n            // just return an empty string if we have a bad address\r\n            return addressString;\r\n        }\r\n\r\n        return std::format(L\"{}/{}\", addressString, PrefixLength);\r\n    }\r\n\r\n    std::wstring GetIpv4BroadcastMask() const\r\n    {\r\n        // start with all bits set, then shift off the prefix\r\n        ULONG prefixMask{0xffffffff};\r\n        prefixMask <<= PrefixLength;\r\n        prefixMask >>= PrefixLength;\r\n\r\n        SOCKADDR_INET address{Address};\r\n        // flip to host-order, then apply the mask\r\n        ULONG hostOrder = ntohl(address.Ipv4.sin_addr.S_un.S_addr);\r\n        hostOrder |= prefixMask;\r\n        address.Ipv4.sin_addr.S_un.S_addr = htonl(hostOrder);\r\n\r\n        return windows::common::string::SockAddrInetToWstring(address);\r\n    }\r\n\r\n    bool IsPreferred() const noexcept\r\n    {\r\n        return PreferredLifetime > 0;\r\n    }\r\n\r\n    bool IsLinkLocal() const\r\n    {\r\n        return (Address.si_family == AF_INET && IN4_IS_ADDR_LINKLOCAL(&Address.Ipv4.sin_addr)) ||\r\n               (Address.si_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&Address.Ipv6.sin6_addr));\r\n    }\r\n};\r\n\r\nstruct EndpointRoute\r\n{\r\n    ADDRESS_FAMILY Family = AF_INET;\r\n    IP_ADDRESS_PREFIX DestinationPrefix{};\r\n    std::wstring DestinationPrefixString{};\r\n    SOCKADDR_INET NextHop{};\r\n    std::wstring NextHopString{};\r\n    unsigned char SitePrefixLength = 0;\r\n    unsigned int Metric = 0;\r\n    bool IsAutoGeneratedPrefixRoute = false;\r\n\r\n    EndpointRoute() = default;\r\n    ~EndpointRoute() noexcept = default;\r\n\r\n    EndpointRoute(EndpointRoute&&) = default;\r\n    EndpointRoute& operator=(EndpointRoute&&) = default;\r\n    EndpointRoute(const EndpointRoute&) = default;\r\n    EndpointRoute& operator=(const EndpointRoute&) = default;\r\n\r\n    // Build a default route (0.0.0.0/0 or ::/0) with the given next hop.\r\n    static EndpointRoute DefaultRoute(ADDRESS_FAMILY family, const SOCKADDR_INET& nextHop)\r\n    {\r\n        EndpointRoute route{};\r\n        route.Family = family;\r\n        route.DestinationPrefix.PrefixLength = 0;\r\n        if (family == AF_INET)\r\n        {\r\n            IN4ADDR_SETANY(&route.DestinationPrefix.Prefix.Ipv4);\r\n            route.DestinationPrefixString = LX_INIT_UNSPECIFIED_ADDRESS;\r\n        }\r\n        else\r\n        {\r\n            IN6ADDR_SETANY(&route.DestinationPrefix.Prefix.Ipv6);\r\n            route.DestinationPrefixString = LX_INIT_UNSPECIFIED_V6_ADDRESS;\r\n        }\r\n        route.NextHop = nextHop;\r\n        route.NextHopString = windows::common::string::SockAddrInetToWstring(nextHop);\r\n        return route;\r\n    }\r\n\r\n    EndpointRoute(const MIB_IPFORWARD_ROW2& RouteRow) :\r\n        Family(RouteRow.NextHop.si_family),\r\n        DestinationPrefix(RouteRow.DestinationPrefix),\r\n        DestinationPrefixString(windows::common::string::SockAddrInetToWstring(RouteRow.DestinationPrefix.Prefix)),\r\n        NextHop(RouteRow.NextHop),\r\n        NextHopString(windows::common::string::SockAddrInetToWstring(RouteRow.NextHop)),\r\n        SitePrefixLength(RouteRow.SitePrefixLength),\r\n        Metric(RouteRow.Metric)\r\n    {\r\n    }\r\n\r\n    unsigned char GetMaxPrefixLength() const\r\n    {\r\n        return (Family == AF_INET) ? 32 : 128;\r\n    }\r\n\r\n    std::wstring GetFullDestinationPrefix() const\r\n    {\r\n        return std::format(L\"{}/{}\", DestinationPrefixString, static_cast<unsigned int>(DestinationPrefix.PrefixLength));\r\n    }\r\n\r\n    bool IsNextHopOnlink() const noexcept\r\n    {\r\n        return (Family == AF_INET && NextHopString == LX_INIT_UNSPECIFIED_ADDRESS) ||\r\n               (Family == AF_INET6 && NextHopString == LX_INIT_UNSPECIFIED_V6_ADDRESS);\r\n    }\r\n\r\n    bool IsDefault() const noexcept\r\n    {\r\n        return (Family == AF_INET && DestinationPrefixString == LX_INIT_UNSPECIFIED_ADDRESS) ||\r\n               (Family == AF_INET6 && DestinationPrefixString == LX_INIT_UNSPECIFIED_V6_ADDRESS);\r\n    }\r\n\r\n    bool IsUnicastAddressRoute() const noexcept\r\n    {\r\n        return (Family == AF_INET && DestinationPrefix.PrefixLength == 32) || (Family == AF_INET6 && DestinationPrefix.PrefixLength == 128);\r\n    }\r\n\r\n    std::wstring ToString() const\r\n    {\r\n        return std::format(L\"{}=>{} [metric {}]\", GetFullDestinationPrefix(), NextHopString, Metric);\r\n    }\r\n\r\n    bool operator==(const EndpointRoute& rhs) const noexcept\r\n    {\r\n        return Family == rhs.Family && DestinationPrefix.PrefixLength == rhs.DestinationPrefix.PrefixLength &&\r\n               DestinationPrefix.Prefix == rhs.DestinationPrefix.Prefix && NextHop == rhs.NextHop &&\r\n               SitePrefixLength == rhs.SitePrefixLength && Metric == rhs.Metric;\r\n    }\r\n\r\n    bool operator!=(const EndpointRoute& other) const\r\n    {\r\n        return !(*this == other);\r\n    }\r\n\r\n    // sort by family, then by next-hop (on-link routes first), then by prefix, then by metric\r\n    bool operator<(const EndpointRoute& rhs) const noexcept\r\n    {\r\n        if (Family == rhs.Family)\r\n        {\r\n            if (NextHop == rhs.NextHop)\r\n            {\r\n                if (DestinationPrefix.Prefix == rhs.DestinationPrefix.Prefix)\r\n                {\r\n                    if (DestinationPrefix.PrefixLength == rhs.DestinationPrefix.PrefixLength)\r\n                    {\r\n                        if (Metric == rhs.Metric)\r\n                        {\r\n                            return SitePrefixLength < rhs.SitePrefixLength;\r\n                        }\r\n                        return Metric < rhs.Metric;\r\n                    }\r\n                    return DestinationPrefix.PrefixLength < rhs.DestinationPrefix.PrefixLength;\r\n                }\r\n                return DestinationPrefix.Prefix < rhs.DestinationPrefix.Prefix;\r\n            }\r\n            return NextHop < rhs.NextHop;\r\n        }\r\n        return Family < rhs.Family;\r\n    }\r\n};\r\n\r\nstruct NetworkSettings\r\n{\r\n    NetworkSettings() = default;\r\n\r\n    NetworkSettings(\r\n        const GUID& interfaceGuid,\r\n        EndpointIpAddress preferredIpAddress,\r\n        EndpointIpAddress preferredIpv6Address,\r\n        EndpointRoute gateway,\r\n        EndpointRoute v6Gateway,\r\n        std::wstring macAddress,\r\n        uint32_t interfaceIndex,\r\n        uint32_t mediaType) :\r\n        InterfaceGuid(interfaceGuid),\r\n        PreferredIpAddress(std::move(preferredIpAddress)),\r\n        PreferredIpv6Address(std::move(preferredIpv6Address)),\r\n        MacAddress(std::move(macAddress)),\r\n        InterfaceIndex(interfaceIndex),\r\n        InterfaceType(mediaType)\r\n    {\r\n        // Only insert routes that have a valid next hop. A default-constructed or empty\r\n        // EndpointRoute indicates no gateway was found for that address family.\r\n        if (!gateway.NextHopString.empty())\r\n        {\r\n            Routes.emplace(std::move(gateway));\r\n        }\r\n\r\n        if (!v6Gateway.NextHopString.empty())\r\n        {\r\n            Routes.emplace(std::move(v6Gateway));\r\n        }\r\n    }\r\n\r\n    GUID InterfaceGuid{};\r\n    EndpointIpAddress PreferredIpAddress{};\r\n    EndpointIpAddress PreferredIpv6Address{};\r\n    std::set<EndpointIpAddress> IpAddresses{}; // Does not include PreferredIpAddress or PreferredIpv6Address.\r\n    std::set<EndpointRoute> Routes{};\r\n    std::wstring MacAddress;\r\n    IF_INDEX InterfaceIndex = 0;\r\n    IFTYPE InterfaceType = 0;\r\n    ULONG IPv4InterfaceMtu = 0;\r\n    ULONG IPv6InterfaceMtu = 0;\r\n    // some interfaces will only have an IPv4 or IPv6 interface\r\n    std::optional<ULONG> IPv4InterfaceMetric = 0;\r\n    std::optional<ULONG> IPv6InterfaceMetric = 0;\r\n    bool IsHidden = false;\r\n    bool IsConnected = false;\r\n    bool IsMetered = false;\r\n    bool DisableIpv4DefaultRoutes = false;\r\n    bool DisableIpv6DefaultRoutes = false;\r\n    bool PendingUpdateToReconnectForMetered = false;\r\n    bool PendingIPInterfaceUpdate = false;\r\n\r\n    auto operator<=>(const NetworkSettings&) const = default;\r\n\r\n    // Returns the next-hop string of the first default route matching the given address family.\r\n    std::wstring GetBestGatewayAddressString(ADDRESS_FAMILY family = AF_INET) const\r\n    {\r\n        const auto& unspecified = (family == AF_INET) ? LX_INIT_UNSPECIFIED_ADDRESS : LX_INIT_UNSPECIFIED_V6_ADDRESS;\r\n        for (const auto& route : Routes)\r\n        {\r\n            if (route.Family == family && route.DestinationPrefix.PrefixLength == 0 && route.DestinationPrefixString == unspecified)\r\n            {\r\n                return route.NextHopString;\r\n            }\r\n        }\r\n\r\n        return {};\r\n    }\r\n\r\n    // Returns the next-hop address of the first default route matching the given address family.\r\n    SOCKADDR_INET GetBestGatewayAddress(ADDRESS_FAMILY family = AF_INET) const\r\n    {\r\n        const auto& unspecified = (family == AF_INET) ? LX_INIT_UNSPECIFIED_ADDRESS : LX_INIT_UNSPECIFIED_V6_ADDRESS;\r\n        for (const auto& route : Routes)\r\n        {\r\n            if (route.Family == family && route.DestinationPrefix.PrefixLength == 0 && route.DestinationPrefixString == unspecified)\r\n            {\r\n                return route.NextHop;\r\n            }\r\n        }\r\n\r\n        return {};\r\n    }\r\n\r\n    std::wstring GetBestGatewayMacAddress(ADDRESS_FAMILY addressFamily) const;\r\n\r\n    std::wstring IpAddressesString() const\r\n    {\r\n        return std::accumulate(std::begin(IpAddresses), std::end(IpAddresses), std::wstring{}, [](const std::wstring& prev, const auto& addr) {\r\n            return addr.AddressString + (prev.empty() ? L\"\" : L\",\" + prev);\r\n        });\r\n    }\r\n\r\n    std::wstring RoutesString() const\r\n    {\r\n        return std::accumulate(std::begin(Routes), std::end(Routes), std::wstring{}, [](const std::wstring& prev, const EndpointRoute& route) {\r\n            return route.ToString() + (prev.empty() ? L\"\" : L\",\" + prev);\r\n        });\r\n    }\r\n\r\n    // will return ULONG_MAX if there's no configured MTU\r\n    ULONG GetEffectiveMtu() const noexcept\r\n    {\r\n        return std::min(IPv4InterfaceMtu > 0 ? IPv4InterfaceMtu : ULONG_MAX, IPv6InterfaceMtu > 0 ? IPv6InterfaceMtu : ULONG_MAX);\r\n    }\r\n\r\n    // will return zero if there's no configured metric\r\n    ULONG GetMinimumMetric() const noexcept\r\n    {\r\n        if (!IPv4InterfaceMetric.has_value() && !IPv6InterfaceMetric.has_value())\r\n        {\r\n            return 0;\r\n        }\r\n        if (!IPv4InterfaceMetric.has_value())\r\n        {\r\n            return IPv6InterfaceMetric.value();\r\n        }\r\n        if (!IPv6InterfaceMetric.has_value())\r\n        {\r\n            return IPv4InterfaceMetric.value();\r\n        }\r\n        return std::min(IPv4InterfaceMetric.value(), IPv6InterfaceMetric.value());\r\n    }\r\n};\r\n\r\nstd::shared_ptr<NetworkSettings> GetEndpointSettings(const wsl::shared::hns::HNSEndpoint& properties);\r\nstd::shared_ptr<NetworkSettings> GetHostEndpointSettings();\r\n\r\n#define TRACE_NETWORKSETTINGS_OBJECT(settings) \\\r\n    TraceLoggingValue((settings)->InterfaceGuid, \"interfaceGuid\"), TraceLoggingValue((settings)->InterfaceIndex, \"interfaceIndex\"), \\\r\n        TraceLoggingValue((settings)->InterfaceType, \"interfaceType\"), \\\r\n        TraceLoggingValue((settings)->IsConnected, \"isConnected\"), TraceLoggingValue((settings)->IsMetered, \"isMetered\"), \\\r\n        TraceLoggingValue((settings)->GetBestGatewayAddressString().c_str(), \"bestGatewayAddress\"), \\\r\n        TraceLoggingValue((settings)->PreferredIpAddress.AddressString.c_str(), \"preferredIpAddress\"), \\\r\n        TraceLoggingValue((settings)->PreferredIpAddress.PrefixLength, \"preferredIpAddressPrefixLength\"), \\\r\n        TraceLoggingValue((settings)->PreferredIpv6Address.AddressString.c_str(), \"preferredIpv6Address\"), \\\r\n        TraceLoggingValue((settings)->PreferredIpv6Address.PrefixLength, \"preferredIpv6AddressPrefixLength\"), \\\r\n        TraceLoggingValue((settings)->GetBestGatewayAddressString(AF_INET6).c_str(), \"bestGatewayV6Address\"), \\\r\n        TraceLoggingValue((settings)->IpAddressesString().c_str(), \"ipAddresses\"), \\\r\n        TraceLoggingValue((settings)->RoutesString().c_str(), \"routes\"), \\\r\n        TraceLoggingValue((settings)->MacAddress.c_str(), \"macAddress\"), \\\r\n        TraceLoggingValue((settings)->IPv4InterfaceMtu, \"IPv4InterfaceMtu\"), \\\r\n        TraceLoggingValue((settings)->IPv6InterfaceMtu, \"IPv6InterfaceMtu\"), \\\r\n        TraceLoggingValue((settings)->IPv4InterfaceMetric.value_or(0xffffffff), \"IPv4InterfaceMetric\"), \\\r\n        TraceLoggingValue((settings)->IPv6InterfaceMetric.value_or(0xffffffff), \"IPv6InterfaceMetric\"), \\\r\n        TraceLoggingValue((settings)->PendingIPInterfaceUpdate, \"PendingIPInterfaceUpdate\"), \\\r\n        TraceLoggingValue((settings)->PendingUpdateToReconnectForMetered, \"PendingUpdateToReconnectForMetered\")\r\n\r\n} // namespace wsl::core::networking\r\n"
  },
  {
    "path": "src/windows/common/WslCoreNetworkingSupport.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include \"precomp.h\"\r\n#include \"WslCoreNetworkingSupport.h\"\r\n#include \"Stringify.h\"\r\n\r\nusing wsl::windows::common::Context;\r\nusing wsl::windows::common::ExecutionContext;\r\n\r\n/// <summary>\r\n/// Used for blocked interface telemetry\r\n/// </summary>\r\nenum class InterfaceUnsupportedReason\r\n{\r\n    UnknownInterface = 0,\r\n    NrptDnsRulesDetected,\r\n    InterfaceDetailsQueryFailed,\r\n    NotPhysicalEthernet,\r\n    BlockedRegistryKey\r\n};\r\n\r\nnamespace details {\r\n\r\nstatic bool FindInterfacesForNetworkAdapter(\r\n    const IF_INDEX interfaceIndex, const GUID& interfaceGuid, bool metered, std::vector<wsl::core::networking::CurrentInterfaceInformation>& returnedNetworks)\r\n{\r\n    bool addedNetwork = false;\r\n\r\n    wsl::core::networking::unique_ifstack_table interfaceStackTable{};\r\n    if (FAILED_WIN32_LOG(GetIfStackTable(&interfaceStackTable)))\r\n    {\r\n        return addedNetwork;\r\n    }\r\n\r\n    wsl::core::networking::unique_address_table addressTable{};\r\n    if (FAILED_WIN32_LOG(GetUnicastIpAddressTable(AF_INET, &addressTable)))\r\n    {\r\n        return addedNetwork;\r\n    }\r\n\r\n    // Find the IP interface(s) in the adapter's interface stack.\r\n    std::vector<IF_INDEX> ipInterfaces{};\r\n    std::queue<IF_INDEX> interfaceStack{};\r\n    interfaceStack.push(interfaceIndex);\r\n    while (!interfaceStack.empty())\r\n    {\r\n        IF_INDEX currInterfaceIndex = interfaceStack.front();\r\n        interfaceStack.pop();\r\n\r\n        for (unsigned int i = 0; i < interfaceStackTable.get()->NumEntries; i++)\r\n        {\r\n            if (interfaceStackTable.get()->Table[i].LowerLayerInterfaceIndex == currInterfaceIndex)\r\n            {\r\n                interfaceStack.push(interfaceStackTable.get()->Table[i].HigherLayerInterfaceIndex);\r\n            }\r\n        }\r\n\r\n        MIB_IPINTERFACE_ROW ipInterfaceRow{};\r\n        ipInterfaceRow.Family = AF_INET;\r\n        ipInterfaceRow.InterfaceIndex = currInterfaceIndex;\r\n        if (SUCCEEDED_WIN32(GetIpInterfaceEntry(&ipInterfaceRow)) && ipInterfaceRow.Connected)\r\n        {\r\n            // We found a connected IP interface.  Ensure it has a preferred IP address too.\r\n            for (unsigned int i = 0; i < addressTable.get()->NumEntries; i++)\r\n            {\r\n                if (addressTable.get()->Table[i].InterfaceIndex == currInterfaceIndex && addressTable.get()->Table[i].DadState == IpDadStatePreferred)\r\n                {\r\n                    ipInterfaces.push_back(currInterfaceIndex);\r\n                    break;\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    for (auto currInterfaceIndex : ipInterfaces)\r\n    {\r\n        MIB_IF_ROW2 row{};\r\n        row.InterfaceIndex = currInterfaceIndex;\r\n        if (FAILED_WIN32_LOG(GetIfEntry2Ex(MibIfEntryNormalWithoutStatistics, &row)))\r\n        {\r\n            continue;\r\n        }\r\n\r\n        WSL_LOG(\r\n            \"FindInterfacesForNetworkAdapter : returning connected network profile for IP interface on NIC\",\r\n            TraceLoggingValue(interfaceGuid, \"underlyingInterfaceGuid\"),\r\n            TraceLoggingValue(row.InterfaceGuid, \"interfaceGuid\"),\r\n            TraceLoggingValue(row.Type, \"ifType\"),\r\n            TraceLoggingValue(row.Alias, \"ifAlias\"),\r\n            TraceLoggingValue(row.Description, \"ifDescription\"));\r\n\r\n        returnedNetworks.emplace_back(row.InterfaceGuid, row.InterfaceLuid, row.Type, row.Alias, row.Description, metered);\r\n        addedNetwork = true;\r\n    }\r\n\r\n    return addedNetwork;\r\n}\r\n\r\n} // namespace details\r\n\r\nbool wsl::core::networking::IsMetered(ABI::Windows::Networking::Connectivity::NetworkCostType cost) noexcept\r\n{\r\n    return (cost == ABI::Windows::Networking::Connectivity::NetworkCostType::NetworkCostType_Fixed) ||\r\n           (cost == ABI::Windows::Networking::Connectivity::NetworkCostType::NetworkCostType_Variable);\r\n}\r\n\r\nbool wsl::core::networking::IsFlowSteeringSupportedByHns() noexcept\r\n{\r\n    static bool supported = false;\r\n    static std::once_flag fseMethodsLoadedFlag;\r\n    static constexpr auto c_computeNetworkModuleName = L\"ComputeNetwork.dll\";\r\n    std::call_once(fseMethodsLoadedFlag, [&]() {\r\n        try\r\n        {\r\n            static LxssDynamicFunction<decltype(HcnReserveGuestNetworkServicePortRange)> allocatePortRange{DynamicFunctionErrorLogs::None};\r\n            RETURN_IF_FAILED_EXPECTED(\r\n                allocatePortRange.load(c_computeNetworkModuleName, \"HcnReserveGuestNetworkServicePortRange\"));\r\n\r\n            static LxssDynamicFunction<decltype(HcnReserveGuestNetworkServicePort)> allocatePort{DynamicFunctionErrorLogs::None};\r\n            RETURN_IF_FAILED_EXPECTED(allocatePortRange.load(c_computeNetworkModuleName, \"HcnReserveGuestNetworkServicePort\"));\r\n\r\n            static LxssDynamicFunction<decltype(HcnReleaseGuestNetworkServicePortReservationHandle)> releasePort{DynamicFunctionErrorLogs::None};\r\n            RETURN_IF_FAILED_EXPECTED(\r\n                allocatePortRange.load(c_computeNetworkModuleName, \"HcnReleaseGuestNetworkServicePortReservationHandle\"));\r\n\r\n            supported = true;\r\n        }\r\n        CATCH_LOG()\r\n        return S_OK;\r\n    });\r\n\r\n    if (!supported)\r\n    {\r\n        WSL_LOG(\"IsFlowSteeringSupportedByHns (false) - Port reservation functions are not present\");\r\n    }\r\n    return supported;\r\n}\r\n\r\nstd::vector<wsl::core::networking::CurrentInterfaceInformation> wsl::core::networking::EnumerateConnectedInterfaces()\r\n{\r\n    using ABI::Windows::Foundation::Collections::IVectorView;\r\n    using ABI::Windows::Networking::Connectivity::ConnectionProfile;\r\n    using ABI::Windows::Networking::Connectivity::IConnectionCost;\r\n    using ABI::Windows::Networking::Connectivity::INetworkAdapter;\r\n    using ABI::Windows::Networking::Connectivity::INetworkInformationStatics;\r\n    using ABI::Windows::Networking::Connectivity::NetworkConnectivityLevel;\r\n    using ABI::Windows::Networking::Connectivity::NetworkCostType;\r\n\r\n    std::vector<wsl::core::networking::CurrentInterfaceInformation> returnedNetworks;\r\n    try\r\n    {\r\n        const auto roInit = wil::RoInitialize();\r\n        auto networkInformationStatics =\r\n            wil::GetActivationFactory<INetworkInformationStatics>(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation);\r\n        THROW_HR_IF_NULL_MSG(E_OUTOFMEMORY, networkInformationStatics.get(), \"null INetworkInformationStatics\");\r\n\r\n        wil::com_ptr<IVectorView<ConnectionProfile*>> connectionList;\r\n        THROW_IF_FAILED(networkInformationStatics->GetConnectionProfiles(&connectionList));\r\n\r\n        for (const auto& connectionProfile : wil::get_range(connectionList.get()))\r\n        {\r\n            NetworkConnectivityLevel connectivityLevel{};\r\n            CONTINUE_IF_FAILED(connectionProfile->GetNetworkConnectivityLevel(&connectivityLevel));\r\n            if (connectivityLevel == NetworkConnectivityLevel::NetworkConnectivityLevel_None)\r\n            {\r\n                continue;\r\n            }\r\n\r\n            wil::com_ptr<IConnectionCost> connectionCost;\r\n            CONTINUE_IF_FAILED(connectionProfile->GetConnectionCost(&connectionCost));\r\n\r\n            NetworkCostType cost{};\r\n            CONTINUE_IF_FAILED(connectionCost->get_NetworkCostType(&cost));\r\n            bool metered = IsMetered(cost);\r\n\r\n            wil::com_ptr<INetworkAdapter> networkAdapter;\r\n            CONTINUE_IF_FAILED(connectionProfile->get_NetworkAdapter(&networkAdapter));\r\n\r\n            IFTYPE ifType{};\r\n            CONTINUE_IF_FAILED(networkAdapter->get_IanaInterfaceType(reinterpret_cast<UINT32*>(&ifType)));\r\n\r\n            GUID interfaceGuid{};\r\n            CONTINUE_IF_FAILED(networkAdapter->get_NetworkAdapterId(&interfaceGuid));\r\n\r\n            NET_LUID interfaceLuid{};\r\n            CONTINUE_IF_FAILED_WIN32(ConvertInterfaceGuidToLuid(&interfaceGuid, &interfaceLuid));\r\n\r\n            MIB_IF_ROW2 row{};\r\n            row.InterfaceLuid = interfaceLuid;\r\n            CONTINUE_IF_FAILED_WIN32(GetIfEntry2Ex(MibIfEntryNormalWithoutStatistics, &row));\r\n\r\n            MIB_IPINTERFACE_ROW ipIfRow{};\r\n            InitializeIpInterfaceEntry(&ipIfRow);\r\n            ipIfRow.Family = AF_INET;\r\n            ipIfRow.InterfaceLuid = interfaceLuid;\r\n            if (FAILED_WIN32(GetIpInterfaceEntry(&ipIfRow)))\r\n            {\r\n                // There is no IP interface directly attached to the given network adapter.  One way this could happen\r\n                // is if the network adapter is under an external vmswitch.  If that's the case, there should be at\r\n                // least one IP interface farther up the network adapter's interface stack.  We return all such IP\r\n                // interfaces as connected interfaces, as we don't know which is preferred at this point.\r\n                WSL_LOG(\r\n                    \"EnumerateConnectedInterfaces : connection profile's network adapter is not directly bound to TCP/IP - \"\r\n                    \"searching its interface stack for an IP interface\",\r\n                    TraceLoggingValue(interfaceGuid, \"physicalInterfaceGuid\"),\r\n                    TraceLoggingValue(ifType, \"ifType\"),\r\n                    TraceLoggingValue(row.Alias, \"ifAlias\"),\r\n                    TraceLoggingValue(row.Description, \"ifDescription\"),\r\n                    TraceLoggingValue(wsl::windows::common::stringify::ToString(connectivityLevel), \"connectivityLevel\"));\r\n\r\n                if (!details::FindInterfacesForNetworkAdapter(row.InterfaceIndex, interfaceGuid, metered, returnedNetworks))\r\n                {\r\n                    WSL_LOG(\r\n                        \"EnumerateConnectedInterfaces : could not find any IP interfaces for connected network profile\",\r\n                        TraceLoggingValue(interfaceGuid, \"interfaceGuid\"));\r\n                }\r\n                // TODO - if FindInterfacesForNetworkAdapter returns false, what should we add to returnedNetworks\r\n            }\r\n            else\r\n            {\r\n                WSL_LOG(\r\n                    \"EnumerateConnectedInterfaces : returning connected network profile\",\r\n                    TraceLoggingValue(interfaceGuid, \"interfaceGuid\"),\r\n                    TraceLoggingValue(row.Type, \"ifType\"),\r\n                    TraceLoggingValue(row.Alias, \"ifAlias\"),\r\n                    TraceLoggingValue(row.Description, \"ifDescription\"),\r\n                    TraceLoggingValue(wsl::windows::common::stringify::ToString(connectivityLevel), \"connectivityLevel\"));\r\n\r\n                returnedNetworks.emplace_back(interfaceGuid, interfaceLuid, ifType, row.Alias, row.Description, metered);\r\n            }\r\n        }\r\n    }\r\n    CATCH_LOG()\r\n\r\n    return returnedNetworks;\r\n}\r\n\r\nwsl::core::networking::EphemeralHcnEndpoint wsl::core::networking::CreateEphemeralHcnEndpoint(\r\n    HCN_NETWORK network, const wsl::shared::hns::HostComputeEndpoint& endpointSettings)\r\n{\r\n    wsl::core::networking::EphemeralHcnEndpoint endpoint{};\r\n    wil::unique_cotaskmem_string error;\r\n    const auto settings = wsl::shared::ToJsonW(endpointSettings);\r\n\r\n    ExecutionContext context(Context::HNS);\r\n    const auto result = HcnCreateEndpoint(network, endpoint.Id, settings.c_str(), &endpoint.Endpoint, &error);\r\n    THROW_IF_FAILED_MSG(result, \"HcnCreateEndpoint(%ls) failed: %ls\", settings.c_str(), error.get());\r\n\r\n    return endpoint;\r\n}\r\n\r\nstd::optional<ULONG> wsl::core::networking::GetMinimumConnectedInterfaceMtu() noexcept\r\n{\r\n    std::optional<ULONG> minMtu{};\r\n    try\r\n    {\r\n        unique_interface_table interfaceTable{};\r\n        THROW_IF_WIN32_ERROR(::GetIpInterfaceTable(AF_UNSPEC, &interfaceTable));\r\n\r\n        for (ULONG index = 0; index < interfaceTable.get()->NumEntries; index++)\r\n        {\r\n            const auto& ipInterface = interfaceTable.get()->Table[index];\r\n            if (ipInterface.Connected)\r\n            {\r\n                minMtu = std::min(minMtu.value_or(ipInterface.NlMtu), ipInterface.NlMtu);\r\n            }\r\n        }\r\n    }\r\n    CATCH_LOG()\r\n\r\n    return minMtu;\r\n}\r\n"
  },
  {
    "path": "src/windows/common/WslCoreNetworkingSupport.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n#include <optional>\r\n#include <variant>\r\n#include <vector>\r\n\r\n#include <windows.h>\r\n#include <ComputeNetwork.h>\r\n#include <mstcpip.h>\r\n#include <netioapi.h>\r\n#include <netlistmgr.h>\r\n#include <nldef.h>\r\n#include <wlantypes.h>\r\n#include <ws2ipdef.h>\r\n\r\n#include \"WslCoreConfig.h\"\r\n#include \"WslTelemetry.h\"\r\n#include \"hcs.hpp\"\r\n\r\n#include <wil/resource.h>\r\n\r\n// Global operator overloads\r\n// enabling usage of common Networking data structures within STL containers\r\n\r\ninline bool operator==(const DOT11_SSID& lhs, const DOT11_SSID& rhs) noexcept\r\n{\r\n    if (lhs.uSSIDLength == rhs.uSSIDLength)\r\n    {\r\n        return (0 == memcmp(lhs.ucSSID, rhs.ucSSID, lhs.uSSIDLength));\r\n    }\r\n    return false;\r\n}\r\n\r\ninline bool operator!=(const DOT11_SSID& lhs, const DOT11_SSID& rhs) noexcept\r\n{\r\n    return !(lhs == rhs);\r\n}\r\n\r\ninline bool operator==(const NL_NETWORK_CONNECTIVITY_HINT& lhs, const NL_NETWORK_CONNECTIVITY_HINT& rhs) noexcept\r\n{\r\n    return lhs.ApproachingDataLimit == rhs.ApproachingDataLimit && lhs.ConnectivityCost == rhs.ConnectivityCost &&\r\n           lhs.ConnectivityLevel == rhs.ConnectivityLevel && lhs.OverDataLimit == rhs.OverDataLimit && lhs.Roaming == rhs.Roaming;\r\n}\r\n\r\ninline bool operator!=(const NL_NETWORK_CONNECTIVITY_HINT& lhs, const NL_NETWORK_CONNECTIVITY_HINT& rhs) noexcept\r\n{\r\n    return !(lhs == rhs);\r\n}\r\n\r\ninline bool operator==(const SOCKADDR_INET& lhs, const SOCKADDR_INET& rhs) noexcept\r\n{\r\n    // not using INETADDR_ISEQUAL, because we can't compare the scopeId value from the v6 address\r\n    // that's the interface index on the host\r\n\r\n    if (lhs.si_family != rhs.si_family)\r\n    {\r\n        return false;\r\n    }\r\n    if (lhs.si_family == AF_INET)\r\n    {\r\n        return IN4_ADDR_EQUAL(&lhs.Ipv4.sin_addr, &rhs.Ipv4.sin_addr);\r\n    }\r\n    return IN6_ADDR_EQUAL(&lhs.Ipv6.sin6_addr, &rhs.Ipv6.sin6_addr);\r\n}\r\n\r\ninline bool operator<(const SOCKADDR_INET& lhs, const SOCKADDR_INET& rhs) noexcept\r\n{\r\n    if (lhs.si_family == rhs.si_family)\r\n    {\r\n        if (lhs.si_family == AF_INET)\r\n        {\r\n            return lhs.Ipv4.sin_addr.S_un.S_addr < rhs.Ipv4.sin_addr.S_un.S_addr;\r\n        }\r\n\r\n        // implementing the comparison operation following the shortcut from mstcpip.h IN6_ADDR_EQUAL\r\n        const __int64 UNALIGNED* lhsRawPointer = (__int64 UNALIGNED*)(&lhs.Ipv6.sin6_addr);\r\n        const __int64 UNALIGNED* rhsRawPointer = (__int64 UNALIGNED*)(&rhs.Ipv6.sin6_addr);\r\n        if (lhsRawPointer[0] == rhsRawPointer[0])\r\n        {\r\n            return lhsRawPointer[1] < rhsRawPointer[1];\r\n        }\r\n        return lhsRawPointer[0] < rhsRawPointer[0];\r\n    }\r\n    return lhs.si_family < rhs.si_family;\r\n}\r\n\r\ninline bool operator>(const SOCKADDR_INET& lhs, const SOCKADDR_INET& rhs) noexcept\r\n{\r\n    if (lhs.si_family == rhs.si_family)\r\n    {\r\n        if (lhs.si_family == AF_INET)\r\n        {\r\n            return lhs.Ipv4.sin_addr.S_un.S_addr > rhs.Ipv4.sin_addr.S_un.S_addr;\r\n        }\r\n\r\n        // implementing the comparison operation following the shortcut from mstcpip.h IN6_ADDR_EQUAL\r\n        const __int64 UNALIGNED* lhsRawPointer = (__int64 UNALIGNED*)(&lhs.Ipv6.sin6_addr);\r\n        const __int64 UNALIGNED* rhsRawPointer = (__int64 UNALIGNED*)(&rhs.Ipv6.sin6_addr);\r\n        if (lhsRawPointer[0] == rhsRawPointer[0])\r\n        {\r\n            return lhsRawPointer[1] > rhsRawPointer[1];\r\n        }\r\n        return lhsRawPointer[0] > rhsRawPointer[0];\r\n    }\r\n    return lhs.si_family > rhs.si_family;\r\n}\r\n\r\ninline bool operator==(const IP_ADDRESS_PREFIX& lhs, const IP_ADDRESS_PREFIX& rhs) noexcept\r\n{\r\n    return lhs.PrefixLength == rhs.PrefixLength && lhs.Prefix == rhs.Prefix;\r\n}\r\n\r\ninline bool operator<(const IP_ADDRESS_PREFIX& lhs, const IP_ADDRESS_PREFIX& rhs) noexcept\r\n{\r\n    if (lhs.PrefixLength == rhs.PrefixLength)\r\n    {\r\n        return lhs.Prefix < rhs.Prefix;\r\n    }\r\n    return lhs.PrefixLength < rhs.PrefixLength;\r\n}\r\n\r\ninline bool operator>(const IP_ADDRESS_PREFIX& lhs, const IP_ADDRESS_PREFIX& rhs) noexcept\r\n{\r\n    if (lhs.PrefixLength == rhs.PrefixLength)\r\n    {\r\n        return lhs.Prefix > rhs.Prefix;\r\n    }\r\n    return lhs.PrefixLength > rhs.PrefixLength;\r\n}\r\n\r\nnamespace wsl::core::networking {\r\n\r\ninline constexpr auto* c_ipv4TestRequestTarget = L\"www.msftconnecttest.com\";\r\ninline constexpr auto* c_ipv4TestRequestTargetA = \"www.msftconnecttest.com\";\r\ninline constexpr auto* c_ipv6TestRequestTarget = L\"ipv6.msftconnecttest.com\";\r\ninline constexpr auto* c_ipv6TestRequestTargetA = \"ipv6.msftconnecttest.com\";\r\n\r\ninline constexpr GUID c_wslFirewallVmCreatorId = {0x40E0AC32, 0x46A5, 0x438A, {0xA0, 0xB2, 0x2B, 0x47, 0x9E, 0x8F, 0x2E, 0x90}};\r\n\r\ninline constexpr auto c_networkAdapterPrefix = L\"VirtualMachine/Devices/NetworkAdapters/\";\r\ninline constexpr auto c_interfaceConstraintKey = L\"ExternalInterfaceConstraint\";\r\n\r\n// RAII types to manage resources returned from NetIO APIs\r\nusing unique_notify_handle = wil::unique_any<HANDLE, decltype(CancelMibChangeNotify2), &CancelMibChangeNotify2>;\r\nusing unique_interface_table = wil::unique_any<PMIB_IPINTERFACE_TABLE, decltype(FreeMibTable), &FreeMibTable>;\r\nusing unique_address_table = wil::unique_any<PMIB_UNICASTIPADDRESS_TABLE, decltype(FreeMibTable), &FreeMibTable>;\r\nusing unique_forward_table = wil::unique_any<PMIB_IPFORWARD_TABLE2, decltype(FreeMibTable), &FreeMibTable>;\r\nusing unique_ifstack_table = wil::unique_any<PMIB_IFSTACK_TABLE, decltype(FreeMibTable), &FreeMibTable>;\r\n\r\ninline wil::unique_couninitialize_call InitializeCOMState()\r\n{\r\n    // Ensure COM is initialized\r\n    auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);\r\n    HRESULT hr = CoInitializeSecurity(\r\n        nullptr, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_STATIC_CLOAKING, nullptr);\r\n    // Ignore error if CoInitializeSecurity has already been invoked\r\n    if (hr == RPC_E_TOO_LATE)\r\n    {\r\n        hr = S_OK;\r\n    }\r\n    THROW_IF_FAILED(hr);\r\n    return coInit;\r\n}\r\n\r\ninline bool IsInterfaceTypeVpn(IFTYPE type) noexcept\r\n{\r\n    return type == IF_TYPE_PPP || type == IF_TYPE_PROP_VIRTUAL;\r\n}\r\n\r\ninline bool IsInterfaceHidden(IF_INDEX InterfaceIndex)\r\n{\r\n    NL_NETWORK_CONNECTIVITY_HINT ConnectivityHint;\r\n\r\n    // Return true if we fail to retrieve the interface information\r\n    if (GetNetworkConnectivityHintForInterface(InterfaceIndex, &ConnectivityHint) != NO_ERROR)\r\n    {\r\n        return true;\r\n    }\r\n    return ConnectivityHint.ConnectivityLevel == NetworkConnectivityLevelHintHidden;\r\n}\r\n\r\ninline bool IsMulticastOrBroadcastIpAddress(const SOCKADDR_INET& address)\r\n{\r\n    switch (address.si_family)\r\n    {\r\n    case AF_INET:\r\n        return IN4_IS_ADDR_MULTICAST(&address.Ipv4.sin_addr) || IN4_IS_ADDR_BROADCAST(&address.Ipv4.sin_addr);\r\n    case AF_INET6:\r\n        return IN6_IS_ADDR_MULTICAST(&address.Ipv6.sin6_addr);\r\n    }\r\n    return false;\r\n}\r\n\r\ninline bool IsLoopbackIpAddress(const SOCKADDR_INET& address)\r\n{\r\n    switch (address.si_family)\r\n    {\r\n    case AF_INET:\r\n        return IN4_IS_ADDR_LOOPBACK(&address.Ipv4.sin_addr);\r\n    case AF_INET6:\r\n        return IN6_IS_ADDR_LOOPBACK(&address.Ipv6.sin6_addr);\r\n    }\r\n    return false;\r\n}\r\n\r\ninline bool IsNetworkErrorForMissingServices(HRESULT hr) noexcept\r\n{\r\n    switch (hr)\r\n    {\r\n    case HCS_E_SERVICE_NOT_AVAILABLE:\r\n    case HRESULT_FROM_WIN32(RPC_S_CALL_FAILED):\r\n    case HRESULT_FROM_WIN32(EPT_S_NOT_REGISTERED):\r\n    case HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_FOUND):\r\n    case HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST):\r\n    case HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED):\r\n        return true;\r\n    }\r\n    return false;\r\n}\r\n\r\ninline std::string ToString(const NLM_CONNECTIVITY& nlmConnectivity)\r\n{\r\n    if (nlmConnectivity == NLM_CONNECTIVITY_DISCONNECTED)\r\n    {\r\n        return \"Disconnected\";\r\n    }\r\n\r\n    std::string returnString;\r\n    if (nlmConnectivity & NLM_CONNECTIVITY_IPV4_NOTRAFFIC)\r\n    {\r\n        returnString += \" IPv4NoTraffic\";\r\n    }\r\n    if (nlmConnectivity & NLM_CONNECTIVITY_IPV6_NOTRAFFIC)\r\n    {\r\n        returnString += \" IPv6NoTraffic\";\r\n    }\r\n    if (nlmConnectivity & NLM_CONNECTIVITY_IPV4_SUBNET)\r\n    {\r\n        returnString += \" IPv4Subnet\";\r\n    }\r\n    if (nlmConnectivity & NLM_CONNECTIVITY_IPV4_LOCALNETWORK)\r\n    {\r\n        returnString += \" IPv4Local\";\r\n    }\r\n    if (nlmConnectivity & NLM_CONNECTIVITY_IPV4_INTERNET)\r\n    {\r\n        returnString += \" IPv4Internet\";\r\n    }\r\n    if (nlmConnectivity & NLM_CONNECTIVITY_IPV6_SUBNET)\r\n    {\r\n        returnString += \" IPv6Subnet\";\r\n    }\r\n    if (nlmConnectivity & NLM_CONNECTIVITY_IPV6_LOCALNETWORK)\r\n    {\r\n        returnString += \" IPv6Local\";\r\n    }\r\n    if (nlmConnectivity & NLM_CONNECTIVITY_IPV6_INTERNET)\r\n    {\r\n        returnString += \" IPv6Internet\";\r\n    }\r\n\r\n    return returnString;\r\n}\r\n\r\nenum class UpdateEndpointFlag\r\n{\r\n    None,\r\n    Default,\r\n    ResendInitialUpdate,\r\n    ForceUpdate,\r\n    ForceIpUpdate,\r\n    BlockClientUpdates,\r\n};\r\n\r\ninline PCSTR ToString(UpdateEndpointFlag flag) noexcept\r\n{\r\n    switch (flag)\r\n    {\r\n    case UpdateEndpointFlag::None:\r\n        return \"None\";\r\n    case UpdateEndpointFlag::Default:\r\n        return \"Default\";\r\n    case UpdateEndpointFlag::ResendInitialUpdate:\r\n        return \"ResendInitialUpdate\";\r\n    case UpdateEndpointFlag::ForceUpdate:\r\n        return \"ForceUpdate\";\r\n    case UpdateEndpointFlag::ForceIpUpdate:\r\n        return \"ForceIpUpdate\";\r\n    case UpdateEndpointFlag::BlockClientUpdates:\r\n        return \"BlockClientUpdates\";\r\n    default:\r\n        return \"<unknown UpdateEndpointFlag>\";\r\n    }\r\n}\r\n\r\n// mapping wsl::shared::hns::* structures to the corresponding message type to send to GNS\r\nconstexpr LX_MESSAGE_TYPE GnsMessageType(const wsl::shared::hns::VmNicCreatedNotification&) noexcept\r\n{\r\n    return LxGnsMessageVmNicCreatedNotification;\r\n}\r\n\r\nconstexpr LX_MESSAGE_TYPE GnsMessageType(const wsl::shared::hns::CreateDeviceRequest&) noexcept\r\n{\r\n    return LxGnsMessageCreateDeviceRequest;\r\n}\r\n\r\nconstexpr LX_MESSAGE_TYPE GnsMessageType(const wsl::shared::hns::LoopbackRoutesRequest&) noexcept\r\n{\r\n    return LxGnsMessageLoopbackRoutesRequest;\r\n}\r\n\r\nconstexpr LX_MESSAGE_TYPE GnsMessageType(const wsl::shared::hns::ModifyGuestDeviceSettingRequest&) noexcept\r\n{\r\n    return LxGnsMessageModifyGuestDeviceSettingRequest;\r\n}\r\n\r\nconstexpr LX_MESSAGE_TYPE GnsMessageType(const wsl::shared::hns::InitialIpConfigurationNotification&) noexcept\r\n{\r\n    return LxGnsMessageInitialIpConfigurationNotification;\r\n}\r\n\r\ninline bool IsInterfaceIndexOfGelnic(DWORD InterfaceIndex) noexcept\r\n{\r\n    // Currently the GELNIC is indicated from HNS as an endpoint with interface index 0.\r\n    static constexpr DWORD c_InterfaceIndexGelnic = 0;\r\n    return InterfaceIndex == c_InterfaceIndexGelnic;\r\n}\r\n\r\nstruct CurrentInterfaceInformation\r\n{\r\n    CurrentInterfaceInformation() = default;\r\n\r\n    CurrentInterfaceInformation(\r\n        const GUID& preferredGuid, const NET_LUID& preferredLuid, IFTYPE preferredType, std::wstring preferredName, std::wstring interfaceDescription, bool metered) :\r\n        m_interfaceType(preferredType),\r\n        m_interfaceName(std::move(preferredName)),\r\n        m_interfaceDescription(std::move(interfaceDescription)),\r\n        m_interfaceGuid(preferredGuid),\r\n        m_interfaceLuid(preferredLuid),\r\n        m_metered(metered)\r\n    {\r\n    }\r\n\r\n    IFTYPE m_interfaceType{IF_TYPE_OTHER}; // == 1 == minimum iftype\r\n    std::wstring m_interfaceName;\r\n    std::wstring m_interfaceDescription;\r\n    std::optional<GUID> m_interfaceGuid{std::nullopt};\r\n    std::optional<NET_LUID> m_interfaceLuid{std::nullopt};\r\n    bool m_metered{false};\r\n};\r\n\r\ninline std::vector<GUID> EnumerateNetworks(std::optional<wsl::shared::hns::NetworkFlags> queryFlags = {})\r\n{\r\n    std::wstring queryString;\r\n    if (queryFlags)\r\n    {\r\n        wsl::shared::hns::HostComputeQuery query{};\r\n        query.Filter = std::format(\"{{\\\"Flags\\\": {}}}\", static_cast<uint32_t>(queryFlags.value()));\r\n        queryString = wsl::shared::ToJsonW(query);\r\n    }\r\n\r\n    wil::unique_cotaskmem_string response;\r\n    wil::unique_cotaskmem_string error;\r\n    const auto result = ::HcnEnumerateNetworks(queryString.empty() ? nullptr : queryString.c_str(), &response, &error);\r\n    THROW_IF_FAILED_MSG(result, \"HcnEnumerateNetworks(%ls) %ls\", queryString.empty() ? nullptr : queryString.c_str(), error.get());\r\n\r\n    return wsl::shared::FromJson<std::vector<GUID>>(response.get());\r\n}\r\n\r\ninline std::vector<GUID> EnumerateEndpointsByNetworkId(const GUID& networkId)\r\n{\r\n    const std::wstring queryString = std::format(\r\n        L\"{{\\\"Filter\\\": \\\"{{\\\\\\\"VirtualNetwork\\\\\\\": \\\\\\\"{}\\\\\\\"}}\\\"}}\",\r\n        wsl::shared::string::GuidToString<wchar_t>(networkId, wsl::shared::string::GuidToStringFlags::None));\r\n\r\n    wil::unique_cotaskmem_string endpointsJson;\r\n    wil::unique_cotaskmem_string errorJson;\r\n    const auto result = HcnEnumerateEndpoints(queryString.c_str(), &endpointsJson, &errorJson);\r\n    THROW_IF_FAILED_MSG(result, \"HcnEnumerateEndpoints failed: %ls, query: '%ls'\", errorJson.get(), queryString.c_str());\r\n    return wsl::shared::FromJson<std::vector<GUID>>(endpointsJson.get());\r\n}\r\n\r\ninline std::vector<GUID> EnumerateMirroredNetworksAndHyperVFirewall(bool enableFirewall)\r\n{\r\n    auto flags = wsl::shared::hns::NetworkFlags::EnableNonPersistent | wsl::shared::hns::NetworkFlags::EnableFlowSteering;\r\n    WI_SetFlagIf(flags, wsl::shared::hns::NetworkFlags::EnableFirewall, enableFirewall);\r\n    std::vector<GUID> networkIds = EnumerateNetworks(flags);\r\n    for (auto& id : networkIds)\r\n    {\r\n        WSL_LOG(\r\n            \"EnumerateMirroredNetworksAndHyperVFirewall\",\r\n            TraceLoggingValue(static_cast<uint32_t>(flags), \"flags\"),\r\n            TraceLoggingValue(id, \"networkId\"));\r\n    }\r\n\r\n    return networkIds;\r\n}\r\n\r\ninline wsl::windows::common::hcs::unique_hcn_network OpenNetwork(const GUID& networkId)\r\n{\r\n    wsl::windows::common::hcs::unique_hcn_network network;\r\n    wil::unique_cotaskmem_string error;\r\n    const auto result = ::HcnOpenNetwork(networkId, &network, &error);\r\n    THROW_IF_FAILED_MSG(result, \"HcnOpenNetwork %ls\", error.get());\r\n\r\n    return network;\r\n}\r\n\r\ninline std::pair<wsl::shared::hns::HNSNetwork, wil::unique_cotaskmem_string> QueryNetworkProperties(HCN_NETWORK network)\r\n{\r\n    wil::unique_cotaskmem_string properties;\r\n    wil::unique_cotaskmem_string error;\r\n    const auto result = ::HcnQueryNetworkProperties(network, nullptr, &properties, &error);\r\n    THROW_IF_FAILED_MSG(result, \"HcnQueryNetworkProperties %ls\", error.get());\r\n\r\n    auto parsed = wsl::shared::FromJson<wsl::shared::hns::HNSNetwork>(properties.get());\r\n    return {std::move(parsed), std::move(properties)};\r\n}\r\n\r\nstruct EphemeralHcnEndpoint\r\n{\r\n    EphemeralHcnEndpoint()\r\n    {\r\n        THROW_IF_FAILED(CoCreateGuid(&Id));\r\n    }\r\n\r\n    EphemeralHcnEndpoint(const EphemeralHcnEndpoint&) = delete;\r\n    EphemeralHcnEndpoint(EphemeralHcnEndpoint&&) = default;\r\n\r\n    EphemeralHcnEndpoint& operator=(const EphemeralHcnEndpoint&) = delete;\r\n    EphemeralHcnEndpoint& operator=(EphemeralHcnEndpoint&&) = default;\r\n\r\n    windows::common::hcs::unique_hcn_endpoint Endpoint;\r\n    GUID Id{};\r\n\r\n    ~EphemeralHcnEndpoint()\r\n    {\r\n        if (Endpoint)\r\n        {\r\n            wil::unique_cotaskmem_string error;\r\n            const auto result = HcnDeleteEndpoint(Id, &error);\r\n            LOG_IF_FAILED_MSG(result, \"HcnDeleteEndpoint failed: %ls\", error.get());\r\n        }\r\n    }\r\n};\r\n\r\n/// <summary>\r\n/// Returns true if the host supports flow steering.\r\n/// </summary>\r\nbool IsFlowSteeringSupportedByHns() noexcept;\r\n\r\nEphemeralHcnEndpoint CreateEphemeralHcnEndpoint(HCN_NETWORK network, const wsl::shared::hns::HostComputeEndpoint& endpointSettings);\r\n\r\nstd::vector<wsl::core::networking::CurrentInterfaceInformation> EnumerateConnectedInterfaces();\r\n\r\nbool IsMetered(ABI::Windows::Networking::Connectivity::NetworkCostType cost) noexcept;\r\n\r\n/// <summary>\r\n/// Gets the minimum MTU across all connected network interfaces.\r\n/// </summary>\r\nstd::optional<ULONG> GetMinimumConnectedInterfaceMtu() noexcept;\r\n\r\n/// <summary>\r\n/// This instance acts as an IP_ADAPTER_ADDRESS pointer.\r\n/// </summary>\r\nclass AdapterAddresses;\r\n\r\n/// <summary>\r\n/// IP_ADAPTER_ADDRESSES wrapper that maintains a reference to the buffer\r\n/// returned by GetAdaptersAddresses such that this instance always points to\r\n/// valid data.\r\n/// </summary>\r\nclass IpAdapterAddress\r\n{\r\npublic:\r\n    /// <summary>\r\n    /// Instance constructor.\r\n    /// </summary>\r\n    IpAdapterAddress(const std::shared_ptr<AdapterAddresses>& AddressContainer, const IP_ADAPTER_ADDRESSES* Address) :\r\n        m_container(AddressContainer), m_address(Address)\r\n    {\r\n    }\r\n\r\n    /// <summary>\r\n    /// This instance acts as an IP_ADAPTER_ADDRESS pointer.\r\n    /// </summary>\r\n    const IP_ADAPTER_ADDRESSES* operator->() const noexcept\r\n    {\r\n        return m_address;\r\n    }\r\n\r\nprivate:\r\n    /// <summary>\r\n    /// Reference to the buffer holding the IP_ADAPTER_ADDRESSES data.\r\n    /// </summary>\r\n    std::shared_ptr<AdapterAddresses> m_container{};\r\n\r\n    /// <summary>\r\n    /// Pointer into the buffer where this specific IP_ADAPTER_ADDRESSES\r\n    /// instance begins.\r\n    /// </summary>\r\n    const IP_ADAPTER_ADDRESSES* m_address{};\r\n};\r\n\r\n/// <summary>\r\n/// GetAdaptersAddresses wrapper\r\n/// </summary>\r\nclass AdapterAddresses : public std::enable_shared_from_this<AdapterAddresses>\r\n{\r\npublic:\r\n    /// <summary>\r\n    /// Calls GetAdaptersAddresses and returns wrapped results.\r\n    /// </summary>\r\n    static std::vector<IpAdapterAddress> GetCurrent()\r\n    {\r\n        const std::shared_ptr<AdapterAddresses> newInstance(new AdapterAddresses());\r\n        return newInstance->Initialize();\r\n    }\r\n\r\nprivate:\r\n    /// <summary>\r\n    /// Default constructor is private as an instance is not directly created\r\n    /// by callers.\r\n    /// </summary>\r\n    AdapterAddresses() = default;\r\n\r\n    /// <summary>\r\n    /// Copy constructor is not allowed.\r\n    /// </summary>\r\n    AdapterAddresses(const AdapterAddresses&) = delete;\r\n\r\n    /// <summary>\r\n    /// Internal function to do the interesting work.\r\n    /// </summary>\r\n    std::vector<IpAdapterAddress> Initialize()\r\n    {\r\n        // N.B. MSDN recommends starting with a 15K buffer as that will be sufficient on\r\n        // most systems and the call to GetAdaptersAddresses is expensive.\r\n        ULONG Result;\r\n        ULONG BufferSize = (15 * 1024);\r\n        do\r\n        {\r\n            m_buffer.resize(BufferSize);\r\n            Result = GetAdaptersAddresses(\r\n                AF_UNSPEC,\r\n                (GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_INCLUDE_GATEWAYS),\r\n                nullptr,\r\n                (PIP_ADAPTER_ADDRESSES)m_buffer.data(),\r\n                &BufferSize);\r\n        } while (Result == ERROR_BUFFER_OVERFLOW);\r\n\r\n        THROW_LAST_ERROR_IF_MSG((Result != ERROR_SUCCESS), \"GetAdaptersAddresses\");\r\n        m_buffer.resize(BufferSize);\r\n        auto AddressBuffer = (PIP_ADAPTER_ADDRESSES)m_buffer.data();\r\n        std::vector<IpAdapterAddress> addresses;\r\n        while (AddressBuffer != nullptr)\r\n        {\r\n            addresses.emplace_back(IpAdapterAddress(shared_from_this(), AddressBuffer));\r\n            AddressBuffer = AddressBuffer->Next;\r\n        }\r\n\r\n        return addresses;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Buffer to hold the results of GetAdaptersAddresses.\r\n    /// </summary>\r\n    std::vector<BYTE> m_buffer{};\r\n};\r\n\r\nclass ConnectivityTelemetry\r\n{\r\npublic:\r\n    ConnectivityTelemetry() = default;\r\n    ~ConnectivityTelemetry() = default;\r\n    ConnectivityTelemetry(const ConnectivityTelemetry&) = delete;\r\n    ConnectivityTelemetry& operator=(const ConnectivityTelemetry&) = delete;\r\n    ConnectivityTelemetry(ConnectivityTelemetry&&) = delete;\r\n    ConnectivityTelemetry& operator=(ConnectivityTelemetry&&) = delete;\r\n\r\n    void StartTimer(std::function<void(NLM_CONNECTIVITY, uint32_t)>&& callback)\r\n    {\r\n        m_callback = std::move(callback);\r\n        m_telemetryConnectionTimer.reset(CreateThreadpoolTimer(TelemetryConnectionTimerCallback, this, nullptr));\r\n        THROW_IF_NULL_ALLOC(m_telemetryConnectionTimer);\r\n    }\r\n\r\n    void UpdateTimer() const noexcept\r\n    {\r\n        if (m_telemetryConnectionTimer)\r\n        {\r\n            FILETIME dueTime =\r\n                wil::filetime::from_int64(static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_backoffTimeMs));\r\n            SetThreadpoolTimer(m_telemetryConnectionTimer.get(), &dueTime, 0, 1000);\r\n        }\r\n    }\r\n\r\n    void Reset() noexcept\r\n    {\r\n        m_telemetryConnectionTimer.reset();\r\n    }\r\n\r\n    static uint32_t LinuxIPv4ConnCheckResult(uint32_t returnedLinuxLevel) noexcept\r\n    {\r\n        // v4 is the lower-16 bits\r\n        return returnedLinuxLevel & 0xffff;\r\n    }\r\n    static uint32_t LinuxIPv6ConnCheckResult(uint32_t returnedLinuxLevel) noexcept\r\n    {\r\n        // v6 is the higher-16 bits\r\n        return returnedLinuxLevel >> 16;\r\n    }\r\n    static uint32_t WindowsIPv4NlmConnectivityLevel(NLM_CONNECTIVITY hostConnectivity) noexcept\r\n    {\r\n        if (hostConnectivity == NLM_CONNECTIVITY_DISCONNECTED)\r\n        {\r\n            return NLM_CONNECTIVITY_DISCONNECTED;\r\n        }\r\n\r\n        return (hostConnectivity & NLM_CONNECTIVITY_IPV4_NOTRAFFIC) | (hostConnectivity & NLM_CONNECTIVITY_IPV4_SUBNET) |\r\n               (hostConnectivity & NLM_CONNECTIVITY_IPV4_LOCALNETWORK) | (hostConnectivity & NLM_CONNECTIVITY_IPV4_INTERNET);\r\n    }\r\n    static uint32_t WindowsIPv6NlmConnectivityLevel(NLM_CONNECTIVITY hostConnectivity) noexcept\r\n    {\r\n        if (hostConnectivity == NLM_CONNECTIVITY_DISCONNECTED)\r\n        {\r\n            return NLM_CONNECTIVITY_DISCONNECTED;\r\n        }\r\n\r\n        return (hostConnectivity & NLM_CONNECTIVITY_IPV6_NOTRAFFIC) | (hostConnectivity & NLM_CONNECTIVITY_IPV6_SUBNET) |\r\n               (hostConnectivity & NLM_CONNECTIVITY_IPV6_LOCALNETWORK) | (hostConnectivity & NLM_CONNECTIVITY_IPV6_INTERNET);\r\n    }\r\n\r\nprivate:\r\n    const uint32_t m_backoffTimeMs = 5000;\r\n    std::function<void(NLM_CONNECTIVITY, uint32_t)> m_callback;\r\n    wil::unique_threadpool_timer m_telemetryConnectionTimer;\r\n    uint32_t m_telemetryCounter = 0;\r\n\r\n    static void __stdcall TelemetryConnectionTimerCallback(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID context, _Inout_ PTP_TIMER) noexcept\r\n    try\r\n    {\r\n        const auto coInit = wil::CoInitializeEx();\r\n        const wil::com_ptr<INetworkListManager> networkListManager = wil::CoCreateInstance<NetworkListManager, INetworkListManager>();\r\n\r\n        NLM_CONNECTIVITY hostConnectivity{};\r\n        THROW_IF_FAILED(networkListManager->GetConnectivity(&hostConnectivity));\r\n\r\n        const auto updatedCounter = ++static_cast<ConnectivityTelemetry*>(context)->m_telemetryCounter;\r\n        static_cast<ConnectivityTelemetry*>(context)->m_callback(hostConnectivity, updatedCounter);\r\n    }\r\n    CATCH_LOG()\r\n};\r\n} // namespace wsl::core::networking"
  },
  {
    "path": "src/windows/common/WslInstall.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslInstall.cpp\n\nAbstract:\n\n    This file contains implementations for installing WSL distributions\n\n--*/\n\n#include \"precomp.h\"\n#include \"WslInstall.h\"\n#include \"registry.hpp\"\n#include \"wslutil.h\"\n#include \"Distribution.h\"\n#include \"HandleConsoleProgressBar.h\"\n#include \"svccomm.hpp\"\n\nextern HINSTANCE g_dllInstance;\n\nconstexpr LPCWSTR c_optionalFeatureInstallStatus = L\"InstallStatus\";\nconstexpr LPCWSTR c_optionalFeatureNameVmp = L\"VirtualMachinePlatform\";\nconstexpr LPCWSTR c_optionalFeatureNameWsl = L\"Microsoft-Windows-Subsystem-Linux\";\n\nusing wsl::shared::Localization;\nusing namespace wsl::windows::common::distribution;\nusing namespace wsl::windows::common::wslutil;\n\nnamespace {\nstd::vector<BYTE> ParseHex(const std::wstring& input);\n\nvoid EnforceFileHash(HANDLE file, const std::wstring& expectedHash)\n{\n    wsl::windows::common::ExecutionContext context(wsl::windows::common::VerifyChecksum);\n\n    const auto fileHash = wsl::windows::common::wslutil::HashFile(file, CALG_SHA_256);\n\n    THROW_LAST_ERROR_IF(SetFilePointer(file, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER);\n    if (fileHash != ParseHex(expectedHash))\n    {\n        THROW_HR_WITH_USER_ERROR(\n            TRUST_E_BAD_DIGEST,\n            wsl::shared::Localization::MessageHashMismatch(\n                expectedHash.c_str(), wsl::windows::common::string::BytesToHex(fileHash).c_str()));\n    }\n}\n\nstd::vector<std::wstring> GetInstalledOptionalComponents()\n{\n    // Query the list of optional components that have already been installed.\n    const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\n    auto [key, error] = wsl::windows::common::registry::OpenKeyNoThrow(lxssKey.get(), c_optionalFeatureInstallStatus, KEY_READ);\n    std::vector<std::wstring> installedComponents;\n    if (key)\n    {\n        const auto components = wsl::windows::common::registry::ReadString(key.get(), nullptr, nullptr, L\"\");\n        installedComponents = wsl::shared::string::Split(components, L',');\n    }\n\n    return installedComponents;\n}\n\nstd::vector<BYTE> ParseHex(const std::wstring& input)\n{\n    std::vector<BYTE> result;\n    for (auto i = 0; i < input.size(); i += 2)\n    {\n        // Skip '0x', if any\n        if (i == 0 && input[0] == '0' && tolower(input[1]) == 'x')\n        {\n            continue;\n        }\n\n        auto current = input.substr(i, 2);\n        wchar_t* endPtr{};\n\n        const auto byte = wcstoul(current.data(), &endPtr, 16);\n        if (endPtr != current.data() + 2)\n        {\n            THROW_HR_WITH_USER_ERROR(E_INVALIDARG, wsl::shared::Localization::MessageInvalidHexString(input.c_str()));\n        }\n\n        result.push_back(static_cast<BYTE>(byte));\n    }\n\n    return result;\n}\n}; // namespace\n\nHRESULT WslInstall::InstallDistribution(\n    _Out_ InstallResult& installResult,\n    _In_ const std::optional<std::wstring>& distributionName,\n    _In_ const std::optional<ULONG>& version,\n    _In_ bool launchAfterInstall,\n    _In_ bool useGitHub,\n    _In_ bool legacy,\n    _In_ bool fixedVhd,\n    _In_ const std::optional<std::wstring>& localName,\n    _In_ const std::optional<std::wstring>& location,\n    _In_ const std::optional<uint64_t>& vhdSize)\ntry\n{\n    wsl::windows::common::ExecutionContext context(wsl::windows::common::InstallDistro);\n\n    try\n    {\n        const auto distributions = wsl::windows::common::distribution::GetAvailable();\n\n        if (distributionName.has_value())\n        {\n            installResult.Distribution = LookupByName(distributions, distributionName->c_str(), legacy);\n        }\n        else\n        {\n            if (legacy)\n            {\n                THROW_HR_IF(\n                    E_UNEXPECTED, !distributions.Manifest.Distributions.has_value() || distributions.Manifest.Distributions->empty());\n                installResult.Distribution = (*distributions.Manifest.Distributions)[0];\n            }\n            else\n            {\n                if (distributions.OverrideManifest.has_value() && distributions.OverrideManifest->Default.has_value())\n                {\n                    installResult.Distribution = LookupByName(distributions, distributions.OverrideManifest->Default->c_str(), false);\n                }\n                else\n                {\n                    if (!distributions.Manifest.Default.has_value())\n                    {\n                        THROW_HR_WITH_USER_ERROR(E_UNEXPECTED, wsl::shared::Localization::MessageNoInstallDefault());\n                    }\n\n                    installResult.Distribution = LookupByName(distributions, distributions.Manifest.Default->c_str(), false);\n                }\n            }\n        }\n\n        if (const auto* distro = std::get_if<ModernDistributionVersion>(&*installResult.Distribution))\n        {\n            std::tie(installResult.Name, installResult.Id) =\n                InstallModernDistribution(*distro, version, localName, location, vhdSize, fixedVhd);\n\n            installResult.InstalledViaGitHub = true;\n        }\n        else if (const auto* distro = std::get_if<Distribution>(&*installResult.Distribution))\n        {\n            std::list<std::pair<bool, LPCWSTR>> unsupportedArguments = {\n                {localName.has_value(), WSL_INSTALL_ARG_NAME_LONG},\n                {location.has_value(), WSL_INSTALL_ARG_LOCATION_LONG},\n                {vhdSize.has_value(), WSL_INSTALL_ARG_VHD_SIZE},\n                {fixedVhd, WSL_INSTALL_ARG_FIXED_VHD}};\n\n            for (const auto& [condition, argument] : unsupportedArguments)\n            {\n                if (condition)\n                {\n                    THROW_HR_WITH_USER_ERROR(WSL_E_INVALID_USAGE, Localization::MessageNotSupportedOnLegacyDistros(argument).c_str());\n                }\n            }\n\n            installResult.Alreadyinstalled = wsl::windows::common::distribution::IsInstalled(*distro, useGitHub);\n            if (!installResult.Alreadyinstalled)\n            {\n                EMIT_USER_WARNING(Localization::MessageUsingLegacyDistribution());\n                if (version.has_value())\n                {\n                    THROW_HR_WITH_USER_ERROR(WSL_E_INVALID_USAGE, Localization::MessageLegacyDistributionVersionArgNotSupported());\n                }\n\n                // If downloading from the store fails, attempt to download from GitHub.\n                if (!useGitHub)\n                {\n                    auto hr =\n                        wil::ResultFromException([&]() { wsl::windows::common::distribution::LegacyInstallViaStore(*distro); });\n                    if (FAILED(hr))\n                    {\n                        useGitHub = true;\n                        auto errorString = wsl::windows::common::wslutil::GetErrorString(hr);\n                        wsl::windows::common::wslutil::PrintMessage(\n                            Localization::MessageDistroStoreInstallFailed(distro->Name.c_str(), errorString.c_str()), stdout);\n                    }\n                }\n\n                if (useGitHub)\n                {\n                    wsl::windows::common::distribution::LegacyInstallViaGithub(*distro);\n                }\n            }\n\n            installResult.Name = distro->FriendlyName;\n            installResult.InstalledViaGitHub = useGitHub;\n        }\n        else\n        {\n            THROW_HR(E_UNEXPECTED);\n        }\n    }\n    catch (...)\n    {\n        // Rethrowing via WIL is required for the error context to be properly set\n        // in case a winrt exception was thrown.\n        THROW_HR(wil::ResultFromCaughtException());\n    }\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nstd::pair<bool, std::vector<std::wstring>> WslInstall::CheckForMissingOptionalComponents(_In_ bool requireWslOptionalComponent)\n{\n    // Include the WSL optional component if it was requested, or if the OS is not Windows 11 or later.\n    std::vector<std::wstring> missingComponents;\n    requireWslOptionalComponent |= !wsl::windows::common::helpers::IsWindows11OrAbove();\n    if (requireWslOptionalComponent && !wsl::windows::common::helpers::IsServicePresent(L\"lxssmanager\"))\n    {\n        missingComponents.emplace_back(c_optionalFeatureNameWsl);\n    }\n\n    if (!wsl::windows::common::wslutil::IsVirtualMachinePlatformInstalled())\n    {\n        missingComponents.emplace_back(c_optionalFeatureNameVmp);\n    }\n\n    // If any required components are not present, a reboot is required.\n    bool rebootRequired = !missingComponents.empty();\n\n    // Query the list of optional components that have already been installed.\n    const auto installedComponents = GetInstalledOptionalComponents();\n    for (const auto& component : installedComponents)\n    {\n        std::erase(missingComponents, component);\n    }\n\n    return {rebootRequired, std::move(missingComponents)};\n}\n\nvoid WslInstall::InstallOptionalComponents(const std::vector<std::wstring>& components)\n{\n    std::wstring systemDirectory;\n    THROW_IF_FAILED(wil::GetSystemDirectoryW(systemDirectory));\n\n    const auto dismPath = std::filesystem::path(std::move(systemDirectory)) / L\"dism.exe\";\n    for (const auto& component : components)\n    {\n        wsl::windows::common::wslutil::PrintMessage(Localization::MessageInstallingWindowsComponent(component));\n\n        auto commandLine = std::format(L\"{} /Online /NoRestart /enable-feature /featurename:{}\", dismPath.wstring(), component);\n        const auto exitCode = wsl::windows::common::helpers::RunProcess(commandLine);\n        if (exitCode != 0 && exitCode != ERROR_SUCCESS_REBOOT_REQUIRED)\n        {\n            THROW_HR_WITH_USER_ERROR(WSL_E_INSTALL_COMPONENT_FAILED, Localization::MessageOptionalComponentInstallFailed(component, exitCode));\n        }\n    }\n\n    // Update the list of optional components that have been installed.\n    auto installedComponents = GetInstalledOptionalComponents();\n    installedComponents.insert(installedComponents.end(), components.begin(), components.end());\n    const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\n    const auto key =\n        wsl::windows::common::registry::CreateKey(lxssKey.get(), c_optionalFeatureInstallStatus, KEY_ALL_ACCESS, nullptr, REG_OPTION_VOLATILE);\n    wsl::windows::common::registry::WriteString(key.get(), nullptr, nullptr, wsl::shared::string::Join(installedComponents, L',').c_str());\n}\n\nstd::pair<std::wstring, GUID> WslInstall::InstallModernDistribution(\n    const ModernDistributionVersion& distribution,\n    const std::optional<ULONG>& version,\n    const std::optional<std::wstring>& name,\n    const std::optional<std::wstring>& location,\n    const std::optional<uint64_t>& vhdSize,\n    const bool fixedVhd)\n{\n    wsl::windows::common::SvcComm service;\n\n    // Fail early if the distributions name is already in use.\n    auto result = wil::ResultFromException([&]() {\n        service.GetDistributionId(name.has_value() ? name->c_str() : distribution.Name.c_str(), LXSS_GET_DISTRO_ID_LIST_ALL);\n    });\n\n    THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), SUCCEEDED(result));\n    LOG_HR_IF(result, result != WSL_E_DISTRO_NOT_FOUND);\n\n    const auto downloadInfo = wsl::shared::Arm64 ? distribution.Arm64Url : distribution.Amd64Url;\n    THROW_HR_IF(E_UNEXPECTED, !downloadInfo.has_value());\n\n    std::wstring installPath;\n    bool fileDownloaded{};\n    auto deleteFile = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n        if (fileDownloaded)\n        {\n            THROW_IF_WIN32_BOOL_FALSE(DeleteFileW(installPath.c_str()));\n        }\n    });\n\n    if (auto localFile = wsl::windows::common::filesystem::TryGetPathFromFileUrl(downloadInfo->Url))\n    {\n        installPath = std::move(localFile.value());\n    }\n    else\n    {\n        PrintMessage(Localization::MessageDownloading(distribution.FriendlyName.c_str()), stdout);\n        installPath = DownloadFile(downloadInfo->Url, distribution.Name + L\".wsl\");\n        fileDownloaded = true;\n    }\n\n    PrintMessage(Localization::MessageInstalling(distribution.FriendlyName.c_str()), stdout);\n\n    wil::unique_handle file{CreateFile(installPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)};\n    THROW_LAST_ERROR_IF(!file);\n\n    EnforceFileHash(file.get(), downloadInfo->Sha256);\n\n    wsl::windows::common::HandleConsoleProgressBar progressBar(file.get(), Localization::MessageImportProgress());\n\n    auto [id, installedName] = service.RegisterDistribution(\n        name.has_value() ? name->c_str() : distribution.Name.c_str(),\n        version.value_or(LXSS_WSL_VERSION_DEFAULT),\n        file.get(),\n        location.has_value() ? location->c_str() : nullptr,\n        fixedVhd ? LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD : 0,\n        vhdSize);\n\n    return {installedName.get(), id};\n}"
  },
  {
    "path": "src/windows/common/WslInstall.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslInstall.h\n\nAbstract:\n\n    This file contains the definitions for installing WSL distributions\n\n--*/\n\n#pragma once\n#include <string_view>\n#include \"Distribution.h\"\n\nclass WslInstall\n{\npublic:\n    struct InstallResult\n    {\n        std::wstring Name;\n        std::optional<GUID> Id;\n        std::optional<wsl::windows::common::distribution::TDistribution> Distribution;\n        bool InstalledViaGitHub{};\n        bool Alreadyinstalled{};\n    };\n\n    static HRESULT InstallDistribution(\n        _Out_ InstallResult& installResult,\n        _In_ const std::optional<std::wstring>& distributionName,\n        _In_ const std::optional<ULONG>& version,\n        _In_ bool launchAfterInstall,\n        _In_ bool useGitHub,\n        _In_ bool legacy,\n        _In_ bool fixedVhd,\n        _In_ const std::optional<std::wstring>& localName,\n        _In_ const std::optional<std::wstring>& location,\n        _In_ const std::optional<uint64_t>& vhdSize);\n\n    static std::pair<bool, std::vector<std::wstring>> CheckForMissingOptionalComponents(_In_ bool requireWslOptionalComponent);\n\n    static void InstallOptionalComponents(const std::vector<std::wstring>& components);\n\n    static std::pair<std::wstring, GUID> InstallModernDistribution(\n        const wsl::windows::common::distribution::ModernDistributionVersion& distribution,\n        const std::optional<ULONG>& version,\n        const std::optional<std::wstring>& name,\n        const std::optional<std::wstring>& location,\n        const std::optional<uint64_t>& vhdSize,\n        const bool fixedVhd);\n};\n"
  },
  {
    "path": "src/windows/common/WslSecurity.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslSecurity.cpp\n\nAbstract:\n\n    This file contains WSL Core security function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"WslSecurity.h\"\n\nstd::unique_ptr<wsl::windows::common::security::privilege_context> wsl::windows::common::security::AcquirePrivilege(_In_ LPCWSTR privilegeName)\n{\n    // Open the token of the current process.\n    wil::unique_handle token;\n    THROW_IF_WIN32_BOOL_FALSE(::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token));\n\n    auto luid = EnableTokenPrivilege(token.get(), privilegeName);\n    return std::make_unique<wsl::windows::common::security::privilege_context>(std::move(token), luid);\n}\n\nstd::vector<std::unique_ptr<wsl::windows::common::security::privilege_context>> wsl::windows::common::security::AcquirePrivileges(\n    _In_ const std::vector<LPCWSTR>& privilegeNames)\n{\n    std::vector<std::unique_ptr<wsl::windows::common::security::privilege_context>> context;\n    for (const auto* name : privilegeNames)\n    {\n        context.emplace_back(AcquirePrivilege(name));\n    }\n\n    return context;\n}\n\nvoid wsl::windows::common::security::ApplyProcessMitigationPolicies()\n{\n    PROCESS_MITIGATION_DYNAMIC_CODE_POLICY codePolicy{};\n    codePolicy.AllowRemoteDowngrade = false;\n    codePolicy.AllowThreadOptOut = false;\n    codePolicy.ProhibitDynamicCode = true;\n    LOG_IF_WIN32_BOOL_FALSE(SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &codePolicy, sizeof(codePolicy)));\n\n    // Note: Enabling PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY::DisallowWin32kSystemCalls\n    // breaks the service initialization logic (CoInitializeSecurity fails).\n\n    PROCESS_MITIGATION_FONT_DISABLE_POLICY fontPolicy{};\n    fontPolicy.DisableNonSystemFonts = true;\n    LOG_IF_WIN32_BOOL_FALSE(SetProcessMitigationPolicy(ProcessFontDisablePolicy, &fontPolicy, sizeof(fontPolicy)));\n\n    PROCESS_MITIGATION_IMAGE_LOAD_POLICY loadPolicy{};\n    loadPolicy.PreferSystem32Images = true;\n    LOG_IF_WIN32_BOOL_FALSE(SetProcessMitigationPolicy(ProcessImageLoadPolicy, &loadPolicy, sizeof(loadPolicy)));\n}\n\nSECURITY_DESCRIPTOR wsl::windows::common::security::CreateSecurityDescriptor(_In_ PSID userSid)\n{\n    SECURITY_DESCRIPTOR sd{};\n    THROW_IF_WIN32_BOOL_FALSE(InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION));\n    THROW_IF_WIN32_BOOL_FALSE(SetSecurityDescriptorOwner(&sd, userSid, false));\n    return sd;\n}\n\nwil::unique_handle wsl::windows::common::security::CreateRestrictedToken(_In_ HANDLE token)\n{\n    // N.B. These operations must be done while impersonating the user to avoid\n    //      accidentally raising the integrity level.\n    auto runAsUser = wil::impersonate_token(token);\n\n    // Get the thread token with appropriate access rights.\n    wil::unique_handle newToken{};\n    THROW_IF_WIN32_BOOL_FALSE(::OpenThreadToken(\n        ::GetCurrentThread(), (TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY), TRUE, &newToken));\n\n    // Create a restricted token with only the SeChangeNotifyPrivilege privilege.\n    wil::unique_handle restrictedToken{};\n    THROW_IF_WIN32_BOOL_FALSE(::CreateRestrictedToken(newToken.get(), DISABLE_MAX_PRIVILEGE, 0, NULL, 0, NULL, 0, NULL, &restrictedToken));\n\n    // Drop the token down to medium integrity level.\n    union\n    {\n        SID sid;\n        BYTE buffer[SECURITY_SID_SIZE(1)];\n    } sidBuffer;\n    SID_IDENTIFIER_AUTHORITY systemSidAuthority = SECURITY_MANDATORY_LABEL_AUTHORITY;\n    THROW_IF_NTSTATUS_FAILED(::RtlInitializeSidEx(&sidBuffer.sid, &systemSidAuthority, 1, SECURITY_MANDATORY_MEDIUM_RID));\n\n    // Set the integrity level to untrusted.\n    TOKEN_MANDATORY_LABEL tokenLabel{};\n    tokenLabel.Label.Attributes = SE_GROUP_INTEGRITY;\n    tokenLabel.Label.Sid = &sidBuffer.sid;\n    THROW_IF_WIN32_BOOL_FALSE(::SetTokenInformation(\n        restrictedToken.get(), TokenIntegrityLevel, &tokenLabel, (sizeof(tokenLabel) + ::GetLengthSid(&sidBuffer.sid))));\n\n    return restrictedToken;\n}\n\nLUID wsl::windows::common::security::EnableTokenPrivilege(_Inout_ HANDLE token, _In_ LPCWSTR privilegeName)\n{\n    // Convert privilege name to an LUID.\n    LUID luid{};\n    THROW_IF_WIN32_BOOL_FALSE(::LookupPrivilegeValueW(nullptr, privilegeName, &luid));\n\n    TOKEN_PRIVILEGES newState{};\n    newState.PrivilegeCount = 1;\n    newState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\n    newState.Privileges[0].Luid = luid;\n    THROW_IF_WIN32_BOOL_FALSE(::AdjustTokenPrivileges(token, FALSE, &newState, 0, nullptr, nullptr));\n\n    return luid;\n}\n\nDWORD wsl::windows::common::security::GetUserBasicIntegrityLevel(_In_ HANDLE token)\n{\n    // Get the integrity level.\n    const auto label = wil::get_token_information<TOKEN_MANDATORY_LABEL>(token);\n    DWORD BasicIntegrityLevel =\n        *GetSidSubAuthority(label->Label.Sid, static_cast<UCHAR>(*::GetSidSubAuthorityCount(label->Label.Sid) - 1));\n\n    // Convert the range of medium integrity level to a single level.\n    if ((BasicIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID) && (BasicIntegrityLevel < SECURITY_MANDATORY_HIGH_RID))\n    {\n        BasicIntegrityLevel = SECURITY_MANDATORY_MEDIUM_RID;\n    }\n\n    return BasicIntegrityLevel;\n}\n\nbool wsl::windows::common::security::IsTokenElevated(_In_ HANDLE token)\n{\n    return (GetUserBasicIntegrityLevel(token) == SECURITY_MANDATORY_HIGH_RID);\n}\n\nwil::unique_handle wsl::windows::common::security::GetUserToken(_In_ TOKEN_TYPE tokenType, _In_ RPC_BINDING_HANDLE handle)\n{\n    wil::unique_handle contextToken;\n\n    // Start by impersonating the caller and getting their token-user data out.\n    {\n        std::variant<int, wil::unique_coreverttoself_call, unique_revert_to_self> runAsClient;\n\n        if (handle == nullptr)\n        {\n            runAsClient = wil::CoImpersonateClient();\n        }\n        else\n        {\n            runAsClient = RpcImpersonateCaller(handle);\n        }\n\n        THROW_IF_WIN32_BOOL_FALSE(::OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_READ, TRUE, &contextToken));\n    }\n\n    wil::unique_handle newToken;\n    THROW_IF_WIN32_BOOL_FALSE(::DuplicateTokenEx(\n        contextToken.get(), TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, nullptr, SecurityImpersonation, tokenType, &newToken));\n\n    return newToken;\n}\n\nbool wsl::windows::common::security::IsTokenLocalSystem(_In_opt_ HANDLE token)\n{\n    auto [sid, sidBuffer] = wsl::windows::common::security::CreateSid(SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID);\n\n    BOOL member{};\n    THROW_IF_WIN32_BOOL_FALSE(::CheckTokenMembership(token, sid, &member));\n\n    return member ? true : false;\n}\n\nwsl::windows::common::security::unique_revert_to_self wsl::windows::common::security::RpcImpersonateCaller(_In_ RPC_BINDING_HANDLE handle)\n{\n    THROW_IF_WIN32_ERROR(static_cast<DWORD>(RpcImpersonateClient(handle)));\n\n    return {};\n}\n"
  },
  {
    "path": "src/windows/common/WslSecurity.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslSecurity.h\n\nAbstract:\n\n    This file contains WSL Core security function declarations.\n\n--*/\n\n#pragma once\n\n#include <xstring>\n#include <optional>\n#include \"wrl/client.h\"\n#include \"wil/resource.h\"\n#include \"Redirector.h\"\n\nnamespace wsl::windows::common::security {\nusing unique_revert_to_self = wil::unique_call<decltype(&::RpcRevertToSelf), ::RpcRevertToSelf>;\n\nusing unique_acl = wil::unique_any<ACL*, decltype(&::LocalFree), ::LocalFree>;\n\nstruct privilege_context\n{\n    privilege_context() = delete;\n    privilege_context(const privilege_context&) = delete;\n\n    privilege_context(wil::unique_handle&& token, LUID luid) : token(std::move(token)), luid(luid)\n    {\n    }\n\n    ~privilege_context()\n    {\n        // Disable the privilege.\n        if (token)\n        {\n            TOKEN_PRIVILEGES newState{};\n            newState.PrivilegeCount = 1;\n            newState.Privileges[0].Attributes = 0;\n            newState.Privileges[0].Luid = luid;\n            LOG_IF_WIN32_BOOL_FALSE(::AdjustTokenPrivileges(token.get(), FALSE, &newState, 0, nullptr, nullptr));\n        }\n    }\n\n    wil::unique_handle token;\n    LUID luid;\n};\n\n/// <summary>\n/// Acquires the specified privilege on the current process token.\n/// </summary>\nstd::unique_ptr<privilege_context> AcquirePrivilege(_In_ LPCWSTR privilegeName);\n\n/// <summary>\n/// Acquires the specified privileges on the current process token.\n/// </summary>\nstd::vector<std::unique_ptr<privilege_context>> AcquirePrivileges(_In_ const std::vector<LPCWSTR>& privilegeNames);\n\n/// <summary>\n/// Apply process mitigation policies to current process.\n/// </summary>\nvoid ApplyProcessMitigationPolicies();\n\n/// <summary>\n/// Creates a security descriptor from the provided user sid.\n/// </summary>\nSECURITY_DESCRIPTOR CreateSecurityDescriptor(_In_ PSID userSid);\n\ntemplate <typename... TArgs>\nstd::pair<PSID, std::vector<char>> CreateSid(SID_IDENTIFIER_AUTHORITY Authority, TArgs... values)\n{\n    std::vector<char> buffer(SECURITY_SID_SIZE(sizeof...(TArgs)));\n    auto* sid = reinterpret_cast<PSID>(buffer.data());\n\n    THROW_IF_NTSTATUS_FAILED(RtlInitializeSidEx(sid, &Authority, sizeof...(TArgs), std::forward<TArgs>(values)...));\n\n    return std::make_pair(sid, std::move(buffer));\n}\n\n/// <summary>\n/// Creates a restricted token from the provided token.\n/// </summary>\nwil::unique_handle CreateRestrictedToken(_In_ HANDLE token);\n\n/// <summary>\n/// Enables a privilege on the token.\n/// </summary>\nLUID EnableTokenPrivilege(_Inout_ HANDLE token, _In_ LPCWSTR privilegeName);\n\n/// <summary>\n/// Returns the basic integrity level for provided token.\n/// </summary>\nDWORD GetUserBasicIntegrityLevel(_In_ HANDLE token);\n\n/// <summary>\n/// Returns the user token for the current client.\n/// </summary>\nwil::unique_handle GetUserToken(_In_ TOKEN_TYPE tokenType, _In_ RPC_BINDING_HANDLE handle = nullptr);\n\n/// <summary>\n/// Queries if the provided token is elevated.\n/// </summary>\nbool IsTokenElevated(_In_ HANDLE token);\n\n/// <summary>\n/// Returns true if the provided token is a member of the localsystem group\n/// </summary>\nbool IsTokenLocalSystem(_In_opt_ HANDLE token);\n\n/// <summary>\n/// Impersonates the RPC caller\n/// </summary>\nunique_revert_to_self RpcImpersonateCaller(_In_ RPC_BINDING_HANDLE handle);\n} // namespace wsl::windows::common::security\n"
  },
  {
    "path": "src/windows/common/WslTelemetry.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslTelemetry.cpp\n\nAbstract:\n\n    This file contains tracing function definitions.\n\n--*/\n\n#include \"wslservice.h\"\n#include \"ExecutionContext.h\"\n#include \"WslTelemetry.h\"\n\n#define WSL_TELEMETRY_KEYWORDS (MICROSOFT_KEYWORD_CRITICAL_DATA | MICROSOFT_KEYWORD_MEASURES | MICROSOFT_KEYWORD_TELEMETRY)\n\nTRACELOGGING_DEFINE_PROVIDER(\n    LxssTelemetryProvider,\n    \"Microsoft.Windows.Subsystem.Lxss\",\n    // {d90b9468-67f0-5b3b-42cc-82ac81ffd960}\n    (0xd90b9468, 0x67f0, 0x5b3b, 0x42, 0xcc, 0x82, 0xac, 0x81, 0xff, 0xd9, 0x60),\n    TraceLoggingOptionMicrosoftTelemetry());\n\nTRACELOGGING_DEFINE_PROVIDER(\n    WslServiceTelemetryProvider,\n    \"Microsoft.Windows.Lxss.Manager\",\n    // {b99cdb5a-039c-5046-e672-1a0de0a40211}\n    (0xb99cdb5a, 0x039c, 0x5046, 0xe6, 0x72, 0x1a, 0x0d, 0xe0, 0xa4, 0x02, 0x11),\n    TraceLoggingOptionMicrosoftTelemetry());\n\n#ifdef DEBUG\n#define HRESULT_STRING_VALUE \\\n    , TraceLoggingValue(wsl::windows::common::wslutil::ErrorCodeToString(failure->hr).c_str(), \"HRESULTString\")\n#else\n#define HRESULT_STRING_VALUE\n#endif\n\nTraceLoggingHProvider g_hTraceLoggingProvider;\n\nnamespace WslTraceLoggingInternal {\nstatic bool g_disableTelemetryByDefault = true;\nstatic std::atomic<long> g_ClientsWithTelemetryEnabled = 0;\nstatic std::atomic<long> g_ClientsWithTelemetryDisabled = 0;\n} // namespace WslTraceLoggingInternal\n\nWslTraceLoggingClient::WslTraceLoggingClient(bool TelemetryEnabled) : m_clientTelemetryEnabled(TelemetryEnabled)\n{\n    if (m_clientTelemetryEnabled)\n    {\n        ++WslTraceLoggingInternal::g_ClientsWithTelemetryEnabled;\n    }\n    else\n    {\n        ++WslTraceLoggingInternal::g_ClientsWithTelemetryDisabled;\n    }\n}\n\nWslTraceLoggingClient::~WslTraceLoggingClient()\n{\n    if (m_clientTelemetryEnabled)\n    {\n        --WslTraceLoggingInternal::g_ClientsWithTelemetryEnabled;\n        WI_ASSERT(WslTraceLoggingInternal::g_ClientsWithTelemetryEnabled >= 0);\n    }\n    else\n    {\n        --WslTraceLoggingInternal::g_ClientsWithTelemetryDisabled;\n        WI_ASSERT(WslTraceLoggingInternal::g_ClientsWithTelemetryDisabled >= 0);\n    }\n}\n\nvoid WslTraceLoggingInitialize(_In_ const TraceLoggingHProvider provider, _In_ BOOLEAN DisableTelemetryByDefault, _In_ std::optional<TLG_PENABLECALLBACK> callback)\n{\n    WI_ASSERT(!g_hTraceLoggingProvider);\n\n    WslTraceLoggingInternal::g_disableTelemetryByDefault = (DisableTelemetryByDefault != FALSE);\n    g_hTraceLoggingProvider = provider;\n\n    if (callback.has_value())\n    {\n        TraceLoggingRegisterEx(g_hTraceLoggingProvider, callback.value(), nullptr);\n    }\n    else\n    {\n        TraceLoggingRegister(g_hTraceLoggingProvider);\n    }\n\n    wil::g_pfnResultLoggingCallback = [](wil::FailureInfo* failure, PWSTR, size_t) WI_NOEXCEPT {\n        if (failure->type == wil::FailureType::Exception || failure->type == wil::FailureType::Return)\n        {\n            wsl::windows::common::ExecutionContext::CollectError(failure->hr);\n        }\n\n        if (g_hTraceLoggingProvider == LxssTelemetryProvider)\n        {\n            // Do not log telemetry for the internal invalid command line argument\n            // error.\n\n            if (failure->hr == WSL_E_INVALID_USAGE)\n            {\n                return;\n            }\n\n            switch (failure->type)\n            {\n            case wil::FailureType::Exception:\n            case wil::FailureType::FailFast:\n                WSL_LOG(\n                    \"LxssException\",\n                    TraceLoggingLevel(WINEVENT_LEVEL_ERROR),\n                    TraceLoggingValue(failure->pszFile, \"File\"),\n                    TraceLoggingValue(failure->pszFunction, \"FunctionName\"),\n                    TraceLoggingValue(failure->uLineNumber, \"Line number\"),\n                    TraceLoggingValue(static_cast<DWORD>(failure->type), \"Type\"),\n                    TraceLoggingHexUInt32(failure->hr, \"HRESULT\"),\n                    TraceLoggingValue(failure->pszMessage, \"Message\"),\n                    TraceLoggingValue(failure->pszCode, \"Code\") HRESULT_STRING_VALUE);\n\n                break;\n\n            default:\n                WSL_LOG(\n                    \"LxssVerboseLog\",\n                    TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),\n                    TraceLoggingValue(failure->pszFile, \"File\"),\n                    TraceLoggingValue(failure->pszFunction, \"FunctionName\"),\n                    TraceLoggingValue(failure->uLineNumber, \"Line number\"),\n                    TraceLoggingValue(static_cast<DWORD>(failure->type), \"Type\"),\n                    TraceLoggingHexUInt32(failure->hr, \"HRESULT\"),\n                    TraceLoggingValue(failure->pszMessage, \"Message\"),\n                    TraceLoggingValue(failure->pszCode, \"Code\") HRESULT_STRING_VALUE);\n\n                break;\n            }\n        }\n        else\n        {\n            switch (failure->type)\n            {\n            case wil::FailureType::Exception:\n            case wil::FailureType::FailFast:\n                WSL_LOG(\n                    \"Error\",\n                    TraceLoggingLevel(WINEVENT_LEVEL_ERROR),\n                    TraceLoggingValue(failure->pszFile, \"file\"),\n                    TraceLoggingValue(failure->uLineNumber, \"linenumber\"),\n                    TraceLoggingValue(static_cast<DWORD>(failure->type), \"type\"),\n                    TraceLoggingValue(failure->cFailureCount, \"failurecount\"),\n                    TraceLoggingValue(GetCurrentThreadId(), \"threadid\"),\n                    TraceLoggingHexUInt32(failure->hr, \"hr\"),\n                    TraceLoggingValue(failure->pszMessage, \"message\"),\n                    TraceLoggingValue(failure->pszCode, \"code\"),\n                    TraceLoggingValue(failure->pszFunction, \"function\") HRESULT_STRING_VALUE);\n\n                break;\n\n            default:\n                WSL_LOG(\n                    \"VerboseLog\",\n                    TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),\n                    TraceLoggingValue(failure->pszFile, \"file\"),\n                    TraceLoggingValue(failure->uLineNumber, \"linenumber\"),\n                    TraceLoggingValue(failure->cFailureCount, \"failurecount\"),\n                    TraceLoggingValue(GetCurrentThreadId(), \"threadid\"),\n                    TraceLoggingHexUInt32(failure->hr, \"hr\"),\n                    TraceLoggingValue(failure->pszMessage, \"message\"),\n                    TraceLoggingValue(failure->pszCode, \"code\"),\n                    TraceLoggingValue(failure->pszFunction, \"function\") HRESULT_STRING_VALUE);\n\n                break;\n            }\n        }\n    };\n}\n\nvoid WslTraceLoggingUninitialize()\n{\n    wil::g_pfnResultLoggingCallback = nullptr;\n    TraceLoggingUnregister(g_hTraceLoggingProvider);\n}\n\nbool WslTraceLoggingShouldDisableTelemetry() noexcept\n{\n    return (WslTraceLoggingInternal::g_ClientsWithTelemetryDisabled > 0) ||\n           (WslTraceLoggingInternal::g_disableTelemetryByDefault && (WslTraceLoggingInternal::g_ClientsWithTelemetryEnabled == 0));\n}"
  },
  {
    "path": "src/windows/common/WslTelemetry.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslTelemetry.h\n\nAbstract:\n\n    This file contains tracing function declarations.\n\n--*/\n\n#pragma once\n\n#include <windows.h>\n#include <winmeta.h>\n#include <evntprov.h>\n#include <wil/resource.h>\n#include <type_traits>\n#include <TraceLoggingProvider.h>\n#include <TraceLoggingActivity.h>\n#include \"traceloggingconfig.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nTRACELOGGING_DECLARE_PROVIDER(LxssTelemetryProvider);\nTRACELOGGING_DECLARE_PROVIDER(WslServiceTelemetryProvider);\n#ifdef __cplusplus\n}\n#endif\n\nextern TraceLoggingHProvider g_hTraceLoggingProvider;\n\n// Initialize tracelogging for the binary.\n//     DisableTelemetryByDefault - In scenarios where there are no active users, assume telemetry is not allowed.\n//                                 This is used in conjunction with WslTraceLoggingClient to represent active clients.\n//     ForceDropPII - Make sure all events are tagged with MICROSOFT_EVENTTAG_DROP_PII.\n//                    WARNING: This will mean backend tools like devicedrill will not work. This should be a temporary setting.\nvoid WslTraceLoggingInitialize(_In_ TraceLoggingHProvider, _In_ BOOLEAN DisableTelemetryByDefault, _In_ std::optional<TLG_PENABLECALLBACK> callback = {});\n\nvoid WslTraceLoggingUninitialize();\n\nbool WslTraceLoggingShouldDisableTelemetry() noexcept;\n\n#ifdef __cplusplus\nclass WslTraceLoggingClient\n{\npublic:\n    WslTraceLoggingClient(bool TelemetryEnabled);\n    ~WslTraceLoggingClient();\n\nprivate:\n    bool m_clientTelemetryEnabled;\n};\n\n#endif\n\n#define WSL_LOG(Name, ...) TraceLoggingWrite(g_hTraceLoggingProvider, Name, __VA_ARGS__)\n\n#define WSL_LOG_DEBUG(Name, ...) \\\n    if constexpr (wsl::shared::Debug) \\\n    { \\\n        WSL_LOG(Name, __VA_ARGS__); \\\n    }\n\n#define WSL_LOG_TELEMETRY(Name, Tag, ...) \\\n    TraceLoggingWrite( \\\n        g_hTraceLoggingProvider, \\\n        Name, \\\n        TraceLoggingValue(WSL_PACKAGE_VERSION, \"wslVersion\"), \\\n        TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \\\n        TelemetryPrivacyDataTag(Tag), \\\n        __VA_ARGS__);\n"
  },
  {
    "path": "src/windows/common/disk.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    disk.cpp\n\nAbstract:\n\n    This file contains disk functions implementations.\n\n--*/\n\n#include \"precomp.h\"\n#include <diskguid.h>\n\n#include \"disk.hpp\"\n#include \"wslutil.h\"\n\nwil::unique_hfile wsl::windows::common::disk::OpenDevice(_In_ LPCWSTR Name, _In_ DWORD Access, size_t TimeoutMs)\n{\n    auto openDevice = [&]() {\n        wil::unique_hfile handle{\n            CreateFileW(Name, Access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, 0, nullptr)};\n\n        THROW_LAST_ERROR_IF(!handle);\n\n        return handle;\n    };\n\n    // E_ACCESSDENIED is returned if the disk is in use, which can happen when the disk has just been detached and is being\n    // attached to the host again. Retry for 5 seconds before failing.\n\n    return wsl::shared::retry::RetryWithTimeout<wil::unique_hfile>(\n        openDevice, c_diskOperationRetry, std::chrono::milliseconds(TimeoutMs), []() {\n            const auto error = wil::ResultFromCaughtException();\n            return error == E_ACCESSDENIED || error == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);\n        });\n}\n\nbool wsl::windows::common::disk::IsDiskOnline(_In_ HANDLE Disk)\n{\n    GET_DISK_ATTRIBUTES attributes = {0};\n    Ioctl(Disk, IOCTL_DISK_GET_DISK_ATTRIBUTES, nullptr, 0, &attributes, sizeof(attributes));\n\n    return !WI_IsFlagSet(attributes.Attributes, DISK_ATTRIBUTE_OFFLINE);\n}\n\nvoid wsl::windows::common::disk::Ioctl(\n    _In_ HANDLE Disk, _In_ DWORD Code, _In_opt_ LPVOID InData, _In_ DWORD InDataSize, _Out_opt_ LPVOID OutData, _In_ DWORD OutDataSize)\n{\n    DWORD bytesReturned = 0;\n    THROW_LAST_ERROR_IF(!DeviceIoControl(Disk, Code, InData, InDataSize, OutData, OutDataSize, &bytesReturned, nullptr));\n}\n\nvoid wsl::windows::common::disk::LockVolume(_In_ HANDLE Disk)\n{\n    Ioctl(Disk, FSCTL_LOCK_VOLUME);\n}\n\nvoid wsl::windows::common::disk::SetOnline(_In_ HANDLE Disk, _In_ bool Online, _In_ size_t TimeoutMs)\n{\n    // Lock and unmount all the volumes contained in the disk.\n    // This is done to make sure than the disk is not in use before setting\n    // the offline attribute (which doesn't fail if the disk is in use)\n\n    if (!Online)\n    {\n        const auto volumes = ListDiskVolumes(Disk);\n\n        // Lock all the volumes first so that we we're confident\n        // that FSCTL_DISMOUNT_VOLUME won't fail\n        // There's no need to unlock the volumes here as this is done when the handle is closed\n\n        for (const auto& e : volumes)\n        {\n            try\n            {\n                wsl::shared::retry::RetryWithTimeout<void>(\n                    std::bind(&LockVolume, e.second.get()), c_diskOperationRetry, std::chrono::milliseconds(TimeoutMs), []() {\n                        return wil::ResultFromCaughtException() == E_ACCESSDENIED;\n                    });\n            }\n            catch (...)\n            {\n                // FSCTL_LOCK_VOLUME returns access denied if the disk is in use.\n                // Let's make the error a bit better for the user\n\n                THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_DRIVE_LOCKED), wil::ResultFromCaughtException() == E_ACCESSDENIED);\n                throw;\n            }\n        }\n\n        for (const auto& e : volumes)\n        {\n            Ioctl(e.second.get(), FSCTL_DISMOUNT_VOLUME);\n        }\n    }\n\n    SET_DISK_ATTRIBUTES attributes = {0};\n    attributes.Version = sizeof(attributes);\n    attributes.AttributesMask = DISK_ATTRIBUTE_OFFLINE;\n    attributes.Attributes = Online ? 0 : DISK_ATTRIBUTE_OFFLINE;\n    Ioctl(Disk, IOCTL_DISK_SET_DISK_ATTRIBUTES, &attributes, sizeof(attributes));\n}\n\nDWORD\nwsl::windows::common::disk::GetDiskNumber(_In_ HANDLE Disk)\n{\n    STORAGE_DEVICE_NUMBER DiskNumber;\n    Ioctl(Disk, IOCTL_STORAGE_GET_DEVICE_NUMBER, nullptr, 0, &DiskNumber, sizeof(DiskNumber));\n\n    return DiskNumber.DeviceNumber;\n}\n\nstd::map<std::wstring, wil::unique_hfile> wsl::windows::common::disk::ListDiskVolumes(_In_ HANDLE Disk)\n{\n    ValidateDiskVolumesAreReady(Disk);\n\n    size_t partitionCount = 16;\n    std::vector<char> buffer;\n\n    for (;;)\n    {\n        buffer.resize(offsetof(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) + partitionCount * sizeof(PARTITION_INFORMATION_EX));\n\n        if (!DeviceIoControl(Disk, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, nullptr, 0, buffer.data(), gsl::narrow_cast<DWORD>(buffer.size()), nullptr, nullptr))\n        {\n            THROW_LAST_ERROR_IF(GetLastError() != ERROR_INSUFFICIENT_BUFFER);\n            THROW_IF_FAILED(SizeTMult(partitionCount, 2, &partitionCount));\n        }\n        else\n        {\n            break;\n        }\n    }\n\n    const auto* partitions = reinterpret_cast<PDRIVE_LAYOUT_INFORMATION_EX>(buffer.data());\n    std::vector<DWORD> partitionNumbers;\n    for (const auto& partition : gsl::make_span(partitions->PartitionEntry, partitions->PartitionCount))\n    {\n        if (partition.PartitionStyle == PARTITION_STYLE_MBR && partition.Mbr.PartitionType != PARTITION_ENTRY_UNUSED &&\n            partition.Mbr.PartitionType != PARTITION_SPACES && partition.Mbr.PartitionType != PARTITION_EXTENDED &&\n            partition.Mbr.PartitionType != PARTITION_XINT13_EXTENDED)\n        {\n            partitionNumbers.emplace_back(partition.PartitionNumber);\n        }\n        else if (\n            partition.PartitionStyle == PARTITION_STYLE_GPT && partition.Gpt.PartitionType != PARTITION_ENTRY_UNUSED_GUID &&\n            partition.Gpt.PartitionType != PARTITION_SPACES_GUID)\n        {\n            partitionNumbers.emplace_back(partition.PartitionNumber);\n        }\n\n        // If the partition scheme is neither MBR nor GPT, then it's RAW, which means\n        // that Windows doesn't recognize it.\n    }\n\n    auto diskNumber = GetDiskNumber(Disk);\n\n    auto transform = [diskNumber](DWORD partitionNumber) {\n        auto path = std::format(L\"\\\\\\\\?\\\\Harddisk{}Partition{}\", diskNumber, partitionNumber);\n        return std::make_pair(std::move(path), OpenDevice(path.c_str(), GENERIC_ALL));\n    };\n\n    std::map<std::wstring, wil::unique_hfile> output;\n    std::transform(partitionNumbers.begin(), partitionNumbers.end(), std::inserter(output, output.begin()), transform);\n\n    return output;\n}\n\nvoid wsl::windows::common::disk::ValidateDiskVolumesAreReady(_In_ HANDLE Disk)\n{\n    // Will throw if the disk is not ready\n    Ioctl(Disk, IOCTL_DISK_ARE_VOLUMES_READY);\n}\n"
  },
  {
    "path": "src/windows/common/disk.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    disk.hpp\n\nAbstract:\n\n    This file contains disk functions declarations.\n\n--*/\n\n#pragma once\n\nnamespace wsl::windows::common::disk {\n\nconstexpr inline auto c_diskOperationRetry = std::chrono::milliseconds(500);\n\nwil::unique_hfile OpenDevice(_In_ LPCWSTR Name, _In_ DWORD Access = GENERIC_READ, _In_ size_t TimeoutMs = 5 * 1000);\n\nbool IsDiskOnline(_In_ HANDLE Disk);\n\nvoid SetOnline(_In_ HANDLE Disk, _In_ bool Online, _In_ size_t TimeoutMs = 5 * 1000);\n\nvoid LockVolume(_In_ HANDLE Disk);\n\nvoid Ioctl(_In_ HANDLE Disk, _In_ DWORD Code, _In_opt_ LPVOID InData = nullptr, _In_ DWORD InDataSize = 0, _Out_opt_ LPVOID OutData = nullptr, _In_ DWORD OutDataSize = 0);\n\nstd::map<std::wstring, wil::unique_hfile> ListDiskVolumes(_In_ HANDLE Disk);\n\nstd::vector<std::wstring> GetVolumeDevices(_In_ HANDLE Volume);\n\nDWORD\nGetDiskNumber(_In_ HANDLE Disk);\n\nstd::vector<std::wstring> ListDiskPartitions(_In_ HANDLE Disk);\n\nvoid ValidateDiskVolumesAreReady(_In_ HANDLE Disk);\n} // namespace wsl::windows::common::disk\n"
  },
  {
    "path": "src/windows/common/filesystem.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    filesystem.cpp\n\nAbstract:\n\n    This file contains file system function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"filesystem.hpp\"\n\n#define FULL_PATH_PREFIX L\"\\\\\\\\?\\\\\"\n#define LXSS_DOMAIN_NAME_DEFAULT \"localdomain\"\n#define LXSS_EA_BUFFER_INCREMENT_SIZE 4096\n\nnamespace {\n\nenum CaseSensitivity\n{\n    Invalid,\n    Disabled,\n    Enabled\n};\n\nconstexpr const wchar_t* c_fileSystemKeyName = L\"System\\\\CurrentControlSet\\\\Control\\\\FileSystem\";\nconstexpr const wchar_t* c_enableDirCaseSensitivityValue = L\"NtfsEnableDirCaseSensitivity\";\nconstexpr DWORD c_enableDirCaseSensitivity = 0x1;\nconstexpr DWORD c_enableDirCaseSensitivityEmptyDirOnly = 0x2;\n\nstd::vector<char> CreateMetaDataEaBuffer(LX_UID_T Uid, LX_GID_T Gid, LX_MODE_T Mode)\n{\n    constexpr auto c_nameSize = (RTL_NUMBER_OF(LX_FILE_METADATA_UID_EA_NAME) - 1);\n\n    static_assert(RTL_NUMBER_OF(LX_FILE_METADATA_UID_EA_NAME) == RTL_NUMBER_OF(LX_FILE_METADATA_GID_EA_NAME));\n    static_assert(RTL_NUMBER_OF(LX_FILE_METADATA_UID_EA_NAME) == RTL_NUMBER_OF(LX_FILE_METADATA_MODE_EA_NAME));\n\n    // Simplified version of FILE_FULL_EA_INFORMATION since the names have constant sizes.\n    // See: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_file_full_ea_information\n\n#pragma pack(push, 1)\n    struct EA_ENTRY\n    {\n        ULONG NextEntryOffset;\n        UCHAR Flags;\n        UCHAR EaNameLength;\n        USHORT EaValueLength;\n        CHAR EaName[c_nameSize];\n        char padding[1];\n        ULONG EaValue{};\n        char padding2[1];\n    };\n#pragma pack(pop)\n\n    static_assert(sizeof(EA_ENTRY) == 20);\n\n    std::vector<char> buffer;\n    __unaligned EA_ENTRY* currentEntry = nullptr;\n\n    auto writeEntry = [&currentEntry, &buffer](const std::string_view& Name, ULONG Value) {\n        WI_ASSERT(Name.size() == c_nameSize);\n\n        const auto currentOffset = buffer.size();\n        buffer.resize(currentOffset + sizeof(EA_ENTRY));\n\n        currentEntry = reinterpret_cast<EA_ENTRY*>(buffer.data() + currentOffset);\n        currentEntry->NextEntryOffset = sizeof(EA_ENTRY);\n        currentEntry->EaNameLength = c_nameSize; // Does not include null terminator.\n        currentEntry->EaValueLength = sizeof(ULONG);\n        currentEntry->EaValue = Value;\n        std::copy(Name.begin(), Name.end(), &currentEntry->EaName[0]);\n    };\n\n    if (Uid != LX_UID_INVALID)\n    {\n        writeEntry(LX_FILE_METADATA_UID_EA_NAME, Uid);\n    }\n\n    if (Gid != LX_GID_INVALID)\n    {\n        writeEntry(LX_FILE_METADATA_GID_EA_NAME, Gid);\n    }\n\n    if (Mode != LX_MODE_INVALID)\n    {\n        writeEntry(LX_FILE_METADATA_MODE_EA_NAME, Mode);\n    }\n\n    if (currentEntry != nullptr)\n    {\n        currentEntry->NextEntryOffset = 0;\n    }\n\n    WI_ASSERT((buffer.size() % sizeof(EA_ENTRY)) == 0);\n\n    return buffer;\n}\n\nvoid CopyFileWithMetadata(_In_ PCWSTR Source, _In_ PCWSTR Destination, _In_ ULONG Mode, _In_ ULONG DistroVersion)\n{\n    //\n    // Impersonate the client, copy the file, and write the extended attributes.\n    //\n\n    {\n        auto runAsUser = wil::CoImpersonateClient();\n        THROW_IF_WIN32_BOOL_FALSE(CopyFileW(Source, Destination, FALSE));\n\n        //\n        // Apply DrvFs-style attributes for instances using WslFs; otherwise,\n        // use the old LxFs-style attributes.\n        //\n\n        const wil::unique_hfile file{CreateFileW(\n            Destination, GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};\n\n        IO_STATUS_BLOCK ioStatus{};\n\n        //\n        // Write the extended attributes.\n        //\n\n        if (LXSS_DISTRO_USES_WSL_FS(DistroVersion) != FALSE)\n        {\n            auto buffer = CreateMetaDataEaBuffer(LX_UID_ROOT, LX_GID_ROOT, Mode);\n            THROW_IF_NTSTATUS_FAILED(ZwSetEaFile(file.get(), &ioStatus, buffer.data(), static_cast<ULONG>(buffer.size())));\n        }\n        else\n        {\n            LX_FILE_ATTRIBUTES_EA LxFs{};\n            LX_FILE_ATTRIBUTES_EA_INITIALIZE(&LxFs);\n            LxFs.Attributes.Mode = Mode;\n\n            THROW_IF_NTSTATUS_FAILED(ZwSetEaFile(file.get(), &ioStatus, &LxFs, sizeof(LxFs)));\n        }\n    }\n}\n\nDWORD GetNtfsDirCaseSensitivityFlags()\n{\n    return wsl::windows::common::registry::ReadDword(HKEY_LOCAL_MACHINE, c_fileSystemKeyName, c_enableDirCaseSensitivityValue, 0);\n}\n\nvoid SetNtfsDirCaseSensitivityFlags(_In_ ULONG Flags)\n{\n    //\n    // The service is already impersonating when this is used, and the user\n    // likely doesn't have permission to set this key, so temporarily revert\n    // impersonation.\n    //\n\n    auto RunAsSelf = wil::run_as_self();\n    wsl::windows::common::registry::WriteDword(HKEY_LOCAL_MACHINE, c_fileSystemKeyName, c_enableDirCaseSensitivityValue, Flags);\n}\n\nCaseSensitivity GetCaseSensitivity()\n{\n    ULONG caseSensitiveRaw;\n    THROW_IF_NTSTATUS_FAILED(::NtQueryInformationThread(\n        GetCurrentThread(), ThreadExplicitCaseSensitivity, &caseSensitiveRaw, sizeof(caseSensitiveRaw), nullptr));\n\n    return (caseSensitiveRaw == 0) ? Disabled : Enabled;\n}\n\nNTSTATUS SetCaseSensitivity(_In_ CaseSensitivity value)\n{\n    ULONG caseSensitiveRaw;\n    switch (value)\n    {\n    case Disabled:\n        caseSensitiveRaw = 0;\n        break;\n\n    case Enabled:\n        caseSensitiveRaw = 1;\n        break;\n\n    default:\n        return STATUS_INVALID_PARAMETER;\n    }\n\n    return NtSetInformationThread(GetCurrentThread(), ThreadExplicitCaseSensitivity, &caseSensitiveRaw, sizeof(caseSensitiveRaw));\n}\n\nusing revert_dir_case_sensitivity =\n    wil::unique_any<DWORD, decltype(&SetNtfsDirCaseSensitivityFlags), SetNtfsDirCaseSensitivityFlags, wil::details::pointer_access_none, DWORD, DWORD, DWORD_MAX, DWORD>;\n\nusing revert_case_sensitivity =\n    wil::unique_any<CaseSensitivity, decltype(&SetCaseSensitivity), SetCaseSensitivity, wil::details::pointer_access_none, CaseSensitivity, CaseSensitivity, Invalid, CaseSensitivity>;\n\nrevert_dir_case_sensitivity EnableNtfsDirCaseSensitivity()\n{\n    auto Flags = GetNtfsDirCaseSensitivityFlags();\n    auto NewFlags = Flags;\n    WI_SetFlag(NewFlags, c_enableDirCaseSensitivity);\n    WI_ClearFlag(NewFlags, c_enableDirCaseSensitivityEmptyDirOnly);\n\n    //\n    // Check if a change needs to be made.\n    //\n\n    if (Flags == NewFlags)\n    {\n        return {};\n    }\n\n    SetNtfsDirCaseSensitivityFlags(NewFlags);\n\n    //\n    // Just in case, make sure at least the main enable flag is set after\n    // reverting; otherwise, WSL will break.\n    //\n\n    WI_SetFlag(Flags, c_enableDirCaseSensitivity);\n    return revert_dir_case_sensitivity{Flags};\n}\n\nrevert_case_sensitivity EnableCaseSensitivity()\n{\n    CaseSensitivity oldCaseSensitive = GetCaseSensitivity();\n    THROW_IF_NTSTATUS_FAILED(SetCaseSensitivity(CaseSensitivity::Enabled));\n    return revert_case_sensitivity(oldCaseSensitive);\n}\n\nbool HasReadAccessToDrive(wchar_t drive)\n{\n    // Note: Using GetFileSecurity / AccessCheck doesn't work if the user doesn't have access\n    // to a drive (for instance the EFI partition), since the ACL returned by GetFileSecurity\n    // allows read access to Everyone.\n    // Using FindFirstFile guarantees that the user actually has read access to that drive.\n\n    const wchar_t path[] = {drive, ':', '\\\\', '*', '\\0'};\n\n    WIN32_FIND_DATAW findData{};\n    const wil::unique_hfind find{FindFirstFileW(path, &findData)};\n\n    return !!find;\n}\n\nvoid EnsureCaseSensitiveDirectoryRecursive(_In_ HANDLE Directory)\n{\n    FILE_CASE_SENSITIVE_INFORMATION CaseInfo{};\n    IO_STATUS_BLOCK IoStatus{};\n    std::vector<std::byte> buffer{sizeof(FILE_ID_BOTH_DIR_INFORMATION) + MAX_PATH};\n    bool restart = true;\n\n    while (true)\n    {\n        const auto result = NtQueryDirectoryFile(\n            Directory,\n            nullptr,\n            nullptr,\n            nullptr,\n            &IoStatus,\n            buffer.data(),\n            static_cast<DWORD>(buffer.size()),\n            static_cast<FILE_INFORMATION_CLASS>(FileIdBothDirectoryInformation),\n            true,\n            nullptr,\n            restart);\n\n        WI_ASSERT(result != STATUS_PENDING);\n\n        if (result == STATUS_NO_MORE_FILES || result == STATUS_NO_SUCH_FILE)\n        {\n            break;\n        }\n        else if (result == STATUS_BUFFER_OVERFLOW)\n        {\n            buffer.resize(buffer.size() * 2);\n            continue;\n        }\n\n        THROW_IF_NTSTATUS_FAILED(result);\n\n        restart = false;\n\n        const auto* information = reinterpret_cast<const FILE_ID_BOTH_DIR_INFORMATION*>(buffer.data());\n\n        //\n        // Only process non-reparse point directories.\n        //\n        // N.B. Nothing needs to be done for files.\n        //\n\n        if ((WI_IsFlagSet(information->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) &&\n            (WI_IsFlagClear(information->FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT)))\n        {\n\n            //\n            // Skip the . and .. entries.\n            //\n\n            const auto name = std::wstring_view(&information->FileName[0], information->FileNameLength / sizeof(wchar_t));\n            if (name == L\".\" || name == L\"..\")\n            {\n                continue;\n            }\n\n            UNICODE_STRING Name{};\n            RtlInitUnicodeString(&Name, information->FileName);\n\n            auto Child = wsl::windows::common::filesystem::OpenRelativeFile(\n                Directory,\n                &Name,\n                (FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE),\n                FILE_OPEN,\n                (FILE_OPEN_REPARSE_POINT | FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT));\n\n            THROW_IF_NTSTATUS_FAILED(NtQueryInformationFile(Child.get(), &IoStatus, &CaseInfo, sizeof(CaseInfo), FileCaseSensitiveInformation));\n\n            //\n            // Skip if the directory already has the flag.\n            //\n\n            if (WI_IsFlagClear(CaseInfo.Flags, FILE_CS_FLAG_CASE_SENSITIVE_DIR))\n            {\n                EnsureCaseSensitiveDirectoryRecursive(Child.get());\n            }\n        }\n    }\n\n    //\n    // After all children are processed, mark the directory case-sensitive.\n    //\n    // N.B. This is done with a retry because if the NtfsEnableDirCaseSensitivity\n    //      flag was just changed from 3 to 1, NTFS may not have updated its\n    //      behavior yet in which case it will fail with STATUS_DIRECTORY_NOT_EMPTY.\n    //\n\n    CaseInfo.Flags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;\n    wsl::shared::retry::RetryWithTimeout<void>(\n        [&]() {\n            THROW_IF_NTSTATUS_FAILED(NtSetInformationFile(Directory, &IoStatus, &CaseInfo, sizeof(CaseInfo), FileCaseSensitiveInformation));\n        },\n        std::chrono::milliseconds{100},\n        std::chrono::seconds{1},\n        []() { return wil::ResultFromCaughtException() == HRESULT_FROM_NT(STATUS_DIRECTORY_NOT_EMPTY); });\n}\n\nvoid SetDirectoryCaseSensitive(_In_ PCWSTR Path)\n{\n    const wil::unique_hfile Directory{CreateFileW(\n        Path,\n        FILE_WRITE_ATTRIBUTES,\n        (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),\n        nullptr,\n        OPEN_EXISTING,\n        (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT),\n        nullptr)};\n\n    IO_STATUS_BLOCK IoStatus;\n    FILE_CASE_SENSITIVE_INFORMATION CaseInfo;\n    CaseInfo.Flags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;\n    THROW_IF_NTSTATUS_FAILED(NtSetInformationFile(Directory.get(), &IoStatus, &CaseInfo, sizeof(CaseInfo), FileCaseSensitiveInformation));\n}\n\nvoid SetExtendedAttributesLxFs(_In_ PCWSTR Path, _In_ ULONG Mode, _In_ ULONG Uid, _In_ ULONG Gid)\n{\n    const wil::unique_hfile FileHandle(::CreateFileW(\n        Path,\n        FILE_GENERIC_READ | FILE_GENERIC_WRITE,\n        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\n        nullptr,\n        OPEN_EXISTING,\n        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,\n        nullptr));\n\n    THROW_LAST_ERROR_IF(!FileHandle);\n\n    LX_FILE_ATTRIBUTES_EA AttributesEa;\n    IO_STATUS_BLOCK IoStatusBlock;\n    const NTSTATUS Status = wsl::windows::common::filesystem::QuerySingleEaFileNoThrow(\n        FileHandle.get(), &IoStatusBlock, LX_FILE_ATTRIBUTES_NAME, &AttributesEa, sizeof(AttributesEa));\n\n    //\n    // If the attributes exist and are valid, leave them alone. Users can\n    // change the attributes on a root inode (e.g. with chmod/chown) and those\n    // changes should not be overwritten.\n    //\n\n    if ((NT_SUCCESS(Status)) && (IoStatusBlock.Information == sizeof(AttributesEa)) &&\n        (AttributesEa.u.EaInformation.EaValueLength == sizeof(AttributesEa.Attributes)) &&\n        (AttributesEa.Attributes.u.Flags.Version == LX_FILE_ATTRIBUTES_CURRENT_VERSION))\n    {\n        return;\n    }\n\n    LX_FILE_ATTRIBUTES_EA_INITIALIZE(&AttributesEa);\n    AttributesEa.Attributes.Uid = Uid;\n    AttributesEa.Attributes.Gid = Gid;\n    AttributesEa.Attributes.Mode = Mode;\n    THROW_IF_NTSTATUS_FAILED(ZwSetEaFile(FileHandle.get(), &IoStatusBlock, &AttributesEa, sizeof(AttributesEa)));\n}\n\nvoid SetExtendedAttributesDrvFs(_In_ PCWSTR Path, _In_ ULONG Mode, _In_ ULONG Uid, _In_ ULONG Gid)\n{\n    const wil::unique_hfile FileHandle{::CreateFileW(\n        Path,\n        FILE_GENERIC_READ | FILE_GENERIC_WRITE,\n        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\n        nullptr,\n        OPEN_EXISTING,\n        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,\n        nullptr)};\n\n    THROW_LAST_ERROR_IF(!FileHandle);\n\n    //\n    // Use FILE_STAT_LX_INFORMATION as an easy way to determine what attributes\n    // the file already has.\n    //\n\n    FILE_STAT_LX_INFORMATION Info;\n    IO_STATUS_BLOCK IoStatus;\n    THROW_IF_NTSTATUS_FAILED(NtQueryInformationFile(FileHandle.get(), &IoStatus, &Info, sizeof(Info), FileStatLxInformation));\n\n    LX_UID_T UidToSet = LX_UID_INVALID;\n    LX_GID_T GidToSet = LX_GID_INVALID;\n    LX_MODE_T ModeToSet = LX_MODE_INVALID;\n    bool NeedUpdate = false;\n    if (WI_IsFlagClear(Info.LxFlags, LX_FILE_METADATA_HAS_UID))\n    {\n        UidToSet = Uid;\n        NeedUpdate = true;\n    }\n\n    if (WI_IsFlagClear(Info.LxFlags, LX_FILE_METADATA_HAS_GID))\n    {\n        GidToSet = Gid;\n        NeedUpdate = true;\n    }\n\n    if (WI_IsFlagClear(Info.LxFlags, LX_FILE_METADATA_HAS_MODE))\n    {\n        ModeToSet = Mode;\n        NeedUpdate = true;\n    }\n\n    if (NeedUpdate != false)\n    {\n        auto buffer = CreateMetaDataEaBuffer(UidToSet, GidToSet, ModeToSet);\n        IO_STATUS_BLOCK IoStatus{};\n\n        THROW_IF_NTSTATUS_FAILED_MSG(\n            ZwSetEaFile(FileHandle.get(), &IoStatus, buffer.data(), static_cast<DWORD>(buffer.size())), \"%ls\", Path);\n    }\n}\n\nvoid SetExtendedAttributes(_In_ PCWSTR Path, _In_ ULONG Mode, _In_ ULONG Uid, _In_ ULONG Gid, _In_ ULONG DistroVersion)\n{\n    //\n    // Apply DrvFs-style attributes for instances using WslFs; otherwise, use\n    // the old LxFs-style attributes.\n    //\n\n    if (LXSS_DISTRO_USES_WSL_FS(DistroVersion) != FALSE)\n    {\n        SetExtendedAttributesDrvFs(Path, Mode, Uid, Gid);\n    }\n    else\n    {\n        SetExtendedAttributesLxFs(Path, Mode, Uid, Gid);\n    }\n}\n\n} // namespace\n\nwsl::windows::common::filesystem::TempFile::TempFile(\n    _In_ DWORD DesiredAccess, _In_ DWORD ShareMode, _In_ DWORD CreationDisposition, _In_ TempFileFlags Flags, _In_opt_ std::wstring_view Extension) :\n    Flags(Flags)\n{\n    Path = GetTempFilename();\n    if (!Extension.empty())\n    {\n        Path.replace_extension(Extension);\n    }\n\n    LPSECURITY_ATTRIBUTES SecurityAttributes{};\n    SECURITY_ATTRIBUTES Attributes = {sizeof(SECURITY_ATTRIBUTES), nullptr, true};\n    if (WI_IsFlagSet(Flags, TempFileFlags::InheritHandle))\n    {\n        SecurityAttributes = &Attributes;\n    }\n\n    DWORD FlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY;\n    WI_SetFlagIf(FlagsAndAttributes, FILE_FLAG_DELETE_ON_CLOSE, WI_IsFlagSet(Flags, TempFileFlags::DeleteOnClose));\n    Handle.reset(CreateFileW(Path.c_str(), DesiredAccess, ShareMode, SecurityAttributes, CreationDisposition, FlagsAndAttributes, nullptr));\n    THROW_LAST_ERROR_IF(!Handle);\n}\n\nwsl::windows::common::filesystem::TempFile::~TempFile()\n{\n    // If the delete on close flag is not set, close the handle and delete the file.\n    if (!Path.empty() && WI_IsFlagClear(Flags, TempFileFlags::DeleteOnClose))\n    {\n        Handle.reset();\n        LOG_IF_WIN32_BOOL_FALSE(DeleteFileW(Path.c_str()));\n    }\n}\n\nwsl::windows::common::filesystem::unique_lxss_addmount wsl::windows::common::filesystem::CreateMount(\n    _In_ PCWSTR NtPath, _In_ PCWSTR Source, _In_opt_ LPCSTR Target, _In_ LPCSTR FsType, _In_ ULONG Mode, _In_ bool forWrite)\n{\n    unique_lxss_addmount mount = {};\n    mount.WindowsDataRoot = OpenDirectoryHandle(NtPath, forWrite).release();\n    mount.Source =\n        wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(wsl::shared::string::WideToMultiByte(Source).c_str()).release();\n    if (ARGUMENT_PRESENT(Target))\n    {\n        mount.Target = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(Target).release();\n    }\n\n    mount.FsType = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(FsType).release();\n    mount.MountFlags = LX_MS_NOATIME;\n    WI_SetFlagIf(mount.MountFlags, LX_MS_RDONLY, !forWrite);\n    mount.Mode = Mode;\n    mount.Uid = LX_UID_ROOT;\n    mount.Gid = LX_GID_ROOT;\n    return mount;\n}\n\nvoid wsl::windows::common::filesystem::CreateRootFs(_In_ PCWSTR Path, _In_ ULONG Version)\n{\n    //\n    // Declare a scope exit variable to clean up on failure.\n    //\n\n    bool deleteRootFs = false;\n    const wil::unique_handle userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n        if (deleteRootFs)\n        {\n            auto runAsUser = wil::impersonate_token(userToken.get());\n            LOG_IF_FAILED(wil::RemoveDirectoryRecursiveNoThrow(Path));\n        }\n    });\n\n    //\n    // Create the rootfs directory while impersonating the user, fail if the\n    // directory already exists.\n    //\n    // N.B. Throw ERROR_FILE_EXISTS instead of ERROR_ALREADY_EXISTS for consistent\n    //      error messages with WSL2.\n    //\n\n    {\n        auto runAsUser = wil::impersonate_token(userToken.get());\n        if (!CreateDirectoryW(Path, nullptr))\n        {\n            const auto lastError = GetLastError();\n            THROW_WIN32_MSG(lastError == ERROR_ALREADY_EXISTS ? ERROR_FILE_EXISTS : lastError, \"CreateDirectoryW\");\n        }\n\n        deleteRootFs = true;\n        SetExtendedAttributes(Path, (LX_S_IFDIR | 0755), LX_UID_ROOT, LX_GID_ROOT, Version);\n    }\n\n    //\n    // Make sure the directory is marked case-sensitive.\n    //\n    // N.B. This is done without impersonating the client because setting this\n    //      attribute requires the \"delete subfolders and files\" permission on\n    //      the parent directory.\n    //\n\n    SetDirectoryCaseSensitive(Path);\n    cleanup.release();\n}\n\n// Sends an ioctl to a device, and waits for the result.\nvoid wsl::windows::common::filesystem::DeviceIoControl(_In_ HANDLE handle, _In_ ULONG code, _In_ gsl::span<const gsl::byte> input)\n{\n    THROW_IF_NTSTATUS_FAILED(DeviceIoControlNoThrow(handle, code, input));\n}\n\n// Sends an ioctl to a device, and waits for the result.\nNTSTATUS\nwsl::windows::common::filesystem::DeviceIoControlNoThrow(_In_ HANDLE handle, _In_ ULONG code, _In_ gsl::span<const gsl::byte> input)\n{\n    PVOID inputBuffer{};\n    if (input.size() > 0)\n    {\n        inputBuffer = const_cast<gsl::byte*>(input.data());\n    }\n\n    IO_STATUS_BLOCK ioStatus;\n    wil::unique_event event;\n    event.create();\n    NTSTATUS status = NtDeviceIoControlFile(\n        handle, event.get(), nullptr, nullptr, &ioStatus, code, inputBuffer, gsl::narrow_cast<ULONG>(input.size()), nullptr, 0);\n\n    if (status == STATUS_PENDING)\n    {\n        event.wait();\n        status = ioStatus.Status;\n    }\n\n    return status;\n}\n\nstd::pair<ULONG, ULONG> wsl::windows::common::filesystem::EnumerateFixedDrives(HANDLE Token)\n{\n    std::variant<wil::unique_coreverttoself_call, wil::unique_token_reverter> runAsUser;\n\n    if (Token == nullptr)\n    {\n        runAsUser = wil::CoImpersonateClient();\n    }\n    else\n    {\n        runAsUser = wil::impersonate_token(Token);\n    }\n\n    ULONG fixedDriveBitmap = GetLogicalDrives();\n    ULONG driveBitmap = fixedDriveBitmap;\n    ULONG index = 0;\n    ULONG nonReadableDrives = 0;\n    wchar_t drivePath[] = L\"A:\\\\\";\n    while (driveBitmap != 0)\n    {\n        WI_VERIFY(_BitScanForward(&index, driveBitmap) != FALSE);\n\n        const ULONG driveMask = (1 << index);\n        driveBitmap ^= driveMask;\n        const auto driveName = static_cast<wchar_t>(L'A' + index);\n        drivePath[0] = driveName;\n        if (GetDriveTypeW(drivePath) != DRIVE_FIXED)\n        {\n            // Don't try to check if the user has read access to non-fixed drives.\n            // This can cause a hang for network devices. See https://github.com/microsoft/WSL/issues/11460 .\n            fixedDriveBitmap ^= driveMask;\n            continue;\n        }\n\n        if (!HasReadAccessToDrive(driveName))\n        {\n            nonReadableDrives |= driveMask;\n        }\n    }\n\n    return {fixedDriveBitmap & ~nonReadableDrives, nonReadableDrives};\n}\n\nvoid wsl::windows::common::filesystem::EnsureCaseSensitiveDirectory(_In_ PCWSTR Path, _In_ ULONG Flags)\n{\n    // N.B. Passing SYNCHRONIZE and FILE_SYNCHRONOUS_IO_NONALERT is required; otherwise, NtQueryDirectoryFile\n    // might return STATUS_PENDING, which would break our folder enumeration logic.\n\n    const wil::unique_hfile Directory{CreateFileW(\n        Path,\n        (FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE),\n        (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),\n        nullptr,\n        OPEN_EXISTING,\n        (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT),\n        nullptr)};\n\n    FILE_CASE_SENSITIVE_INFORMATION CaseInfo;\n    QueryInformationFile(Directory.get(), CaseInfo, FileCaseSensitiveInformation);\n\n    //\n    // Because upgrading is done depth-first, if the directory already has the\n    // flag all its children must too; this allows checking for upgrade at\n    // every start with low cost, and resuming of interrupted upgrades.\n    //\n\n    if (WI_IsFlagSet(CaseInfo.Flags, FILE_CS_FLAG_CASE_SENSITIVE_DIR))\n    {\n        return;\n    }\n\n    //\n    // Abort if upgrading is not allowed.\n    //\n\n    if (WI_IsFlagClear(Flags, LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE))\n    {\n        THROW_HR(WSL_E_FS_UPGRADE_NEEDED);\n    }\n\n    //\n    // Enable per-thread case sensitivity on the thread.\n    //\n    // N.B. This requires the service is running as PPL. The lifted service will\n    //      return an error in this case but this is a legacy upgrade path for\n    //      WSL distributions that have not been launched since RS3. This logic\n    //      should be refactored in the lifted service to not require per-thread\n    //      case sensitivity\n    //\n\n    revert_case_sensitivity revertCase;\n    if (WI_IsFlagClear(Flags, c_case_sensitive_folders_only))\n    {\n        auto runAsSelf = wil::run_as_self();\n        auto revertPrivilege = wsl::windows::common::security::AcquirePrivilege(SE_DEBUG_NAME);\n        revertCase = EnableCaseSensitivity();\n    }\n\n    //\n    // Upgrading requires that setting the per-directory case sensitivity flag\n    // is allowed on non-empty directories, which requires changing the\n    // registry.\n    //\n    // N.B. This change is reverted after the operation is complete.\n    //\n\n    auto dirCaseSensitivity = EnableNtfsDirCaseSensitivity();\n    EnsureCaseSensitiveDirectoryRecursive(Directory.get());\n}\n\nbool wsl::windows::common::filesystem::EnsureDirectory(_In_ LPCWSTR pPath)\n{\n    //\n    // Return true if a new directory is created.\n    //\n\n    if (CreateDirectoryW(pPath, nullptr))\n    {\n        return true;\n    }\n\n    //\n    // Return false if the directory existed.\n    //\n\n    const auto lastError = GetLastError();\n    if (lastError == ERROR_ALREADY_EXISTS)\n    {\n        return false;\n    }\n    else if (lastError == ERROR_PATH_NOT_FOUND)\n    {\n        wil::CreateDirectoryDeep(pPath);\n    }\n    else\n    {\n        THROW_WIN32_MSG(lastError, \"CreateDirectoryW(%ls)\", pPath);\n    }\n\n    return true;\n}\n\nvoid wsl::windows::common::filesystem::EnsureDirectoryWithAttributes(\n    _In_ PCWSTR Path, _In_ ULONG Mode, _In_ ULONG Uid, _In_ ULONG Gid, _In_ ULONG Flags, _In_ ULONG DistroVersion)\n{\n    const bool newDirectory = EnsureDirectory(Path);\n    SetExtendedAttributes(Path, LX_S_IFDIR | Mode, Uid, Gid, DistroVersion);\n\n    //\n    // Mark a new directory case-sensitive, or upgrade the entire tree if it\n    // exists. If the root is already case-sensitive, it's assumed the entire\n    // tree is.\n    //\n\n    if (newDirectory)\n    {\n        SetDirectoryCaseSensitive(Path);\n    }\n    else\n    {\n        EnsureCaseSensitiveDirectory(Path, Flags);\n    }\n}\n\nbool wsl::windows::common::filesystem::FileExists(_In_ LPCWSTR Path)\n{\n    const DWORD Attributes = GetFileAttributesW(Path);\n    return (Attributes != INVALID_FILE_ATTRIBUTES);\n}\n\nstd::filesystem::path wsl::windows::common::filesystem::GetFullPath(_In_ LPCWSTR Path)\n{\n    DWORD Attributes = GetFileAttributesW(Path);\n    THROW_LAST_ERROR_IF(Attributes == INVALID_FILE_ATTRIBUTES);\n\n    const wil::unique_hfile Handle(CreateFileW(\n        Path,\n        GENERIC_READ,\n        (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),\n        nullptr,\n        OPEN_EXISTING,\n        (WI_IsFlagSet(Attributes, FILE_ATTRIBUTE_DIRECTORY) ? FILE_FLAG_BACKUP_SEMANTICS : FILE_ATTRIBUTE_NORMAL),\n        nullptr));\n\n    THROW_LAST_ERROR_IF(!Handle);\n\n    std::wstring FullPath;\n    THROW_IF_FAILED(wil::GetFinalPathNameByHandleW(Handle.get(), FullPath));\n\n    return std::filesystem::path(std::move(FullPath));\n}\n\nstd::pair<std::string, std::string> wsl::windows::common::filesystem::GetHostAndDomainNames()\n{\n    std::string hostName = GetLinuxHostName();\n\n    DWORD size = 0;\n    WI_VERIFY(GetComputerNameExA(ComputerNameDnsDomain, nullptr, &size) == FALSE);\n\n    // If there is no domain name, initialize with a default. Truncate the\n    // domain name to the max size that the driver allows.\n    // N.B. If the buffer is too small, GetComputerNameEx() sets 'size' to the string size,\n    // ** including ** the null terminator. On success it returns the string size,\n    // See: https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexa\n\n    std::string domainName{};\n    if (size <= 1)\n    {\n        domainName = LXSS_DOMAIN_NAME_DEFAULT;\n    }\n    else\n    {\n        domainName.resize(size - 1, L'\\0');\n        THROW_LAST_ERROR_IF(!GetComputerNameExA(ComputerNameDnsDomain, domainName.data(), &size));\n        WI_ASSERT(domainName.size() == size);\n\n        if (domainName.size() > LX_DOMAIN_NAME_MAX)\n        {\n            domainName.resize(LX_DOMAIN_NAME_MAX);\n        }\n    }\n\n    return {std::move(hostName), std::move(domainName)};\n}\n\nstd::filesystem::path wsl::windows::common::filesystem::GetLegacyBasePath(_In_ HANDLE UserToken)\n{\n    return GetLocalAppDataPath(UserToken) / L\"lxss\";\n}\n\nstd::string wsl::windows::common::filesystem::GetLinuxHostName()\n{\n    DWORD size = 0;\n    WI_VERIFY(GetComputerNameExA(ComputerNamePhysicalDnsHostname, nullptr, &size) == FALSE);\n    std::string hostName(size, '\\0');\n    THROW_LAST_ERROR_IF(!GetComputerNameExA(ComputerNamePhysicalDnsHostname, hostName.data(), &size));\n\n    WI_ASSERT((size <= LX_HOST_NAME_MAX) && (hostName.size() == size + 1));\n\n    return wsl::shared::string::CleanHostname(hostName);\n}\n\nstd::filesystem::path wsl::windows::common::filesystem::GetLocalAppDataPath(_In_ HANDLE userToken)\n{\n    return GetKnownFolderPath(FOLDERID_LocalAppData, (KF_FLAG_CREATE | KF_FLAG_NO_APPCONTAINER_REDIRECTION), userToken);\n}\n\nstd::filesystem::path wsl::windows::common::filesystem::GetKnownFolderPath(const KNOWNFOLDERID& id, DWORD flags, HANDLE token)\n{\n    wil::unique_cotaskmem_string path;\n    THROW_IF_FAILED(::SHGetKnownFolderPath(id, flags, token, &path));\n\n    return std::filesystem::path(path.get());\n}\n\nstd::filesystem::path wsl::windows::common::filesystem::GetTempFilename()\n{\n    WCHAR Path[MAX_PATH + 1];\n    std::wstring File(MAX_PATH + 1, L'\\0');\n    THROW_LAST_ERROR_IF(GetTempPathW(ARRAYSIZE(Path), Path) == 0);\n    THROW_LAST_ERROR_IF(GetTempFileNameW(Path, L\"lx\", 0, File.data()) == 0);\n    File.resize(wcsnlen(File.c_str(), File.size()));\n    return std::filesystem::path(std::move(File));\n}\n\nstd::filesystem::path wsl::windows::common::filesystem::GetTempFolderPath(_In_ HANDLE userToken)\n{\n    return GetLocalAppDataPath(userToken) / L\"temp\";\n}\n\nstd::string wsl::windows::common::filesystem::GetWindowsHosts(const std::filesystem::path& Path)\n{\n    std::ifstream Stream(Path.c_str());\n    THROW_HR_IF_MSG(E_FAIL, (Stream.bad() || !Stream.is_open()), \"errno = %d\", errno);\n\n    // Discard any BOM header.\n    int potentialHeader[] = {Stream.get(), Stream.get(), Stream.get()};\n    if (potentialHeader[0] != 0xEF || potentialHeader[1] != 0xBB || potentialHeader[2] != 0xBF)\n    {\n        Stream.seekg(0); // Reset the position to beginning of the file if no BOM header is found.\n    }\n\n    std::string WindowsHosts;\n    std::string Line;\n    while (std::getline(Stream, Line))\n    {\n        // Ignore all text after comment characters.\n\n        const size_t Comment = Line.find_first_of('#');\n        if (Comment != std::string::npos)\n        {\n            Line.resize(Comment);\n        }\n\n        if (Line.size() == 0)\n        {\n            continue;\n        }\n\n        // Create a copy of the line since the string tokenizing API is\n        // destructive.\n\n        std::string LineCopy = Line;\n\n        // Each line is in the following format:\n        // <host-address> <host-alias1> <host-alias2> ...\n        //\n        // N.B. There must be at least one host aliases for each host address.\n\n        std::string CurrentEntry;\n        PCHAR ElementContext = nullptr;\n        PCHAR Element = strtok_s(&LineCopy[0], \" \\t\\r\\n\", &ElementContext);\n        while (Element != nullptr)\n        {\n            CurrentEntry.append(Element);\n            Element = strtok_s(nullptr, \" \\t\\r\\n\", &ElementContext);\n            if (Element != nullptr)\n            {\n                CurrentEntry.append(\"\\t\");\n            }\n            else\n            {\n                CurrentEntry.append(\"\\n\");\n                WindowsHosts.append(CurrentEntry);\n            }\n        }\n    }\n\n    WI_ASSERT(Stream.eof());\n\n    return WindowsHosts;\n}\n\nwil::unique_hfile wsl::windows::common::filesystem::OpenDirectoryHandle(_In_ LPCWSTR pPath, _In_ bool forWrite)\n{\n    wil::unique_hfile handle(OpenDirectoryHandleNoThrow(pPath, forWrite));\n    THROW_LAST_ERROR_IF(!handle);\n\n    return handle;\n}\n\nwil::unique_hfile wsl::windows::common::filesystem::OpenDirectoryHandleNoThrow(_In_ LPCWSTR pPath, _In_ bool forWrite)\n{\n    DWORD AccessMask = FILE_GENERIC_READ | FILE_GENERIC_EXECUTE;\n    if (forWrite)\n    {\n        WI_SetAllFlags(AccessMask, FILE_GENERIC_WRITE);\n    }\n\n    wil::unique_hfile handle(CreateFileW(\n        pPath, AccessMask, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr));\n\n    return handle;\n}\n\nwil::unique_hfile wsl::windows::common::filesystem::OpenNulDevice(_In_ DWORD DesiredAccess)\n{\n    wil::unique_hfile nulDevice{CreateFileW(\n        L\"nul\", DesiredAccess, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};\n\n    THROW_LAST_ERROR_IF(!nulDevice);\n\n    return nulDevice;\n}\n\nwil::unique_hfile wsl::windows::common::filesystem::OpenRelativeFile(\n    _In_opt_ HANDLE Parent,\n    _In_ PUNICODE_STRING RelativePath,\n    _In_ ACCESS_MASK DesiredAccess,\n    _In_ ULONG Disposition,\n    _In_ ULONG CreateOptions,\n    _In_opt_ PVOID EaBuffer,\n    _In_ ULONG EaSize)\n\n{\n    auto [Status, File] = OpenRelativeFileNoThrow(Parent, RelativePath, DesiredAccess, Disposition, CreateOptions, EaBuffer, EaSize);\n    THROW_IF_NTSTATUS_FAILED_MSG(Status, \"Path: %.*ls\", RelativePath->Length, RelativePath->Buffer);\n\n    return std::move(File);\n}\n\nstd::pair<NTSTATUS, wil::unique_hfile> wsl::windows::common::filesystem::OpenRelativeFileNoThrow(\n    _In_opt_ HANDLE Parent,\n    _In_ PUNICODE_STRING RelativePath,\n    _In_ ACCESS_MASK DesiredAccess,\n    _In_ ULONG Disposition,\n    _In_ ULONG CreateOptions,\n    _In_opt_ PVOID EaBuffer,\n    _In_ ULONG EaSize)\n\n{\n    OBJECT_ATTRIBUTES Attributes;\n    InitializeObjectAttributes(&Attributes, RelativePath, 0, Parent, nullptr);\n    wil::unique_hfile File;\n    IO_STATUS_BLOCK IoStatus;\n    NTSTATUS Status = NtCreateFile(\n        &File, DesiredAccess, &Attributes, &IoStatus, nullptr, 0, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), Disposition, CreateOptions, EaBuffer, EaSize);\n\n    return std::make_pair(Status, std::move(File));\n}\n\nwil::unique_hfile wsl::windows::common::filesystem::ReopenFile(_In_ HANDLE Handle, _In_ ACCESS_MASK DesiredAccess, _In_ ULONG CreateOptions)\n{\n    UNICODE_STRING Empty;\n    RtlInitUnicodeString(&Empty, L\"\");\n    return OpenRelativeFile(Handle, &Empty, DesiredAccess, FILE_OPEN, CreateOptions);\n}\n\nvoid wsl::windows::common::filesystem::QueryInformationFile(\n    _In_ HANDLE Handle, _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_ FILE_INFORMATION_CLASS FileInformationClass)\n{\n    IO_STATUS_BLOCK IoStatus;\n    THROW_IF_NTSTATUS_FAILED(NtQueryInformationFile(Handle, &IoStatus, Buffer, Length, FileInformationClass));\n}\n\nVOID wsl::windows::common::filesystem::QuerySingleEaFile(\n    _In_ HANDLE Handle, _Out_ PIO_STATUS_BLOCK IoStatus, _In_ std::string_view EaName, _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length)\n{\n    THROW_IF_NTSTATUS_FAILED(QuerySingleEaFileNoThrow(Handle, IoStatus, EaName, Buffer, Length));\n}\n\nstd::vector<CHAR> wsl::windows::common::filesystem::QuerySingleEaFile(_In_ HANDLE Handle, _In_ std::string_view EaName)\n{\n    std::vector<CHAR> Buffer;\n    NTSTATUS Status;\n    IO_STATUS_BLOCK IoStatus;\n    ULONG Size = 0;\n    do\n    {\n        Size += LXSS_EA_BUFFER_INCREMENT_SIZE;\n        Buffer.resize(Size);\n        Status = QuerySingleEaFileNoThrow(Handle, &IoStatus, EaName, &Buffer[0], Size);\n    } while ((Status == STATUS_BUFFER_OVERFLOW) && (Size <= USHORT_MAX));\n\n    THROW_IF_NTSTATUS_FAILED(Status);\n\n    //\n    // Resize to the actual size of the attribute.\n    //\n\n    Buffer.resize(IoStatus.Information);\n    return Buffer;\n}\n\nNTSTATUS\nwsl::windows::common::filesystem::QuerySingleEaFileNoThrow(\n    _In_ HANDLE Handle, _Out_ PIO_STATUS_BLOCK IoStatus, _In_ std::string_view EaName, _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length)\n{\n    union\n    {\n        FILE_GET_EA_INFORMATION List;\n        CHAR Buffer[offsetof(FILE_GET_EA_INFORMATION, EaName) + UCHAR_MAX];\n    } EaList;\n\n    RtlZeroMemory(&EaList, sizeof(EaList));\n\n    WI_ASSERT(EaName.size() < UCHAR_MAX);\n\n    EaList.List.EaNameLength = static_cast<UCHAR>(EaName.size());\n    RtlCopyMemory(EaList.List.EaName, EaName.data(), EaName.size());\n    return ZwQueryEaFile(Handle, IoStatus, Buffer, Length, TRUE, &EaList, sizeof(EaList), nullptr, TRUE);\n}\n\nvoid wsl::windows::common::filesystem::SetInformationFile(\n    _In_ HANDLE Handle, _In_reads_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_ FILE_INFORMATION_CLASS FileInformationClass)\n{\n    IO_STATUS_BLOCK IoStatus;\n    THROW_IF_NTSTATUS_FAILED(NtSetInformationFile(Handle, &IoStatus, Buffer, Length, FileInformationClass));\n}\n\nstd::optional<std::filesystem::path> wsl::windows::common::filesystem::TryGetPathFromFileUrl(const std::wstring& Url)\n{\n    constexpr auto filePrefix = L\"file://\";\n\n    if (!Url.starts_with(filePrefix))\n    {\n        return {};\n    }\n\n    // Skip third '/', if any\n    auto startIndex = wcslen(filePrefix);\n    if (Url.size() > startIndex && Url[startIndex] == L'/')\n    {\n        startIndex++;\n    }\n\n    // Replace '/' with '\\', for convenience.\n    auto path = Url.substr(startIndex);\n    std::replace(path.begin(), path.end(), '/', '\\\\');\n\n    return path;\n}\n\nstd::wstring wsl::windows::common::filesystem::UnquotePath(_In_ LPCWSTR Path)\n{\n    std::wstring UnquotedPath{Path};\n\n    // N.B. PathUnquoteSpaces() returns false if no quotes were found. No error handling is needed.\n    PathUnquoteSpaces(UnquotedPath.data());\n    UnquotedPath.resize(wcslen(UnquotedPath.c_str()));\n\n    return UnquotedPath;\n}\n\nvoid wsl::windows::common::filesystem::UpdateInit(_In_ PCWSTR BasePath, _In_ ULONG DistroVersion)\n{\n    const auto source = wsl::windows::common::wslutil::GetBasePath() / L\"tools\" / L\"init\";\n    const auto dest = std::filesystem::path(BasePath) / LXSS_ROOTFS_DIRECTORY / L\"init\";\n    CopyFileWithMetadata(source.c_str(), dest.c_str(), (LX_S_IFREG | 0755), DistroVersion);\n}\n\nwil::unique_hfile wsl::windows::common::filesystem::WipeAndOpenDirectory(_In_ LPCWSTR pPath)\n{\n    const auto result = wil::RemoveDirectoryRecursiveNoThrow(pPath);\n    THROW_HR_IF(result, (result != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) && (result != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)));\n\n    EnsureDirectory(pPath);\n\n    return OpenDirectoryHandle(pPath, true);\n}\n"
  },
  {
    "path": "src/windows/common/filesystem.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    filesystem.hpp\n\nAbstract:\n\n    This file contains file system function declarations.\n\n--*/\n\n#pragma once\n\n#include \"wslservice.h\"\n\n#define LXSS_FS_TYPE_DRVFS \"drvfs\"\n#define LXSS_FS_TYPE_LXFS \"lxfs\"\n#define LXSS_FS_TYPE_SHAREFS \"sharefs\"\n#define LXSS_FS_TYPE_TMPFS \"tmpfs\"\n#define LXSS_FS_TYPE_WSLFS \"wslfs\"\n\nnamespace wsl::windows::common::filesystem {\n\nenum class TempFileFlags\n{\n    None = 0x0,\n    DeleteOnClose = 0x1,\n    InheritHandle = 0x2\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(TempFileFlags);\n\n// Used only in unit tests.\nconstexpr ULONG c_case_sensitive_folders_only = 0x100;\n\n// Make sure that the above flag doesn't conflict with create instance flags\nstatic_assert((LXSS_CREATE_INSTANCE_FLAGS_ALL & c_case_sensitive_folders_only) == 0);\n\nstruct TempFile\n{\n    std::filesystem::path Path;\n    wil::unique_hfile Handle;\n    TempFileFlags Flags = TempFileFlags::None;\n\n    TempFile(\n        _In_ DWORD DesiredAccess,\n        _In_ DWORD ShareMode,\n        _In_ DWORD CreationDisposition,\n        _In_ TempFileFlags Flags = TempFileFlags::None,\n        _In_opt_ std::wstring_view Extension = {});\n\n    ~TempFile();\n\n    TempFile(const TempFile&) = delete;\n    TempFile& operator=(const TempFile&) = delete;\n\n    TempFile(TempFile&& other) noexcept\n    {\n        *this = std::move(other);\n    }\n\n    TempFile& operator=(TempFile&& other) noexcept\n    {\n        std::swap(Path, other.Path);\n        std::swap(Handle, other.Handle);\n        std::swap(Flags, other.Flags);\n        return *this;\n    }\n};\n\ninline void FreeLXSS_ADDMOUNT(_Inout_opt_ PLX_KMAPPATHS_ADDMOUNT pMount)\n{\n    if (pMount)\n    {\n        if (pMount->Source)\n        {\n            CoTaskMemFree((LPVOID)pMount->Source);\n        }\n\n        if (pMount->Target)\n        {\n            CoTaskMemFree((LPVOID)pMount->Target);\n        }\n\n        if (pMount->FsType)\n        {\n            CoTaskMemFree((LPVOID)pMount->FsType);\n        }\n\n        if (pMount->WindowsDataRoot && (pMount->WindowsDataRoot != INVALID_HANDLE_VALUE))\n        {\n            CloseHandle(pMount->WindowsDataRoot);\n        }\n    }\n}\n\nusing unique_lxss_addmount = wil::unique_struct<LX_KMAPPATHS_ADDMOUNT, decltype(&FreeLXSS_ADDMOUNT), FreeLXSS_ADDMOUNT>;\n\n/// <summary>\n/// Creates a mount for instance creation.\n/// </summary>\nunique_lxss_addmount CreateMount(\n    _In_ PCWSTR NtPath, _In_ PCWSTR Source, _In_opt_ LPCSTR Target, _In_ LPCSTR FsType, _In_ ULONG Mode, _In_ bool forWrite = true);\n\n/// <summary>\n/// Creates a directory for the root file system.\n/// </summary>\nvoid CreateRootFs(_In_ PCWSTR Path, _In_ ULONG Version);\n\nvoid DeviceIoControl(_In_ HANDLE handle, _In_ ULONG code, _In_ gsl::span<const gsl::byte> input = {});\n\nNTSTATUS\nDeviceIoControlNoThrow(_In_ HANDLE handle, _In_ ULONG code, _In_ gsl::span<const gsl::byte> input = {});\n\nstd::pair<ULONG, ULONG> EnumerateFixedDrives(HANDLE Token = nullptr);\n\n/// <summary>\n/// Creates a directory with the given path if it does not exist. Throws if creating the directory\n/// failed.\n/// </summary>\nbool EnsureDirectory(_In_ LPCWSTR pPath);\n\n/// <summary>\n/// Marks every directory in a tree case-sensitive.\n/// </summary>\nvoid EnsureCaseSensitiveDirectory(_In_ PCWSTR Path, _In_ ULONG Flags);\n\n/// <summary>\n/// Creates a directory with the given path if it does not exist, and applies\n/// the specified attributes if the directory doesn't have any. Throws if\n/// creating the directory or applying the attributes failed.\n/// </summary>\nvoid EnsureDirectoryWithAttributes(_In_ PCWSTR Path, _In_ ULONG Mode, _In_ ULONG Uid, _In_ ULONG Gid, _In_ ULONG Flags, _In_ ULONG DistroVersion);\n\nbool FileExists(_In_ LPCWSTR Path);\n\nstd::filesystem::path GetFullPath(_In_ LPCWSTR Path);\n\nstd::pair<std::string, std::string> GetHostAndDomainNames();\n\nstd::string GetLinuxHostName();\n\n/// <summary>\n/// Gets the base path for legacy installs.\n/// </summary>\nstd::filesystem::path GetLegacyBasePath(_In_ HANDLE UserToken);\n\nstd::filesystem::path GetLocalAppDataPath(_In_ HANDLE userToken);\n\nstd::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id, DWORD flags, HANDLE token = nullptr);\n\nstd::filesystem::path GetTempFilename();\n\nstd::filesystem::path GetTempFolderPath(_In_ HANDLE userToken);\n\nstd::string GetWindowsHosts(const std::filesystem::path& Path);\n\n/// <summary>\n/// Opens a directory handle with read/execute, optionally also write, & full sharing. The path\n/// must exist and be a directory. Throws if the directory cannot be opened.\n/// </summary>\nwil::unique_hfile OpenDirectoryHandle(_In_ LPCWSTR pPath, _In_ bool forWrite);\n\n/// <summary>\n/// Opens a directory handle with read/execute, optionally also write, & full sharing. The path\n/// must exist and be a directory.\n/// </summary>\nwil::unique_hfile OpenDirectoryHandleNoThrow(_In_ LPCWSTR pPath, _In_ bool forWrite);\n\n/// <summary>\n/// Opens the null device.\n/// </summary>\nwil::unique_hfile OpenNulDevice(_In_ DWORD DesiredAccess);\n\nwil::unique_hfile OpenRelativeFile(\n    _In_opt_ HANDLE Parent,\n    _In_ PUNICODE_STRING RelativePath,\n    _In_ ACCESS_MASK DesiredAccess,\n    _In_ ULONG Disposition,\n    _In_ ULONG CreateOptions,\n    _In_opt_ PVOID EaBuffer = nullptr,\n    _In_ ULONG EaSize = 0);\n\nstd::pair<NTSTATUS, wil::unique_hfile> OpenRelativeFileNoThrow(\n    _In_opt_ HANDLE Parent,\n    _In_ PUNICODE_STRING RelativePath,\n    _In_ ACCESS_MASK DesiredAccess,\n    _In_ ULONG Disposition,\n    _In_ ULONG CreateOptions,\n    _In_opt_ PVOID EaBuffer = nullptr,\n    _In_ ULONG EaSize = 0);\n\nwil::unique_hfile ReopenFile(_In_ HANDLE Handle, _In_ ACCESS_MASK DesiredAccess, _In_ ULONG CreateOptions);\n\nvoid QueryInformationFile(_In_ HANDLE Handle, _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_ FILE_INFORMATION_CLASS FileInformationClass);\n\ntemplate <typename T>\nvoid QueryInformationFile(_In_ HANDLE Handle, _Out_ T& Buffer, _In_ FILE_INFORMATION_CLASS FileInformationClass)\n{\n    QueryInformationFile(Handle, &Buffer, sizeof(Buffer), FileInformationClass);\n}\n\nVOID QuerySingleEaFile(_In_ HANDLE Handle, _Out_ PIO_STATUS_BLOCK IoStatus, _In_ std::string_view EaName, _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length);\n\nstd::vector<CHAR> QuerySingleEaFile(_In_ HANDLE Handle, _In_ std::string_view EaName);\n\nNTSTATUS\nQuerySingleEaFileNoThrow(\n    _In_ HANDLE Handle, _Out_ PIO_STATUS_BLOCK IoStatus, _In_ std::string_view EaName, _Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length);\n\nvoid SetInformationFile(_In_ HANDLE Handle, _In_reads_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_ FILE_INFORMATION_CLASS FileInformationClass);\n\ntemplate <typename T>\nvoid SetInformationFile(_In_ HANDLE Handle, _In_ T& Buffer, _In_ FILE_INFORMATION_CLASS FileInformationClass)\n{\n    SetInformationFile(Handle, &Buffer, sizeof(Buffer), FileInformationClass);\n}\n\nstd::optional<std::filesystem::path> TryGetPathFromFileUrl(const std::wstring& Url);\n\nstd::wstring UnquotePath(_In_ LPCWSTR Path);\n\n/// <summary>\n/// Updates the init binary.\n/// </summary>\nvoid UpdateInit(_In_ PCWSTR BasePath, _In_ ULONG DistroVersion);\n\n/// <summary>\n/// Wipes out the directory with the given path if it exists, then creates it again and returns\n/// an open directory handle onto it.\n/// </summary>\nwil::unique_hfile WipeAndOpenDirectory(_In_ LPCWSTR pPath);\n\n} // namespace wsl::windows::common::filesystem\n"
  },
  {
    "path": "src/windows/common/hcs.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    hcs.cpp\n\nAbstract:\n\n    This file contains helper function definitions for interacting with the\n    host compute service.\n\n--*/\n\n#include \"precomp.h\"\n#include \"hcs.hpp\"\n#include <ComputeCore.h>\n\n#pragma hdrstop\n\nusing wsl::windows::common::Context;\nusing wsl::windows::common::ExecutionContext;\n\nconstexpr auto c_processorCapabilities = \"ProcessorCapabilities\";\nconstexpr LPCWSTR c_processorCapabilitiesQuery = L\"{ \\\"PropertyQueries\\\": {\\\"ProcessorCapabilities\\\" : {}}}\";\nconstexpr LPCWSTR c_scsiResourcePath = L\"VirtualMachine/Devices/Scsi/0/Attachments/\";\n\nvoid wsl::windows::common::hcs::AddPlan9Share(\n    _In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR Name, _In_ PCWSTR AccessName, _In_ PCWSTR Path, _In_ UINT32 Port, _In_ Plan9ShareFlags Flags, _In_opt_ HANDLE UserToken)\n{\n    ModifySettingRequest<Plan9Share> request{};\n    request.RequestType = ModifyRequestType::Add;\n    request.ResourcePath = L\"VirtualMachine/Devices/Plan9/Shares\";\n    request.Settings.Name = Name;\n    request.Settings.AccessName = AccessName;\n    request.Settings.Path = Path;\n    request.Settings.Port = Port;\n    WI_SetFlagIf(Flags, Plan9ShareFlags::UseShareRootIdentity, ARGUMENT_PRESENT(UserToken));\n    request.Settings.Flags = Flags;\n\n    ModifyComputeSystem(ComputeSystem, wsl::shared::ToJsonW(request).c_str(), UserToken);\n}\n\nvoid wsl::windows::common::hcs::AddVhd(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR VhdPath, _In_ ULONG Lun, _In_ bool ReadOnly)\n{\n    ModifySettingRequest<Attachment> request{};\n    request.RequestType = ModifyRequestType::Add;\n    request.ResourcePath = c_scsiResourcePath + std::to_wstring(Lun);\n    request.Settings.Path = VhdPath;\n    request.Settings.ReadOnly = ReadOnly;\n    request.Settings.Type = AttachmentType::VirtualDisk;\n    request.Settings.SupportCompressedVolumes = true;\n    request.Settings.AlwaysAllowSparseFiles = true;\n    request.Settings.SupportEncryptedFiles = true;\n\n    ModifyComputeSystem(ComputeSystem, wsl::shared::ToJsonW(request).c_str());\n}\n\nvoid wsl::windows::common::hcs::AddPassThroughDisk(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR Disk, _In_ ULONG Lun)\n{\n    ModifySettingRequest<Attachment> request{};\n    request.RequestType = ModifyRequestType::Add;\n    request.Settings.Path = Disk;\n    request.ResourcePath = c_scsiResourcePath + std::to_wstring(Lun);\n    request.Settings.Type = AttachmentType::PassThru;\n\n    ModifyComputeSystem(ComputeSystem, wsl::shared::ToJsonW(request).c_str());\n}\n\nwsl::windows::common::hcs::unique_hcs_operation wsl::windows::common::hcs::CreateOperation()\n{\n    unique_hcs_operation operation(::HcsCreateOperation(nullptr, nullptr));\n    THROW_LAST_ERROR_IF_MSG(!operation, \"HcsCreateOperation\");\n\n    return operation;\n}\n\nwsl::windows::common::hcs::unique_hcs_system wsl::windows::common::hcs::CreateComputeSystem(_In_ PCWSTR Id, _In_ PCWSTR Configuration)\n{\n    WSL_LOG_DEBUG(\"HcsCreateComputeSystem\", TraceLoggingValue(Id, \"id\"), TraceLoggingValue(Configuration, \"configuration\"));\n\n    ExecutionContext context(Context::HCS);\n\n    const unique_hcs_operation operation = CreateOperation();\n    unique_hcs_system system{};\n    THROW_IF_FAILED(::HcsCreateComputeSystem(Id, Configuration, operation.get(), nullptr, &system));\n\n    wil::unique_cotaskmem_string resultDocument;\n    const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);\n    if (FAILED(result))\n    {\n        // N.B. Logging is split into two calls because the configuration and error strings can be quite long.\n        LOG_HR_MSG(result, \"HcsCreateComputeSystem(%ls, %ls)\", Id, Configuration);\n        THROW_HR_MSG(result, \"HcsCreateComputeSystem failed (error string: %ls)\", resultDocument.get());\n    }\n\n    return system;\n}\n\nconst std::vector<std::string>& wsl::windows::common::hcs::GetProcessorFeatures()\n{\n    static std::vector<std::string> g_processorFeatures;\n    static std::once_flag flag;\n    std::call_once(flag, []() {\n        ExecutionContext context(Context::HCS);\n\n        wil::unique_cotaskmem_string result;\n        THROW_IF_FAILED(::HcsGetServiceProperties(c_processorCapabilitiesQuery, &result));\n\n        const auto properties =\n            wsl::shared::FromJson<ServicePropertiesResponse<PropertyResponse<ProcessorCapabilitiesInfo>>>(result.get());\n\n        const auto& response = properties.PropertyResponses.at(c_processorCapabilities);\n        if (response.Error)\n        {\n            THROW_HR_MSG(static_cast<HRESULT>(response.Error->Error), \"%hs\", response.Error->ErrorMessage.c_str());\n        }\n\n        g_processorFeatures = response.Response.ProcessorFeatures;\n    });\n\n    return g_processorFeatures;\n}\n\nwsl::shared::hns::HNSEndpoint wsl::windows::common::hcs::GetEndpointProperties(HCN_ENDPOINT Endpoint)\n{\n    WSL_LOG_DEBUG(\"HcsGetEndpointProperties\");\n\n    ExecutionContext context(Context::HNS);\n\n    wil::unique_cotaskmem_string propertiesString;\n    wil::unique_cotaskmem_string error;\n    const auto result = HcnQueryEndpointProperties(Endpoint, nullptr, &propertiesString, &error);\n    THROW_IF_FAILED_MSG(result, \"HcnQueryEndpointProperties %ls\", error.get());\n\n    return wsl::shared::FromJson<wsl::shared::hns::HNSEndpoint>(propertiesString.get());\n}\n\nGUID wsl::windows::common::hcs::GetRuntimeId(_In_ HCS_SYSTEM ComputeSystem)\n{\n    ExecutionContext context(Context::HCS);\n\n    const unique_hcs_operation operation = CreateOperation();\n    THROW_IF_FAILED(::HcsGetComputeSystemProperties(ComputeSystem, operation.get(), nullptr));\n\n    wil::unique_cotaskmem_string resultDocument;\n    const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);\n    THROW_IF_FAILED_MSG(result, \"HcsGetComputeSystemProperties failed (error string: %ls)\", resultDocument.get());\n\n    const auto properties = wsl::shared::FromJson<Properties>(resultDocument.get());\n    THROW_HR_IF(HCS_E_SYSTEM_NOT_FOUND, (properties.SystemType != SystemType::VirtualMachine));\n\n    return properties.RuntimeId;\n}\n\nstd::pair<uint32_t, uint32_t> wsl::windows::common::hcs::GetSchemaVersion()\n{\n    static std::pair<uint32_t, uint32_t> g_schemaVersion{};\n    static std::once_flag flag;\n    std::call_once(flag, []() {\n        ExecutionContext context(Context::HCS);\n\n        PropertyQuery query;\n        query.PropertyTypes.emplace_back(PropertyType::Basic);\n        wil::unique_cotaskmem_string result;\n        THROW_IF_FAILED(::HcsGetServiceProperties(wsl::shared::ToJsonW(query).c_str(), &result));\n\n        const auto properties = wsl::shared::FromJson<ServiceProperties<BasicInformation>>(result.get());\n        THROW_HR_IF_MSG(E_UNEXPECTED, properties.Properties.empty(), \"%ls\", result.get());\n\n        uint32_t majorVersion = 0;\n        uint32_t minorVersion = 0;\n        for (const auto& version : properties.Properties[0].SupportedSchemaVersions)\n        {\n            if (version.Major >= majorVersion)\n            {\n                if ((version.Major > majorVersion) || (version.Minor > minorVersion))\n                {\n                    majorVersion = version.Major;\n                    minorVersion = version.Minor;\n                }\n            }\n        }\n\n        g_schemaVersion = {majorVersion, minorVersion};\n    });\n\n    return g_schemaVersion;\n}\n\nvoid wsl::windows::common::hcs::GrantVmAccess(_In_ PCWSTR VmId, _In_ PCWSTR FilePath)\n{\n    WSL_LOG_DEBUG(\"HcsGrantVmAccess\", TraceLoggingValue(VmId, \"vmId\"), TraceLoggingValue(FilePath, \"filePath\"));\n\n    ExecutionContext context(Context::HCS);\n\n    THROW_IF_FAILED_MSG(::HcsGrantVmAccess(VmId, FilePath), \"HcsGrantVmAccess(%ls, %ls)\", VmId, FilePath);\n}\n\nvoid wsl::windows::common::hcs::ModifyComputeSystem(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR Configuration, _In_opt_ HANDLE Identity)\n{\n    WSL_LOG_DEBUG(\"HcsModifyComputeSystem\", TraceLoggingValue(Configuration, \"configuration\"));\n\n    ExecutionContext context(Context::HCS);\n\n    const unique_hcs_operation operation = CreateOperation();\n    THROW_IF_FAILED_MSG(\n        ::HcsModifyComputeSystem(ComputeSystem, operation.get(), Configuration, Identity), \"HcsModifyComputeSystem (%ls)\", Configuration);\n\n    wil::unique_cotaskmem_string resultDocument;\n    const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);\n    if (FAILED(result))\n    {\n        // N.B. Logging is split into two calls because the configuration and error strings can be quite long.\n        LOG_HR_MSG(result, \"HcsModifyComputeSystem(%ls)\", Configuration);\n        THROW_HR_MSG(result, \"HcsModifyComputeSystem failed (error string: %ls)\", resultDocument.get());\n    }\n}\n\nwsl::windows::common::hcs::unique_hcs_system wsl::windows::common::hcs::OpenComputeSystem(_In_ PCWSTR Id, _In_ DWORD RequestedAccess)\n{\n    WSL_LOG_DEBUG(\"HcsOpenComputeSystem\", TraceLoggingValue(Id, \"id\"), TraceLoggingValue(RequestedAccess, \"requestedAccess\"));\n\n    ExecutionContext context(Context::HCS);\n\n    unique_hcs_system system;\n    THROW_IF_FAILED_MSG(::HcsOpenComputeSystem(Id, RequestedAccess, &system), \"HcsOpenComputeSystem(%ls)\", Id);\n\n    return system;\n}\n\nvoid wsl::windows::common::hcs::RegisterCallback(_In_ HCS_SYSTEM ComputeSystem, _In_ HCS_EVENT_CALLBACK Callback, _In_ void* Context)\n{\n    WSL_LOG_DEBUG(\"HcsSetComputeSystemCallback\");\n\n    ExecutionContext context(Context::HCS);\n\n    THROW_IF_FAILED(::HcsSetComputeSystemCallback(ComputeSystem, HcsEventOptionNone, Context, Callback));\n}\n\nvoid wsl::windows::common::hcs::RemoveScsiDisk(_In_ HCS_SYSTEM ComputeSystem, _In_ ULONG Lun)\n{\n    ModifySettingRequest<void> request{};\n    request.RequestType = ModifyRequestType::Remove;\n    request.ResourcePath = c_scsiResourcePath + std::to_wstring(Lun);\n    ModifyComputeSystem(ComputeSystem, wsl::shared::ToJsonW(request).c_str());\n}\n\nvoid wsl::windows::common::hcs::RevokeVmAccess(_In_ PCWSTR VmId, _In_ PCWSTR FilePath)\n{\n    WSL_LOG_DEBUG(\"HcsRevokeVmAccess\", TraceLoggingValue(VmId, \"vmId\"), TraceLoggingValue(FilePath, \"filePath\"));\n\n    ExecutionContext context(Context::HNS);\n\n    THROW_IF_FAILED_MSG(::HcsRevokeVmAccess(VmId, FilePath), \"HcsRevokeVmAccess(%ls, %ls)\", VmId, FilePath);\n}\n\nvoid wsl::windows::common::hcs::StartComputeSystem(_In_ HCS_SYSTEM ComputeSystem, _In_ LPCWSTR Configuration)\n{\n    WSL_LOG_DEBUG(\"HcsStartComputeSystem\", TraceLoggingValue(Configuration, \"configuration\"));\n\n    ExecutionContext context(Context::HCS);\n\n    const unique_hcs_operation operation = CreateOperation();\n    THROW_IF_FAILED(::HcsStartComputeSystem(ComputeSystem, operation.get(), nullptr));\n\n    wil::unique_cotaskmem_string resultDocument;\n    const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);\n    if (FAILED(result))\n    {\n        // N.B. Logging is split into two calls because the configuration and error strings can be quite long.\n        LOG_HR_MSG(result, \"HcsStartComputeSystem(%ls)\", Configuration);\n        THROW_HR_MSG(result, \"HcsStartComputeSystem failed (error string: %ls)\", resultDocument.get());\n    }\n}\n\nvoid wsl::windows::common::hcs::TerminateComputeSystem(_In_ HCS_SYSTEM ComputeSystem)\n{\n    WSL_LOG_DEBUG(\"HcsTerminateComputeSystem\");\n\n    ExecutionContext context(Context::HCS);\n\n    const unique_hcs_operation operation = CreateOperation();\n    THROW_IF_FAILED(::HcsTerminateComputeSystem(ComputeSystem, operation.get(), nullptr));\n\n    wil::unique_cotaskmem_string resultDocument;\n    const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);\n    THROW_IF_FAILED_MSG(result, \"HcsTerminateComputeSystem failed (error string: %ls)\", resultDocument.get());\n}\n\nwsl::windows::common::hcs::unique_hcn_service_callback wsl::windows::common::hcs::RegisterServiceCallback(\n    _In_ HCS_NOTIFICATION_CALLBACK Callback, _In_ PVOID Context)\n{\n    WSL_LOG_DEBUG(\"HcsRegisterServiceCallback\");\n\n    ExecutionContext context(Context::HNS);\n\n    unique_hcn_service_callback callbackHandle;\n    THROW_IF_FAILED(::HcnRegisterServiceCallback(Callback, Context, &callbackHandle));\n\n    return callbackHandle;\n}\n\nwsl::windows::common::hcs::unique_hcn_guest_network_service_callback wsl::windows::common::hcs::RegisterGuestNetworkServiceCallback(\n    _In_ const unique_hcn_guest_network_service& GuestNetworkService, _In_ HCS_NOTIFICATION_CALLBACK Callback, _In_ PVOID Context)\n{\n    WSL_LOG_DEBUG(\"HcsRegisterGuestNetworkServiceCallback\");\n\n    ExecutionContext context(Context::HNS);\n\n    unique_hcn_guest_network_service_callback callbackHandle;\n    THROW_IF_FAILED(::HcnRegisterGuestNetworkServiceCallback(GuestNetworkService.get(), Callback, Context, &callbackHandle));\n\n    return callbackHandle;\n}"
  },
  {
    "path": "src/windows/common/hcs.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    hcs.hpp\n\nAbstract:\n\n    This file contains host compute service helper function declarations.\n\n--*/\n\n#pragma once\n#include \"hcs_schema.h\"\n#include \"hns_schema.h\"\n#include <ComputeNetwork.h>\n#include <ComputeCore.h>\n\nnamespace wsl::windows::common::hcs {\n\nusing unique_hcn_endpoint = wil::unique_any<HCN_ENDPOINT, decltype(&HcnCloseEndpoint), HcnCloseEndpoint>;\n\nusing unique_hcn_service_callback = wil::unique_any<HCN_CALLBACK, decltype(&HcnUnregisterServiceCallback), HcnUnregisterServiceCallback>;\n\nusing unique_hcn_guest_network_service_callback =\n    wil::unique_any<HCN_CALLBACK, decltype(&HcnUnregisterGuestNetworkServiceCallback), HcnUnregisterGuestNetworkServiceCallback>;\n\nusing unique_hcn_guest_network_service =\n    wil::unique_any<HCN_GUESTNETWORKSERVICE, decltype(&::HcnCloseGuestNetworkService), ::HcnCloseGuestNetworkService>;\n\nusing unique_hcn_network = wil::unique_any<HCN_NETWORK, decltype(&HcnCloseNetwork), HcnCloseNetwork>;\n\nusing unique_hcs_operation = wil::unique_any<HCS_OPERATION, decltype(&HcsCloseOperation), HcsCloseOperation>;\n\nusing unique_hcs_system = wil::unique_any<HCS_SYSTEM, decltype(&HcsCloseComputeSystem), HcsCloseComputeSystem>;\n\nvoid AddPlan9Share(\n    _In_ HCS_SYSTEM ComputeSystem,\n    _In_ PCWSTR Name,\n    _In_ PCWSTR AccessName,\n    _In_ PCWSTR Path,\n    _In_ UINT32 Port,\n    _In_ Plan9ShareFlags Flags,\n    _In_opt_ HANDLE UserToken = nullptr);\n\nvoid AddVhd(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR VhdPath, _In_ ULONG Lun, _In_ bool ReadOnly = false);\n\nvoid AddPassThroughDisk(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR Disk, _In_ ULONG Lun);\n\nunique_hcs_system CreateComputeSystem(_In_ PCWSTR Id, _In_ PCWSTR Configuration);\n\nunique_hcs_operation CreateOperation();\n\nwsl::shared::hns::HNSEndpoint GetEndpointProperties(HCN_ENDPOINT endpoint);\n\nconst std::vector<std::string>& GetProcessorFeatures();\n\nGUID GetRuntimeId(_In_ HCS_SYSTEM ComputeSystem);\n\nstd::pair<uint32_t, uint32_t> GetSchemaVersion();\n\nvoid GrantVmAccess(_In_ PCWSTR VmId, _In_ PCWSTR FilePath);\n\nvoid ModifyComputeSystem(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR Configuration, _In_opt_ HANDLE Identity = nullptr);\n\nunique_hcs_system OpenComputeSystem(_In_ PCWSTR Id, _In_ DWORD RequestedAccess);\n\nvoid RegisterCallback(_In_ HCS_SYSTEM ComputeSystem, _In_ HCS_EVENT_CALLBACK Callback, _In_ void* Context);\n\nvoid RemoveScsiDisk(_In_ HCS_SYSTEM ComputeSystem, _In_ ULONG Lun);\n\nvoid RevokeVmAccess(_In_ PCWSTR VmId, _In_ PCWSTR FilePath);\n\nvoid StartComputeSystem(_In_ HCS_SYSTEM ComputeSystem, _In_ LPCWSTR Configuration);\n\nvoid TerminateComputeSystem(_In_ HCS_SYSTEM ComputeSystem);\n\nunique_hcn_service_callback RegisterServiceCallback(_In_ HCS_NOTIFICATION_CALLBACK Callback, _In_ PVOID Context);\n\nunique_hcn_guest_network_service_callback RegisterGuestNetworkServiceCallback(\n    _In_ const unique_hcn_guest_network_service& GuestNetworkService, _In_ HCS_NOTIFICATION_CALLBACK Callback, _In_ PVOID Context);\n\n} // namespace wsl::windows::common::hcs\n"
  },
  {
    "path": "src/windows/common/hcs_schema.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    hcs_schema.h\n\nAbstract:\n\n    This file contains the host compute service schema definitions.\n\n--*/\n\n#pragma once\n\n#include \"JsonUtils.h\"\n\n#define OMIT_IF_EMPTY(Json, Object, Value) \\\n    if ((Object).Value.has_value()) \\\n    { \\\n        Json[#Value] = (Object).Value.value(); \\\n    }\n\nnamespace wsl::windows::common::hcs {\n\nenum class ModifyRequestType\n{\n    Add,\n    Update,\n    Remove\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    ModifyRequestType,\n    {\n        {ModifyRequestType::Add, \"Add\"},\n        {ModifyRequestType::Update, \"Update\"},\n        {ModifyRequestType::Remove, \"Remove\"},\n    })\n\nenum class AttachmentType\n{\n    VirtualDisk,\n    PassThru\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    AttachmentType,\n    {\n        {AttachmentType::VirtualDisk, \"VirtualDisk\"},\n        {AttachmentType::PassThru, \"PassThru\"},\n    })\n\nenum class PropertyType\n{\n    Basic\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(PropertyType, {{PropertyType::Basic, \"Basic\"}})\n\nstruct Attachment\n{\n    Attachment() = default;\n    AttachmentType Type;\n    std::wstring Path;\n    bool ReadOnly;\n    bool SupportCompressedVolumes;\n    bool AlwaysAllowSparseFiles;\n    bool SupportEncryptedFiles;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Attachment, Type, Path, ReadOnly, SupportCompressedVolumes, AlwaysAllowSparseFiles, SupportEncryptedFiles);\n};\n\nenum class Plan9ShareFlags\n{\n    None = 0x00000000,\n    ReadOnly = 0x00000001,\n    LinuxMetadata = 0x00000004,\n    CaseSensitive = 0x00000008,\n    UseShareRootIdentity = 0x00000010,\n    AllowOptions = 0x00000020,\n    AllowSubPaths = 0x00000040\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(Plan9ShareFlags);\n\nstruct Plan9Share\n{\n    std::wstring Name;\n    std::wstring AccessName;\n    std::wstring Path;\n    uint32_t Port;\n    Plan9ShareFlags Flags;\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Plan9Share, Name, AccessName, Path, Port, Flags);\n};\n\nenum class FlexibleIoDeviceHostingModel\n{\n    ExternalRestricted\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    FlexibleIoDeviceHostingModel,\n    {\n        {FlexibleIoDeviceHostingModel::ExternalRestricted, \"ExternalRestricted\"},\n    })\n\nstruct FlexibleIoDevice\n{\n    GUID EmulatorId;\n    FlexibleIoDeviceHostingModel HostingModel;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(FlexibleIoDevice, EmulatorId, HostingModel);\n};\n\nstruct NetworkAdapter\n{\n    GUID EndpointId;\n    wsl::shared::string::MacAddress MacAddress;\n    std::optional<GUID> InstanceId;\n    std::optional<bool> IsConnected;\n    std::optional<GUID> SwitchId;\n    std::optional<GUID> PortId;\n};\n\ninline void to_json(nlohmann::json& j, const NetworkAdapter& adapter)\n{\n    j = nlohmann::json{{\"EndpointId\", adapter.EndpointId}, {\"MacAddress\", adapter.MacAddress}};\n\n    OMIT_IF_EMPTY(j, adapter, InstanceId);\n    OMIT_IF_EMPTY(j, adapter, IsConnected);\n    OMIT_IF_EMPTY(j, adapter, SwitchId);\n    OMIT_IF_EMPTY(j, adapter, PortId);\n}\n\nenum class GpuAssignmentMode\n{\n    Mirror\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    GpuAssignmentMode,\n    {\n        {GpuAssignmentMode::Mirror, \"Mirror\"},\n    })\n\nstruct GpuConfiguration\n{\n    GpuAssignmentMode AssignmentMode;\n    bool AllowVendorExtension;\n    std::optional<bool> DisableGdiAcceleration;\n    std::optional<bool> DisablePresentation;\n};\n\ninline void to_json(nlohmann::json& j, const GpuConfiguration& configuration)\n{\n    j = nlohmann::json{{\"AssignmentMode\", configuration.AssignmentMode}, {\"AllowVendorExtension\", configuration.AllowVendorExtension}};\n\n    OMIT_IF_EMPTY(j, configuration, DisableGdiAcceleration);\n    OMIT_IF_EMPTY(j, configuration, DisablePresentation);\n}\n\ntemplate <typename TSettings>\nstruct ModifySettingRequest\n{\n    std::wstring ResourcePath;\n    ModifyRequestType RequestType;\n    TSettings Settings;\n};\n\ntemplate <>\nstruct ModifySettingRequest<void>\n{\n    ModifySettingRequest() = default;\n    std::wstring ResourcePath;\n    ModifyRequestType RequestType;\n};\n\ntemplate <typename TSettings>\ninline void to_json(nlohmann::json& j, const ModifySettingRequest<TSettings>& request)\n{\n    j = nlohmann::json{{\"ResourcePath\", request.ResourcePath}, {\"RequestType\", request.RequestType}};\n\n    if constexpr (!std::is_same_v<TSettings, void>)\n    {\n        j[\"Settings\"] = request.Settings;\n    }\n}\n\nstruct PropertyQuery\n{\n    std::vector<PropertyType> PropertyTypes;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(PropertyQuery, PropertyTypes);\n};\n\nenum ProcessorFeature\n{\n    NestedVirt = 70\n};\n\nstruct _Error // Renamed from 'Error' to make the macro happy.\n{\n    int32_t Error;\n    std::string ErrorMessage;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(_Error, Error, ErrorMessage);\n};\n\nstruct ProcessorCapabilitiesInfo\n{\n    std::vector<std::string> ProcessorFeatures;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ProcessorCapabilitiesInfo, ProcessorFeatures);\n};\n\nstruct Version\n{\n    uint32_t Major;\n    uint32_t Minor;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Version, Major, Minor);\n};\n\nstruct BasicInformation\n{\n    std::vector<Version> SupportedSchemaVersions;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(BasicInformation, SupportedSchemaVersions);\n};\n\ntemplate <typename TResponse>\nstruct PropertyResponse\n{\n    std::optional<_Error> Error;\n    TResponse Response;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(PropertyResponse<ProcessorCapabilitiesInfo>, Error, Response);\n};\n\ntemplate <typename TResponse>\nstruct ServicePropertiesResponse\n{\n    std::map<std::string, TResponse> PropertyResponses;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ServicePropertiesResponse<PropertyResponse<ProcessorCapabilitiesInfo>>, PropertyResponses);\n};\n\ntemplate <typename TResponse>\nstruct ServiceProperties\n{\n    std::vector<TResponse> Properties;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ServiceProperties<BasicInformation>, Properties);\n};\n\nenum class SystemType\n{\n    VirtualMachine = 1\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    SystemType,\n    {\n        {SystemType::VirtualMachine, \"VirtualMachine\"},\n    })\n\nstruct Properties\n{\n    GUID RuntimeId;\n    SystemType SystemType;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Properties, RuntimeId, SystemType);\n};\n\nenum class MemoryBackingPageSize\n{\n    Small = 0\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    MemoryBackingPageSize,\n    {\n        {MemoryBackingPageSize::Small, \"Small\"},\n    })\n\nstruct Memory\n{\n    uint64_t SizeInMB;\n    bool AllowOvercommit;\n    bool EnableDeferredCommit;\n    bool EnableColdDiscardHint;\n    std::optional<MemoryBackingPageSize> BackingPageSize;\n    std::optional<uint32_t> FaultClusterSizeShift;\n    std::optional<uint32_t> DirectMapFaultClusterSizeShift;\n    std::optional<uint64_t> HighMmioGapInMB;\n    std::optional<uint64_t> HighMmioBaseInMB;\n    std::optional<std::wstring> HostingProcessNameSuffix;\n};\n\ninline void to_json(nlohmann::json& j, const Memory& memory)\n{\n    j = nlohmann::json{\n        {\"SizeInMB\", memory.SizeInMB},\n        {\"AllowOvercommit\", memory.AllowOvercommit},\n        {\"EnableDeferredCommit\", memory.EnableDeferredCommit},\n        {\"EnableColdDiscardHint\", memory.EnableColdDiscardHint}};\n\n    OMIT_IF_EMPTY(j, memory, BackingPageSize);\n    OMIT_IF_EMPTY(j, memory, FaultClusterSizeShift);\n    OMIT_IF_EMPTY(j, memory, DirectMapFaultClusterSizeShift);\n    OMIT_IF_EMPTY(j, memory, HighMmioGapInMB);\n    OMIT_IF_EMPTY(j, memory, HighMmioBaseInMB);\n    OMIT_IF_EMPTY(j, memory, HostingProcessNameSuffix);\n}\n\nstruct Processor\n{\n    uint32_t Count;\n    std::optional<bool> ExposeVirtualizationExtensions;\n    std::optional<bool> EnablePerfmonPmu;\n    std::optional<bool> EnablePerfmonLbr;\n};\n\ninline void to_json(nlohmann::json& j, const Processor& processor)\n{\n    j = nlohmann::json{{\"Count\", processor.Count}};\n    OMIT_IF_EMPTY(j, processor, ExposeVirtualizationExtensions)\n    OMIT_IF_EMPTY(j, processor, EnablePerfmonPmu)\n    OMIT_IF_EMPTY(j, processor, EnablePerfmonLbr)\n}\n\nstruct Topology\n{\n    Processor Processor;\n    Memory Memory;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Topology, Processor, Memory);\n};\n\nstruct VirtioSerialPort\n{\n    std::wstring Name;\n    std::wstring NamedPipe;\n    bool ConsoleSupport;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(VirtioSerialPort, Name, NamedPipe, ConsoleSupport);\n};\n\nstruct VirtioSerial\n{\n    std::map<std::string, VirtioSerialPort> Ports;\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(VirtioSerial, Ports);\n};\n\nstruct ComPort\n{\n    std::wstring NamedPipe;\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(ComPort, NamedPipe);\n};\n\nstruct LinuxKernelDirect\n{\n    std::wstring KernelFilePath;\n    std::wstring InitRdPath;\n    std::wstring KernelCmdLine;\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(LinuxKernelDirect, KernelFilePath, InitRdPath, KernelCmdLine);\n};\n\nenum class UefiBootDevice\n{\n    VmbFs\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(\n    UefiBootDevice,\n    {\n        {UefiBootDevice::VmbFs, \"VmbFs\"},\n    })\n\nstruct UefiBootEntry\n{\n    UefiBootDevice DeviceType;\n    std::wstring VmbFsRootPath;\n    std::wstring DevicePath;\n    std::wstring OptionalData;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(UefiBootEntry, DeviceType, VmbFsRootPath, DevicePath, OptionalData);\n};\n\nstruct Uefi\n{\n    UefiBootEntry BootThis;\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Uefi, BootThis);\n};\n\nstruct EmptyObject\n{\n};\n\ninline void to_json(nlohmann::json& j, const EmptyObject& memory)\n{\n    j = nlohmann::json::object();\n}\n\nstruct HvSocketSystemConfig\n{\n    std::wstring DefaultBindSecurityDescriptor;\n    std::wstring DefaultConnectSecurityDescriptor;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(HvSocketSystemConfig, DefaultBindSecurityDescriptor, DefaultConnectSecurityDescriptor);\n};\n\nstruct HvSocket\n{\n    HvSocketSystemConfig HvSocketConfig;\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(HvSocket, HvSocketConfig);\n};\n\nstruct Chipset\n{\n    bool UseUtc;\n    std::optional<LinuxKernelDirect> LinuxKernelDirect;\n    std::optional<Uefi> Uefi;\n};\n\ninline void to_json(nlohmann::json& j, const Chipset& chipset)\n{\n    j = nlohmann::json{{\"UseUtc\", chipset.UseUtc}};\n\n    OMIT_IF_EMPTY(j, chipset, LinuxKernelDirect)\n    OMIT_IF_EMPTY(j, chipset, Uefi)\n}\n\nstruct Scsi\n{\n    std::map<std::string, Attachment> Attachments;\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Scsi, Attachments);\n};\n\nstruct Devices\n{\n    std::optional<VirtioSerial> VirtioSerial;\n    std::map<std::string, ComPort> ComPorts;\n    EmptyObject Plan9;\n    EmptyObject Battery;\n    HvSocket HvSocket;\n    std::map<std::string, Scsi> Scsi;\n};\n\ninline void to_json(nlohmann::json& j, const Devices& devices)\n{\n    j = nlohmann::json{\n        {\"ComPorts\", devices.ComPorts},\n        {\"Plan9\", devices.Plan9},\n        {\"Battery\", devices.Battery},\n        {\"HvSocket\", devices.HvSocket},\n        {\"Scsi\", devices.Scsi}};\n\n    OMIT_IF_EMPTY(j, devices, VirtioSerial);\n}\n\nstruct VirtualMachine\n{\n    bool StopOnReset;\n    Chipset Chipset;\n    Topology ComputeTopology;\n    Devices Devices;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(VirtualMachine, StopOnReset, Chipset, ComputeTopology, Devices);\n};\n\nstruct ComputeSystem\n{\n    std::wstring Owner;\n    bool ShouldTerminateOnLastHandleClosed;\n    Version SchemaVersion;\n    VirtualMachine VirtualMachine;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(ComputeSystem, Owner, ShouldTerminateOnLastHandleClosed, SchemaVersion, VirtualMachine)\n};\n\nstruct CrashReport\n{\n    std::wstring CrashLog;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(CrashReport, CrashLog);\n};\n\n} // namespace wsl::windows::common::hcs\n\n#undef OMIT_IF_EMPTY"
  },
  {
    "path": "src/windows/common/helpers.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    helpers.cpp\n\nAbstract:\n\n    This file contains helper function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"helpers.hpp\"\n#include \"Stringify.h\"\n#include \"svccomm.hpp\"\n#include \"socket.hpp\"\n#include \"hvsocket.hpp\"\n#include \"relay.hpp\"\n#include \"LxssMessagePort.h\"\n#include <gsl/algorithm>\n#include <gslhelpers.h>\n#include \"registry.hpp\"\n#include \"versionhelpers.h\"\n#include <regstr.h>\n\nusing wsl::windows::common::helpers::LaunchWslRelayFlags;\n\nconstexpr auto c_WslSupportInterfaceKey = L\"Software\\\\Classes\\\\Interface\\\\{46f3c96d-ffa3-42f0-b052-52f5e7ecbb08}\";\nconstexpr auto c_WslSupportInterfaceName = L\"IWslSupport\";\n\nnamespace {\n\nclass ProcessLauncher\n{\npublic:\n    ProcessLauncher(LPCWSTR executable) : m_executable(executable)\n    {\n    }\n\n    ProcessLauncher(LPCWSTR executable, LPCWSTR commandLine) : m_executable(executable), m_commandLine(commandLine)\n    {\n    }\n\n    ProcessLauncher(const ProcessLauncher&) = delete;\n    ProcessLauncher& operator=(const ProcessLauncher&) = delete;\n\n    ProcessLauncher(ProcessLauncher&& other) noexcept\n    {\n        *this = std::move(other);\n    }\n\n    ProcessLauncher& operator=(ProcessLauncher&& other) noexcept\n    {\n        std::swap(m_executable, other.m_executable);\n        std::swap(m_commandLine, other.m_commandLine);\n        std::swap(m_handles, other.m_handles);\n        return *this;\n    }\n\n    void AddOption(LPCWSTR OptionName, LPCWSTR OptionValue = nullptr)\n    {\n        m_commandLine += L' ';\n        m_commandLine += OptionName;\n        if (OptionValue)\n        {\n            m_commandLine += L\" \";\n            m_commandLine += OptionValue;\n        }\n    };\n\n    void AddGuidOption(LPCWSTR OptionName, LPCGUID Guid)\n    {\n        if (ARGUMENT_PRESENT(Guid))\n        {\n            AddOption(OptionName, wsl::shared::string::GuidToString<wchar_t>(*Guid).c_str());\n        }\n    };\n\n    void AddHandleOption(LPCWSTR OptionName, HANDLE Handle)\n    {\n        if (ARGUMENT_PRESENT(Handle))\n        {\n            AddOption(OptionName, std::to_wstring(HandleToUlong(Handle)).c_str());\n            m_handles.push_back(Handle);\n            wsl::windows::common::helpers::SetHandleInheritable(Handle);\n        }\n    };\n\n    [[nodiscard]] wil::unique_handle Launch(_In_opt_ HANDLE UserToken, _In_ bool HideWindow, _In_ bool CreateNoWindow = false) const\n    {\n        // If a user token was provided, create an environment block from the token.\n        wsl::windows::common::helpers::unique_environment_block environmentBlock{nullptr};\n        if (ARGUMENT_PRESENT(UserToken))\n        {\n            THROW_LAST_ERROR_IF(!CreateEnvironmentBlock(&environmentBlock, UserToken, false));\n        }\n\n        wsl::windows::common::SubProcess process(m_executable.data(), m_commandLine.data(), CREATE_UNICODE_ENVIRONMENT);\n\n        for (const auto e : m_handles)\n        {\n            process.InheritHandle(e);\n        }\n\n        if (HideWindow)\n        {\n            process.SetShowWindow(SW_HIDE);\n        }\n\n        if (CreateNoWindow)\n        {\n            process.SetFlags(CREATE_NO_WINDOW);\n        }\n\n        process.SetEnvironment(environmentBlock.get());\n        process.SetToken(UserToken);\n\n        // Launch the process.\n        return process.Start();\n    }\n\nprivate:\n    std::wstring m_executable;\n    std::wstring m_commandLine;\n    std::vector<HANDLE> m_handles;\n};\n\n[[nodiscard]] wil::unique_handle LaunchWslHost(\n    _In_opt_ LPCGUID DistroId, _In_opt_ HANDLE InteropHandle, _In_opt_ HANDLE EventHandle, _In_opt_ HANDLE ParentHandle, _In_opt_ LPCGUID VmId, _In_opt_ HANDLE UserToken)\n{\n    // Construct the command line.\n    //\n    // N.B. The two places that launch wslhost.exe are the wsl.exe the service.\n    const auto path = wsl::windows::common::wslutil::GetBasePath();\n\n    // Format the command line.\n    ProcessLauncher launcher((path / L\"wslhost.exe\").c_str());\n    launcher.AddGuidOption(wslhost::distro_id_option, DistroId);\n    launcher.AddGuidOption(wslhost::vm_id_option, VmId);\n    launcher.AddHandleOption(wslhost::handle_option, InteropHandle);\n    launcher.AddHandleOption(wslhost::event_option, EventHandle);\n    launcher.AddHandleOption(wslhost::parent_option, ParentHandle);\n    return launcher.Launch(UserToken, true);\n}\n\n[[nodiscard]] wil::unique_handle LaunchWslRelay(\n    _In_ wslrelay::RelayMode Mode,\n    _In_opt_ HANDLE InteropHandle,\n    _In_opt_ LPCGUID VmId,\n    _In_opt_ HANDLE PipeHandle,\n    _In_opt_ std::optional<int> Port,\n    _In_opt_ HANDLE ExitEvent,\n    _In_opt_ HANDLE UserToken,\n    _In_ LaunchWslRelayFlags Flags)\n{\n    // Construct the command line.\n    //\n    // N.B. The two places that launch wslrelay.exe are the wsl.exe the service.\n    const auto path = wsl::windows::common::wslutil::GetBasePath();\n\n    // Format the command line.\n    ProcessLauncher launcher((path / L\"wslrelay.exe\").c_str());\n    launcher.AddOption(wslrelay::mode_option, std::to_wstring(Mode).c_str());\n    launcher.AddGuidOption(wslrelay::vm_id_option, VmId);\n    launcher.AddHandleOption(wslrelay::handle_option, InteropHandle);\n    launcher.AddHandleOption(wslrelay::pipe_option, PipeHandle);\n    launcher.AddHandleOption(wslrelay::exit_event_option, ExitEvent);\n    if (Port)\n    {\n        launcher.AddOption(wslrelay::port_option, std::to_wstring(Port.value()).c_str());\n    }\n\n    if (WI_IsFlagSet(Flags, LaunchWslRelayFlags::DisableTelemetry))\n    {\n        launcher.AddOption(wslrelay::disable_telemetry_option);\n    }\n\n    if (WI_IsFlagSet(Flags, LaunchWslRelayFlags::ConnectPipe))\n    {\n        launcher.AddOption(wslrelay::connect_pipe_option);\n    }\n\n    return launcher.Launch(UserToken, WI_IsFlagSet(Flags, LaunchWslRelayFlags::HideWindow));\n}\n} // namespace\n\nvoid wsl::windows::common::helpers::ConnectPipe(_In_ HANDLE Pipe, _In_ DWORD Timeout, _In_ const std::vector<HANDLE>& ExitEvents)\n{\n    const wil::unique_event OverlappedEvent(wil::EventOptions::ManualReset);\n    OVERLAPPED Overlapped = {0};\n    Overlapped.hEvent = OverlappedEvent.get();\n    if (!ConnectNamedPipe(Pipe, &Overlapped))\n    {\n        switch (GetLastError())\n        {\n        case ERROR_PIPE_CONNECTED:\n            break;\n\n        case ERROR_IO_PENDING:\n        {\n            DWORD Bytes;\n            auto Cancel = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n                CancelIoEx(Pipe, &Overlapped);\n                GetOverlappedResult(Pipe, &Overlapped, &Bytes, TRUE);\n            });\n\n            std::vector<HANDLE> WaitHandles;\n            WaitHandles.push_back(Overlapped.hEvent);\n            for (auto ExitEvent : ExitEvents)\n            {\n                WaitHandles.push_back(ExitEvent);\n            }\n\n            const auto Result = WaitForMultipleObjects(gsl::narrow_cast<DWORD>(WaitHandles.size()), WaitHandles.data(), FALSE, Timeout);\n            if (!ExitEvents.empty() && Result > WAIT_OBJECT_0 && Result <= WAIT_OBJECT_0 + WaitHandles.size())\n            {\n                THROW_HR(E_ABORT);\n            }\n\n            THROW_LAST_ERROR_IF(Result != WAIT_OBJECT_0);\n\n            Cancel.release();\n            THROW_IF_WIN32_BOOL_FALSE(GetOverlappedResult(Pipe, &Overlapped, &Bytes, FALSE));\n        }\n\n        break;\n\n        default:\n            THROW_LAST_ERROR();\n        }\n    }\n}\n\nstd::wstring_view wsl::windows::common::helpers::ConsumeArgument(_In_ std::wstring_view CommandLine, _In_ std::wstring_view Argument)\n{\n    WI_ASSERT((CommandLine.size() >= Argument.size()) && (wcsncmp(CommandLine.data(), Argument.data(), Argument.size()) == 0));\n\n    CommandLine.remove_prefix(Argument.size());\n    return string::StripLeadingWhitespace(CommandLine);\n}\n\nvoid wsl::windows::common::helpers::CreateConsole(_In_ LPCWSTR ConsoleTitle)\n{\n    THROW_IF_WIN32_BOOL_FALSE(AllocConsole());\n    WI_VERIFY(wsl::windows::common::helpers::ReopenStdHandles());\n    if (ConsoleTitle != nullptr)\n    {\n        LOG_IF_WIN32_BOOL_FALSE(SetConsoleTitleW(ConsoleTitle));\n    }\n}\n\nwsl::windows::common::helpers::unique_proc_attribute_list wsl::windows::common::helpers::CreateProcThreadAttributeList(_In_ DWORD AttributeCount)\n{\n    SIZE_T Size = 0;\n    if (!InitializeProcThreadAttributeList(nullptr, AttributeCount, 0, &Size))\n    {\n        THROW_LAST_ERROR_IF(GetLastError() != ERROR_INSUFFICIENT_BUFFER);\n    }\n\n    unique_proc_attribute_list List(reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(CoTaskMemAlloc(Size)));\n    THROW_IF_WIN32_BOOL_FALSE(InitializeProcThreadAttributeList(List.get(), AttributeCount, 0, &Size));\n\n    return List;\n}\n\nstd::vector<gsl::byte> wsl::windows::common::helpers::GenerateConfigurationMessage(\n    _In_ const std::wstring& DistributionName,\n    _In_ ULONG FixedDrivesBitmap,\n    _In_ ULONG DefaultUid,\n    _In_ const std::string& Timezone,\n    _In_ const std::wstring& Plan9SocketPath,\n    _In_ ULONG FeatureFlags,\n    _In_ LX_INIT_DRVFS_MOUNT DrvfsMount)\n{\n    auto [hostName, domainName] = filesystem::GetHostAndDomainNames();\n\n    std::string windowsHosts;\n\n    // If DNS tunneling is enabled, we don't need to reflect the Windows hosts file in Linux, as the\n    // Windows DNS client will use the Windows hosts file for tunneled DNS requests\n    if (!WI_IsFlagSet(FeatureFlags, LxInitFeatureDnsTunneling))\n    {\n        // Parse the Windows hosts file.\n        //\n        // N.B. failures generating the hosts string are non-fatal.\n        try\n        {\n\n            // Parse the Windows hosts file.\n            std::wstring SystemDirectory;\n            THROW_IF_FAILED(wil::GetSystemDirectoryW(SystemDirectory));\n\n            windowsHosts =\n                filesystem::GetWindowsHosts(std::filesystem::path(std::move(SystemDirectory)) / L\"drivers\" / L\"etc\" / L\"hosts\");\n        }\n        CATCH_LOG()\n    }\n\n    shared::MessageWriter<LX_INIT_CONFIGURATION_INFORMATION> message(LxInitMessageInitialize);\n    message->DrvFsVolumesBitmap = FixedDrivesBitmap;\n    message->DrvFsDefaultOwner = DefaultUid;\n    message->FeatureFlags = FeatureFlags;\n    message->DrvfsMount = DrvfsMount;\n    message.WriteString(message->HostnameOffset, hostName);\n    message.WriteString(message->DomainnameOffset, domainName);\n    message.WriteString(message->WindowsHostsOffset, windowsHosts);\n    message.WriteString(message->DistributionNameOffset, DistributionName);\n    message.WriteString(message->Plan9SocketOffset, Plan9SocketPath);\n    message.WriteString(message->TimezoneOffset, Timezone);\n\n    return message.MoveBuffer();\n}\n\nstd::vector<gsl::byte> wsl::windows::common::helpers::GenerateTimezoneUpdateMessage(_In_ std::string_view Timezone)\n{\n    // Construct the timezone update message.\n    shared::MessageWriter<LX_INIT_TIMEZONE_INFORMATION> message(LxInitMessageTimezoneInformation);\n    message.WriteString(message->TimezoneOffset, Timezone);\n\n    return message.MoveBuffer();\n}\n\nstd::string wsl::windows::common::helpers::GetLinuxTimezone(_In_opt_ HANDLE UserToken)\n{\n    std::string timezone{};\n    try\n    {\n        // If a user token was specified, impersonate to get the per-user region settings.\n        auto runAsSelf = UserToken ? wil::impersonate_token(UserToken) : wil::run_as_self();\n\n        // Query the system region.\n        std::vector<WCHAR> geoName;\n        int length;\n        do\n        {\n            length = GetUserDefaultGeoName(nullptr, 0);\n            THROW_LAST_ERROR_IF(length == 0);\n\n            geoName.resize(length + 1);\n            length = GetUserDefaultGeoName(geoName.data(), length);\n        } while ((length == 0) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER));\n\n        THROW_LAST_ERROR_IF(length == 0);\n\n        const auto region = wsl::shared::string::WideToMultiByte(geoName.data());\n\n        // Query the Windows timezone.\n        DYNAMIC_TIME_ZONE_INFORMATION zoneInfo;\n        THROW_LAST_ERROR_IF(GetDynamicTimeZoneInformation(&zoneInfo) == TIME_ZONE_ID_INVALID);\n\n        UErrorCode status = U_ZERO_ERROR;\n        auto windowsId = reinterpret_cast<const UChar*>(zoneInfo.TimeZoneKeyName);\n        const auto size = ucal_getTimeZoneIDForWindowsID(windowsId, -1, region.c_str(), nullptr, 0, &status);\n\n        // If no mapping exists, return an empty string.\n        THROW_HR_IF_MSG(\n            E_UNEXPECTED,\n            size == 0,\n            \"GetTimeZoneIDForWindowsID(%ls, -1, %hs, nullptr, 0, &status) returned %d\",\n            zoneInfo.TimeZoneKeyName,\n            region.c_str(),\n            status);\n\n        WI_ASSERT(status == UErrorCode::U_BUFFER_OVERFLOW_ERROR);\n\n        std::vector<UChar> buffer(size + 1);\n        status = U_ZERO_ERROR;\n        WI_VERIFY(ucal_getTimeZoneIDForWindowsID(windowsId, -1, region.c_str(), buffer.data(), size, &status) == size);\n\n        THROW_HR_IF_MSG(E_FAIL, (U_FAILURE(status) != false), \"%hs\", u_errorName(status));\n\n        timezone.resize(buffer.size());\n        u_UCharsToChars(buffer.data(), timezone.data(), static_cast<int32_t>(timezone.size()));\n    }\n    CATCH_LOG()\n\n    return timezone;\n}\n\nwsl::windows::common::helpers::WindowsVersion wsl::windows::common::helpers::GetWindowsVersion()\n{\n    static WindowsVersion version;\n    static std::once_flag flag;\n    std::call_once(flag, [&]() {\n        const auto regKey = registry::OpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_NT_CURRENTVERSION, KEY_READ);\n        const auto majorVersion = registry::ReadDword(regKey.get(), nullptr, L\"CurrentMajorVersionNumber\", 0);\n        const auto minorVersion = registry::ReadDword(regKey.get(), nullptr, L\"CurrentMinorVersionNumber\", 0);\n        const auto buildNumberString = registry::ReadString(regKey.get(), nullptr, REGSTR_VAL_CURRENT_BUILD, L\"0\");\n        const auto buildNumber = wcstoul(buildNumberString.c_str(), nullptr, 10);\n        const auto revision = registry::ReadDword(regKey.get(), nullptr, L\"UBR\", 0);\n        version = {majorVersion, minorVersion, buildNumber, revision};\n    });\n\n    return version;\n}\n\nstd::wstring wsl::windows::common::helpers::GetUniquePipeName()\n{\n    GUID pipeId;\n    THROW_IF_FAILED(CoCreateGuid(&pipeId));\n    return wslutil::ConstructPipePath(wsl::shared::string::GuidToString<wchar_t>(pipeId));\n}\n\nstd::filesystem::path wsl::windows::common::helpers::GetUserProfilePath(_In_opt_ HANDLE userToken)\n{\n    if (userToken != nullptr)\n    {\n        // N.B. stringSize includes the null terminator.\n        DWORD stringSize = 0;\n        ::GetUserProfileDirectoryW(userToken, nullptr, &stringSize);\n        WI_ASSERT(stringSize > 0);\n\n        std::wstring path(stringSize - 1, L'\\0');\n        THROW_IF_WIN32_BOOL_FALSE(::GetUserProfileDirectoryW(userToken, path.data(), &stringSize));\n\n        return std::filesystem::path(std::move(path));\n    }\n    else\n    {\n        wil::unique_cotaskmem_string profileDir;\n        THROW_IF_FAILED(SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &profileDir));\n\n        return std::filesystem::path(profileDir.get());\n    }\n}\n\nstd::string wsl::windows::common::helpers::GetWindowsVersionString()\n{\n    std::string versionString{};\n    try\n    {\n        const auto version = GetWindowsVersion();\n        std::stringstream stream;\n        stream << version.MajorVersion << \".\" << version.MinorVersion << \".\" << version.BuildNumber << \".\" << version.UpdateBuildRevision;\n        versionString = stream.str();\n    }\n    CATCH_LOG()\n\n    return versionString;\n}\n\nstd::filesystem::path wsl::windows::common::helpers::GetWslConfigPath(_In_opt_ HANDLE userToken)\n{\n    return wsl::windows::common::helpers::GetUserProfilePath(userToken) / L\".wslconfig\";\n}\n\nbool wsl::windows::common::helpers::IsPackageInstalled(_In_ LPCWSTR PackageFamilyName)\n{\n    UINT32 packageCount = 0;\n    UINT32 bufferSize = 0;\n    const auto result = GetPackagesByPackageFamily(PackageFamilyName, &packageCount, nullptr, &bufferSize, nullptr);\n\n    THROW_HR_IF(HRESULT_FROM_WIN32(result), result != ERROR_INSUFFICIENT_BUFFER && result != STATUS_SUCCESS && result != STATUS_NOT_FOUND);\n\n    return result != STATUS_NOT_FOUND && packageCount > 0;\n}\n\nbool wsl::windows::common::helpers::IsServicePresent(_In_ LPCWSTR ServiceName)\n{\n    const wil::unique_schandle manager{OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT)};\n    THROW_LAST_ERROR_IF(!manager);\n\n    const wil::unique_schandle service{OpenService(manager.get(), ServiceName, SERVICE_QUERY_CONFIG)};\n    return !!service;\n}\n\nbool wsl::windows::common::helpers::IsWindows11OrAbove()\n{\n    return GetWindowsVersion().BuildNumber >= WindowsBuildNumbers::Cobalt;\n}\n\nbool wsl::windows::common::helpers::IsWslOptionalComponentPresent()\n{\n    // Query if the lxss service (the lxss.sys driver) is present.\n    return IsServicePresent(L\"lxss\");\n}\n\nbool wsl::windows::common::helpers::IsWslSupportInterfacePresent()\n{\n    // Check if the IWslSupport interface is registered. This interface is present on all Windows builds\n    // that support the lifted WSL package.\n    wil::unique_hkey key;\n    try\n    {\n        key = windows::common::registry::OpenKey(HKEY_LOCAL_MACHINE, c_WslSupportInterfaceKey, KEY_READ);\n        WI_ASSERT(windows::common::registry::ReadString(key.get(), nullptr, nullptr, nullptr) == c_WslSupportInterfaceName);\n    }\n    CATCH_LOG()\n\n    return !!key;\n}\n\nvoid wsl::windows::common::helpers::LaunchDebugConsole(\n    _In_ LPCWSTR PipeName, _In_ bool ConnectExistingPipe, _In_ HANDLE UserToken, _In_opt_ HANDLE LogFile, _In_ bool DisableTelemetry)\n{\n    LaunchWslRelayFlags flags{};\n    wil::unique_hfile pipe;\n    if (ConnectExistingPipe)\n    {\n        // Connect to an existing pipe. The connection should be:\n        //     Asynchronous (FILE_FLAG_OVERLAPPED)\n        //     Anonymous (SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS)\n        //         - Don't allow the pipe server to impersonate the connecting client.\n        pipe.reset(CreateFileW(\n            PipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, nullptr));\n    }\n    else\n    {\n        // Create a new pipe server the child process will connect to. The pipe should be:\n        //     Bi-directional: PIPE_ACCESS_DUPLEX\n        //     Asynchronous: FILE_FLAG_OVERLAPPED\n        //     Raw: PIPE_TYPE_BYTE | PIPE_READMODE_BYTE\n        //     Blocking: PIPE_WAIT\n        WI_SetFlag(flags, LaunchWslRelayFlags::ConnectPipe);\n        pipe.reset(CreateNamedPipeW(\n            PipeName, (PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED), (PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT), 1, LX_RELAY_BUFFER_SIZE, LX_RELAY_BUFFER_SIZE, 0, nullptr));\n    }\n\n    THROW_LAST_ERROR_IF(!pipe);\n\n    WI_SetFlagIf(flags, LaunchWslRelayFlags::DisableTelemetry, DisableTelemetry);\n    wil::unique_handle info{LaunchWslRelay(wslrelay::RelayMode::DebugConsole, LogFile, nullptr, pipe.get(), {}, nullptr, UserToken, flags)};\n}\n\n[[nodiscard]] wil::unique_handle wsl::windows::common::helpers::LaunchInteropServer(\n    _In_opt_ LPCGUID DistroId, _In_ HANDLE InteropHandle, _In_opt_ HANDLE EventHandle, _In_opt_ HANDLE ParentHandle, _In_opt_ LPCGUID VmId, _In_opt_ HANDLE UserToken)\n{\n    return LaunchWslHost(DistroId, InteropHandle, EventHandle, ParentHandle, VmId, UserToken);\n}\n\nvoid wsl::windows::common::helpers::LaunchKdRelay(_In_ LPCWSTR PipeName, _In_ HANDLE UserToken, _In_ int Port, _In_ HANDLE ExitEvent, _In_ bool DisableTelemetry)\n{\n    // Create a new pipe server. The pipe should be:\n    //     Bi-directional: PIPE_ACCESS_DUPLEX\n    //     Asynchronous: FILE_FLAG_OVERLAPPED\n    //     Raw: PIPE_TYPE_BYTE | PIPE_READMODE_BYTE\n    //     Blocking: PIPE_WAIT\n    const wil::unique_hfile pipe{CreateNamedPipeW(\n        PipeName, (PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED), (PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT), 1, LX_RELAY_BUFFER_SIZE, LX_RELAY_BUFFER_SIZE, 0, nullptr)};\n\n    THROW_LAST_ERROR_IF(!pipe);\n\n    LaunchWslRelayFlags flags = LaunchWslRelayFlags::ConnectPipe;\n    WI_SetFlagIf(flags, LaunchWslRelayFlags::DisableTelemetry, DisableTelemetry);\n    wil::unique_handle info{LaunchWslRelay(wslrelay::RelayMode::KdRelay, nullptr, nullptr, pipe.get(), Port, ExitEvent, UserToken, flags)};\n}\n\nvoid wsl::windows::common::helpers::LaunchPortRelay(_In_ SOCKET Socket, _In_ const GUID& VmId, _In_ HANDLE UserToken, _In_ bool DisableTelemetry)\n{\n    LaunchWslRelayFlags flags{};\n    WI_SetFlagIf(flags, LaunchWslRelayFlags::DisableTelemetry, DisableTelemetry);\n    wil::unique_handle info{LaunchWslRelay(\n        wslrelay::RelayMode::PortRelay, reinterpret_cast<HANDLE>(Socket), &VmId, nullptr, {}, nullptr, UserToken, flags)};\n}\n\nvoid wsl::windows::common::helpers::LaunchWslSettingsOOBE(_In_ HANDLE UserToken)\n{\n    const auto wslSettingsExePath = wsl::windows::common::wslutil::GetBasePath() / L\"wslsettings\" / L\"wslsettings.exe\";\n    static constexpr auto commandLine = L\" ----ms-protocol:wsl-settings://oobe\";\n\n    wsl::windows::common::SubProcess process(wslSettingsExePath.c_str(), commandLine);\n    process.SetToken(UserToken);\n    process.SetShowWindow(SW_SHOW);\n\n    wsl::windows::common::helpers::unique_environment_block environmentBlock{nullptr};\n    THROW_LAST_ERROR_IF(!CreateEnvironmentBlock(&environmentBlock, UserToken, false));\n\n    process.SetEnvironment(environmentBlock.get());\n\n    process.Start();\n}\n\nstd::wstring_view wsl::windows::common::helpers::ParseArgument(_In_ std::wstring_view CommandLine, _In_ bool HandleQuotes)\n{\n    std::wstring_view Argument = CommandLine;\n    const size_t Index = Argument.find_first_of(L\" \\t\");\n    if (Index != std::wstring_view::npos)\n    {\n        Argument = Argument.substr(0, Index);\n    }\n\n    if (HandleQuotes && CommandLine.find_first_of(L\"\\\"\") == 0)\n    {\n        const auto QuoteIndex = CommandLine.find_first_of(L\"\\\"\", 1);\n        if (QuoteIndex != std::wstring_view::npos)\n        {\n            Argument = CommandLine.substr(0, QuoteIndex + 1);\n        }\n    }\n\n    return Argument;\n}\n\nbool wsl::windows::common::helpers::ReopenStdHandles()\n{\n    // Reopen the standard streams to make sure *printf* methods will write to the correct place.\n    if (_wfreopen(L\"CONIN$\", L\"r\", stdin) == nullptr || _wfreopen(L\"CONOUT$\", L\"w\", stdout) == nullptr ||\n        _wfreopen(L\"CONOUT$\", L\"w\", stderr) == nullptr)\n    {\n        return false;\n    }\n\n    // Configure std::cout, std::cerr and std::cin to use the reopened FILE*.\n    std::ios::sync_with_stdio();\n\n    return true;\n}\n\n#ifdef _WIN64\nINT64\nwsl::windows::common::helpers::RoundUpToNearestPowerOfTwo(_In_ INT64 Num)\n#else\nINT32\nwsl::windows::common::helpers::RoundUpToNearestPowerOfTwo(_In_ INT32 Num)\n#endif\n{\n    // Don't round the number up further if it's zero or already a power of two.\n    if (Num == 0 || (Num & (Num - 1)) == 0)\n    {\n        return Num;\n    }\n\n    // Round the number up to the nearest power of two.\n#ifdef _WIN64\n    ULONG index = 0;\n    WI_VERIFY(_BitScanReverse64(&index, Num));\n\n    return 1i64 << (index + 1);\n#else\n    ULONG index = 0;\n    WI_VERIFY(_BitScanReverse(&index, Num));\n\n    return 1i32 << (index + 1);\n#endif\n}\n\nDWORD\nwsl::windows::common::helpers::RunProcess(_Inout_ std::wstring& CommandLine)\n{\n    SubProcess process(nullptr, CommandLine.c_str());\n    return process.Run();\n}\n\nvoid wsl::windows::common::helpers::SetHandleInheritable(_In_ HANDLE Handle, _In_ bool Inheritable)\n{\n    THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(Handle, HANDLE_FLAG_INHERIT, Inheritable ? HANDLE_FLAG_INHERIT : 0));\n}\n\nbool wsl::windows::common::helpers::TryAttachConsole()\n{\n    if (!AttachConsole(GetCurrentProcessId()) && !AttachConsole(ATTACH_PARENT_PROCESS))\n    {\n        return false;\n    }\n\n    return ReopenStdHandles();\n}\n"
  },
  {
    "path": "src/windows/common/helpers.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    helpers.hpp\n\nAbstract:\n\n    This file contains helper function declarations.\n\n--*/\n\n#pragma once\n\n#include <winsock2.h>\n#include <string_view>\n#include <unordered_set>\n#include <unordered_map>\n#include <lxcoreapi.h>\n#include <gsl/gsl>\n#include <wil/com.h>\n#include <wil/filesystem.h>\n#include <wil/result.h>\n#include <winternl.h>\n#include \"lxinitshared.h\"\n\n#define _1KB ((UINT64)(1024))\n#define _1MB (_1KB * _1KB)\n#define _1GB (_1KB * _1MB)\n\n#define LXSS_LAUNCH_FLAG_ENABLE_INTEROP 0x1\n#define LXSS_LAUNCH_FLAG_TRANSLATE_ENVIRONMENT 0x2\n#define LXSS_LAUNCH_FLAG_USE_SYSTEM_DISTRO 0x4\n#define LXSS_LAUNCH_FLAG_SHELL_LOGIN 0x8\n\n#define LXSS_IS_WHITESPACE(_Char) (((_Char) == L' ') || ((_Char) == L'\\t'))\n\n#define LXSS_ROOTFS_DIRECTORY L\"rootfs\"\n#define LXSS_TEMP_DIRECTORY L\"temp\"\n\n#define CONTINUE_IF_FAILED(x) \\\n    { \\\n        if (FAILED_LOG((x))) \\\n        { \\\n            continue; \\\n        } \\\n    }\n\n#define CONTINUE_IF_FAILED_WIN32(x) \\\n    { \\\n        if (FAILED_WIN32_LOG((x))) \\\n        { \\\n            continue; \\\n        } \\\n    }\n\nnamespace wsl::windows::common::helpers {\n\nenum class LaunchWslRelayFlags\n{\n    None = 0,\n    DisableTelemetry = 1,\n    HideWindow = 2,\n    ConnectPipe = 4\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(LaunchWslRelayFlags);\n\nenum WindowsBuildNumbers : ULONG\n{\n    Vibranium = 19041,\n    Vibranium_20H2 = 19042,\n    Vibranium_21H1 = 19043,\n    Vibranium_21H2 = 19044,\n    Vibranium_22H2 = 19045,\n    Iron = 20348,\n    Cobalt = 22000,\n    Nickel = 22621,\n    Nickel_23H2 = 22631,\n    Zinc = 25398,\n    Germanium = 26100,\n};\n\nstruct GuidLess\n{\n    bool operator()(REFGUID left, REFGUID right) const\n    {\n        return memcmp(&left, &right, sizeof(GUID)) < 0;\n    }\n};\n\ntypedef wil::unique_any_handle_null<decltype(&::ClosePseudoConsole), ::ClosePseudoConsole> unique_pseudo_console;\n\nusing unique_environment_block = wil::unique_any<LPVOID, decltype(&DestroyEnvironmentBlock), DestroyEnvironmentBlock>;\n\ninline void DeleteProcThreadAttributeList(_In_ PPROC_THREAD_ATTRIBUTE_LIST AttributeList)\n{\n    ::DeleteProcThreadAttributeList(AttributeList);\n    CoTaskMemFree(AttributeList);\n}\n\nusing unique_proc_attribute_list =\n    wil::unique_any<PPROC_THREAD_ATTRIBUTE_LIST, decltype(&DeleteProcThreadAttributeList), DeleteProcThreadAttributeList>;\n\nusing unique_environment_strings = wil::unique_any<LPTCH, decltype(&::FreeEnvironmentStrings), ::FreeEnvironmentStrings>;\n\nusing unique_pseudo_console = wil::unique_any_handle_null<decltype(&ClosePseudoConsole), ClosePseudoConsole>;\n\nusing unique_mta_cookie = wil::unique_any<CO_MTA_USAGE_COOKIE, decltype(::CoDecrementMTAUsage), &::CoDecrementMTAUsage>;\n\nvoid ConnectPipe(_In_ HANDLE Pipe, _In_ DWORD Timeout = INFINITE, _In_ const std::vector<HANDLE>& ExitEvents = {});\n\nstd::wstring_view ConsumeArgument(_In_ std::wstring_view CommandLine, _In_ std::wstring_view Argument);\n\nvoid CreateConsole(_In_ LPCWSTR ConsoleTitle = nullptr);\n\nunique_proc_attribute_list CreateProcThreadAttributeList(_In_ DWORD AttributeCount);\n\nstd::vector<gsl::byte> GenerateConfigurationMessage(\n    _In_ const std::wstring& DistributionName,\n    _In_ ULONG FixedDrivesBitmap = 0,\n    _In_ ULONG DefaultUid = LX_UID_ROOT,\n    _In_ const std::string& Timezone = {},\n    _In_ const std::wstring& Plan9SocketPath = {},\n    _In_ ULONG FeatureFlags = 0,\n    _In_ LX_INIT_DRVFS_MOUNT DrvfsMount = LxInitDrvfsMountElevated);\n\nstd::vector<gsl::byte> GenerateTimezoneUpdateMessage(_In_ std::string_view Timezone);\n\nstd::string GetLinuxTimezone(_In_opt_ HANDLE UserToken = nullptr);\n\nstd::wstring GetUniquePipeName();\n\nstruct WindowsVersion\n{\n    DWORD MajorVersion;\n    DWORD MinorVersion;\n    ULONG BuildNumber;\n    DWORD UpdateBuildRevision;\n};\n\nWindowsVersion GetWindowsVersion();\n\nstd::string GetWindowsVersionString();\n\nstd::filesystem::path GetUserProfilePath(_In_opt_ HANDLE userToken = nullptr);\n\nstd::filesystem::path GetWslConfigPath(_In_opt_ HANDLE userToken = nullptr);\n\nbool IsPackageInstalled(_In_ LPCWSTR PackageFamilyName);\n\nbool IsServicePresent(_In_ LPCWSTR ServiceName);\n\nbool IsWindows11OrAbove();\n\nbool IsWslOptionalComponentPresent();\n\nbool IsWslSupportInterfacePresent();\n\nvoid LaunchDebugConsole(_In_ LPCWSTR PipeName, _In_ bool ConnectExistingPipe, _In_ HANDLE UserToken, _In_opt_ HANDLE LogFile, _In_ bool DisableTelemetry);\n\n[[nodiscard]] wil::unique_handle LaunchInteropServer(\n    _In_opt_ LPCGUID DistroId,\n    _In_ HANDLE InteropHandle,\n    _In_opt_ HANDLE EventHandle,\n    _In_opt_ HANDLE ParentHandle,\n    _In_opt_ LPCGUID VmId,\n    _In_opt_ HANDLE UserToken = nullptr);\n\nvoid LaunchKdRelay(_In_ LPCWSTR PipeName, _In_ HANDLE UserToken, _In_ int Port, _In_ HANDLE ExitEvent, _In_ bool DisableTelemetry);\n\nvoid LaunchPortRelay(_In_ SOCKET Socket, _In_ const GUID& VmId, _In_ HANDLE UserToken, _In_ bool DisableTelemetry);\n\nvoid LaunchWslSettingsOOBE(_In_ HANDLE UserToken);\n\nstd::wstring_view ParseArgument(_In_ std::wstring_view CommandLine, _In_ bool HandleQuotes = false);\n\nbool ReopenStdHandles();\n\n#ifdef _WIN64\nINT64\nRoundUpToNearestPowerOfTwo(_In_ INT64 Num);\n#else\nINT32\nRoundUpToNearestPowerOfTwo(_In_ INT32 Num);\n#endif\n\nDWORD RunProcess(_Inout_ std::wstring& CommandLine);\n\nvoid SetHandleInheritable(_In_ HANDLE Handle, _In_ bool Inheritable = true);\n\nbool TryAttachConsole();\n\n} // namespace wsl::windows::common::helpers\n"
  },
  {
    "path": "src/windows/common/hvsocket.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    hvsocket.cpp\n\nAbstract:\n\n    This file contains hvsocket helper function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include <mutex>\n#include \"socket.hpp\"\n#include \"hvsocket.hpp\"\n#pragma hdrstop\n\n#define CONNECT_TIMEOUT (30 * 1000)\n\nnamespace {\nvoid InitializeSocketAddress(_In_ const GUID& VmId, _In_ unsigned long Port, _Out_ PSOCKADDR_HV Address)\n{\n    RtlZeroMemory(Address, sizeof(*Address));\n    Address->Family = AF_HYPERV;\n    Address->VmId = VmId;\n    Address->ServiceId = HV_GUID_VSOCK_TEMPLATE;\n    Address->ServiceId.Data1 = Port;\n}\n\nvoid InitializeWildcardSocketAddress(_Out_ PSOCKADDR_HV Address)\n{\n    RtlZeroMemory(Address, sizeof(*Address));\n    Address->Family = AF_HYPERV;\n    Address->VmId = HV_GUID_WILDCARD;\n    Address->ServiceId = HV_GUID_WILDCARD;\n}\n} // namespace\n\nstd::optional<wil::unique_socket> wsl::windows::common::hvsocket::CancellableAccept(\n    _In_ SOCKET ListenSocket, _In_ DWORD Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)\n{\n    wil::unique_socket Socket = Create();\n    if (!socket::CancellableAccept(ListenSocket, Socket.get(), Timeout, ExitHandle, Location))\n    {\n        return {};\n    }\n\n    return Socket;\n}\n\nwil::unique_socket wsl::windows::common::hvsocket::Connect(\n    _In_ const GUID& VmId, _In_ unsigned long Port, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)\n{\n    OVERLAPPED Overlapped{};\n    const wil::unique_event OverlappedEvent(wil::EventOptions::ManualReset);\n    Overlapped.hEvent = OverlappedEvent.get();\n\n    auto Socket = Create();\n\n    static constexpr GUID ConnectExGuid = WSAID_CONNECTEX;\n    LPFN_CONNECTEX ConnectFn{};\n    DWORD BytesReturned;\n    const auto Result = WSAIoctl(\n        Socket.get(),\n        SIO_GET_EXTENSION_FUNCTION_POINTER,\n        const_cast<GUID*>(&ConnectExGuid),\n        sizeof(ConnectExGuid),\n        &ConnectFn,\n        sizeof(ConnectFn),\n        &BytesReturned,\n        &Overlapped,\n        nullptr);\n\n    if (Result != 0)\n    {\n        socket::GetResult(Socket.get(), Overlapped, INFINITE, ExitHandle, Location);\n    }\n\n    ULONG Timeout = CONNECT_TIMEOUT;\n    THROW_LAST_ERROR_IF(\n        setsockopt(Socket.get(), HV_PROTOCOL_RAW, HVSOCKET_CONNECT_TIMEOUT, reinterpret_cast<char*>(&Timeout), sizeof(Timeout)) == SOCKET_ERROR);\n\n    SOCKADDR_HV Addr;\n    InitializeWildcardSocketAddress(&Addr);\n    THROW_LAST_ERROR_IF(bind(Socket.get(), reinterpret_cast<sockaddr*>(&Addr), sizeof(Addr)) == SOCKET_ERROR);\n    InitializeSocketAddress(VmId, Port, &Addr);\n    OverlappedEvent.ResetEvent();\n    const BOOL Success = ConnectFn(Socket.get(), reinterpret_cast<sockaddr*>(&Addr), sizeof(Addr), nullptr, 0, nullptr, &Overlapped);\n    if (Success == FALSE)\n    {\n        socket::GetResult(Socket.get(), Overlapped, INFINITE, ExitHandle, Location);\n    }\n\n    return Socket;\n}\n\nwil::unique_socket wsl::windows::common::hvsocket::Create()\n{\n    wil::unique_socket Socket(WSASocket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW, nullptr, 0, WSA_FLAG_OVERLAPPED));\n    THROW_LAST_ERROR_IF(!Socket);\n\n    ULONG Enable = 1;\n    THROW_LAST_ERROR_IF(\n        setsockopt(Socket.get(), HV_PROTOCOL_RAW, HVSOCKET_CONNECTED_SUSPEND, reinterpret_cast<char*>(&Enable), sizeof(Enable)) == SOCKET_ERROR);\n\n    return Socket;\n}\n\nwil::unique_socket wsl::windows::common::hvsocket::Listen(_In_ const GUID& VmId, _In_ unsigned long Port, _In_ int Backlog)\n{\n    SOCKADDR_HV Addr;\n    InitializeSocketAddress(VmId, Port, &Addr);\n    auto Socket = Create();\n    THROW_LAST_ERROR_IF(bind(Socket.get(), reinterpret_cast<sockaddr*>(&Addr), sizeof(Addr)) == SOCKET_ERROR);\n    THROW_LAST_ERROR_IF(listen(Socket.get(), Backlog) == SOCKET_ERROR);\n    return Socket;\n}\n"
  },
  {
    "path": "src/windows/common/hvsocket.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    hvsocket.hpp\n\nAbstract:\n\n    This file contains hvsocket helper function declarations.\n\n--*/\n\n#pragma once\n\n#include <hvsocket.h>\n#include <wil/resource.h>\n\nnamespace wsl::windows::common::hvsocket {\n\nstd::optional<wil::unique_socket> CancellableAccept(\n    _In_ SOCKET ListenSocket,\n    _In_ DWORD Timeout,\n    _In_opt_ HANDLE ExitHandle = nullptr,\n    const std::source_location& Location = std::source_location::current());\n\nwil::unique_socket Connect(\n    _In_ const GUID& VmId,\n    _In_ unsigned long Port,\n    _In_opt_ HANDLE ExitHandle = nullptr,\n    const std::source_location& Location = std::source_location::current());\n\nwil::unique_socket Create();\n\nwil::unique_socket Listen(_In_ const GUID& VmId, _In_ unsigned long Port, _In_ int Backlog = -1);\n\n} // namespace wsl::windows::common::hvsocket\n"
  },
  {
    "path": "src/windows/common/install.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    install.cpp\n\nAbstract:\n\n    This file contains MSI/Wintrust install helper functions.\n    Split from wslutil.cpp to avoid pulling msi.dll/wintrust.dll\n    into targets that don't need them.\n\n--*/\n\n#include \"precomp.h\"\n#include \"install.h\"\n#include \"wslutil.h\"\n#include \"WslPluginApi.h\"\n#include \"wslinstallerservice.h\"\n\n#include \"ConsoleProgressBar.h\"\n#include \"ExecutionContext.h\"\n#include \"MsiQuery.h\"\n\nusing winrt::Windows::Foundation::Uri;\nusing winrt::Windows::Management::Deployment::DeploymentOptions;\nusing wsl::shared::Localization;\nusing wsl::windows::common::Context;\nusing namespace wsl::windows::common::registry;\nusing namespace wsl::windows::common::wslutil;\nusing namespace wsl::windows::common::install;\n\nnamespace {\n\nbool PromptForKeyPress()\n{\n    THROW_IF_WIN32_BOOL_FALSE(FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)));\n\n    // Note: Ctrl-c causes _getch to return 0x3.\n    return _getch() != 0x3;\n}\n\nbool PromptForKeyPressWithTimeout()\n{\n    // Run PromptForKeyPress on a separate thread so we can apply a timeout.\n    // If PromptForKeyPress fails, fulfill the promise with false so the caller doesn't hang.\n    std::promise<bool> pressedKey;\n    auto thread = std::thread([&pressedKey]() {\n        try\n        {\n            pressedKey.set_value(PromptForKeyPress());\n        }\n        catch (...)\n        {\n            LOG_CAUGHT_EXCEPTION();\n            try\n            {\n                pressedKey.set_value(false);\n            }\n            CATCH_LOG()\n        }\n    });\n\n    auto cancelRead = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&thread]() {\n        if (thread.joinable())\n        {\n            LOG_IF_WIN32_BOOL_FALSE(CancelSynchronousIo(thread.native_handle()));\n            thread.join();\n        }\n    });\n\n    auto future = pressedKey.get_future();\n    const auto waitResult = future.wait_for(std::chrono::minutes(1));\n\n    return waitResult == std::future_status::ready && future.get();\n}\n\nint UpdatePackageImpl(bool preRelease, bool repair)\n{\n    if (!repair)\n    {\n        PrintMessage(Localization::MessageCheckingForUpdates());\n    }\n\n    auto [version, release] = GetLatestGitHubRelease(preRelease);\n\n    if (!repair && ParseWslPackageVersion(version) <= wsl::shared::PackageVersion)\n    {\n        PrintMessage(Localization::MessageUpdateNotNeeded());\n        return 0;\n    }\n\n    PrintMessage(Localization::MessageUpdatingToVersion(version.c_str()));\n\n    const bool msiInstall = wsl::shared::string::EndsWith<wchar_t>(release.name, L\".msi\");\n    const auto downloadPath = DownloadFile(release.url, release.name);\n    if (msiInstall)\n    {\n        auto logFile = std::filesystem::temp_directory_path() / L\"wsl-install-logs.txt\";\n        auto clearLogs =\n            wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&logFile]() { LOG_IF_WIN32_BOOL_FALSE(DeleteFile(logFile.c_str())); });\n\n        const auto exitCode = UpgradeViaMsi(downloadPath.c_str(), L\"\", logFile.c_str(), &MsiMessageCallback);\n\n        if (exitCode != 0)\n        {\n            clearLogs.release();\n            THROW_HR_WITH_USER_ERROR(\n                HRESULT_FROM_WIN32(exitCode),\n                wsl::shared::Localization::MessageUpdateFailed(exitCode) + L\"\\r\\n\" +\n                    wsl::shared::Localization::MessageSeeLogFile(logFile.c_str()));\n        }\n    }\n    else\n    {\n        // Set FILE_FLAG_DELETE_ON_CLOSE on the file to make sure it's deleted when the installation completes.\n        const wil::unique_hfile package{CreateFileW(\n            downloadPath.c_str(), DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, nullptr)};\n\n        THROW_LAST_ERROR_IF(!package);\n\n        const winrt::Windows::Management::Deployment::PackageManager packageManager;\n        const auto result = packageManager.AddPackageAsync(\n            Uri{downloadPath.c_str()}, nullptr, DeploymentOptions::ForceApplicationShutdown | DeploymentOptions::ForceTargetApplicationShutdown);\n\n        THROW_IF_FAILED(result.get().ExtendedErrorCode());\n\n        // Note: If the installation is successful, this process is expected to receive and Ctrl-C and exit\n    }\n\n    return 0;\n}\n\nvoid WaitForMsiInstall()\n{\n    wil::com_ptr_t<IWslInstaller> installer;\n\n    auto retry_pred = []() {\n        const auto errorCode = wil::ResultFromCaughtException();\n        return errorCode == REGDB_E_CLASSNOTREG;\n    };\n\n    wsl::shared::retry::RetryWithTimeout<void>(\n        [&installer]() { installer = wil::CoCreateInstance<IWslInstaller>(__uuidof(WslInstaller), CLSCTX_LOCAL_SERVER); },\n        std::chrono::seconds(1),\n        std::chrono::minutes(1),\n        retry_pred);\n\n    fputws(wsl::shared::Localization::MessageFinishMsiInstallation().c_str(), stderr);\n\n    auto finishLine = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { fputws(L\"\\n\", stderr); });\n\n    UINT exitCode = -1;\n    wil::unique_cotaskmem_string message{};\n    THROW_IF_FAILED(installer->Install(&exitCode, &message));\n\n    if (message && *message.get() != UNICODE_NULL)\n    {\n        finishLine.release();\n        wprintf(L\"\\n%ls\\n\", message.get());\n    }\n\n    if (exitCode != 0)\n    {\n        THROW_HR_WITH_USER_ERROR(HRESULT_FROM_WIN32(exitCode), wsl::shared::Localization::MessageUpdateFailed(exitCode));\n    }\n}\n\nwil::unique_handle CreateJob()\n{\n    // Create a job object that will terminate all processes in the job on\n    // close but will not terminate the children of the processes in the job.\n    // This is used to ensure that when forwarding from an inbox binary (I)\n    // to a lifted binary (L), if I is terminated L is terminated as well but\n    // any children of L (e.g. wslhost.exe) continue to run.\n    wil::unique_handle job{CreateJobObject(nullptr, nullptr)};\n    THROW_LAST_ERROR_IF_NULL(job.get());\n\n    JOBOBJECT_EXTENDED_LIMIT_INFORMATION info{};\n    info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;\n    THROW_IF_WIN32_BOOL_FALSE(SetInformationJobObject(job.get(), JobObjectExtendedLimitInformation, &info, sizeof(info)));\n\n    return job;\n}\n\nint WINAPI InstallRecordHandler(void* context, UINT messageType, LPCWSTR message)\n{\n    try\n    {\n        WSL_LOG(\"MSIMessage\", TraceLoggingValue(messageType, \"type\"), TraceLoggingValue(message, \"message\"));\n        auto type = (INSTALLMESSAGE)(0xFF000000 & (UINT)messageType);\n\n        if (type == INSTALLMESSAGE_ERROR || type == INSTALLMESSAGE_FATALEXIT || type == INSTALLMESSAGE_WARNING)\n        {\n            WriteInstallLog(std::format(\"MSI message: {}\", message));\n        }\n\n        auto* callback = reinterpret_cast<const std::function<void(INSTALLMESSAGE, LPCWSTR)>*>(context);\n        if (callback != nullptr)\n        {\n            (*callback)(type, message);\n        }\n    }\n    CATCH_LOG();\n\n    return IDOK;\n}\n\nvoid ConfigureMsiLogging(_In_opt_ LPCWSTR LogFile, _In_ const std::function<void(INSTALLMESSAGE, LPCWSTR)>& Callback)\n{\n    if (LogFile != nullptr)\n    {\n        LOG_IF_WIN32_ERROR(MsiEnableLog(INSTALLLOGMODE_VERBOSE | INSTALLLOGMODE_EXTRADEBUG | INSTALLLOGMODE_PROGRESS, LogFile, 0));\n    }\n\n    MsiSetExternalUI(\n        &InstallRecordHandler,\n        INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO |\n            INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA |\n            INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE | INSTALLLOGMODE_SHOWDIALOG,\n        (void*)&Callback);\n\n    MsiSetInternalUI(INSTALLUILEVEL(INSTALLUILEVEL_NONE | INSTALLUILEVEL_UACONLY | INSTALLUILEVEL_SOURCERESONLY), nullptr);\n}\n\n} // namespace\n\nint wsl::windows::common::install::CallMsiPackage()\n{\n    wsl::windows::common::ExecutionContext context(wsl::windows::common::CallMsi);\n\n    auto msiPath = GetMsiPackagePath();\n    if (!msiPath.has_value())\n    {\n        wsl::windows::common::ExecutionContext context(wsl::windows::common::Install);\n\n        try\n        {\n            WaitForMsiInstall();\n            msiPath = GetMsiPackagePath();\n        }\n        catch (...)\n        {\n            LOG_CAUGHT_EXCEPTION();\n\n            // GetMsiPackagePath() will generate a user error if the registry access fails.\n            // Save the error from GetMsiPackagePath() to return a proper 'install failed' message.\n            auto savedError = context.ReportedError();\n\n            // There is a race where the service might stop before returning the install result.\n            // if this happens, only fail if the MSI still isn't installed.\n            msiPath = GetMsiPackagePath();\n            if (!msiPath.has_value())\n            {\n                // Offer to directly install the MSI package if the MsixInstaller logic fails\n                // This can trigger a UAC so only do it\n                if (IsInteractiveConsole())\n                {\n                    auto errorCode = savedError.has_value() ? ErrorToString(savedError.value()).Code\n                                                            : ErrorCodeToString(wil::ResultFromCaughtException());\n\n                    EMIT_USER_WARNING(wsl::shared::Localization::MessageInstallationCorrupted(errorCode));\n\n                    if (PromptForKeyPressWithTimeout())\n                    {\n                        return UpdatePackage(false, true);\n                    }\n                }\n\n                if (savedError.has_value())\n                {\n                    THROW_HR_WITH_USER_ERROR(savedError->Code, savedError->Message.value_or(L\"\"));\n                }\n\n                throw;\n            }\n        }\n\n        THROW_HR_IF(E_UNEXPECTED, !msiPath.has_value());\n    }\n\n    auto target = msiPath.value() + L\"\\\\\" WSL_BINARY_NAME;\n\n    SubProcess process(target.c_str(), GetCommandLine());\n    process.SetDesktopAppPolicy(PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_ENABLE_PROCESS_TREE);\n    auto runningProcess = process.Start();\n\n    // N.B. The job cannot be assigned at process creation time as the packaged process\n    //      creation path will assign the new process to a per package job object.\n    //      In the case of multiple processes running in a single package, assigning\n    //      the new process to the per package job object will fail for the second request\n    //      since both jobs already have processes which prevents a job hierarchy from\n    //      being established.\n    auto job = CreateJob();\n\n    // Assign the process to the job, ignoring failures when the process has\n    // terminated.\n    //\n    // N.B. Assigning the job after process creation without CREATE_SUSPENDED is\n    //      safe to do here since only the new child process will be in the job\n    //      object. None of the grandchildren processes are included since the\n    //      job is created with JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK.\n    if (!AssignProcessToJobObject(job.get(), runningProcess.get()))\n    {\n        auto lastError = GetLastError();\n        if (lastError != ERROR_ACCESS_DENIED)\n        {\n            THROW_WIN32(lastError);\n        }\n    }\n\n    return static_cast<int>(SubProcess::GetExitCode(runningProcess.get()));\n}\n\nvoid wsl::windows::common::install::MsiMessageCallback(INSTALLMESSAGE type, LPCWSTR message)\n{\n    switch (type)\n    {\n    case INSTALLMESSAGE_ERROR:\n    case INSTALLMESSAGE_FATALEXIT:\n    case INSTALLMESSAGE_WARNING:\n        wprintf(L\"%ls\\n\", message);\n        break;\n\n    default:\n        break;\n    }\n}\n\nint wsl::windows::common::install::UpdatePackage(bool PreRelease, bool Repair)\n{\n    // Register a console control handler so \"^C\" is not printed when the app platform terminates the process.\n    THROW_IF_WIN32_BOOL_FALSE(SetConsoleCtrlHandler(\n        [](DWORD ctrlType) {\n            if (ctrlType == CTRL_C_EVENT)\n            {\n                ExitProcess(0);\n            }\n            return FALSE;\n        },\n        TRUE));\n\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [] { SetConsoleCtrlHandler(nullptr, FALSE); });\n\n    try\n    {\n        return UpdatePackageImpl(PreRelease, Repair);\n    }\n    catch (...)\n    {\n        // Rethrowing via WIL is required for the error context to be properly set in case a winrt exception was thrown.\n        THROW_HR(wil::ResultFromCaughtException());\n    }\n}\n\nUINT wsl::windows::common::install::UpgradeViaMsi(\n    _In_ LPCWSTR PackageLocation, _In_opt_ LPCWSTR ExtraArgs, _In_opt_ LPCWSTR LogFile, _In_ const std::function<void(INSTALLMESSAGE, LPCWSTR)>& Callback)\n{\n    WriteInstallLog(std::format(\"Upgrading via MSI package: {}. Args: {}\", PackageLocation, ExtraArgs != nullptr ? ExtraArgs : L\"\"));\n\n    ConfigureMsiLogging(LogFile, Callback);\n\n    auto result = MsiInstallProduct(PackageLocation, ExtraArgs);\n    WSL_LOG(\n        \"MsiInstallResult\",\n        TraceLoggingValue(result, \"result\"),\n        TraceLoggingValue(ExtraArgs != nullptr ? ExtraArgs : L\"\", \"ExtraArgs\"));\n\n    WriteInstallLog(std::format(\"MSI upgrade result: {}\", result));\n\n    return result;\n}\n\nUINT wsl::windows::common::install::UninstallViaMsi(_In_opt_ LPCWSTR LogFile, _In_ const std::function<void(INSTALLMESSAGE, LPCWSTR)>& Callback)\n{\n    const auto key = OpenLxssMachineKey(KEY_READ);\n    const auto productCode = ReadString(key.get(), L\"Msi\", L\"ProductCode\", nullptr);\n\n    WriteInstallLog(std::format(\"Uninstalling MSI package: {}\", productCode));\n\n    ConfigureMsiLogging(LogFile, Callback);\n\n    auto result = MsiConfigureProduct(productCode.c_str(), 0, INSTALLSTATE_ABSENT);\n    WSL_LOG(\"MsiUninstallResult\", TraceLoggingValue(result, \"result\"));\n\n    WriteInstallLog(std::format(\"MSI package uninstall result: {}\", result));\n\n    return result;\n}\n\nwil::unique_hfile wsl::windows::common::install::ValidateFileSignature(LPCWSTR Path)\n{\n    wil::unique_hfile fileHandle{CreateFileW(Path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)};\n    THROW_LAST_ERROR_IF(!fileHandle);\n\n    GUID action = WINTRUST_ACTION_GENERIC_VERIFY_V2;\n    WINTRUST_DATA trust{};\n    trust.cbStruct = sizeof(trust);\n    trust.dwUIChoice = WTD_UI_NONE;\n    trust.dwUnionChoice = WTD_CHOICE_FILE;\n    trust.dwStateAction = WTD_STATEACTION_VERIFY;\n\n    WINTRUST_FILE_INFO file = {0};\n    file.cbStruct = sizeof(file);\n    file.hFile = fileHandle.get();\n    trust.pFile = &file;\n\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\n        trust.dwStateAction = WTD_STATEACTION_CLOSE;\n        WinVerifyTrust(nullptr, &action, &trust);\n    });\n\n    THROW_IF_WIN32_ERROR(WinVerifyTrust(nullptr, &action, &trust));\n\n    return fileHandle;\n}\n\nvoid wsl::windows::common::install::WriteInstallLog(const std::string& Content)\ntry\n{\n    static std::wstring path = wil::GetWindowsDirectoryW<std::wstring>() + L\"\\\\temp\\\\wsl-install-log.txt\";\n\n    // Wait up to 10 seconds for the log file mutex\n    wil::unique_handle mutex{CreateMutex(nullptr, true, L\"Global\\\\WslInstallLog\")};\n    THROW_LAST_ERROR_IF(!mutex);\n\n    THROW_LAST_ERROR_IF(WaitForSingleObject(mutex.get(), 10 * 1000) != WAIT_OBJECT_0);\n\n    wil::unique_handle file{CreateFile(\n        path.c_str(), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_ALWAYS, 0, nullptr)};\n\n    THROW_LAST_ERROR_IF(!file);\n\n    LARGE_INTEGER size{};\n    THROW_IF_WIN32_BOOL_FALSE(GetFileSizeEx(file.get(), &size));\n\n    // Append to the file if its size is below 10MB, otherwise truncate.\n    if (size.QuadPart < 10 * _1MB)\n    {\n        THROW_LAST_ERROR_IF(SetFilePointer(file.get(), 0, nullptr, FILE_END) == INVALID_SET_FILE_POINTER);\n    }\n    else\n    {\n        THROW_IF_WIN32_BOOL_FALSE(SetEndOfFile(file.get()));\n    }\n\n    static auto processName = wil::GetModuleFileNameW<std::wstring>();\n    auto logLine = std::format(\"{:%FT%TZ} {}[{}]: {}\\n\", std::chrono::system_clock::now(), processName, WSL_PACKAGE_VERSION, Content);\n\n    DWORD bytesWritten{};\n    THROW_IF_WIN32_BOOL_FALSE(WriteFile(file.get(), logLine.c_str(), static_cast<DWORD>(logLine.size()), &bytesWritten, nullptr));\n}\nCATCH_LOG();\n"
  },
  {
    "path": "src/windows/common/install.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    install.h\n\nAbstract:\n\n    This file contains MSI/Wintrust install helper function declarations.\n\n--*/\n\n#pragma once\n#include <functional>\n\nnamespace wsl::windows::common::install {\n\nint CallMsiPackage();\n\nvoid MsiMessageCallback(INSTALLMESSAGE type, LPCWSTR message);\n\nwil::unique_hfile ValidateFileSignature(LPCWSTR Path);\n\nint UpdatePackage(bool PreRelease, bool Repair);\n\nUINT UpgradeViaMsi(_In_ LPCWSTR PackageLocation, _In_opt_ LPCWSTR ExtraArgs, _In_opt_ LPCWSTR LogFile, _In_ const std::function<void(INSTALLMESSAGE, LPCWSTR)>& callback);\n\nUINT UninstallViaMsi(_In_opt_ LPCWSTR LogFile, _In_ const std::function<void(INSTALLMESSAGE, LPCWSTR)>& callback);\n\nvoid WriteInstallLog(const std::string& Content);\n\n} // namespace wsl::windows::common::install\n"
  },
  {
    "path": "src/windows/common/interop.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    interop.cpp\n\nAbstract:\n\n    This file contains interop function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"interop.hpp\"\n#include \"helpers.hpp\"\n#include \"socket.hpp\"\n#include \"hvsocket.hpp\"\n#include \"relay.hpp\"\n#include \"LxssServerPort.h\"\n#include \"LxssMessagePort.h\"\n#include <gsl/algorithm>\n#include <gslhelpers.h>\n#include \"WslTelemetry.h\"\n\nnamespace {\n\nstd::wstring BuildEnvironment(gsl::span<gsl::byte> EnvironmentData);\n\nstd::string FormatCommandLine(gsl::span<gsl::byte> CommandLineData, USHORT CommandLineCount);\n\nstruct CreateProcessResult;\nDWORD ProcessInteropMessages(_In_ HANDLE CommunicationChannel, _Inout_ CreateProcessResult* Result);\n\nstruct CreateProcessParsed\n{\n    CreateProcessParsed(_In_ const gsl::span<gsl::byte>& Common)\n    {\n        // Validate the message size and get spans to the various buffers. Note\n        // that the spans will be larger than the actual data since the message\n        // does not specify the size of each data element; the length is encoded\n        // via NULL termination.\n\n        const auto* Params = gslhelpers::try_get_struct<LX_INIT_CREATE_NT_PROCESS_COMMON>(Common);\n        THROW_HR_IF(E_INVALIDARG, !Params || (Common.size() < (Params->CommandLineOffset)));\n\n        // Parse the application name, command line, and current working directory\n        // and convert to unicode.\n\n        ApplicationName = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(Common, Params->FilenameOffset));\n        const auto FormattedCommandLine = FormatCommandLine(Common.subspan(Params->CommandLineOffset), Params->CommandLineCount);\n        CommandLineBuffer = wsl::shared::string::MultiByteToWide(FormattedCommandLine);\n        const auto* Cwd = wsl::shared::string::FromSpan(Common, Params->CurrentWorkingDirectoryOffset);\n        if (strlen(Cwd) > 0)\n        {\n            CwdBuffer = wsl::shared::string::MultiByteToWide(Cwd);\n        }\n\n        // Construct an environment if one was provided.\n\n        if (Params->EnvironmentOffset > 0)\n        {\n            THROW_HR_IF(E_INVALIDARG, Common.size() < Params->EnvironmentOffset);\n\n            EnvironmentBuffer = BuildEnvironment(Common.subspan(Params->EnvironmentOffset));\n        }\n\n        Rows = Params->Rows;\n        Columns = Params->Columns;\n        CreatePseudoconsole = Params->CreatePseudoconsole;\n    }\n\n    LPWSTR CommandLine()\n    {\n        return CommandLineBuffer.data();\n    }\n\n    LPCWSTR Cwd() const\n    {\n        return CwdBuffer.empty() ? nullptr : CwdBuffer.c_str();\n    }\n\n    LPVOID Environment()\n    {\n        return EnvironmentBuffer.empty() ? nullptr : EnvironmentBuffer.data();\n    }\n\n    std::wstring ApplicationName{};\n    std::wstring CommandLineBuffer{};\n    std::wstring EnvironmentBuffer{};\n    std::wstring CwdBuffer{};\n    DWORD Rows{};\n    DWORD Columns{};\n    bool CreatePseudoconsole{};\n};\n\nstruct CreateProcessResult\n{\n    wil::unique_handle Process{};\n    int Status{};\n    unsigned int Flags{};\n    wsl::windows::common::helpers::unique_pseudo_console PseudoConsole{};\n};\n\nstruct CreateProcessVmModeContext\n{\n    GUID VmId{};\n    std::vector<gsl::byte> Buffer{};\n};\n\nstd::wstring BuildEnvironment(gsl::span<gsl::byte> EnvironmentData)\n{\n    std::map<std::wstring, std::wstring> Environment;\n\n    // Construct a map of the current environment strings.\n    const wsl::windows::common::helpers::unique_environment_strings EnvironmentStrings(GetEnvironmentStrings());\n    PCZZWSTR CurrentEnvironment = EnvironmentStrings.get();\n    while (*CurrentEnvironment)\n    {\n        const PCWSTR Divider = wcschr(CurrentEnvironment, '=');\n        THROW_HR_IF_NULL(E_UNEXPECTED, Divider);\n        std::wstring Key(CurrentEnvironment, Divider);\n        Environment[Key] = Divider + 1;\n        CurrentEnvironment += wcslen(CurrentEnvironment) + 1;\n    }\n\n    // Update the map with the Linux environment data.\n    for (;;)\n    {\n        std::string_view Variable = wsl::shared::string::FromSpan(EnvironmentData);\n        if (Variable.empty())\n        {\n            break;\n        }\n\n        const size_t Divider = Variable.find('=');\n        THROW_HR_IF(E_UNEXPECTED, Divider == Variable.npos);\n        std::wstring Key = wsl::shared::string::MultiByteToWide(std::string{Variable.substr(0, Divider)});\n        auto Value = Variable.substr(Divider + 1);\n        if (Value.empty())\n        {\n            Environment.erase(Key);\n        }\n        else\n        {\n            Environment[Key] = wsl::shared::string::MultiByteToWide(std::string{Value});\n        }\n\n        EnvironmentData = EnvironmentData.subspan(Variable.size() + 1);\n    }\n\n    // Construct a new environment block.\n    std::wstring Block;\n    for (const auto& Variable : Environment)\n    {\n        Block += Variable.first;\n        Block += L'=';\n        Block += Variable.second;\n        Block += L'\\0';\n    }\n\n    return Block;\n}\n\nHRESULT GetProcessImageSubSystem(_In_ HANDLE Process, _Out_ ULONG* ImageSubsystem)\n{\n    *ImageSubsystem = IMAGE_SUBSYSTEM_UNKNOWN;\n\n    PROCESS_BASIC_INFORMATION ProcessBasicInfo{};\n    RETURN_IF_NTSTATUS_FAILED(NtQueryInformationProcess(Process, ProcessBasicInformation, &ProcessBasicInfo, sizeof(ProcessBasicInfo), NULL));\n\n    // Terminal uses a similar method to read the PEB.\n    // See: https://github.com/microsoft/terminal/blob/ec434e3fba2a6ef254123e31f5257c25b04f2547/src/tools/ConsoleBench/conhost.cpp#L159-L164\n    PEB* dummyPeb = nullptr;\n    const auto offset = ((char*)&dummyPeb->Reserved9[24]) - (char*)dummyPeb;\n\n    SIZE_T BytesRead = 0;\n    RETURN_IF_WIN32_BOOL_FALSE(ReadProcessMemory(\n        Process, ((char*)ProcessBasicInfo.PebBaseAddress) + offset, ImageSubsystem, sizeof(*ImageSubsystem), &BytesRead));\n\n    if (BytesRead < sizeof(*ImageSubsystem))\n    {\n        *ImageSubsystem = IMAGE_SUBSYSTEM_UNKNOWN;\n        return E_UNEXPECTED;\n    }\n\n    return S_OK;\n}\n\nCreateProcessResult CreateProcess(_In_ CreateProcessParsed* Parsed, _In_ HANDLE StdIn, _In_ HANDLE StdOut, _In_ HANDLE StdErr)\n{\n    wsl::windows::common::helpers::SetHandleInheritable(StdIn);\n    wsl::windows::common::helpers::SetHandleInheritable(StdOut);\n    wsl::windows::common::helpers::SetHandleInheritable(StdErr);\n\n    // N.B. Passing StartupFlags = 0 so that the cursor feedback is set to its default behavior.\n    // See: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa\n    wsl::windows::common::SubProcess process(Parsed->ApplicationName.c_str(), Parsed->CommandLine(), CREATE_UNICODE_ENVIRONMENT, 0);\n\n    CreateProcessResult Result{};\n    if (Parsed->CreatePseudoconsole)\n    {\n        const COORD Size{static_cast<SHORT>(Parsed->Columns), static_cast<SHORT>(Parsed->Rows)};\n        THROW_IF_FAILED(CreatePseudoConsole(Size, StdIn, StdOut, PSEUDOCONSOLE_INHERIT_CURSOR, &Result.PseudoConsole));\n\n        process.SetPseudoConsole(Result.PseudoConsole.get());\n    }\n    else\n    {\n        // In the case where this is a console process, don't create a new console window.\n        // This is useful for wslg.exe, when a console program is created through interop,\n        // we don't want to create a new console window.\n        // N.B. CREATE_NO_WINDOW only applies to console executables, so GUI applications\n        // are not affected by this flag.\n        process.SetFlags(CREATE_NO_WINDOW);\n        process.SetStdHandles(StdIn, StdOut, StdErr);\n    }\n\n    // Set the breakaway override flag to ensure that processes created via interop are not packaged.\n    process.SetDesktopAppPolicy(PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_OVERRIDE);\n    process.SetEnvironment(Parsed->Environment());\n    process.SetWorkingDirectory(Parsed->Cwd());\n\n    try\n    {\n        Result.Process = process.Start();\n        // Check if the process that was launched is a graphical application.\n        // Non-graphical applications should be terminated when the file\n        // descriptor that represents the process is closed.\n        ULONG ImageSubsystem = IMAGE_SUBSYSTEM_UNKNOWN;\n        LOG_IF_FAILED(GetProcessImageSubSystem(Result.Process.get(), &ImageSubsystem));\n        const bool IsGuiApp = (ImageSubsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI);\n        WI_SetFlagIf(Result.Flags, LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION, IsGuiApp);\n    }\n    catch (...)\n    {\n        const DWORD LastError = wil::ResultFromCaughtException();\n        switch (LastError)\n        {\n        case ERROR_FILE_NOT_FOUND:\n            Result.Status = -LX_ENOENT;\n            break;\n\n        case ERROR_ELEVATION_REQUIRED:\n            Result.Status = -LX_EACCES;\n            break;\n\n        default:\n            Result.Status = -LX_EINVAL;\n            LOG_IF_WIN32_ERROR_MSG(LastError, \"CreateProcessW\");\n            break;\n        }\n    }\n\n    return Result;\n}\n\nvoid CreateProcessVmMode(_In_ const GUID& VmId, _In_ const gsl::span<gsl::byte>& Buffer)\n{\n    // Create a worker thread to service the interop request.\n    //\n    // N.B. The worker thread takes ownership of the arguments.\n    auto Arguments = std::make_unique<CreateProcessVmModeContext>();\n    Arguments->VmId = VmId;\n    Arguments->Buffer.resize(Buffer.size());\n    gsl::copy(Buffer, gsl::make_span(Arguments->Buffer));\n    std::thread([Arguments = std::move(Arguments)]() {\n        try\n        {\n            wsl::windows::common::wslutil::SetThreadDescription(L\"Interop\");\n            auto Message = gsl::make_span(Arguments->Buffer);\n            auto* Params = gslhelpers::try_get_struct<LX_INIT_CREATE_NT_PROCESS_UTILITY_VM>(Message);\n            THROW_HR_IF(E_INVALIDARG, !Params || (Params->Header.MessageType != LxInitMessageCreateProcessUtilityVm));\n\n            // Parse the message.\n            CreateProcessParsed Parsed(Message.subspan(offsetof(LX_INIT_CREATE_NT_PROCESS_UTILITY_VM, Common)));\n\n            // Establish connections on the specified port.\n            static_assert(LX_INIT_CREATE_NT_PROCESS_SOCKETS == 4);\n\n            wil::unique_socket Sockets[LX_INIT_CREATE_NT_PROCESS_SOCKETS];\n            for (ULONG Index = 0; Index < RTL_NUMBER_OF(Sockets); Index += 1)\n            {\n                Sockets[Index] = wsl::windows::common::hvsocket::Connect(Arguments->VmId, Params->Port);\n            }\n\n            // Clean up relay threads.\n            //\n            // N.B. This must be declared before the stdin / stdout / stderr handles so they go out of scope first.\n            std::vector<std::thread> Relays;\n            auto CancelIo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&Relays] {\n                for (auto& Relay : Relays)\n                {\n                    if (Relay.joinable())\n                    {\n                        Relay.join();\n                    }\n                }\n            });\n\n            // If a pseudoconsole is not being used, create hvsocket / pipe relays.\n            wil::unique_handle StdIn{reinterpret_cast<HANDLE>(Sockets[0].release())};\n            wil::unique_handle StdOut{reinterpret_cast<HANDLE>(Sockets[1].release())};\n            wil::unique_handle StdErr{reinterpret_cast<HANDLE>(Sockets[2].release())};\n            if (Parsed.CreatePseudoconsole == FALSE)\n            {\n                auto Pipe = wsl::windows::common::wslutil::OpenAnonymousPipe(0, false, true);\n                Relays.push_back(wsl::windows::common::relay::CreateThread(std::move(StdIn), wil::unique_handle{Pipe.second.release()}));\n                StdIn.reset(Pipe.first.release());\n\n                Pipe = wsl::windows::common::wslutil::OpenAnonymousPipe(0, true, false);\n                Relays.push_back(wsl::windows::common::relay::CreateThread(wil::unique_handle{Pipe.first.release()}, std::move(StdOut)));\n                StdOut.reset(Pipe.second.release());\n\n                Pipe = wsl::windows::common::wslutil::OpenAnonymousPipe(0, true, false);\n                Relays.push_back(wsl::windows::common::relay::CreateThread(wil::unique_handle{Pipe.first.release()}, std::move(StdErr)));\n                StdErr.reset(Pipe.second.release());\n            }\n\n            // Launch the process and write the status via the control channel.\n            auto Result = CreateProcess(&Parsed, StdIn.get(), StdOut.get(), StdErr.get());\n            LX_INIT_CREATE_PROCESS_RESPONSE Response{};\n            Response.Header.MessageType = LxInitMessageCreateProcessResponse;\n            Response.Header.MessageSize = sizeof(Response);\n            Response.Flags = Result.Flags;\n            Response.Result = Result.Status;\n            wsl::windows::common::socket::Send(Sockets[3].get(), gslhelpers::struct_as_bytes(Response));\n            if (Result.Status == 0)\n            {\n                // Process messages from the binfmt interpreter and wait for the process to exit.\n                LX_INIT_PROCESS_EXIT_STATUS ExitStatus;\n                ExitStatus.Header.MessageType = LxInitMessageExitStatus;\n                ExitStatus.Header.MessageSize = sizeof(ExitStatus);\n                ExitStatus.ExitCode = ProcessInteropMessages(reinterpret_cast<HANDLE>(Sockets[3].get()), &Result);\n\n                // Write the exit status to the binfmt interpreter.\n                wsl::windows::common::socket::Send(Sockets[3].get(), gslhelpers::struct_as_bytes(ExitStatus));\n            }\n        }\n        CATCH_LOG()\n    }).detach();\n}\n\nstd::string FormatCommandLine(gsl::span<gsl::byte> CommandLineData, USHORT CommandLineCount)\n{\n    // Concatenate all of the command line arguments into a single string for\n    // the CreateProcess api.\n    //\n    // N.B. Any empty arguments or arguments that contain whitespace must be\n    //      encapsulated in quotes. Quotes must also be escaped according to\n    //      standard command-line parsing rules:\n    //      https://msdn.microsoft.com/en-us/library/17w5ykft.aspx.\n    //\n    //      This logic is largely taken from AppendQuotedForWindows in\n    //      hcsdiag.cpp. In the future it would make sense to merge this\n    //      functionality.\n    std::string CommandLine;\n    for (USHORT Index = 0; Index < CommandLineCount; Index += 1)\n    {\n        std::string_view Buffer = wsl::shared::string::FromSpan(CommandLineData);\n        if (!Buffer.empty() && Buffer.find_first_of(\" \\t\\r\\n\\\"\") == Buffer.npos)\n        {\n            CommandLine.append(Buffer);\n        }\n        else\n        {\n            CommandLine += '\"';\n            size_t BackslashCount = 0;\n            for (const char Ch : Buffer)\n            {\n                switch (Ch)\n                {\n                case '\"':\n                    CommandLine.append(((BackslashCount * 2) + 1), '\\\\');\n                    BackslashCount = 0;\n                    CommandLine += '\"';\n                    break;\n\n                case '\\\\':\n                    BackslashCount += 1;\n                    break;\n\n                default:\n                    CommandLine.append(BackslashCount, '\\\\');\n                    BackslashCount = 0;\n                    CommandLine += Ch;\n                    break;\n                }\n            }\n\n            CommandLine.append(BackslashCount * 2, '\\\\');\n            CommandLine += '\"';\n        }\n\n        // Add a space between command line arguments.\n        if (Index < CommandLineCount - 1)\n        {\n            CommandLine += ' ';\n        }\n\n        CommandLineData = CommandLineData.subspan(Buffer.size() + 1);\n    }\n\n    return CommandLine;\n}\n\nDWORD\nProcessInteropMessages(_In_ HANDLE MessageHandle, _Inout_ CreateProcessResult* Result)\n{\n    OVERLAPPED Overlapped = {0};\n    const wil::unique_event OverlappedEvent(wil::EventOptions::ManualReset);\n    Overlapped.hEvent = OverlappedEvent.get();\n    const HANDLE WaitHandles[] = {Overlapped.hEvent, Result->Process.get()};\n\n    // Read messages from the message handle. Break out of the loop if the pipe\n    // is connection is closed or the process exits.\n    //\n    // N.B. ReadFile will automatically reset the event in the overlapped\n    //      structure.\n    DWORD ExitCode = 1;\n    for (;;)\n    {\n        DWORD BytesRead;\n        LX_INIT_WINDOW_SIZE_CHANGED WindowSizeMessage;\n        bool Success = ReadFile(MessageHandle, &WindowSizeMessage, sizeof(WindowSizeMessage), &BytesRead, &Overlapped);\n        if (!Success)\n        {\n            const auto LastError = GetLastError();\n            if ((LastError == ERROR_BROKEN_PIPE) || (LastError == ERROR_HANDLE_EOF))\n            {\n                if (WI_IsFlagClear(Result->Flags, LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION))\n                {\n                    THROW_IF_WIN32_BOOL_FALSE(TerminateProcess(Result->Process.get(), 1));\n                }\n\n                break;\n            }\n\n            THROW_LAST_ERROR_IF(LastError != ERROR_IO_PENDING);\n\n            auto CancelIo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n                CancelIoEx(MessageHandle, &Overlapped);\n                GetOverlappedResult(MessageHandle, &Overlapped, &BytesRead, TRUE);\n            });\n\n            const DWORD WaitStatus = WaitForMultipleObjects(RTL_NUMBER_OF(WaitHandles), WaitHandles, FALSE, INFINITE);\n            if (WaitStatus == WAIT_OBJECT_0)\n            {\n                Success = GetOverlappedResult(MessageHandle, &Overlapped, &BytesRead, FALSE);\n                CancelIo.release();\n                if ((!Success) || (BytesRead == 0))\n                {\n                    if (WI_IsFlagClear(Result->Flags, LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION))\n                    {\n                        THROW_IF_WIN32_BOOL_FALSE(TerminateProcess(Result->Process.get(), 1));\n                    }\n\n                    break;\n                }\n\n                WI_ASSERT((BytesRead == sizeof(WindowSizeMessage)) && (WindowSizeMessage.Header.MessageType == LxInitMessageWindowSizeChanged));\n\n                const COORD Size{static_cast<SHORT>(WindowSizeMessage.Columns), static_cast<SHORT>(WindowSizeMessage.Rows)};\n                THROW_IF_FAILED(ResizePseudoConsole(Result->PseudoConsole.get(), Size));\n            }\n            else if (WaitStatus == (WAIT_OBJECT_0 + 1))\n            {\n                THROW_IF_WIN32_BOOL_FALSE(GetExitCodeProcess(Result->Process.get(), &ExitCode));\n\n                // Close the pseudoconsole, this causes all pending data to be flushed.\n                Result->PseudoConsole.reset();\n                break;\n            }\n            else\n            {\n                THROW_HR(E_UNEXPECTED);\n            }\n        }\n    }\n\n    return ExitCode;\n}\n\n} // namespace\n\nvoid wsl::windows::common::interop::WorkerThread(_In_ wil::unique_handle&& ServerPortHandle)\n{\n    // This thread waits for connections and processes create process messages.\n    //\n    // N.B. This thread lives until the main thread of the process exits.\n    //\n    // TODO_LX: Wait for connection blocks in the driver until the server port\n    //          is closed. The wait for connection ioctl should be moved to\n    //          async so this thread can wait on the server port and a second\n    //          event indicating that the thread should exit.\n\n    LxssServerPort ServerPort(std::move(ServerPortHandle));\n    for (;;)\n    {\n        try\n        {\n            // Wait for a client to connect, break out of the loop on disconnect.\n            std::unique_ptr<LxssMessagePort> MessagePort;\n            if (!NT_SUCCESS(ServerPort.WaitForConnectionNoThrow(&MessagePort)))\n            {\n                break;\n            }\n\n            std::thread([MessagePort = std::move(MessagePort)]() mutable {\n                try\n                {\n                    // Read the create process request from the client.\n                    auto CreateProcessMessage = MessagePort->Receive();\n                    const auto Message = gsl::make_span(CreateProcessMessage);\n                    const auto* Params = gslhelpers::try_get_struct<LX_INIT_CREATE_NT_PROCESS>(Message);\n                    THROW_HR_IF(E_INVALIDARG, !Params || (Params->Header.MessageType != LxInitMessageCreateProcess));\n\n                    // Parse the message.\n                    CreateProcessParsed Parsed(Message.subspan(offsetof(LX_INIT_CREATE_NT_PROCESS, Common)));\n\n                    // Unmarshal the handles to be used as stdin / stdout / stederr and mark\n                    // them as inheritable.\n                    static_assert(LX_INIT_STD_FD_COUNT == 3);\n\n                    wil::unique_handle StdHandles[LX_INIT_STD_FD_COUNT];\n                    for (ULONG Index = 0; Index < LX_INIT_STD_FD_COUNT; Index += 1)\n                    {\n                        StdHandles[Index] = MessagePort->UnmarshalVfsFile(Params->StdFdIds[Index]);\n                    }\n\n                    // Create the signal pipe to handle resize requests.\n                    auto SignalPipe = wsl::windows::common::wslutil::OpenAnonymousPipe(0, true, true);\n\n                    // Launch the process.\n                    auto Result = CreateProcess(&Parsed, StdHandles[0].get(), StdHandles[1].get(), StdHandles[2].get());\n\n                    // Send a response back to the init daemon.\n                    LX_INIT_CREATE_PROCESS_RESPONSE Response{};\n                    Response.Header.MessageType = LxInitMessageCreateProcessResponse;\n                    Response.Header.MessageSize = sizeof(Response);\n                    Response.Flags = Result.Flags;\n                    Response.Result = Result.Status;\n                    if (Result.Status == 0)\n                    {\n                        // Marshal the write end of the signal pipe.\n                        const LXBUS_IPC_MESSAGE_MARSHAL_HANDLE_DATA HandleData{HandleToUlong(SignalPipe.second.get()), LxBusIpcMarshalHandleTypeOutput};\n                        Response.SignalPipeId = MessagePort->MarshalHandle(&HandleData);\n                        SignalPipe.second.reset();\n\n                        // Write the response to the binfmt interpreter.\n                        MessagePort->Send(&Response, sizeof(Response));\n\n                        // Process messages from the binfmt interpreter and wait for the\n                        // process to exit.\n                        LX_INIT_PROCESS_EXIT_STATUS ExitStatus;\n                        ExitStatus.Header.MessageType = LxInitMessageExitStatus;\n                        ExitStatus.Header.MessageSize = sizeof(ExitStatus);\n                        ExitStatus.ExitCode = ProcessInteropMessages(SignalPipe.first.get(), &Result);\n\n                        // Write the exit status to the binfmt interpreter.\n                        MessagePort->Send(&ExitStatus, sizeof(ExitStatus));\n                    }\n                    else\n                    {\n                        MessagePort->Send(&Response, sizeof(Response));\n                    }\n                }\n                CATCH_LOG()\n            }).detach();\n        }\n        CATCH_LOG()\n    }\n}\n\nDWORD\nwsl::windows::common::interop::VmModeWorkerThread(_In_ wsl::shared::SocketChannel& Channel, _In_ const GUID& VmId, _In_ bool IgnoreExit)\n{\n    std::vector<gsl::byte> Buffer;\n\n    for (;;)\n    {\n        auto [Message, Span] = Channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\n        if (Message == nullptr)\n        {\n            break;\n        }\n\n        switch (Message->MessageType)\n        {\n        case LxInitMessageExitStatus:\n        {\n            const auto* ExitStatusMessage = gslhelpers::try_get_struct<LX_INIT_PROCESS_EXIT_STATUS>(Span);\n            THROW_HR_IF(E_INVALIDARG, !ExitStatusMessage);\n\n            Channel.SendMessage<LX_INIT_PROCESS_EXIT_STATUS>(Span);\n\n            if (!IgnoreExit)\n            {\n                return ExitStatusMessage->ExitCode;\n            }\n\n            break;\n        }\n\n        case LxInitMessageCreateProcessUtilityVm:\n            THROW_HR_IF(E_INVALIDARG, (Span.size() < sizeof(LX_INIT_CREATE_PROCESS_UTILITY_VM)));\n\n            CreateProcessVmMode(VmId, Span);\n            break;\n\n        default:\n            THROW_HR_MSG(E_UNEXPECTED, \"Unexpected message %d\", Message->MessageType);\n        }\n    }\n\n    return 1;\n}\n"
  },
  {
    "path": "src/windows/common/interop.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    interop.hpp\n\nAbstract:\n\n    This file contains interop function declarations.\n\n--*/\n\n#pragma once\n\n#include <winsock2.h>\n\nnamespace wsl::windows::common::interop {\n\nvoid WorkerThread(_In_ wil::unique_handle&& ServerPortHandle);\n\nDWORD VmModeWorkerThread(_In_ wsl::shared::SocketChannel& channel, _In_ const GUID& VmId, _In_ bool IgnoreExit = false);\n\n} // namespace wsl::windows::common::interop\n"
  },
  {
    "path": "src/windows/common/lxssbusclient.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    lxbusclient.cpp\n\nAbstract:\n\n    This file contains the LxBus client library implementation.\n\n--*/\n\n#include \"precomp.h\"\n\nNTSTATUS\nLxBusClientpIoctlInternal(\n    _In_ HANDLE Handle,\n    _In_opt_ HANDLE Event,\n    _Inout_ PIO_STATUS_BLOCK IoStatusBlock,\n    _In_ ULONG ControlCode,\n    _In_reads_bytes_(InputBufferSize) PVOID InputBuffer,\n    _In_ ULONG InputBufferSize,\n    _Out_writes_bytes_(OutputBufferSize) PVOID OutputBuffer,\n    _In_ ULONG OutputBufferSize);\n\nNTSTATUS\nLxBusClientCreateUnnamedServer(_In_ HANDLE MessagePortHandle, _Out_ PLXBUS_IPC_MESSAGE_CREATE_UNNAMED_SERVER_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine creates an unnamed server with an LxBus message port.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to a message port.\n\n    Parameters - Supplies a pointer to the create unnamed server parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_CREATE_UNNAMED_SERVER, NULL, 0, Parameters, sizeof(*Parameters));\n}\n\nNTSTATUS\nLxBusClientDisconnectConsole(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_DISCONNECT_CONSOLE_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a disconnect console ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the disconnect console parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_DISCONNECT_CONSOLE, Parameters, sizeof(*Parameters), NULL, 0);\n}\n\nNTSTATUS\nLxBusClientMarshalConsole(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_MARSHAL_CONSOLE_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a marshal console ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the marshal console parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(\n        MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_MARSHAL_CONSOLE, Parameters, sizeof(*Parameters), Parameters, sizeof(*Parameters));\n}\n\nNTSTATUS\nLxBusClientReleaseConsole(_In_ HANDLE MessagePortHandle, _In_ PLXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a cleanup console ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the release console parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL, Parameters, sizeof(*Parameters), NULL, 0);\n}\n\nNTSTATUS\nLxBusClientMarshalForkToken(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_MARSHAL_FORK_TOKEN_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a marshal fork token ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the marshal fork token parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(\n        MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_MARSHAL_FORK_TOKEN, Parameters, sizeof(*Parameters), Parameters, sizeof(*Parameters));\n}\n\nNTSTATUS\nLxBusClientReleaseForkToken(_In_ HANDLE MessagePortHandle, _In_ PLXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a cleanup fork token ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the release fork token parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL, Parameters, sizeof(*Parameters), NULL, 0);\n}\n\nNTSTATUS\nLxBusClientMarshalHandle(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_MARSHAL_HANDLE_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a marshal handle ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the marshal handle parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(\n        MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_MARSHAL_HANDLE, Parameters, sizeof(*Parameters), Parameters, sizeof(*Parameters));\n}\n\nNTSTATUS\nLxBusClientReleaseHandle(_In_ HANDLE MessagePortHandle, _In_ PLXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a cleanup handle ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the release handle parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL, Parameters, sizeof(*Parameters), NULL, 0);\n}\n\nNTSTATUS\nLxBusClientMarshalProcess(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_MARSHAL_PROCESS_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a marshal process ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the marshal process parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(\n        MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_MARSHAL_PROCESS, Parameters, sizeof(*Parameters), Parameters, sizeof(*Parameters));\n}\n\nNTSTATUS\nLxBusClientpIoctl(\n    _In_ HANDLE Handle,\n    _In_ ULONG ControlCode,\n    _In_reads_bytes_(InputBufferSize) PVOID InputBuffer,\n    _In_ ULONG InputBufferSize,\n    _Out_writes_bytes_(OutputBufferSize) PVOID OutputBuffer,\n    _In_ ULONG OutputBufferSize)\n\n/*++\n\nRoutine Description:\n\n    This routine implements the lxbus control device IOCTL handler.\n\nArguments:\n\n    Handle - Supplies the handle to issue the ioctl to.\n\n    ControlCode - Supplies ioctl to be processed.\n\n    InputBuffer - Supplies the input buffer as passed by user.\n\n    InputBufferSize - Supplies the size of the input buffer.\n\n    OutputBuffer - Supplies a output buffer as passed by the user.\n\n    OutputBufferSize - Supplies the size of the output buffer.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    NTSTATUS Status;\n    IO_STATUS_BLOCK IoStatus;\n\n    ZeroMemory(&IoStatus, sizeof(IoStatus));\n    Status = LxBusClientpIoctlInternal(Handle, NULL, &IoStatus, ControlCode, InputBuffer, InputBufferSize, OutputBuffer, OutputBufferSize);\n\n    WI_ASSERT(Status != STATUS_PENDING);\n\n    WI_ASSERT(!NT_SUCCESS(Status) || (OutputBufferSize == IoStatus.Information));\n\n    return Status;\n}\n\nNTSTATUS\nLxBusClientpIoctlInternal(\n    _In_ HANDLE Handle,\n    _In_opt_ HANDLE Event,\n    _Inout_ PIO_STATUS_BLOCK IoStatus,\n    _In_ ULONG ControlCode,\n    _In_reads_bytes_(InputBufferSize) PVOID InputBuffer,\n    _In_ ULONG InputBufferSize,\n    _Out_writes_bytes_(OutputBufferSize) PVOID OutputBuffer,\n    _In_ ULONG OutputBufferSize)\n\n/*++\n\nRoutine Description:\n\n    This routine implements the lxbus control device IOCTL handler.\n\nArguments:\n\n    Handle - Supplies the handle to issue the ioctl to.\n\n    Event - Supplies an optional event used for async IO.\n\n    IoStatus - Supplies a pointer to the IO status block.\n\n    ControlCode - Supplies ioctl to be processed.\n\n    InputBuffer - Supplies the input buffer as passed by user.\n\n    InputBufferSize - Supplies the size of the input buffer.\n\n    OutputBuffer - Supplies a output buffer as passed by the user.\n\n    OutputBufferSize - Supplies the size of the output buffer.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    NTSTATUS Status;\n\n    if (Handle == NULL)\n    {\n        Status = STATUS_DEVICE_NOT_CONNECTED;\n        goto LxBusClientpIoctlInternalEnd;\n    }\n\n    Status = NtDeviceIoControlFile(Handle, Event, NULL, NULL, IoStatus, ControlCode, InputBuffer, InputBufferSize, OutputBuffer, OutputBufferSize);\n\nLxBusClientpIoctlInternalEnd:\n    return Status;\n}\n\nNTSTATUS\nLxBusClientReceiveMessage(_In_ HANDLE MessagePortHandle, _Out_writes_to_(BufferSize, *SizeReceived) PVOID Buffer, _In_ ULONG BufferSize, _Out_ PULONG SizeReceived)\n\n/*++\n\nRoutine Description:\n\n    This routine receives a message from the given message port synchronously.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to a message port.\n\n    Buffer - Supplies a pointer to a buffer.\n\n    BufferSize - Supplies the size of the buffer in bytes.\n\n    SizeReceived - Supplies a buffer to store the number of bytes received.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    HANDLE Event;\n    IO_STATUS_BLOCK IoStatus;\n    NTSTATUS Status;\n\n    Event = NULL;\n    RtlZeroMemory(&IoStatus, sizeof(IoStatus));\n    Status = ZwCreateEvent(&Event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);\n\n    if (!NT_SUCCESS(Status))\n    {\n        goto LxBusClientReceiveMessageEnd;\n    }\n\n    Status = LxBusClientReceiveMessageAsync(MessagePortHandle, Buffer, BufferSize, SizeReceived, &IoStatus, Event);\n\n    if (!NT_SUCCESS(Status))\n    {\n        goto LxBusClientReceiveMessageEnd;\n    }\n\n    if (Status == STATUS_PENDING)\n    {\n        Status = NtWaitForSingleObject(Event, FALSE, NULL);\n        if (!NT_SUCCESS(Status))\n        {\n            goto LxBusClientReceiveMessageEnd;\n        }\n    }\n\n    WI_ASSERT(NT_SUCCESS(Status));\n\n    WI_ASSERT(IoStatus.Information <= ULONG_MAX);\n\n    *SizeReceived = (ULONG)IoStatus.Information;\n    Status = IoStatus.Status;\n\nLxBusClientReceiveMessageEnd:\n    if (Event != NULL)\n    {\n        NtClose(Event);\n    }\n\n    return Status;\n}\n\nNTSTATUS\nLxBusClientReceiveMessageAsync(\n    _In_ HANDLE MessagePortHandle,\n    _Out_writes_bytes_to_(BufferSize, *SizeReceived) PVOID Buffer,\n    _In_ ULONG BufferSize,\n    _Out_ PULONG SizeReceived,\n    _Out_ PIO_STATUS_BLOCK IoStatus,\n    _Inout_opt_ HANDLE Event)\n\n/*++\n\nRoutine Description:\n\n    This routine receives a message from the given message port asynchronously.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to a message port.\n\n    Buffer - Supplies a pointer to a buffer.\n\n    BufferSize - Supplies the size of the buffer in bytes.\n\n    SizeReceived - Supplies a buffer to store the number of bytes received.\n\n    IoStatus - Supplies a pointer to an io status block.\n\n    Event - Supplies an optional event to be signaled when the operation is\n        complete.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    LARGE_INTEGER ByteOffset{};\n    const NTSTATUS Status = NtReadFile(MessagePortHandle, Event, NULL, NULL, IoStatus, Buffer, BufferSize, &ByteOffset, NULL);\n    if (Status == STATUS_SUCCESS)\n    {\n        WI_ASSERT(IoStatus->Information <= ULONG_MAX);\n\n        *SizeReceived = (ULONG)IoStatus->Information;\n    }\n\n    return Status;\n}\n\nNTSTATUS\nLxBusClientRegisterServer(_In_ HANDLE LxBusHandle, _Inout_ PLXBUS_REGISTER_SERVER_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine registers an LxBus server with the given name.\n\nArguments:\n\n    LxBusHandle - The LxBusHandle on which to perform the registration.\n\n    Parameters - Supplies a pointer to the register server parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(LxBusHandle, LXBUS_IOCTL_REGISTER_SERVER, Parameters, sizeof(*Parameters), Parameters, sizeof(*Parameters));\n}\n\nNTSTATUS\nLxBusClientRegisterUserCallbackAsync(\n    _In_ HANDLE LxBusHandle,\n    _In_ HANDLE Event,\n    _Inout_ PIO_STATUS_BLOCK IoStatus,\n    _Inout_ PLXBUS_REGISTER_USER_CALLBACK_PARAMETERS Parameters,\n    _Out_ PVOID OutputBuffer,\n    _In_ ULONG OutputBufferSize)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a user-callback registration ioctl to the specified\n    instance handle.\n\nArguments:\n\n    LxBusHandle - Supplies ths instance handle to issue the ioctl to.\n\n    Event - Supplies an event to be signalled when the request has completed.\n\n    IoStatus - Supplies a pointer to the IO status block.\n\n    Parameters - Supplies a pointer to the user callback parameters.\n\n    OutputBuffer - Supplies a pointer to the output buffer.\n\n    OutputBufferSize - Supplies the size in bytes of the output buffer.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctlInternal(\n        LxBusHandle, Event, IoStatus, LXBUS_IOCTL_REGISTER_USER_CALLBACK, Parameters, sizeof(*Parameters), OutputBuffer, OutputBufferSize);\n}\n\nNTSTATUS\nLxBusClientSendMessage(_In_ HANDLE MessagePortHandle, _In_reads_bytes_(BufferSize) PVOID Buffer, _In_ ULONG BufferSize)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a message to the given message port synchronously.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to a message port.\n\n    Buffer - Supplies a pointer to a buffer.\n\n    BufferSize - Supplies the size of the buffer in bytes.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    HANDLE Event;\n    IO_STATUS_BLOCK IoStatus;\n    NTSTATUS Status;\n\n    Event = NULL;\n    RtlZeroMemory(&IoStatus, sizeof(IoStatus));\n    Status = ZwCreateEvent(&Event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);\n\n    if (!NT_SUCCESS(Status))\n    {\n        goto LxBusClientSendMessageEnd;\n    }\n\n    Status = LxBusClientSendMessageAsync(MessagePortHandle, Buffer, BufferSize, &IoStatus, Event);\n\n    if (!NT_SUCCESS(Status))\n    {\n        goto LxBusClientSendMessageEnd;\n    }\n\n    WI_ASSERT((Status != STATUS_SUCCESS) || (BufferSize == IoStatus.Information));\n\n    if (Status == STATUS_PENDING)\n    {\n        Status = NtWaitForSingleObject(Event, FALSE, NULL);\n        if (!NT_SUCCESS(Status))\n        {\n            goto LxBusClientSendMessageEnd;\n        }\n    }\n\n    Status = IoStatus.Status;\n\nLxBusClientSendMessageEnd:\n    if (Event != NULL)\n    {\n        NtClose(Event);\n    }\n\n    return Status;\n}\n\nNTSTATUS\nLxBusClientSendMessageAsync(\n    _In_ HANDLE MessagePortHandle, _In_reads_bytes_(BufferSize) PVOID Buffer, _In_ ULONG BufferSize, _Out_ PIO_STATUS_BLOCK IoStatus, _Inout_opt_ HANDLE Event)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a message to the given message port asynchronously.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to a message port.\n\n    Buffer - Supplies a pointer to a buffer.\n\n    BufferSize - Supplies the size of the buffer in bytes.\n\n    IoStatus - Supplies a pointer to an io status block.\n\n    Event - Supplies an optional event to be signaled when the operation is\n        complete.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    LARGE_INTEGER ByteOffset{};\n    const NTSTATUS Status = NtWriteFile(MessagePortHandle, Event, NULL, NULL, IoStatus, Buffer, BufferSize, &ByteOffset, NULL);\n\n    WI_ASSERT((Status != STATUS_SUCCESS) || (BufferSize == IoStatus->Information));\n\n    return Status;\n}\n\nNTSTATUS\nLxBusClientUnmarshalProcess(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_UNMARSHAL_PROCESS_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a unmarshal process ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the unmarshal process parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(\n        MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_PROCESS, Parameters, sizeof(*Parameters), Parameters, sizeof(*Parameters));\n}\n\nNTSTATUS\nLxBusClientUnmarshalVfsFile(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_UNMARSHAL_VFS_FILE_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a unmarshal vfs file ioctl to the specified message port\n    handle.\n\nArguments:\n\n    MessagePortHandle - Supplies a handle to the message port to issue the\n        ioctl to.\n\n    Parameters - Supplies a pointer to the unmarshal vfs file parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(\n        MessagePortHandle, LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_VFS_FILE, Parameters, sizeof(*Parameters), Parameters, sizeof(*Parameters));\n}\n\nNTSTATUS\nLxBusClientUserCallbackSendResponse(_In_ HANDLE LxBusHandle, _Inout_ PLXBUS_REGISTER_USER_CALLBACK_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a response from a user-callback to the specified\n    instance handle.\n\nArguments:\n\n    LxBusHandle - Supplies ths instance handle to issue the ioctl to.\n\n    Parameters - Supplies a pointer to the user callback parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(LxBusHandle, LXBUS_IOCTL_REGISTER_USER_CALLBACK, Parameters, sizeof(*Parameters), NULL, 0);\n}\n\nNTSTATUS\nLxBusClientWaitForConnection(_In_ HANDLE ServerPortHandle, _Out_ PLXBUS_IPC_SERVER_WAIT_FOR_CONNECTION_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    Waits for a client connection on the provided server port.\n\nArguments:\n\n    ServerPortHandle - Supplies a handle to the server port.\n\n    Parameters - Supplies a pointer to the wait for connection parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(\n        ServerPortHandle, LXBUS_IPC_SERVER_IOCTL_WAIT_FOR_CONNECTION, Parameters, sizeof(*Parameters), Parameters, sizeof(*Parameters));\n}\n\nNTSTATUS\nLxBusClientWaitForLxProcess(_In_ HANDLE LxProcessHandle, _Out_ PLXBUS_IPC_LX_PROCESS_WAIT_FOR_TERMINATION_PARAMETERS Parameters)\n\n/*++\n\nRoutine Description:\n\n    Waits for a client connection on the provided server port.\n\nArguments:\n\n    LxProcessHandle - Supplies a handle to the LX process.\n\n    Parameters - Supplies a pointer to the wait for termination parameters.\n\nReturn Value:\n\n    NT status code.\n\n--*/\n\n{\n    return LxBusClientpIoctl(\n        LxProcessHandle, LXBUS_IPC_LX_PROCESS_IOCTL_WAIT_FOR_TERMINATION, Parameters, sizeof(*Parameters), Parameters, sizeof(*Parameters));\n}\n"
  },
  {
    "path": "src/windows/common/lxssclient.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    lxssclient.cpp\n\nAbstract:\n\n    This file contains the LXSS client library implementation.\n\n--*/\n\n#include \"precomp.h\"\n#include \"lxssbusclient.h\"\n\nHANDLE LxssRootHandle = NULL;\n\nNTSTATUS\nLxssClientInitialize(VOID)\n\n/*++\n\nRoutine Description:\n\n    Initializes a new LXSS client by connecting to the LXSS driver.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    NTSTATUS.\n\n--*/\n\n{\n    UNICODE_STRING ControlDevicePath;\n    IO_STATUS_BLOCK IoStatus;\n    OBJECT_ATTRIBUTES ObjectAttributes;\n    NTSTATUS Status;\n\n    WI_ASSERT(LxssRootHandle == NULL);\n\n    //\n    // Create the device string and connect to the device.\n    //\n\n    RtlInitUnicodeString(&ControlDevicePath, LX_CONTROL_DEVICE_ROOT);\n    InitializeObjectAttributes(&ObjectAttributes, &ControlDevicePath, OBJ_CASE_INSENSITIVE, NULL, NULL);\n\n    Status = NtOpenFile(&LxssRootHandle, FILE_WRITE_DATA, &ObjectAttributes, &IoStatus, 0, 0);\n\n    WI_ASSERT(Status != STATUS_PENDING);\n\n    if (!NT_SUCCESS(Status))\n    {\n        if (LxssRootHandle != NULL)\n        {\n            WI_VERIFY(NT_SUCCESS(NtClose(LxssRootHandle)));\n        }\n\n        LxssRootHandle = NULL;\n    }\n\n    return Status;\n}\n\nNTSTATUS\nLxssClientInstanceCreate(_In_ PLX_KINSTANCECREATESTART Parameters, _Out_ PHANDLE InstanceHandle)\n\n/*++\n\nRoutine Description:\n\n    This routine sends an instance create request to the LXSS driver.\n\nArguments:\n\n    Parameters - Supplies a pointer to the input parameters.\n\n    InstanceHandle - Supplies a buffer to receive a handle to the instance.\n\nReturn Value:\n\n    NTSTATUS.\n\n--*/\n\n{\n    //\n    // Create the instance.\n    //\n\n    return LxBusClientpIoctl(\n        LxssRootHandle, LXBUS_ROOT_IOCTL_CREATE_INSTANCE, Parameters, sizeof(*Parameters), InstanceHandle, sizeof(*InstanceHandle));\n}\n\nNTSTATUS\nLxssClientInstanceDestroy(_In_ HANDLE InstanceHandle)\n\n/*++\n\nRoutine Description:\n\n    This routine sends an instance destroy request to the LXSS driver.\n\nArguments:\n\n    InstanceHandle - Supplies a handle to an instance.\n\nReturn Value:\n\n    NTSTATUS.\n\n--*/\n\n{\n    LX_KINSTANCESETSTATE InstanceSetState;\n    NTSTATUS Status;\n\n    //\n    // Destroy the instance.\n    //\n\n    RtlZeroMemory(&InstanceSetState, sizeof(InstanceSetState));\n    InstanceSetState.Type = LxKInstanceSetStateTypeDestroy;\n    Status = LxBusClientpIoctl(InstanceHandle, LXBUS_IOCTL_SET_INSTANCE_STATE, &InstanceSetState, sizeof(InstanceSetState), NULL, 0);\n\n    return Status;\n}\n\nNTSTATUS\nLxssClientInstanceGetExitStatus(_In_ HANDLE InstanceHandle, _Out_ PLONG ExitStatus)\n\n/*++\n\nRoutine Description:\n\n    This routine sends a instance get exit status request to the LXSS driver.\n\nArguments:\n\n    InstanceHandle - Supplies a handle to an instance.\n\n    ExitStatus - Supplies a buffer to receive the instance exit status.\n\nReturn Value:\n\n    NTSTATUS.\n\n--*/\n\n{\n    NTSTATUS ExitStatusLocal;\n    NTSTATUS Status;\n\n    Status = LxBusClientpIoctl(InstanceHandle, LXBUS_INSTANCE_IOCTL_GET_INIT_EXIT_STATUS, NULL, 0, &ExitStatusLocal, sizeof(ExitStatusLocal));\n\n    if (!NT_SUCCESS(Status))\n    {\n        goto ClientInstanceGetExitStatusEnd;\n    }\n\n    *ExitStatus = ExitStatusLocal;\n\nClientInstanceGetExitStatusEnd:\n    return Status;\n}\n\nNTSTATUS\nLxssClientInstanceStart(_In_ HANDLE InstanceHandle, _In_ HANDLE ParentProcessHandle)\n\n/*++\n\nRoutine Description:\n\n    This routine sends an instance start request to the LXSS driver.\n\nArguments:\n\n    InstanceHandle - Supplies a handle to an instance.\n\n    ParentProcessHandle - Supplies a handle to the parent process for the\n        instance.\n\nReturn Value:\n\n    NTSTATUS.\n\n--*/\n\n{\n    LX_KINSTANCESETSTATE InstanceSetState;\n    NTSTATUS Status;\n\n    //\n    // Start the instance.\n    //\n\n    RtlZeroMemory(&InstanceSetState, sizeof(InstanceSetState));\n    InstanceSetState.Type = LxKInstanceSetStateTypeStart;\n    InstanceSetState.TypeData.StartParentProcessHandle = HandleToUlong(ParentProcessHandle);\n\n    Status = LxBusClientpIoctl(InstanceHandle, LXBUS_IOCTL_SET_INSTANCE_STATE, &InstanceSetState, sizeof(InstanceSetState), NULL, 0);\n\n    return Status;\n}\n\nNTSTATUS\nLxssClientInstanceStop(_In_ HANDLE InstanceHandle)\n\n/*++\n\nRoutine Description:\n\n    This routine sends an instance stop request to the LXSS driver.\n\nArguments:\n\n    InstanceHandle - Supplies a handle to an instance.\n\nReturn Value:\n\n    NTSTATUS.\n\n--*/\n\n{\n    LX_KINSTANCESETSTATE InstanceSetState;\n    NTSTATUS Status;\n\n    //\n    // Stop the instance.\n    //\n\n    RtlZeroMemory(&InstanceSetState, sizeof(InstanceSetState));\n    InstanceSetState.Type = LxKInstanceSetStateTypeStop;\n    Status = LxBusClientpIoctl(InstanceHandle, LXBUS_IOCTL_SET_INSTANCE_STATE, &InstanceSetState, sizeof(InstanceSetState), NULL, 0);\n\n    return Status;\n}\n\nVOID LxssClientUninitialize(VOID)\n\n/*++\n\nRoutine Description:\n\n    Uninitializes a new LXSS client by disconnecting from LXSS driver.\n\nArguments:\n\n    None.\n\nReturn Value:\n\n    None.\n\n--*/\n\n{\n    if (LxssRootHandle != NULL)\n    {\n        WI_VERIFY(NT_SUCCESS(NtClose(LxssRootHandle)));\n        LxssRootHandle = NULL;\n    }\n}\n"
  },
  {
    "path": "src/windows/common/notifications.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    notifications.cpp\n\nAbstract:\n\n    This file contains notification function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include <windows.ui.notifications.h>\n#include <NotificationActivationCallback.h>\n#include <appmodel.h>\n#include <wrl\\wrappers\\corewrappers.h>\n\nusing namespace ABI::Windows::Data::Xml::Dom;\nusing namespace ABI::Windows::UI::Notifications;\nusing namespace Microsoft::WRL;\nusing namespace Microsoft::WRL::Wrappers;\n\nusing wsl::shared::Localization;\n\nnamespace {\nHRESULT CreateToastNotifier(IToastNotifier** notifier)\n{\n    ComPtr<IToastNotificationManagerStatics> toastStatics;\n    RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(\n        HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &toastStatics));\n\n    return toastStatics->CreateToastNotifierWithId(HStringReference(L\"Microsoft.WSL\").Get(), notifier);\n}\n\nHRESULT CreateXmlDocumentFromString(const wchar_t* xmlString, IXmlDocument** doc)\n{\n    ComPtr<IXmlDocument> answer;\n    RETURN_IF_FAILED(Windows::Foundation::ActivateInstance(HStringReference(RuntimeClass_Windows_Data_Xml_Dom_XmlDocument).Get(), &answer));\n\n    ComPtr<IXmlDocumentIO> docIO;\n    RETURN_IF_FAILED(answer.As(&docIO));\n\n    RETURN_IF_FAILED(docIO->LoadXml(HStringReference(xmlString).Get()));\n\n    return answer.CopyTo(doc);\n}\n\nHRESULT CreateToastNotification(IXmlDocument* content, IToastNotification** notification)\n{\n    ComPtr<IToastNotificationFactory> factory;\n    RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(\n        HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &factory));\n\n    return factory->CreateToastNotification(content, notification);\n}\n\nHRESULT DisplayNotification(IXmlDocument* doc)\n{\n    // Create the notifier\n    // Classic Win32 apps MUST use the compat method to create the notifier\n    wil::com_ptr<IToastNotifier> notifier;\n    RETURN_IF_FAILED(CreateToastNotifier(&notifier));\n\n    // Create the notification itself (using helper method from compat library)\n    wil::com_ptr<IToastNotification> toast;\n    RETURN_IF_FAILED(CreateToastNotification(doc, &toast));\n\n    // And show it!\n    return notifier->Show(toast.get());\n}\n} // namespace\n\nnamespace wsl::windows::common::notifications {\nHRESULT DisplayUpdateNotification(const std::wstring& versionString)\ntry\n{\n    const std::wstring creationString = std::format(\n        LR\"(<toast>\n                <visual>\n                    <binding template='ToastGeneric'>\n                        <text>{}</text>\n                        <text>{}</text>\n                    </binding>\n                </visual>\n                <actions>\n                    <action arguments='{}' content='{}'/>\n                    <action arguments='{}' content='{}'/>\n                </actions>\n            </toast>)\",\n        Localization::MessageNewWslVersionAvailable(Localization::Options::DontImpersonate),\n        Localization::MessageUpdateToVersion(versionString.c_str(), Localization::Options::DontImpersonate),\n        wslhost::update_arg,\n        Localization::MessageUpdate(Localization::Options::DontImpersonate),\n        wslhost::release_notes_arg,\n        Localization::MessageViewReleaseNotes(Localization::Options::DontImpersonate));\n\n    wil::com_ptr<IXmlDocument> doc{};\n    THROW_IF_FAILED(CreateXmlDocumentFromString(creationString.c_str(), &doc));\n\n    THROW_IF_FAILED(DisplayNotification(doc.get()));\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT DisplayFilesystemNotification(_In_ LPCSTR binaryName)\ntry\n{\n    const std::wstring creationString = std::format(\n        LR\"(<toast>\n                <visual>\n                    <binding template='ToastGeneric'>\n                        <text>{}</text>\n                        <text>{}</text>\n                    </binding>\n                </visual>\n                <actions>\n                    <action arguments='{} {}' content='{}'/>\n                    <action arguments='{} {}' content=\"{}\"/>\n                </actions>\n            </toast>)\",\n        Localization::MessagePerformanceTip(Localization::Options::DontImpersonate),\n        Localization::MessageProblematicDrvFsUsage(binaryName, Localization::Options::DontImpersonate),\n        wslhost::docs_arg,\n        wslhost::docs_arg_filesystem_url,\n        Localization::MessageViewDocs(Localization::Options::DontImpersonate),\n        wslhost::disable_notification_arg,\n        LXSS_NOTIFICATION_DRVFS_PERF_DISABLED,\n        Localization::MessageDontShowAgain(Localization::Options::DontImpersonate));\n\n    wil::com_ptr<IXmlDocument> doc{};\n    THROW_IF_FAILED(CreateXmlDocumentFromString(creationString.c_str(), &doc));\n\n    THROW_IF_FAILED(DisplayNotification(doc.get()));\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nvoid DisplayWarningsNotification()\ntry\n{\n    const std::wstring creationString = std::format(\n        LR\"(<toast>\n               <visual>\n                   <binding template='ToastGeneric'>\n                       <text>{}</text>\n                   </binding>\n               </visual>\n               <actions>\n                   <action arguments='{}' content='{}'/>\n               </actions>\n           </toast>)\",\n        Localization::MessageWarningDuringStartup(),\n        wslhost::event_viewer_arg,\n        Localization::MessageOpenEventViewer());\n\n    wil::com_ptr<IXmlDocument> doc{};\n    THROW_IF_FAILED(CreateXmlDocumentFromString(creationString.c_str(), &doc));\n\n    THROW_IF_FAILED(DisplayNotification(doc.get()));\n}\nCATCH_LOG()\n\nvoid DisplayOptionalComponentsNotification()\ntry\n{\n    const std::wstring creationString = std::format(\n        LR\"(<toast>\n               <visual>\n                   <binding template='ToastGeneric'>\n                       <text>{}</text>\n                   </binding>\n               </visual>\n               <actions>\n                   <action arguments='{}' content='{}'/>\n               </actions>\n           </toast>)\",\n        Localization::MessageMissingOptionalComponents(),\n        wslhost::install_prerequisites_arg,\n        Localization::MessageInstallMissingOptionalComponents());\n\n    wil::com_ptr<IXmlDocument> doc{};\n    THROW_IF_FAILED(CreateXmlDocumentFromString(creationString.c_str(), &doc));\n\n    THROW_IF_FAILED(DisplayNotification(doc.get()));\n}\nCATCH_LOG()\n\nvoid DisplayProxyChangeNotification(const std::wstring& message)\ntry\n{\n    const std::wstring creationString =\n        std::format(LR\"(<toast><visual><binding template='ToastGeneric'><text>{}</text></binding></visual></toast>)\", message);\n\n    wil::com_ptr<IXmlDocument> doc{};\n    THROW_IF_FAILED(CreateXmlDocumentFromString(creationString.c_str(), &doc));\n\n    THROW_IF_FAILED(DisplayNotification(doc.get()));\n}\nCATCH_LOG()\n} // namespace wsl::windows::common::notifications\n"
  },
  {
    "path": "src/windows/common/notifications.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    notifications.h\n\nAbstract:\n\n    This file contains notification function definitions.\n\n--*/\n\n#pragma once\n\nnamespace wsl::windows::common::notifications {\n\n/// <summary>\n/// Displays the notification that a WSL update is available.\n/// </summary>\nHRESULT DisplayUpdateNotification(const std::wstring& versionString);\n\n/// <summary>\n/// Displays the notification that performance will be poor due to DrvFs usage.\n/// </summary>\nHRESULT DisplayFilesystemNotification(_In_ LPCSTR binaryName);\n\n/// <summary>\n/// Displays the notification saying that warnings were emitted during launch.\n/// </summary>\nvoid DisplayWarningsNotification();\n\n/// <summary>\n/// Displays the notification saying that a proxy change has been detected.\n/// </summary>\nvoid DisplayProxyChangeNotification(const std::wstring& message);\n\n/// <summary>\n/// Displays the notification saying that optional components need to be installed.\n/// </summary>\nvoid DisplayOptionalComponentsNotification();\n\n} // namespace wsl::windows::common::notifications\n"
  },
  {
    "path": "src/windows/common/precomp.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    precomp.h\n\nAbstract:\n\n    The precompiled header for common.\n\n--*/\n\n#pragma warning(disable : 4200 4214)\n\n#ifdef __cplusplus\n#define _WINSOCKAPI_\n\n// System headers\n#include <windows.h>\n#include <winapifamily.h>\n#include <wincon.h>\n#include <initguid.h>\n#include <sddl.h>\n#include <winsock2.h>\n#include <ws2ipdef.h>\n#include <ws2spi.h>\n#include <wdk.h>\n#include <FileApi.h>\n#include <userenv.h>\n#include <appmodel.h>\n#include <virtdisk.h>\n#include <conio.h>\n#include <VersionHelpers.h>\n#include <KnownFolders.h>\n#include <shtypes.h>\n#include <Shlwapi.h>\n#include <Shlobj.h>\n#include <icu.h>\n#define ENABLE_INTSAFE_SIGNED_FUNCTIONS\n#include <intsafe.h>\n#include <safeint.h>\n#include <windns.h>\n#include <ipifcons.h>\n#include <iphlpapi.h>\n#include <netioapi.h>\n#include <ip2string.h>\n#include <atlsafe.h> // Note: needs to be included before tchar.h\n#include <SoftPub.h>\n#include <wintrust.h>\n#include <msi.h>\n#include <AccCtrl.h>\n#include <AclAPI.h>\n#include \"windowsdefs.h\"\n\n// Annotations\n#include <sal.h>\n\n// Standard library C-style\n#include <wchar.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <fcntl.h>\n#include <io.h>\n#include <time.h>\n\n// Standard library C++ style\n#include <string>\n#include <array>\n#include <xstring>\n#include <algorithm>\n#include <map>\n#include <memory>\n#include <vector>\n#include <locale>\n#include <fstream>\n#include <streambuf>\n#include <string_view>\n#include <thread>\n#include <bitset>\n#include <sstream>\n#include <optional>\n#include <filesystem>\n#include <mutex>\n#include <chrono>\n#include <codecvt>\n#include <random>\n#include <future>\n#include <unordered_set>\n#include <queue>\n#include <numeric>\n#include <regex>\n#include <source_location>\n#include <ranges>\n#include <format>\n#include <cwctype>\n#include <variant>\n\n// Socket APIs\n#include <mswsock.h>\n#include <ws2tcpip.h>\n#include <hvsocket.h>\n#include <wininet.h>\n#include <mstcpip.h>\n\n// Windows Internal Library\n#include <wil/resource.h>\n#include <wil/result.h>\n#include <wil/filesystem.h>\n#include <wil/token_helpers.h>\n#include <wil/stl.h>\n#include <wil/cppwinrt.h>\n#include <wil/winrt.h>\n#include <wil/wrl.h>\n#include <wil/registry.h>\n\n// Winrt headers\n#include <winrt/Windows.Foundation.h>\n#include <winrt/Windows.Foundation.Collections.h>\n#include <winrt/Windows.ApplicationModel.h>\n#include <winrt/Windows.ApplicationModel.Core.h>\n#include <winrt/Windows.Management.Deployment.h>\n#include <winrt/Windows.Storage.Streams.h>\n#include <winrt/Windows.Web.Http.h>\n#include <winrt/windows.web.Http.Filters.h>\n#include <winrt/Windows.Web.Http.Headers.h>\n#include <winrt/Windows.ApplicationModel.Store.Preview.InstallControl.h>\n#include <winrt/Windows.Services.Store.h>\n#include <winrt/Windows.Storage.h>\n\n// Safe path handling\n#include <pathcch.h>\n\n// Telemetry Header\n#include \"WslTelemetry.h\"\n\n// LxCore headers\n#include <lxcoreapi.h>\n#include <lxbusapi.h>\n\n// Utility/helper functions\n#include \"conncheckshared.h\"\n#include \"helpers.hpp\"\n#include \"string.hpp\"\n#include \"filesystem.hpp\"\n#include \"Localization.h\"\n#include \"wslutil.h\"\n#include \"gslhelpers.h\"\n#include \"socket.hpp\"\n#include \"defs.h\"\n#include \"configfile.h\"\n#include \"socketshared.h\"\n#include \"stringshared.h\"\n#include \"retryshared.h\"\n#include \"wslconfig.h\"\n#include \"Redirector.h\"\n#include \"hvsocket.hpp\"\n#include \"registry.hpp\"\n#include \"LxssDynamicFunction.h\"\n#include \"relay.hpp\"\n#include \"svccomm.hpp\"\n#include \"ConsoleState.h\"\n#include \"disk.hpp\"\n#include \"WslSecurity.h\"\n#include \"ExecutionContext.h\"\n#include \"WslClient.h\"\n#include \"wslsupport.h\"\n#include <wslservice.h>\n#include \"message.h\"\n#include \"wslpolicies.h\"\n#include \"SubProcess.h\"\n#include \"notifications.h\"\n#include \"SocketChannel.h\"\n#include \"interop.hpp\"\n#include \"lxssbusclient.h\"\n#include \"lxssclient.h\"\n#include \"lxinitshared.h\"\n#include \"wslhost.h\"\n#include \"wslrelay.h\"\n#include \"wsl.h\"\n#include \"LxssPort.h\"\n\n#endif\n"
  },
  {
    "path": "src/windows/common/registry.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    registry.cpp\n\nAbstract:\n\n    This file contains registry management helper function implementation.\n\n--*/\n\n#include \"precomp.h\"\n#include \"registry.hpp\"\n#include \"svccomm.hpp\"\n#pragma hdrstop\n\nnamespace {\nstd::wstring GetKeyPath(_In_ HKEY Key)\n{\n    if (Key == HKEY_LOCAL_MACHINE)\n    {\n        return L\"HKLM\";\n    }\n    else if (Key == HKEY_CLASSES_ROOT)\n    {\n        return L\"HKCR\";\n    }\n    else if (Key == HKEY_USERS)\n    {\n        return L\"HKU\";\n    }\n    else if (Key == HKEY_CURRENT_USER)\n    {\n        return L\"HKCU\";\n    }\n    else if (Key == HKEY_CURRENT_CONFIG)\n    {\n        return L\"HKCC\";\n    }\n\n    ULONG requiredSize{};\n    auto status = ZwQueryKey(Key, KeyNameInformation, nullptr, 0, &requiredSize);\n    if (status != STATUS_BUFFER_TOO_SMALL)\n    {\n        THROW_NTSTATUS(status);\n    }\n\n    std::vector<char> buffer(requiredSize, 0);\n\n    status = ZwQueryKey(Key, KeyNameInformation, buffer.data(), static_cast<ULONG>(buffer.size()), &requiredSize);\n    THROW_IF_WIN32_ERROR(status);\n\n    const auto* info = reinterpret_cast<KEY_NAME_INFORMATION*>(buffer.data());\n\n    return std::wstring{info->Name, info->NameLength / sizeof(WCHAR)};\n}\n\nvoid ReportErrorIfFailed(_In_ LSTATUS Error, _In_ HKEY Key, _In_opt_ LPCWSTR Subkey, _In_opt_ LPCWSTR Value)\n{\n    if (Error == ERROR_SUCCESS)\n    {\n        return;\n    }\n\n    const auto result = HRESULT_FROM_WIN32(Error);\n    if (Key == nullptr)\n    {\n        const auto errorString = wsl::windows::common::wslutil::GetSystemErrorString(result);\n        THROW_HR_WITH_USER_ERROR(\n            result, wsl::shared::Localization::MessageRegistryError(Subkey ? Subkey : L\"[null]\", errorString.c_str()).c_str());\n    }\n\n    auto path = GetKeyPath(Key);\n    if (Subkey != nullptr)\n    {\n        path += L\"\\\\\" + std::wstring(Subkey);\n    }\n\n    if (Value != nullptr)\n    {\n        path += L\"\\\\\" + std::wstring(Value);\n    }\n\n    if (wsl::windows::common::ExecutionContext::ShouldCollectErrorMessage())\n    {\n        const auto errorString = wsl::windows::common::wslutil::GetSystemErrorString(result);\n        THROW_HR_WITH_USER_ERROR(result, wsl::shared::Localization::MessageRegistryError(path.c_str(), errorString.c_str()).c_str());\n    }\n    else\n    {\n        THROW_HR_MSG(result, \"An error occurred accessing the registry. Path: %ls\", path.c_str());\n    }\n}\n} // namespace\nvoid wsl::windows::common::registry::ClearSubkeys(_In_ HKEY Key)\n{\n    for (const auto& e : EnumKeys(Key, KEY_READ))\n    {\n        DeleteKey(Key, e.first.c_str());\n    }\n}\n\nwil::unique_hkey wsl::windows::common::registry::CreateKey(\n    _In_ HKEY Key, _In_ LPCWSTR KeyName, _In_ REGSAM AccessMask, _Out_opt_ LPDWORD Disposition, _In_ DWORD Options)\n{\n    wil::unique_hkey NewKey;\n    THROW_IF_WIN32_ERROR(RegCreateKeyExW(Key, KeyName, 0, nullptr, Options, AccessMask, nullptr, &NewKey, Disposition));\n\n    return NewKey;\n}\n\nbool wsl::windows::common::registry::DeleteKey(_In_ HKEY Key, _In_ LPCWSTR KeyName)\n{\n    const LSTATUS Result = RegDeleteTreeW(Key, KeyName);\n    if (Result != ERROR_FILE_NOT_FOUND)\n    {\n        LOG_IF_WIN32_ERROR(Result);\n    }\n\n    return Result == NO_ERROR;\n}\n\nvoid wsl::windows::common::registry::DeleteKeyValue(_In_ HKEY Key, _In_ LPCWSTR KeyName)\n{\n    const LSTATUS Result = RegDeleteKeyValueW(Key, nullptr, KeyName);\n    if (Result != ERROR_FILE_NOT_FOUND)\n    {\n        LOG_IF_WIN32_ERROR(Result);\n    }\n}\n\nvoid wsl::windows::common::registry::DeleteValue(_In_ HKEY Key, _In_ LPCWSTR KeyName)\n{\n    const LSTATUS Result = RegDeleteValueW(Key, KeyName);\n    if (Result != ERROR_FILE_NOT_FOUND)\n    {\n        LOG_IF_WIN32_ERROR(Result);\n    }\n}\n\nstd::map<std::wstring, wil::unique_hkey> wsl::windows::common::registry::EnumKeys(_In_ HKEY Key, _In_ DWORD SubkeyAccess)\n{\n    // Get the max size of a subkey\n    DWORD MaxSubkeySize = 0;\n    QueryInfo(Key, &MaxSubkeySize);\n\n    std::map<std::wstring, wil::unique_hkey> keys;\n    for (DWORD Index = 0;; Index++)\n    {\n        std::wstring Name(MaxSubkeySize, '\\0');\n        DWORD NameSize = MaxSubkeySize + 1;\n        const LSTATUS result = RegEnumKeyExW(Key, Index, Name.data(), &NameSize, nullptr, nullptr, nullptr, nullptr);\n        if (result == ERROR_NO_MORE_ITEMS)\n        {\n            break;\n        }\n\n        ReportErrorIfFailed(result, Key, nullptr, nullptr);\n\n        Name.resize(NameSize);\n\n        auto subKey = OpenKey(Key, Name.c_str(), SubkeyAccess);\n        keys.emplace(std::move(Name), std::move(subKey));\n    }\n\n    return keys;\n}\n\nstd::vector<std::pair<GUID, std::wstring>> wsl::windows::common::registry::EnumGuidKeys(_In_ HKEY Key)\n{\n    // Iterate through the provided keys and return a list of all sub-keys that are GUIDs.\n    WCHAR buffer[39];\n    std::vector<std::pair<GUID, std::wstring>> subKeys;\n    DWORD index = 0;\n    for (;;)\n    {\n        DWORD bufferSize = ARRAYSIZE(buffer);\n        const LSTATUS error = RegEnumKeyExW(Key, index, buffer, &bufferSize, nullptr, nullptr, nullptr, nullptr);\n        index += 1;\n        if (error == ERROR_NO_MORE_ITEMS)\n        {\n            break;\n        }\n        if ((error == ERROR_MORE_DATA) || ((error == ERROR_SUCCESS) && (bufferSize != (ARRAYSIZE(buffer) - 1))))\n        {\n            continue;\n        }\n\n        ReportErrorIfFailed(error, Key, nullptr, nullptr);\n\n        // Ignore any subkeys that are not GUIDs.\n        auto guid = wsl::shared::string::ToGuid(buffer);\n        if (!guid.has_value())\n        {\n            continue;\n        }\n\n        subKeys.emplace_back(std::make_pair(guid.value(), std::wstring(buffer)));\n    }\n\n    return subKeys;\n}\n\nstd::vector<std::pair<std::wstring, DWORD>> wsl::windows::common::registry::EnumValues(_In_ HKEY Key)\n{\n    std::vector<std::pair<std::wstring, DWORD>> values;\n    DWORD maxValueNameSize = 0;\n    QueryInfo(Key, nullptr, &maxValueNameSize);\n\n    for (DWORD Index = 0;; Index++)\n    {\n        std::wstring valueName(maxValueNameSize, '\\0');\n        DWORD size = maxValueNameSize + 1;\n        DWORD type = 0;\n\n        const auto error = RegEnumValueW(Key, Index, valueName.data(), &size, nullptr, &type, nullptr, nullptr);\n        if (error == ERROR_NO_MORE_ITEMS)\n        {\n            break;\n        }\n        THROW_IF_WIN32_ERROR(error);\n\n        valueName.resize(size);\n        values.emplace_back(std::move(valueName), type);\n    }\n\n    return values;\n}\n\nbool wsl::windows::common::registry::IsKeyVolatile(_In_ HKEY Key)\n{\n    KEY_FLAGS_INFORMATION info{};\n    DWORD resultSize{};\n    THROW_IF_NTSTATUS_FAILED(ZwQueryKey(Key, KeyFlagsInformation, &info, sizeof(info), &resultSize));\n\n    return WI_IsFlagSet(info.KeyFlags, REG_OPTION_VOLATILE);\n}\n\nwil::unique_hkey wsl::windows::common::registry::OpenCurrentUser(_In_ REGSAM AccessMask)\n{\n    wil::unique_hkey UserKey;\n    THROW_IF_WIN32_ERROR(RegOpenCurrentUser(AccessMask, &UserKey));\n\n    return UserKey;\n}\n\nstd::pair<wil::unique_hkey, HRESULT> wsl::windows::common::registry::OpenKeyNoThrow(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ REGSAM AccessMask, _In_ DWORD Options)\n{\n    wil::unique_hkey OpenedKey;\n    const auto error = RegOpenKeyExW(Key, SubKey, Options, AccessMask, &OpenedKey);\n\n    return {std::move(OpenedKey), HRESULT_FROM_WIN32(error)};\n}\n\nwil::unique_hkey wsl::windows::common::registry::OpenKey(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ REGSAM AccessMask, _In_ DWORD Options)\n{\n    auto [key, error] = OpenKeyNoThrow(Key, SubKey, AccessMask, Options);\n    ReportErrorIfFailed(error, Key, SubKey, nullptr);\n\n    return std::move(key);\n}\n\nwil::unique_hkey wsl::windows::common::registry::OpenLxssMachineKey(REGSAM AccessMask)\n{\n    wil::unique_hkey LxssKey = CreateKey(HKEY_LOCAL_MACHINE, LXSS_REGISTRY_PATH, AccessMask);\n    THROW_LAST_ERROR_IF(!LxssKey);\n\n    return LxssKey;\n}\n\nwil::unique_hkey wsl::windows::common::registry::OpenLxssUserKey()\n{\n    const wil::unique_hkey UserKey = OpenCurrentUser();\n    wil::unique_hkey LxssKey = CreateKey(UserKey.get(), LXSS_REGISTRY_PATH);\n    THROW_LAST_ERROR_IF(!LxssKey);\n\n    return LxssKey;\n}\n\nwil::unique_hkey wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(_In_ PSID UserSid)\n{\n    // In this method we use the user SID to open a user specific key under HKLM\n    // The reason for not using HKCU is that lxss trusts this key and will mount\n    // all the volumes listed under it.\n    // Given that only elevated users are allowed to mount disks, using HKCU would\n    // create a security issue as non-admin users could write anything they want there.\n    std::wstring path = std::format(L\"{}\\\\{}\", LXSS_DISK_MOUNTS_REGISTRY_PATH, wsl::windows::common::wslutil::SidToString(UserSid).get());\n\n    // Create a volatile key so that disk states aren't kept after a reboot\n    return CreateKey(HKEY_LOCAL_MACHINE, path.c_str(), KEY_ALL_ACCESS, nullptr, REG_OPTION_VOLATILE);\n}\n\nvoid wsl::windows::common::registry::QueryInfo(_In_ HKEY Key, _In_opt_ DWORD* MaxSubKeySize, _In_opt_ DWORD* MaxValueNameSize, _In_opt_ DWORD* MaxValueDataSize)\n{\n    const auto error = (RegQueryInfoKeyW(\n        Key, nullptr, nullptr, nullptr, nullptr, MaxSubKeySize, nullptr, nullptr, MaxValueNameSize, MaxValueDataSize, nullptr, nullptr));\n\n    ReportErrorIfFailed(error, Key, nullptr, nullptr);\n}\n\nDWORD\nwsl::windows::common::registry::ReadDword(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_ DWORD DefaultValue)\n{\n    DWORD Returned = 0;\n    DWORD Size = sizeof(Returned);\n    const LONG Result = RegGetValueW(Key, KeyName, ValueName, RRF_RT_REG_DWORD, nullptr, &Returned, &Size);\n    if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND))\n    {\n        return DefaultValue;\n    }\n\n    ReportErrorIfFailed(Result, Key, KeyName, ValueName);\n    return Returned;\n}\n\nULONG64\nwsl::windows::common::registry::ReadQword(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_ ULONG64 DefaultValue)\n{\n    ULONG64 Returned = 0;\n    DWORD Size = sizeof(Returned);\n    const LONG Result = RegGetValueW(Key, KeyName, ValueName, RRF_RT_REG_QWORD, nullptr, &Returned, &Size);\n    if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND))\n    {\n        return DefaultValue;\n    }\n\n    ReportErrorIfFailed(Result, Key, KeyName, ValueName);\n\n    return Returned;\n}\n\nstd::wstring wsl::windows::common::registry::ReadString(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_opt_ LPCWSTR Default)\n{\n    auto value = ReadOptionalString(Key, KeyName, ValueName);\n    if (!value.has_value())\n    {\n        if (ARGUMENT_PRESENT(Default))\n        {\n            return Default;\n        }\n        else\n        {\n            ReportErrorIfFailed(ERROR_PATH_NOT_FOUND, Key, KeyName, ValueName);\n        }\n    }\n\n    return value.value();\n}\n\nstd::optional<std::wstring> wsl::windows::common::registry::ReadOptionalString(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName)\n{\n    DWORD Size = 0;\n    LONG Result = RegGetValueW(Key, KeyName, ValueName, (RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ), nullptr, nullptr, &Size);\n    if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND) || (Size == 0))\n    {\n        return {};\n    }\n\n    ReportErrorIfFailed(Result, Key, KeyName, ValueName);\n\n    //\n    // Allocate a buffer and read the value of the key.\n    //\n\n    std::wstring Buffer(Size / sizeof(WCHAR), L'\\0');\n    Result = RegGetValueW(Key, KeyName, ValueName, (RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ), nullptr, Buffer.data(), &Size);\n    if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND) || (Size == 0))\n    {\n        return {};\n    }\n\n    ReportErrorIfFailed(Result, Key, KeyName, ValueName);\n\n    Buffer.resize(wcsnlen(Buffer.c_str(), Buffer.size()));\n    return Buffer;\n}\n\nstd::vector<std::string> wsl::windows::common::registry::ReadStringSet(\n    _In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, const std::vector<std::string>& Default)\n{\n    //\n    // Detect if the key exists and determine how large of a buffer is needed.\n    // If the key does not exist, return the default value.\n    //\n\n    LONG Result;\n    DWORD Size = 0;\n    Result = RegGetValueW(Key, KeyName, ValueName, RRF_RT_REG_MULTI_SZ, nullptr, nullptr, &Size);\n    if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND) || (Size == 0))\n    {\n        //\n        // Convert the supplied string into a vector of strings.\n        //\n\n        return Default;\n    }\n\n    ReportErrorIfFailed(Result, Key, KeyName, ValueName);\n\n    //\n    // Allocate a buffer to hold the value and two NULL terminators.\n    //\n\n    std::vector<WCHAR> Buffer(Size + 2);\n\n    //\n    // Read the value.\n    //\n\n    Result = RegGetValueW(Key, KeyName, ValueName, RRF_RT_REG_MULTI_SZ, nullptr, Buffer.data(), &Size);\n    ReportErrorIfFailed(Result, Key, KeyName, ValueName);\n\n    //\n    // Convert the reg value into a vector of strings.\n    //\n\n    std::vector<std::string> Values{};\n    for (auto Current = Buffer.data(); UNICODE_NULL != *Current; Current += wcslen(Current) + 1)\n    {\n        Values.push_back(wsl::shared::string::WideToMultiByte(Current));\n    }\n\n    return Values;\n}\n\nvoid wsl::windows::common::registry::WriteDword(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR ValueName, _In_ DWORD Value)\n{\n    const auto Result = RegSetKeyValueW(Key, SubKey, ValueName, REG_DWORD, &Value, sizeof(Value));\n    ReportErrorIfFailed(Result, Key, SubKey, ValueName);\n}\n\nvoid wsl::windows::common::registry::WriteQword(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR ValueName, _In_ ULONG64 Value)\n{\n    const auto Result = RegSetKeyValueW(Key, SubKey, ValueName, REG_QWORD, &Value, sizeof(Value));\n    ReportErrorIfFailed(Result, Key, SubKey, ValueName);\n}\n\nvoid wsl::windows::common::registry::WriteDefaultString(_In_ HKEY Key, _In_ LPCWSTR Value)\n{\n    SIZE_T StringLength = wcslen(Value);\n    THROW_IF_FAILED(SizeTAdd(StringLength, 1, &StringLength));\n    THROW_IF_FAILED(SizeTMult(StringLength, sizeof(WCHAR), &StringLength));\n\n    THROW_HR_IF(E_INVALIDARG, (StringLength > (SIZE_T)DWORD_MAX));\n\n    const auto Result = RegSetValueExW(Key, NULL, 0, REG_SZ, reinterpret_cast<const BYTE*>(Value), static_cast<DWORD>(StringLength));\n\n    ReportErrorIfFailed(Result, Key, nullptr, nullptr);\n}\n\nvoid wsl::windows::common::registry::WriteString(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR ValueName, _In_ LPCWSTR Value)\n{\n    SIZE_T StringLength = wcslen(Value);\n    THROW_IF_FAILED(SizeTAdd(StringLength, 1, &StringLength));\n    THROW_IF_FAILED(SizeTMult(StringLength, sizeof(WCHAR), &StringLength));\n\n    THROW_HR_IF(E_INVALIDARG, (StringLength > (SIZE_T)DWORD_MAX));\n\n    const auto Result = RegSetKeyValueW(Key, SubKey, ValueName, REG_SZ, Value, static_cast<DWORD>(StringLength));\n    ReportErrorIfFailed(Result, Key, SubKey, ValueName);\n}\n\nvoid wsl::windows::common::registry::WriteStringSet(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR ValueName, _In_ const std::vector<std::wstring>& StringSet)\n{\n    THROW_HR_IF(E_INVALIDARG, (StringSet.size() == 0));\n\n    //\n    // Combine each element into a NULL-separated string ending with two NULL\n    // terminators.\n    //\n\n    std::wstring Value;\n    for (SIZE_T Index = 0; Index < StringSet.size(); Index += 1)\n    {\n        Value += StringSet[Index] + UNICODE_NULL;\n    }\n\n    Value += UNICODE_NULL;\n\n    //\n    // Ensure the wstring ends with two NULL terminators.\n    //\n\n    WI_ASSERT((Value.size() >= 2) && (Value.at(Value.size() - 1) == UNICODE_NULL) && (Value.at(Value.size() - 2) == UNICODE_NULL));\n\n    //\n    // Store the value in the registry.\n    //\n\n    SIZE_T ValueSize;\n    THROW_IF_FAILED(SizeTMult(Value.size(), sizeof(WCHAR), &ValueSize));\n    THROW_HR_IF(E_INVALIDARG, (ValueSize > (SIZE_T)DWORD_MAX));\n\n    const auto Result = RegSetKeyValueW(Key, SubKey, ValueName, REG_MULTI_SZ, Value.c_str(), static_cast<DWORD>(ValueSize));\n    ReportErrorIfFailed(Result, Key, SubKey, ValueName);\n}"
  },
  {
    "path": "src/windows/common/registry.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    registry.hpp\n\nAbstract:\n\n    This is the header file for registry management helper functions.\n\n--*/\n\n#pragma once\n\n#define LXSS_SERVICE_REGISTRY_PATH L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\LxssManager\"\n\nnamespace wsl::windows::common::registry {\n\nvoid ClearSubkeys(_In_ HKEY Key);\n\nwil::unique_hkey CreateKey(\n    _In_ HKEY Key, _In_ LPCWSTR KeyName, _In_ REGSAM AccessMask = (KEY_READ | KEY_WRITE), _Out_opt_ LPDWORD Disposition = nullptr, _In_ DWORD Options = 0);\n\nbool DeleteKey(_In_ HKEY Key, _In_ LPCWSTR KeyName);\n\nvoid DeleteKeyValue(_In_ HKEY Key, _In_ LPCWSTR KeyName);\n\nvoid DeleteValue(_In_ HKEY Key, _In_ LPCWSTR KeyName);\n\nstd::vector<std::pair<GUID, std::wstring>> EnumGuidKeys(_In_ HKEY LxssKey);\n\nstd::map<std::wstring, wil::unique_hkey> EnumKeys(_In_ HKEY Key, _In_ DWORD SubkeyAccess);\n\nstd::vector<std::pair<std::wstring, DWORD>> EnumValues(_In_ HKEY Key);\n\nDWORD GetMachinePolicyValue(_In_ LPCWSTR Name, HKEY lxssKey);\n\nbool IsKeyVolatile(_In_ HKEY Key);\n\nwil::unique_hkey OpenCurrentUser(_In_ REGSAM AccessMask = (KEY_READ | KEY_WRITE));\n\nwil::unique_hkey OpenKey(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ REGSAM AccessMask, _In_ DWORD Options = 0);\n\nstd::pair<wil::unique_hkey, HRESULT> OpenKeyNoThrow(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ REGSAM AccessMask, _In_ DWORD Options = 0);\n\nwil::unique_hkey OpenLxssMachineKey(REGSAM AccessMask = KEY_READ);\n\nwil::unique_hkey OpenLxssUserKey();\n\nwil::unique_hkey OpenOrCreateLxssDiskMountsKey(_In_ PSID UserSid);\n\nvoid QueryInfo(_In_ HKEY Key, _In_opt_ DWORD* MaxSubKeySize = nullptr, _In_opt_ DWORD* MaxValueNameSize = nullptr, _In_opt_ DWORD* MaxValueDataSize = nullptr);\n\nDWORD\nReadDword(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_ DWORD DefaultValue);\n\nULONG64\nReadQword(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_ ULONG64 DefaultValue);\n\nstd::wstring ReadString(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_opt_ LPCWSTR Default = nullptr);\nstd::optional<std::wstring> ReadOptionalString(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName);\n\nstd::vector<std::string> ReadStringSet(_In_ HKEY Key, _In_opt_ LPCWSTR KeyName, _In_opt_ LPCWSTR ValueName, _In_ const std::vector<std::string>& Default);\n\nvoid WriteDword(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR KeyName, _In_ DWORD Value);\n\nvoid WriteQword(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR KeyName, _In_ ULONG64 Value);\n\nvoid WriteDefaultString(_In_ HKEY Key, _In_ LPCWSTR Value);\n\nvoid WriteString(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR KeyName, _In_ LPCWSTR Value);\n\nvoid WriteStringSet(_In_ HKEY Key, _In_ LPCWSTR SubKey, _In_ LPCWSTR KeyName, _In_ const std::vector<std::wstring>& StringSet);\n\n} // namespace wsl::windows::common::registry\n"
  },
  {
    "path": "src/windows/common/relay.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    relay.cpp\n\nAbstract:\n\n    This file contains function definitions for relay worker thread routines.\n\n--*/\n\n#include \"precomp.h\"\n#include \"relay.hpp\"\n#pragma hdrstop\n\nusing wsl::windows::common::relay::EventHandle;\nusing wsl::windows::common::relay::HandleWrapper;\nusing wsl::windows::common::relay::IOHandleStatus;\nusing wsl::windows::common::relay::MultiHandleWait;\nusing wsl::windows::common::relay::OverlappedIOHandle;\nusing wsl::windows::common::relay::ScopedMultiRelay;\nusing wsl::windows::common::relay::ScopedRelay;\nusing wsl::windows::common::relay::SingleAcceptHandle;\n\nnamespace {\n\nLARGE_INTEGER InitializeFileOffset(HANDLE File)\n{\n    LARGE_INTEGER Offset{};\n    if (GetFileType(File) == FILE_TYPE_DISK)\n    {\n        LOG_IF_WIN32_BOOL_FALSE(SetFilePointerEx(File, {}, &Offset, FILE_CURRENT));\n    }\n\n    return Offset;\n}\n\n} // namespace\n\nstd::thread wsl::windows::common::relay::CreateThread(_In_ HANDLE InputHandle, _In_ HANDLE OutputHandle, _In_opt_ HANDLE ExitHandle, _In_ size_t BufferSize)\n{\n    return std::thread([InputHandle, OutputHandle, ExitHandle, BufferSize]() {\n        try\n        {\n            wsl::windows::common::wslutil::SetThreadDescription(L\"IO Relay\");\n            InterruptableRelay(InputHandle, OutputHandle, ExitHandle, BufferSize);\n        }\n        CATCH_LOG()\n    });\n}\n\nstd::thread wsl::windows::common::relay::CreateThread(\n    _In_ wil::unique_handle&& InputHandle, _In_ HANDLE OutputHandle, _In_opt_ HANDLE ExitHandle, _In_ size_t BufferSize)\n{\n    return std::thread([InputHandle = std::move(InputHandle), OutputHandle, ExitHandle, BufferSize]() {\n        try\n        {\n            wsl::windows::common::wslutil::SetThreadDescription(L\"IO Relay\");\n            InterruptableRelay(InputHandle.get(), OutputHandle, ExitHandle, BufferSize);\n        }\n        CATCH_LOG()\n    });\n}\n\nstd::thread wsl::windows::common::relay::CreateThread(\n    _In_ HANDLE InputHandle, _In_ wil::unique_handle&& OutputHandle, _In_opt_ HANDLE ExitHandle, _In_ size_t BufferSize)\n{\n    return std::thread([InputHandle, OutputHandle = std::move(OutputHandle), ExitHandle, BufferSize]() {\n        try\n        {\n            wsl::windows::common::wslutil::SetThreadDescription(L\"IO Relay\");\n            InterruptableRelay(InputHandle, OutputHandle.get(), ExitHandle, BufferSize);\n        }\n        CATCH_LOG()\n    });\n}\n\nstd::thread wsl::windows::common::relay::CreateThread(\n    _In_ wil::unique_handle&& InputHandle, _In_ wil::unique_handle&& OutputHandle, _In_opt_ HANDLE ExitHandle, _In_ size_t BufferSize)\n{\n    return std::thread([InputHandle = std::move(InputHandle), OutputHandle = std::move(OutputHandle), ExitHandle, BufferSize]() {\n        try\n        {\n            wsl::windows::common::wslutil::SetThreadDescription(L\"IO Relay\");\n            InterruptableRelay(InputHandle.get(), OutputHandle.get(), ExitHandle, BufferSize);\n        }\n        CATCH_LOG()\n    });\n}\n\nDWORD\nwsl::windows::common::relay::InterruptableRead(\n    _In_ HANDLE InputHandle, _In_ gsl::span<gsl::byte> Buffer, _In_ const std::vector<HANDLE>& ExitHandles, _In_opt_ LPOVERLAPPED Overlapped)\n{\n    // Initialize an overlapped structure if one was not provided by the caller.\n    OVERLAPPED overlapped = {};\n    wil::unique_event overlappedEvent = {};\n    if (!ARGUMENT_PRESENT(Overlapped))\n    {\n        overlappedEvent.create(wil::EventOptions::ManualReset);\n        overlapped.hEvent = overlappedEvent.get();\n        Overlapped = &overlapped;\n    }\n\n    DWORD bytesRead = 0;\n    if (!ReadFile(InputHandle, Buffer.data(), gsl::narrow_cast<DWORD>(Buffer.size()), &bytesRead, Overlapped))\n    {\n        auto lastError = GetLastError();\n        if ((lastError == ERROR_HANDLE_EOF) || (lastError == ERROR_BROKEN_PIPE))\n        {\n            return 0;\n        }\n\n        THROW_LAST_ERROR_IF_MSG(lastError != ERROR_IO_PENDING, \"Handle: 0x%p\", (void*)InputHandle);\n\n        auto cancelRead = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n            CancelIoEx(InputHandle, Overlapped);\n            GetOverlappedResult(InputHandle, Overlapped, &bytesRead, TRUE);\n        });\n\n        // Wait for the read to complete, or the client to exit.\n        if (!InterruptableWait(Overlapped->hEvent, ExitHandles))\n        {\n            return 0;\n        }\n\n        if (!GetOverlappedResult(InputHandle, Overlapped, &bytesRead, FALSE))\n        {\n            lastError = GetLastError();\n            if ((lastError == ERROR_HANDLE_EOF) || (lastError == ERROR_BROKEN_PIPE))\n            {\n                return 0;\n            }\n\n            THROW_LAST_ERROR();\n        }\n\n        cancelRead.release();\n    }\n\n    return bytesRead;\n}\n\nvoid wsl::windows::common::relay::InterruptableRelay(_In_ HANDLE InputHandle, _In_opt_ HANDLE OutputHandle, _In_opt_ HANDLE ExitHandle, _In_ size_t BufferSize)\n{\n    // If the handle file is seekable, make sure to respect the offset.\n    // This is useful in cases when WSL is invoked on an existing file, like: wsl.exe echo foo >> file\n    // See: https://github.com/microsoft/WSL/issues/11799\n\n    LARGE_INTEGER writeOffset = InitializeFileOffset(OutputHandle);\n    LARGE_INTEGER readOffset = InitializeFileOffset(InputHandle);\n\n    std::vector<gsl::byte> buffer(BufferSize);\n    const auto readSpan = gsl::make_span(buffer);\n\n    std::vector<HANDLE> exitHandles;\n    if (ExitHandle)\n    {\n        exitHandles.push_back(ExitHandle);\n    }\n\n    OVERLAPPED overlapped = {0};\n    const wil::unique_event overlappedEvent(wil::EventOptions::ManualReset);\n    overlapped.hEvent = overlappedEvent.get();\n    for (;;)\n    {\n        overlapped.Offset = readOffset.LowPart;\n        overlapped.OffsetHigh = readOffset.HighPart;\n        const auto bytesRead = InterruptableRead(InputHandle, readSpan, exitHandles, &overlapped);\n        if (bytesRead == 0)\n        {\n            break;\n        }\n\n        readOffset.QuadPart += bytesRead;\n\n        if (OutputHandle)\n        {\n            overlapped.Offset = writeOffset.LowPart;\n            overlapped.OffsetHigh = writeOffset.HighPart;\n            auto writeSpan = readSpan.first(bytesRead);\n            const auto bytesWritten = InterruptableWrite(OutputHandle, writeSpan, exitHandles, &overlapped);\n            if (bytesWritten == 0)\n            {\n                break;\n            }\n\n            WI_ASSERT(bytesWritten == bytesRead);\n        }\n\n        writeOffset.QuadPart += bytesRead;\n    }\n}\n\nbool wsl::windows::common::relay::InterruptableWait(_In_ HANDLE WaitObject, _In_ const std::vector<HANDLE>& ExitHandles)\n{\n    // Wait for the object to become signaled or one of the exit handles to be signaled.\n    std::vector<HANDLE> waitObjects{WaitObject};\n    for (const auto& exitHandle : ExitHandles)\n    {\n        waitObjects.push_back(exitHandle);\n    }\n\n    const DWORD waitResult = WaitForMultipleObjects(gsl::narrow_cast<DWORD>(waitObjects.size()), waitObjects.data(), FALSE, INFINITE);\n    if (waitResult != WAIT_OBJECT_0)\n    {\n        if (waitResult > WAIT_OBJECT_0 && waitResult <= WAIT_OBJECT_0 + waitObjects.size())\n        {\n            return false;\n        }\n\n        THROW_HR_MSG(E_FAIL, \"WaitForMultipleObjects %d\", waitResult);\n    }\n\n    return true;\n}\n\nDWORD\nwsl::windows::common::relay::InterruptableWrite(\n    _In_ HANDLE OutputHandle, _In_ gsl::span<const gsl::byte> Buffer, _In_ const std::vector<HANDLE>& ExitHandles, _In_ LPOVERLAPPED Overlapped)\n{\n    const DWORD bytesToWrite = gsl::narrow_cast<DWORD>(Buffer.size());\n    DWORD bytesWritten = 0;\n    BOOL success = WriteFile(OutputHandle, Buffer.data(), bytesToWrite, &bytesWritten, Overlapped);\n    if (!success)\n    {\n        const auto lastError = GetLastError();\n        if (lastError == ERROR_NO_DATA)\n        {\n            return 0;\n        }\n\n        THROW_LAST_ERROR_IF(lastError != ERROR_IO_PENDING);\n\n        auto cancelWrite = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n            CancelIoEx(OutputHandle, Overlapped);\n            GetOverlappedResult(OutputHandle, Overlapped, &bytesWritten, TRUE);\n        });\n\n        if (InterruptableWait(Overlapped->hEvent, ExitHandles))\n        {\n            success = GetOverlappedResult(OutputHandle, Overlapped, &bytesWritten, FALSE);\n            if (success)\n            {\n                cancelWrite.release();\n            }\n        }\n    }\n\n    WI_ASSERT(!success || (bytesWritten == bytesToWrite));\n\n    return bytesWritten;\n}\n\nvoid wsl::windows::common::relay::BidirectionalRelay(_In_ HANDLE LeftHandle, _In_ HANDLE RightHandle, _In_ size_t BufferSize, _In_ RelayFlags Flags)\n{\n    std::vector<gsl::byte> leftBuffer(BufferSize);\n    const auto leftReadSpan = gsl::make_span(leftBuffer);\n    OVERLAPPED leftOverlapped = {0};\n    const wil::unique_event leftOverlappedEvent(wil::EventOptions::None);\n    leftOverlapped.hEvent = leftOverlappedEvent.get();\n    LARGE_INTEGER leftOffset{};\n\n    std::vector<gsl::byte> rightBuffer(BufferSize);\n    const auto rightReadSpan = gsl::make_span(rightBuffer);\n    OVERLAPPED rightOverlapped = {0};\n    const wil::unique_event rightOverlappedEvent(wil::EventOptions::None);\n    rightOverlapped.hEvent = rightOverlappedEvent.get();\n    LARGE_INTEGER rightOffset{};\n\n    bool leftReadPending = false;\n    bool rightReadPending = false;\n    auto cancelReads = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n        DWORD bytes;\n        if (leftReadPending)\n        {\n            CancelIoEx(LeftHandle, &leftOverlapped);\n            GetOverlappedResult(LeftHandle, &leftOverlapped, &bytes, TRUE);\n        }\n\n        if (rightReadPending)\n        {\n            CancelIoEx(RightHandle, &rightOverlapped);\n            GetOverlappedResult(RightHandle, &rightOverlapped, &bytes, TRUE);\n        }\n    });\n\n    DWORD bytesWritten;\n    const HANDLE waitObjects[] = {leftOverlapped.hEvent, rightOverlapped.hEvent};\n    for (;;)\n    {\n        if ((LeftHandle == nullptr) || (RightHandle == nullptr))\n        {\n            break;\n        }\n\n        DWORD leftBytesRead = 0;\n        if (!leftReadPending && LeftHandle)\n        {\n            if (!ReadFile(LeftHandle, leftReadSpan.data(), gsl::narrow_cast<DWORD>(leftReadSpan.size()), &leftBytesRead, &leftOverlapped))\n            {\n                THROW_LAST_ERROR_IF(GetLastError() != ERROR_IO_PENDING);\n            }\n\n            leftReadPending = true;\n        }\n\n        DWORD rightBytesRead = 0;\n        if (!rightReadPending && RightHandle)\n        {\n            if (!ReadFile(RightHandle, rightReadSpan.data(), gsl::narrow_cast<DWORD>(rightReadSpan.size()), &rightBytesRead, &rightOverlapped))\n            {\n                THROW_LAST_ERROR_IF(GetLastError() != ERROR_IO_PENDING);\n            }\n\n            rightReadPending = true;\n        }\n\n        const DWORD waitResult = WaitForMultipleObjects(RTL_NUMBER_OF(waitObjects), waitObjects, FALSE, INFINITE);\n        if (waitResult == WAIT_OBJECT_0)\n        {\n            LOG_LAST_ERROR_IF_MSG(\n                !GetOverlappedResult(LeftHandle, &leftOverlapped, &leftBytesRead, FALSE), \"WSAGetLastError %d\", WSAGetLastError());\n\n            leftReadPending = false;\n            if (leftBytesRead == 0)\n            {\n                LeftHandle = nullptr;\n                if (WI_IsFlagSet(Flags, RelayFlags::RightIsSocket))\n                {\n                    LOG_LAST_ERROR_IF(shutdown(reinterpret_cast<SOCKET>(RightHandle), SD_SEND) == SOCKET_ERROR);\n                }\n            }\n            else if (RightHandle != nullptr)\n            {\n                auto writeSpan = leftReadSpan.first(leftBytesRead);\n                bytesWritten = InterruptableWrite(RightHandle, writeSpan, {}, &leftOverlapped);\n                if (bytesWritten == 0)\n                {\n                    break;\n                }\n\n                leftOffset.QuadPart += leftBytesRead;\n                leftOverlapped.Offset = leftOffset.LowPart;\n                leftOverlapped.OffsetHigh = leftOffset.HighPart;\n            }\n        }\n        else if (waitResult == (WAIT_OBJECT_0 + 1))\n        {\n            LOG_LAST_ERROR_IF_MSG(\n                !GetOverlappedResult(RightHandle, &rightOverlapped, &rightBytesRead, FALSE), \"WSAGetLastError %d\", WSAGetLastError());\n\n            rightReadPending = false;\n            if (rightBytesRead == 0)\n            {\n                RightHandle = nullptr;\n                if (WI_IsFlagSet(Flags, RelayFlags::LeftIsSocket))\n                {\n                    LOG_LAST_ERROR_IF(shutdown(reinterpret_cast<SOCKET>(LeftHandle), SD_SEND) == SOCKET_ERROR);\n                }\n            }\n            else if (LeftHandle != nullptr)\n            {\n                auto writeSpan = rightReadSpan.first(rightBytesRead);\n                bytesWritten = InterruptableWrite(LeftHandle, writeSpan, {}, &rightOverlapped);\n                if (bytesWritten == 0)\n                {\n                    break;\n                }\n\n                rightOffset.QuadPart += rightBytesRead;\n                rightOverlapped.Offset = rightOffset.LowPart;\n                rightOverlapped.OffsetHigh = rightOffset.HighPart;\n            }\n        }\n        else\n        {\n            THROW_HR_MSG(E_FAIL, \"WaitForMultipleObjects %d\", waitResult);\n        }\n    }\n}\n\nvoid wsl::windows::common::relay::SocketRelay(_In_ SOCKET LeftSocket, _In_ SOCKET RightSocket, _In_ size_t BufferSize)\n{\n    constexpr RelayFlags flags = RelayFlags::LeftIsSocket | RelayFlags::RightIsSocket;\n    BidirectionalRelay(reinterpret_cast<HANDLE>(LeftSocket), reinterpret_cast<HANDLE>(RightSocket), BufferSize, flags);\n}\n\nvoid ScopedRelay::Sync()\n{\n    if (m_thread.joinable())\n    {\n        m_thread.join();\n    }\n}\n\nScopedRelay::~ScopedRelay()\n{\n    try\n    {\n        m_onDestroy();\n    }\n    CATCH_LOG();\n\n    m_exitEvent.SetEvent();\n    Sync();\n}\n\nvoid ScopedRelay::Run(_In_ HANDLE Input, _In_ HANDLE Output, size_t BufferSize) const\n{\n    wsl::windows::common::wslutil::SetThreadDescription(L\"ScopedRelay\");\n\n    try\n    {\n        InterruptableRelay(Input, Output, m_exitEvent.get(), BufferSize);\n    }\n    CATCH_LOG();\n}\n\nScopedMultiRelay::ScopedMultiRelay(const std::vector<HANDLE>& Inputs, const TWriteMethod& Write, size_t BufferSize)\n{\n    m_thread = std::thread{[this, BufferSize = BufferSize, Inputs = std::move(Inputs), Write = std::move(Write)]() {\n        Run(Inputs, Write, BufferSize);\n    }};\n}\n\nvoid ScopedMultiRelay::Sync()\n{\n    if (m_thread.joinable())\n    {\n        m_thread.join();\n    }\n}\n\nScopedMultiRelay::~ScopedMultiRelay()\n{\n    m_exitEvent.SetEvent();\n    Sync();\n}\n\nvoid ScopedMultiRelay::Run(const std::vector<HANDLE>& Handles, const TWriteMethod& Write, size_t BufferSize) const\ntry\n{\n    enum State\n    {\n        Standby,\n        Pending,\n        Eof\n    };\n\n    struct Input\n    {\n        HANDLE Handle;\n        LARGE_INTEGER Offset;\n        std::vector<std::byte> Buffer;\n        wil::unique_event Event{wil::EventOptions::ManualReset};\n        OVERLAPPED Overlapped;\n        State State = Standby;\n\n        Input(Input&&) = default;\n        Input& operator=(Input&&) = default;\n\n        Input(HANDLE Handle, LARGE_INTEGER Offset, size_t BufferSize) : Handle(Handle), Offset(Offset), Buffer(BufferSize)\n        {\n            Overlapped.hEvent = Event.get();\n        }\n\n        ~Input()\n        {\n            // Cancel outstanding IO, if any.\n            if (State == Pending)\n            {\n                CancelIoEx(Handle, &Overlapped);\n                DWORD bytesRead{};\n                GetOverlappedResult(Handle, &Overlapped, &bytesRead, TRUE);\n            }\n        }\n    };\n\n    std::vector<Input> Inputs;\n    for (const auto& e : Handles)\n    {\n        Inputs.emplace_back(e, InitializeFileOffset(e), BufferSize);\n    }\n\n    while (true)\n    {\n        // Exit if all inputs are completed, or if the exit event is set.\n        if (m_exitEvent.is_signaled() || std::all_of(Inputs.begin(), Inputs.end(), [](const auto& e) { return e.State == Eof; }))\n        {\n            return;\n        }\n\n        for (size_t i = 0; i < Inputs.size(); i++)\n        {\n            auto& e = Inputs[i];\n\n            // If a read has been scheduled, check if IO is available.\n            if (e.State == Pending)\n            {\n                if (e.Event.is_signaled())\n                {\n                    DWORD Transferred{};\n                    if (!GetOverlappedResult(e.Handle, &e.Overlapped, &Transferred, TRUE))\n                    {\n                        auto lastError = GetLastError();\n                        if ((lastError == ERROR_HANDLE_EOF) || (lastError == ERROR_BROKEN_PIPE))\n                        {\n                            e.State = Eof;\n                            continue;\n                        }\n\n                        THROW_LAST_ERROR_IF(lastError != ERROR_IO_PENDING);\n                    }\n\n                    // IO is available.\n                    Write(i, gsl::make_span(e.Buffer.data(), Transferred));\n\n                    // Update input state.\n                    e.Offset.QuadPart += Transferred;\n                    e.State = Standby;\n                }\n            }\n\n            // If no read is pending, start one.\n            if (e.State == Standby)\n            {\n                e.Event.ResetEvent();\n\n                e.Overlapped.Offset = e.Offset.LowPart;\n                e.Overlapped.OffsetHigh = e.Offset.HighPart;\n\n                DWORD BytesRead{};\n                if (ReadFile(e.Handle, e.Buffer.data(), static_cast<DWORD>(e.Buffer.size()), &BytesRead, &e.Overlapped))\n                {\n                    // IO is available.\n                    Write(i, gsl::make_span(e.Buffer.data(), BytesRead));\n\n                    // Update input state.\n                    e.Offset.QuadPart += BytesRead;\n                    e.State = Standby;\n                }\n                else\n                {\n                    auto lastError = GetLastError();\n                    if ((lastError == ERROR_HANDLE_EOF) || (lastError == ERROR_BROKEN_PIPE))\n                    {\n                        e.State = Eof;\n                        continue;\n                    }\n\n                    THROW_LAST_ERROR_IF(lastError != ERROR_IO_PENDING);\n                    e.State = Pending;\n                }\n            }\n        }\n\n        // Only wait if all non-completed inputs have a scheduled ReadFile to avoid a pipe hang.\n        if (std::all_of(Inputs.begin(), Inputs.end(), [](const auto& e) { return e.State == Eof || e.State == Pending; }))\n        {\n            // Wait until a handle is signaled.\n            std::vector<HANDLE> waits{m_exitEvent.get()};\n            for (const auto& e : Inputs)\n            {\n                if (e.State == Pending)\n                {\n                    waits.emplace_back(e.Event.get());\n                }\n            }\n\n            THROW_LAST_ERROR_IF(WaitForMultipleObjects(static_cast<DWORD>(waits.size()), waits.data(), false, INFINITE) == WAIT_FAILED);\n        }\n    }\n}\nCATCH_LOG()\n\nvoid MultiHandleWait::AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags)\n{\n    m_handles.emplace_back(flags, std::move(handle));\n}\n\nvoid MultiHandleWait::Cancel()\n{\n    m_cancel = true;\n}\nbool MultiHandleWait::Run(std::optional<std::chrono::milliseconds> Timeout)\n{\n    m_cancel = false; // Run may be called multiple times.\n\n    std::optional<std::chrono::steady_clock::time_point> deadline;\n\n    if (Timeout.has_value())\n    {\n        deadline = std::chrono::steady_clock::now() + Timeout.value();\n    }\n\n    // Run until all handles are completed.\n\n    while (!m_handles.empty() && !m_cancel)\n    {\n        // Schedule IO on each handle until all are either pending, or completed.\n        for (size_t i = 0; i < m_handles.size(); i++)\n        {\n            while (m_handles[i].second->GetState() == IOHandleStatus::Standby)\n            {\n                try\n                {\n                    m_handles[i].second->Schedule();\n                }\n                catch (...)\n                {\n                    if (WI_IsFlagSet(m_handles[i].first, Flags::IgnoreErrors))\n                    {\n                        m_handles[i].second.reset(); // Reset the handle so it can be deleted.\n                    }\n                    else\n                    {\n                        throw;\n                    }\n                }\n            }\n        }\n\n        // Remove completed handles from m_handles.\n        for (auto it = m_handles.begin(); it != m_handles.end();)\n        {\n            if (!it->second)\n            {\n                it = m_handles.erase(it);\n            }\n            else if (it->second->GetState() == IOHandleStatus::Completed)\n            {\n                if (WI_IsFlagSet(it->first, Flags::CancelOnCompleted))\n                {\n                    m_cancel = true; // Cancel the IO if a handle with CancelOnCompleted is in the completed state.\n                }\n\n                it = m_handles.erase(it);\n            }\n            else\n            {\n                ++it;\n            }\n        }\n\n        if (m_handles.empty() || m_cancel)\n        {\n            break;\n        }\n\n        // Wait for the next operation to complete.\n        std::vector<HANDLE> waitHandles;\n        for (const auto& e : m_handles)\n        {\n            waitHandles.emplace_back(e.second->GetHandle());\n        }\n\n        DWORD waitTimeout = INFINITE;\n        if (deadline.has_value())\n        {\n            auto miliseconds =\n                std::chrono::duration_cast<std::chrono::milliseconds>(deadline.value() - std::chrono::steady_clock::now()).count();\n\n            waitTimeout = static_cast<DWORD>(std::max(0LL, miliseconds));\n        }\n\n        auto result = WaitForMultipleObjects(static_cast<DWORD>(waitHandles.size()), waitHandles.data(), false, waitTimeout);\n        if (result == WAIT_TIMEOUT)\n        {\n            THROW_WIN32(ERROR_TIMEOUT);\n        }\n        else if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + m_handles.size())\n        {\n            auto index = result - WAIT_OBJECT_0;\n\n            try\n            {\n                m_handles[index].second->Collect();\n            }\n            catch (...)\n            {\n                if (WI_IsFlagSet(m_handles[index].first, Flags::IgnoreErrors))\n                {\n                    m_handles.erase(m_handles.begin() + index);\n                }\n                else\n                {\n                    throw;\n                }\n            }\n        }\n        else\n        {\n            THROW_LAST_ERROR_MSG(\"Timeout: %lu, Count: %llu\", waitTimeout, waitHandles.size());\n        }\n    }\n\n    return !m_cancel;\n}\n\nIOHandleStatus OverlappedIOHandle::GetState() const\n{\n    return State;\n}\n\nEventHandle::EventHandle(HandleWrapper&& Handle, std::function<void()>&& OnSignalled) :\n    Handle(std::move(Handle)), OnSignalled(std::move(OnSignalled))\n{\n}\n\nvoid EventHandle::Schedule()\n{\n    State = IOHandleStatus::Pending;\n}\n\nvoid EventHandle::Collect()\n{\n    State = IOHandleStatus::Completed;\n    OnSignalled();\n}\n\nHANDLE EventHandle::GetHandle() const\n{\n    return Handle.Get();\n}\n\nSingleAcceptHandle::SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted) :\n    ListenSocket(std::move(ListenSocket)), AcceptedSocket(std::move(AcceptedSocket)), OnAccepted(std::move(OnAccepted))\n{\n    Overlapped.hEvent = Event.get();\n}\n\nSingleAcceptHandle::~SingleAcceptHandle()\n{\n    if (State == IOHandleStatus::Pending)\n    {\n        LOG_IF_WIN32_BOOL_FALSE(CancelIoEx(ListenSocket.Get(), &Overlapped));\n\n        DWORD bytesProcessed{};\n        DWORD flagsReturned{};\n        if (!WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesProcessed, TRUE, &flagsReturned))\n        {\n            auto error = GetLastError();\n            LOG_LAST_ERROR_IF(error != ERROR_CONNECTION_ABORTED && error != ERROR_OPERATION_ABORTED);\n        }\n    }\n}\n\nvoid SingleAcceptHandle::Schedule()\n{\n    WI_ASSERT(State == IOHandleStatus::Standby);\n\n    // Schedule the accept.\n    DWORD bytesReturned{};\n    if (AcceptEx((SOCKET)ListenSocket.Get(), (SOCKET)AcceptedSocket.Get(), &AcceptBuffer, 0, sizeof(SOCKADDR_STORAGE), sizeof(SOCKADDR_STORAGE), &bytesReturned, &Overlapped))\n    {\n        // Accept completed immediately.\n        State = IOHandleStatus::Completed;\n        OnAccepted();\n    }\n    else\n    {\n        auto error = WSAGetLastError();\n        THROW_HR_IF_MSG(HRESULT_FROM_WIN32(error), error != ERROR_IO_PENDING, \"Handle: 0x%p\", (void*)ListenSocket.Get());\n\n        State = IOHandleStatus::Pending;\n    }\n}\n\nvoid SingleAcceptHandle::Collect()\n{\n    WI_ASSERT(State == IOHandleStatus::Pending);\n\n    DWORD bytesReceived{};\n    DWORD flagsReturned{};\n\n    THROW_IF_WIN32_BOOL_FALSE(WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesReceived, false, &flagsReturned));\n\n    State = IOHandleStatus::Completed;\n    OnAccepted();\n}\n\nHANDLE SingleAcceptHandle::GetHandle() const\n{\n    return Event.get();\n}"
  },
  {
    "path": "src/windows/common/relay.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    relay.hpp\n\nAbstract:\n\n    This file contains function declarations for the relay worker thread routines.\n\n--*/\n\n#pragma once\n\n#include <winsock2.h>\n#include \"ConsoleState.h\"\n\n#define LX_RELAY_BUFFER_SIZE 0x1000\n\nnamespace wsl::windows::common::relay {\n\nstd::thread CreateThread(_In_ HANDLE InputHandle, _In_ HANDLE OutputHandle, _In_opt_ HANDLE ExitHandle = nullptr, _In_ size_t BufferSize = LX_RELAY_BUFFER_SIZE);\n\nstd::thread CreateThread(_In_ wil::unique_handle&& InputHandle, _In_ HANDLE OutputHandle, _In_opt_ HANDLE ExitHandle = nullptr, _In_ size_t BufferSize = LX_RELAY_BUFFER_SIZE);\n\nstd::thread CreateThread(_In_ HANDLE InputHandle, _In_ wil::unique_handle&& OutputHandle, _In_opt_ HANDLE ExitHandle = nullptr, _In_ size_t BufferSize = LX_RELAY_BUFFER_SIZE);\n\nstd::thread CreateThread(\n    _In_ wil::unique_handle&& InputHandle,\n    _In_ wil::unique_handle&& OutputHandle,\n    _In_opt_ HANDLE ExitHandle = nullptr,\n    _In_ size_t BufferSize = LX_RELAY_BUFFER_SIZE);\n\nDWORD\nInterruptableRead(_In_ HANDLE InputHandle, _In_ gsl::span<gsl::byte> Buffer, _In_ const std::vector<HANDLE>& ExitHandles, _In_opt_ LPOVERLAPPED Overlapped = nullptr);\n\nvoid InterruptableRelay(_In_ HANDLE InputHandle, _In_opt_ HANDLE OutputHandle, _In_opt_ HANDLE ExitHandle = nullptr, _In_ size_t BufferSize = LX_RELAY_BUFFER_SIZE);\n\nbool InterruptableWait(_In_ HANDLE WaitObject, _In_ const std::vector<HANDLE>& ExitHandles = {});\n\nDWORD\nInterruptableWrite(_In_ HANDLE OutputHandle, _In_ gsl::span<const gsl::byte> Buffer, _In_ const std::vector<HANDLE>& ExitHandles, _In_ LPOVERLAPPED Overlapped);\n\nenum class RelayFlags\n{\n    None = 0,\n    LeftIsSocket = 1,\n    RightIsSocket = 2\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(RelayFlags);\n\nvoid BidirectionalRelay(_In_ HANDLE LeftHandle, _In_ HANDLE RightHandle, _In_ size_t BufferSize = LX_RELAY_BUFFER_SIZE, _In_ RelayFlags Flags = RelayFlags::None);\n\nvoid SocketRelay(_In_ SOCKET LeftSocket, _In_ SOCKET RightSocket, _In_ size_t BufferSize = LX_RELAY_BUFFER_SIZE);\n\nclass ScopedMultiRelay\n{\npublic:\n    using TWriteMethod = std::function<void(size_t, const gsl::span<gsl::byte>& buffer)>;\n    ScopedMultiRelay(const std::vector<HANDLE>& Inputs, const TWriteMethod& Write, size_t BufferSize = LX_RELAY_BUFFER_SIZE);\n\n    ~ScopedMultiRelay();\n\n    ScopedMultiRelay(ScopedMultiRelay&& other) = default;\n    ScopedMultiRelay(const ScopedMultiRelay&) = delete;\n\n    ScopedMultiRelay& operator=(const ScopedMultiRelay&) = delete;\n    ScopedMultiRelay& operator=(ScopedMultiRelay&&) = delete;\n\n    // Blocks until the relaying is complete.\n    // This is useful for situations where the relay should make sure that all\n    // the content has been flushed before exiting.\n    void Sync();\n\nprivate:\n    void Run(const std::vector<HANDLE>& Inputs, const TWriteMethod& Write, size_t BufferSize = LX_RELAY_BUFFER_SIZE) const;\n\n    std::thread m_thread;\n    wil::unique_event m_exitEvent{wil::EventOptions::ManualReset};\n};\n\n// Helper class to relay the output of a handle to another.\n// Note: The relay can take ownership of the handles if desired.\n// Doing that will cause the handle to be released when the relaying is complete.\n\nclass ScopedRelay\n{\npublic:\n    template <typename TInput, typename TOutput>\n    ScopedRelay(\n        TInput&& Input, TOutput&& Output, size_t BufferSize = LX_RELAY_BUFFER_SIZE, std::function<void()>&& OnDestroy = []() {}) :\n        m_onDestroy(std::move(OnDestroy))\n    {\n        m_thread = std::thread{[this, Input = std::move(Input), Output = std::move(Output), BufferSize = BufferSize]() {\n            try\n            {\n                Run(GetUnderlyingHandle(Input), GetUnderlyingHandle(Output), BufferSize);\n            }\n            CATCH_LOG();\n        }};\n    }\n\n    ~ScopedRelay();\n\n    ScopedRelay(ScopedRelay&& other) = default;\n    ScopedRelay(const ScopedRelay&) = delete;\n\n    ScopedRelay& operator=(const ScopedRelay&) = delete;\n    ScopedRelay& operator=(ScopedRelay&&) = delete;\n\n    // Blocks until the relaying is complete.\n    // This is useful for situations where the relay should make sure that all\n    // the content has been flushed before exiting.\n    void Sync();\n\nprivate:\n    template <typename THandle>\n    static HANDLE GetUnderlyingHandle(THandle& handle)\n    {\n        if constexpr (std::is_same_v<std::remove_cv_t<THandle>, HANDLE>)\n        {\n            return handle;\n        }\n        else if constexpr (std::is_same_v<std::remove_cv_t<THandle>, wil::unique_handle>)\n        {\n            return handle.get();\n        }\n        else if constexpr (std::is_same_v<std::remove_cv_t<THandle>, wil::unique_socket>)\n        {\n            return reinterpret_cast<HANDLE>(handle.get());\n        }\n        else if constexpr (std::is_same_v<std::remove_cv_t<THandle>, SOCKET>)\n        {\n            return reinterpret_cast<HANDLE>(handle);\n        }\n        else\n        {\n            // If this assert fails, an invalid type was passed to ScopedRelay\n            static_assert(sizeof(THandle) != sizeof(THandle));\n        }\n    }\n\n    void Run(_In_ HANDLE Input, _In_ HANDLE Output, size_t BufferSize) const;\n\n    std::thread m_thread;\n    wil::unique_event m_exitEvent{wil::EventOptions::ManualReset};\n    std::function<void()> m_onDestroy;\n};\n\nenum class IOHandleStatus\n{\n    Standby,\n    Pending,\n    Completed\n};\n\nstruct HandleWrapper\n{\n    DEFAULT_MOVABLE(HandleWrapper);\n    NON_COPYABLE(HandleWrapper)\n\n    HandleWrapper(\n        wil::unique_handle&& handle, std::function<void()>&& OnClose = []() {}) :\n        Handle(handle.get()), OwnedHandle(std::move(handle)), OnClose(std::move(OnClose))\n    {\n    }\n\n    HandleWrapper(\n        wil::unique_socket&& handle, std::function<void()>&& OnClose = []() {}) :\n        Handle((HANDLE)handle.get()), OwnedHandle(wil::unique_socket{handle.release()}), OnClose(std::move(OnClose))\n    {\n    }\n\n    HandleWrapper(\n        wil::unique_event&& handle, std::function<void()>&& OnClose = []() {}) :\n        Handle(handle.get()), OwnedHandle(wil::unique_handle{handle.release()}), OnClose(std::move(OnClose))\n    {\n    }\n\n    HandleWrapper(\n        SOCKET handle, std::function<void()>&& OnClose = []() {}) :\n        Handle(reinterpret_cast<HANDLE>(handle)), OnClose(std::move(OnClose))\n    {\n    }\n\n    HandleWrapper(HANDLE handle, std::function<void()>&& OnClose = []() {}) : Handle(handle), OnClose(std::move(OnClose))\n    {\n    }\n\n    HandleWrapper(\n        wil::unique_hfile&& handle, std::function<void()>&& OnClose = []() {}) :\n        Handle(handle.get()), OwnedHandle(wil::unique_handle{handle.release()}), OnClose(std::move(OnClose))\n    {\n    }\n\n    ~HandleWrapper()\n    {\n        Reset();\n    }\n\n    HANDLE Get() const\n    {\n        return Handle;\n    }\n\n    void Reset()\n    {\n        if (OnClose != nullptr)\n        {\n            OnClose();\n            OnClose = nullptr;\n        }\n\n        OwnedHandle = {};\n        Handle = nullptr;\n    }\n\nprivate:\n    HANDLE Handle{};\n    std::variant<wil::unique_handle, wil::unique_socket> OwnedHandle;\n    std::function<void()> OnClose;\n};\n\nclass OverlappedIOHandle\n{\npublic:\n    NON_COPYABLE(OverlappedIOHandle)\n    NON_MOVABLE(OverlappedIOHandle)\n\n    OverlappedIOHandle() = default;\n    virtual ~OverlappedIOHandle() = default;\n    virtual void Schedule() = 0;\n    virtual void Collect() = 0;\n    virtual HANDLE GetHandle() const = 0;\n    IOHandleStatus GetState() const;\n\nprotected:\n    IOHandleStatus State = IOHandleStatus::Standby;\n};\n\nclass EventHandle : public OverlappedIOHandle\n{\npublic:\n    NON_COPYABLE(EventHandle)\n    NON_MOVABLE(EventHandle)\n\n    EventHandle(HandleWrapper&& Handle, std::function<void()>&& OnSignalled = []() {});\n    void Schedule() override;\n    void Collect() override;\n    HANDLE GetHandle() const override;\n\nprivate:\n    HandleWrapper Handle;\n    std::function<void()> OnSignalled;\n};\n\nclass SingleAcceptHandle : public OverlappedIOHandle\n{\npublic:\n    NON_COPYABLE(SingleAcceptHandle)\n    NON_MOVABLE(SingleAcceptHandle)\n\n    SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted);\n    ~SingleAcceptHandle();\n\n    void Schedule() override;\n    void Collect() override;\n    HANDLE GetHandle() const override;\n\nprivate:\n    HandleWrapper ListenSocket;\n    HandleWrapper AcceptedSocket;\n    wil::unique_event Event{wil::EventOptions::ManualReset};\n    OVERLAPPED Overlapped{};\n    std::function<void()> OnAccepted;\n    char AcceptBuffer[2 * sizeof(SOCKADDR_STORAGE)];\n};\n\nclass MultiHandleWait\n{\npublic:\n    enum Flags\n    {\n        None = 0,\n        CancelOnCompleted = 1,\n        IgnoreErrors = 2\n    };\n\n    MultiHandleWait() = default;\n\n    void AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags = Flags::None);\n    bool Run(std::optional<std::chrono::milliseconds> Timeout);\n    void Cancel();\n\nprivate:\n    std::vector<std::pair<Flags, std::unique_ptr<OverlappedIOHandle>>> m_handles;\n    bool m_cancel = false;\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(MultiHandleWait::Flags);\n\n} // namespace wsl::windows::common::relay\n"
  },
  {
    "path": "src/windows/common/socket.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    socket.cpp\n\nAbstract:\n\n    This file contains socket helper function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include <mutex>\n#include \"socket.hpp\"\n#pragma hdrstop\n\nbool wsl::windows::common::socket::CancellableAccept(\n    _In_ SOCKET ListenSocket, _In_ SOCKET Socket, _In_ DWORD Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)\n{\n    relay::MultiHandleWait io;\n\n    bool accepted = false;\n\n    io.AddHandle(std::make_unique<relay::SingleAcceptHandle>(ListenSocket, Socket, [&]() { accepted = true; }), relay::MultiHandleWait::CancelOnCompleted);\n\n    if (ExitHandle != nullptr)\n    {\n        io.AddHandle(std::make_unique<relay::EventHandle>(ExitHandle), relay::MultiHandleWait::CancelOnCompleted);\n    }\n\n    io.Run(std::chrono::milliseconds(Timeout));\n\n    if (!accepted)\n    {\n        return false; // Accept was cancelled by the exit event.\n    }\n\n    // Set the accept context to mark the socket as connected.\n    THROW_LAST_ERROR_IF_MSG(\n        setsockopt(Socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, reinterpret_cast<char*>(&ListenSocket), sizeof(ListenSocket)) == SOCKET_ERROR,\n        \"From: %hs\",\n        std::format(\"{}\", Location).c_str());\n\n    return true;\n}\n\nstd::pair<DWORD, DWORD> wsl::windows::common::socket::GetResult(\n    _In_ SOCKET Socket, _In_ OVERLAPPED& Overlapped, _In_ DWORD Timeout, _In_ HANDLE ExitHandle, _In_ const std::source_location& Location)\n{\n    const int error = WSAGetLastError();\n    THROW_HR_IF(HRESULT_FROM_WIN32(error), error != WSA_IO_PENDING);\n\n    std::vector<HANDLE> waitObjects{};\n    waitObjects.push_back(Overlapped.hEvent);\n    if (ARGUMENT_PRESENT(ExitHandle))\n    {\n        waitObjects.push_back(ExitHandle);\n    }\n\n    DWORD bytesProcessed;\n    DWORD flagsReturned;\n    auto cancelFunction = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n        CancelIoEx(reinterpret_cast<HANDLE>(Socket), &Overlapped);\n        WSAGetOverlappedResult(Socket, &Overlapped, &bytesProcessed, TRUE, &flagsReturned);\n    });\n\n    const DWORD waitStatus = WaitForMultipleObjects(gsl::narrow_cast<DWORD>(waitObjects.size()), waitObjects.data(), FALSE, Timeout);\n    if (waitObjects.size() > 1 && waitStatus == WAIT_OBJECT_0 + 1)\n    {\n        return {0, 0};\n    }\n\n    THROW_HR_IF_MSG(HCS_E_CONNECTION_TIMEOUT, (waitStatus != WAIT_OBJECT_0), \"From: %hs\", std::format(\"{}\", Location).c_str());\n\n    cancelFunction.release();\n    const bool result = WSAGetOverlappedResult(Socket, &Overlapped, &bytesProcessed, FALSE, &flagsReturned);\n    if (!result)\n    {\n        const auto lastError = WSAGetLastError();\n        if (lastError != WSAECONNABORTED || (ExitHandle != nullptr && WaitForSingleObject(ExitHandle, 0) == WAIT_TIMEOUT))\n        {\n            THROW_WIN32(lastError);\n        }\n        else\n        {\n            return {0, 0};\n        }\n    }\n    return {bytesProcessed, flagsReturned};\n}\n\nint wsl::windows::common::socket::Receive(\n    _In_ SOCKET Socket, _In_ gsl::span<gsl::byte> Buffer, _In_opt_ HANDLE ExitHandle, _In_ DWORD Flags, _In_ DWORD Timeout, _In_ const std::source_location& Location)\n{\n    const int BytesRead = ReceiveNoThrow(Socket, Buffer, ExitHandle, Flags, Timeout, Location);\n    THROW_LAST_ERROR_IF(BytesRead == SOCKET_ERROR);\n\n    return BytesRead;\n}\n\nint wsl::windows::common::socket::ReceiveNoThrow(\n    _In_ SOCKET Socket, _In_ gsl::span<gsl::byte> Buffer, _In_opt_ HANDLE ExitHandle, _In_ DWORD Flags, _In_ DWORD Timeout, _In_ const std::source_location& Location)\n{\n    OVERLAPPED Overlapped{};\n    const wil::unique_event OverlappedEvent(wil::EventOptions::ManualReset);\n    WSABUF VectorBuffer = {gsl::narrow_cast<ULONG>(Buffer.size()), reinterpret_cast<CHAR*>(Buffer.data())};\n    Overlapped.hEvent = OverlappedEvent.get();\n    DWORD BytesReturned{};\n    if (WSARecv(Socket, &VectorBuffer, 1, &BytesReturned, &Flags, &Overlapped, nullptr) != 0)\n    {\n        try\n        {\n            BytesReturned = SOCKET_ERROR;\n            auto [innerBytes, Flags] = GetResult(Socket, Overlapped, Timeout, ExitHandle, Location);\n            BytesReturned = innerBytes;\n        }\n        catch (...)\n        {\n            LOG_CAUGHT_EXCEPTION();\n            // Receive will call GetLastError to look for the error code\n            SetLastError(wil::ResultFromCaughtException());\n        }\n    }\n\n    return BytesReturned;\n}\n\nstd::vector<gsl::byte> wsl::windows::common::socket::Receive(\n    _In_ SOCKET Socket, _In_opt_ HANDLE ExitHandle, _In_ DWORD Timeout, _In_ const std::source_location& Location)\n{\n    Receive(Socket, {}, ExitHandle, MSG_PEEK, Timeout, Location);\n\n    ULONG Size = 0;\n    THROW_LAST_ERROR_IF(ioctlsocket(Socket, FIONREAD, &Size) == SOCKET_ERROR);\n\n    std::vector<gsl::byte> Buffer(Size);\n    WI_VERIFY(Receive(Socket, gsl::make_span(Buffer), ExitHandle, MSG_WAITALL, Timeout, Location) == static_cast<int>(Size));\n\n    return Buffer;\n}\n\nint wsl::windows::common::socket::Send(\n    _In_ SOCKET Socket, _In_ gsl::span<const gsl::byte> Buffer, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)\n{\n    const wil::unique_event OverlappedEvent(wil::EventOptions::ManualReset);\n    OVERLAPPED Overlapped{};\n    Overlapped.hEvent = OverlappedEvent.get();\n\n    DWORD Offset = 0;\n    while (Offset < Buffer.size())\n    {\n        OverlappedEvent.ResetEvent();\n\n        WSABUF VectorBuffer = {\n            gsl::narrow_cast<ULONG>(Buffer.size() - Offset), const_cast<CHAR*>(reinterpret_cast<const CHAR*>(Buffer.data() + Offset))};\n\n        DWORD BytesWritten{};\n        if (WSASend(Socket, &VectorBuffer, 1, &BytesWritten, 0, &Overlapped, nullptr) != 0)\n        {\n            // If WSASend returns non-zero, expect WSA_IO_PENDING.\n            if (auto error = WSAGetLastError(); error != WSA_IO_PENDING)\n            {\n                THROW_WIN32_MSG(error, \"WSASend failed. From: %hs\", std::format(\"{}\", Location).c_str());\n            }\n\n            DWORD Flags;\n            std::tie(BytesWritten, Flags) = GetResult(Socket, Overlapped, INFINITE, ExitHandle, Location);\n            if (BytesWritten == 0)\n            {\n                THROW_WIN32_MSG(ERROR_CONNECTION_ABORTED, \"Socket closed during WSASend(). From: %hs\", std::format(\"{}\", Location).c_str());\n            }\n        }\n\n        Offset += BytesWritten;\n        if (Offset < Buffer.size())\n        {\n            WSL_LOG(\"PartialSocketWrite\", TraceLoggingValue(Buffer.size(), \"MessagSize\"), TraceLoggingValue(Offset, \"Offset\"));\n        }\n    }\n\n    WI_ASSERT(Offset == gsl::narrow_cast<DWORD>(Buffer.size()));\n\n    return Offset;\n}\n"
  },
  {
    "path": "src/windows/common/socket.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    socket.hpp\n\nAbstract:\n\n    This file contains socket helper function declarations.\n\n--*/\n\n#pragma once\n\n#include <wil/resource.h>\n\nnamespace wsl::windows::common::socket {\n\nbool CancellableAccept(\n    _In_ SOCKET ListenSocket,\n    _In_ SOCKET Socket,\n    _In_ DWORD Timeout,\n    _In_opt_ HANDLE ExitHandle,\n    _In_ const std::source_location& Location = std::source_location::current());\n\nstd::pair<DWORD, DWORD> GetResult(\n    _In_ SOCKET Socket, _In_ OVERLAPPED& Overlapped, _In_ DWORD Timeout, _In_ HANDLE ExitHandle, _In_ const std::source_location& Location);\n\nint Receive(\n    _In_ SOCKET Socket,\n    _In_ gsl::span<gsl::byte> Buffer,\n    _In_opt_ HANDLE ExitHandle = nullptr,\n    _In_ DWORD Flags = MSG_WAITALL,\n    _In_ DWORD Timeout = INFINITE,\n    _In_ const std::source_location& Location = std::source_location::current());\n\nstd::vector<gsl::byte> Receive(\n    _In_ SOCKET Socket,\n    _In_opt_ HANDLE ExitHandle = nullptr,\n    _In_ DWORD Timeout = INFINITE,\n    _In_ const std::source_location& Location = std::source_location::current());\n\nint ReceiveNoThrow(\n    _In_ SOCKET Socket,\n    _In_ gsl::span<gsl::byte> Buffer,\n    _In_opt_ HANDLE ExitHandle = nullptr,\n    _In_ DWORD Flags = MSG_WAITALL,\n    _In_ DWORD Timeout = INFINITE,\n    _In_ const std::source_location& Location = std::source_location::current());\n\nint Send(\n    _In_ SOCKET Socket,\n    _In_ gsl::span<const gsl::byte> Buffer,\n    _In_opt_ HANDLE ExitHandle = nullptr,\n    _In_ const std::source_location& Location = std::source_location::current());\n\n} // namespace wsl::windows::common::socket\n"
  },
  {
    "path": "src/windows/common/string.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    string.cpp\n\nAbstract:\n\n    This file contains string helper function definitions.\n\n--*/\n\n#include \"precomp.h\"\n\nstd::vector<std::string> wsl::windows::common::string::InitializeStringSet(_In_count_(BufferSize) LPCSTR Buffer, _In_ SIZE_T BufferSize)\n{\n    // Ensure the buffer ends with two NULL terminators.\n    THROW_HR_IF(E_INVALIDARG, ((BufferSize < 2) || (Buffer[BufferSize - 1] != ANSI_NULL) || (Buffer[BufferSize - 2] != ANSI_NULL)));\n\n    std::vector<std::string> values{};\n    for (LPCSTR current = Buffer; ANSI_NULL != *current; current += strlen(current) + 1)\n    {\n        values.push_back(current);\n    }\n\n    return values;\n}\n\nbool wsl::windows::common::string::IsPathComponentEqual(const std::wstring_view String1, const std::wstring_view String2)\n{\n    return CompareStringOrdinal(String1.data(), static_cast<int>(String1.size()), String2.data(), static_cast<int>(String2.size()), true) == CSTR_EQUAL;\n}\n\nstd::wstring wsl::windows::common::string::MultiByteToWide(_In_ LPCSTR Source, _In_ size_t CharacterCount)\n{\n    if (CharacterCount == -1)\n    {\n        CharacterCount = Source ? strlen(Source) : 0;\n    }\n\n    if (CharacterCount == 0)\n    {\n        return {};\n    }\n\n    THROW_HR_IF(E_BOUNDS, (CharacterCount > static_cast<size_t>(std::numeric_limits<int>::max())));\n\n    int required = MultiByteToWideChar(CP_UTF8, 0, Source, gsl::narrow_cast<int>(CharacterCount), nullptr, 0);\n    THROW_LAST_ERROR_IF(required == 0);\n\n    std::wstring converted(required, L'\\0');\n    required = MultiByteToWideChar(CP_UTF8, 0, Source, gsl::narrow_cast<int>(CharacterCount), converted.data(), required);\n    THROW_LAST_ERROR_IF(required == 0);\n\n    return converted;\n}\n\nstd::wstring wsl::windows::common::string::MultiByteToWide(_In_ std::string_view Source)\n{\n    return MultiByteToWide(Source.data(), Source.size());\n}\n\nstd::wstring_view wsl::windows::common::string::StripLeadingWhitespace(_In_ std::wstring_view String)\n{\n    const size_t Index = String.find_first_not_of(L\" \\t\");\n    if (Index != std::wstring_view::npos)\n    {\n        String.remove_prefix(Index);\n    }\n    else\n    {\n        String = {};\n    }\n\n    return String;\n}\n\nstd::wstring_view wsl::windows::common::string::StripQuotes(_In_ std::wstring_view String)\n{\n    // If the string begins and ends with a quote character, remove them.\n    std::wstring_view Stripped = String;\n    if ((Stripped.size() > 1) && (Stripped[0] == L'\\\"') && (Stripped[Stripped.size() - 1] == L'\\\"'))\n    {\n        Stripped.remove_prefix(1);\n        Stripped.remove_suffix(1);\n    }\n\n    return Stripped;\n}\n\nstd::string wsl::windows::common::string::IpPrefixAddressToString(const IP_ADDRESS_PREFIX& ipAddressPrefix)\n{\n    return std::format(\"{}/{}\", SockAddrInetToString(ipAddressPrefix.Prefix), static_cast<uint32_t>(ipAddressPrefix.PrefixLength));\n}\n\nstd::string wsl::windows::common::string::SockAddrInetToString(const SOCKADDR_INET& sockAddrInet)\n{\n    std::string ipAddress(INET6_ADDRSTRLEN, '\\0');\n    switch (sockAddrInet.si_family)\n    {\n    case AF_INET:\n        RtlIpv4AddressToStringA(&sockAddrInet.Ipv4.sin_addr, ipAddress.data());\n        break;\n    case AF_INET6:\n        RtlIpv6AddressToStringA(&sockAddrInet.Ipv6.sin6_addr, ipAddress.data());\n        break;\n    default:\n        ipAddress = std::format(\"[[ADDRESS_FAMILY {}]]\", sockAddrInet.si_family);\n        break;\n    }\n    ipAddress.resize(std::strlen(ipAddress.data()));\n    return ipAddress;\n}\n\nstd::wstring wsl::windows::common::string::SockAddrInetToWstring(const SOCKADDR_INET& sockAddrInet)\n{\n    std::wstring ipAddress(INET6_ADDRSTRLEN, '\\0');\n    switch (sockAddrInet.si_family)\n    {\n    case AF_INET:\n        RtlIpv4AddressToStringW(&sockAddrInet.Ipv4.sin_addr, ipAddress.data());\n        break;\n    case AF_INET6:\n        RtlIpv6AddressToStringW(&sockAddrInet.Ipv6.sin6_addr, ipAddress.data());\n        break;\n    default:\n        ipAddress = std::format(L\"[[ADDRESS_FAMILY {}]]\", sockAddrInet.si_family);\n        break;\n    }\n    ipAddress.resize(std::wcslen(ipAddress.data()));\n    return ipAddress;\n}\n\nstd::wstring wsl::windows::common::string::IntegerIpv4ToWstring(const uint32_t ipAddress)\n{\n    in_addr address{};\n    address.S_un.S_addr = ipAddress;\n\n    std::wstring stringAddress(INET_ADDRSTRLEN, '\\0');\n    WI_VERIFY(InetNtopW(AF_INET, &address, stringAddress.data(), stringAddress.size()) != nullptr);\n    stringAddress.resize(wcslen(stringAddress.c_str()));\n\n    return stringAddress;\n}\n\nSOCKADDR_INET wsl::windows::common::string::StringToSockAddrInet(const std::wstring& stringIpAddress)\n{\n    SOCKADDR_INET returnSockaddr{};\n    if (stringIpAddress.empty())\n    {\n        // return an empty IPv4 sockaddr\n        returnSockaddr.si_family = AF_INET;\n    }\n    else if (stringIpAddress.find(':', 0) == std::string::npos)\n    {\n        returnSockaddr.si_family = AF_INET;\n        const wchar_t* terminator;\n        THROW_IF_WIN32_ERROR_MSG(\n            RtlIpv4StringToAddressW(stringIpAddress.c_str(), TRUE, &terminator, &returnSockaddr.Ipv4.sin_addr),\n            \"RtlIpv4StringToAddressW(%ws)\",\n            stringIpAddress.c_str());\n    }\n    else\n    {\n        returnSockaddr.si_family = AF_INET6;\n        const wchar_t* terminator;\n        THROW_IF_WIN32_ERROR_MSG(\n            RtlIpv6StringToAddressW(stringIpAddress.c_str(), &terminator, &returnSockaddr.Ipv6.sin6_addr),\n            \"RtlIpv6StringToAddressW(%ws)\",\n            stringIpAddress.c_str());\n    }\n\n    return returnSockaddr;\n}\n\nstd::wstring wsl::windows::common::string::BytesToHex(const std::vector<BYTE>& bytes)\n{\n    std::wstringstream str;\n\n    str << L\"0x\";\n    str << std::hex;\n\n    for (const auto e : bytes)\n    {\n        str << std::setw(2) << std::setfill(L'0') << static_cast<int>(e);\n    }\n\n    return str.str();\n}\n\nstd::string wsl::windows::common::string::WideToMultiByte(_In_opt_ LPCWSTR Source, _In_ size_t CharacterCount)\n{\n    if (CharacterCount == -1)\n    {\n        CharacterCount = Source ? wcslen(Source) : 0;\n    }\n\n    if (CharacterCount == 0)\n    {\n        return {};\n    }\n\n    THROW_HR_IF(E_BOUNDS, (CharacterCount > static_cast<size_t>(std::numeric_limits<int>::max())));\n\n    int required = WideCharToMultiByte(CP_UTF8, 0, Source, gsl::narrow_cast<int>(CharacterCount), nullptr, 0, nullptr, nullptr);\n    THROW_LAST_ERROR_IF(required == 0);\n\n    std::string converted(required, '\\0');\n    required = WideCharToMultiByte(CP_UTF8, 0, Source, gsl::narrow_cast<int>(CharacterCount), converted.data(), required, nullptr, nullptr);\n    THROW_LAST_ERROR_IF(required == 0);\n\n    return converted;\n}\n\nstd::string wsl::windows::common::string::WideToMultiByte(_In_ std::wstring_view Source)\n{\n    return WideToMultiByte(Source.data(), Source.length());\n}"
  },
  {
    "path": "src/windows/common/string.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    string.hpp\n\nAbstract:\n\n    This file contains string management function declarations.\n\n--*/\n\n#pragma once\n\n#include \"helpers.hpp\"\n#include \"stringshared.h\"\n\n// Forward declare types to avoid pulling in excessive number of headers.\nusing IP_ADDRESS_PREFIX = struct _IP_ADDRESS_PREFIX;\nusing SOCKADDR_INET = union _SOCKADDR_INET;\n\nnamespace wsl::windows::common::string {\n\nstd::vector<std::string> InitializeStringSet(_In_count_(BufferSize) LPCSTR Buffer, _In_ SIZE_T BufferSize);\n\nbool IsPathComponentEqual(const std::wstring_view String1, const std::wstring_view String2);\n\nstd::wstring MultiByteToWide(_In_ LPCSTR Source, _In_ size_t CharacterCount = -1);\n\nstd::wstring MultiByteToWide(_In_ std::string_view Source);\n\nstd::wstring_view StripLeadingWhitespace(_In_ std::wstring_view String);\n\nstd::wstring_view StripQuotes(_In_ std::wstring_view String);\n\nstd::string IpPrefixAddressToString(const IP_ADDRESS_PREFIX& ipAddressPrefix);\nstd::string SockAddrInetToString(const SOCKADDR_INET& sockAddrInet);\nstd::wstring SockAddrInetToWstring(const SOCKADDR_INET& sockAddrInet);\nstd::wstring IntegerIpv4ToWstring(const uint32_t ipAddress);\nSOCKADDR_INET StringToSockAddrInet(const std::wstring& stringIpAddress);\nstd::wstring BytesToHex(const std::vector<BYTE>& bytes);\n\nstd::string WideToMultiByte(_In_opt_ LPCWSTR Source, _In_ size_t CharacterCount = -1);\n\nstd::string WideToMultiByte(_In_ std::wstring_view Source);\n\nstruct PhysicalMacAddress\n{\n    BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH]{};\n};\n\n} // namespace wsl::windows::common::string\n"
  },
  {
    "path": "src/windows/common/svccomm.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    svccomm.cpp\n\nAbstract:\n\n    This file contains function definitions for the SvcComm helper class.\n\n--*/\n\n#include \"precomp.h\"\n#include \"svccomm.hpp\"\n#include \"registry.hpp\"\n#include \"relay.hpp\"\n\n#pragma hdrstop\n\n//\n// Macros to test exit status (defined in sys\\wait.h).\n//\n\n#define LXSS_WEXITSTATUS(_status) ((_status) >> 8)\n#define LXSS_WSTATUS(_status) ((_status) & 0x7F)\n#define LXSS_WIFEXITED(_status) (LXSS_WSTATUS((_status)) == 0)\n\n#define IS_VALID_HANDLE(_handle) ((_handle != NULL) && (_handle != INVALID_HANDLE_VALUE))\n\n#define TTY_ALT_NUMPAD_VK_MENU (0x12)\n#define TTY_ESCAPE_CHARACTER (L'\\x1b')\n#define TTY_INPUT_EVENT_BUFFER_SIZE (16)\n#define TTY_UTF8_TRANSLATION_BUFFER_SIZE (4 * TTY_INPUT_EVENT_BUFFER_SIZE)\n\nusing wsl::windows::common::ClientExecutionContext;\nnamespace {\n\nBOOL GetNextCharacter(_In_ INPUT_RECORD* InputRecord, _Out_ PWCHAR NextCharacter);\nBOOL IsActionableKey(_In_ PKEY_EVENT_RECORD KeyEvent);\nvoid SpawnWslHost(_In_ HANDLE ServerPort, _In_ const GUID& DistroId, _In_opt_ LPCGUID VmId);\n\nstruct CreateProcessArguments\n{\n    CreateProcessArguments(LPCWSTR Filename, int Argc, LPCWSTR Argv[], ULONG LaunchFlags, LPCWSTR WorkingDirectory)\n    {\n        // Populate the current working directory.\n        //\n        // N.B. Failure to get the current working directory is non-fatal.\n        if (ARGUMENT_PRESENT(WorkingDirectory))\n        {\n            // If a current working directory was provided, it must be a Linux-style path.\n            WI_ASSERT(*WorkingDirectory == L'/' || *WorkingDirectory == L'~');\n\n            CurrentWorkingDirectory = WorkingDirectory;\n        }\n        else\n        {\n            LOG_IF_FAILED(wil::GetCurrentDirectoryW(CurrentWorkingDirectory));\n        }\n\n        // Populate the command line and file name.\n        //\n        // N.B. The CommandLineStrings vector contains weak references to the\n        //      strings in the CommandLine vector.\n        if (Argc > 0)\n        {\n            CommandLine.reserve(Argc);\n            std::transform(Argv, Argv + Argc, std::back_inserter(CommandLine), [](LPCWSTR Arg) {\n                return wsl::shared::string::WideToMultiByte(Arg);\n            });\n\n            CommandLineStrings.reserve(CommandLine.size());\n            std::transform(CommandLine.cbegin(), CommandLine.cend(), std::back_inserter(CommandLineStrings), [](const std::string& string) {\n                return string.c_str();\n            });\n        }\n\n        if (ARGUMENT_PRESENT(Filename))\n        {\n            FilenameString = wsl::shared::string::WideToMultiByte(Filename);\n        }\n\n        // Query the current NT %PATH% environment variable.\n        //\n        // N.B. Failure to query the path is non-fatal.\n        LOG_IF_FAILED(wil::ExpandEnvironmentStringsW(L\"%PATH%\", NtPath));\n\n        if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_TRANSLATE_ENVIRONMENT))\n        {\n            NtEnvironment.reset(GetEnvironmentStringsW());\n\n            // Calculate the size of the environment block.\n            for (PCWSTR Variable = NtEnvironment.get(); Variable[0] != '\\0';)\n            {\n                const size_t Length = wcslen(Variable) + 1;\n                NtEnvironmentLength += Length;\n                Variable += Length;\n            }\n\n            NtEnvironmentLength += 1;\n        }\n    }\n\n    std::vector<std::string> CommandLine{};\n    std::vector<LPCSTR> CommandLineStrings{};\n    std::wstring CurrentWorkingDirectory{};\n    std::string FilenameString{};\n    wsl::windows::common::helpers::unique_environment_strings NtEnvironment;\n    size_t NtEnvironmentLength{};\n    std::wstring NtPath{};\n};\n\nBOOL GetNextCharacter(_In_ INPUT_RECORD* InputRecord, _Out_ PWCHAR NextCharacter)\n{\n    BOOL IsNextCharacterValid = FALSE;\n    if (InputRecord->EventType == KEY_EVENT)\n    {\n        const auto KeyEvent = &InputRecord->Event.KeyEvent;\n        if ((IsActionableKey(KeyEvent) != FALSE) && ((KeyEvent->bKeyDown != FALSE) || (KeyEvent->wVirtualKeyCode == TTY_ALT_NUMPAD_VK_MENU)))\n        {\n            *NextCharacter = KeyEvent->uChar.UnicodeChar;\n            IsNextCharacterValid = TRUE;\n        }\n    }\n\n    return IsNextCharacterValid;\n}\n\nBOOL IsActionableKey(_In_ PKEY_EVENT_RECORD KeyEvent)\n{\n    //\n    // This is a bit complicated to discern.\n    //\n    // 1. Our first check is that we only want structures that\n    //    represent at least one key press. If we have 0, then we don't\n    //    need to bother. If we have >1, we'll send the key through\n    //    that many times into the pipe.\n    // 2. Our second check is where it gets confusing.\n    //    a. Characters that are non-null get an automatic pass. Copy\n    //       them through to the pipe.\n    //    b. Null characters need further scrutiny. We generally do not\n    //       pass nulls through EXCEPT if they're sourced from the\n    //       virtual terminal engine (or another application living\n    //       above our layer). If they're sourced by a non-keyboard\n    //       source, they'll have no scan code (since they didn't come\n    //       from a keyboard). But that rule has an exception too:\n    //       \"Enhanced keys\" from above the standard range of scan\n    //       codes will return 0 also with a special flag set that says\n    //       they're an enhanced key. That means the desired behavior\n    //       is:\n    //           Scan Code = 0, ENHANCED_KEY = 0\n    //               -> This came from the VT engine or another app\n    //                  above our layer.\n    //           Scan Code = 0, ENHANCED_KEY = 1\n    //               -> This came from the keyboard, but is a special\n    //                  key like 'Volume Up' that wasn't generally a\n    //                  part of historic (pre-1990s) keyboards.\n    //           Scan Code = <anything else>\n    //               -> This came from a keyboard directly.\n    //\n\n    if ((KeyEvent->wRepeatCount == 0) || ((KeyEvent->uChar.UnicodeChar == UNICODE_NULL) &&\n                                          ((KeyEvent->wVirtualScanCode != 0) || (WI_IsFlagSet(KeyEvent->dwControlKeyState, ENHANCED_KEY)))))\n    {\n        return FALSE;\n    }\n\n    return TRUE;\n}\n\nvoid InitializeInterop(_In_ HANDLE ServerPort, _In_ const GUID& DistroId)\n{\n    //\n    // Create a thread to handle interop requests.\n    //\n\n    wil::unique_handle WorkerThreadSeverPort{wsl::windows::common::wslutil::DuplicateHandle(ServerPort)};\n    std::thread([WorkerThreadSeverPort = std::move(WorkerThreadSeverPort)]() mutable {\n        wsl::windows::common::wslutil::SetThreadDescription(L\"Interop\");\n        wsl::windows::common::interop::WorkerThread(std::move(WorkerThreadSeverPort));\n    }).detach();\n\n    //\n    // Spawn wslhost to handle interop requests from processes that have\n    // been backgrounded and their console window has been closed.\n    //\n\n    SpawnWslHost(ServerPort, DistroId, nullptr);\n}\n\nvoid SpawnWslHost(_In_ HANDLE ServerPort, _In_ const GUID& DistroId, _In_opt_ LPCGUID VmId)\n{\n    wsl::windows::common::helpers::SetHandleInheritable(ServerPort);\n    const auto RegistrationComplete = wil::unique_event(wil::EventOptions::None);\n    const wil::unique_handle ParentProcess{wsl::windows::common::wslutil::DuplicateHandle(GetCurrentProcess(), std::nullopt, TRUE)};\n    THROW_LAST_ERROR_IF(!ParentProcess);\n\n    const wil::unique_handle Process{wsl::windows::common::helpers::LaunchInteropServer(\n        &DistroId, ServerPort, RegistrationComplete.get(), ParentProcess.get(), VmId)};\n\n    // Wait for either the child to exit, or the registration complete event to be set.\n    const HANDLE WaitHandles[] = {Process.get(), RegistrationComplete.get()};\n    const DWORD WaitStatus = WaitForMultipleObjects(RTL_NUMBER_OF(WaitHandles), WaitHandles, FALSE, INFINITE);\n    LOG_HR_IF_MSG(E_FAIL, (WaitStatus == WAIT_OBJECT_0), \"wslhost failed to register\");\n}\n} // namespace\n\n//\n// Exported function definitions.\n//\n\nvoid wsl::windows::common::RelayStandardInput(\n    HANDLE ConsoleHandle,\n    HANDLE OutputHandle,\n    const std::shared_ptr<wsl::shared::SocketChannel>& ControlChannel,\n    HANDLE ExitEvent,\n    wsl::windows::common::ConsoleState* Io)\ntry\n{\n    if (GetFileType(ConsoleHandle) != FILE_TYPE_CHAR)\n    {\n        wsl::windows::common::relay::InterruptableRelay(ConsoleHandle, OutputHandle, ExitEvent);\n        return;\n    }\n\n    //\n    // N.B. ReadConsoleInputEx has no associated import library.\n    //\n\n    static LxssDynamicFunction<decltype(ReadConsoleInputExW)> readConsoleInput(L\"Kernel32.dll\", \"ReadConsoleInputExW\");\n\n    INPUT_RECORD InputRecordBuffer[TTY_INPUT_EVENT_BUFFER_SIZE];\n    INPUT_RECORD* InputRecordPeek = &(InputRecordBuffer[1]);\n    KEY_EVENT_RECORD* KeyEvent;\n    DWORD RecordsRead;\n    OVERLAPPED Overlapped = {0};\n    const wil::unique_event OverlappedEvent(wil::EventOptions::ManualReset);\n    Overlapped.hEvent = OverlappedEvent.get();\n    const HANDLE WaitHandles[] = {ExitEvent, ConsoleHandle};\n    const std::vector<HANDLE> ExitHandles = {ExitEvent};\n    for (;;)\n    {\n        //\n        // Because some input events generated by the console are encoded with\n        // more than one input event, we have to be smart about reading the\n        // events.\n        //\n        // First, we peek at the next input event.\n        // If it's an escape (wch == L'\\x1b') event, then the characters that\n        //      follow are part of an input sequence. We can't know for sure\n        //      how long that sequence is, but we can assume it's all sent to\n        //      the input queue at once, and it's less that 16 events.\n        //      Furthermore, we can assume that if there's an Escape in those\n        //      16 events, that the escape marks the start of a new sequence.\n        //      So, we'll peek at another 15 events looking for escapes.\n        //      If we see an escape, then we'll read one less than that,\n        //      such that the escape remains the next event in the input.\n        //      From those read events, we'll aggregate chars into a single\n        //      string to send to the subsystem.\n        // If it's not an escape, send the event through one at a time.\n        //\n\n        //\n        // Read one input event.\n        //\n\n        DWORD WaitStatus = (WAIT_OBJECT_0 + 1);\n        do\n        {\n            THROW_IF_WIN32_BOOL_FALSE(readConsoleInput(ConsoleHandle, InputRecordBuffer, 1, &RecordsRead, CONSOLE_READ_NOWAIT));\n\n            if (RecordsRead == 0)\n            {\n                WaitStatus = WaitForMultipleObjects(RTL_NUMBER_OF(WaitHandles), WaitHandles, false, INFINITE);\n            }\n        } while ((WaitStatus == (WAIT_OBJECT_0 + 1)) && (RecordsRead == 0));\n\n        //\n        // Stop processing if the exit event has been signaled.\n        //\n\n        if (WaitStatus != (WAIT_OBJECT_0 + 1))\n        {\n            WI_ASSERT(WaitStatus == WAIT_OBJECT_0);\n\n            break;\n        }\n\n        WI_ASSERT(RecordsRead == 1);\n\n        //\n        // Don't read additional records if the first entry is a window size\n        // event, or a repeated character. Handle those events on their own.\n        //\n\n        DWORD RecordsPeeked = 0;\n        if ((InputRecordBuffer[0].EventType != WINDOW_BUFFER_SIZE_EVENT) &&\n            ((InputRecordBuffer[0].EventType != KEY_EVENT) || (InputRecordBuffer[0].Event.KeyEvent.wRepeatCount < 2)))\n        {\n            //\n            // Read additional input records into the buffer if available.\n            //\n\n            THROW_IF_WIN32_BOOL_FALSE(PeekConsoleInputW(ConsoleHandle, InputRecordPeek, (RTL_NUMBER_OF(InputRecordBuffer) - 1), &RecordsPeeked));\n        }\n\n        //\n        // Iterate over peeked records [1, RecordsPeeked].\n        //\n\n        DWORD AdditionalRecordsToRead = 0;\n        WCHAR NextCharacter;\n        for (DWORD RecordIndex = 1; RecordIndex <= RecordsPeeked; RecordIndex++)\n        {\n            if (GetNextCharacter(&InputRecordBuffer[RecordIndex], &NextCharacter) != FALSE)\n            {\n                KeyEvent = &InputRecordBuffer[RecordIndex].Event.KeyEvent;\n                if (NextCharacter == TTY_ESCAPE_CHARACTER)\n                {\n                    //\n                    // CurrentRecord is an escape event. We will start here\n                    // on the next input loop.\n                    //\n\n                    break;\n                }\n                else if (KeyEvent->wRepeatCount > 1)\n                {\n                    //\n                    // Repeated keys are handled on their own. Start with this\n                    // key on the next input loop.\n                    //\n\n                    break;\n                }\n                else if (IS_HIGH_SURROGATE(NextCharacter) && (RecordIndex >= (RecordsPeeked - 1)))\n                {\n                    //\n                    // If there is not enough room for the second character of\n                    // a surrogate pair, start with this character on the next\n                    // input loop.\n                    //\n                    // N.B. The test is for at least two remaining records\n                    //      because typically a surrogate pair will be entered\n                    //      via copy/paste, which will appear as an input\n                    //      record with alt-down, alt-up and character. So to\n                    //      include the next character of the surrogate pair it\n                    //      is likely that the alt-up record will need to be\n                    //      read first.\n                    //\n\n                    break;\n                }\n            }\n            else if (InputRecordBuffer[RecordIndex].EventType == WINDOW_BUFFER_SIZE_EVENT)\n            {\n                //\n                // A window size event is handled on its own.\n                //\n\n                break;\n            }\n\n            //\n            // Process the additional input record.\n            //\n\n            AdditionalRecordsToRead += 1;\n        }\n\n        if (AdditionalRecordsToRead > 0)\n        {\n            THROW_IF_WIN32_BOOL_FALSE(readConsoleInput(ConsoleHandle, InputRecordPeek, AdditionalRecordsToRead, &RecordsRead, CONSOLE_READ_NOWAIT));\n\n            if (RecordsRead == 0)\n            {\n                //\n                // This would be an unexpected case. We've already peeked to see\n                // that there are AdditionalRecordsToRead # of records in the\n                // input that need reading, yet we didn't get them when we read.\n                // In this case, move along and finish this input event.\n                //\n\n                break;\n            }\n\n            //\n            // We already had one input record in the buffer before reading\n            // additional, So account for that one too\n            //\n\n            RecordsRead += 1;\n        }\n\n        //\n        // Process each input event. Keydowns will get aggregated into\n        // Utf8String before getting injected into the subsystem.\n        //\n\n        WCHAR Utf16String[TTY_INPUT_EVENT_BUFFER_SIZE];\n        ULONG Utf16StringSize = 0;\n        COORD WindowSize{};\n        LX_INIT_WINDOW_SIZE_CHANGED WindowSizeMessage{};\n        for (DWORD RecordIndex = 0; RecordIndex < RecordsRead; RecordIndex++)\n        {\n            INPUT_RECORD* CurrentInputRecord = &(InputRecordBuffer[RecordIndex]);\n            switch (CurrentInputRecord->EventType)\n            {\n            case KEY_EVENT:\n\n                //\n                // Filter out key up events unless they are from an <Alt> key.\n                // Key up with an <Alt> key could contain a Unicode character\n                // pasted from the clipboard and converted to an <Alt>+<Numpad> sequence.\n                //\n\n                KeyEvent = &CurrentInputRecord->Event.KeyEvent;\n                if ((KeyEvent->bKeyDown == FALSE) && (KeyEvent->wVirtualKeyCode != TTY_ALT_NUMPAD_VK_MENU))\n                {\n                    break;\n                }\n\n                //\n                // Filter out key presses that are not actionable, such as just\n                // pressing <Ctrl>, <Alt>, <Shift> etc. These key presses return\n                // the character of null but will have a valid scan code off the\n                // keyboard. Certain other key sequences such as Ctrl+A,\n                // Ctrl+<space>, and Ctrl+@ will also return the character null\n                // but have no scan code.\n                // <Alt> + <NumPad> sequences will show an <Alt> but will have\n                // a scancode and character specified, so they should be actionable.\n                //\n\n                if (IsActionableKey(KeyEvent) == FALSE)\n                {\n                    break;\n                }\n\n                Utf16String[Utf16StringSize] = KeyEvent->uChar.UnicodeChar;\n                Utf16StringSize += 1;\n                break;\n\n            case WINDOW_BUFFER_SIZE_EVENT:\n\n                //\n                // Query the window size and send an update message via the\n                // control channel.\n                //\n                if (ControlChannel)\n                {\n                    WindowSize = Io->GetWindowSize();\n                    WindowSizeMessage.Header.MessageType = LxInitMessageWindowSizeChanged;\n                    WindowSizeMessage.Header.MessageSize = sizeof(WindowSizeMessage);\n                    WindowSizeMessage.Columns = WindowSize.X;\n                    WindowSizeMessage.Rows = WindowSize.Y;\n\n                    try\n                    {\n                        ControlChannel->SendMessage(WindowSizeMessage);\n                    }\n                    CATCH_LOG();\n                }\n\n                break;\n            }\n        }\n\n        CHAR Utf8String[TTY_UTF8_TRANSLATION_BUFFER_SIZE];\n        DWORD Utf8StringSize = 0;\n        if (Utf16StringSize > 0)\n        {\n            //\n            // Windows uses UTF-16LE encoding, Linux uses UTF-8 by default.\n            // Convert each UTF-16LE character into the proper UTF-8 byte\n            // sequence equivalent.\n            //\n\n            THROW_LAST_ERROR_IF(\n                (Utf8StringSize = WideCharToMultiByte(\n                     CP_UTF8, 0, Utf16String, Utf16StringSize, Utf8String, sizeof(Utf8String), nullptr, nullptr)) == 0);\n        }\n\n        //\n        // Send the input bytes to the terminal.\n        //\n\n        DWORD BytesWritten = 0;\n        const auto Utf8Span = gslhelpers::struct_as_bytes(Utf8String).first(Utf8StringSize);\n        if ((RecordsRead == 1) && (InputRecordBuffer[0].EventType == KEY_EVENT) && (InputRecordBuffer[0].Event.KeyEvent.wRepeatCount > 1))\n        {\n            WI_ASSERT(Utf16StringSize == 1);\n\n            //\n            // Handle repeated characters. They aren't part of an input\n            // sequence, so there's only one event that's generating characters.\n            //\n\n            WORD RepeatIndex;\n            for (RepeatIndex = 0; RepeatIndex < InputRecordBuffer[0].Event.KeyEvent.wRepeatCount; RepeatIndex += 1)\n            {\n                BytesWritten = wsl::windows::common::relay::InterruptableWrite(OutputHandle, Utf8Span, ExitHandles, &Overlapped);\n                if (BytesWritten == 0)\n                {\n                    break;\n                }\n            }\n        }\n        else if (Utf8StringSize > 0)\n        {\n            BytesWritten = wsl::windows::common::relay::InterruptableWrite(OutputHandle, Utf8Span, ExitHandles, &Overlapped);\n            if (BytesWritten == 0)\n            {\n                break;\n            }\n        }\n    }\n\n    return;\n}\nCATCH_LOG()\n\nwsl::windows::common::SvcComm::SvcComm()\n{\n    // Ensure that the OS has support for running lifted WSL. This interface is always present on Windows 11 and later.\n    //\n    // Prior to Windows 11 there are two cases where the IWslSupport interface may not be present:\n    //     1. The machine has not installed the DCR that contains support for lifted WSL.\n    //     2. The WSL optional component which contains the interface is not installed.\n    if (!wsl::windows::common::helpers::IsWindows11OrAbove() && !wsl::windows::common::helpers::IsWslSupportInterfacePresent())\n    {\n        THROW_HR(wsl::windows::common::helpers::IsWslOptionalComponentPresent() ? WSL_E_OS_NOT_SUPPORTED : WSL_E_WSL_OPTIONAL_COMPONENT_REQUIRED);\n    }\n\n    auto retry_pred = []() {\n        const auto errorCode = wil::ResultFromCaughtException();\n\n        return errorCode == HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) || errorCode == REGDB_E_CLASSNOTREG;\n    };\n\n    wsl::shared::retry::RetryWithTimeout<void>(\n        [this]() { m_userSession = wil::CoCreateInstance<LxssUserSession, ILxssUserSession>(CLSCTX_LOCAL_SERVER); },\n        std::chrono::seconds(1),\n        std::chrono::minutes(1),\n        retry_pred);\n\n    // Query client security interface.\n    auto clientSecurity = m_userSession.query<IClientSecurity>();\n\n    // Get the current proxy blanket settings.\n    DWORD authnSvc, authzSvc, authnLvl, capabilities;\n    THROW_IF_FAILED(clientSecurity->QueryBlanket(m_userSession.get(), &authnSvc, &authzSvc, NULL, &authnLvl, NULL, NULL, &capabilities));\n\n    // Make sure that dynamic cloaking is used.\n    WI_ClearFlag(capabilities, EOAC_STATIC_CLOAKING);\n    WI_SetFlag(capabilities, EOAC_DYNAMIC_CLOAKING);\n    THROW_IF_FAILED(clientSecurity->SetBlanket(\n        m_userSession.get(), authnSvc, authzSvc, NULL, authnLvl, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, capabilities));\n}\n\nwsl::windows::common::SvcComm::~SvcComm()\n{\n}\n\nvoid wsl::windows::common::SvcComm::ConfigureDistribution(_In_opt_ LPCGUID DistroGuid, _In_ ULONG DefaultUid, _In_ ULONG Flags) const\n{\n    ClientExecutionContext context;\n    THROW_IF_FAILED(m_userSession->ConfigureDistribution(DistroGuid, DefaultUid, Flags, context.OutError()));\n}\n\nvoid wsl::windows::common::SvcComm::CreateInstance(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags)\n{\n    ClientExecutionContext context;\n    THROW_IF_FAILED(CreateInstanceNoThrow(DistroGuid, Flags, context.OutError()));\n}\n\nHRESULT\nwsl::windows::common::SvcComm::CreateInstanceNoThrow(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error) const\n{\n    return m_userSession->CreateInstance(DistroGuid, Flags, Error);\n}\n\nstd::vector<LXSS_ENUMERATE_INFO> wsl::windows::common::SvcComm::EnumerateDistributions() const\n{\n    ExecutionContext enumerateDistroContext(Context::EnumerateDistros);\n    ClientExecutionContext context;\n\n    wil::unique_cotaskmem_array_ptr<LXSS_ENUMERATE_INFO> Distributions;\n    THROW_IF_FAILED(m_userSession->EnumerateDistributions(Distributions.size_address<ULONG>(), &Distributions, context.OutError()));\n\n    std::vector<LXSS_ENUMERATE_INFO> DistributionList;\n    for (size_t Index = 0; Index < Distributions.size(); Index += 1)\n    {\n        DistributionList.push_back(Distributions[Index]);\n    }\n\n    return DistributionList;\n}\n\nHRESULT\nwsl::windows::common::SvcComm::ExportDistribution(_In_opt_ LPCGUID DistroGuid, _In_ HANDLE FileHandle, _In_ ULONG Flags) const\n{\n    ClientExecutionContext context;\n\n    // Create a pipe for reading errors from bsdtar.\n    wil::unique_handle stdErrRead;\n    wil::unique_handle stdErrWrite;\n    THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&stdErrRead, &stdErrWrite, nullptr, 0));\n\n    relay::ScopedRelay stdErrRelay(\n        std::move(stdErrRead), GetStdHandle(STD_ERROR_HANDLE), LX_RELAY_BUFFER_SIZE, [&stdErrWrite]() { stdErrWrite.reset(); });\n\n    HRESULT result = E_FAIL;\n    if (GetFileType(FileHandle) != FILE_TYPE_PIPE)\n    {\n        result = m_userSession->ExportDistribution(DistroGuid, FileHandle, stdErrWrite.get(), Flags, context.OutError());\n    }\n    else\n    {\n        result = m_userSession->ExportDistributionPipe(DistroGuid, FileHandle, stdErrWrite.get(), Flags, context.OutError());\n    }\n\n    stdErrWrite.reset();\n    stdErrRelay.Sync();\n\n    RETURN_HR(result);\n}\n\nvoid wsl::windows::common::SvcComm::GetDistributionConfiguration(\n    _In_opt_ LPCGUID DistroGuid,\n    _Out_ LPWSTR* Name,\n    _Out_ ULONG* Version,\n    _Out_ ULONG* DefaultUid,\n    _Out_ ULONG* DefaultEnvironmentCount,\n    _Out_ LPSTR** DefaultEnvironment,\n    _Out_ ULONG* Flags) const\n{\n    ClientExecutionContext context;\n\n    THROW_IF_FAILED(m_userSession->GetDistributionConfiguration(\n        DistroGuid, Name, Version, DefaultUid, DefaultEnvironmentCount, DefaultEnvironment, Flags, context.OutError()));\n}\n\nDWORD\nwsl::windows::common::SvcComm::LaunchProcess(\n    _In_opt_ LPCGUID DistroGuid,\n    _In_opt_ LPCWSTR Filename,\n    _In_ int Argc,\n    _In_reads_(Argc) LPCWSTR Argv[],\n    _In_ ULONG LaunchFlags,\n    _In_opt_ PCWSTR Username,\n    _In_opt_ PCWSTR CurrentWorkingDirectory,\n    _In_ DWORD Timeout) const\n{\n    ClientExecutionContext context;\n\n    //\n    // Parse the input arguments.\n    //\n\n    DWORD ExitCode = 1;\n    CreateProcessArguments Parsed(Filename, Argc, Argv, LaunchFlags, CurrentWorkingDirectory);\n\n    //\n    // Create the process.\n    //\n\n    ConsoleState Io;\n    COORD WindowSize = Io.GetWindowSize();\n    ULONG Flags = LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE;\n    if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_USE_SYSTEM_DISTRO))\n    {\n        WI_SetFlag(Flags, LXSS_CREATE_INSTANCE_FLAGS_USE_SYSTEM_DISTRO);\n    }\n\n    if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_SHELL_LOGIN))\n    {\n        WI_SetFlag(Flags, LXSS_CREATE_INSTANCE_FLAGS_SHELL_LOGIN);\n    }\n\n    // This method is also used by Terminal.\n    // See: https://github.com/microsoft/terminal/blob/ec434e3fba2a6ef254123e31f5257c25b04f2547/src/tools/ConsoleBench/conhost.cpp#L159-L164\n    HANDLE console = NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->Reserved2[0];\n\n    LXSS_STD_HANDLES StdHandles{};\n    const HANDLE InputHandle = GetStdHandle(STD_INPUT_HANDLE);\n    const bool IsConsoleInput = wsl::windows::common::wslutil::IsConsoleHandle(InputHandle);\n    StdHandles.StdIn.HandleType = IsConsoleInput ? LxssHandleConsole : LxssHandleInput;\n    StdHandles.StdIn.Handle = IsConsoleInput ? LXSS_HANDLE_USE_CONSOLE : HandleToUlong(InputHandle);\n    const HANDLE OutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);\n    const bool IsConsoleOutput = wsl::windows::common::wslutil::IsConsoleHandle(OutputHandle);\n    StdHandles.StdOut.HandleType = IsConsoleOutput ? LxssHandleConsole : LxssHandleOutput;\n    StdHandles.StdOut.Handle = IsConsoleOutput ? LXSS_HANDLE_USE_CONSOLE : HandleToUlong(OutputHandle);\n    const HANDLE ErrorHandle = GetStdHandle(STD_ERROR_HANDLE);\n    const bool IsConsoleError = wsl::windows::common::wslutil::IsConsoleHandle(ErrorHandle);\n    StdHandles.StdErr.HandleType = IsConsoleError ? LxssHandleConsole : LxssHandleOutput;\n    StdHandles.StdErr.Handle = IsConsoleError ? LXSS_HANDLE_USE_CONSOLE : HandleToUlong(ErrorHandle);\n\n    GUID DistributionId;\n    GUID InstanceId;\n    wil::unique_handle ProcessHandle;\n    wil::unique_handle ServerPortHandle;\n    wil::unique_handle StdInSocket;\n    wil::unique_handle StdOutSocket;\n    wil::unique_handle StdErrSocket;\n    wil::unique_handle ControlSocket;\n    wil::unique_handle InteropSocket;\n\n    if (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_CHAR)\n    {\n        context.EnableInteractiveWarnings();\n    }\n\n    THROW_IF_FAILED(m_userSession->CreateLxProcess(\n        DistroGuid,\n        Parsed.FilenameString.empty() ? nullptr : Parsed.FilenameString.c_str(),\n        Argc,\n        Parsed.CommandLineStrings.data(),\n        Parsed.CurrentWorkingDirectory.empty() ? nullptr : Parsed.CurrentWorkingDirectory.c_str(),\n        Parsed.NtPath.empty() ? nullptr : Parsed.NtPath.c_str(),\n        Parsed.NtEnvironment.get(),\n        static_cast<ULONG>(Parsed.NtEnvironmentLength),\n        Username,\n        WindowSize.X,\n        WindowSize.Y,\n        HandleToUlong(console),\n        &StdHandles,\n        Flags,\n        &DistributionId,\n        &InstanceId,\n        &ProcessHandle,\n        &ServerPortHandle,\n        &StdInSocket,\n        &StdOutSocket,\n        &StdErrSocket,\n        &ControlSocket,\n        &InteropSocket,\n        context.OutError()));\n\n    context.FlushWarnings();\n\n    WI_ASSERT((!ARGUMENT_PRESENT(DistroGuid)) || (IsEqualGUID(*DistroGuid, DistributionId)));\n\n    //\n    // If a process handle was returned, this is a WSL process. Otherwise, the\n    // process is running in a utility VM.\n    //\n\n    if (ProcessHandle)\n    {\n        //\n        // Mark the process handle as uninheritable.\n        //\n\n        helpers::SetHandleInheritable(ProcessHandle.get(), false);\n\n        //\n        // If the caller requested interop and a server port was created, start\n        // the interop worker thread and background wslhost process.\n        //\n\n        if ((WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_ENABLE_INTEROP)) && (ServerPortHandle))\n        {\n            try\n            {\n                InitializeInterop(ServerPortHandle.get(), DistributionId);\n            }\n            CATCH_LOG()\n        }\n\n        ServerPortHandle.reset();\n\n        //\n        // Wait for the launched process to exit and return the process exit\n        // code.\n        //\n\n        LXBUS_IPC_LX_PROCESS_WAIT_FOR_TERMINATION_PARAMETERS Parameters{};\n        Parameters.Input.TimeoutMs = Timeout;\n        THROW_IF_NTSTATUS_FAILED(LxBusClientWaitForLxProcess(ProcessHandle.get(), &Parameters));\n\n        if (LXSS_WIFEXITED(Parameters.Output.ExitStatus))\n        {\n            Parameters.Output.ExitStatus = LXSS_WEXITSTATUS(Parameters.Output.ExitStatus);\n        }\n\n        ExitCode = Parameters.Output.ExitStatus;\n    }\n    else\n    {\n        //\n        // Create stdin, stdout and stderr worker threads.\n        //\n\n        std::thread StdOutWorker;\n        std::thread StdErrWorker;\n        auto ExitEvent = wil::unique_event(wil::EventOptions::ManualReset);\n        auto outWorkerExit = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&StdOutWorker, &StdErrWorker, &ExitEvent] {\n            ExitEvent.SetEvent();\n            if (StdOutWorker.joinable())\n            {\n                StdOutWorker.join();\n            }\n\n            if (StdErrWorker.joinable())\n            {\n                StdErrWorker.join();\n            }\n        });\n\n        // This channel needs to be a shared_ptr because closing it will cause the linux relay to exit so we should keep it open\n        // even after the stdin thread exits, but we can't keep give a simple reference to that thread because the main thread\n        // might return from this method before the stdin relay thread does.\n\n        auto ControlChannel = std::make_shared<wsl::shared::SocketChannel>(\n            wil::unique_socket{reinterpret_cast<SOCKET>(ControlSocket.release())}, \"Control\");\n\n        auto StdIn = GetStdHandle(STD_INPUT_HANDLE);\n        if (IS_VALID_HANDLE(StdIn))\n        {\n            std::thread([StdIn, StdInSocket = std::move(StdInSocket), ControlChannel = ControlChannel, ExitHandle = ExitEvent.get(), Io = &Io]() mutable {\n                RelayStandardInput(StdIn, StdInSocket.get(), ControlChannel, ExitHandle, Io);\n            }).detach();\n        }\n\n        auto StdOut = GetStdHandle(STD_OUTPUT_HANDLE);\n        StdOutWorker = relay::CreateThread(std::move(StdOutSocket), IS_VALID_HANDLE(StdOut) ? StdOut : nullptr);\n        auto StdErr = GetStdHandle(STD_ERROR_HANDLE);\n        StdErrWorker = relay::CreateThread(std::move(StdErrSocket), IS_VALID_HANDLE(StdErr) ? StdErr : nullptr);\n\n        //\n        // Spawn wslhost to handle interop requests from processes that have\n        // been backgrounded and their console window has been closed.\n        //\n\n        if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_ENABLE_INTEROP))\n        {\n            try\n            {\n                SpawnWslHost(InteropSocket.get(), DistributionId, &InstanceId);\n            }\n            CATCH_LOG()\n        }\n\n        //\n        // Begin reading messages from the utility vm.\n        //\n\n        wsl::shared::SocketChannel InteropChannel{\n            wil::unique_socket{reinterpret_cast<SOCKET>(InteropSocket.release())}, \"Interop\"};\n        ExitCode = interop::VmModeWorkerThread(InteropChannel, InstanceId);\n    }\n\n    return ExitCode;\n}\n\nGUID wsl::windows::common::SvcComm::GetDefaultDistribution() const\n{\n    ClientExecutionContext context;\n    GUID DistroId;\n    THROW_IF_FAILED(m_userSession->GetDefaultDistribution(context.OutError(), &DistroId));\n\n    return DistroId;\n}\n\nULONG\nwsl::windows::common::SvcComm::GetDistributionFlags(_In_opt_ LPCGUID DistroGuid) const\n{\n    ClientExecutionContext context;\n\n    wil::unique_cotaskmem_string Name;\n    ULONG Version;\n    ULONG Uid;\n    wil::unique_cotaskmem_array_ptr<wil::unique_cotaskmem_ansistring> Environment;\n    ULONG Flags;\n    THROW_IF_FAILED(m_userSession->GetDistributionConfiguration(\n        DistroGuid, &Name, &Version, &Uid, Environment.size_address<ULONG>(), &Environment, &Flags, context.OutError()));\n\n    return Flags;\n}\n\nGUID wsl::windows::common::SvcComm::GetDistributionId(_In_ LPCWSTR Name, _In_ ULONG Flags) const\n{\n    ClientExecutionContext context;\n\n    GUID DistroId;\n    THROW_IF_FAILED(m_userSession->GetDistributionId(Name, Flags, context.OutError(), &DistroId));\n\n    return DistroId;\n}\n\nGUID wsl::windows::common::SvcComm::ImportDistributionInplace(_In_ LPCWSTR Name, _In_ LPCWSTR VhdPath) const\n{\n    ClientExecutionContext context;\n\n    GUID DistroGuid;\n    THROW_IF_FAILED(m_userSession->ImportDistributionInplace(Name, VhdPath, context.OutError(), &DistroGuid));\n\n    return DistroGuid;\n}\n\nvoid wsl::windows::common::SvcComm::MoveDistribution(_In_ const GUID& DistroGuid, _In_ LPCWSTR Location) const\n{\n    ClientExecutionContext context;\n\n    THROW_IF_FAILED(m_userSession->MoveDistribution(&DistroGuid, Location, context.OutError()));\n}\n\nstd::pair<GUID, wil::unique_cotaskmem_string> wsl::windows::common::SvcComm::RegisterDistribution(\n    _In_ LPCWSTR Name,\n    _In_ ULONG Version,\n    _In_ HANDLE FileHandle,\n    _In_ LPCWSTR TargetDirectory,\n    _In_ ULONG Flags,\n    _In_ std::optional<uint64_t> VhdSize,\n    _In_opt_ LPCWSTR PackageFamilyName) const\n{\n    ClientExecutionContext context;\n\n    // Create a pipe for reading errors from bsdtar.\n    wil::unique_handle stdErrRead;\n    wil::unique_handle stdErrWrite;\n    THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&stdErrRead, &stdErrWrite, nullptr, 0));\n\n    relay::ScopedRelay stdErrRelay(\n        std::move(stdErrRead), GetStdHandle(STD_ERROR_HANDLE), LX_RELAY_BUFFER_SIZE, [&stdErrWrite]() { stdErrWrite.reset(); });\n\n    GUID DistroGuid{};\n    HRESULT Result = E_FAIL;\n    wil::unique_cotaskmem_string installedName;\n    if (GetFileType(FileHandle) != FILE_TYPE_PIPE)\n    {\n        Result = m_userSession->RegisterDistribution(\n            Name,\n            Version,\n            FileHandle,\n            stdErrWrite.get(),\n            TargetDirectory,\n            Flags,\n            VhdSize.value_or(0),\n            PackageFamilyName,\n            &installedName,\n            context.OutError(),\n            &DistroGuid);\n    }\n    else\n    {\n        Result = m_userSession->RegisterDistributionPipe(\n            Name,\n            Version,\n            FileHandle,\n            stdErrWrite.get(),\n            TargetDirectory,\n            Flags,\n            VhdSize.value_or(0),\n            PackageFamilyName,\n            &installedName,\n            context.OutError(),\n            &DistroGuid);\n    }\n\n    stdErrWrite.reset();\n    stdErrRelay.Sync();\n\n    THROW_IF_FAILED(Result);\n\n    return std::make_pair(DistroGuid, std::move(installedName));\n}\n\nvoid wsl::windows::common::SvcComm::SetDefaultDistribution(_In_ LPCGUID DistroGuid) const\n{\n    ClientExecutionContext context;\n    THROW_IF_FAILED(m_userSession->SetDefaultDistribution(DistroGuid, context.OutError()));\n}\n\nHRESULT\nwsl::windows::common::SvcComm::SetSparse(_In_ LPCGUID DistroGuid, _In_ BOOL Sparse, _In_ BOOL AllowUnsafe) const\n{\n    ClientExecutionContext context;\n\n    RETURN_HR(m_userSession->SetSparse(DistroGuid, Sparse, AllowUnsafe, context.OutError()));\n}\n\nHRESULT\nwsl::windows::common::SvcComm::ResizeDistribution(_In_ LPCGUID DistroGuid, _In_ ULONG64 NewSize) const\n{\n    ClientExecutionContext context;\n\n    wil::unique_handle outputRead;\n    wil::unique_handle outputWrite;\n    THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&outputRead, &outputWrite, nullptr, 0));\n\n    relay::ScopedRelay outputRelay(\n        std::move(outputRead), GetStdHandle(STD_ERROR_HANDLE), LX_RELAY_BUFFER_SIZE, [&outputWrite]() { outputWrite.reset(); });\n\n    const auto result = m_userSession->ResizeDistribution(DistroGuid, outputWrite.get(), NewSize, context.OutError());\n\n    outputWrite.reset();\n    outputRelay.Sync();\n\n    RETURN_HR(result);\n}\n\nHRESULT\nwsl::windows::common::SvcComm::SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Version) const\n{\n    ClientExecutionContext context;\n\n    // Create a pipe for reading errors from bsdtar.\n    wil::unique_handle stdErrRead;\n    wil::unique_handle stdErrWrite;\n    THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&stdErrRead, &stdErrWrite, nullptr, 0));\n\n    relay::ScopedRelay stdErrRelay(\n        std::move(stdErrRead), GetStdHandle(STD_ERROR_HANDLE), LX_RELAY_BUFFER_SIZE, [&stdErrWrite]() { stdErrWrite.reset(); });\n\n    RETURN_HR(m_userSession->SetVersion(DistroGuid, Version, stdErrWrite.get(), context.OutError()));\n}\n\nHRESULT\nwsl::windows::common::SvcComm::AttachDisk(_In_ LPCWSTR Disk, _In_ ULONG Flags) const\n{\n    ClientExecutionContext context;\n\n    RETURN_HR(m_userSession->AttachDisk(Disk, Flags, context.OutError()));\n}\n\nstd::pair<int, int> wsl::windows::common::SvcComm::DetachDisk(_In_opt_ LPCWSTR Disk) const\n{\n    ClientExecutionContext context;\n\n    int Result = -1;\n    int Step = 0;\n    THROW_IF_FAILED(m_userSession->DetachDisk(Disk, &Result, &Step, context.OutError()));\n\n    return std::make_pair(Result, Step);\n}\n\nwsl::windows::common::SvcComm::MountResult wsl::windows::common::SvcComm::MountDisk(\n    _In_ LPCWSTR Disk, _In_ ULONG Flags, _In_ ULONG PartitionIndex, _In_opt_ LPCWSTR Name, _In_opt_ LPCWSTR Type, _In_opt_ LPCWSTR Options) const\n{\n    ClientExecutionContext context;\n\n    MountResult Result;\n    THROW_IF_FAILED(m_userSession->MountDisk(\n        Disk, Flags, PartitionIndex, Name, Type, Options, &Result.Result, &Result.Step, &Result.MountName, context.OutError()));\n\n    return Result;\n}\n\nvoid wsl::windows::common::SvcComm::Shutdown(_In_ bool Force) const\n{\n    THROW_IF_FAILED(m_userSession->Shutdown(Force));\n}\n\nvoid wsl::windows::common::SvcComm::TerminateInstance(_In_opt_ LPCGUID DistroGuid) const\n{\n    ClientExecutionContext context;\n\n    //\n    // If there is an instance running, terminate it.\n    //\n\n    THROW_IF_FAILED(m_userSession->TerminateDistribution(DistroGuid, context.OutError()));\n}\n\nvoid wsl::windows::common::SvcComm::UnregisterDistribution(_In_ LPCGUID DistroGuid) const\n{\n    ClientExecutionContext context;\n    THROW_IF_FAILED(m_userSession->UnregisterDistribution(DistroGuid, context.OutError()));\n}\n"
  },
  {
    "path": "src/windows/common/svccomm.hpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    svccomm.hpp\n\nAbstract:\n\n    This file contains function declarations for the SvcComm helper class.\n\n--*/\n\n#pragma once\n\n#include <vector>\n#include <memory>\n#include \"helpers.hpp\"\n#include \"SocketChannel.h\"\n\nnamespace wsl::windows::common {\n\nvoid RelayStandardInput(HANDLE ConsoleHandle, HANDLE OutputHandle, const std::shared_ptr<wsl::shared::SocketChannel>& ControlChannel, HANDLE ExitEvent, ConsoleState* Io);\n\nclass SvcComm\n{\npublic:\n    struct MountResult\n    {\n        int Result = -1;\n        int Step = 0;\n        wil::unique_cotaskmem_string MountName;\n    };\n\n    SvcComm();\n    ~SvcComm();\n\n    void ConfigureDistribution(_In_opt_ LPCGUID DistroGuid, _In_ ULONG DefaultUid, _In_ ULONG Flags) const;\n\n    void CreateInstance(_In_opt_ LPCGUID DistroGuid = nullptr, _In_ ULONG Flags = LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE);\n\n    HRESULT\n    CreateInstanceNoThrow(_In_opt_ LPCGUID DistroGuid = nullptr, _In_ ULONG Flags = LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE, LXSS_ERROR_INFO* Error = nullptr) const;\n\n    std::vector<LXSS_ENUMERATE_INFO> EnumerateDistributions() const;\n\n    HRESULT\n    ExportDistribution(_In_opt_ LPCGUID DistroGuid, _In_ HANDLE FileHandle, _In_ ULONG Flags = 0) const;\n\n    void GetDistributionConfiguration(\n        _In_opt_ LPCGUID DistroGuid,\n        _Out_ LPWSTR* Name,\n        _Out_ ULONG* Version,\n        _Out_ ULONG* DefaultUid,\n        _Out_ ULONG* DefaultEnvironmentCount,\n        _Out_ LPSTR** DefaultEnvironment,\n        _Out_ ULONG* Flags) const;\n\n    DWORD\n    LaunchProcess(\n        _In_opt_ LPCGUID DistroGuid,\n        _In_opt_ LPCWSTR Filename,\n        _In_ int Argc,\n        _In_reads_(Argc) LPCWSTR Argv[],\n        _In_ ULONG LaunchFlags = 0,\n        _In_opt_ PCWSTR Username = nullptr,\n        _In_opt_ PCWSTR CurrentWorkingDirectory = nullptr,\n        _In_ DWORD Timeout = INFINITE) const;\n\n    GUID GetDefaultDistribution() const;\n\n    ULONG\n    GetDistributionFlags(_In_opt_ LPCGUID DistroGuid = nullptr) const;\n\n    GUID GetDistributionId(_In_ LPCWSTR Name, _In_ ULONG Flags = 0) const;\n\n    GUID ImportDistributionInplace(_In_ LPCWSTR Name, _In_ LPCWSTR VhdPath) const;\n\n    MountResult MountDisk(_In_ LPCWSTR Disk, _In_ ULONG Flags, _In_ ULONG PartitionIndex, _In_opt_ LPCWSTR Name, _In_opt_ LPCWSTR Type, _In_opt_ LPCWSTR Options) const;\n\n    std::pair<GUID, wil::unique_cotaskmem_string> RegisterDistribution(\n        _In_ LPCWSTR Name,\n        _In_ ULONG Version,\n        _In_ HANDLE FileHandle,\n        _In_ LPCWSTR TargetDirectory,\n        _In_ ULONG Flags,\n        _In_ std::optional<uint64_t> VhdSize = std::nullopt,\n        _In_opt_ LPCWSTR PackageFamilyName = nullptr) const;\n\n    HRESULT\n    ResizeDistribution(_In_ LPCGUID DistroGuid, _In_ ULONG64 NewSize) const;\n\n    void SetDefaultDistribution(_In_ LPCGUID DistroGuid) const;\n\n    HRESULT\n    SetSparse(_In_ LPCGUID DistroGuid, _In_ BOOL Sparse, _In_ BOOL AllowUnsafe) const;\n\n    HRESULT\n    SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Version) const;\n\n    HRESULT\n    AttachDisk(_In_ LPCWSTR Disk, _In_ ULONG Flags) const;\n\n    std::pair<int, int> DetachDisk(_In_opt_ LPCWSTR Disk) const;\n\n    void Shutdown(_In_ bool Force) const;\n\n    void TerminateInstance(_In_opt_ LPCGUID DistroGuid = nullptr) const;\n\n    void UnregisterDistribution(_In_ LPCGUID DistroGuid) const;\n\n    void MoveDistribution(_In_ const GUID& DistroGuid, _In_ LPCWSTR Location) const;\n\nprivate:\n    wil::com_ptr<ILxssUserSession> m_userSession;\n};\n} // namespace wsl::windows::common\n"
  },
  {
    "path": "src/windows/common/wslutil.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslutil.cpp\n\nAbstract:\n\n    This file contains helper function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"wslutil.h\"\n#include \"WslPluginApi.h\"\n#include \"wslinstallerservice.h\"\n\n#include \"ConsoleProgressBar.h\"\n#include \"ExecutionContext.h\"\n#include \"MsiQuery.h\"\n\nusing winrt::Windows::Foundation::Uri;\nusing winrt::Windows::Management::Deployment::DeploymentOptions;\nusing wsl::shared::Localization;\nusing wsl::windows::common::Context;\nusing namespace wsl::windows::common::registry;\nusing namespace wsl::windows::common::wslutil;\n\nconstexpr auto c_latestReleaseUrl = L\"https://api.github.com/repos/Microsoft/WSL/releases/latest\";\nconstexpr auto c_releaseListUrl = L\"https://api.github.com/repos/Microsoft/WSL/releases\";\nconstexpr auto c_specificReleaseListUrl = L\"https://api.github.com/repos/Microsoft/WSL/releases/tags/\";\nconstexpr auto c_userAgent = L\"wsl-install\"; // required to use the GitHub API\nconstexpr auto c_pipePrefix = L\"\\\\\\\\.\\\\pipe\\\\\";\n\nnamespace {\n\n#define X(Error) {(Error), L## #Error}\n#define X_WIN32(Error) {HRESULT_FROM_WIN32(Error), L## #Error}\n\nstatic const std::map<HRESULT, LPCWSTR> g_commonErrors{\n    X(WSL_E_DEFAULT_DISTRO_NOT_FOUND),\n    X(WSL_E_DISTRO_NOT_FOUND),\n    X(WSL_E_WSL1_NOT_SUPPORTED),\n    X(WSL_E_VM_MODE_NOT_SUPPORTED),\n    X(WSL_E_TOO_MANY_DISKS_ATTACHED),\n    X(WSL_E_CONSOLE),\n    X(WSL_E_CUSTOM_KERNEL_NOT_FOUND),\n    X(WSL_E_USER_NOT_FOUND),\n    X(WSL_E_INVALID_USAGE),\n    X(WSL_E_EXPORT_FAILED),\n    X(WSL_E_IMPORT_FAILED),\n    X(WSL_E_TTY_LIMIT),\n    X(WSL_E_CUSTOM_SYSTEM_DISTRO_ERROR),\n    X(WSL_E_LOWER_INTEGRITY),\n    X(WSL_E_HIGHER_INTEGRITY),\n    X(WSL_E_FS_UPGRADE_NEEDED),\n    X(WSL_E_USER_VHD_ALREADY_ATTACHED),\n    X(WSL_E_VM_MODE_INVALID_STATE),\n    X(WSL_E_VM_MODE_MOUNT_NAME_ALREADY_EXISTS),\n    X(WSL_E_ELEVATION_NEEDED_TO_MOUNT_DISK),\n    X(WSL_E_DISK_ALREADY_ATTACHED),\n    X(WSL_E_DISK_ALREADY_MOUNTED),\n    X(WSL_E_DISK_MOUNT_FAILED),\n    X(WSL_E_DISK_UNMOUNT_FAILED),\n    X(WSL_E_WSL2_NEEDED),\n    X(WSL_E_VM_MODE_INVALID_MOUNT_NAME),\n    X(WSL_E_GUI_APPLICATIONS_DISABLED),\n    X(WSL_E_DISTRO_ONLY_AVAILABLE_FROM_STORE),\n    X(WSL_E_WSL_MOUNT_NOT_SUPPORTED),\n    X(WSL_E_WSL_OPTIONAL_COMPONENT_REQUIRED),\n    X(WSL_E_VMSWITCH_NOT_FOUND),\n    X(WSL_E_WSL_MOUNT_NOT_SUPPORTED),\n    X(WSL_E_VMSWITCH_NOT_SET),\n    X(WSL_E_INSTALL_PROCESS_FAILED),\n    X(WSL_E_OS_NOT_SUPPORTED),\n    X(WSL_E_INSTALL_COMPONENT_FAILED),\n    X(WSL_E_PLUGIN_REQUIRES_UPDATE),\n    X(WSL_E_DISK_MOUNT_DISABLED),\n    X(WSL_E_WSL1_DISABLED),\n    X(WSL_E_VIRTUAL_MACHINE_PLATFORM_REQUIRED),\n    X(WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED),\n    X(WSL_E_DISK_CORRUPTED),\n    X(WSL_E_DISTRIBUTION_NAME_NEEDED),\n    X(WSL_E_INVALID_JSON),\n    X(WSL_E_VM_CRASHED),\n    X(WSL_E_NOT_A_LINUX_DISTRO),\n    X(E_ACCESSDENIED),\n    X_WIN32(ERROR_NOT_FOUND),\n    X_WIN32(ERROR_VERSION_PARSE_ERROR),\n    X(E_INVALIDARG),\n    X_WIN32(ERROR_FILE_NOT_FOUND),\n    X(WININET_E_CANNOT_CONNECT),\n    X(WININET_E_NAME_NOT_RESOLVED),\n    X(HTTP_E_STATUS_NOT_FOUND),\n    X(HCS_E_SERVICE_NOT_AVAILABLE),\n    X_WIN32(ERROR_PATH_NOT_FOUND),\n    X(HCS_E_CONNECTION_TIMEOUT),\n    X(E_FAIL),\n    X(E_UNEXPECTED),\n    X(HCN_E_ADDR_INVALID_OR_RESERVED),\n    X_WIN32(RPC_S_CALL_FAILED),\n    X(RPC_E_DISCONNECTED),\n    X_WIN32(ERROR_PIPE_NOT_CONNECTED),\n    X_WIN32(ERROR_PIPE_BUSY),\n    X_WIN32(ERROR_UNSUPPORTED_TYPE),\n    X_WIN32(ERROR_CANCELLED),\n    X_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY),\n    X_WIN32(HCS_E_HYPERV_NOT_INSTALLED),\n    X(E_NOINTERFACE),\n    X(REGDB_E_CLASSNOTREG),\n    X(CERT_E_UNTRUSTEDROOT),\n    X(E_ABORT),\n    X_WIN32(ERROR_SERVICE_NOT_ACTIVE),\n    X_WIN32(ERROR_SHARING_VIOLATION),\n    X_WIN32(ERROR_DISK_REPAIR_DISABLED),\n    X(WSL_E_DISTRO_NOT_STOPPED),\n    X_WIN32(ERROR_UNHANDLED_EXCEPTION),\n    X(TRUST_E_NOSIGNATURE),\n    X(TRUST_E_BAD_DIGEST),\n    X(E_INVALID_PROTOCOL_FORMAT),\n    X_WIN32(ERROR_MOD_NOT_FOUND),\n    X_WIN32(ERROR_INSTALL_USEREXIT),\n    X_WIN32(ERROR_INSTALL_FAILURE),\n    X_WIN32(ERROR_SERVICE_DOES_NOT_EXIST),\n    X_WIN32(WSAENOTCONN),\n    X_WIN32(ERROR_FILE_EXISTS),\n    X_WIN32(ERROR_ALREADY_EXISTS),\n    X_WIN32(ERROR_INVALID_NAME),\n    X_WIN32(ERROR_NOT_SUPPORTED),\n    X_WIN32(ERROR_INVALID_HANDLE),\n    X_WIN32(ERROR_INVALID_DATA),\n    X(HCS_E_INVALID_JSON),\n    X_WIN32(ERROR_INVALID_SECURITY_DESCR),\n    X(VM_E_INVALID_STATE),\n    X_WIN32(STATUS_SHUTDOWN_IN_PROGRESS),\n    X_WIN32(ERROR_BAD_PATHNAME),\n    X(WININET_E_TIMEOUT)};\n\n#undef X\n\n#define X(Ctx) {Context::Ctx, L## #Ctx}\n\nstatic const std::map<Context, LPCWSTR> g_contextStrings{\n    X(Empty),\n    X(Wsl),\n    X(Wslg),\n    X(Bash),\n    X(WslConfig),\n    X(InstallDistro),\n    X(Service),\n    X(RegisterDistro),\n    X(CreateInstance),\n    X(AttachDisk),\n    X(DetachDisk),\n    X(CreateVm),\n    X(ParseConfig),\n    X(ConfigureNetworking),\n    X(ConfigureGpu),\n    X(LaunchProcess),\n    X(UpdatePackage),\n    X(ConfigureDistro),\n    X(CreateLxProcess),\n    X(EnumerateDistros),\n    X(ExportDistro),\n    X(GetDefaultDistro),\n    X(GetDistroConfiguration),\n    X(GetDistroId),\n    X(SetDefaultDistro),\n    X(SetVersion),\n    X(TerminateDistro),\n    X(UnregisterDistro),\n    X(RegisterLxBus),\n    X(MountDisk),\n    X(QueryLatestGitHubRelease),\n    X(DebugShell),\n    X(Plugin),\n    X(CallMsi),\n    X(Install),\n    X(HCS),\n    X(HNS),\n    X(ReadDistroConfig),\n    X(MoveDistro),\n    X(VerifyChecksum)};\n\n#undef X\n\nwil::unique_hlocal_string GetWinInetErrorString(HRESULT error)\n{\n    const wil::unique_hmodule library{LoadLibrary(L\"WinInet.dll\")};\n    if (!library)\n    {\n        return {};\n    }\n\n    wil::unique_hlocal_string message{};\n    LOG_HR_IF(\n        E_UNEXPECTED,\n        FormatMessageW(\n            (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK),\n            library.get(),\n            error - 0x80070000, // Mandatory to correctly resolve the error string\n            0,\n            wil::out_param_ptr<LPWSTR>(message),\n            0,\n            nullptr) == 0);\n\n    return message;\n}\n\nbool IsWinInetError(HRESULT error)\n{\n    const DWORD code = error - 0x80070000;\n\n    return code >= INTERNET_ERROR_BASE && code <= INTERNET_ERROR_LAST;\n}\n\nconstexpr uint16_t EndianSwap(uint16_t value)\n{\n    return (value & 0xFF00) >> 8 | (value & 0x00FF) << 8;\n}\n\nconstexpr uint32_t EndianSwap(uint32_t value)\n{\n    return (value & 0xFF000000) >> 24 | (value & 0x00FF0000) >> 8 | (value & 0x0000FF00) << 8 | (value & 0x000000FF) << 24;\n}\n\nconstexpr unsigned long EndianSwap(unsigned long value)\n{\n    return gsl::narrow_cast<unsigned long>(EndianSwap(gsl::narrow_cast<uint32_t>(value)));\n}\n\nconstexpr GUID EndianSwap(GUID value)\n{\n    value.Data1 = EndianSwap(value.Data1);\n    value.Data2 = EndianSwap(value.Data2);\n    value.Data3 = EndianSwap(value.Data3);\n    return value;\n}\n\n} // namespace\n\ntemplate <typename TInterface>\nwil::com_ptr<TInterface> wsl::windows::common::wslutil::CoGetCallContext()\n{\n    wil::com_ptr<TInterface> context;\n    const HRESULT hr = ::CoGetCallContext(IID_PPV_ARGS(&context));\n    THROW_HR_IF(hr, FAILED(hr) && (hr != RPC_E_CALL_COMPLETE));\n\n    return context;\n}\n\nvoid wsl::windows::common::wslutil::CoInitializeSecurity()\n{\n    THROW_IF_FAILED(CoInitializeSecurity(\n        nullptr, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_STATIC_CLOAKING, 0));\n}\n\nvoid wsl::windows::common::wslutil::ConfigureCrt()\n{\n    // _CALL_REPORTFAULT will cause the process to actually crash instead of just exiting.\n    _set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT);\n}\n\n// Copied from the terminal repository:\n// https://github.com/microsoft/terminal/blob/52262b05fa0a97d2d3a0fce0990840ffc0fa53f1/src/types/utils.cpp#L926\nGUID wsl::windows::common::wslutil::CreateV5Uuid(const GUID& namespaceGuid, const std::span<const std::byte> name)\n{\n    // v5 uuid generation happens over values in network byte order, so let's enforce that\n    auto correctEndianNamespaceGuid{EndianSwap(namespaceGuid)};\n\n    wil::unique_bcrypt_hash hash;\n    THROW_IF_NTSTATUS_FAILED(BCryptCreateHash(BCRYPT_SHA1_ALG_HANDLE, &hash, nullptr, 0, nullptr, 0, 0));\n\n    // According to N4713 8.2.1.11 [basic.lval], accessing the bytes underlying an object\n    // through unsigned char or char pointer *is defined*.\n    THROW_IF_NTSTATUS_FAILED(BCryptHashData(hash.get(), reinterpret_cast<PUCHAR>(&correctEndianNamespaceGuid), sizeof(GUID), 0));\n    // BCryptHashData is ill-specified in that it leaves off \"const\" qualification for pbInput\n    THROW_IF_NTSTATUS_FAILED(\n        BCryptHashData(hash.get(), reinterpret_cast<PUCHAR>(const_cast<std::byte*>(name.data())), gsl::narrow<ULONG>(name.size()), 0));\n\n    std::array<uint8_t, 20> buffer;\n    THROW_IF_NTSTATUS_FAILED(BCryptFinishHash(hash.get(), buffer.data(), gsl::narrow<ULONG>(buffer.size()), 0));\n\n    buffer.at(6) = (buffer.at(6) & 0x0F) | 0x50; // set the uuid version to 5\n    buffer.at(8) = (buffer.at(8) & 0x3F) | 0x80; // set the variant to 2 (RFC4122)\n\n    // We're using memcpy here pursuant to N4713 6.7.2/3 [basic.types],\n    // \"...the underlying bytes making up the object can be copied into an array\n    // of char or unsigned char...array is copied back into the object...\"\n    // std::copy may compile down to ::memcpy for these types, but using it might\n    // contravene the standard and nobody's got time for that.\n    GUID newGuid{0};\n    ::memcpy_s(&newGuid, sizeof(GUID), buffer.data(), sizeof(GUID));\n    return EndianSwap(newGuid);\n}\n\nstd::wstring wsl::windows::common::wslutil::DownloadFile(std::wstring_view Url, std::wstring Filename)\n{\n    const auto lastSlash = Url.find_last_of('/');\n    THROW_HR_IF(E_INVALIDARG, lastSlash == std::wstring::npos);\n\n    if (Filename.empty())\n    {\n        Filename = Url.substr(lastSlash + 1);\n    }\n\n    const auto downloadFolder =\n        winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(std::filesystem::temp_directory_path().wstring()).get();\n\n    const auto file =\n        downloadFolder.CreateFileAsync(Filename, winrt::Windows::Storage::CreationCollisionOption::GenerateUniqueName).get();\n    auto deleteFileOnFailure = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { file.DeleteAsync().get(); });\n\n    const auto outputStream = file.OpenAsync(winrt::Windows::Storage::FileAccessMode::ReadWrite).get().GetOutputStreamAt(0);\n\n    // By default downloaded files are cached in %appdata%/local/packages/{package-family}/AC/InetCache .\n    // Disable caching since there's no reason to keep local copies of .msixbundle files.\n    const winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter filter;\n    filter.CacheControl().WriteBehavior(winrt::Windows::Web::Http::Filters::HttpCacheWriteBehavior::NoCache);\n\n    const winrt::Windows::Web::Http::HttpClient client(filter);\n    client.DefaultRequestHeaders().Append(L\"Accept\", L\"application/octet-stream\");\n    client.DefaultRequestHeaders().Append(L\"User-Agent\", c_userAgent);\n    const auto asyncResponse = client.GetInputStreamAsync(winrt::Windows::Foundation::Uri(Url));\n\n    std::atomic<uint64_t> totalBytes;\n    wsl::windows::common::ConsoleProgressBar progressBar;\n    asyncResponse.Progress(\n        [&](const winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Windows::Storage::Streams::IInputStream, winrt::Windows::Web::Http::HttpProgress>&,\n            const winrt::Windows::Web::Http::HttpProgress& progress) {\n            if (progress.TotalBytesToReceive)\n            {\n                totalBytes = progress.TotalBytesToReceive.GetUInt64();\n            }\n        });\n\n    auto download = winrt::Windows::Storage::Streams::RandomAccessStream::CopyAsync(asyncResponse.get(), outputStream);\n\n    download.Progress([&](const auto& _, uint64_t progress) {\n        if (totalBytes != 0)\n        {\n            progressBar.Print(progress, totalBytes);\n        }\n    });\n\n    download.get();\n    progressBar.Clear();\n    deleteFileOnFailure.release();\n\n    return file.Path().c_str();\n}\n\n[[nodiscard]] HANDLE wsl::windows::common::wslutil::DuplicateHandle(_In_ HANDLE Handle, _In_ std::optional<DWORD> DesiredAccess, _In_ BOOL InheritHandle)\n{\n    HANDLE newHandle;\n    THROW_IF_WIN32_BOOL_FALSE(::DuplicateHandle(\n        GetCurrentProcess(), Handle, GetCurrentProcess(), &newHandle, DesiredAccess.value_or(0), InheritHandle, DesiredAccess.has_value() ? 0 : DUPLICATE_SAME_ACCESS));\n\n    return newHandle;\n}\n\n[[nodiscard]] HANDLE wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(_In_ HANDLE Handle)\n{\n    const wil::unique_handle caller = OpenCallingProcess(PROCESS_DUP_HANDLE);\n    THROW_LAST_ERROR_IF(!caller);\n\n    HANDLE newHandle;\n    THROW_IF_WIN32_BOOL_FALSE(::DuplicateHandle(caller.get(), Handle, GetCurrentProcess(), &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS));\n\n    return newHandle;\n}\n\n[[nodiscard]] HANDLE wsl::windows::common::wslutil::DuplicateHandleToCallingProcess(_In_ HANDLE Handle, _In_ std::optional<DWORD> DesiredAccess)\n{\n    const wil::unique_handle caller = OpenCallingProcess(PROCESS_DUP_HANDLE);\n    THROW_LAST_ERROR_IF(!caller);\n\n    HANDLE newHandle;\n    THROW_IF_WIN32_BOOL_FALSE(::DuplicateHandle(\n        GetCurrentProcess(), Handle, caller.get(), &newHandle, DesiredAccess.value_or(0), FALSE, DesiredAccess.has_value() ? 0 : DUPLICATE_SAME_ACCESS));\n\n    return newHandle;\n}\n\nvoid wsl::windows::common::wslutil::EnforceFileLimit(LPCWSTR Path, size_t Limit, const std::function<bool(const std::filesystem::directory_entry&)>& pred)\n{\n    if (Limit <= 0)\n    {\n        return;\n    }\n\n    std::map<std::filesystem::file_time_type, std::filesystem::path> files;\n    for (auto const& e : std::filesystem::directory_iterator{Path})\n    {\n        if (pred(e))\n        {\n            files.emplace(e.last_write_time(), e.path());\n        }\n    }\n\n    if (files.size() < Limit)\n    {\n        return;\n    }\n\n    auto fileToRemove = files.begin()->second.c_str();\n\n    WSL_LOG(\n        \"File limit exceeded, deleting oldest file\", TraceLoggingValue(Path, \"Folder\"), TraceLoggingValue(fileToRemove, \"File\"));\n\n    LOG_IF_WIN32_BOOL_FALSE(DeleteFile(fileToRemove));\n}\n\nstd::wstring wsl::windows::common::wslutil::ErrorCodeToString(HRESULT Error)\n{\n    std::wstringstream output;\n    const auto resultString = g_commonErrors.find(Error);\n    if (resultString != g_commonErrors.end())\n    {\n        output << resultString->second;\n    }\n    else\n    {\n        output << L\"0x\" << std::hex << Error;\n    }\n\n    return output.str();\n}\n\nwsl::windows::common::ErrorStrings wsl::windows::common::wslutil::ErrorToString(const Error& error)\n{\n    ErrorStrings errorStrings;\n\n    if (error.Message.has_value())\n    {\n        errorStrings.Message = error.Message.value();\n    }\n    else\n    {\n        errorStrings.Message = GetErrorString(error.Code);\n    }\n\n    std::wstringstream errorCode;\n    bool first = true;\n    const std::bitset<64> bits(error.Context);\n    for (auto i = 0; i < bits.size(); i++)\n    {\n        if (bits[i])\n        {\n            auto context = static_cast<Context>(1ull << i);\n            auto it = g_contextStrings.find(context);\n            if (first)\n            {\n                first = false;\n            }\n            else\n            {\n                errorCode << L\"/\";\n            }\n\n            if (it == g_contextStrings.end())\n            {\n                errorCode << L\"?(\" << context << L\")\";\n            }\n            else\n            {\n                errorCode << it->second;\n            }\n        }\n    }\n\n    errorCode << \"/\" << ErrorCodeToString(error.Code);\n\n    errorStrings.Code = errorCode.str();\n\n    return errorStrings;\n}\n\nstd::wstring wsl::windows::common::wslutil::ConstructPipePath(std::wstring_view PipeName)\n{\n    return c_pipePrefix + std::wstring(PipeName);\n}\n\nstd::filesystem::path wsl::windows::common::wslutil::GetBasePath()\n{\n    auto path = wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle());\n    THROW_IF_FAILED(::PathCchRemoveFileSpec(path.data(), path.size()));\n\n    path.resize(std::wcslen(path.c_str()));\n    return std::filesystem::path(std::move(path));\n}\n\nstd::wstring wsl::windows::common::wslutil::GetDebugShellPipeName(_In_ PSID Sid)\n{\n    return ConstructPipePath(std::wstring(L\"wsl_debugshell_\") + SidToString(Sid).get());\n}\n\nDWORD\nwsl::windows::common::wslutil::GetDefaultVersion(void)\n{\n    DWORD version = LXSS_WSL_VERSION_2;\n    const auto hr = [&version] {\n        wil::unique_hkey userKey{};\n        RETURN_IF_WIN32_ERROR(RegOpenCurrentUser(KEY_READ, &userKey));\n\n        wil::unique_hkey lxssKey{};\n        RETURN_IF_WIN32_ERROR(RegOpenKeyEx(userKey.get(), LXSS_REGISTRY_PATH, 0, KEY_READ, &lxssKey));\n\n        DWORD size = sizeof(version);\n        RETURN_IF_WIN32_ERROR(RegGetValueW(lxssKey.get(), nullptr, LXSS_WSL_DEFAULT_VERSION, RRF_RT_REG_DWORD, nullptr, &version, &size));\n\n        return S_OK;\n    }();\n\n    if (FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) && (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)))\n    {\n        THROW_IF_FAILED(hr);\n    }\n\n    return version;\n}\n\nstd::wstring wsl::windows::common::wslutil::GetErrorString(HRESULT result)\n{\n    ULONG buildNumber = 0;\n    std::wstring kbUrl;\n\n    switch (result)\n    {\n    case E_ILLEGAL_STATE_CHANGE:\n        return Localization::MessageInvalidState();\n\n    case WSL_E_USER_NOT_FOUND:\n        return Localization::MessageUserNotFound();\n\n    case WSL_E_CONSOLE:\n        return Localization::MessageInvalidConsole();\n\n    case WSL_E_LOWER_INTEGRITY:\n        return Localization::MessageLowerIntegrity();\n\n    case WSL_E_HIGHER_INTEGRITY:\n        return Localization::MessageHigherIntegrity();\n\n    case WSL_E_DEFAULT_DISTRO_NOT_FOUND:\n        return Localization::MessageNoDefaultDistro();\n\n    case HRESULT_FROM_WIN32(WSAECONNABORTED):\n    case HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS):\n        return Localization::MessageInstanceTerminated();\n\n    case WSL_E_DISTRO_NOT_FOUND:\n        return Localization::MessageDistroNotFound();\n\n    case HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS):\n        return Localization::MessageDistroNameAlreadyExists();\n\n    case WSL_E_DISTRIBUTION_NAME_NEEDED:\n        return Localization::MessageDistributionNameNeeded();\n\n    case HRESULT_FROM_WIN32(ERROR_FILE_EXISTS):\n        return Localization::MessageDistroInstallPathAlreadyExists();\n\n    case WSL_E_TOO_MANY_DISKS_ATTACHED:\n        return Localization::MessageTooManyDisks();\n\n    case WSL_E_USER_VHD_ALREADY_ATTACHED:\n        return Localization::MessageUserVhdAlreadyAttached();\n\n    case WSL_E_VM_MODE_NOT_SUPPORTED:\n        return Localization::MessageVmModeNotSupported();\n\n    case HCS_E_HYPERV_NOT_INSTALLED:\n        return Localization::MessageEnableVirtualization();\n\n    case WSL_E_VM_MODE_INVALID_STATE:\n        return Localization::MessageAlreadyRequestedVersion();\n\n    case WSL_E_WSL2_NEEDED:\n        return Localization::MessageWsl2Needed();\n\n    case WSL_E_WSL1_NOT_SUPPORTED:\n        return Localization::MessageWsl1NotSupported();\n\n    case WSL_E_DISTRO_ONLY_AVAILABLE_FROM_STORE:\n        return Localization::MessageDistroOnlyAvailableFromStore();\n\n    case WSL_E_WSL_MOUNT_NOT_SUPPORTED:\n        return Localization::MessageWslMountNotSupportedOnArm();\n\n    case WSL_E_WSL_OPTIONAL_COMPONENT_REQUIRED:\n        return Localization::MessageWslOptionalComponentRequired();\n\n    case WSL_E_EXPORT_FAILED:\n        return Localization::MessageExportFailed();\n\n    case WSL_E_IMPORT_FAILED:\n        return Localization::MessageImportFailed();\n\n    case WSL_E_DISTRO_NOT_STOPPED:\n        return Localization::MessageVhdInUse();\n\n    case WSL_E_OS_NOT_SUPPORTED:\n        buildNumber = helpers::GetWindowsVersion().BuildNumber;\n        if (buildNumber >= helpers::WindowsBuildNumbers::Cobalt)\n        {\n            kbUrl = L\"https://aka.ms/store-wsl-kb-win11\";\n        }\n        else if (buildNumber >= helpers::WindowsBuildNumbers::Iron)\n        {\n            kbUrl = L\"https://aka.ms/store-wsl-kb-winserver2022\";\n        }\n        else if (buildNumber >= helpers::WindowsBuildNumbers::Vibranium)\n        {\n            kbUrl = L\"https://aka.ms/store-wsl-kb-win10\";\n        }\n        else\n        {\n            // Don't throw from here, the caller might be in a catch block.\n            kbUrl = std::format(L\"[Unexpected build number: {}]\", buildNumber);\n        }\n\n        return Localization::MessageOsNotSupported(helpers::GetWindowsVersionString().c_str(), kbUrl.c_str());\n\n    // All the errors below this comment are not supposed to be reachable here (since there's meant to be emitted from the\n    // service). But if we somehow hit them here, it's better show something useful to the user.\n    case WSL_E_VM_MODE_MOUNT_NAME_ALREADY_EXISTS:\n        return Localization::MessageDiskMountNameAlreadyExists();\n\n    case WSL_E_VM_MODE_INVALID_MOUNT_NAME:\n        return Localization::MessageDiskMountNameInvalid();\n\n    case WSL_E_ELEVATION_NEEDED_TO_MOUNT_DISK:\n        return Localization::MessageElevationNeededToMountDisk();\n\n    case WSL_E_DISK_ALREADY_ATTACHED:\n        return Localization::MessageDiskAlreadyAttached(L\"\");\n\n    case WSL_E_DISK_ALREADY_MOUNTED:\n        return Localization::MessageDiskAlreadyMounted();\n\n    case WSL_E_CUSTOM_KERNEL_NOT_FOUND:\n        return Localization::MessageCustomKernelNotFound(helpers::GetWslConfigPath().c_str(), L\"\");\n\n    case WSL_E_CUSTOM_SYSTEM_DISTRO_ERROR:\n        return Localization::MessageCustomSystemDistroError(helpers::GetWslConfigPath().c_str());\n\n    case WSL_E_GUI_APPLICATIONS_DISABLED:\n        return Localization::GuiApplicationsDisabled(helpers::GetWslConfigPath().c_str());\n\n    case WSL_E_VMSWITCH_NOT_FOUND:\n        return Localization::MessageVmSwitchNotFound(L\"\", L\"\");\n\n    case WSL_E_VMSWITCH_NOT_SET:\n        return Localization::MessageVmSwitchNotSet();\n\n    case WSL_E_DISK_MOUNT_DISABLED:\n        return Localization::MessageWSLMountDisabled();\n\n    case WSL_E_VIRTUAL_MACHINE_PLATFORM_REQUIRED:\n        return Localization::MessageVirtualMachinePlatformNotInstalled();\n\n    case WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED:\n        return Localization::MessageLocalSystemNotSupported();\n\n    case WSL_E_DISK_CORRUPTED:\n        return Localization::MessageDiskCorrupted();\n\n    case WSL_E_NOT_A_LINUX_DISTRO:\n        return Localization::MessageInvalidDistributionTar();\n\n    case WSL_E_INVALID_USAGE:\n    {\n        const auto* context = wsl::windows::common::ExecutionContext::Current();\n        if (context == nullptr)\n        {\n            // Should be unreachable, but better fallback on something.\n            break;\n        }\n\n        if (WI_IsFlagSet(context->CurrentContext(), Context::Wsl))\n        {\n            return wsl::shared::Localization::MessageWslUsage();\n        }\n        else if (WI_IsFlagSet(context->CurrentContext(), Context::Wslg))\n        {\n            return wsl::shared::Localization::MessageWslgUsage();\n        }\n        else if (WI_IsFlagSet(context->CurrentContext(), Context::WslConfig))\n        {\n            return wsl::shared::Localization::MessageWslconfigUsage();\n        }\n    }\n    }\n\n    return GetSystemErrorString(result);\n}\n\nstd::optional<std::pair<std::wstring, GitHubReleaseAsset>> wsl::windows::common::wslutil::GetGitHubAssetFromRelease(const GitHubRelease& Release)\n{\n    auto findAsset = [&Release](LPCWSTR Suffix) {\n        for (const auto& asset : Release.assets)\n        {\n            std::wstring filename(asset.name.size(), '\\0');\n            std::transform(asset.name.begin(), asset.name.end(), filename.begin(), towlower);\n\n            if (wsl::shared::string::EndsWith<wchar_t>(filename, Suffix))\n            {\n                return std::make_optional(std::make_pair(Release.name, asset));\n            }\n        }\n\n        return std::optional<std::pair<std::wstring, GitHubReleaseAsset>>();\n    };\n\n    // Look for an MSI package first\n    auto asset = findAsset(wsl::shared::Arm64 ? L\".arm64.msi\" : L\".x64.msi\");\n    if (asset.has_value())\n    {\n        return asset.value();\n    }\n\n    // If none was found, look for an msixbundle\n    asset = findAsset(L\".msixbundle\");\n\n    return asset.value();\n}\n\nstd::pair<std::wstring, GitHubReleaseAsset> wsl::windows::common::wslutil::GetLatestGitHubRelease(bool preRelease)\n{\n    ExecutionContext context(Context::QueryLatestGitHubRelease);\n\n    auto registryKey = registry::OpenLxssMachineKey();\n    const auto url =\n        registry::ReadString(registryKey.get(), nullptr, c_githubUrlOverrideRegistryValue, preRelease ? c_releaseListUrl : c_latestReleaseUrl);\n    WSL_LOG(\"PollLatestGitHubRelease\", TraceLoggingValue(url.c_str(), \"url\"));\n\n    winrt::Windows::Web::Http::HttpClient client;\n    client.DefaultRequestHeaders().Append(L\"User-Agent\", c_userAgent);\n    const auto response = client.GetAsync(winrt::Windows::Foundation::Uri(url)).get();\n    response.EnsureSuccessStatusCode();\n\n    return GetLatestGitHubRelease(preRelease, response.Content().ReadAsStringAsync().get().c_str());\n}\n\nstd::pair<std::wstring, GitHubReleaseAsset> wsl::windows::common::wslutil::GetLatestGitHubRelease(bool preRelease, LPCWSTR releases)\n{\n    std::optional<GitHubRelease> parsed{};\n\n    if (preRelease)\n    {\n        std::optional<std::tuple<uint32_t, uint32_t, uint32_t>> highestVersion;\n        for (const auto& e : wsl::shared::FromJson<std::vector<GitHubRelease>>(releases))\n        {\n            auto version = ParseWslPackageVersion(e.name);\n            if (!highestVersion.has_value() || version > highestVersion)\n            {\n                parsed.emplace(std::move(e));\n                highestVersion = version;\n            }\n        }\n    }\n    else\n    {\n        parsed = wsl::shared::FromJson<GitHubRelease>(releases);\n    }\n\n    THROW_HR_IF(E_UNEXPECTED, !parsed.has_value());\n\n    // Find the latest release with an msix package asset\n    auto asset = GetGitHubAssetFromRelease(parsed.value());\n    THROW_HR_IF_MSG(E_UNEXPECTED, !asset.has_value(), \"No suitable WSL release found on github\");\n\n    return asset.value();\n}\n\nGitHubRelease wsl::windows::common::wslutil::GetGitHubReleaseByTag(_In_ const std::wstring& inTag)\n{\n    ExecutionContext context(Context::QueryLatestGitHubRelease);\n\n    const winrt::Windows::Web::Http::HttpClient client;\n    client.DefaultRequestHeaders().Append(L\"User-Agent\", c_userAgent);\n    const auto url = c_specificReleaseListUrl + inTag;\n    const auto response = client.GetAsync(winrt::Windows::Foundation::Uri(url)).get();\n    response.EnsureSuccessStatusCode();\n\n    const auto content = response.Content().ReadAsStringAsync().get();\n\n    return wsl::shared::FromJson<GitHubRelease>(content.c_str());\n}\n\nint wsl::windows::common::wslutil::GetLogicalProcessorCount()\n{\n    std::vector<gsl::byte> buffer;\n    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX information = nullptr;\n    DWORD length = 0;\n    while (!GetLogicalProcessorInformationEx(RelationProcessorCore, information, &length))\n    {\n        const DWORD error = GetLastError();\n        if (error == ERROR_INSUFFICIENT_BUFFER)\n        {\n            WI_ASSERT(buffer.size() < length);\n            buffer.resize(length);\n            information = reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(buffer.data());\n        }\n        else\n        {\n            THROW_WIN32_MSG(error, \"GetLogicalProcessorInformationEx\");\n        }\n    }\n\n    int processorCount = 0;\n    for (size_t offset = 0; offset < buffer.size(); offset += information->Size)\n    {\n        information = reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(&buffer[offset]);\n        for (WORD group = 0; group < information->Processor.GroupCount; group += 1)\n        {\n            processorCount += std::popcount(information->Processor.GroupMask[group].Mask);\n        }\n    }\n\n    return processorCount;\n}\n\nstd::optional<std::wstring> wsl::windows::common::wslutil::GetMsiPackagePath()\n{\n    const auto key = OpenLxssMachineKey(KEY_READ);\n\n    try\n    {\n        return ReadString(key.get(), L\"Msi\", L\"InstallLocation\", nullptr);\n    }\n    catch (...)\n    {\n        return {};\n    }\n}\n\nstd::wstring wsl::windows::common::wslutil::GetPackageFamilyName(_In_ HANDLE process)\n{\n    std::wstring packageFamilyName;\n    UINT32 length = 0;\n    switch (::GetPackageFamilyName(process, &length, nullptr))\n    {\n    case APPMODEL_ERROR_NO_PACKAGE:\n        break;\n\n    case ERROR_INSUFFICIENT_BUFFER:\n        packageFamilyName.resize(length);\n        THROW_IF_WIN32_ERROR(::GetPackageFamilyName(process, &length, packageFamilyName.data()));\n\n        break;\n\n    default:\n        THROW_LAST_ERROR_MSG(\"GetPackageFamilyName\");\n    }\n\n    return packageFamilyName;\n}\n\nstd::wstring wsl::windows::common::wslutil::GetSystemErrorString(_In_ HRESULT result)\n{\n    wil::unique_hlocal_string message{};\n\n    // Special treatment for wininet errors\n    if (IsWinInetError(result))\n    {\n        message = GetWinInetErrorString(result);\n    }\n\n    if (!message)\n    {\n        LOG_HR_IF(\n            E_UNEXPECTED,\n            FormatMessageW(\n                (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK),\n                nullptr,\n                result,\n                0,\n                wil::out_param_ptr<LPWSTR>(message),\n                0,\n                nullptr) == 0);\n    }\n\n    if (message)\n    {\n        return std::wstring{message.get()};\n    }\n\n    std::wstringstream stream;\n    stream << std::hex << result;\n    return std::wstring(L\"Error: 0x\" + stream.str());\n}\n\nstd::vector<BYTE> wsl::windows::common::wslutil::HashFile(HANDLE file, DWORD Algorithm)\n{\n    wil::unique_hcryptprov provider;\n    THROW_IF_WIN32_BOOL_FALSE(CryptAcquireContext(&provider, nullptr, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT));\n\n    wil::unique_hcrypthash hash;\n    THROW_IF_WIN32_BOOL_FALSE(CryptCreateHash(provider.get(), Algorithm, 0, 0, &hash));\n\n    constexpr auto bufferSize = 10 * 1024 * 1024; // 10 MB.\n    std::vector<char> buffer(bufferSize);\n\n    DWORD readBytes{};\n    while (true)\n    {\n        THROW_IF_WIN32_BOOL_FALSE(ReadFile(file, buffer.data(), bufferSize, &readBytes, nullptr));\n        if (readBytes == 0)\n        {\n            break;\n        }\n\n        THROW_IF_WIN32_BOOL_FALSE(CryptHashData(hash.get(), reinterpret_cast<const BYTE*>(buffer.data()), readBytes, 0));\n    }\n\n    std::vector<BYTE> fileHash(32);\n    DWORD hashSize = static_cast<DWORD>(fileHash.size());\n\n    THROW_IF_WIN32_BOOL_FALSE(CryptGetHashParam(hash.get(), HP_HASHVAL, fileHash.data(), &hashSize, 0));\n    THROW_HR_IF(E_UNEXPECTED, hashSize != fileHash.size());\n\n    return fileHash;\n}\n\nvoid wsl::windows::common::wslutil::InitializeWil()\n{\n    wil::WilInitialize_CppWinRT();\n\n    if constexpr (!wsl::shared::Debug)\n    {\n        wil::g_fResultFailFastUnknownExceptions = false;\n    }\n}\n\nbool wsl::windows::common::wslutil::IsConsoleHandle(HANDLE Handle)\n{\n    DWORD Mode;\n    return GetFileType(Handle) == FILE_TYPE_CHAR && GetConsoleMode(Handle, &Mode);\n}\n\nbool wsl::windows::common::wslutil::IsInteractiveConsole()\n{\n    return IsConsoleHandle(GetStdHandle(STD_INPUT_HANDLE));\n}\n\nbool wsl::windows::common::wslutil::IsRunningInMsix()\n{\n    UINT32 dummy{};\n    const auto result = GetCurrentPackageId(&dummy, nullptr);\n\n    if (result == NOERROR || result == ERROR_INSUFFICIENT_BUFFER)\n    {\n        return true;\n    }\n    else\n    {\n        // It's safer to return false by default since returning true incorrectly could create an infinity of wsl.exe.\n        LOG_HR_IF_MSG(E_UNEXPECTED, result != APPMODEL_ERROR_NO_PACKAGE, \"Unexpected error from: %ld\", result);\n        return false;\n    }\n}\n\nbool wsl::windows::common::wslutil::IsVhdFile(_In_ const std::filesystem::path& path)\n{\n    return wsl::windows::common::string::IsPathComponentEqual(path.extension().native(), c_vhdFileExtension) ||\n           wsl::windows::common::string::IsPathComponentEqual(path.extension().native(), c_vhdxFileExtension);\n}\n\nstd::vector<DWORD> wsl::windows::common::wslutil::ListRunningProcesses()\n{\n    std::vector<DWORD> pids(1024);\n    DWORD bytesReturned = 0;\n    while (!EnumProcesses(pids.data(), (DWORD)pids.size() * sizeof(DWORD), &bytesReturned))\n    {\n        THROW_LAST_ERROR_IF(GetLastError() != ERROR_MORE_DATA);\n\n        pids.resize(pids.size() * 2);\n    }\n\n    pids.resize(bytesReturned / sizeof(DWORD));\n\n    return pids;\n}\n\nstd::pair<wil::unique_hfile, wil::unique_hfile> wsl::windows::common::wslutil::OpenAnonymousPipe(DWORD Size, bool ReadPipeOverlapped, bool WritePipeOverlapped)\n{\n    // Default to 4096 byte buffer, just like CreatePipe().\n    if (Size == 0)\n    {\n        Size = 4096;\n    }\n\n    // Open the pipe device. Performing a relative open against this will\n    // create an anonymous pipe.\n    const wil::unique_hfile pipeDevice{\n        CreateFileW(L\"\\\\\\\\.\\\\pipe\\\\\", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr)};\n\n    THROW_LAST_ERROR_IF(!pipeDevice);\n\n    LARGE_INTEGER timeout{};\n    timeout.QuadPart = -10 * 1000 * 1000 * 120; // 120 seconds (doesn't actually matter)\n\n    UNICODE_STRING empty{};\n    OBJECT_ATTRIBUTES objectAttributes;\n    InitializeObjectAttributes(&objectAttributes, &empty, 0, pipeDevice.get(), nullptr);\n\n    IO_STATUS_BLOCK ioStatusBlock{};\n\n    wil::unique_hfile readPipe;\n    THROW_IF_NTSTATUS_FAILED(NtCreateNamedPipeFile(\n        &readPipe,\n        GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,\n        &objectAttributes,\n        &ioStatusBlock,\n        FILE_SHARE_READ | FILE_SHARE_WRITE,\n        FILE_CREATE,\n        ReadPipeOverlapped ? 0 : FILE_SYNCHRONOUS_IO_NONALERT,\n        0,\n        0,\n        0,\n        1,\n        Size,\n        Size,\n        &timeout));\n\n    InitializeObjectAttributes(&objectAttributes, &empty, 0, readPipe.get(), nullptr);\n\n    wil::unique_hfile writePipe;\n    THROW_IF_NTSTATUS_FAILED(NtOpenFile(\n        &writePipe,\n        GENERIC_WRITE | SYNCHRONIZE | FILE_READ_ATTRIBUTES,\n        &objectAttributes,\n        &ioStatusBlock,\n        FILE_SHARE_READ | FILE_SHARE_WRITE,\n        (WritePipeOverlapped ? 0 : FILE_SYNCHRONOUS_IO_NONALERT) | FILE_NON_DIRECTORY_FILE));\n\n    return {std::move(readPipe), std::move(writePipe)};\n}\n\nbool wsl::windows::common::wslutil::IsVirtualMachinePlatformInstalled()\n{\n    // Note for Windows 11 22H2 and above builds: If hyper-v is installed but VMP platform isn't, HNS and vmcompute are available\n    // but calls to HNS will fail if vfpext isn't installed.\n    return wsl::windows::common::helpers::IsServicePresent(L\"HNS\") &&\n           wsl::windows::common::helpers::IsServicePresent(L\"vmcompute\") &&\n           (helpers::GetWindowsVersion().BuildNumber < helpers::WindowsBuildNumbers::Nickel ||\n            wsl::windows::common::helpers::IsServicePresent(L\"vfpext\"));\n}\n\nwil::unique_handle wsl::windows::common::wslutil::OpenCallingProcess(_In_ DWORD access)\n{\n    wil::unique_handle caller{};\n    const auto context = wsl::windows::common::wslutil::CoGetCallContext<ICallingProcessInfo>();\n    if (context)\n    {\n        THROW_IF_FAILED(context->OpenCallerProcessHandle(access, &caller));\n    }\n\n    return caller;\n}\n\nstd::tuple<uint32_t, uint32_t, uint32_t> wsl::windows::common::wslutil::ParseWslPackageVersion(_In_ const std::wstring& Version)\n{\n    const std::wregex pattern(L\"(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+).*\");\n    std::wsmatch match;\n    if (!std::regex_match(Version, match, pattern) || match.size() != 4)\n    {\n        THROW_HR_MSG(E_UNEXPECTED, \"Failed to parse WSL package version: '%ls'\", Version.c_str());\n    }\n\n    auto get = [&](int position) { return std::stoul(match.str(position)); };\n\n    try\n    {\n        return std::make_tuple(get(1), get(2), get(3));\n    }\n    catch (const std::exception& e)\n    {\n        THROW_HR_MSG(E_UNEXPECTED, \"Failed to parse WSL package version: '%ls', %hs\", Version.c_str(), e.what());\n    }\n}\n\nvoid wsl::windows::common::wslutil::PrintSystemError(_In_ HRESULT result, _Inout_ FILE* const stream)\n{\n    fwprintf(stream, L\"%ls\\n\", GetSystemErrorString(result).c_str());\n}\n\nvoid wsl::windows::common::wslutil::PrintMessageImpl(_In_ const std::wstring& message, _In_ va_list& args, _Inout_ FILE* const stream)\n{\n    vfwprintf(stream, message.c_str(), args);\n    fputws(L\"\\n\", stream);\n}\n\nvoid wsl::windows::common::wslutil::PrintMessageImpl(_In_ const std::wstring& message, _Inout_ FILE* const stream, ...)\n{\n    va_list arguments{};\n    va_start(arguments, stream);\n    auto vaEnd = wil::scope_exit([&arguments] { va_end(arguments); });\n    PrintMessageImpl(message, arguments, stream);\n}\n\nvoid wsl::windows::common::wslutil::PrintMessage(_In_ const std::wstring& message, _Inout_ FILE* const stream)\n{\n    fwprintf(stream, L\"%ls\\n\", message.c_str());\n}\n\nvoid wsl::windows::common::wslutil::SetCrtEncoding(int Mode)\n{\n    // Configure the CRT to manipulate text as the specified mode.\n    auto setMode = [](FILE* stream, int Mode) {\n        const auto fileNumber = _fileno(stream);\n        if (fileNumber >= 0)\n        {\n            WI_VERIFY(_setmode(fileNumber, Mode) != -1);\n        }\n    };\n\n    setMode(stdin, Mode);\n    setMode(stdout, Mode);\n    setMode(stderr, Mode);\n\n    // Set the locale to the current environment's default locale.\n    WI_VERIFY(_wsetlocale(LC_ALL, L\"\") != NULL);\n}\n\nvoid wsl::windows::common::wslutil::SetThreadDescription(LPCWSTR Name)\n{\n    LOG_IF_FAILED(::SetThreadDescription(GetCurrentThread(), Name));\n}\n\nwil::unique_hlocal_string wsl::windows::common::wslutil::SidToString(_In_ PSID UserSid)\n{\n    wil::unique_hlocal_string sid;\n    THROW_LAST_ERROR_IF(!ConvertSidToStringSid(UserSid, &sid));\n\n    return sid;\n}\n\nwinrt::Windows::Management::Deployment::PackageVolume wsl::windows::common::wslutil::GetSystemVolume()\ntry\n{\n    const auto packageManager = winrt::Windows::Management::Deployment::PackageManager();\n    const auto volumes = packageManager.FindPackageVolumes();\n    for (auto volume : volumes)\n    {\n        if (volume.IsSystemVolume())\n        {\n            return volume;\n        }\n    }\n\n    WSL_LOG(\"GetSystemVolumeNotFound\");\n    return nullptr;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return nullptr;\n}"
  },
  {
    "path": "src/windows/common/wslutil.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslutil.h\n\nAbstract:\n\n    This file contains helper function declarations.\n\n--*/\n\n#pragma once\n#include <functional>\n#include <span>\n#include <type_traits>\n#include \"SubProcess.h\"\n#include <winrt/windows.management.deployment.h>\n#include \"JsonUtils.h\"\n\nnamespace wsl::windows::common {\nstruct Error;\n\nstruct ErrorStrings\n{\n    std::wstring Message;\n    std::wstring Code;\n};\n} // namespace wsl::windows::common\n\nnamespace wsl::windows::common::wslutil {\n\n// Namespace GUID used for Windows Terminal profile generation.\n// {BE9372FE-59E1-4876-BDA9-C33C8F2F1AF1}\ninline constexpr GUID WslTerminalNamespace = {0xbe9372fe, 0x59e1, 0x4876, {0xbd, 0xa9, 0xc3, 0x3c, 0x8f, 0x2f, 0x1a, 0xf1}};\n\n// Namespace GUID for automatically generated Windows Terminal profiles.\n// {2bde4a90-d05f-401c-9492-e40884ead1d8}\ninline constexpr GUID GeneratedProfilesTerminalNamespace = {0x2bde4a90, 0xd05f, 0x401c, {0x94, 0x92, 0xe4, 0x8, 0x84, 0xea, 0xd1, 0xd8}};\n\ninline auto c_msixPackageFamilyName = L\"MicrosoftCorporationII.WindowsSubsystemForLinux_8wekyb3d8bbwe\";\ninline auto c_githubUrlOverrideRegistryValue = L\"GitHubUrlOverride\";\ninline auto c_vhdFileExtension = L\".vhd\";\ninline auto c_vhdxFileExtension = L\".vhdx\";\ninline constexpr auto c_vmOwner = L\"WSL\";\n\nstruct GitHubReleaseAsset\n{\n    std::wstring url;\n    uint64_t id{};\n    std::wstring name;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GitHubReleaseAsset, url, id, name);\n};\n\nstruct GitHubRelease\n{\n    std::wstring name;\n    std::vector<GitHubReleaseAsset> assets;\n    std::wstring created_at;\n\n    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GitHubRelease, name, assets, created_at);\n};\n\ntemplate <typename T>\nvoid AssertValidPrintfArg()\n{\n    static_assert(std::is_fundamental_v<T> || std::is_same_v<wchar_t*, T> || std::is_same_v<char*, T> || std::is_same_v<HRESULT, T>);\n}\n\ntemplate <typename TInterface>\nwil::com_ptr<TInterface> CoGetCallContext();\n\nvoid CoInitializeSecurity();\n\nvoid ConfigureCrt();\n\n/// <summary>\n/// Creates a COM server with user impersonation.\n/// </summary>\ntemplate <typename Interface>\nwil::com_ptr_t<Interface> CreateComServerAsUser(_In_ REFCLSID RefClsId, _In_ HANDLE UserToken)\n{\n    auto revert = wil::impersonate_token(UserToken);\n    return wil::CoCreateInstance<Interface>(RefClsId, (CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA));\n}\n\ntemplate <typename Class, typename Interface>\nwil::com_ptr_t<Interface> CreateComServerAsUser(_In_ HANDLE UserToken)\n{\n    return CreateComServerAsUser<Interface>(__uuidof(Class), UserToken);\n}\n\nstd::wstring ConstructPipePath(_In_ std::wstring_view PipeName);\n\nGUID CreateV5Uuid(const GUID& namespaceGuid, const std::span<const std::byte> name);\n\nstd::wstring DownloadFile(std::wstring_view Url, std::wstring Filename);\n\n[[nodiscard]] HANDLE DuplicateHandle(_In_ HANDLE Handle, _In_ std::optional<DWORD> DesiredAccess = std::nullopt, _In_ BOOL InheritHandle = FALSE);\n\n[[nodiscard]] HANDLE DuplicateHandleFromCallingProcess(_In_ HANDLE Handle);\n\n[[nodiscard]] HANDLE DuplicateHandleToCallingProcess(_In_ HANDLE Handle, _In_ std::optional<DWORD> Permissions = {});\n\nvoid EnforceFileLimit(LPCWSTR Folder, size_t limit, const std::function<bool(const std::filesystem::directory_entry&)>& pred);\n\nstd::wstring ErrorCodeToString(HRESULT Error);\n\nErrorStrings ErrorToString(const Error& error);\n\nstd::filesystem::path GetBasePath();\n\nDWORD GetDefaultVersion(void);\n\nstd::wstring GetErrorString(_In_ HRESULT result);\n\nstd::optional<std::pair<std::wstring, GitHubReleaseAsset>> GetGitHubAssetFromRelease(const GitHubRelease& Release);\n\nstd::pair<std::wstring, GitHubReleaseAsset> GetLatestGitHubRelease(_In_ bool preRelease);\n\nstd::pair<std::wstring, GitHubReleaseAsset> GetLatestGitHubRelease(_In_ bool preRelease, _In_ LPCWSTR releases);\n\nGitHubRelease GetGitHubReleaseByTag(_In_ const std::wstring& Version);\n\nint GetLogicalProcessorCount();\n\nstd::optional<std::wstring> GetMsiPackagePath();\n\nstd::wstring GetPackageFamilyName(_In_ HANDLE process = GetCurrentProcess());\n\nstd::wstring GetSystemErrorString(_In_ HRESULT result);\n\nstd::wstring GetDebugShellPipeName(_In_ PSID Sid);\n\nstd::vector<BYTE> HashFile(HANDLE File, DWORD Algorithm);\n\nvoid InitializeWil();\n\nbool IsConsoleHandle(HANDLE Handle);\n\nbool IsInteractiveConsole();\n\nbool IsRunningInMsix();\n\nbool IsVhdFile(_In_ const std::filesystem::path& path);\n\nbool IsVirtualMachinePlatformInstalled();\n\nstd::vector<DWORD> ListRunningProcesses();\n\nstd::pair<wil::unique_hfile, wil::unique_hfile> OpenAnonymousPipe(DWORD Size, bool ReadPipeOverlapped, bool WritePipeOverlapped);\n\nwil::unique_handle OpenCallingProcess(_In_ DWORD access);\n\nstd::tuple<uint32_t, uint32_t, uint32_t> ParseWslPackageVersion(_In_ const std::wstring& Version);\n\nvoid PrintSystemError(_In_ HRESULT result, _Inout_ FILE* stream = stdout);\n\nvoid PrintMessageImpl(_In_ const std::wstring& message, _In_ va_list& args, _Inout_ FILE* stream = stdout);\n\nvoid PrintMessageImpl(_In_ const std::wstring& message, _Inout_ FILE* stream = stdout, ...);\n\nvoid PrintMessage(_In_ const std::wstring& message, _Inout_ FILE* stream = stdout);\n\n// This template is used to switch between the varargs and non-vararg versions of PrintMessage().\n// If no varargs are passed, then the string shouldn't be used as a printf format specifier.\ntemplate <typename... Args>\nvoid PrintMessage(_In_ const std::wstring& message, _Inout_ FILE* const stream = stdout, Args... args)\n{\n    static_assert(sizeof...(Args) > 0);\n\n    // Validate that all Args are valid printf arguments.\n    (\n        [](auto e) {\n            using T = decltype(e);\n            if constexpr (std::is_pointer_v<T>)\n            {\n                AssertValidPrintfArg<std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<T>>>>();\n            }\n            else\n            {\n                AssertValidPrintfArg<std::remove_const_t<T>>();\n            }\n        }(args),\n        ...);\n\n    PrintMessageImpl(message, stream, std::forward<Args>(args)...);\n}\n\nvoid SetCrtEncoding(int Mode);\n\nvoid SetThreadDescription(LPCWSTR Name);\n\nwil::unique_hlocal_string SidToString(_In_ PSID Sid);\n\nwinrt::Windows::Management::Deployment::PackageVolume GetSystemVolume();\n\n} // namespace wsl::windows::common::wslutil\n"
  },
  {
    "path": "src/windows/inc/LxssDynamicFunction.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssDynamicFunction.h\n\nAbstract:\n\n    This file contains a helper for accessing dynamically loaded functions.\n\n--*/\n\n#pragma once\n\nenum class DynamicFunctionErrorLogs\n{\n    None\n};\n\n/// <summary>\n/// Wrapper for a runtime dynamically-loaded function.\n/// </summary>\ntemplate <typename FunctionType>\nclass LxssDynamicFunction\n{\npublic:\n    /// <summary>\n    /// Constructor.\n    /// </summary>\n    LxssDynamicFunction(const wil::shared_hmodule& module, LPCSTR functionName) :\n        m_function{reinterpret_cast<FunctionType*>(GetProcAddress(module.get(), functionName))}, m_module{module}\n    {\n        THROW_LAST_ERROR_IF(!m_function);\n    }\n\n    /// <summary>\n    /// Constructor that loads the module.\n    /// </summary>\n    LxssDynamicFunction(LPCWSTR moduleName, LPCSTR functionName) :\n        LxssDynamicFunction{LoadLibraryHelper(moduleName), functionName}\n    {\n    }\n\n    /// <summary>\n    /// Constructor - this constructs an object that deliberately does not want exceptions (Telemetry Errors on failure)\n    /// With this constructor, the caller must call load() later to attempt to load the specified module\n    /// Note: there is not a default c'tor - we do not want accidental construction\n    /// </summary>\n    LxssDynamicFunction(DynamicFunctionErrorLogs) noexcept\n    {\n    }\n\n    /// <summary>\n    /// Attempt to dynamically load the function\n    /// will return an error instead of throwing on failure - which can be needed when we do not want Error traces on failure\n    /// </summary>\n    HRESULT load(const wil::shared_hmodule& module, LPCSTR functionName) noexcept\n    {\n        m_module.reset();\n\n        m_function = reinterpret_cast<FunctionType*>(GetProcAddress(module.get(), functionName));\n        RETURN_LAST_ERROR_IF_EXPECTED(!m_function);\n        m_module = module;\n        return S_OK;\n    }\n\n    HRESULT load(LPCWSTR moduleName, LPCSTR functionName) noexcept\n    {\n        const wil::shared_hmodule module{LoadLibraryEx(moduleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)};\n        RETURN_LAST_ERROR_IF_EXPECTED(!module);\n        return load(module, functionName);\n    }\n\n    /// <summary>\n    /// Call through to the dynamically loaded function.\n    /// </summary>\n\n    template <typename... Args>\n    decltype(auto) operator()(Args&&... args)\n    {\n        return m_function(std::forward<Args>(args)...);\n    }\n\nprivate:\n    static wil::shared_hmodule LoadLibraryHelper(LPCWSTR moduleName)\n    {\n        wil::shared_hmodule module{LoadLibraryEx(moduleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)};\n        THROW_LAST_ERROR_IF_MSG(!module, \"Failed to load %ls\", moduleName);\n        return module;\n    }\n\n    /// <summary>\n    /// No default constructor.\n    /// </summary>\n    LxssDynamicFunction() = delete;\n\n    /// <summary>\n    /// Dynamically loaded function wrapper.\n    /// </summary>\n    FunctionType* m_function;\n\n    /// <summary>\n    /// Reference to the module containing the dynamically loaded function.\n    /// </summary>\n    wil::shared_hmodule m_module;\n};\n"
  },
  {
    "path": "src/windows/inc/RegistryWatcher.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include <functional>\r\n#include <windows.h>\r\n#include <wil/stl.h>\r\n#include <wil/resource.h>\r\n#include <wil/registry.h>\r\n\r\nnamespace wsl::windows::common {\r\n\r\nconstexpr DWORD registry_notify_filter = REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC;\r\n\r\nclass slim_registry_watcher\r\n{\r\npublic:\r\n    slim_registry_watcher() noexcept = default;\r\n\r\n    // Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch.\r\n    HRESULT create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, std::function<void(::wil::RegistryChangeKind)>&& callback) noexcept\r\n    {\r\n        ::wil::unique_hkey keyToWatch;\r\n        HRESULT hr = HRESULT_FROM_WIN32(::RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr));\r\n        if (FAILED(hr))\r\n        {\r\n            return hr;\r\n        }\r\n        return create_common(std::move(keyToWatch), isRecursive, std::move(callback));\r\n    }\r\n\r\n    HRESULT create(::wil::unique_hkey&& keyToWatch, bool isRecursive, std::function<void(::wil::RegistryChangeKind)>&& callback) noexcept\r\n    {\r\n        return create_common(std::move(keyToWatch), isRecursive, std::move(callback));\r\n    }\r\n\r\nprivate:\r\n    // using the default d'tor, destruction must occur in this order\r\n    std::function<void(::wil::RegistryChangeKind)> m_callback;\r\n    ::wil::unique_hkey m_keyToWatch;\r\n    ::wil::unique_event_nothrow m_eventHandle;\r\n    ::wil::unique_threadpool_wait m_threadPoolWait;\r\n    bool m_isRecursive;\r\n\r\n    static void __stdcall callback(PTP_CALLBACK_INSTANCE, void* context, TP_WAIT*, TP_WAIT_RESULT) noexcept\r\n    {\r\n        const auto this_ptr = static_cast<slim_registry_watcher*>(context);\r\n\r\n        const LSTATUS error = ::RegNotifyChangeKeyValue(\r\n            this_ptr->m_keyToWatch.get(), this_ptr->m_isRecursive, registry_notify_filter, this_ptr->m_eventHandle.get(), TRUE);\r\n\r\n        // Call the client before re-arming to ensure that multiple callbacks don't\r\n        // run concurrently.\r\n        switch (error)\r\n        {\r\n        case ERROR_SUCCESS:\r\n        case ERROR_ACCESS_DENIED:\r\n            // Normal modification: send RegistryChangeKind::Modify and re-arm.\r\n            this_ptr->m_callback(::wil::RegistryChangeKind::Modify);\r\n            ::SetThreadpoolWait(this_ptr->m_threadPoolWait.get(), this_ptr->m_eventHandle.get(), nullptr);\r\n            break;\r\n\r\n        case ERROR_KEY_DELETED:\r\n            // Key deleted: send RegistryChangeKind::Delete but do not re-arm.\r\n            this_ptr->m_callback(::wil::RegistryChangeKind::Delete);\r\n            break;\r\n\r\n        case ERROR_HANDLE_REVOKED:\r\n            // Handle revoked.  This can occur if the user session ends before the watcher shuts-down.\r\n            // Do not re-arm since there is generally no way to respond.\r\n            break;\r\n\r\n        default:\r\n            // failure here is a programming error.\r\n            FAIL_FAST_HR(HRESULT_FROM_WIN32(error));\r\n        }\r\n    }\r\n\r\n    HRESULT create_common(::wil::unique_hkey&& keyToWatch, bool isRecursive, std::function<void(::wil::RegistryChangeKind)>&& callback) noexcept\r\n    {\r\n        RETURN_IF_FAILED(m_eventHandle.create());\r\n\r\n        m_threadPoolWait.reset(CreateThreadpoolWait(&slim_registry_watcher::callback, this, nullptr));\r\n        RETURN_LAST_ERROR_IF(!m_threadPoolWait);\r\n\r\n        // associate the notification handle with the threadpool before passing it to RegNotifyChangeKeyValue so we get immediate callbacks in the tp\r\n        SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr);\r\n\r\n        // 'this' object must be fully created before calling RegNotifyChangeKeyValue, as callbacks can start immediately\r\n        m_keyToWatch = std::move(keyToWatch);\r\n        m_isRecursive = isRecursive;\r\n        m_callback = std::move(callback);\r\n\r\n        // no failures after RegNotifyChangeKeyValue succeeds,\r\n        RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(m_keyToWatch.get(), m_isRecursive, registry_notify_filter, m_eventHandle.get(), TRUE));\r\n        return S_OK;\r\n    }\r\n};\r\n\r\n} // namespace wsl::windows::common"
  },
  {
    "path": "src/windows/inc/WmiService.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n// cpp headers\r\n#include <algorithm>\r\n#include <iterator>\r\n#include <exception>\r\n#include <memory>\r\n#include <stdexcept>\r\n#include <utility>\r\n// os headers\r\n// os headers\r\n#include <Windows.h>\r\n#include <objbase.h>\r\n#include <oleauto.h>\r\n#include <WbemIdl.h>\r\n// wil headers\r\n#include <wil/stl.h>\r\n#include <wil/com.h>\r\n#include <wil/resource.h>\r\n\r\n#include \"WmiVariant.h\"\r\n\r\nnamespace wsl::core {\r\n// Callers must instantiate a WmiService instance in order to use any of the Wmi* classes\r\n// This class tracks the WMI initialization of the IWbemLocator and IWbemService interfaces\r\n// which maintain a connection to the specified WMI Service through which WMI calls are made\r\nclass WmiService\r\n{\r\npublic:\r\n    // CoInitializeSecurity is not called by the Wmi* classes. This security\r\n    //   policy should be defined by the code consuming these libraries, as these\r\n    //   libraries cannot assume the security context to apply to the process.\r\n    explicit WmiService(PCWSTR path)\r\n    {\r\n        m_wbemLocator = wil::CoCreateInstance<WbemLocator, IWbemLocator>();\r\n\r\n        THROW_IF_FAILED(m_wbemLocator->ConnectServer(\r\n            wil::make_bstr(path).get(), // Object path of WMI namespace\r\n            nullptr,                    // User name. NULL = current user\r\n            nullptr,                    // User password. NULL = current\r\n            nullptr,                    // Locale. NULL indicates current\r\n            0,                          // Security flags.\r\n            nullptr,                    // Authority (e.g. Kerberos)\r\n            nullptr,                    // Context object\r\n            m_wbemServices.put()));     // receive pointer to IWbemServices proxy\r\n\r\n        THROW_IF_FAILED(CoSetProxyBlanket(\r\n            m_wbemServices.get(),        // Indicates the proxy to set\r\n            RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx\r\n            RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx\r\n            nullptr,                     // Server principal name\r\n            RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx\r\n            RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx\r\n            nullptr,                     // client identity\r\n            EOAC_NONE));                 // proxy capabilities\r\n    }\r\n\r\n    ~WmiService() = default;\r\n    WmiService(const WmiService& service) noexcept = default;\r\n    WmiService& operator=(const WmiService& service) noexcept = default;\r\n    WmiService(WmiService&& rhs) noexcept = default;\r\n    WmiService& operator=(WmiService&& rhs) noexcept = default;\r\n\r\n    IWbemServices* operator->() noexcept\r\n    {\r\n        return m_wbemServices.get();\r\n    }\r\n\r\n    const IWbemServices* operator->() const noexcept\r\n    {\r\n        return m_wbemServices.get();\r\n    }\r\n\r\n    bool operator==(const WmiService& service) const noexcept\r\n    {\r\n        return m_wbemLocator == service.m_wbemLocator && m_wbemServices == service.m_wbemServices;\r\n    }\r\n\r\n    bool operator!=(const WmiService& service) const noexcept\r\n    {\r\n        return !(*this == service);\r\n    }\r\n\r\n    IWbemServices* get() noexcept\r\n    {\r\n        return m_wbemServices.get();\r\n    }\r\n\r\n    [[nodiscard]] const IWbemServices* get() const noexcept\r\n    {\r\n        return m_wbemServices.get();\r\n    }\r\n\r\n    void delete_path(PCWSTR objPath, const wil::com_ptr<IWbemContext>& context) const\r\n    {\r\n        wil::com_ptr<IWbemCallResult> result;\r\n        THROW_IF_FAILED(m_wbemServices->DeleteInstance(\r\n            wil::make_bstr(objPath).get(), WBEM_FLAG_RETURN_IMMEDIATELY, context.get(), result.addressof()));\r\n        // wait for the call to complete\r\n        HRESULT status;\r\n        THROW_IF_FAILED(result->GetCallStatus(WBEM_INFINITE, &status));\r\n        THROW_IF_FAILED(status);\r\n    }\r\n\r\n    // Deletes the WMI object based off the object path specified in the input\r\n    // The object path takes the form of:\r\n    //    MyClass.MyProperty1='33',MyProperty2='value'\r\n    void delete_path(PCWSTR objPath) const\r\n    {\r\n        const wil::com_ptr<IWbemContext> nullcontext;\r\n        delete_path(objPath, nullcontext.get());\r\n    }\r\n\r\nprivate:\r\n    wil::com_ptr<IWbemLocator> m_wbemLocator{};\r\n    wil::com_ptr<IWbemServices> m_wbemServices{};\r\n};\r\n\r\nclass WmiClassObject\r\n{\r\npublic:\r\n    //\r\n    // forward declare iterator classes\r\n    //\r\n    class property_iterator;\r\n    class method_iterator;\r\n\r\n    WmiClassObject(WmiService wbemServices, wil::com_ptr<IWbemClassObject> wbemClass) noexcept :\r\n        m_wbemServices(std::move(wbemServices)), m_wbemClassObject(std::move(wbemClass))\r\n    {\r\n    }\r\n\r\n    WmiClassObject(WmiService wbemServices, PCWSTR className) : m_wbemServices(std::move(wbemServices))\r\n    {\r\n        THROW_IF_FAILED(m_wbemServices->GetObject(wil::make_bstr(className).get(), 0, nullptr, m_wbemClassObject.put(), nullptr));\r\n    }\r\n\r\n    WmiClassObject(WmiService wbemServices, BSTR className) : m_wbemServices(std::move(wbemServices))\r\n    {\r\n        THROW_IF_FAILED(m_wbemServices->GetObjectW(className, 0, nullptr, m_wbemClassObject.put(), nullptr));\r\n    }\r\n\r\n    [[nodiscard]] wil::com_ptr<IWbemClassObject> get_class_object() const noexcept\r\n    {\r\n        return m_wbemClassObject;\r\n    }\r\n\r\n    [[nodiscard]] property_iterator property_begin(bool fNonSystemPropertiesOnly = true) const\r\n    {\r\n        return property_iterator(m_wbemClassObject, fNonSystemPropertiesOnly);\r\n    }\r\n\r\n    [[nodiscard]] static property_iterator property_end() noexcept\r\n    {\r\n        return {};\r\n    }\r\n\r\n    // A forward property_iterator class type to enable forward-traversing instances of the queried WMI provider\r\n    class property_iterator\r\n    {\r\n    public:\r\n        property_iterator() = default;\r\n\r\n        property_iterator(wil::com_ptr<IWbemClassObject> classObj, bool fNonSystemPropertiesOnly) :\r\n            m_wbemClassObj(std::move(classObj)), m_index(0)\r\n        {\r\n            THROW_IF_FAILED(m_wbemClassObj->BeginEnumeration(fNonSystemPropertiesOnly ? WBEM_FLAG_NONSYSTEM_ONLY : 0));\r\n            increment();\r\n        }\r\n\r\n        ~property_iterator() noexcept = default;\r\n        property_iterator(const property_iterator&) = default;\r\n        property_iterator& operator=(const property_iterator&) = default;\r\n        property_iterator(property_iterator&&) = default;\r\n        property_iterator& operator=(property_iterator&&) = default;\r\n\r\n        void swap(property_iterator& rhs) noexcept\r\n        {\r\n            using std::swap;\r\n            swap(m_index, rhs.m_index);\r\n            swap(m_wbemClassObj, rhs.m_wbemClassObj);\r\n            swap(m_propertyName, rhs.m_propertyName);\r\n            swap(m_propertyType, rhs.m_propertyType);\r\n        }\r\n\r\n        ////////////////////////////////////////////////////////////////////////////////\r\n        ///\r\n        /// accessors:\r\n        /// - dereference operators to access the property name\r\n        /// - explicit type() method to expose its CIM type\r\n        ///\r\n        ////////////////////////////////////////////////////////////////////////////////\r\n        BSTR operator*()\r\n        {\r\n            if (m_index == c_endIteratorIndex)\r\n            {\r\n                throw std::out_of_range(\"WmiClassObject::property_iterator::operator * - invalid subscript\");\r\n            }\r\n            return m_propertyName.get();\r\n        }\r\n\r\n        BSTR operator*() const\r\n        {\r\n            if (m_index == c_endIteratorIndex)\r\n            {\r\n                throw std::out_of_range(\"WmiClassObject::property_iterator::operator * - invalid subscript\");\r\n            }\r\n            return m_propertyName.get();\r\n        }\r\n\r\n        BSTR* operator->()\r\n        {\r\n            if (m_index == c_endIteratorIndex)\r\n            {\r\n                throw std::out_of_range(\"WmiClassObject::property_iterator::operator-> - invalid subscript\");\r\n            }\r\n            return m_propertyName.addressof();\r\n        }\r\n\r\n        [[nodiscard]] CIMTYPE type() const\r\n        {\r\n            if (m_index == c_endIteratorIndex)\r\n            {\r\n                throw std::out_of_range(\"WmiClassObject::property_iterator::type - invalid subscript\");\r\n            }\r\n            return m_propertyType;\r\n        }\r\n\r\n        bool operator==(const property_iterator& iter) const noexcept\r\n        {\r\n            if (m_index != c_endIteratorIndex)\r\n            {\r\n                return m_index == iter.m_index && m_wbemClassObj == iter.m_wbemClassObj;\r\n            }\r\n            return m_index == iter.m_index;\r\n        }\r\n\r\n        bool operator!=(const property_iterator& iter) const noexcept\r\n        {\r\n            return !(*this == iter);\r\n        }\r\n\r\n        // preincrement\r\n        property_iterator& operator++()\r\n        {\r\n            increment();\r\n            return *this;\r\n        }\r\n\r\n        // postincrement\r\n        property_iterator operator++(int)\r\n        {\r\n            property_iterator temp(*this);\r\n            increment();\r\n            return temp;\r\n        }\r\n\r\n        // increment by integer\r\n        property_iterator& operator+=(uint32_t inc)\r\n        {\r\n            for (auto loop = 0ul; loop < inc; ++loop)\r\n            {\r\n                increment();\r\n                if (m_index == c_endIteratorIndex)\r\n                {\r\n                    throw std::out_of_range(\"WmiClassObject::property_iterator::operator+= - invalid subscript\");\r\n                }\r\n            }\r\n            return *this;\r\n        }\r\n\r\n        // property_iterator_traits\r\n        // - allows <algorithm> functions to be used\r\n        using iterator_category = std::forward_iterator_tag;\r\n        using value_type = wil::shared_bstr;\r\n        using difference_type = int;\r\n        using pointer = BSTR;\r\n        using reference = wil::shared_bstr&;\r\n\r\n    private:\r\n        static constexpr uint32_t c_endIteratorIndex = ULONG_MAX;\r\n\r\n        wil::com_ptr<IWbemClassObject> m_wbemClassObj{};\r\n        wil::shared_bstr m_propertyName;\r\n        CIMTYPE m_propertyType = 0;\r\n        uint32_t m_index = c_endIteratorIndex;\r\n\r\n        void increment()\r\n        {\r\n            if (m_index == c_endIteratorIndex)\r\n            {\r\n                throw std::out_of_range(\"WmiClassObject::property_iterator - cannot increment: at the end\");\r\n            }\r\n\r\n            CIMTYPE nextCimtype{};\r\n            wil::shared_bstr nextName;\r\n            switch (const auto hr = m_wbemClassObj->Next(0, nextName.put(), nullptr, &nextCimtype, nullptr))\r\n            {\r\n            case WBEM_S_NO_ERROR:\r\n            {\r\n                // update the instance members\r\n                ++m_index;\r\n                using std::swap;\r\n                swap(m_propertyName, nextName);\r\n                swap(m_propertyType, nextCimtype);\r\n                break;\r\n            }\r\n            case WBEM_S_NO_MORE_DATA:\r\n            {\r\n                // at the end...\r\n                m_index = c_endIteratorIndex;\r\n                m_propertyName.reset();\r\n                m_propertyType = 0;\r\n                break;\r\n            }\r\n\r\n            default:\r\n                THROW_HR(hr);\r\n            }\r\n        }\r\n    };\r\n\r\nprivate:\r\n    WmiService m_wbemServices;\r\n    wil::com_ptr<IWbemClassObject> m_wbemClassObject{};\r\n};\r\n\r\nclass WmiInstance\r\n{\r\npublic:\r\n    // Constructors:\r\n    // requires a IWbemServices object already connected to WMI\r\n    // - one c'tor creates an empty instance (if set later)\r\n    // - one c'tor takes the WMI class name to instantiate a new instance\r\n    // - one c'tor takes an existing IWbemClassObject instance\r\n    explicit WmiInstance(WmiService service) noexcept : m_wbemServices(std::move(service))\r\n    {\r\n    }\r\n\r\n    WmiInstance(WmiService service, PCWSTR className) : m_wbemServices(std::move(service))\r\n    {\r\n        // get the object from the WMI service\r\n        wil::com_ptr<IWbemClassObject> classObject;\r\n        THROW_IF_FAILED(m_wbemServices->GetObject(wil::make_bstr(className).get(), 0, nullptr, classObject.addressof(), nullptr));\r\n        // spawn an instance of this object\r\n        THROW_IF_FAILED(classObject->SpawnInstance(0, m_instanceObject.put()));\r\n    }\r\n\r\n    WmiInstance(WmiService service, wil::com_ptr<IWbemClassObject> classObject) noexcept :\r\n        m_wbemServices(std::move(service)), m_instanceObject(std::move(classObject))\r\n    {\r\n    }\r\n\r\n    bool operator==(const WmiInstance& obj) const noexcept\r\n    {\r\n        return m_wbemServices == obj.m_wbemServices && m_instanceObject == obj.m_instanceObject;\r\n    }\r\n\r\n    bool operator!=(const WmiInstance& obj) const noexcept\r\n    {\r\n        return !(*this == obj);\r\n    }\r\n\r\n    [[nodiscard]] wil::com_ptr<IWbemClassObject> get_instance() const noexcept\r\n    {\r\n        return m_instanceObject;\r\n    }\r\n\r\n    [[nodiscard]] wil::unique_bstr get_path() const\r\n    {\r\n        wil::unique_variant objectPathVariant;\r\n        get(L\"__RELPATH\", objectPathVariant.addressof());\r\n\r\n        if (IsVariantEmptyOrNull(objectPathVariant.addressof()))\r\n        {\r\n            return nullptr;\r\n        }\r\n\r\n        if (V_VT(&objectPathVariant) != VT_BSTR)\r\n        {\r\n            THROW_HR(E_INVALIDARG);\r\n        }\r\n\r\n        return wil::make_bstr(V_BSTR(&objectPathVariant));\r\n    }\r\n\r\n    [[nodiscard]] WmiService get_service() const noexcept\r\n    {\r\n        return m_wbemServices;\r\n    }\r\n\r\n    // Retrieves the class name this WmiInstance is representing if any\r\n    [[nodiscard]] wil::unique_bstr get_class_name() const\r\n    {\r\n        wil::unique_variant classVariant;\r\n        get(L\"__CLASS\", classVariant.addressof());\r\n\r\n        if (IsVariantEmptyOrNull(classVariant.addressof()))\r\n        {\r\n            return nullptr;\r\n        }\r\n        if (V_VT(&classVariant) != VT_BSTR)\r\n        {\r\n            THROW_HR(E_INVALIDARG);\r\n        }\r\n\r\n        return wil::make_bstr(V_BSTR(&classVariant));\r\n    }\r\n\r\n    // Returns a class object for the class represented by this instance\r\n    [[nodiscard]] WmiClassObject get_class_object() const noexcept\r\n    {\r\n        return {m_wbemServices, m_instanceObject};\r\n    }\r\n\r\n    // Writes the instantiated object to the WMI repository\r\n    // Supported wbemFlags:\r\n    //   WBEM_FLAG_CREATE_OR_UPDATE\r\n    //   WBEM_FLAG_UPDATE_ONLY\r\n    //   WBEM_FLAG_CREATE_ONLY\r\n    void write_instance(_In_opt_ IWbemContext* context, const LONG wbemFlags = WBEM_FLAG_CREATE_OR_UPDATE)\r\n    {\r\n        wil::com_ptr<IWbemCallResult> result;\r\n        THROW_IF_FAILED(m_wbemServices->PutInstance(\r\n            m_instanceObject.get(), wbemFlags | WBEM_FLAG_RETURN_IMMEDIATELY, context, result.addressof()));\r\n        // wait for the call to complete\r\n        HRESULT status;\r\n        THROW_IF_FAILED(result->GetCallStatus(WBEM_INFINITE, &status));\r\n        THROW_IF_FAILED(status);\r\n    }\r\n\r\n    void write_instance(LONG wbemFlags = WBEM_FLAG_CREATE_OR_UPDATE)\r\n    {\r\n        write_instance(nullptr, wbemFlags);\r\n    }\r\n\r\n    void delete_instance()\r\n    {\r\n        // delete the instance based off the __REPATH property\r\n        wil::com_ptr<IWbemCallResult> result;\r\n        THROW_IF_FAILED(m_wbemServices->DeleteInstance(get_path().get(), WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, result.addressof()));\r\n        // wait for the call to complete\r\n        HRESULT status;\r\n        THROW_IF_FAILED(result->GetCallStatus(WBEM_INFINITE, &status));\r\n        THROW_IF_FAILED(status);\r\n    }\r\n\r\n    // Invokes an instance method with zero -> 5 arguments from the instantiated IWbemClassObject\r\n    // Returns a WmiInstance containing the [out] parameters from the method call\r\n    // (the property \"ReturnValue\" contains the return value)\r\n    WmiInstance execute_method(PCWSTR method)\r\n    {\r\n        return execute_method_impl(method, nullptr);\r\n    }\r\n\r\n    template <typename Arg1>\r\n    WmiInstance execute_method(PCWSTR method, Arg1 arg1)\r\n    {\r\n        // establish the class object for the [in] params to the method\r\n        wil::com_ptr<IWbemClassObject> inParamsDefinition;\r\n        THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));\r\n\r\n        // spawn an instance to store the params\r\n        wil::com_ptr<IWbemClassObject> inParamsInstance;\r\n        THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));\r\n\r\n        // Instantiate a class object to iterate through each property\r\n        const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);\r\n        auto propertyIterator = propertyObject.property_begin();\r\n\r\n        // write the property\r\n        WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);\r\n        propertyclassObject.set(*propertyIterator, arg1);\r\n\r\n        // execute the method with the properties set\r\n        return execute_method_impl(method, inParamsInstance.get());\r\n    }\r\n\r\n    template <typename Arg1, typename Arg2>\r\n    WmiInstance execute_method(PCWSTR method, Arg1 arg1, Arg2 arg2)\r\n    {\r\n        // establish the class object for the [in] params to the method\r\n        wil::com_ptr<IWbemClassObject> inParamsDefinition;\r\n        THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));\r\n\r\n        // spawn an instance to store the params\r\n        wil::com_ptr<IWbemClassObject> inParamsInstance;\r\n        THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));\r\n\r\n        // Instantiate a class object to iterate through each property\r\n        const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);\r\n        auto propertyIterator = propertyObject.property_begin();\r\n\r\n        // write each property\r\n        WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);\r\n        propertyclassObject.set(*propertyIterator, arg1);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg2);\r\n\r\n        // execute the method with the properties set\r\n        return execute_method_impl(method, inParamsInstance.get());\r\n    }\r\n\r\n    template <typename Arg1, typename Arg2, typename Arg3>\r\n    WmiInstance execute_method(PCWSTR method, Arg1 arg1, Arg2 arg2, Arg3 arg3)\r\n    {\r\n        // establish the class object for the [in] params to the method\r\n        wil::com_ptr<IWbemClassObject> inParamsDefinition;\r\n        THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));\r\n\r\n        // spawn an instance to store the params\r\n        wil::com_ptr<IWbemClassObject> inParamsInstance;\r\n        THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));\r\n\r\n        // Instantiate a class object to iterate through each property\r\n        const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);\r\n        auto propertyIterator = propertyObject.property_begin();\r\n\r\n        // write each property\r\n        WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);\r\n        propertyclassObject.set(*propertyIterator, arg1);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg2);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg3);\r\n\r\n        // execute the method with the properties set\r\n        return execute_method_impl(method, inParamsInstance.get());\r\n    }\r\n\r\n    template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>\r\n    WmiInstance execute_method(PCWSTR method, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)\r\n    {\r\n        // establish the class object for the [in] params to the method\r\n        wil::com_ptr<IWbemClassObject> inParamsDefinition;\r\n        THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));\r\n\r\n        // spawn an instance to store the params\r\n        wil::com_ptr<IWbemClassObject> inParamsInstance;\r\n        THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));\r\n\r\n        // Instantiate a class object to iterate through each property\r\n        const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);\r\n        auto propertyIterator = propertyObject.property_begin();\r\n\r\n        // write each property\r\n        WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);\r\n        propertyclassObject.set(*propertyIterator, arg1);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg2);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg3);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg4);\r\n\r\n        // execute the method with the properties set\r\n        return execute_method_impl(method, inParamsInstance.get());\r\n    }\r\n\r\n    template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>\r\n    WmiInstance execute_method(PCWSTR method, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)\r\n    {\r\n        // establish the class object for the [in] params to the method\r\n        wil::com_ptr<IWbemClassObject> inParamsDefinition;\r\n        THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));\r\n\r\n        // spawn an instance to store the params\r\n        wil::com_ptr<IWbemClassObject> inParamsInstance;\r\n        THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));\r\n\r\n        // Instantiate a class object to iterate through each property\r\n        const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);\r\n        auto propertyIterator = propertyObject.property_begin();\r\n\r\n        // write each property\r\n        //\r\n        WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);\r\n        propertyclassObject.set(*propertyIterator, arg1);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg2);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg3);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg4);\r\n        ++propertyIterator;\r\n        propertyclassObject.set(*propertyIterator, arg5);\r\n\r\n        // execute the method with the properties set\r\n        return execute_method_impl(method, inParamsInstance.get());\r\n    }\r\n\r\n    bool is_null(PCWSTR propname) const\r\n    {\r\n        wil::unique_variant variant;\r\n        get_property(propname, variant.addressof());\r\n        return V_VT(&variant) == VT_NULL;\r\n    }\r\n\r\n    // get() and set()\r\n    //\r\n    //   Exposes the properties of the WMI object instantiated\r\n    //   WMI instances don't use all VARIANT types - some specializations\r\n    //   exist because, for example, 64-bit integers actually get passed through\r\n    //   WMI as BSTRs (even though variants support 64-bit integers directly).\r\n    //   See the MSDN documentation for WMI MOF Data Types (Numbers):\r\n    //   http://msdn.microsoft.com/en-us/library/aa392716(v=VS.85).aspx\r\n    //\r\n    //   Even though VARIANTs support 16- and 32-bit unsigned integers, WMI passes them both\r\n    //   around as 32-bit signed integers. Yes, that means you can't pass very large UINT32 values\r\n    //   correctly through WMI directly.\r\n    //\r\n    // get() returns false if the value is empty or null\r\n    //       returns true if retrieved the matching type\r\n\r\n    bool get(PCWSTR propname, _Inout_ VARIANT* value) const\r\n    {\r\n        VariantClear(value);\r\n        get_property(propname, value);\r\n        return !IsVariantEmptyOrNull(value);\r\n    }\r\n\r\n    template <typename T>\r\n    bool get(PCWSTR propname, _Out_ T* value) const\r\n    {\r\n        wil::unique_variant variant;\r\n        get_property(propname, variant.addressof());\r\n        return WmiReadFromVariant(variant.addressof(), value);\r\n    }\r\n\r\n    void set(PCWSTR propname, _In_ const VARIANT* value) const\r\n    {\r\n        set_property(propname, value);\r\n    }\r\n\r\n    template <typename T>\r\n    void set(PCWSTR propname, const T value) const\r\n    {\r\n        set_property(propname, WmiMakeVariant(value).addressof());\r\n    }\r\n\r\n    // Calling IWbemClassObject::Delete on a property of an instance resets to the default value.\r\n    void set_default(PCWSTR propname) const\r\n    {\r\n        THROW_IF_FAILED(m_instanceObject->Delete(propname));\r\n    }\r\n\r\nprivate:\r\n    void get_property(PCWSTR propertyName, _Inout_ VARIANT* pVariant) const\r\n    {\r\n        auto* pInstance = m_instanceObject.get();\r\n        THROW_IF_FAILED(pInstance->Get(propertyName, 0, pVariant, nullptr, nullptr));\r\n    }\r\n\r\n    void set_property(PCWSTR propname, _In_ const VARIANT* pVariant) const\r\n    {\r\n        THROW_IF_FAILED(m_instanceObject->Put(\r\n            propname,\r\n            0,\r\n            const_cast<VARIANT*>(pVariant), // COM is not const-correct\r\n            0));\r\n    }\r\n\r\n    WmiInstance execute_method_impl(PCWSTR method, _In_opt_ IWbemClassObject* pParams)\r\n    {\r\n        // exec the method semi-synchronously from this instance based off the __REPATH property\r\n        wil::com_ptr<IWbemCallResult> result;\r\n        THROW_IF_FAILED(m_wbemServices->ExecMethod(\r\n            get_path().get(), wil::make_bstr(method).get(), WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, pParams, nullptr, result.addressof()));\r\n\r\n        // wait for the call to complete - and get the [out] param object\r\n        wil::com_ptr<IWbemClassObject> outParamsInstance;\r\n        THROW_IF_FAILED(result->GetResultObject(WBEM_INFINITE, outParamsInstance.addressof()));\r\n\r\n        // the call went through - return a WmiInstance from this retrieved instance\r\n        return {m_wbemServices, outParamsInstance};\r\n    }\r\n\r\n    WmiService m_wbemServices;\r\n    wil::com_ptr<IWbemClassObject> m_instanceObject{};\r\n};\r\n\r\n// Exposes enumerating instances of a WMI Provider through an iterator interface.\r\nclass WmiEnumerate\r\n{\r\npublic:\r\n    // A forward iterator class type to enable forward-traversing instances of the queried WMI provider\r\n    //\r\n    class iterator\r\n    {\r\n    public:\r\n        explicit iterator(WmiService service) noexcept : m_wbemServices(std::move(service))\r\n        {\r\n        }\r\n\r\n        iterator(WmiService service, wil::com_ptr<IEnumWbemClassObject> wbemEnumerator) :\r\n            m_index(0), m_wbemServices(std::move(service)), m_wbemEnumerator(std::move(wbemEnumerator))\r\n        {\r\n            increment();\r\n        }\r\n\r\n        ~iterator() noexcept = default;\r\n        iterator(const iterator&) noexcept = default;\r\n        iterator& operator=(const iterator&) noexcept = default;\r\n        iterator(iterator&&) noexcept = default;\r\n        iterator& operator=(iterator&&) noexcept = default;\r\n\r\n        void swap(_Inout_ iterator& rhs) noexcept\r\n        {\r\n            using std::swap;\r\n            swap(m_index, rhs.m_index);\r\n            swap(m_wbemServices, rhs.m_wbemServices);\r\n            swap(m_wbemEnumerator, rhs.m_wbemEnumerator);\r\n            swap(m_wmiInstance, rhs.m_wmiInstance);\r\n        }\r\n\r\n        [[nodiscard]] uint32_t location() const noexcept\r\n        {\r\n            return m_index;\r\n        }\r\n\r\n        WmiInstance& operator*() const noexcept\r\n        {\r\n            return *m_wmiInstance;\r\n        }\r\n\r\n        WmiInstance* operator->() const noexcept\r\n        {\r\n            return m_wmiInstance.get();\r\n        }\r\n\r\n        bool operator==(const iterator&) const noexcept;\r\n        bool operator!=(const iterator&) const noexcept;\r\n\r\n        iterator& operator++();         // preincrement\r\n        iterator operator++(int);       // postincrement\r\n        iterator& operator+=(uint32_t); // increment by integer\r\n\r\n        // iterator_traits\r\n        // - allows <algorithm> functions to be used\r\n        using iterator_category = std::forward_iterator_tag;\r\n        using value_type = WmiInstance;\r\n        using difference_type = int;\r\n        using pointer = WmiInstance*;\r\n        using reference = WmiInstance&;\r\n\r\n    private:\r\n        void increment();\r\n\r\n        static constexpr uint32_t c_endIteratorIndex = ULONG_MAX;\r\n        uint32_t m_index = c_endIteratorIndex;\r\n        WmiService m_wbemServices;\r\n        wil::com_ptr<IEnumWbemClassObject> m_wbemEnumerator;\r\n        std::shared_ptr<WmiInstance> m_wmiInstance;\r\n    };\r\n\r\n    explicit WmiEnumerate(WmiService wbemServices) noexcept : m_wbemServices(std::move(wbemServices))\r\n    {\r\n    }\r\n\r\n    // Allows for executing a WMI query against the WMI service for an enumeration of WMI objects.\r\n    // Assumes the query is of the WQL query language.\r\n    const WmiEnumerate& query(_In_ PCWSTR query)\r\n    {\r\n        THROW_IF_FAILED(m_wbemServices->ExecQuery(\r\n            wil::make_bstr(L\"WQL\").get(), wil::make_bstr(query).get(), WBEM_FLAG_BIDIRECTIONAL, nullptr, m_wbemEnumerator.put()));\r\n        return *this;\r\n    }\r\n\r\n    const WmiEnumerate& query(_In_ PCWSTR query, const wil::com_ptr<IWbemContext>& context)\r\n    {\r\n        THROW_IF_FAILED(m_wbemServices->ExecQuery(\r\n            wil::make_bstr(L\"WQL\").get(), wil::make_bstr(query).get(), WBEM_FLAG_BIDIRECTIONAL, context.get(), m_wbemEnumerator.put()));\r\n        return *this;\r\n    }\r\n\r\n    iterator begin() const\r\n    {\r\n        if (nullptr == m_wbemEnumerator.get())\r\n        {\r\n            return end();\r\n        }\r\n        THROW_IF_FAILED(m_wbemEnumerator->Reset());\r\n        return iterator(m_wbemServices, m_wbemEnumerator);\r\n    }\r\n\r\n    iterator end() const noexcept\r\n    {\r\n        return iterator(m_wbemServices);\r\n    }\r\n\r\n    iterator cbegin() const\r\n    {\r\n        if (nullptr == m_wbemEnumerator.get())\r\n        {\r\n            return cend();\r\n        }\r\n        THROW_IF_FAILED(m_wbemEnumerator->Reset());\r\n        return iterator(m_wbemServices, m_wbemEnumerator);\r\n    }\r\n\r\n    iterator cend() const noexcept\r\n    {\r\n        return iterator(m_wbemServices);\r\n    }\r\n\r\nprivate:\r\n    WmiService m_wbemServices;\r\n    // Marking wbemEnumerator mutable to allow for const correctness of begin() and end()\r\n    //   specifically, invoking Reset() is an implementation detail and should not affect external contracts\r\n    mutable wil::com_ptr<IEnumWbemClassObject> m_wbemEnumerator;\r\n};\r\n\r\ninline bool WmiEnumerate::iterator::operator==(const iterator& iter) const noexcept\r\n{\r\n    if (m_index != c_endIteratorIndex)\r\n    {\r\n        return m_index == iter.m_index && m_wbemServices == iter.m_wbemServices && m_wbemEnumerator == iter.m_wbemEnumerator &&\r\n               m_wmiInstance == iter.m_wmiInstance;\r\n    }\r\n    return m_index == iter.m_index && m_wbemServices == iter.m_wbemServices;\r\n}\r\n\r\ninline bool WmiEnumerate::iterator::operator!=(const iterator& iter) const noexcept\r\n{\r\n    return !(*this == iter);\r\n}\r\n\r\n// preincrement\r\ninline WmiEnumerate::iterator& WmiEnumerate::iterator::operator++()\r\n{\r\n    increment();\r\n    return *this;\r\n}\r\n\r\n// postincrement\r\ninline WmiEnumerate::iterator WmiEnumerate::iterator::operator++(int)\r\n{\r\n    auto temp(*this);\r\n    increment();\r\n    return temp;\r\n}\r\n\r\n// increment by integer\r\ninline WmiEnumerate::iterator& WmiEnumerate::iterator::operator+=(uint32_t inc)\r\n{\r\n    for (auto loop = 0ul; loop < inc; ++loop)\r\n    {\r\n        increment();\r\n        if (m_index == c_endIteratorIndex)\r\n        {\r\n            throw std::out_of_range(\"WmiEnumerate::iterator::operator+= - invalid subscript\");\r\n        }\r\n    }\r\n    return *this;\r\n}\r\n\r\ninline void WmiEnumerate::iterator::increment()\r\n{\r\n    if (m_index == c_endIteratorIndex)\r\n    {\r\n        throw std::out_of_range(\"WmiEnumerate::iterator::increment at the end\");\r\n    }\r\n\r\n    ULONG uReturn;\r\n    wil::com_ptr<IWbemClassObject> wbemTarget;\r\n    THROW_IF_FAILED(m_wbemEnumerator->Next(WBEM_INFINITE, 1, wbemTarget.put(), &uReturn));\r\n\r\n    if (0 == uReturn)\r\n    {\r\n        // at the end...\r\n        m_index = c_endIteratorIndex;\r\n        m_wmiInstance.reset();\r\n    }\r\n    else\r\n    {\r\n        ++m_index;\r\n        m_wmiInstance = std::make_shared<WmiInstance>(m_wbemServices, wbemTarget);\r\n    }\r\n}\r\n} // namespace wsl::core"
  },
  {
    "path": "src/windows/inc/WmiVariant.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n// cpp headers\r\n#include <vector>\r\n#include <string>\r\n\r\n// os headers\r\n#include <Windows.h>\r\n#include <objbase.h>\r\n#include <wil/stl.h>\r\n#include <wil/com.h>\r\n#include <wil/resource.h>\r\n\r\n// WmiMakeVariant(const ) functions are specializations designed to help callers\r\n// who want a way to construct a VARIANT that is safe for passing into WMI\r\n// since WMI has limitations on what VARIANT types it accepts\r\nnamespace wsl::core {\r\ninline bool IsVariantEmptyOrNull(_In_ const VARIANT* variant) noexcept\r\n{\r\n    return V_VT(variant) == VT_EMPTY || V_VT(variant) == VT_NULL;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const bool value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_BOOL;\r\n    V_BOOL(localVariant.addressof()) = value ? TRUE : FALSE;\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ bool* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_BOOL);\r\n    *value = V_BOOL(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const char value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_UI1;\r\n    V_UI1(localVariant.addressof()) = value;\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ char* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_UI1);\r\n    *value = V_UI1(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const unsigned char value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_UI1;\r\n    V_UI1(localVariant.addressof()) = value;\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ unsigned char* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_UI1);\r\n    *value = V_UI1(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const short value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_I2;\r\n    V_I2(localVariant.addressof()) = value;\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ short* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_I2);\r\n    *value = V_I2(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const unsigned short value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_I2;\r\n    V_I2(localVariant.addressof()) = static_cast<short>(value);\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ unsigned short* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_I2);\r\n    *value = V_I2(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const long value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_I4;\r\n    V_I4(localVariant.addressof()) = value;\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ long* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_I4);\r\n    *value = V_I4(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const unsigned long value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_I4;\r\n    V_I4(localVariant.addressof()) = static_cast<long>(value);\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ unsigned long* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_I4);\r\n    *value = V_I4(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const int value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_I4;\r\n    V_I4(localVariant.addressof()) = value;\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ int* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_I4);\r\n    *value = V_I4(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const unsigned int value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_I4;\r\n    V_I4(localVariant.addressof()) = static_cast<long>(value);\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ unsigned int* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_I4);\r\n    *value = V_I4(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const float value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_R4;\r\n    V_R4(localVariant.addressof()) = value;\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ float* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_R4);\r\n    *value = V_R4(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const double value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_R8;\r\n    V_R8(localVariant.addressof()) = value;\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ double* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_R8);\r\n    *value = V_R8(variant);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(SYSTEMTIME value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    DOUBLE time{};\r\n    THROW_HR_IF(E_INVALIDARG, !::SystemTimeToVariantTime(&value, &time));\r\n    V_VT(localVariant.addressof()) = VT_DATE;\r\n    V_DATE(localVariant.addressof()) = time;\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ SYSTEMTIME* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_DATE);\r\n    THROW_HR_IF(E_INVALIDARG, !::VariantTimeToSystemTime(V_DATE(variant), value));\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(_In_ const BSTR value) // NOLINT(misc-misplaced-const)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_BSTR;\r\n    V_BSTR(localVariant.addressof()) = SysAllocString(value);\r\n    THROW_IF_NULL_ALLOC(V_BSTR(localVariant.addressof()));\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ BSTR* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_BSTR);\r\n    *value = SysAllocString(V_BSTR(variant));\r\n    THROW_IF_NULL_ALLOC(*value);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(_In_ PCWSTR value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_BSTR;\r\n    V_BSTR(localVariant.addressof()) = SysAllocString(value);\r\n    THROW_IF_NULL_ALLOC(V_BSTR(localVariant.addressof()));\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Inout_ std::wstring* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_BSTR);\r\n    value->assign(V_BSTR(variant));\r\n    return true;\r\n}\r\n\r\n// Even though VARIANTs support 64-bit integers, WMI passes them around as BSTRs\r\ninline wil::unique_variant WmiMakeVariant(const unsigned long long value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_BSTR;\r\n    V_BSTR(localVariant.addressof()) = SysAllocString(std::to_wstring(value).c_str());\r\n    THROW_IF_NULL_ALLOC(V_BSTR(localVariant.addressof()));\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ unsigned long long* value)\r\n{\r\n    *value = 0;\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_BSTR);\r\n    *value = _wcstoui64(V_BSTR(variant), nullptr, 10);\r\n    return true;\r\n}\r\n\r\n// Even though VARIANTs support 64-bit integers, WMI passes them around as BSTRs\r\ninline wil::unique_variant WmiMakeVariant(_In_ const long long value)\r\n{\r\n    wil::unique_variant localVariant;\r\n    V_VT(localVariant.addressof()) = VT_BSTR;\r\n    V_BSTR(localVariant.addressof()) = SysAllocString(std::to_wstring(value).c_str());\r\n    THROW_IF_NULL_ALLOC(V_BSTR(localVariant.addressof()));\r\n    return localVariant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Out_ long long* value)\r\n{\r\n    *value = 0;\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_BSTR);\r\n    *value = _wcstoi64(V_BSTR(variant), nullptr, 10);\r\n    return true;\r\n}\r\n\r\ntemplate <typename T>\r\nwil::unique_variant WmiMakeVariant(const wil::com_ptr<T>& value) noexcept\r\n{\r\n    wil::unique_variant variant;\r\n    V_VT(&variant) = VT_UNKNOWN;\r\n    V_UNKNOWN(variant.addressof()) = value.get();\r\n    // Must deliberately AddRef the raw pointer assigned to punkVal in the variant\r\n    V_UNKNOWN(variant.addressof())->AddRef();\r\n    return variant;\r\n}\r\n\r\ntemplate <typename T>\r\nbool WmiReadFromVariant(_In_ const VARIANT* variant, _Inout_ wil::com_ptr<T>* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != VT_UNKNOWN);\r\n    THROW_IF_FAILED(V_UNKNOWN(variant)->QueryInterface(__uuidof(T), reinterpret_cast<void**>(value->put())));\r\n    return true;\r\n}\r\n\r\ntemplate <typename T>\r\nbool WmiReadFromVariant(_In_ const VARIANT* variant, _Inout_ std::vector<wil::com_ptr<T>>* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != (VT_UNKNOWN | VT_ARRAY));\r\n\r\n    IUnknown** iUnknownArray;\r\n    THROW_IF_FAILED(::SafeArrayAccessData(variant->parray, reinterpret_cast<void**>(&iUnknownArray)));\r\n    const auto unaccessArray = wil::scope_exit([&]() noexcept { SafeArrayUnaccessData(variant->parray); });\r\n\r\n    std::vector<wil::com_ptr<T>> tempData;\r\n    for (auto loop = 0ul; loop < variant->parray->rgsabound[0].cElements; ++loop)\r\n    {\r\n        wil::com_ptr<T> tempPtr;\r\n        THROW_IF_FAILED(iUnknownArray[loop]->QueryInterface(__uuidof(T), reinterpret_cast<void**>(tempPtr.put())));\r\n        tempData.push_back(tempPtr);\r\n    }\r\n    value->swap(tempData);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const std::vector<std::wstring>& data)\r\n{\r\n    auto* const tempSafeArray = SafeArrayCreateVector(VT_BSTR, 0, static_cast<ULONG>(data.size()));\r\n    THROW_IF_NULL_ALLOC(tempSafeArray);\r\n    auto guardArray = wil::scope_exit([&]() noexcept { SafeArrayDestroy(tempSafeArray); });\r\n\r\n    for (size_t loop = 0; loop < data.size(); ++loop)\r\n    {\r\n        // SafeArrayPutElement requires an array of indexes for each dimension of the array\r\n        // - in this case, we have a 1-dimensional array, thus an array of 1 LONG - assigned to the loop variable\r\n        long index[1]{static_cast<long>(loop)};\r\n\r\n        auto* const bstr = SysAllocString(data[loop].c_str());\r\n        THROW_IF_NULL_ALLOC(bstr);\r\n        THROW_IF_FAILED(::SafeArrayPutElement(tempSafeArray, index, bstr));\r\n    }\r\n\r\n    wil::unique_variant variant;\r\n    variant.parray = tempSafeArray;\r\n    variant.vt = VT_BSTR | VT_ARRAY;\r\n\r\n    // don't free the SAFEARRAY on success - its lifetime is transferred to variant\r\n    guardArray.release();\r\n    return variant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Inout_ std::vector<std::wstring>* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != (VT_BSTR | VT_ARRAY));\r\n\r\n    BSTR* stringArray{};\r\n    THROW_IF_FAILED(::SafeArrayAccessData(variant->parray, reinterpret_cast<void**>(&stringArray)));\r\n    const auto unaccessArray = wil::scope_exit([&]() noexcept { SafeArrayUnaccessData(variant->parray); });\r\n\r\n    std::vector<std::wstring> tempData;\r\n    for (auto loop = 0ul; loop < variant->parray->rgsabound[0].cElements; ++loop)\r\n    {\r\n        tempData.emplace_back(stringArray[loop]);\r\n    }\r\n    value->swap(tempData);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const std::vector<uint32_t>& data)\r\n{\r\n    auto* const tempSafeArray = SafeArrayCreateVector(VT_UI4, 0, static_cast<ULONG>(data.size()));\r\n    THROW_IF_NULL_ALLOC(tempSafeArray);\r\n    auto guardArray = wil::scope_exit([&]() noexcept { SafeArrayDestroy(tempSafeArray); });\r\n\r\n    for (size_t loop = 0; loop < data.size(); ++loop)\r\n    {\r\n        // SafeArrayPutElement requires an array of indexes for each dimension of the array\r\n        // - in this case, we have a 1-dimensional array, thus an array of 1 LONG - assigned to the loop variable\r\n        long index[1]{static_cast<long>(loop)};\r\n\r\n        uint32_t value = data[loop];\r\n        THROW_IF_FAILED(::SafeArrayPutElement(tempSafeArray, index, &value));\r\n    }\r\n\r\n    wil::unique_variant variant;\r\n    variant.parray = tempSafeArray;\r\n    variant.vt = VT_UI4 | VT_ARRAY;\r\n\r\n    // don't free the SAFEARRAY on success - its lifetime is transferred to variant\r\n    guardArray.release();\r\n    return variant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Inout_ std::vector<uint32_t>* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != (VT_UI4 | VT_ARRAY));\r\n\r\n    uint32_t* intArray{};\r\n    THROW_IF_FAILED(::SafeArrayAccessData(variant->parray, reinterpret_cast<void**>(&intArray)));\r\n    const auto unaccessArray = wil::scope_exit([&]() noexcept { SafeArrayUnaccessData(variant->parray); });\r\n\r\n    std::vector<uint32_t> tempData;\r\n    for (auto loop = 0ul; loop < variant->parray->rgsabound[0].cElements; ++loop)\r\n    {\r\n        tempData.push_back(intArray[loop]);\r\n    }\r\n    value->swap(tempData);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const std::vector<unsigned short>& data)\r\n{\r\n    // WMI marshaler complains type mismatch using VT_UI2 | VT_ARRAY, and VT_I4 | VT_ARRAY works fine.\r\n    auto* const tempSafeArray = SafeArrayCreateVector(VT_I4, 0, static_cast<ULONG>(data.size()));\r\n    THROW_IF_NULL_ALLOC(tempSafeArray);\r\n    auto guardArray = wil::scope_exit([&]() noexcept { SafeArrayDestroy(tempSafeArray); });\r\n\r\n    for (size_t loop = 0; loop < data.size(); ++loop)\r\n    {\r\n        // SafeArrayPutElement requires an array of indexes for each dimension of the array\r\n        // - in this case, we have a 1-dimensional array, thus an array of 1 LONG - assigned to the loop variable\r\n        long index[1]{static_cast<long>(loop)};\r\n\r\n        // Expand unsigned short to long because the SAFEARRAY created assumes VT_I4 elements\r\n        long value = data[loop];\r\n        THROW_IF_FAILED(::SafeArrayPutElement(tempSafeArray, index, &value));\r\n    }\r\n\r\n    wil::unique_variant variant;\r\n    variant.parray = tempSafeArray;\r\n    variant.vt = VT_I4 | VT_ARRAY;\r\n\r\n    // don't free the SAFEARRAY on success - its lifetime is transferred to variant\r\n    guardArray.release();\r\n    return variant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Inout_ std::vector<unsigned short>* value)\r\n{\r\n    // WMI marshaler complains type mismatch using VT_UI2 | VT_ARRAY, and VT_I4 | VT_ARRAY works fine.\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != (VT_I4 | VT_ARRAY));\r\n\r\n    long* intArray{};\r\n    THROW_IF_FAILED(::SafeArrayAccessData(variant->parray, reinterpret_cast<void**>(&intArray)));\r\n    const auto unaccessArray = wil::scope_exit([&]() noexcept { SafeArrayUnaccessData(variant->parray); });\r\n\r\n    std::vector<unsigned short> tempData;\r\n    for (auto loop = 0ul; loop < variant->parray->rgsabound[0].cElements; ++loop)\r\n    {\r\n        THROW_HR_IF(E_INVALIDARG, intArray[loop] > MAXUINT16);\r\n        tempData.push_back(static_cast<unsigned short>(intArray[loop]));\r\n    }\r\n    value->swap(tempData);\r\n    return true;\r\n}\r\n\r\ninline wil::unique_variant WmiMakeVariant(const std::vector<unsigned char>& data)\r\n{\r\n    auto* const tempSafeArray = SafeArrayCreateVector(VT_UI1, 0, static_cast<ULONG>(data.size()));\r\n    THROW_IF_NULL_ALLOC(tempSafeArray);\r\n    auto guardArray = wil::scope_exit([&]() noexcept { SafeArrayDestroy(tempSafeArray); });\r\n\r\n    for (size_t loop = 0; loop < data.size(); ++loop)\r\n    {\r\n        // SafeArrayPutElement requires an array of indexes for each dimension of the array\r\n        // - in this case, we have a 1-dimensional array, thus an array of 1 LONG - assigned to the loop variable\r\n        long index[1]{static_cast<long>(loop)};\r\n\r\n        unsigned char value = data[loop];\r\n        THROW_IF_FAILED(::SafeArrayPutElement(tempSafeArray, index, &value));\r\n    }\r\n\r\n    wil::unique_variant variant;\r\n    variant.parray = tempSafeArray;\r\n    variant.vt = VT_UI1 | VT_ARRAY;\r\n\r\n    // don't free the SAFEARRAY on success - its lifetime is transferred to variant\r\n    guardArray.release();\r\n    return variant;\r\n}\r\n\r\ninline bool WmiReadFromVariant(_In_ const VARIANT* variant, _Inout_ std::vector<unsigned char>* value)\r\n{\r\n    if (IsVariantEmptyOrNull(variant))\r\n    {\r\n        return false;\r\n    }\r\n    THROW_HR_IF(E_INVALIDARG, V_VT(variant) != (VT_UI1 | VT_ARRAY));\r\n\r\n    unsigned char* charArray{};\r\n    THROW_IF_FAILED(::SafeArrayAccessData(variant->parray, reinterpret_cast<void**>(&charArray)));\r\n    const auto unaccessArray = wil::scope_exit([&]() noexcept { SafeArrayUnaccessData(variant->parray); });\r\n\r\n    std::vector<unsigned char> tempData;\r\n    for (auto loop = 0ul; loop < variant->parray->rgsabound[0].cElements; ++loop)\r\n    {\r\n        tempData.push_back(charArray[loop]);\r\n    }\r\n    value->swap(tempData);\r\n    return true;\r\n}\r\n} // namespace wsl::core"
  },
  {
    "path": "src/windows/inc/WslCoreConfigInterface.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreConfigInterface.h\n\nAbstract:\n\n    This file contains the WSL Core Config Interface class interface declaration.\n\n--*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <basetyps.h>\n\nenum WslConfigEntry\n{\n    NoEntry,\n    ProcessorCount,\n    MemorySizeBytes,\n    SwapSizeBytes,\n    SwapFilePath,\n    VhdSizeBytes,\n    Networking,\n    FirewallEnabled,\n    IgnoredPorts,\n    LocalhostForwardingEnabled,\n    HostAddressLoopbackEnabled,\n    AutoProxyEnabled,\n    InitialAutoProxyTimeout,\n    DNSProxyEnabled,\n    DNSTunnelingEnabled,\n    BestEffortDNSParsingEnabled,\n    AutoMemoryReclaim,\n    GUIApplicationsEnabled,\n    NestedVirtualizationEnabled,\n    SafeModeEnabled,\n    SparseVHDEnabled,\n    VMIdleTimeout,\n    DebugConsoleEnabled,\n    HardwarePerformanceCountersEnabled,\n    KernelPath,\n    SystemDistroPath,\n    KernelModulesPath,\n};\n\nenum NetworkingConfiguration\n{\n    None = 0,\n    Nat = 1,\n    Bridged = 2,\n    Mirrored = 3,\n    VirtioProxy = 4\n};\n\nenum MemoryReclaimConfiguration\n{\n    Disabled = 0,\n    Gradual = 1,\n    DropCache = 2\n};\n\ntypedef struct WslConfig* WslConfig_t;\n\nstruct WslConfigSetting\n{\n    enum WslConfigEntry ConfigEntry;\n    union\n    {\n        const wchar_t* StringValue;\n        unsigned __int64 UInt64Value;\n        int Int32Value;\n        bool BoolValue;\n        enum NetworkingConfiguration NetworkingConfigurationValue;\n        enum MemoryReclaimConfiguration MemoryReclaimModeValue;\n    };\n};\n\nSTDAPI_(const wchar_t*)\nGetWslConfigFilePath();\n\nSTDAPI_(WslConfig_t)\nCreateWslConfig(const wchar_t* wslConfigFilePath);\n\nSTDAPI_(void)\nFreeWslConfig(WslConfig_t wslConfig);\n\nSTDAPI_(struct WslConfigSetting)\nGetWslConfigSetting(WslConfig_t wslConfig, enum WslConfigEntry ConfigEntry);\n\nSTDAPI_(unsigned long)\nSetWslConfigSetting(WslConfig_t wslConfig, struct WslConfigSetting setting);"
  },
  {
    "path": "src/windows/inc/WslPluginApi.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslPluginApi.h\n\nAbstract:\n\n    This file contains the interface for WSL plugins to interact with WSL distributions.\n\n--*/\n\n#pragma once\n\n#include <stdint.h>\n\n// Must be lowercase. See: https://github.com/microsoft/WSL/issues/12580\n#include <windows.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define WSLPLUGINAPI_ENTRYPOINTV1 WSLPluginAPIV1_EntryPoint\n#define WSL_E_PLUGIN_REQUIRES_UPDATE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x8004032A)\n\n#define WSL_PLUGIN_REQUIRE_VERSION(_Major, _Minor, _Revision, Api) \\\n    if (Api->Version.Major < (_Major) || (Api->Version.Major == (_Major) && Api->Version.Minor < (_Minor)) || \\\n        (Api->Version.Major == (_Major) && Api->Version.Minor == (_Minor) && Api->Version.Revision < (_Revision))) \\\n    { \\\n        return WSL_E_PLUGIN_REQUIRES_UPDATE; \\\n    }\n\nstruct WSLVersion\n{\n    uint32_t Major;\n    uint32_t Minor;\n    uint32_t Revision;\n};\n\nenum WSLUserConfiguration\n{\n    None = 0,\n    WSLUserConfigurationCustomKernel = 1,\n    WSLUserConfigurationCustomKernelCommandLine = 2\n};\n\n#ifdef __cplusplus\nDEFINE_ENUM_FLAG_OPERATORS(WSLUserConfiguration);\n#endif\n\nstruct WSLVmCreationSettings\n{\n    enum WSLUserConfiguration CustomConfigurationFlags;\n};\n\ntypedef DWORD WSLSessionId;\n\nstruct WSLSessionInformation\n{\n    WSLSessionId SessionId;\n    HANDLE UserToken;\n    PSID UserSid;\n};\n\nstruct WSLDistributionInformation\n{\n    GUID Id; // Distribution ID, guaranteed to be the same across reboots\n    LPCWSTR Name;\n    uint64_t PidNamespace;\n    LPCWSTR PackageFamilyName; // Package family name, or NULL if none\n    uint32_t InitPid;          // Pid of the init process. Introduced in 2.0.5\n    LPCWSTR Flavor;            // Type of distribution (ubuntu, debian, ...). Introduced in 2.4.4\n    LPCWSTR Version;           // Distribution version. Introduced in 2.4.4\n};\n\nstruct WslOfflineDistributionInformation\n{\n    GUID Id; // Distribution ID, guaranteed to be the same across reboots\n    LPCWSTR Name;\n    LPCWSTR PackageFamilyName; // Package family name, or NULL if none\n    LPCWSTR Flavor;            // Type of distribution (ubuntu, debian, ...). Introduced in 2.4.4\n    LPCWSTR Version;           // Distribution version. Introduced in 2.4.4\n};\n\n// Create plan9 mount between Windows & Linux\ntypedef HRESULT (*WSLPluginAPI_MountFolder)(WSLSessionId Session, LPCWSTR WindowsPath, LPCWSTR LinuxPath, BOOL ReadOnly, LPCWSTR Name);\n\n// Execute a program in the root namespace.\n// On success, 'Socket' is connected to stdin & stdout (stderr goes to dmesg) // 'Arguments' is expected to be NULL terminated\ntypedef HRESULT (*WSLPluginAPI_ExecuteBinary)(WSLSessionId Session, LPCSTR Path, LPCSTR* Arguments, SOCKET* Socket);\n\n// Execute a program in a user distribution\n// On success, 'Socket' is connected to stdin & stdout (stderr goes to dmesg) // 'Arguments' is expected to be NULL terminated\ntypedef HRESULT (*WSLPluginAPI_ExecuteBinaryInDistribution)(WSLSessionId Session, const GUID* Distribution, LPCSTR Path, LPCSTR* Arguments, SOCKET* Socket);\n\n// Set the error message to display to the user if the VM or distribution creation fails.\n// Must be called synchronously in either OnVMStarted() or OnDistributionStarted().\ntypedef HRESULT (*WSLPluginAPI_PluginError)(LPCWSTR UserMessage);\n\n// Synchronous notifications sent to the plugin\n\n// Called when the VM has started.\n// 'Session' and 'UserSettings' are only valid during while the call is in progress.\ntypedef HRESULT (*WSLPluginAPI_OnVMStarted)(const struct WSLSessionInformation* Session, const struct WSLVmCreationSettings* UserSettings);\n\n// Called when the VM is about to stop.\n// 'Session' is only valid during while the call is in progress.\ntypedef HRESULT (*WSLPluginAPI_OnVMStopping)(const struct WSLSessionInformation* Session);\n\n// Called when a distribution has started.\n// 'Session' and 'Distribution' is only valid during while the call is in progress.\ntypedef HRESULT (*WSLPluginAPI_OnDistributionStarted)(const struct WSLSessionInformation* Session, const struct WSLDistributionInformation* Distribution);\n\n// Called when a distribution is about to stop.\n// 'Session' and 'Distribution' is only valid during while the call is in progress.\n// Note: It's possible that stopping a distribution fails (for instance if a file is in use).\n// In this case, it's possible for this notification to be called multiple times for the same distribution.\ntypedef HRESULT (*WSLPluginAPI_OnDistributionStopping)(const struct WSLSessionInformation* Session, const struct WSLDistributionInformation* Distribution);\n\n// Called when a distribution is registered or unregistered.\n// Returning failure will NOT cause the operation to fail.\ntypedef HRESULT (*WSLPluginAPI_OnDistributionRegistered)(const struct WSLSessionInformation* Session, const struct WslOfflineDistributionInformation* Distribution);\n\nstruct WSLPluginHooksV1\n{\n    WSLPluginAPI_OnVMStarted OnVMStarted;\n    WSLPluginAPI_OnVMStopping OnVMStopping;\n    WSLPluginAPI_OnDistributionStarted OnDistributionStarted;\n    WSLPluginAPI_OnDistributionStopping OnDistributionStopping;\n    WSLPluginAPI_OnDistributionRegistered OnDistributionRegistered;   // Introduced in 2.1.2\n    WSLPluginAPI_OnDistributionRegistered OnDistributionUnregistered; // Introduced in 2.1.2\n};\n\nstruct WSLPluginAPIV1\n{\n    struct WSLVersion Version;\n    WSLPluginAPI_MountFolder MountFolder;\n    WSLPluginAPI_ExecuteBinary ExecuteBinary;\n    WSLPluginAPI_PluginError PluginError;\n    WSLPluginAPI_ExecuteBinaryInDistribution ExecuteBinaryInDistribution; // Introduced in 2.1.2\n};\n\ntypedef HRESULT (*WSLPluginAPI_EntryPointV1)(const struct WSLPluginAPIV1* Api, struct WSLPluginHooksV1* Hooks);\n\n#ifdef __cplusplus\n}\n#endif"
  },
  {
    "path": "src/windows/inc/comservicehelper.h",
    "content": "///////////////////////////////////////////////////////////////////////////////\r\n//                                                                           //\r\n// Copyright (c) Microsoft Corporation. All rights reserved.                 //\r\n// comservicehelper.h                                                        //\r\n//                                                                           //\r\n// Provides a template class to handle a Service entry point.                //\r\n//                                                                           //\r\n///////////////////////////////////////////////////////////////////////////////\r\n\r\n#pragma once\r\n\r\n#include <sddl.h>\r\n#include <ctxtcall.h>\r\n#include <wil\\result.h>\r\n#include <wil\\resource.h>\r\n\r\nnamespace Windows { namespace Internal {\r\n\r\n    struct ServerDescriptor final\r\n    {\r\n        const wchar_t* ServerName = nullptr;\r\n    };\r\n\r\n    template <typename TBase>\r\n    struct ModuleServerDescriptor\r\n    {\r\n        constexpr static const ServerDescriptor Create()\r\n        {\r\n            constexpr const ServerDescriptor serverDescriptor = {TBase::ServerName};\r\n            return serverDescriptor;\r\n        }\r\n    };\r\n\r\n    struct DefaultServerDescriptor final\r\n    {\r\n    };\r\n\r\n    class ServiceModuleBase\r\n    {\r\n    public:\r\n        ServiceModuleBase()\r\n        {\r\n        }\r\n\r\n        ~ServiceModuleBase()\r\n        {\r\n        }\r\n\r\n        template <typename TSecurityPolicy, GLOBALOPT_EH_VALUES TExceptionPolicy, typename TServerDescriptor = DefaultServerDescriptor>\r\n        HRESULT Initialize(_In_ boolean ownProcess, _In_ boolean addRefModule, _In_ boolean hasDedicatedThread = true, _In_ HANDLE stopEvent = nullptr)\r\n        {\r\n            auto uninitializeOnFailure = wil::scope_exit([&]() { Uninitialize(); });\r\n\r\n            if (hasDedicatedThread)\r\n            {\r\n                // If the ServiceModule is being initialized on its own dedicated thread (i.e. the thread hangs around until it's\r\n                // time to call SvcModuleBase::Uninitialize) then initialize COM for this thread.\r\n                m_hrMtaInitialized = Windows::Foundation::Initialize(RO_INIT_MULTITHREADED);\r\n            }\r\n            else\r\n            {\r\n                // Otherwise, take a reference on the MTA apartment.\r\n                m_hrMtaInitialized = CoIncrementMTAUsage(&m_mtaUsageCookie);\r\n            }\r\n            RETURN_IF_FAILED(m_hrMtaInitialized);\r\n\r\n            __if_exists(TServerDescriptor::Create)\r\n            {\r\n                m_serverDescriptor = TServerDescriptor::Create();\r\n            }\r\n\r\n            if (ownProcess)\r\n            {\r\n                RETURN_IF_FAILED(InitializeSecurity<TSecurityPolicy>());\r\n            }\r\n\r\n            // Tell COM how to mask fatal exceptions.\r\n            if (ownProcess)\r\n            {\r\n                wil::com_ptr<IGlobalOptions> pIGLB;\r\n                RETURN_IF_FAILED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pIGLB)));\r\n                RETURN_IF_FAILED(pIGLB->Set(COMGLB_EXCEPTION_HANDLING, TExceptionPolicy));\r\n            }\r\n\r\n            // SubInitialize must be called before IncrementObjectCount or the ContextCallback.\r\n            // The ContextCallback will register the COM objects, and as soon as that happens, incoming activations may arrive\r\n            // which will call IncrementObjectCount.\r\n            RETURN_IF_FAILED(SubInitialize());\r\n\r\n            // Add the extra module reference to prevent shutdown before the ContextCallback because once we register the COM objects,\r\n            // an object may be released and drop the module reference count to zero if the extra reference isn't added yet.\r\n            if (addRefModule)\r\n            {\r\n                IncrementObjectCount();\r\n                m_addedModuleReference = true;\r\n            }\r\n\r\n            RETURN_IF_FAILED(CoCreateInstance(CLSID_ContextSwitcher, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_icc)));\r\n\r\n            RETURN_IF_FAILED(m_icc->ContextCallback(\r\n                &Windows::Internal::ServiceModuleBase::ConnectCallbackThunk, reinterpret_cast<ComCallData*>(this), IID_IContextCallback, 5, nullptr));\r\n\r\n            uninitializeOnFailure.release();\r\n            return S_OK;\r\n        }\r\n\r\n        HRESULT Uninitialize()\r\n        {\r\n            if (m_icc)\r\n            {\r\n                m_icc->ContextCallback(\r\n                    &ServiceModuleBase::DisconnectCallbackThunk, reinterpret_cast<ComCallData*>(this), IID_IContextCallback, 5, nullptr);\r\n                m_icc = nullptr;\r\n            }\r\n\r\n            if (m_addedModuleReference)\r\n            {\r\n                DecrementObjectCount();\r\n                m_addedModuleReference = false;\r\n            }\r\n\r\n            if (SUCCEEDED(m_hrMtaInitialized))\r\n            {\r\n                if (m_mtaUsageCookie)\r\n                {\r\n                    m_mtaUsageCookie.reset();\r\n                }\r\n                else\r\n                {\r\n                    Windows::Foundation::Uninitialize();\r\n                }\r\n\r\n                m_hrMtaInitialized = E_FAIL;\r\n            }\r\n            return S_OK;\r\n        }\r\n\r\n        virtual HRESULT ConnectCallback() = 0;\r\n\r\n        virtual HRESULT DisconnectCallback() = 0;\r\n\r\n        STDMETHOD_(ULONG, IncrementObjectCount()) = 0;\r\n\r\n        STDMETHOD_(ULONG, DecrementObjectCount()) = 0;\r\n\r\n        static HRESULT __stdcall ConnectCallbackThunk(_In_ ComCallData* pv)\r\n        {\r\n            ServiceModuleBase* pThis = reinterpret_cast<ServiceModuleBase*>(pv);\r\n            return pThis->ConnectCallback();\r\n        }\r\n\r\n        static HRESULT __stdcall DisconnectCallbackThunk(_In_ ComCallData* pv)\r\n        {\r\n            ServiceModuleBase* pThis = reinterpret_cast<ServiceModuleBase*>(pv);\r\n            return pThis->DisconnectCallback();\r\n        }\r\n\r\n    public:\r\n        //\r\n        // These are not fully-fledged policy objects, but they all rely on SDDL instead.\r\n        //\r\n        // Useful references:\r\n        //\r\n        // Access Control Lists for COM\r\n        //   http://msdn.microsoft.com/en-us/library/windows/desktop/ms693364(v=vs.85).aspx\r\n        //\r\n        // Security Descriptor String Format\r\n        //   http://msdn.microsoft.com/en-us/library/windows/desktop/aa379570(v=vs.85).aspx\r\n        //\r\n        // ACE Strings\r\n        //   http://msdn.microsoft.com/en-us/library/windows/desktop/aa374928(v=vs.85).aspx\r\n        //\r\n        struct SecurityPolicyEveryoneLocal\r\n        {\r\n            static LPCWSTR GetSDDLText()\r\n            {\r\n                //\r\n                // The current one explicitly allows Everyone and App Packages for local clients only.\r\n                //\r\n                // O: = Owner\r\n                // PS = principal self\r\n                // G: = Group\r\n                // BU = Built-in users\r\n                // D: = DACL\r\n                // A  = access allowed\r\n                // 0B = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL | COM_RIGHTS_ACTIVATE_LOCAL\r\n                // AC = App Packages\r\n                // WD = everyone\r\n                // S: = SACL\r\n                // ML = Mandatory Label\r\n                // NX = NO_EXECUTE_UP\r\n                // LW = Low Integrity\r\n                //\r\n                return L\"O:PSG:BUD:(A;;0xB;;;AC)(A;;0xB;;;WD)S:(ML;;NX;;;LW)\";\r\n            }\r\n        };\r\n\r\n        struct SecurityPolicyEveryoneLocalAndRemote\r\n        {\r\n            static LPCWSTR GetSDDLText()\r\n            {\r\n                //\r\n                // The current one explicitly allows Everyone and App Packages for local and remote clients.\r\n                //\r\n                // O: = Owner\r\n                // PS = principal self\r\n                // G: = Group\r\n                // BU = Built-in users\r\n                // D: = DACL\r\n                // A  = access allowed\r\n                // 1F = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL | COM_RIGHTS_ACTIVATE_LOCAL | COM_RIGHTS_EXECUTE_REMOTE | COM_RIGHTS_ACTIVATE_REMOTE\r\n                // AC = App Packages\r\n                // WD = everyone\r\n                // S: = SACL\r\n                // ML = Mandatory Label\r\n                // NX = NO_EXECUTE_UP\r\n                // LW = Low Integrity\r\n                //\r\n                return L\"O:PSG:BUD:(A;;0x1F;;;AC)(A;;0x1F;;;WD)S:(ML;;NX;;;LW)\";\r\n            }\r\n        };\r\n\r\n    protected:\r\n        // _module is a reference, so the compiler can't generate these. Hide them.\r\n        ServiceModuleBase(const ServiceModuleBase&);\r\n        ServiceModuleBase& operator=(const ServiceModuleBase&);\r\n\r\n        // Used by derived classes to initialize any necessary state\r\n        virtual HRESULT SubInitialize()\r\n        {\r\n            return S_OK;\r\n        }\r\n\r\n        template <typename TSecurityPolicy>\r\n        HRESULT InitializeSecurity()\r\n        {\r\n            PACL pDacl = nullptr, pSacl = nullptr;\r\n            PSID pOwner = nullptr, pPrimaryGroup = nullptr;\r\n            PSECURITY_DESCRIPTOR pSDRelative = nullptr, pSDAbsolute = nullptr;\r\n            DWORD cbSDAbsolute = 0, cbDacl = 0, cbSacl = 0, cbOwner = 0, cbPrimaryGroup = 0;\r\n\r\n            auto cleanup = wil::scope_exit([&] {\r\n                HeapFree(GetProcessHeap(), 0, pDacl);\r\n                HeapFree(GetProcessHeap(), 0, pSacl);\r\n                HeapFree(GetProcessHeap(), 0, pOwner);\r\n                HeapFree(GetProcessHeap(), 0, pPrimaryGroup);\r\n                HeapFree(GetProcessHeap(), 0, pSDAbsolute);\r\n                HeapFree(GetProcessHeap(), 0, pSDRelative);\r\n            });\r\n\r\n            // The following call returns a self-relative security descriptor...\r\n            RETURN_IF_WIN32_BOOL_FALSE(ConvertStringSecurityDescriptorToSecurityDescriptor(\r\n                TSecurityPolicy::GetSDDLText(), SDDL_REVISION_1, &pSDRelative, nullptr));\r\n\r\n            // ...before we pass it to CoInitializeSecurity, we need to make it absolute. We call MakeAbsoluteSD once to find out how large our buffers need to be...\r\n            RETURN_LAST_ERROR_IF(\r\n                MakeAbsoluteSD(pSDRelative, nullptr, &cbSDAbsolute, nullptr, &cbDacl, nullptr, &cbSacl, nullptr, &cbOwner, nullptr, &cbPrimaryGroup) ||\r\n                ERROR_INSUFFICIENT_BUFFER != GetLastError());\r\n\r\n            // Then we allocate the buffers...\r\n            pSDAbsolute = reinterpret_cast<PSECURITY_DESCRIPTOR>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbSDAbsolute));\r\n            RETURN_IF_NULL_ALLOC(pSDAbsolute);\r\n\r\n            pDacl = reinterpret_cast<PACL>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDacl));\r\n            RETURN_IF_NULL_ALLOC(pDacl);\r\n\r\n            pSacl = reinterpret_cast<PACL>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbSacl));\r\n            RETURN_IF_NULL_ALLOC(pSacl);\r\n\r\n            pOwner = reinterpret_cast<PSID>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbOwner));\r\n            RETURN_IF_NULL_ALLOC(pOwner);\r\n\r\n            pPrimaryGroup = reinterpret_cast<PSID>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbPrimaryGroup));\r\n            RETURN_IF_NULL_ALLOC(pPrimaryGroup);\r\n\r\n            // ...then we call MakeAbsoluteSD again with the buffers we just allocated\r\n            RETURN_IF_WIN32_BOOL_FALSE(MakeAbsoluteSD(\r\n                pSDRelative, pSDAbsolute, &cbSDAbsolute, pDacl, &cbDacl, pSacl, &cbSacl, pOwner, &cbOwner, pPrimaryGroup, &cbPrimaryGroup));\r\n\r\n            // ...and now we can call CoInitializeSecurity\r\n            RETURN_IF_FAILED(CoInitializeSecurity(\r\n                pSDAbsolute, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, nullptr));\r\n\r\n            return S_OK;\r\n        }\r\n\r\n        // Declare before any COM member variables in this object\r\n        wil::unique_mta_usage_cookie m_mtaUsageCookie;\r\n\r\n        // Result of initializing the COM apartment\r\n        HRESULT m_hrMtaInitialized = E_FAIL;\r\n\r\n        // Track whether we added an extra module reference\r\n        bool m_addedModuleReference = false;\r\n\r\n        // COM callback object to support unloading shared-process services\r\n        wil::com_ptr<IContextCallback> m_icc;\r\n\r\n        // COM Server descriptor\r\n        ServerDescriptor m_serverDescriptor{};\r\n    };\r\n\r\n    class ServiceModule : public ServiceModuleBase, public Microsoft::WRL::Module<Microsoft::WRL::OutOfProc, ServiceModule>\r\n    {\r\n    public:\r\n        STDMETHOD_(ULONG, IncrementObjectCount()) override\r\n        {\r\n            return Microsoft::WRL::Module<Microsoft::WRL::OutOfProc, ServiceModule>::IncrementObjectCount();\r\n        }\r\n\r\n        STDMETHOD_(ULONG, DecrementObjectCount()) override\r\n        {\r\n            return Microsoft::WRL::Module<Microsoft::WRL::OutOfProc, ServiceModule>::DecrementObjectCount();\r\n        }\r\n\r\n        HRESULT ConnectCallback() override\r\n        {\r\n            return __super::RegisterObjects(m_serverDescriptor.ServerName);\r\n        }\r\n\r\n        HRESULT DisconnectCallback() override\r\n        {\r\n            __super::UnregisterObjects(m_serverDescriptor.ServerName);\r\n            return CoDisconnectContext(INFINITE);\r\n        }\r\n    };\r\n\r\n    enum LastObjectReleaseBehavior\r\n    {\r\n        ShutdownAfterLastObjectReleased = 1,\r\n        ContinueRunningWithNoObjects = 2,\r\n    };\r\n\r\n    template <\r\n        typename TBase,\r\n        LastObjectReleaseBehavior TLastObjectReleaseBehavior = ShutdownAfterLastObjectReleased,\r\n        typename TSecurityPolicy = ServiceModule::SecurityPolicyEveryoneLocal,\r\n        GLOBALOPT_EH_VALUES TExceptionPolicy = COMGLB_EXCEPTION_DONOT_HANDLE_ANY,\r\n        typename TServerDescriptor = DefaultServerDescriptor>\r\n    class Service\r\n    {\r\n    public:\r\n        Service()\r\n        {\r\n            _serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;\r\n            _serviceStatus.dwCurrentState = SERVICE_RUNNING;\r\n            _serviceStatus.dwWin32ExitCode = NO_ERROR;\r\n        }\r\n\r\n        ~Service()\r\n        {\r\n            __if_exists(TBase::OnLowPowerModeChanged)\r\n            {\r\n                if (_powerHandle != nullptr)\r\n                {\r\n                    PowerSettingUnregisterNotification(_powerHandle);\r\n                    _powerHandle = nullptr;\r\n                }\r\n            }\r\n\r\n            if (_stopEvent != nullptr)\r\n            {\r\n                CloseHandle(_stopEvent);\r\n                _stopEvent = nullptr;\r\n            }\r\n        }\r\n\r\n        // Runs the main function for a service that lives in its own process.\r\n        static HRESULT ProcessMain()\r\n        {\r\n            const SERVICE_TABLE_ENTRY DispatchTable[] = {\r\n                {const_cast<LPWSTR>(L\"\"),\r\n                 (LPSERVICE_MAIN_FUNCTION)&Service<TBase, TLastObjectReleaseBehavior, TSecurityPolicy, TExceptionPolicy>::SvcMain},\r\n                {nullptr, nullptr}};\r\n\r\n            RETURN_IF_WIN32_BOOL_FALSE(StartServiceCtrlDispatcher(DispatchTable));\r\n\r\n            return s_LastServiceMainHR;\r\n        }\r\n\r\n        // Runs the service itself. Only necessary when ProcessMain isn't used.\r\n        static void ServiceMainSharedProcess()\r\n        {\r\n            TBase instance;\r\n            s_LastServiceMainHR = instance.RunServiceMain(false);\r\n        }\r\n\r\n        HRESULT RunServiceMain(_In_ boolean fOwnProcess)\r\n        {\r\n            __if_exists(TBase::ServiceStopped)\r\n            {\r\n                _serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_STOP;\r\n            }\r\n\r\n            __if_exists(TBase::OnSystemShutdown)\r\n            {\r\n                _serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;\r\n            }\r\n\r\n            __if_exists(TBase::OnSessionChanged)\r\n            {\r\n                _serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;\r\n            }\r\n\r\n            ServiceModuleBase* pModule = nullptr;\r\n            HRESULT hr = [&]() {\r\n                // The service handle need not be closed.\r\n                _serviceStatusHandle = RegisterServiceCtrlHandlerEx(TBase::GetName(), &Service::HandlerExStatic, this);\r\n                RETURN_LAST_ERROR_IF(_serviceStatusHandle == 0);\r\n\r\n                _stopEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);\r\n                RETURN_LAST_ERROR_IF(_stopEvent == nullptr);\r\n\r\n                __if_exists(TBase::OnServiceStarting)\r\n                {\r\n                    RETURN_IF_FAILED(reinterpret_cast<TBase*>(this)->OnServiceStarting());\r\n                }\r\n\r\n                if (fOwnProcess)\r\n                {\r\n                    pModule = &(ServiceModule::Create(this, GetModuleCallback<TLastObjectReleaseBehavior>()));\r\n                }\r\n                else\r\n                {\r\n                    RETURN_HR(E_NOTIMPL);\r\n                }\r\n\r\n                constexpr bool addModuleReference = (TLastObjectReleaseBehavior == ContinueRunningWithNoObjects);\r\n\r\n                RETURN_IF_FAILED((pModule->Initialize<TSecurityPolicy, TExceptionPolicy, TServerDescriptor>(\r\n                    fOwnProcess, addModuleReference, true /*hasDedicatedThread*/, _stopEvent)));\r\n\r\n                RETURN_IF_FAILED(hr = reinterpret_cast<TBase*>(this)->ServiceStarted());\r\n                auto serviceStopped = wil::scope_exit([&] {\r\n                    __if_exists(TBase::ServiceStopped)\r\n                    {\r\n                        reinterpret_cast<TBase*>(this)->ServiceStopped();\r\n                    }\r\n                });\r\n\r\n                __if_exists(TBase::OnLowPowerModeChanged)\r\n                {\r\n                    RETURN_IF_WIN32_ERROR(PowerSettingRegisterNotification(\r\n                        &GUID_LOW_POWER_EPOCH_PRV, DEVICE_NOTIFY_SERVICE_HANDLE, _serviceStatusHandle, &_powerHandle));\r\n                }\r\n\r\n                __if_exists(TBase::OnLowPowerModeChanged)\r\n                {\r\n                    _serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_POWEREVENT;\r\n                }\r\n\r\n                ReportCurrentStatus();\r\n                WaitForSingleObject(_stopEvent, INFINITE);\r\n\r\n                // The service is stopping now.\r\n                serviceStopped.reset();\r\n\r\n                __if_exists(TBase::OnLowPowerModeChanged)\r\n                {\r\n                    if (_powerHandle != nullptr)\r\n                    {\r\n                        PowerSettingUnregisterNotification(_powerHandle);\r\n                        _powerHandle = nullptr;\r\n                    }\r\n                }\r\n\r\n                return S_OK;\r\n            }();\r\n\r\n            //\r\n            // See http://blogs.msdn.com/b/oldnewthing/archive/2006/11/03/942851.aspx for\r\n            // a discussion on why this is lossy.\r\n            //\r\n            if (HRESULT_FACILITY(hr) == FACILITY_WIN32)\r\n            {\r\n                _serviceStatus.dwWin32ExitCode = HRESULT_CODE(hr);\r\n            }\r\n            else\r\n            {\r\n                if (FAILED(hr))\r\n                {\r\n                    _serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r\n                }\r\n                _serviceStatus.dwServiceSpecificExitCode = hr;\r\n            }\r\n\r\n            // Unregister the COM objects if the service module was created.\r\n            if (pModule != nullptr)\r\n            {\r\n                pModule->Uninitialize();\r\n            }\r\n\r\n            _serviceStatus.dwCurrentState = SERVICE_STOPPED;\r\n            ReportCurrentStatus();\r\n\r\n            RETURN_IF_FAILED(hr);\r\n\r\n            return S_OK;\r\n        }\r\n\r\n        // Returns the service status handle for this service.\r\n        SERVICE_STATUS_HANDLE GetServiceStatusHandle() const\r\n        {\r\n            return _serviceStatusHandle;\r\n        }\r\n\r\n    protected:\r\n        // Reports the current status information.\r\n        void ReportCurrentStatus()\r\n        {\r\n            SetServiceStatus(_serviceStatusHandle, &_serviceStatus);\r\n        }\r\n\r\n        // Gets a mutable reference to the current status information.\r\n        LPSERVICE_STATUS GetServiceStatusReference()\r\n        {\r\n            return &_serviceStatus;\r\n        }\r\n\r\n        // Asynchronously stops this service, typically in response to a SERVICE_CONTROL_STOP request.\r\n        void StopAsync()\r\n        {\r\n            if (_serviceStatus.dwCurrentState != SERVICE_STOP_PENDING && _serviceStatus.dwCurrentState != SERVICE_STOPPED)\r\n            {\r\n                _serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;\r\n                ReportCurrentStatus();\r\n            }\r\n            SetEvent(_stopEvent);\r\n        }\r\n\r\n        // Asynchronously stops this service, typically in response to an async initialization issue\r\n        void StopAsync(HRESULT hr)\r\n        {\r\n            if (hr != S_OK)\r\n            {\r\n                if (HRESULT_FACILITY(hr) == FACILITY_WIN32)\r\n                {\r\n                    _serviceStatus.dwWin32ExitCode = HRESULT_CODE(hr);\r\n                }\r\n                else\r\n                {\r\n                    if (FAILED(hr))\r\n                    {\r\n                        _serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r\n                    }\r\n                    _serviceStatus.dwServiceSpecificExitCode = hr;\r\n                }\r\n            }\r\n\r\n            StopAsync();\r\n        }\r\n\r\n    private:\r\n        static void SvcMain(DWORD, LPWSTR*)\r\n        {\r\n            TBase instance;\r\n            s_LastServiceMainHR = instance.RunServiceMain(true);\r\n        }\r\n\r\n        static DWORD WINAPI HandlerExStatic(_In_ DWORD dwControl, _In_ DWORD dwEventType, _In_ LPVOID lpEventData, _In_ LPVOID lpContext)\r\n        {\r\n            Service* self = reinterpret_cast<Service*>(lpContext);\r\n            return self->HandlerEx(dwControl, dwEventType, lpEventData);\r\n        }\r\n\r\n        DWORD WINAPI HandlerEx(_In_ DWORD dwControl, _In_ DWORD dwEventType, _In_ LPVOID lpEventData)\r\n        {\r\n            // Unreferenced when OnLowPowerModeChanged isn't defined, but this won't hurt.\r\n            UNREFERENCED_PARAMETER(dwEventType);\r\n            UNREFERENCED_PARAMETER(lpEventData);\r\n\r\n            DWORD dwResult = ERROR_CALL_NOT_IMPLEMENTED;\r\n\r\n            __if_exists(TBase::OnHandlerEx)\r\n            {\r\n                dwResult = reinterpret_cast<TBase*>(this)->OnHandlerEx(dwControl, dwEventType, lpEventData);\r\n            }\r\n\r\n            // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683241(v=vs.85).aspx for codes.\r\n            if (dwControl == SERVICE_CONTROL_STOP)\r\n            {\r\n                StopAsync();\r\n            }\r\n\r\n            // Provide first-class support for lower power mode when OnLowPowerModeChanged is available.\r\n            // Additional support can be implemented by overriding OnHandlerEx.\r\n            __if_exists(TBase::OnLowPowerModeChanged)\r\n            {\r\n                if (dwControl == SERVICE_CONTROL_POWEREVENT)\r\n                {\r\n                    PPOWERBROADCAST_SETTING powerSetting;\r\n                    switch (dwEventType)\r\n                    {\r\n                    case PBT_POWERSETTINGCHANGE:\r\n                        powerSetting = static_cast<PPOWERBROADCAST_SETTING>(lpEventData);\r\n                        if (!memcmp(&powerSetting->PowerSetting, &GUID_LOW_POWER_EPOCH_PRV, sizeof(powerSetting->PowerSetting)) &&\r\n                            powerSetting->DataLength == sizeof(ULONG))\r\n                        {\r\n                            switch (*reinterpret_cast<ULONG*>(powerSetting->Data))\r\n                            {\r\n                            case 0:\r\n                                // Exiting lower power mode change.\r\n                                reinterpret_cast<TBase*>(this)->OnLowPowerModeChanged(false);\r\n                                dwResult = NO_ERROR;\r\n                                break;\r\n\r\n                            case 1:\r\n                                // Entering lower power mode change.\r\n                                reinterpret_cast<TBase*>(this)->OnLowPowerModeChanged(true);\r\n                                dwResult = NO_ERROR;\r\n                                break;\r\n                            }\r\n                        }\r\n                        break;\r\n                    case PBT_APMPOWERSTATUSCHANGE:\r\n                    case PBT_APMRESUMEAUTOMATIC:\r\n                    case PBT_APMSUSPEND:\r\n                    default:\r\n                        break;\r\n                    }\r\n                }\r\n            }\r\n\r\n            __if_exists(TBase::OnSessionChanged)\r\n            {\r\n                if (dwControl == SERVICE_CONTROL_SESSIONCHANGE)\r\n                {\r\n                    PWTSSESSION_NOTIFICATION sessionNotification;\r\n                    sessionNotification = static_cast<PWTSSESSION_NOTIFICATION>(lpEventData);\r\n                    reinterpret_cast<TBase*>(this)->OnSessionChanged(dwEventType, sessionNotification->dwSessionId);\r\n                    dwResult = NO_ERROR;\r\n                }\r\n            }\r\n\r\n            // Provide first-class support for system shutdown when OnSystemShutdown is available.\r\n            __if_exists(TBase::OnSystemShutdown)\r\n            {\r\n                if (dwControl == SERVICE_CONTROL_SHUTDOWN)\r\n                {\r\n                    //\r\n                    // If a service accepts this control code, it must stop\r\n                    // after it performs its cleanup tasks and return NO_ERROR.\r\n                    // After the SCM sends this control code, it will not send other\r\n                    // control codes to the service.\r\n                    //\r\n                    // We stop asynchronously to have the same codepath as system\r\n                    // stop requests.\r\n                    //\r\n                    reinterpret_cast<TBase*>(this)->OnSystemShutdown();\r\n                    StopAsync();\r\n                    dwResult = NO_ERROR;\r\n                }\r\n            }\r\n\r\n            return (dwControl == SERVICE_CONTROL_STOP || dwControl == SERVICE_CONTROL_INTERROGATE) ? NO_ERROR : dwResult;\r\n        }\r\n\r\n        typedef void (Service::*CallbackFn)();\r\n\r\n        template <LastObjectReleaseBehavior>\r\n        CallbackFn GetModuleCallback();\r\n\r\n        template <>\r\n        CallbackFn GetModuleCallback<ShutdownAfterLastObjectReleased>()\r\n        {\r\n            return &Service::StopAsync;\r\n        }\r\n\r\n        template <>\r\n        CallbackFn GetModuleCallback<ContinueRunningWithNoObjects>()\r\n        {\r\n            return &Service::DummyNoOpCallback;\r\n        }\r\n\r\n        void DummyNoOpCallback()\r\n        {\r\n        }\r\n\r\n        // HRESULT of the last service main call. Only used when ProcessMain is called.\r\n        static HRESULT s_LastServiceMainHR;\r\n\r\n        // Don't force all callers to adjust project include paths for a single constant.\r\n        static const GUID GUID_LOW_POWER_EPOCH_PRV;\r\n\r\n        // A handle to the power registration for low power epoch.\r\n        HPOWERNOTIFY _powerHandle = nullptr;\r\n\r\n        // Handle to identify this service instance.\r\n        SERVICE_STATUS_HANDLE _serviceStatusHandle = nullptr;\r\n\r\n        // Structure used to report service status updates.\r\n        SERVICE_STATUS _serviceStatus{};\r\n\r\n        // Event object to signal that the service should stop.\r\n        HANDLE _stopEvent = nullptr;\r\n    };\r\n\r\n    template <typename TBase, LastObjectReleaseBehavior TLastObjectReleaseBehavior, typename TSecurityPolicy, GLOBALOPT_EH_VALUES TExceptionPolicy, typename TServerDescriptor>\r\n    __declspec(selectany) HRESULT Service<TBase, TLastObjectReleaseBehavior, TSecurityPolicy, TExceptionPolicy, TServerDescriptor>::s_LastServiceMainHR;\r\n\r\n    template <typename TBase, LastObjectReleaseBehavior TLastObjectReleaseBehavior, typename TSecurityPolicy, GLOBALOPT_EH_VALUES TExceptionPolicy, typename TServerDescriptor>\r\n    __declspec(selectany)\r\n    const GUID Service<TBase, TLastObjectReleaseBehavior, TSecurityPolicy, TExceptionPolicy, TServerDescriptor>::GUID_LOW_POWER_EPOCH_PRV = {\r\n        0xe1233993, 0xeaa4, 0x470f, {0x9d, 0xe7, 0xa3, 0x51, 0xc1, 0xb6, 0xfb, 0x71}};\r\n\r\n}} // namespace Windows::Internal\r\n"
  },
  {
    "path": "src/windows/inc/lxssbusclient.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    lxssbusclient.h\n\nAbstract:\n\n    This header file contains data structures and prototypes for the LxBus\n    client library.\n\n--*/\n\n#pragma once\n\nNTSTATUS\nLxBusClientCreateUnnamedServer(_In_ HANDLE MessagePortHandle, _Out_ PLXBUS_IPC_MESSAGE_CREATE_UNNAMED_SERVER_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientDisconnectConsole(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_DISCONNECT_CONSOLE_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientpIoctl(\n    _In_ HANDLE Handle,\n    _In_ ULONG ControlCode,\n    _In_reads_bytes_(InputBufferSize) PVOID InputBuffer,\n    _In_ ULONG InputBufferSize,\n    _Out_writes_bytes_(OutputBufferSize) PVOID OutputBuffer,\n    _In_ ULONG OutputBufferSize);\n\nNTSTATUS\nLxBusClientMarshalConsole(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_MARSHAL_CONSOLE_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientMarshalForkToken(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_MARSHAL_FORK_TOKEN_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientMarshalHandle(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_MARSHAL_HANDLE_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientMarshalProcess(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_MARSHAL_PROCESS_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientReceiveMessage(_In_ HANDLE MessagePortHandle, _Out_writes_to_(BufferSize, *SizeReceived) PVOID Buffer, _In_ ULONG BufferSize, _Out_ PULONG SizeReceived);\n\nNTSTATUS\nLxBusClientReceiveMessageAsync(\n    _In_ HANDLE MessagePortHandle,\n    _Out_writes_bytes_to_(BufferSize, *SizeReceived) PVOID Buffer,\n    _In_ ULONG BufferSize,\n    _Out_ PULONG SizeReceived,\n    _Out_ PIO_STATUS_BLOCK IoStatus,\n    _Inout_opt_ HANDLE Event);\n\nNTSTATUS\nLxBusClientRegisterServer(_In_ HANDLE LxBusHandle, _Inout_ PLXBUS_REGISTER_SERVER_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientRegisterUserCallbackAsync(\n    _In_ HANDLE MessagePortHandle,\n    _In_ HANDLE Event,\n    _Inout_ PIO_STATUS_BLOCK IoStatus,\n    _Inout_ PLXBUS_REGISTER_USER_CALLBACK_PARAMETERS Parameters,\n    _Out_ PVOID OutputBuffer,\n    _In_ ULONG OutputBufferSize);\n\nNTSTATUS\nLxBusClientReleaseConsole(_In_ HANDLE MessagePortHandle, _In_ PLXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientReleaseForkToken(_In_ HANDLE MessagePortHandle, _In_ PLXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientReleaseHandle(_In_ HANDLE MessagePortHandle, _In_ PLXBUS_IPC_MESSAGE_IOCTL_CANCEL_MARSHAL_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientSendMessage(_In_ HANDLE MessagePortHandle, _In_reads_bytes_(BufferSize) PVOID Buffer, _In_ ULONG BufferSize);\n\nNTSTATUS\nLxBusClientSendMessageAsync(\n    _In_ HANDLE MessagePortHandle, _In_reads_bytes_(BufferSize) PVOID Buffer, _In_ ULONG BufferSize, _Out_ PIO_STATUS_BLOCK IoStatus, _Inout_opt_ HANDLE Event);\n\nNTSTATUS\nLxBusClientUnmarshalProcess(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_UNMARSHAL_PROCESS_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientUnmarshalVfsFile(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_IPC_MESSAGE_UNMARSHAL_VFS_FILE_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientUserCallbackSendResponse(_In_ HANDLE MessagePortHandle, _Inout_ PLXBUS_REGISTER_USER_CALLBACK_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientWaitForConnection(_In_ HANDLE ServerPortHandle, _Out_ PLXBUS_IPC_SERVER_WAIT_FOR_CONNECTION_PARAMETERS Parameters);\n\nNTSTATUS\nLxBusClientWaitForLxProcess(_In_ HANDLE LxProcessHandle, _Out_ PLXBUS_IPC_LX_PROCESS_WAIT_FOR_TERMINATION_PARAMETERS Parameters);\n"
  },
  {
    "path": "src/windows/inc/lxssclient.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    lxssclient.h\n\nAbstract:\n\n    This header file contains data structures and prototypes for the LXSS\n    client library.\n\n--*/\n\n#pragma once\n\nNTSTATUS\nLxssClientInitialize(VOID);\n\nNTSTATUS\nLxssClientInstanceCreate(_In_ PLX_KINSTANCECREATESTART Parameters, _Out_ PHANDLE InstanceHandle);\n\nNTSTATUS\nLxssClientInstanceDestroy(_In_ HANDLE InstanceHandle);\n\nNTSTATUS\nLxssClientInstanceGetExitStatus(_In_ HANDLE InstanceHandle, _Out_ PLONG ExitStatus);\n\nNTSTATUS\nLxssClientInstanceStart(_In_ HANDLE InstanceHandle, _In_ HANDLE ParentProcess);\n\nNTSTATUS\nLxssClientInstanceStop(_In_ HANDLE InstanceHandle);\n\nNTSTATUS\nLxssClientMapPath(\n    _In_ HANDLE InstanceHandle,\n    _In_ HANDLE WindowsDataRoot,\n    _In_ LPCSTR Source,\n    _In_ LPCSTR Target,\n    _In_ LPCSTR FsType,\n    _In_ ULONG MountFlags,\n    _In_ ULONG Uid,\n    _In_ ULONG Gid,\n    _In_ ULONG Mode);\n\nVOID LxssClientUninitialize(VOID);\n\nNTSTATUS\nLxssClientUnmapPath(_In_ HANDLE InstanceHandle, _In_ LPCSTR MountPath);\n"
  },
  {
    "path": "src/windows/inc/traceloggingconfig.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    traceloggingconfig.h\n\nAbstract:\n\n    This file contains constants for ETL logging.\n\n--*/\n\n#pragma once\n\n#include \"defs.h\"\n\nstatic_assert(!wsl::shared::OfficialBuild);\n\n#define MICROSOFT_KEYWORD_TELEMETRY 0\n#define MICROSOFT_KEYWORD_MEASURES 0\n#define MICROSOFT_KEYWORD_CRITICAL_DATA 0\n#define PDT_ProductAndServicePerformance 0\n#define PDT_ProductAndServiceUsage 0\n#define TelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), \"PartA_PrivTags\")\n#define TraceLoggingOptionMicrosoftTelemetry() \\\n    TraceLoggingOptionGroup(0x9aa7a361, 0x583f, 0x4c09, 0xb1, 0xf1, 0xce, 0xa1, 0xef, 0x58, 0x63, 0xb0)"
  },
  {
    "path": "src/windows/inc/wdk.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wdk.h\n\nAbstract:\n\n    This file contains various definitions that are needed for WSL to build.\n\n--*/\n\n#pragma once\n\n#include <winternl.h>\n#include <computedefs.h>\n#include <winnt.h>\n#include <windns.h>\n\n#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)\n#define STATUS_NO_SUCH_DEVICE ((NTSTATUS)0xC000000EL)\n#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)\n#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)\n#define STATUS_DEVICE_NOT_CONNECTED ((NTSTATUS)0xC000009DL)\n#define STATUS_DIRECTORY_NOT_EMPTY ((NTSTATUS)0xC0000101L)\n#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)\n#define STATUS_NOT_A_DIRECTORY ((NTSTATUS)0xC0000103L)\n#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)\n#define STATUS_REDIRECTOR_STARTED ((NTSTATUS)0xC00000FCL)\n#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)\n#define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS)0xC0000035L)\n#define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS)0xC000003AL)\n#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L)\n#define STATUS_CANCELLED ((NTSTATUS)0xC0000120L)\n#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)\n#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)\n#define STATUS_NO_SUCH_FILE ((NTSTATUS)0xC000000FL)\n#define STATUS_SHUTDOWN_IN_PROGRESS ((NTSTATUS)0xC00002FEL)\n\n#define IOCTL_DISK_ARE_VOLUMES_READY CTL_CODE(IOCTL_DISK_BASE, 0x0087, METHOD_BUFFERED, FILE_READ_ACCESS)\n#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF)\n\n#define JobObjectTimerVirtualizationInformation ((JOBOBJECTINFOCLASS)23) // TODO: Undocumented.\n#define ThreadExplicitCaseSensitivity ((THREADINFOCLASS)43)              // TODO: Undocumented.\n#define FileAttributeTagInformation ((FILE_INFORMATION_CLASS)35)\n#define FileStatLxInformation ((FILE_INFORMATION_CLASS)70)\n#define FileCaseSensitiveInformation ((FILE_INFORMATION_CLASS)71)\n#define FileFullDirectoryInformation ((FILE_INFORMATION_CLASS)2)\n#define FileStatInformation ((FILE_INFORMATION_CLASS)68)\n\n#define IO_REPARSE_TAG_LX_SYMLINK (0xA000001D)\n\n#define ARGUMENT_PRESENT(x) (((x) != NULL))\n#define MAXULONG ULONG_MAX\n\n// Note: These flags are documented but not in the SDK.\n// See: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_stat_lx_information\n#define LX_FILE_METADATA_HAS_UID 0x1\n#define LX_FILE_METADATA_HAS_GID 0x2\n#define LX_FILE_METADATA_HAS_MODE 0x4\n\n// Note: This flag is documented but not in the SDK.\n// See: https://learn.microsoft.com/en-us/windows/console/readconsoleinputex\n#define CONSOLE_READ_NOWAIT 0x0002\n\n// Note: This flag is already published in: https://github.com/microsoft/terminal/blob/main/dep/Console/condrv.h\n#define IOCTL_CONDRV_GET_SERVER_PID CTL_CODE(FILE_DEVICE_CONSOLE, 8, METHOD_NEITHER, FILE_ANY_ACCESS)\n\n#define VM_E_INVALID_STATE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x1001) // TODO: Undocumented.\n\n// {FC36C5C6-7A87-4841-A47A-1D352987055B}\nDEFINE_GUID(VIRTIO_PLAN9_DEVICE_ID, 0xFC36C5C6, 0x7A87, 0x4841, 0xA4, 0x7A, 0x1D, 0x35, 0x29, 0x87, 0x05, 0x5B); // TODO: Undocumented.\n\n// {a8679153-843f-467f-ad7e-f429328f7568}\nDEFINE_GUID(FLEXIO_DEVICE_ID, 0xa8679153, 0x843f, 0x467f, 0xad, 0x7e, 0xf4, 0x29, 0x32, 0x8f, 0x75, 0x68); // TODO: Undocumented.\n\ntypedef struct _KEY_FLAGS_INFORMATION // TODO: Undocumented.\n{\n    ULONG UserFlags;\n    ULONG KeyFlags; // LSB bit set --> Key is Volatile\n    // second to LSB bit set --> Key is symlink\n\n    ULONG ControlFlags; // combination of the above\n} KEY_FLAGS_INFORMATION, *PKEY_FLAGS_INFORMATION;\n\ntypedef enum _EVENT_TYPE\n{\n    NotificationEvent,\n    SynchronizationEvent\n} EVENT_TYPE;\n\ntypedef IO_STATUS_BLOCK* PIO_STATUS_BLOCK;\n\n#define RtlEqualLuid(L1, L2) (((L1)->LowPart == (L2)->LowPart) && ((L1)->HighPart == (L2)->HighPart))\n\ntypedef enum _FSINFOCLASS\n{\n    FileFsDeviceInformation = 4,\n    FileIdBothDirectoryInformation = 37\n} FS_INFORMATION_CLASS,\n    *PFS_INFORMATION_CLASS;\n\ntypedef struct _FILE_FS_DEVICE_INFORMATION\n{                                                           // ntddk nthal\n    DEVICE_TYPE DeviceType;                                 // ntddk nthal\n    ULONG Characteristics;                                  // ntddk nthal\n} FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION; // ntddk nthal\n\ntypedef struct _REPARSE_DATA_BUFFER\n{\n    ULONG ReparseTag;\n    USHORT ReparseDataLength;\n    USHORT Reserved;\n    union\n    {\n        struct\n        {\n            USHORT SubstituteNameOffset;\n            USHORT SubstituteNameLength;\n            USHORT PrintNameOffset;\n            USHORT PrintNameLength;\n            ULONG Flags;\n            WCHAR PathBuffer[1];\n        } SymbolicLinkReparseBuffer;\n        struct\n        {\n            USHORT SubstituteNameOffset;\n            USHORT SubstituteNameLength;\n            USHORT PrintNameOffset;\n            USHORT PrintNameLength;\n            WCHAR PathBuffer[1];\n        } MountPointReparseBuffer;\n        struct\n        {\n            UCHAR DataBuffer[1];\n        } GenericReparseBuffer;\n    } DUMMYUNIONNAME;\n} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;\n\ntypedef struct _FILE_ATTRIBUTE_TAG_INFORMATION\n{\n    ULONG FileAttributes;\n    ULONG ReparseTag;\n} FILE_ATTRIBUTE_TAG_INFORMATION, *PFILE_ATTRIBUTE_TAG_INFORMATION;\n\n#define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)\n\ntypedef struct _FILE_GET_EA_INFORMATION\n{\n    ULONG NextEntryOffset;\n    UCHAR EaNameLength;\n    CHAR EaName[1];\n} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION;\n\ntypedef struct _FILE_FULL_EA_INFORMATION\n{\n    ULONG NextEntryOffset;\n    UCHAR Flags;\n    UCHAR EaNameLength;\n    USHORT EaValueLength;\n    CHAR EaName[1];\n} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;\n\ntypedef struct _FILE_ID_BOTH_DIR_INFORMATION\n{\n    ULONG NextEntryOffset;\n    ULONG FileIndex;\n    LARGE_INTEGER CreationTime;\n    LARGE_INTEGER LastAccessTime;\n    LARGE_INTEGER LastWriteTime;\n    LARGE_INTEGER ChangeTime;\n    LARGE_INTEGER EndOfFile;\n    LARGE_INTEGER AllocationSize;\n    ULONG FileAttributes;\n    ULONG FileNameLength;\n    ULONG EaSize;\n    CCHAR ShortNameLength;\n    WCHAR ShortName[12];\n    LARGE_INTEGER FileId;\n    WCHAR FileName[1];\n} FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nBOOL WINAPI ReadConsoleInputExW(\n    _In_ HANDLE hConsoleInput, _Out_writes_(nLength) PINPUT_RECORD lpBuffer, _In_ DWORD nLength, _Out_ LPDWORD lpNumberOfEventsRead, _In_ USHORT wFlags);\n\nNTSTATUS WINAPI NtCancelIoFileEx(__in HANDLE FileHandle, __in_opt PIO_STATUS_BLOCK IoRequestToCancel, __out PIO_STATUS_BLOCK IoStatusBlock);\n\nNTSTATUS\nNtCreateNamedPipeFile(\n    __out PHANDLE FileHandle,\n    __in ULONG DesiredAccess,\n    __in POBJECT_ATTRIBUTES ObjectAttributes,\n    __out PIO_STATUS_BLOCK IoStatusBlock,\n    __in ULONG ShareAccess,\n    __in ULONG CreateDisposition,\n    __in ULONG CreateOptions,\n    __in ULONG NamedPipeType,\n    __in ULONG ReadMode,\n    __in ULONG CompletionMode,\n    __in ULONG MaximumInstances,\n    __in ULONG InboundQuota,\n    __in ULONG OutboundQuota,\n    __in_opt PLARGE_INTEGER DefaultTimeout);\n\nNTSTATUS\nNTSYSCALLAPI\nNtFsControlFile(\n    __in HANDLE FileHandle,\n    __in_opt HANDLE Event,\n    __in_opt PIO_APC_ROUTINE ApcRoutine,\n    __in_opt PVOID ApcContext,\n    __out PIO_STATUS_BLOCK IoStatusBlock,\n    __in ULONG IoControlCode,\n    __in_bcount_opt(InputBufferLength) PVOID InputBuffer,\n    __in ULONG InputBufferLength,\n    __out_bcount_opt(OutputBufferLength) PVOID OutputBuffer,\n    __in ULONG OutputBufferLength);\n\nNTSTATUS\nNTSYSCALLAPI\nNtQueryInformationByName(\n    __in POBJECT_ATTRIBUTES ObjectAttributes,\n    __out PIO_STATUS_BLOCK IoStatusBlock,\n    __out_bcount(Length) PVOID FileInformation,\n    __in ULONG Length,\n    __in FILE_INFORMATION_CLASS FileInformationClass);\n\nNTSTATUS\nNTSYSCALLAPI\nNtQueryVolumeInformationFile(\n    __in HANDLE FileHandle, __out PIO_STATUS_BLOCK IoStatusBlock, __out_bcount(Length) PVOID FsInformation, __in ULONG Length, __in FS_INFORMATION_CLASS FsInformationClass);\n\nNTSTATUS RtlDosPathNameToNtPathName_U_WithStatus(\n    __in PCWSTR DosFileName, __out PUNICODE_STRING NtFileName, __deref_opt_out_opt PWSTR* FilePart, __reserved PVOID Reserved);\n\nNTSTATUS\nNTSYSAPI\nRtlInitializeSidEx(__out_bcount(SECURITY_SID_SIZE(SubAuthorityCount)) PSID Sid, __in PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, __in UCHAR SubAuthorityCount, ...);\n\nNTSTATUS\nNTSYSAPI\nZwCreateDirectoryObject(__out PHANDLE DirectoryHandle, __in ACCESS_MASK DesiredAccess, __in POBJECT_ATTRIBUTES ObjectAttributes);\n\nNTSTATUS\nWINAPI\nNtOpenDirectoryObject(__out PHANDLE DirectoryHandle, __in ACCESS_MASK DesiredAccess, __in POBJECT_ATTRIBUTES ObjectAttributes);\n\nNTSTATUS\nNTSYSCALLAPI\nNtQueryInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass);\n\nNTSTATUS NTSYSCALLAPI NtSetInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass);\n\nNTSTATUS\nZwQueryEaFile(\n    __in HANDLE FileHandle,\n    __out PIO_STATUS_BLOCK IoStatusBlock,\n    __out_bcount(Length) PVOID Buffer,\n    __in ULONG Length,\n    __in BOOLEAN ReturnSingleEntry,\n    __in_bcount_opt(EaListLength) PVOID EaList,\n    __in ULONG EaListLength,\n    __in_opt PULONG EaIndex,\n    __in BOOLEAN RestartScan);\n\nNTSTATUS\nNTSYSAPI\nZwCreateEvent(__out PHANDLE EventHandle, __in ACCESS_MASK DesiredAccess, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in EVENT_TYPE EventType, __in BOOLEAN InitialState);\n\nNTSTATUS\nNtReadFile(\n    __in HANDLE FileHandle,\n    __in_opt HANDLE Event,\n    __in_opt PIO_APC_ROUTINE ApcRoutine,\n    __in_opt PVOID ApcContext,\n    __out PIO_STATUS_BLOCK IoStatusBlock,\n    __out PVOID Buffer,\n    __in ULONG Length,\n    __in_opt PLARGE_INTEGER ByteOffset,\n    __in_opt PULONG Key);\n\nNTSTATUS\nNTSYSCALLAPI\nNtWriteFile(\n    __in HANDLE FileHandle,\n    __in_opt HANDLE Event,\n    __in_opt PIO_APC_ROUTINE ApcRoutine,\n    __in_opt PVOID ApcContext,\n    __out PIO_STATUS_BLOCK IoStatusBlock,\n    __in PVOID Buffer,\n    __in ULONG Length,\n    __in_opt PLARGE_INTEGER ByteOffset,\n    __in_opt PULONG Key);\n\nNTSTATUS\nNTSYSCALLAPI\nNtQueryDirectoryFile(\n    HANDLE FileHandle,\n    HANDLE Event,\n    PIO_APC_ROUTINE ApcRoutine,\n    PVOID ApcContext,\n    PIO_STATUS_BLOCK IoStatusBlock,\n    PVOID FileInformation,\n    ULONG Length,\n    FILE_INFORMATION_CLASS FileInformationClass,\n    BOOLEAN ReturnSingleEntry,\n    PUNICODE_STRING FileName,\n    BOOLEAN RestartScan);\n\nNTSTATUS ZwSetEaFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length);\n\n// These extended attributes are defined in ntioapi_x.h in the OS repo and are not present in the SDK.\n#define LX_FILE_METADATA_UID_EA_NAME \"$LXUID\"\n#define LX_FILE_METADATA_GID_EA_NAME \"$LXGID\"\n#define LX_FILE_METADATA_MODE_EA_NAME \"$LXMOD\"\n#define LX_FILE_METADATA_DEVICE_ID_EA_NAME \"$LXDEV\"\n\n#define SYMLINK_FLAG_RELATIVE 0x00000001 // If set then this is a relative symlink.\n\n#ifdef _AMD64_\n\ntypedef struct _HV_X64_HYPERVISOR_HARDWARE_FEATURES\n{\n    //\n    // Eax\n    //\n    UINT32 ApicOverlayAssistInUse : 1;\n    UINT32 MsrBitmapsInUse : 1;\n    UINT32 ArchitecturalPerformanceCountersInUse : 1;\n    UINT32 SecondLevelAddressTranslationInUse : 1;\n    UINT32 DmaRemappingInUse : 1;\n    UINT32 InterruptRemappingInUse : 1;\n    UINT32 MemoryPatrolScrubberPresent : 1;\n    UINT32 DmaProtectionInUse : 1;\n    UINT32 HpetRequested : 1;\n    UINT32 SyntheticTimersVolatile : 1;\n    UINT32 HypervisorLevel : 4;\n    UINT32 PhysicalDestinationModeRequired : 1;\n    UINT32 UseVmfuncForAliasMapSwitch : 1;\n    UINT32 HvRegisterForMemoryZeroingSupported : 1;\n    UINT32 UnrestrictedGuestSupported : 1;\n    UINT32 RdtAFeaturesSupported : 1;\n    UINT32 RdtMFeaturesSupported : 1;\n    UINT32 ChildPerfmonPmuSupported : 1;\n    UINT32 ChildPerfmonLbrSupported : 1;\n    UINT32 ChildPerfmonIptSupported : 1;\n    UINT32 ApicEmulationSupported : 1;\n    UINT32 ChildX2ApicRecommended : 1;\n    UINT32 HardwareWatchdogReserved : 1;\n    UINT32 DeviceAccessTrackingSupported : 1;\n    UINT32 Reserved : 5;\n\n    //\n    // Ebx\n    //\n    UINT32 DeviceDomainInputWidth : 8;\n    UINT32 ReservedEbx : 24;\n\n    //\n    // Ecx\n    //\n    UINT32 ReservedEcx;\n\n    //\n    // Edx\n    //\n    UINT32 ReservedEdx;\n\n} HV_X64_HYPERVISOR_HARDWARE_FEATURES, *PHV_X64_HYPERVISOR_HARDWARE_FEATURES;\n\n#define HvCpuIdFunctionMsHvHardwareFeatures 0x40000006\n\n#endif\n\ntypedef enum _KEY_INFORMATION_CLASS\n{\n    KeyBasicInformation,\n    KeyNodeInformation,\n    KeyFullInformation,\n    KeyNameInformation,\n    KeyCachedInformation,\n    KeyFlagsInformation,\n    KeyVirtualizationInformation,\n    KeyHandleTagsInformation,\n    KeyTrustInformation,\n    KeyLayerInformation,\n    MaxKeyInfoClass\n} KEY_INFORMATION_CLASS;\n\ntypedef struct _KEY_NAME_INFORMATION\n{\n    ULONG NameLength;\n    WCHAR Name[1];\n} KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION;\n\nNTSYSAPI NTSTATUS ZwQueryKey(HANDLE KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength);\n\nSTDAPI\nGetVmWorkerProcess(_In_ REFGUID VirtualMachineId, _In_ REFIID ObjectIid, _Outptr_opt_ IUnknown** Object);\n\nHRESULT\nWINAPI\nHdvProxyDeviceHost(HCS_SYSTEM ComputeSystem, _In_ PVOID DeviceHost_IUnknown, DWORD TargetProcessId, _Out_ UINT64* IpcSectionHandle);\n\n#ifdef __cplusplus\n}\n\n// List of methods that shouldn't be used because they aren't available on older builds that WSL supports\n\ntemplate <typename T>\nT BreakBuild()\n{\n    static_assert(false, \"Do not use GetTempPath2W(). (Not available on vb)\");\n    return T{};\n}\n\n#define GetTempPath2W(...) (BreakBuild<bool>())\n\n#endif\n"
  },
  {
    "path": "src/windows/inc/wsl.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wsl.h\n\nAbstract:\n\n    This file contains command line arguments for wsl.exe.\n\n--*/\n\n#pragma once\n\n#define WSL_BINARY_NAME L\"wsl.exe\"\n#define WSL_CHANGE_DIRECTORY_ARG L\"--cd\"\n#define WSL_CWD_HOME L\"~\"\n#define WSL_DEBUG_SHELL_ARG_LONG L\"--debug-shell\"\n#define WSL_DISTRO_ARG L\"-d\"\n#define WSL_DISTRO_ARG_LONG L\"--distribution\"\n#define WSL_DISTRIBUTION_ID_ARG L\"--distribution-id\"\n#define WSL_EXEC_ARG L\"-e\"\n#define WSL_EXEC_ARG_LONG L\"--exec\"\n#define WSL_EXPORT_ARG L\"--export\"\n#define WSL_EXPORT_ARG_STDOUT L\"-\"\n#define WSL_EXPORT_ARG_FORMAT_OPTION L\"--format\"\n#define WSL_EXPORT_ARG_VHD_OPTION L\"--vhd\"\n#define WSL_HELP_ARG L\"--help\"\n#define WSL_IMPORT_ARG L\"--import\"\n#define WSL_IMPORT_ARG_STDIN L\"-\"\n#define WSL_IMPORT_ARG_VERSION L\"--version\"\n#define WSL_IMPORT_ARG_VHD L\"--vhd\"\n#define WSL_IMPORT_INPLACE_ARG L\"--import-in-place\"\n#define WSL_INSTALL_ARG L\"--install\"\n#define WSL_INSTALL_ARG_DIST_OPTION L'd'\n#define WSL_INSTALL_ARG_DIST_OPTION_LONG L\"--distribution\"\n#define WSL_INSTALL_ARG_ENABLE_WSL1_LONG L\"--enable-wsl1\"\n#define WSL_INSTALL_ARG_FIXED_VHD L\"--fixed-vhd\"\n#define WSL_INSTALL_ARG_FROM_FILE_OPTION L'f'\n#define WSL_INSTALL_ARG_FROM_FILE_LONG L\"--from-file\"\n#define WSL_INSTALL_ARG_LEGACY_LONG L\"--legacy\"\n#define WSL_INSTALL_ARG_LOCATION_OPTION L'l'\n#define WSL_INSTALL_ARG_LOCATION_LONG L\"--location\"\n#define WSL_INSTALL_ARG_NAME_LONG L\"--name\"\n#define WSL_INSTALL_ARG_NO_LAUNCH_OPTION L'n'\n#define WSL_INSTALL_ARG_NO_DISTRIBUTION_OPTION L\"--no-distribution\"\n#define WSL_INSTALL_ARG_NO_LAUNCH_OPTION_LONG L\"--no-launch\"\n#define WSL_INSTALL_ARG_VHD_SIZE L\"--vhd-size\"\n#define WSL_INSTALL_ARG_VERSION L\"--version\"\n#define WSL_INSTALL_ARG_WEB_DOWNLOAD_LONG L\"--web-download\"\n#define WSL_INSTALL_ARG_PRERELEASE_LONG L\"--pre-release\"\n#define WSL_INSTALL_ARG_PROMPT_BEFORE_EXIT_OPTION L\"--prompt-before-exit\"\n#define WSL_LIST_ARG L\"-l\"\n#define WSL_LIST_ARG_LONG L\"--list\"\n#define WSL_LIST_ARG_ALL_OPTION L\"--all\"\n#define WSL_LIST_ARG_ONLINE_OPTION L'o'\n#define WSL_LIST_ARG_ONLINE_OPTION_LONG L\"--online\"\n#define WSL_LIST_ARG_QUIET_OPTION L'q'\n#define WSL_LIST_ARG_QUIET_OPTION_LONG L\"--quiet\"\n#define WSL_LIST_ARG_RUNNING_OPTION L\"--running\"\n#define WSL_LIST_ARG_VERBOSE_OPTION L'v'\n#define WSL_LIST_ARG_VERBOSE_OPTION_LONG L\"--verbose\"\n#define WSL_LIST_HEADER_FRIENDLY_NAME L\"FRIENDLY NAME\"\n#define WSL_LIST_HEADER_NAME L\"NAME\"\n#define WSL_LIST_HEADER_STATE L\"STATE\"\n#define WSL_LIST_HEADER_VERSION L\"VERSION\"\n#define WSL_MANAGE_ARG L\"--manage\"\n#define WSL_MANAGE_ARG_ALLOW_UNSAFE L\"--allow-unsafe\"\n#define WSL_MANAGE_ARG_MOVE_OPTION L'm'\n#define WSL_MANAGE_ARG_MOVE_OPTION_LONG L\"--move\"\n#define WSL_MANAGE_ARG_RESIZE_OPTION L'r'\n#define WSL_MANAGE_ARG_RESIZE_OPTION_LONG L\"--resize\"\n#define WSL_MANAGE_ARG_SET_SPARSE_OPTION L's'\n#define WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG L\"--set-sparse\"\n#define WSL_MANAGE_ARG_SET_DEFAULT_USER_OPTION_LONG L\"--set-default-user\"\n#define WSL_MOUNT_ARG L\"--mount\"\n#define WSL_MOUNT_ARG_VHD_OPTION_LONG L\"--vhd\"\n#define WSL_MOUNT_ARG_BARE_OPTION_LONG L\"--bare\"\n#define WSL_MOUNT_ARG_NAME_OPTION_LONG L\"--name\"\n#define WSL_MOUNT_ARG_TYPE_OPTION_LONG L\"--type\"\n#define WSL_MOUNT_ARG_OPTIONS_OPTION_LONG L\"--options\"\n#define WSL_MOUNT_ARG_PARTITION_OPTION_LONG L\"--partition\"\n#define WSL_MOUNT_ARG_BARE_OPTION L'b'\n#define WSL_MOUNT_ARG_NAME_OPTION L'n'\n#define WSL_MOUNT_ARG_TYPE_OPTION L't'\n#define WSL_MOUNT_ARG_OPTIONS_OPTION L'o'\n#define WSL_MOUNT_ARG_PARTITION_OPTION L'p'\n#define WSL_PARENT_CONSOLE_ARG L\"--parent-console\"\n#define WSL_SET_DEFAULT_DISTRO_ARG L\"-s\"\n#define WSL_SET_DEFAULT_DISTRO_ARG_LEGACY L\"--setdefault\"\n#define WSL_SET_DEFAULT_DISTRO_ARG_LONG L\"--set-default\"\n#define WSL_SET_DEFAULT_VERSION_ARG L\"--set-default-version\"\n#define WSL_SET_VERSION_ARG L\"--set-version\"\n#define WSL_SHELL_OPTION_ARG L\"--shell-type\"\n#define WSL_SHELL_OPTION_ARG_LOGIN_OPTION L\"login\"\n#define WSL_SHELL_OPTION_ARG_NOSHELL_OPTION L\"none\"\n#define WSL_SHELL_OPTION_ARG_STANDARD_OPTION L\"standard\"\n#define WSL_SHUTDOWN_ARG L\"--shutdown\"\n#define WSL_SHUTDOWN_OPTION_FORCE L\"--force\"\n#define WSL_STATUS_ARG L\"--status\"\n#define WSL_STOP_PARSING_ARG L\"--\"\n#define WSL_SYSTEM_DISTRO_ARG L\"--system\"\n#define WSL_TERMINATE_ARG L\"-t\"\n#define WSL_TERMINATE_ARG_LONG L\"--terminate\"\n#define WSL_UNINSTALL_ARG L\"--uninstall\"\n#define WSL_UNMOUNT_ARG L\"--unmount\"\n#define WSL_UNREGISTER_ARG L\"--unregister\"\n#define WSL_UPDATE_ARG L\"--update\"\n#define WSL_UPDATE_ARG_CONFIRM_OPTION_LONG L\"--confirm\"\n#define WSL_UPDATE_ARG_PRE_RELEASE_OPTION_LONG L\"--pre-release\"\n#define WSL_UPDATE_ARG_PROMPT_OPTION_LONG L\"--prompt-before-exit\"\n#define WSL_UPDATE_ARG_WEB_DOWNLOAD_OPTION_LONG L\"--web-download\"\n#define WSL_USER_ARG L\"-u\"\n#define WSL_USER_ARG_LONG L\"--user\"\n#define WSL_VERSION_ARG L\"-v\"\n#define WSL_VERSION_ARG_LONG L\"--version\"\n"
  },
  {
    "path": "src/windows/inc/wslconfig.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslconfig.h\n\nAbstract:\n\n    Header file containing command line arguments for wslconfig.exe.\n\n--*/\n\n#pragma once\n\n#define WSLCONFIG_COMMAND_LIST L\"/list\"\n#define WSLCONFIG_COMMAND_LIST_ALL L\"/all\"\n#define WSLCONFIG_COMMAND_LIST_RUNNING L\"/running\"\n#define WSLCONFIG_COMMAND_LIST_SHORT L\"/l\"\n#define WSLCONFIG_COMMAND_SET_DEFAULT L\"/setdefault\"\n#define WSLCONFIG_COMMAND_SET_DEFAULT_SHORT L\"/s\"\n#define WSLCONFIG_COMMAND_TERMINATE L\"/terminate\"\n#define WSLCONFIG_COMMAND_TERMINATE_SHORT L\"/t\"\n#define WSLCONFIG_COMMAND_UNREGISTER_DISTRIBUTION L\"/unregister\"\n#define WSLCONFIG_COMMAND_UNREGISTER_DISTRIBUTION_SHORT L\"/u\""
  },
  {
    "path": "src/windows/inc/wslhost.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslhost.h\n\nAbstract:\n\n    Header file containing command line arguments for wslhost.exe.\n\n--*/\n\n#pragma once\n\nnamespace wslhost {\n\nLPCWSTR const binary_name = L\"wslhost.exe\";\n\n// Arguments for NotificationActivator::Activate entrypoint.\nLPCWSTR const disable_notification_arg = L\"--disable-notification\";\nLPCWSTR const docs_arg = L\"--docs\";\nLPCWSTR const docs_arg_filesystem_url = L\"https://aka.ms/wslfilesystemdocs\";\nLPCWSTR const event_viewer_arg = L\"--event-viewer\";\nLPCWSTR const install_prerequisites_arg = L\"--install-prerequisites\";\nLPCWSTR const release_notes_arg = L\"--release-notes\";\nLPCWSTR const update_arg = L\"--update\";\n\n// Arguments for wWinMain entrypoint.\nLPCWSTR const distro_id_option = L\"--distro-id\";\nLPCWSTR const handle_option = L\"--handle\";\nLPCWSTR const event_option = L\"--event\";\nLPCWSTR const parent_option = L\"--parent\";\nLPCWSTR const vm_id_option = L\"--vm-id\";\nLPCWSTR const embedding_option = L\"-Embedding\";\n} // namespace wslhost"
  },
  {
    "path": "src/windows/inc/wslpolicies.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslpolicies.h\n\nAbstract:\n\n    This file contains a helpers for querying WSL policies.\n\n--*/\n\n#pragma once\n\n#define ROOT_POLICIES_KEY L\"Software\\\\Policies\"\n\nnamespace wsl::windows::policies {\ninline constexpr auto c_registryKey = ROOT_POLICIES_KEY L\"\\\\WSL\";\ninline constexpr auto c_allowInboxWSL = L\"AllowInboxWSL\";\ninline constexpr auto c_allowWSL = L\"AllowWSL\";\ninline constexpr auto c_allowWSL1 = L\"AllowWSL1\";\ninline constexpr auto c_allowCustomKernelUserSetting = L\"AllowKernelUserSetting\";\ninline constexpr auto c_allowCustomSystemDistroUserSetting = L\"AllowSystemDistroUserSetting\";\ninline constexpr auto c_allowCustomKernelCommandLineUserSetting = L\"AllowKernelCommandLineUserSetting\";\ninline constexpr auto c_allowDebugShellUserSetting = L\"AllowDebugShell\";\ninline constexpr auto c_allowNestedVirtualizationUserSetting = L\"AllowNestedVirtualization\";\ninline constexpr auto c_allowKernelDebuggingUserSetting = L\"AllowKernelDebugUserSetting\";\ninline constexpr auto c_allowDiskMount = L\"AllowDiskMount\";\ninline constexpr auto c_allowCustomNetworkingModeUserSetting = L\"AllowNetworkingModeUserSetting\";\ninline constexpr auto c_allowCustomFirewallUserSetting = L\"AllowFirewallUserSetting\";\ninline constexpr auto c_defaultNetworkingMode = L\"DefaultNetworkingMode\";\n\ninline wil::unique_hkey CreatePoliciesKey(DWORD desiredAccess)\n{\n    wil::unique_hkey key;\n    LOG_IF_WIN32_ERROR(\n        RegCreateKeyExW(HKEY_LOCAL_MACHINE, c_registryKey, 0, nullptr, REG_OPTION_NON_VOLATILE, desiredAccess, nullptr, &key, nullptr));\n\n    return key;\n}\n\ninline std::optional<DWORD> GetPolicyValue(HKEY key, LPCWSTR name)\ntry\n{\n    if (key == nullptr)\n    {\n        return std::nullopt;\n    }\n\n    DWORD value = 0;\n    DWORD size = sizeof(value);\n    const LONG result = RegGetValueW(key, nullptr, name, RRF_RT_REG_DWORD, nullptr, &value, &size);\n    if (result == ERROR_PATH_NOT_FOUND || result == ERROR_FILE_NOT_FOUND)\n    {\n        return std::nullopt;\n    }\n\n    THROW_IF_WIN32_ERROR(result);\n\n    return value;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION_MSG(\"Error reading the policy value: %ls\", name);\n    return std::nullopt;\n}\n\ninline bool IsFeatureAllowed(HKEY key, LPCWSTR name)\ntry\n{\n    const auto policy = GetPolicyValue(key, name);\n    if (!policy.has_value())\n    {\n        return true;\n    }\n\n    const auto value = policy.value();\n    THROW_HR_IF_MSG(E_UNEXPECTED, value != 0 && value != 1, \"Invalid value for policy: %ls: %lu\", name, value);\n\n    return value == 1;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return true;\n}\n\ninline wil::unique_hkey OpenPoliciesKey()\n{\n    wil::unique_hkey key;\n    const auto result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_registryKey, 0, KEY_READ, &key);\n    if (result == ERROR_PATH_NOT_FOUND || result == ERROR_FILE_NOT_FOUND)\n    {\n        // N.B. Return an empty result if the registry key doesn't exist to make it easier\n        // to check for policies without having a special code path for this case.\n        return {};\n    }\n\n    LOG_IF_WIN32_ERROR(result);\n    return key;\n}\n\n} // namespace wsl::windows::policies"
  },
  {
    "path": "src/windows/inc/wslrelay.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslrelay.h\n\nAbstract:\n\n    Header file containing command line arguments for wslrelay.exe.\n\n--*/\n\n#pragma once\n\nnamespace wslrelay {\nenum RelayMode\n{\n    Invalid = -1,\n    DebugConsole,\n    PortRelay,\n    KdRelay\n};\n\nLPCWSTR const binary_name = L\"wslrelay.exe\";\nLPCWSTR const mode_option = L\"--mode\";\nLPCWSTR const handle_option = L\"--handle\";\nLPCWSTR const vm_id_option = L\"--vm-id\";\nLPCWSTR const pipe_option = L\"--pipe\";\nLPCWSTR const exit_event_option = L\"--exit-event\";\nLPCWSTR const port_option = L\"--port\";\nLPCWSTR const disable_telemetry_option = L\"--disable-telemetry\";\nLPCWSTR const connect_pipe_option = L\"--connect-pipe\";\n} // namespace wslrelay"
  },
  {
    "path": "src/windows/inc/wslversioninfo.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    wslversioninfo.h\n\nAbstract:\n\n    This file contains version definitions for WSL binaries.\n\n--*/\n\n#pragma once\n\n#define VER_FILEFLAGSMASK VS_FFI_FILEFLAGSMASK\n#define VER_FILEOS VOS_NT_WINDOWS32\n#define VER_PRODUCTNAME_STR \"Windows Subsystem for Linux\"\n#define VER_PRODUCTVERSION_STR WSL_PACKAGE_VERSION\n#define VER_FILEVERSION_STR WSL_PACKAGE_VERSION\n#define VER_COMPANYNAME_STR \"Microsoft Corporation\"\n#define VER_FILEVERSION WSL_PACKAGE_VERSION_MAJOR, WSL_PACKAGE_VERSION_MINOR, WSL_PACKAGE_VERSION_REVISION, 0\n\n#ifdef _WINDLL\n\n#define VER_FILETYPE VFT_DLL\n#define VER_FILESUBTYPE VFT2_UNKNOWN\n\n#else\n\n#define VER_FILETYPE VFT_APP\n#define VER_FILESUBTYPE VFT2_UNKNOWN\n\n#endif\n\n#ifdef DEBUG\n\n#define VER_FILEFLAGS VS_FF_DEBUG | VS_FF_PRIVATEBUILD\n\n#else\n\n#ifdef WSL_OFFICIAL_BUILD\n\n#define VER_FILEFLAGS 0\n\n#else\n\n#define VER_FILEFLAGS VS_FF_PRIVATEBUILD\n\n#endif\n\n#endif\n"
  },
  {
    "path": "src/windows/libwsl/CMakeLists.txt",
    "content": "set(SOURCES\n    DllMain.cpp\n    WslCoreConfigInterface.cpp)\n\nadd_library(libwsl SHARED ${SOURCES} libwsl.def)\n\ntarget_include_directories(libwsl\n    PUBLIC ${CMAKE_CURRENT_LIST_DIR})\n\nset_target_properties(libwsl PROPERTIES FOLDER windows)\ntarget_precompile_headers(libwsl REUSE_FROM common)\n\ntarget_link_libraries(libwsl\n                      ${COMMON_LINK_LIBRARIES}\n                      common\n                      configfile\n                      legacy_stdio_definitions\n                      VirtDisk.lib)"
  },
  {
    "path": "src/windows/libwsl/DllMain.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    DllMain.cpp\r\n\r\nAbstract:\r\n\r\n    This file contains the entrypoint for libwsl.\r\n\r\n--*/\r\n\r\n#include \"precomp.h\"\r\n\r\nstatic HINSTANCE g_dllInstance;\r\n\r\nEXTERN_C BOOL STDAPICALLTYPE DllMain(_In_ HINSTANCE Instance, _In_ DWORD Reason, _In_opt_ LPVOID Reserved)\r\n{\r\n    wil::DLLMain(Instance, Reason, Reserved);\r\n\r\n    switch (Reason)\r\n    {\r\n    case DLL_PROCESS_ATTACH:\r\n        g_dllInstance = Instance;\r\n        WslTraceLoggingInitialize(LxssTelemetryProvider, TRUE, nullptr);\r\n\r\n        // Accidentally including a Module<OutOfProc> can result in lifetime issues because it will call\r\n        // CoAddRefServerProcess/CoReleaseServerProcess outside of any WRL::Module<> that may be in use in the caller,\r\n        // which means the global counter is getting updated without the Module<> specific checks (e.g. last reference\r\n        // has been released).\r\n        FAIL_FAST_HR_IF_MSG(\r\n            HRESULT_FROM_WIN32(ERROR_INVALID_STATE),\r\n            Microsoft::WRL::GetModuleBase() != nullptr,\r\n            \"A WRL::Module has been included\");\r\n\r\n        break;\r\n\r\n    case DLL_PROCESS_DETACH:\r\n        WslTraceLoggingUninitialize();\r\n        break;\r\n    }\r\n\r\n    return TRUE;\r\n}\r\n"
  },
  {
    "path": "src/windows/libwsl/WslCoreConfigInterface.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreConfigInterface.cpp\n\nAbstract:\n\n    This file contains the WSL Core Config Interface class interface definition.\n\n--*/\n\n#include <WslCoreConfigInterface.h>\n#include \"WslCoreConfig.h\"\n#include <helpers.hpp>\n\nusing namespace wsl::shared::string;\nusing namespace wsl::core;\n\nstatic_assert(NetworkingConfiguration::None == static_cast<int32_t>(wsl::core::NetworkingMode::None));\nstatic_assert(NetworkingConfiguration::Nat == static_cast<int32_t>(wsl::core::NetworkingMode::Nat));\nstatic_assert(NetworkingConfiguration::Bridged == static_cast<int32_t>(wsl::core::NetworkingMode::Bridged));\nstatic_assert(NetworkingConfiguration::Mirrored == static_cast<int32_t>(wsl::core::NetworkingMode::Mirrored));\nstatic_assert(NetworkingConfiguration::VirtioProxy == static_cast<int32_t>(wsl::core::NetworkingMode::VirtioProxy));\n\nstatic_assert(MemoryReclaimConfiguration::Disabled == static_cast<int32_t>(wsl::core::MemoryReclaimMode::Disabled));\nstatic_assert(MemoryReclaimConfiguration::Gradual == static_cast<int32_t>(wsl::core::MemoryReclaimMode::Gradual));\nstatic_assert(MemoryReclaimConfiguration::DropCache == static_cast<int32_t>(wsl::core::MemoryReclaimMode::DropCache));\n\nstruct WslConfig\n{\n    WslConfig() = default;\n\n    WslConfig(const wchar_t* wslConfigFilePath) :\n        ConfigFilePath(wslConfigFilePath == nullptr ? std::filesystem::path() : wslConfigFilePath), Config(wslConfigFilePath)\n    {\n    }\n\n    std::filesystem::path ConfigFilePath;\n    wsl::core::Config Config;\n    std::wstring IgnoredPortsStr;\n};\n\nconst wchar_t* GetWslConfigFilePath()\n{\n    static std::filesystem::path g_wslConfigFilePath;\n    static std::once_flag flag;\n    std::call_once(flag, [&]() { g_wslConfigFilePath = wsl::windows::common::helpers::GetWslConfigPath(nullptr); });\n\n    return g_wslConfigFilePath.c_str();\n}\n\nWslConfig_t CreateWslConfig(const wchar_t* wslConfigFilePath)\n{\n    return new WslConfig(wslConfigFilePath);\n}\n\nvoid FreeWslConfig(WslConfig_t wslConfig)\n{\n    if (wslConfig)\n    {\n        delete wslConfig;\n    }\n}\n\nWslConfigSetting GetWslConfigSetting(WslConfig_t wslConfig, WslConfigEntry wslConfigEntry)\n{\n    if (wslConfig == nullptr)\n    {\n        return {.ConfigEntry = WslConfigEntry::NoEntry};\n    }\n\n    WslConfigSetting wslConfigSetting{.ConfigEntry = wslConfigEntry};\n    switch (wslConfigEntry)\n    {\n    case NoEntry:\n        // In addition to returning this entry type on error (e.g. invalid WslConfig_t parameter),\n        // the caller can request this entry type returned to initialize an empty WslConfigSetting object.\n        // The caller can then use the returned object in future calls to this function. The intention\n        // of this is an interop scenario where an auto-generated interop layer manages unmanaged memory\n        // (the returned WslConfigSetting object in this case) and is unable or doesn't generate the interop\n        // code to create such an object itself.\n        return wslConfigSetting;\n    case ProcessorCount:\n        static_assert(std::is_same<decltype(wslConfigSetting.Int32Value), decltype(wslConfig->Config.ProcessorCount)>::value);\n        wslConfigSetting.Int32Value = wslConfig->Config.ProcessorCount;\n        break;\n    case MemorySizeBytes:\n        static_assert(std::is_same<decltype(wslConfigSetting.UInt64Value), decltype(wslConfig->Config.MemorySizeBytes)>::value);\n        wslConfigSetting.UInt64Value = wslConfig->Config.MemorySizeBytes;\n        break;\n    case SwapSizeBytes:\n        static_assert(std::is_same<decltype(wslConfigSetting.UInt64Value), decltype(wslConfig->Config.SwapSizeBytes)>::value);\n        wslConfigSetting.UInt64Value = wslConfig->Config.SwapSizeBytes;\n        return wslConfigSetting;\n    case SwapFilePath:\n        static_assert(std::is_same<decltype(wslConfigSetting.StringValue), decltype(wslConfig->Config.SwapFilePath.c_str())>::value);\n        wslConfigSetting.StringValue = wslConfig->Config.SwapFilePath.c_str();\n        break;\n    case VhdSizeBytes:\n        static_assert(std::is_same<decltype(wslConfigSetting.UInt64Value), decltype(wslConfig->Config.VhdSizeBytes)>::value);\n        wslConfigSetting.UInt64Value = wslConfig->Config.VhdSizeBytes;\n        break;\n    case Networking:\n        wslConfigSetting.NetworkingConfigurationValue = static_cast<NetworkingConfiguration>(wslConfig->Config.NetworkingMode);\n        break;\n    case FirewallEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.FirewallConfig.Enabled())>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.FirewallConfig.Enabled();\n        break;\n    case IgnoredPorts:\n    {\n        // The IgnoredPorts member is stored as a set of 16-bit unsigned integers.\n        // These are converted back to a string here for the caller to consume.\n        wslConfig->IgnoredPortsStr.clear();\n        std::vector<uint16_t> ignoredPorts{wslConfig->Config.IgnoredPorts.begin(), wslConfig->Config.IgnoredPorts.end()};\n        std::sort(ignoredPorts.begin(), ignoredPorts.end());\n\n        wslConfig->IgnoredPortsStr = Join(ignoredPorts, L',');\n\n        static_assert(std::is_same<decltype(wslConfigSetting.StringValue), decltype(wslConfig->IgnoredPortsStr.c_str())>::value);\n        wslConfigSetting.StringValue = wslConfig->IgnoredPortsStr.c_str();\n    }\n    break;\n    case LocalhostForwardingEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableLocalhostRelay)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableLocalhostRelay;\n        break;\n    case HostAddressLoopbackEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableHostAddressLoopback)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableHostAddressLoopback;\n        break;\n    case AutoProxyEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableAutoProxy)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableAutoProxy;\n        break;\n    case InitialAutoProxyTimeout:\n        static_assert(std::is_same<decltype(wslConfigSetting.Int32Value), decltype(wslConfig->Config.InitialAutoProxyTimeout)>::value);\n        wslConfigSetting.Int32Value = wslConfig->Config.InitialAutoProxyTimeout;\n        break;\n    case DNSProxyEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableDnsProxy)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableDnsProxy;\n        break;\n    case DNSTunnelingEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableDnsTunneling)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableDnsTunneling;\n        break;\n    case BestEffortDNSParsingEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.BestEffortDnsParsing)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.BestEffortDnsParsing;\n        break;\n    case AutoMemoryReclaim:\n        wslConfigSetting.MemoryReclaimModeValue = static_cast<MemoryReclaimConfiguration>(wslConfig->Config.MemoryReclaim);\n        break;\n    case GUIApplicationsEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableGuiApps)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableGuiApps;\n        break;\n    case NestedVirtualizationEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableNestedVirtualization)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableNestedVirtualization;\n        break;\n    case SafeModeEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableSafeMode)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableSafeMode;\n        break;\n    case SparseVHDEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableSparseVhd)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableSparseVhd;\n        break;\n    case VMIdleTimeout:\n        static_assert(std::is_same<decltype(wslConfigSetting.Int32Value), decltype(wslConfig->Config.VmIdleTimeout)>::value);\n        wslConfigSetting.Int32Value = wslConfig->Config.VmIdleTimeout;\n        break;\n    case DebugConsoleEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableDebugConsole)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableDebugConsole;\n        break;\n    case HardwarePerformanceCountersEnabled:\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableHardwarePerformanceCounters)>::value);\n        wslConfigSetting.BoolValue = wslConfig->Config.EnableHardwarePerformanceCounters;\n        break;\n    case KernelPath:\n        static_assert(std::is_same<decltype(wslConfigSetting.StringValue), decltype(wslConfig->Config.KernelPath.c_str())>::value);\n        wslConfigSetting.StringValue = wslConfig->Config.KernelPath.c_str();\n        break;\n    case SystemDistroPath:\n        static_assert(std::is_same<decltype(wslConfigSetting.StringValue), decltype(wslConfig->Config.SystemDistroPath.c_str())>::value);\n        wslConfigSetting.StringValue = wslConfig->Config.SystemDistroPath.c_str();\n        break;\n    case KernelModulesPath:\n        static_assert(std::is_same<decltype(wslConfigSetting.StringValue), decltype(wslConfig->Config.KernelModulesPath.c_str())>::value);\n        wslConfigSetting.StringValue = wslConfig->Config.KernelModulesPath.c_str();\n        break;\n    default:\n        FAIL_FAST();\n    }\n\n    return wslConfigSetting;\n}\n\n// Template instantiation acts as a type check for the ValueType parameter (e.g. an\n// implicit assert for newValue and outValue having the same type).\ntemplate <typename ValueType>\nunsigned long SetWslConfigSetting(WslConfig_t wslConfig, const char* keyName, ValueType& defaultValue, ValueType& newValue, ValueType& outValue)\n{\n    ConfigKey configKey(keyName, newValue);\n    const auto removeKey = defaultValue == newValue;\n    const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), configKey, removeKey);\n    if (result == 0)\n    {\n        outValue = newValue;\n    }\n\n    return result;\n}\n\nunsigned long SetWslConfigSetting(WslConfig_t wslConfig, const char* keyName, const wchar_t* defaultValue, const wchar_t* newValue, std::wstring& outValue)\n{\n    std::wstring newValueStr{newValue};\n    const ConfigKey configKey(keyName, newValueStr);\n    const auto removeKey = wsl::shared::string::IsEqual(defaultValue, newValueStr, true);\n    const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), configKey, removeKey);\n    if (result == 0)\n    {\n        outValue = std::move(newValueStr);\n    }\n\n    return result;\n}\n\nunsigned long SetWslConfigSetting(\n    WslConfig_t wslConfig, const char* keyName, const std::filesystem::path& defaultValue, const wchar_t* newValue, std::filesystem::path& outValue)\n{\n    // Normalize the path.\n    std::filesystem::path filePath;\n    try\n    {\n        filePath = std::filesystem::path(newValue);\n    }\n    catch (const std::exception&)\n    {\n        return ERROR_INVALID_PARAMETER;\n    }\n\n    // The WSL config file needs to have backslashes escaped for file paths.\n    // Assuming these are valid Windows Paths, replace any backslashes with double backslashes.\n    auto newValueStr = std::regex_replace(filePath.wstring(), std::wregex(L\"\\\\\\\\\"), L\"\\\\\\\\\");\n    const ConfigKey configKey(keyName, newValueStr);\n    const auto removeKey = defaultValue == newValue;\n    const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), configKey, removeKey);\n    if (result == 0)\n    {\n        outValue = newValue;\n    }\n\n    return result;\n}\n\nunsigned long SetWslConfigSetting(\n    WslConfig_t wslConfig, const char* keyName, const MemoryString& defaultValue, const MemoryString& newValue, unsigned __int64& outValue)\n{\n    const ConfigKey configKey(keyName, newValue);\n    const auto removeKey = defaultValue.m_value == newValue.m_value;\n    const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), configKey, removeKey);\n    if (result == 0)\n    {\n        outValue = newValue.m_value;\n    }\n\n    return result;\n}\n\nunsigned long SetWslConfigSetting(WslConfig_t wslConfig, WslConfigSetting wslConfigSetting)\n{\n    if (wslConfig == nullptr)\n    {\n        return ERROR_INVALID_PARAMETER;\n    }\n\n    // Create a Config object with the default initialized values.\n    wsl::core::Config defaultConfig(nullptr);\n\n    // The following scope exit is intended to re-initialize/update the Config object after a change is made\n    // upon a successful write to the config file and updating of the corresponding member of the Config object.\n    // Depending on the member and value updated, the value itself or others may change as well.\n    auto initializeConfig = wil::scope_exit([&] { wslConfig->Config.Initialize(); });\n\n    switch (wslConfigSetting.ConfigEntry)\n    {\n    case ProcessorCount:\n        return SetWslConfigSetting(\n            wslConfig, ConfigSetting::Processors, defaultConfig.ProcessorCount, wslConfigSetting.Int32Value, wslConfig->Config.ProcessorCount);\n    case MemorySizeBytes:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::Memory,\n            MemoryString(defaultConfig.MemorySizeBytes),\n            MemoryString(wslConfigSetting.UInt64Value),\n            wslConfig->Config.MemorySizeBytes);\n    case SwapSizeBytes:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::Swap,\n            MemoryString(defaultConfig.SwapSizeBytes),\n            MemoryString(wslConfigSetting.UInt64Value),\n            wslConfig->Config.SwapSizeBytes);\n    case SwapFilePath:\n    {\n        if (wslConfigSetting.StringValue == nullptr)\n        {\n            return ERROR_INVALID_PARAMETER;\n        }\n\n        return SetWslConfigSetting(\n            wslConfig, ConfigSetting::SwapFile, defaultConfig.SwapFilePath, wslConfigSetting.StringValue, wslConfig->Config.SwapFilePath);\n    }\n    case VhdSizeBytes:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::DefaultVhdSize,\n            MemoryString(defaultConfig.VhdSizeBytes),\n            MemoryString(wslConfigSetting.UInt64Value),\n            wslConfig->Config.VhdSizeBytes);\n    case Networking:\n    {\n        wsl::core::NetworkingMode networkingConfiguration{static_cast<wsl::core::NetworkingMode>(wslConfigSetting.NetworkingConfigurationValue)};\n        ConfigKey key({ConfigSetting::NetworkingMode, ConfigSetting::Experimental::NetworkingMode}, wsl::core::NetworkingModes, networkingConfiguration);\n        const auto removeKey = defaultConfig.NetworkingMode == networkingConfiguration;\n        const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), key, removeKey);\n        if (result == 0)\n        {\n            wslConfig->Config.NetworkingMode = networkingConfiguration;\n            wslConfig->Config.NetworkingModePresence = removeKey ? ConfigKeyPresence::Absent : ConfigKeyPresence::Present;\n        }\n        return result;\n    }\n    case FirewallEnabled:\n    {\n        ConfigKey key({ConfigSetting::Firewall, ConfigSetting::Experimental::Firewall}, wslConfigSetting.BoolValue);\n        const auto removeKey = defaultConfig.FirewallConfig.Enabled() == wslConfigSetting.BoolValue;\n        const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), key, removeKey);\n        if (result == 0)\n        {\n            if (wslConfigSetting.BoolValue)\n            {\n                wslConfig->Config.FirewallConfig.Enable();\n            }\n            else\n            {\n                wslConfig->Config.FirewallConfig.reset();\n            }\n\n            wslConfig->Config.FirewallConfigPresence = removeKey ? ConfigKeyPresence::Absent : ConfigKeyPresence::Present;\n        }\n\n        return result;\n    }\n    case IgnoredPorts:\n    {\n        if (wslConfigSetting.StringValue == nullptr)\n        {\n            return ERROR_INVALID_PARAMETER;\n        }\n\n        std::string ignoredPortsUtf8;\n        if (FAILED(wil::ResultFromException(\n                [&]() { ignoredPortsUtf8 = wsl::shared::string::WideToMultiByte(wslConfigSetting.StringValue); })))\n        {\n            return ERROR_INVALID_PARAMETER;\n        }\n\n        // IgnoredPorts is unique compared to other settings as it parses a string into a set of 16-bit unsigned integers.\n        // In this case, write out the ignored ports as a string. On success, parse the string into a set of 16-bit unsigned\n        // integers and update the IgnoredPorts member of the Config object.\n        static_assert(std::is_same<decltype(wslConfigSetting.StringValue), decltype(wslConfig->IgnoredPortsStr.c_str())>::value);\n        const auto ignoredPortsKeyName = ConfigSetting::Experimental::IgnoredPorts;\n\n        const std::vector<uint16_t> defaultIgnoredPorts{defaultConfig.IgnoredPorts.begin(), defaultConfig.IgnoredPorts.end()};\n        const std::wstring defaultIgnoredPortsStr = Join(defaultIgnoredPorts, L',');\n\n        const auto result = SetWslConfigSetting(\n            wslConfig, ignoredPortsKeyName, defaultIgnoredPortsStr.c_str(), wslConfigSetting.StringValue, wslConfig->IgnoredPortsStr);\n        if (result == 0)\n        {\n            wslConfig->Config.IgnoredPorts.clear();\n            auto parseIgnoredPorts = [&wslConfig](const char*, const char* value, const wchar_t*, unsigned long) {\n                const auto ignoredPortsVector = Split(std::string{value}, ',');\n                for (const auto& portString : ignoredPortsVector)\n                {\n                    int number = 0;\n                    if (FAILED(wil::ResultFromException([&]() { number = std::stoi(portString); })) || (number <= 0 || number > USHRT_MAX))\n                    {\n                        continue;\n                    }\n\n                    wslConfig->Config.IgnoredPorts.insert(static_cast<uint16_t>(number));\n                }\n            };\n\n            ConfigKey key(ignoredPortsKeyName, std::move(parseIgnoredPorts));\n            key.Parse(ignoredPortsKeyName, ignoredPortsUtf8.c_str(), nullptr, 0);\n        }\n\n        return result;\n    }\n    case LocalhostForwardingEnabled:\n    {\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableLocalhostRelay)>::value);\n        ConfigKey key(ConfigSetting::LocalhostForwarding, wslConfigSetting.BoolValue);\n        const auto removeKey = defaultConfig.EnableLocalhostRelay == wslConfigSetting.BoolValue;\n        const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), key, removeKey);\n        if (result == 0)\n        {\n            wslConfig->Config.EnableLocalhostRelay = wslConfigSetting.BoolValue;\n            wslConfig->Config.LocalhostRelayConfigPresence = removeKey ? ConfigKeyPresence::Absent : ConfigKeyPresence::Present;\n        }\n\n        return result;\n    }\n    case HostAddressLoopbackEnabled:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::Experimental::HostAddressLoopback,\n            defaultConfig.EnableHostAddressLoopback,\n            wslConfigSetting.BoolValue,\n            wslConfig->Config.EnableHostAddressLoopback);\n    case AutoProxyEnabled:\n    {\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableAutoProxy)>::value);\n        ConfigKey key({ConfigSetting::AutoProxy, ConfigSetting::Experimental::AutoProxy}, wslConfigSetting.BoolValue);\n        const auto removeKey = defaultConfig.EnableAutoProxy == wslConfigSetting.BoolValue;\n        const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), key, removeKey);\n        if (result == 0)\n        {\n            wslConfig->Config.EnableAutoProxy = wslConfigSetting.BoolValue;\n        }\n\n        return result;\n    }\n    case InitialAutoProxyTimeout:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::Experimental::InitialAutoProxyTimeout,\n            defaultConfig.InitialAutoProxyTimeout,\n            wslConfigSetting.Int32Value,\n            wslConfig->Config.InitialAutoProxyTimeout);\n    case DNSProxyEnabled:\n        return SetWslConfigSetting(\n            wslConfig, ConfigSetting::DnsProxy, defaultConfig.EnableDnsProxy, wslConfigSetting.BoolValue, wslConfig->Config.EnableDnsProxy);\n    case DNSTunnelingEnabled:\n    {\n        static_assert(std::is_same<decltype(wslConfigSetting.BoolValue), decltype(wslConfig->Config.EnableDnsTunneling)>::value);\n        ConfigKey key({ConfigSetting::DnsTunneling, ConfigSetting::Experimental::DnsTunneling}, wslConfigSetting.BoolValue);\n        const auto removeKey = defaultConfig.EnableDnsTunneling == wslConfigSetting.BoolValue;\n        const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), key, removeKey);\n        if (result == 0)\n        {\n            wslConfig->Config.EnableDnsTunneling = wslConfigSetting.BoolValue;\n            wslConfig->Config.DnsTunnelingConfigPresence = removeKey ? ConfigKeyPresence::Absent : ConfigKeyPresence::Present;\n        }\n\n        return result;\n    }\n    case BestEffortDNSParsingEnabled:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::Experimental::BestEffortDnsParsing,\n            defaultConfig.BestEffortDnsParsing,\n            wslConfigSetting.BoolValue,\n            wslConfig->Config.BestEffortDnsParsing);\n    case AutoMemoryReclaim:\n    {\n        wsl::core::MemoryReclaimMode memoryReclaimMode{static_cast<wsl::core::MemoryReclaimMode>(wslConfigSetting.MemoryReclaimModeValue)};\n        ConfigKey key({ConfigSetting::Experimental::AutoMemoryReclaim}, wsl::core::MemoryReclaimModes, memoryReclaimMode);\n        const auto removeKey = defaultConfig.MemoryReclaim == memoryReclaimMode;\n        const auto result = wslConfig->Config.WriteConfigFile(wslConfig->ConfigFilePath.c_str(), key, removeKey);\n        if (result == 0)\n        {\n            wslConfig->Config.MemoryReclaim = memoryReclaimMode;\n        }\n\n        return result;\n    }\n    case GUIApplicationsEnabled:\n        return SetWslConfigSetting(\n            wslConfig, ConfigSetting::GuiApplications, defaultConfig.EnableGuiApps, wslConfigSetting.BoolValue, wslConfig->Config.EnableGuiApps);\n    case NestedVirtualizationEnabled:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::NestedVirtualization,\n            defaultConfig.EnableNestedVirtualization,\n            wslConfigSetting.BoolValue,\n            wslConfig->Config.EnableNestedVirtualization);\n    case SafeModeEnabled:\n        return SetWslConfigSetting(\n            wslConfig, ConfigSetting::SafeMode, defaultConfig.EnableSafeMode, wslConfigSetting.BoolValue, wslConfig->Config.EnableSafeMode);\n    case SparseVHDEnabled:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::Experimental::SparseVhd,\n            defaultConfig.EnableSparseVhd,\n            wslConfigSetting.BoolValue,\n            wslConfig->Config.EnableSparseVhd);\n    case VMIdleTimeout:\n        return SetWslConfigSetting(\n            wslConfig, ConfigSetting::VmIdleTimeout, defaultConfig.VmIdleTimeout, wslConfigSetting.Int32Value, wslConfig->Config.VmIdleTimeout);\n    case DebugConsoleEnabled:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::DebugConsole,\n            defaultConfig.EnableDebugConsole,\n            wslConfigSetting.BoolValue,\n            wslConfig->Config.EnableDebugConsole);\n    case HardwarePerformanceCountersEnabled:\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::HardwarePerformanceCounters,\n            defaultConfig.EnableHardwarePerformanceCounters,\n            wslConfigSetting.BoolValue,\n            wslConfig->Config.EnableHardwarePerformanceCounters);\n    case KernelPath:\n    {\n        if (wslConfigSetting.StringValue == nullptr)\n        {\n            return ERROR_INVALID_PARAMETER;\n        }\n\n        return SetWslConfigSetting(\n            wslConfig, ConfigSetting::Kernel, defaultConfig.KernelPath, wslConfigSetting.StringValue, wslConfig->Config.KernelPath);\n    }\n    case SystemDistroPath:\n    {\n        if (wslConfigSetting.StringValue == nullptr)\n        {\n            return ERROR_INVALID_PARAMETER;\n        }\n\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::SystemDistro,\n            defaultConfig.SystemDistroPath,\n            wslConfigSetting.StringValue,\n            wslConfig->Config.SystemDistroPath);\n    }\n    case KernelModulesPath:\n    {\n        if (wslConfigSetting.StringValue == nullptr)\n        {\n            return ERROR_INVALID_PARAMETER;\n        }\n\n        return SetWslConfigSetting(\n            wslConfig,\n            ConfigSetting::KernelModules,\n            defaultConfig.KernelModulesPath,\n            wslConfigSetting.StringValue,\n            wslConfig->Config.KernelModulesPath);\n    }\n    default:\n        FAIL_FAST();\n    }\n}"
  },
  {
    "path": "src/windows/libwsl/libwsl.def",
    "content": "LIBRARY libwsl\n\nEXPORTS\n    GetWslConfigFilePath\n    CreateWslConfig\n    FreeWslConfig\n    GetWslConfigSetting\n    SetWslConfigSetting"
  },
  {
    "path": "src/windows/service/CMakeLists.txt",
    "content": "add_compile_definitions(\"PROXY_CLSID_IS={0x4EA0C6DD,0xE9FF,0x48E7,{0x99,0x4e,0x13,0xa3,0x1d,0x10,0xdc,0x60}}\")\nadd_compile_definitions(\"REGISTER_PROXY_DLL\")\n\nadd_subdirectory(exe)\nadd_subdirectory(inc)\nadd_subdirectory(stub)\nadd_subdirectory(mc)"
  },
  {
    "path": "src/windows/service/exe/BridgedNetworking.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"precomp.h\"\n#include \"BridgedNetworking.h\"\n#include \"hcs.hpp\"\n\nusing wsl::core::BridgedNetworking;\nusing wsl::core::networking::NetworkSettings;\nusing namespace wsl::windows::common;\n\nBridgedNetworking::BridgedNetworking(HCS_SYSTEM system, const Config& config) : m_system(system), m_config(config)\n{\n}\n\nvoid BridgedNetworking::Initialize()\n{\n    if (m_config.VmSwitch.empty())\n    {\n        THROW_HR_WITH_USER_ERROR(WSL_E_VMSWITCH_NOT_SET, wsl::shared::Localization::MessageVmSwitchNotSet());\n    }\n\n    std::vector<std::wstring> availableSwitches;\n    wsl::windows::common::hcs::unique_hcn_network network;\n    std::optional<GUID> switchId;\n    for (const auto& id : wsl::core::networking::EnumerateNetworks())\n    {\n        try\n        {\n            network = wsl::core::networking::OpenNetwork(id);\n            auto [networkProperties, propertiesString] = wsl::core::networking::QueryNetworkProperties(network.get());\n            if (networkProperties.Name == m_config.VmSwitch)\n            {\n                switchId = id;\n                break;\n            }\n\n            availableSwitches.emplace_back(std::move(networkProperties.Name));\n        }\n        CATCH_LOG()\n    }\n\n    if (!switchId.has_value())\n    {\n        THROW_HR_WITH_USER_ERROR(\n            WSL_E_VMSWITCH_NOT_FOUND,\n            wsl::shared::Localization::MessageVmSwitchNotFound(\n                m_config.VmSwitch.c_str(), wsl::shared::string::Join<wchar_t>(availableSwitches, ',').c_str()));\n    }\n\n    wsl::shared::hns::HostComputeEndpoint hnsEndpoint{};\n    hnsEndpoint.SchemaVersion.Major = 2;\n    hnsEndpoint.SchemaVersion.Minor = 16;\n    hnsEndpoint.HostComputeNetwork = switchId.value();\n    wsl::shared::hns::EndpointPolicy<wsl::shared::hns::PortnameEndpointPolicySetting> endpointPortNamePolicy{};\n    endpointPortNamePolicy.Type = wsl::shared::hns::EndpointPolicyType::PortName;\n    hnsEndpoint.Policies.emplace_back(std::move(endpointPortNamePolicy));\n    m_endpoint = wsl::core::networking::CreateEphemeralHcnEndpoint(network.get(), hnsEndpoint);\n\n    hcs::ModifySettingRequest<hcs::NetworkAdapter> networkRequest{};\n    networkRequest.Settings.MacAddress = m_config.MacAddress;\n    networkRequest.Settings.EndpointId = m_endpoint.Id;\n    networkRequest.Settings.InstanceId = m_endpoint.Id;\n    networkRequest.RequestType = hcs::ModifyRequestType::Add;\n    networkRequest.ResourcePath = networking::c_networkAdapterPrefix +\n                                  wsl::shared::string::GuidToString<wchar_t>(m_endpoint.Id, wsl::shared::string::GuidToStringFlags::None);\n\n    windows::common::hcs::ModifyComputeSystem(m_system, wsl::shared::ToJsonW(networkRequest).c_str());\n}\n\nvoid BridgedNetworking::TraceLoggingRundown() noexcept\n{\n    // No-op.\n}\n\nvoid BridgedNetworking::FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message)\n{\n    message.NetworkingMode = LxMiniInitNetworkingModeBridged;\n    message.DisableIpv6 = !m_config.EnableIpv6;\n    message.EnableDhcpClient = m_config.EnableDhcp;\n    message.DhcpTimeout = static_cast<int>(std::round(m_config.DhcpTimeout / 1000));\n    message.PortTrackerType = m_config.EnableLocalhostRelay ? LxMiniInitPortTrackerTypeRelay : LxMiniInitPortTrackerTypeNone;\n}\n\nvoid BridgedNetworking::StartPortTracker(wil::unique_socket&&)\n{\n    WI_ASSERT(false);\n}"
  },
  {
    "path": "src/windows/service/exe/BridgedNetworking.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include \"INetworkingEngine.h\"\n#include \"GnsChannel.h\"\n#include \"WslCoreConfig.h\"\n#include \"WslCoreNetworkEndpointSettings.h\"\n\nnamespace wsl::core {\n\nclass BridgedNetworking : public INetworkingEngine\n{\npublic:\n    BridgedNetworking(HCS_SYSTEM system, const Config& config);\n    ~BridgedNetworking() override = default;\n\n    void Initialize() override;\n    void TraceLoggingRundown() noexcept override;\n    void FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message) override;\n    void StartPortTracker(wil::unique_socket&& socket) override;\n\nprivate:\n    // Handle for the Hcn* Api. Owned by the caller (WslCoreVm), this is a non-owning copy\n    const HCS_SYSTEM m_system{};\n\n    const Config& m_config;\n    wsl::core::networking::EphemeralHcnEndpoint m_endpoint;\n};\n\n} // namespace wsl::core\n"
  },
  {
    "path": "src/windows/service/exe/CMakeLists.txt",
    "content": "set(SOURCES\n    DistributionRegistration.cpp\n    LxssUserCallback.cpp\n    LxssUserSession.cpp\n    LxssUserSessionFactory.cpp\n    LxssIptables.cpp\n    LxssHttpProxy.cpp\n    LxssInstance.cpp\n    PluginManager.cpp\n    ServiceMain.cpp\n    BridgedNetworking.cpp\n    GnsRpcServer.cpp\n    GuestTelemetryLogger.cpp\n    Lifetime.cpp\n    LxssConsoleManager.cpp\n    LxssCreateProcess.cpp\n    MirroredNetworking.cpp\n    WslCoreGuestNetworkService.cpp\n    WslCoreInstance.cpp\n    WslMirroredNetworking.cpp\n    WslCoreTcpIpStateTracking.cpp\n    WslCoreVm.cpp\n    main.rc\n    ${CMAKE_CURRENT_BINARY_DIR}/../mc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/wsleventschema.rc\n    application.manifest)\n\nset(HEADERS\n    ../../inc/comservicehelper.h\n    DistributionRegistration.h\n    LxssUserCallback.h\n    LxssUserSession.h\n    LxssUserSessionFactory.h\n    LxssIptables.h\n    LxssHttpProxy.h\n    PluginManager.h\n    LxssInstance.h\n    BridgedNetworking.h\n    GnsRpcServer.h\n    GuestTelemetryLogger.h\n    IMirroredNetworkManager.h\n    Lifetime.h\n    LxssConsoleManager.h\n    LxssCreateProcess.h\n    MirroredNetworking.h\n    WslCoreGuestNetworkService.h\n    WslCoreInstance.h\n    WslMirroredNetworking.h\n    WslCoreNetworkEndpoint.h\n    WslCoreTcpIpStateTracking.h\n    WslCoreVm.h)\n\nadd_executable(wslservice ${SOURCES} ${HEADERS})\nadd_dependencies(wslservice wslserviceidl wslservicemc)\nadd_compile_definitions(__WRL_CLASSIC_COM__)\nadd_compile_definitions(__WRL_DISABLE_STATIC_INITIALIZE__)\nadd_compile_definitions(USE_COM_CONTEXT_DEF=1)\nset_target_properties(wslservice PROPERTIES LINK_FLAGS \"/merge:minATL=.rdata /include:__minATLObjMap_LxssUserSession_COM\")\ntarget_link_libraries(wslservice\n                      ${COMMON_LINK_LIBRARIES}\n                      ${MSI_LINK_LIBRARIES}\n                      ${HCS_LINK_LIBRARIES}\n                      ${SERVICE_LINK_LIBRARIES}\n                      common\n                      configfile\n                      legacy_stdio_definitions\n                      VirtDisk.lib\n                      Winhttp.lib\n                      Synchronization.lib)\n\ntarget_precompile_headers(wslservice REUSE_FROM common)\nset_target_properties(wslservice PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/service/exe/DistributionRegistration.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    DistributionRegistration.cpp\n\nAbstract:\n\n    This file contains the DistributionRegistration helper class implementation.\n\n--*/\n\n#include \"precomp.h\"\n\n#include \"DistributionRegistration.h\"\n\nusing wsl::windows::service::DistributionRegistration;\nusing namespace wsl::windows::common;\nusing namespace registry;\n\nconstexpr auto DefaultDistro = L\"DefaultDistribution\";\n\nnamespace {\n\ntemplate <typename T>\nT ApplyTransform(T&& value, T (*transform)(T))\n{\n    if (transform == nullptr)\n    {\n        return value;\n    }\n\n    return transform(std::move(value));\n}\n} // namespace\n\nDistributionRegistration DistributionRegistration::Open(HKEY LxssKey, const GUID& Id)\n{\n    ExecutionContext context(Context::ReadDistroConfig);\n\n    const auto distroGuidString = wsl::shared::string::GuidToString<wchar_t>(Id);\n\n    wil::unique_hkey distroKey;\n    try\n    {\n        distroKey = wsl::windows::common::registry::OpenKey(LxssKey, distroGuidString.c_str(), (KEY_READ | KEY_WRITE));\n    }\n    catch (...)\n    {\n        THROW_HR_IF(WSL_E_DISTRO_NOT_FOUND, wil::ResultFromCaughtException() == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));\n        throw;\n    }\n\n    return DistributionRegistration(Id, std::move(distroKey));\n}\n\nDistributionRegistration DistributionRegistration::Create(\n    HKEY LxssKey, const std::optional<GUID>& Id, LPCWSTR Name, ULONG Version, LPCWSTR BasePath, ULONG Flags, ULONG DefaultUID, LPCWSTR PackageFamilyName, LPCWSTR VhdFileName, bool EnableOobe)\n{\n    std::wstring distroGuidString;\n    GUID distroId{};\n    wil::unique_hkey distroKey{};\n    if (Id.has_value())\n    {\n        distroId = Id.value();\n        distroGuidString = wsl::shared::string::GuidToString<wchar_t>(distroId);\n        distroKey = wsl::windows::common::registry::CreateKey(LxssKey, distroGuidString.c_str(), (KEY_READ | KEY_WRITE));\n    }\n    else\n    {\n        DWORD disposition = 0;\n        do\n        {\n            THROW_IF_FAILED(CoCreateGuid(&distroId));\n\n            distroGuidString = wsl::shared::string::GuidToString<wchar_t>(distroId);\n            distroKey = wsl::windows::common::registry::CreateKey(LxssKey, distroGuidString.c_str(), (KEY_READ | KEY_WRITE), &disposition);\n        } while (disposition != REG_CREATED_NEW_KEY);\n    }\n\n    WI_ASSERT(distroKey && !distroGuidString.empty());\n\n    // Set up a scope exit member to delete the key if registration fails.\n    auto cleanup = wil::scope_exit([&] { wsl::windows::common::registry::DeleteKey(LxssKey, distroGuidString.c_str()); });\n\n    DistributionRegistration distribution(distroId, std::move(distroKey));\n\n    distribution.Write(Property::State, LxssDistributionStateInstalling);\n\n    if (Name != nullptr)\n    {\n        distribution.Write(Property::Name, Name);\n    }\n    distribution.Write(Property::Version, Version);\n    distribution.Write(Property::BasePath, BasePath);\n    distribution.Write(Property::Flags, Flags);\n    distribution.Write(Property::Flags, Flags);\n    distribution.Write(Property::DefaultUid, DefaultUID);\n    distribution.Write(Property::RunOOBE, EnableOobe);\n\n    if (ARGUMENT_PRESENT(PackageFamilyName))\n    {\n        WI_ASSERT(wcslen(PackageFamilyName) > 0);\n\n        distribution.Write(Property::PackageFamilyName, PackageFamilyName);\n    }\n\n    if (ARGUMENT_PRESENT(VhdFileName))\n    {\n        distribution.Write(Property::VhdFileName, VhdFileName);\n    }\n\n    // Dismiss the scope exit member so the key is persisted.\n    cleanup.release();\n    return distribution;\n}\n\nstd::optional<DistributionRegistration> DistributionRegistration::OpenDefault(HKEY LxssKey)\n{\n    const auto defaultId = wsl::windows::common::registry::ReadOptionalString(LxssKey, nullptr, DefaultDistro);\n    if (!defaultId.has_value())\n    {\n        return {};\n    }\n\n    const auto distroGuid = wsl::shared::string::ToGuid(defaultId.value());\n    if (!distroGuid.has_value())\n    {\n        return {};\n    }\n\n    try\n    {\n        return Open(LxssKey, distroGuid.value());\n    }\n    catch (...)\n    {\n        // If we hit this block, it means that the default distribution value point to a distribution that doesn't exist.\n        // Handle gracefully so this doesn't prevent the user from installing new distros.\n\n        LOG_CAUGHT_EXCEPTION_MSG(\"Broken default distro. ID: %ls\", defaultId->c_str());\n        return {};\n    }\n}\n\nDistributionRegistration DistributionRegistration::OpenOrDefault(HKEY LxssKey, const GUID* Id)\n{\n    if (Id == nullptr)\n    {\n        auto defaultDistribution = OpenDefault(LxssKey);\n        THROW_HR_IF(WSL_E_DEFAULT_DISTRO_NOT_FOUND, !defaultDistribution.has_value());\n\n        return std::move(defaultDistribution.value());\n    }\n    else\n    {\n        return Open(LxssKey, *Id);\n    }\n}\n\nvoid DistributionRegistration::SetDefault(HKEY LxssKey, const DistributionRegistration& Distro)\n{\n    wsl::windows::common::registry::WriteString(\n        LxssKey, nullptr, DefaultDistro, wsl::shared::string::GuidToString<wchar_t>(Distro.Id()).c_str());\n}\n\nvoid DistributionRegistration::DeleteDefault(HKEY LxssKey)\n{\n    wsl::windows::common::registry::DeleteKeyValue(LxssKey, DefaultDistro);\n}\n\nDistributionRegistration::DistributionRegistration(const GUID& Id, wil::unique_hkey&& key) : m_id(Id), m_key{std::move(key)}\n{\n}\n\nconst GUID& DistributionRegistration::Id() const\n{\n    return m_id;\n}\n\nstd::wstring DistributionRegistration::Read(const DistributionPropertyWithDefault<LPCWSTR>& property) const\n{\n    return ReadString(m_key.get(), nullptr, property.Name, property.DefaultValue);\n}\n\nDWORD DistributionRegistration::Read(const DistributionPropertyWithDefault<DWORD>& property) const\n{\n    return ApplyTransform(ReadDword(m_key.get(), nullptr, property.Name, property.DefaultValue), property.Transform);\n}\n\nstd::optional<std::wstring> DistributionRegistration::Read(const DistributionProperty<LPCWSTR>& property) const\n{\n    return ReadOptionalString(m_key.get(), nullptr, property.Name);\n}\n\nstd::vector<std::string> DistributionRegistration::Read(const DistributionPropertyWithDefault<std::vector<std::string>>& property) const\n{\n    return wsl::windows::common::registry::ReadStringSet(m_key.get(), nullptr, property.Name, property.DefaultValue);\n}\n\nstd::wstring DistributionRegistration::Read(const ExpectedProperty<LPCWSTR>& property) const\n{\n    auto value = Read(static_cast<DistributionProperty<LPCWSTR>>(property));\n    if (!value.has_value())\n    {\n        THROW_HR_WITH_USER_ERROR(\n            E_UNEXPECTED,\n            wsl::shared::Localization::MessageCorruptedDistroRegistration(\n                property.Name, wsl::shared::string::GuidToString<wchar_t>(m_id).c_str()));\n    }\n\n    return value.value();\n}\n\nvoid DistributionRegistration::Write(const DistributionProperty<LPCWSTR>& property, LPCWSTR value) const\n{\n    return WriteString(m_key.get(), nullptr, property.Name, value);\n}\n\nvoid DistributionRegistration::Write(const DistributionProperty<DWORD>& property, DWORD value) const\n{\n    return WriteDword(m_key.get(), nullptr, property.Name, value);\n}\n\nstd::filesystem::path DistributionRegistration::ReadVhdFilePath() const\n{\n    return std::filesystem::path(Read(Property::BasePath)) / Read(Property::VhdFileName);\n}\n\nDWORD\nDistributionRegistration::ApplyGlobalFlagsOverride(DWORD Flags)\n{\n    WI_ASSERT(!WI_IsAnyFlagSet(Flags, ~LXSS_DISTRO_FLAGS_ALL));\n\n    DWORD globalFlags =\n        wsl::windows::common::registry::ReadDword(HKEY_LOCAL_MACHINE, LXSS_SERVICE_REGISTRY_PATH, L\"DistributionFlags\", LXSS_DISTRO_FLAGS_ALL);\n\n    // The VM Mode flag cannot be overridden by global flags.\n    WI_SetFlag(globalFlags, LXSS_DISTRO_FLAGS_VM_MODE);\n    Flags &= (globalFlags & LXSS_DISTRO_FLAGS_ALL);\n    return Flags;\n}\n\nvoid DistributionRegistration::Delete(HKEY LxssKey) const\n{\n    DeleteKey(LxssKey, wsl::shared::string::GuidToString<wchar_t>(m_id).c_str());\n}"
  },
  {
    "path": "src/windows/service/exe/DistributionRegistration.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    DistributionRegistration.h\n\nAbstract:\n\n    This file contains the DistributionRegistration helper class definition.\n\n--*/\n\n#pragma once\n\nnamespace wsl::windows::service {\n\ntemplate <typename T>\nstruct DistributionProperty\n{\n    LPCWSTR Name;\n    T (*Transform)(T) = nullptr;\n};\n\ntemplate <typename T>\nstruct DistributionPropertyWithDefault : public DistributionProperty<T>\n{\n    DistributionPropertyWithDefault(LPCWSTR Name, T DefaultValue, T (*Transform)(T) = nullptr) :\n        DistributionProperty<T>(Name, Transform), DefaultValue(DefaultValue)\n    {\n    }\n\n    T DefaultValue;\n};\n\ntemplate <typename T>\nstruct ExpectedProperty : public DistributionProperty<T>\n{\n    ExpectedProperty(LPCWSTR Name, T (*Transform)(T) = nullptr) : DistributionProperty<T>(Name, Transform)\n    {\n    }\n};\n\nclass DistributionRegistration\n{\npublic:\n    static DistributionRegistration Create(\n        HKEY LxssKey,\n        const std::optional<GUID>& Id,\n        LPCWSTR Name,\n        ULONG Version,\n        LPCWSTR BasePath,\n        ULONG Flags,\n        ULONG DefaultUID,\n        LPCWSTR PackageFamilyName,\n        LPCWSTR VhdFileName,\n        bool EnableOobe);\n\n    static DistributionRegistration Open(HKEY LxssKey, const GUID& Id);\n    static DistributionRegistration OpenOrDefault(HKEY LxssKey, const GUID* Id);\n    static std::optional<DistributionRegistration> OpenDefault(HKEY LxssKey);\n    static void SetDefault(HKEY LxssKey, const DistributionRegistration& Distro);\n    static void DeleteDefault(HKEY LxssKey);\n\n    DistributionRegistration() = default;\n    const GUID& Id() const;\n\n    std::wstring Read(const DistributionPropertyWithDefault<LPCWSTR>& property) const;\n    std::optional<std::wstring> Read(const DistributionProperty<LPCWSTR>& property) const;\n    DWORD Read(const DistributionPropertyWithDefault<DWORD>& property) const;\n    std::vector<std::string> Read(const DistributionPropertyWithDefault<std::vector<std::string>>& Property) const;\n    std::wstring Read(const ExpectedProperty<LPCWSTR>& property) const;\n\n    void Write(const DistributionProperty<LPCWSTR>& property, LPCWSTR Value) const;\n    void Write(const DistributionProperty<DWORD>& property, DWORD Value) const;\n    void Delete(HKEY LxssKey) const;\n\n    std::filesystem::path ReadVhdFilePath() const;\n\n    static DWORD ApplyGlobalFlagsOverride(DWORD Flags);\n\nprivate:\n    DistributionRegistration(const GUID& Id, wil::unique_hkey&& key);\n\n    GUID m_id{};\n    wil::unique_hkey m_key;\n};\n\nnamespace Property {\n    inline DistributionPropertyWithDefault<LPCWSTR> PackageFamilyName{L\"PackageFamilyName\", L\"\"};\n    inline DistributionPropertyWithDefault<LPCWSTR> KernelCommandLine{L\"KernelCommandLine\", L\"\"};\n    inline DistributionPropertyWithDefault<LPCWSTR> VhdFileName{L\"VhdFileName\", LXSS_VM_MODE_VHD_NAME};\n    inline ExpectedProperty<LPCWSTR> Name{L\"DistributionName\"};\n    inline ExpectedProperty<LPCWSTR> BasePath{L\"BasePath\"};\n    inline DistributionProperty<LPCWSTR> Flavor{L\"Flavor\"};\n    inline DistributionProperty<LPCWSTR> OsVersion{L\"OsVersion\"};\n    inline DistributionProperty<LPCWSTR> ShortcutPath{L\"ShortcutPath\"};\n    inline DistributionProperty<LPCWSTR> TerminalProfilePath{L\"TerminalProfilePath\"};\n\n    inline DistributionPropertyWithDefault<DWORD> Version{L\"Version\", LXSS_DISTRO_VERSION_CURRENT};\n    inline DistributionPropertyWithDefault<DWORD> Flags{L\"Flags\", LXSS_DISTRO_FLAGS_DEFAULT, &DistributionRegistration::ApplyGlobalFlagsOverride};\n    inline DistributionPropertyWithDefault<DWORD> DefaultUid{L\"DefaultUid\", LX_UID_ROOT};\n    inline DistributionPropertyWithDefault<DWORD> State{L\"State\", LxssDistributionStateInvalid};\n    inline DistributionPropertyWithDefault<DWORD> RunOOBE{L\"RunOOBE\", 0};\n    inline DistributionPropertyWithDefault<DWORD> Modern{L\"Modern\", 0};\n\n    inline DistributionPropertyWithDefault<std::vector<std::string>> DefaultEnvironment{\n        L\"DefaultEnvironment\",\n        {\"HOSTTYPE=x86_64\",\n         \"LANG=en_US.UTF-8\",\n         \"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games\",\n         \"TERM=xterm-256color\"}};\n\n} // namespace Property\n\n} // namespace wsl::windows::service\n"
  },
  {
    "path": "src/windows/service/exe/GnsRpcServer.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"precomp.h\"\n#include \"GnsRpcServer.h\"\n#include \"wsldeps.h\"\n\nstatic constexpr PCWSTR c_SystemOnlySDDL =\n    // Owner: System\n    L\"O:SY\"\n    // Group: System\n    L\"G:SY\"\n    // Begin DACL\n    L\"D:\"\n    // Allow System full access\n    L\"(A;;GA;;;SY)\";\n\nstatic std::weak_ptr<GnsRpcServer> instance;\nwil::unique_hlocal_security_descriptor systemOnlySD;\n\nstd::shared_ptr<GnsRpcServer> GnsRpcServer::GetOrCreate()\n{\n    static wil::srwlock instanceLock;\n    auto lock = instanceLock.lock_exclusive();\n\n    if (nullptr == systemOnlySD)\n    {\n        THROW_IF_WIN32_BOOL_FALSE(ConvertStringSecurityDescriptorToSecurityDescriptor(c_SystemOnlySDDL, SDDL_REVISION_1, &systemOnlySD, nullptr));\n    }\n\n    auto ptr = instance.lock();\n    if (!ptr)\n    {\n        ptr = std::make_shared<GnsRpcServer>();\n        instance = ptr;\n    }\n\n    return ptr;\n}\n\nGnsRpcServer::GnsRpcServer()\n{\n    THROW_IF_FAILED(WslDepsRegisterGnsRpcServer(systemOnlySD.get(), &m_serverUuid));\n}\n\nGnsRpcServer::~GnsRpcServer() noexcept\n{\n    if (!m_moved)\n    {\n        LOG_IF_FAILED(WslDepsUnregisterGnsRpcServer(&m_serverUuid));\n    }\n}\n\nGnsRpcServer::GnsRpcServer(GnsRpcServer&& Other) noexcept\n{\n    *this = std::move(Other);\n}\n\nGnsRpcServer& GnsRpcServer::operator=(GnsRpcServer&& Other) noexcept\n{\n    m_serverUuid = Other.m_serverUuid;\n    Other.m_serverUuid = GUID{};\n    Other.m_moved = true;\n\n    return *this;\n}\n\nconst UUID& GnsRpcServer::GetServerUuid() const noexcept\n{\n    return m_serverUuid;\n}\n"
  },
  {
    "path": "src/windows/service/exe/GnsRpcServer.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#pragma once\n\n#include \"helpers.hpp\"\n#include <map>\n#include <memory>\n#include <wil/resource.h>\n\nclass GnsRpcServer\n{\npublic:\n    using AdapterCallback = std::function<HRESULT(LPCWSTR)>;\n\n    GnsRpcServer();\n    ~GnsRpcServer() noexcept;\n\n    GnsRpcServer(const GnsRpcServer&) = delete;\n    GnsRpcServer& operator=(const GnsRpcServer&) = delete;\n\n    GnsRpcServer(GnsRpcServer&&) noexcept;\n    GnsRpcServer& operator=(GnsRpcServer&&) noexcept;\n\n    static std::shared_ptr<GnsRpcServer> GetOrCreate();\n\n    const UUID& GetServerUuid() const noexcept;\n\nprivate:\n    wil::srwlock m_lock;\n    bool m_moved = false;\n    UUID m_serverUuid{};\n};\n"
  },
  {
    "path": "src/windows/service/exe/GuestTelemetryLogger.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    GuestTelemetryLogger.cpp\n\nAbstract:\n\n    This file contains logic to log guest telemetry.\n\n--*/\n\n#include \"precomp.h\"\n#include \"GuestTelemetryLogger.h\"\n\nGuestTelemetryLogger::GuestTelemetryLogger(GUID VmId) : m_runtimeId(VmId)\n{\n    m_threadExit.create(wil::EventOptions::ManualReset);\n}\n\nGuestTelemetryLogger::~GuestTelemetryLogger()\n{\n    m_threadExit.SetEvent();\n    if (m_thread.joinable())\n    {\n        m_thread.join();\n    }\n}\n\nstd::shared_ptr<GuestTelemetryLogger> GuestTelemetryLogger::Create(GUID VmId, const wil::unique_event& ExitEvent)\n{\n    auto logger = std::shared_ptr<GuestTelemetryLogger>(new GuestTelemetryLogger(VmId));\n    logger->Start(ExitEvent);\n    return logger;\n}\n\nLPCWSTR GuestTelemetryLogger::GetPipeName() const\n{\n    return m_pipeName.c_str();\n}\n\nvoid GuestTelemetryLogger::Start(const wil::unique_event& ExitEvent)\n{\n    THROW_HR_IF(E_UNEXPECTED, !m_pipeName.empty());\n\n    m_pipeName = wsl::windows::common::helpers::GetUniquePipeName();\n    wil::unique_hfile pipe(CreateNamedPipeW(\n        m_pipeName.c_str(), (PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED), (PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT), 1, LX_RELAY_BUFFER_SIZE, LX_RELAY_BUFFER_SIZE, 0, nullptr));\n\n    THROW_LAST_ERROR_IF(!pipe);\n\n    wil::unique_handle exitEvent(wsl::windows::common::wslutil::DuplicateHandle(ExitEvent.get()));\n    m_thread = std::thread([Self = shared_from_this(), Pipe = std::move(pipe), ExitEvent = std::move(exitEvent)]() {\n        try\n        {\n            wsl::windows::common::wslutil::SetThreadDescription(L\"GuestTelemetryLogger\");\n\n            // When the pipe connects, start reading data.\n            const std::vector<HANDLE> exitEvents = {Self->m_threadExit.get(), ExitEvent.get()};\n            wsl::windows::common::helpers::ConnectPipe(Pipe.get(), INFINITE, exitEvents);\n\n            std::vector<gsl::byte> buffer(LX_RELAY_BUFFER_SIZE);\n            OVERLAPPED overlapped = {};\n            const wil::unique_event overlappedEvent(wil::EventOptions::ManualReset);\n            overlapped.hEvent = overlappedEvent.get();\n            for (;;)\n            {\n                overlappedEvent.ResetEvent();\n                const auto bytesRead =\n                    wsl::windows::common::relay::InterruptableRead(Pipe.get(), gsl::make_span(buffer), exitEvents, &overlapped);\n\n                if (bytesRead == 0)\n                {\n                    break;\n                }\n\n                Self->ProcessInput(std::string_view{reinterpret_cast<const char*>(buffer.data()), bytesRead});\n            }\n        }\n        CATCH_LOG()\n    });\n}\n\nvoid GuestTelemetryLogger::ProcessInput(const std::string_view Input)\n{\n    m_ringBuffer.Insert(Input);\n\n    const auto delimiterCount = std::count(Input.begin(), Input.end(), '\\n');\n    const auto newStrings = m_ringBuffer.GetLastDelimitedStrings('\\n', delimiterCount);\n    for (const auto& logLine : newStrings)\n    {\n        WSL_LOG(\"GuestTelemetry\", TraceLoggingValue(logLine.c_str(), \"text\"), TraceLoggingValue(m_runtimeId, \"vmId\"));\n    }\n}"
  },
  {
    "path": "src/windows/service/exe/GuestTelemetryLogger.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    GuestTelemetryLogger.h\n\nAbstract:\n\n    This file contains declarations used to log guest telemetry.\n\n--*/\n\n#pragma once\n\n#include \"RingBuffer.h\"\n#include \"relay.hpp\"\n\nclass GuestTelemetryLogger : public std::enable_shared_from_this<GuestTelemetryLogger>\n{\npublic:\n    GuestTelemetryLogger() = delete;\n    ~GuestTelemetryLogger();\n\n    static std::shared_ptr<GuestTelemetryLogger> Create(GUID VmId, const wil::unique_event& ExitEvent);\n\n    LPCWSTR GetPipeName() const;\n\nprivate:\n    GuestTelemetryLogger(GUID VmId);\n\n    void Start(const wil::unique_event& ExitEvent);\n    void ProcessInput(const std::string_view Input);\n\n    std::wstring m_pipeName;\n    wil::unique_event m_threadExit;\n    std::thread m_thread;\n    GUID m_runtimeId{};\n    RingBuffer m_ringBuffer{LX_RELAY_BUFFER_SIZE};\n};\n"
  },
  {
    "path": "src/windows/service/exe/IMirroredNetworkManager.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include \"precomp.h\"\r\n#include \"WslCoreNetworkEndpoint.h\"\r\n#include \"WslCoreNetworkEndpointSettings.h\"\r\n\r\nnamespace wsl::core::networking {\r\n\r\nenum class GnsCallbackFlags\r\n{\r\n    DontWait = 0,\r\n    Wait = 1\r\n};\r\n\r\nusing GnsMessageCallback = std::function<HRESULT(LX_MESSAGE_TYPE, const std::wstring&, GnsCallbackFlags)>;\r\nusing GnsMessageCallbackWithCallbackResult =\r\n    std::function<HRESULT(LX_MESSAGE_TYPE, const std::wstring&, GnsCallbackFlags, _Out_opt_ int*)>;\r\nusing AddNetworkEndpointCallback = std::function<void(GUID)>;\r\n\r\nDEFINE_ENUM_FLAG_OPERATORS(GnsCallbackFlags);\r\n\r\nclass IMirroredNetworkManager\r\n{\r\npublic:\r\n    IMirroredNetworkManager() noexcept = default;\r\n    virtual ~IMirroredNetworkManager() noexcept = default;\r\n\r\n    // Disable copy and assign.\r\n    IMirroredNetworkManager(const IMirroredNetworkManager&) = delete;\r\n    IMirroredNetworkManager& operator=(const IMirroredNetworkManager&) = delete;\r\n\r\n    // Disable move semantics.\r\n    IMirroredNetworkManager(IMirroredNetworkManager&&) noexcept = delete;\r\n    IMirroredNetworkManager& operator=(IMirroredNetworkManager&&) = delete;\r\n\r\n    enum class HnsStatus\r\n    {\r\n        NoNetworkEverConnected,\r\n        NetworkConnectedWithHnsNotification,\r\n        NetworkConnectedNoHnsNotification\r\n    };\r\n\r\n    virtual HnsStatus Stop() noexcept = 0;\r\n\r\n    virtual _Check_return_ HRESULT EnumerateNetworks(_Out_ std::vector<GUID>& NetworkIds) const noexcept = 0;\r\n\r\n    virtual void AddEndpoint(networking::NetworkEndpoint&& newEndpoint, wsl::shared::hns::HNSEndpoint&& endpointProperties) noexcept = 0;\r\n\r\n    virtual void SendCreateNotificationsForInitialEndpoints() noexcept = 0;\r\n\r\n    // This API is not serialized with other API calls.\r\n    virtual HRESULT WaitForMirroredGoalState() noexcept = 0;\r\n\r\n    virtual _Check_return_ bool DoesEndpointExist(GUID networkId) const noexcept = 0;\r\n\r\n    virtual void OnNetworkConnectivityHintChange() noexcept = 0;\r\n    virtual void OnNetworkEndpointChange() noexcept = 0;\r\n    virtual void OnDnsSuffixChange() noexcept = 0;\r\n\r\n    virtual void TunAdapterStateChanged(_In_ const std::string& interfaceName, _In_ bool up) noexcept = 0;\r\n\r\n    // Client should call this if they detect the network is in a bad state and needs to be reconnected\r\n    virtual void ReconnectGuestNetwork() = 0;\r\n\r\n    // Returns the network settings of the endpoint.\r\n    virtual std::shared_ptr<NetworkSettings> GetEndpointSettings(const wsl::shared::hns::HNSEndpoint& endpointProperties) const = 0;\r\n\r\n    virtual void TraceLoggingRundown() const = 0;\r\n};\r\n\r\n} // namespace wsl::core::networking\r\n"
  },
  {
    "path": "src/windows/service/exe/Lifetime.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Lifetime.cpp\n\nAbstract:\n\n    This file contains function definitions around client lifetime.\n\n--*/\n\n#include \"precomp.h\"\n#include \"Lifetime.h\"\n\n#define RETRY_TIMER_PERIOD (60 * 1000)\n#define RETRY_TIMER_WINDOW (1000)\n\nstatic bool IsSameProcess(_In_ HANDLE process1, _In_ HANDLE process2)\n{\n    const DWORD pid1 = GetProcessId(process1);\n    THROW_LAST_ERROR_IF(pid1 == 0);\n\n    const DWORD pid2 = GetProcessId(process2);\n    THROW_LAST_ERROR_IF(pid2 == 0);\n\n    return pid1 == pid2;\n}\n\nLifetimeManager::LifetimeManager() : m_nextClientKey(0)\n{\n}\n\nLifetimeManager::~LifetimeManager()\n{\n    if (wil::ProcessShutdownInProgress())\n    {\n        return;\n    }\n\n    ClearCallbacks();\n}\n\nvoid LifetimeManager::ClearCallbacks()\n{\n    // Synchronization with the termination callbacks is tricky and must avoid:\n    //  (1) deadlocks\n    //  (2) concurrent modification of the callback list\n    //  (3) closing the process handle while the wait is still pending\n    //\n    // The strategy is to:\n    //  1. Take the lock\n    //  2. Move all clients to a local list\n    //  3. Release the lock\n    //  4. Wait for any pending callbacks (when the local vectors go out of\n    //     scope).\n    std::vector<ClientCallback> callbacks;\n    std::vector<wil::unique_threadpool_wait> waits;\n    {\n        std::lock_guard<std::mutex> lock(m_lock);\n\n        // Set m_exiting to make sure no new callbacks can be scheduled.\n        m_exiting = true;\n\n        for (auto& callback : m_callbackList)\n        {\n            for (auto& child : callback.clientProcesses)\n            {\n                waits.emplace_back(child.terminationWait.release());\n            }\n\n            callbacks.emplace_back(std::move(callback));\n        }\n\n        m_callbackList.clear();\n    }\n}\n\nULONG64 LifetimeManager::GetRegistrationId()\n{\n    std::lock_guard<std::mutex> lock(m_lock);\n    THROW_IF_FAILED(ULong64Add(m_nextClientKey, 1, &m_nextClientKey));\n\n    return m_nextClientKey;\n}\n\nbool LifetimeManager::IsAnyProcessRegistered(_In_ ULONG64 ClientKey)\n{\n    std::lock_guard<std::mutex> lock(m_lock);\n    const auto client = _FindClient(ClientKey);\n    return (client != m_callbackList.end());\n}\n\nvoid LifetimeManager::RegisterCallback(_In_ ULONG64 ClientKey, _In_ const std::function<bool(void)>& Callback, _In_opt_ HANDLE ClientProcess, _In_ DWORD TimeoutMs)\n{\n    std::lock_guard<std::mutex> lock(m_lock);\n    auto client = _FindClient(ClientKey);\n    if (client == m_callbackList.end())\n    {\n        ClientCallback newClient{};\n        newClient.callback = std::move(Callback);\n        newClient.clientKey = ClientKey;\n        newClient.timeout = TimeoutMs;\n        newClient.CreateTimer(s_OnTimeout, this);\n        if (!ARGUMENT_PRESENT(ClientProcess))\n        {\n            newClient.SetTimer(TimeoutMs);\n        }\n\n        m_callbackList.emplace_back(std::move(newClient));\n        client = _FindClient(ClientKey);\n    }\n    else\n    {\n        // If a client was found, update the callback and timeout and cancel\n        // any pending timer.\n        client->callback = std::move(Callback);\n        client->timeout = TimeoutMs;\n        if (ARGUMENT_PRESENT(ClientProcess))\n        {\n            client->CancelTimer();\n        }\n    }\n\n    WI_ASSERT(client != m_callbackList.end());\n\n    if (ARGUMENT_PRESENT(ClientProcess))\n    {\n        const auto proc = client->FindProcess(ClientProcess);\n        if (proc == client->clientProcesses.end())\n        {\n            OwnedProcess newProcess{};\n            newProcess.process.reset(wsl::windows::common::wslutil::DuplicateHandle(ClientProcess));\n            newProcess.InitializeListenForTermination(s_OnClientProcessTerminated, this);\n            client->clientProcesses.emplace_back(std::move(newProcess));\n            client->clientProcesses.back().ListenForTermination();\n        }\n    }\n}\n\nbool LifetimeManager::RemoveCallback(_In_ ULONG64 ClientKey)\n{\n    bool callbackFound = false;\n    ClientCallback oldClient{};\n    std::lock_guard<std::mutex> lock(m_lock);\n    const auto client = _FindClient(ClientKey);\n    if (client != m_callbackList.end())\n    {\n        oldClient = std::move(*client);\n        m_callbackList.erase(client);\n        callbackFound = true;\n    }\n\n    return callbackFound;\n}\n\nVOID CALLBACK LifetimeManager::s_OnClientProcessTerminated(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_WAIT Wait, _In_ TP_WAIT_RESULT WaitResult)\n{\n    UNREFERENCED_PARAMETER(WaitResult);\n    WI_ASSERT(WaitResult == WAIT_OBJECT_0);\n\n    try\n    {\n        const auto manager = static_cast<LifetimeManager*>(Context);\n        ClientCallback clientLocal{};\n        wil::unique_threadpool_wait previousCallbackWait{};\n\n        // Search for a callback with a matching threadpool wait.\n        {\n            std::list<OwnedProcess>::iterator proc;\n            std::lock_guard<std::mutex> lock(manager->m_lock);\n            const auto client = std::find_if(manager->m_callbackList.begin(), manager->m_callbackList.end(), [&](ClientCallback& c) {\n                proc = std::find_if(c.clientProcesses.begin(), c.clientProcesses.end(), [&Wait](const OwnedProcess& p) {\n                    return (p.terminationWait.get() == Wait);\n                });\n\n                return (proc != c.clientProcesses.end());\n            });\n\n            if (client != manager->m_callbackList.end())\n            {\n                previousCallbackWait.reset(proc->terminationWait.release());\n                previousCallbackWait.swap(manager->m_lastCallbackWait);\n\n                // If this is the last client process, execute the callback\n                // or queue a timer if a timeout was specified.\n                //\n                // N.B. The callback must be executed after dropping the lock.\n                client->clientProcesses.erase(proc);\n                if (client->clientProcesses.empty())\n                {\n                    if (client->timeout == 0)\n                    {\n                        clientLocal = std::move(*client);\n                        manager->m_callbackList.erase(client);\n                    }\n                    else\n                    {\n                        client->SetTimer(client->timeout);\n                    }\n                }\n            }\n        }\n\n        // Callbacks that have a zero timeout must return success because they\n        // are not retried.\n        if (clientLocal.callback)\n        {\n            WI_VERIFY(clientLocal.callback());\n        }\n    }\n    CATCH_LOG()\n}\n\nVOID CALLBACK LifetimeManager::s_OnTimeout(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER Timer)\n{\n    try\n    {\n        const auto manager = static_cast<LifetimeManager*>(Context);\n        ClientCallback clientLocal;\n        wil::unique_threadpool_timer previousTimerWait{};\n\n        // Search for a callback with a matching timer.\n        {\n            std::lock_guard<std::mutex> lock(manager->m_lock);\n            const auto client =\n                std::find_if(manager->m_callbackList.begin(), manager->m_callbackList.end(), [Timer](const ClientCallback& c) {\n                    return (Timer == c.timer.get());\n                });\n\n            if ((client != manager->m_callbackList.end()) && (client->clientProcesses.empty()))\n            {\n                clientLocal = std::move(*client);\n                manager->m_callbackList.erase(client);\n            }\n\n            // The destructor has not run because m_callbackList is not empty.\n            // Put the current timer in previousTimerWait to make sure the destructor waits for us if\n            // it runs after we drop manager->m_lock.\n            clientLocal.CancelTimer();\n            previousTimerWait.reset(clientLocal.timer.release());\n            previousTimerWait.swap(manager->m_lastTimerWait);\n        }\n\n        // If a callback was found, execute it. If the callback succeeds the\n        // timer is cancelled. Otherwise, the callback is retried.\n        //\n        // N.B. The callback must be executed after dropping the lock.\n        if (clientLocal.callback)\n        {\n            if (!clientLocal.callback())\n            {\n                std::lock_guard<std::mutex> lock(manager->m_lock);\n\n                // Only re-queue the timer if not exiting (see ClearCallbacks())\n                if (!manager->m_exiting)\n                {\n                    clientLocal.CreateTimer(s_OnTimeout, manager);\n                    clientLocal.SetTimer(clientLocal.timeout);\n                    manager->m_callbackList.emplace_back(std::move(clientLocal));\n                }\n            }\n        }\n    }\n    CATCH_LOG()\n}\n\n_Requires_lock_held_(m_lock)\nstd::list<LifetimeManager::ClientCallback>::iterator LifetimeManager::_FindClient(_In_ ULONG64 ClientKey)\n{\n    return std::find_if(m_callbackList.begin(), m_callbackList.end(), [&ClientKey](const ClientCallback& c) {\n        return (ClientKey == c.clientKey);\n    });\n}\n\nLifetimeManager::OwnedProcess::OwnedProcess()\n{\n}\n\nLifetimeManager::OwnedProcess::~OwnedProcess()\n{\n}\n\nLifetimeManager::OwnedProcess::OwnedProcess(OwnedProcess&& other) noexcept\n{\n    *this = std::move(other);\n}\n\nvoid LifetimeManager::OwnedProcess::operator=(OwnedProcess&& source) noexcept\n{\n    process = std::move(source.process);\n    terminationWait = std::move(source.terminationWait);\n}\n\nvoid LifetimeManager::OwnedProcess::InitializeListenForTermination(_In_ PTP_WAIT_CALLBACK Callback, _In_ PVOID Context)\n{\n    terminationWait.reset(CreateThreadpoolWait(Callback, Context, nullptr));\n    THROW_LAST_ERROR_IF(!terminationWait);\n}\n\nvoid LifetimeManager::OwnedProcess::ListenForTermination() const\n{\n    SetThreadpoolWait(terminationWait.get(), process.get(), nullptr);\n}\n\nLifetimeManager::ClientCallback::ClientCallback()\n{\n}\n\nLifetimeManager::ClientCallback::~ClientCallback()\n{\n}\n\nLifetimeManager::ClientCallback::ClientCallback(ClientCallback&& other) noexcept\n{\n    *this = std::move(other);\n}\n\nvoid LifetimeManager::ClientCallback::operator=(ClientCallback&& source) noexcept\n{\n    timer = std::move(source.timer);\n    clientKey = source.clientKey;\n    callback = std::move(source.callback);\n    timeout = source.timeout;\n    clientProcesses = std::move(source.clientProcesses);\n}\n\nvoid LifetimeManager::ClientCallback::CancelTimer() const\n{\n    SetThreadpoolTimer(timer.get(), nullptr, 0, 0);\n}\n\nvoid LifetimeManager::ClientCallback::CreateTimer(_In_ PTP_TIMER_CALLBACK Callback, _In_ PVOID Context)\n{\n    timer.reset(CreateThreadpoolTimer(Callback, Context, nullptr));\n    THROW_LAST_ERROR_IF(!timer);\n}\n\nstd::list<LifetimeManager::OwnedProcess>::iterator LifetimeManager::ClientCallback::FindProcess(_In_ HANDLE Process)\n{\n    return std::find_if(clientProcesses.begin(), clientProcesses.end(), [&Process](const OwnedProcess& p) {\n        return IsSameProcess(Process, p.process.get());\n    });\n}\n\nvoid LifetimeManager::ClientCallback::SetTimer(_In_ DWORD DueTimeMs) const\n{\n    FILETIME dueTime = wil::filetime::from_int64(static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * DueTimeMs));\n    SetThreadpoolTimer(timer.get(), &dueTime, RETRY_TIMER_PERIOD, RETRY_TIMER_WINDOW);\n}\n"
  },
  {
    "path": "src/windows/service/exe/Lifetime.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Lifetime.h\n\nAbstract:\n\n    This file contains function declarations around client lifetime.\n\n--*/\n\n#pragma once\n#include <mutex>\n#include <wil/resource.h>\n\nclass LifetimeManager\n{\npublic:\n    LifetimeManager();\n    ~LifetimeManager();\n\n    LifetimeManager(const LifetimeManager&) = delete;\n    void operator=(const LifetimeManager&) = delete;\n    LifetimeManager(LifetimeManager&& source) = delete;\n\n    ULONG64 GetRegistrationId();\n\n    bool IsAnyProcessRegistered(_In_ ULONG64 ClientKey);\n\n    void RegisterCallback(_In_ ULONG64 ClientKey, _In_ const std::function<bool(void)>& Callback, _In_opt_ HANDLE ClientProcess, _In_ DWORD TimeoutMs = 0);\n\n    bool RemoveCallback(_In_ ULONG64 ClientKey);\n\n    void ClearCallbacks();\n\n    struct OwnedProcess\n    {\n        OwnedProcess();\n        ~OwnedProcess();\n        OwnedProcess(OwnedProcess&& other) noexcept;\n        void operator=(OwnedProcess&&) noexcept;\n        OwnedProcess(const OwnedProcess&) = delete;\n        void operator=(const OwnedProcess&) = delete;\n\n        void InitializeListenForTermination(_In_ PTP_WAIT_CALLBACK Callback, _In_ PVOID Context);\n        void ListenForTermination() const;\n\n        wil::unique_handle process;\n        wil::unique_threadpool_wait_nowait terminationWait;\n    };\n\n    struct ClientCallback\n    {\n        ClientCallback();\n        ~ClientCallback();\n        ClientCallback(ClientCallback&& other) noexcept;\n        void operator=(ClientCallback&&) noexcept;\n        ClientCallback(const ClientCallback&) = delete;\n        void operator=(const ClientCallback&) = delete;\n\n        void CancelTimer() const;\n        void CreateTimer(_In_ PTP_TIMER_CALLBACK Callback, _In_ PVOID Context);\n        std::list<OwnedProcess>::iterator FindProcess(_In_ HANDLE Process);\n        void SetTimer(_In_ DWORD DueTimeMs) const;\n\n        std::list<OwnedProcess> clientProcesses;\n        wil::unique_threadpool_timer_nowait timer;\n        ULONG64 clientKey{};\n        std::function<bool(void)> callback;\n        DWORD timeout{};\n    };\n\nprivate:\n    _Requires_lock_held_(m_lock)\n    std::list<ClientCallback>::iterator _FindClient(_In_ ULONG64 ClientKey);\n\n    static VOID CALLBACK s_OnClientProcessTerminated(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_WAIT Wait, _In_ TP_WAIT_RESULT WaitResult);\n\n    static VOID CALLBACK s_OnTimeout(_Inout_ PTP_CALLBACK_INSTANCE Instance, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER Timer);\n\n    std::mutex m_lock;\n\n    _Guarded_by_(m_lock) bool m_exiting = false;\n\n    _Guarded_by_(m_lock) ULONG64 m_nextClientKey;\n\n    _Guarded_by_(m_lock) std::list<ClientCallback> m_callbackList;\n\n    // N.B. There is a race that could cause AV between callbacks firing and\n    //      the destruction of the lifetime manager class. To avoid the race\n    //      create a chain of waits where each callback waits for the previous\n    //      callback to finish. The destructor of the class waits on the final\n    //      callback before returning.\n    wil::unique_threadpool_wait m_lastCallbackWait;\n    wil::unique_threadpool_timer m_lastTimerWait;\n};\n"
  },
  {
    "path": "src/windows/service/exe/LxssConsoleManager.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ConsoleManager.cpp\n\nAbstract:\n\n    This file contains function definitions around console management.\n\n--*/\n\n#include \"precomp.h\"\n#include \"LxssConsoleManager.h\"\n\nusing namespace std::placeholders;\n\nConsoleManager::ConsoleManager(_In_ const std::shared_ptr<LxssPort>& Port) : m_initPort(Port)\n{\n}\n\nConsoleManager::~ConsoleManager()\n{\n    //\n    // N.B. Leave the callback cleanup to the lifetime manager destructor.\n    //      Because a shared pointer to the ConsoleManager is passed as a\n    //      parameter when adding process callback to lifetime manager, the only\n    //      way this destructor could be reached is if the callbacks are no\n    //      longer valid.\n    //\n}\n\nstd::shared_ptr<ConsoleManager> ConsoleManager::CreateConsoleManager(_In_ const std::shared_ptr<LxssPort>& Port)\n{\n    std::shared_ptr<ConsoleManager> newConsoleManager(new ConsoleManager(Port));\n    return newConsoleManager;\n}\n\nstd::shared_ptr<LxssPort> ConsoleManager::GetSessionLeader(_In_ const CreateLxProcessConsoleData& ConsoleData, _In_ bool Elevated, _Out_opt_ bool* Created)\n{\n    auto lock = m_initPort->Lock();\n    ULONG ConsoleId = ULONG_MAX;\n    auto LocalPort = _RegisterProcess(ConsoleData, Elevated, &ConsoleId);\n    auto ConsoleManagerEraser =\n        wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _UnregisterProcess(ConsoleData.ConsoleHandle, Elevated); });\n\n    //\n    // If the session leader doesn't exist yet for a given console, create one.\n    //\n\n    bool createdLocal = false;\n    if (!LocalPort)\n    {\n        LocalPort = m_initPort->CreateSessionLeader(ConsoleData.ClientProcess.get());\n        _SetPort(ConsoleId, Elevated, LocalPort);\n        createdLocal = true;\n    }\n\n    ConsoleManagerEraser.release();\n    if (ARGUMENT_PRESENT(Created))\n    {\n        *Created = createdLocal;\n    }\n\n    return LocalPort;\n}\n\nstd::shared_ptr<LxssPort> ConsoleManager::_RegisterProcess(_In_ const CreateLxProcessConsoleData& ConsoleData, _In_ bool Elevated, _Out_ ULONG* ConsoleId)\n{\n    ULONG ConsoleIdLocal;\n    wil::unique_handle ConhostHandle;\n    std::shared_ptr<LxssPort> Port;\n    ULONG64 ClientCallbackId;\n\n    _GetConsoleInfo(ConsoleData.ConsoleHandle, ConsoleIdLocal, ConhostHandle);\n    {\n        std::lock_guard<std::mutex> lock(m_mappingListLock);\n        SessionLeaderKey key{ConsoleIdLocal, Elevated};\n        const auto mapping = m_mappings.find({ConsoleIdLocal, Elevated});\n        if (mapping == m_mappings.end())\n        {\n            SessionLeaderMapping newMapping;\n            newMapping.console = std::move(ConhostHandle);\n            newMapping.clientCallbackId = m_lifetimeManager.GetRegistrationId();\n            THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(\n                GetCurrentProcess(), ConsoleData.ClientProcess.get(), GetCurrentProcess(), &newMapping.firstClient, 0, FALSE, DUPLICATE_SAME_ACCESS));\n\n            newMapping.port = nullptr;\n            ClientCallbackId = newMapping.clientCallbackId;\n            m_mappings.emplace(std::make_pair(key, std::move(newMapping)));\n        }\n        else\n        {\n            Port = mapping->second.port;\n            ClientCallbackId = mapping->second.clientCallbackId;\n        }\n\n        m_lifetimeManager.RegisterCallback(\n            ClientCallbackId, std::bind(s_OnProcessTerminated, this, ConsoleIdLocal, Elevated), ConsoleData.ClientProcess.get());\n    }\n\n    *ConsoleId = ConsoleIdLocal;\n    return Port;\n}\n\nvoid ConsoleManager::_SetPort(_In_ ULONG ConsoleId, _In_ bool Elevated, _In_ std::shared_ptr<LxssPort>& Port)\n{\n    std::lock_guard<std::mutex> lock(m_mappingListLock);\n    const auto mapping = m_mappings.find(SessionLeaderKey{ConsoleId, Elevated});\n    if (mapping != m_mappings.end())\n    {\n        mapping->second.port = Port;\n    }\n}\n\nvoid ConsoleManager::_UnregisterProcess(_In_ const wil::unique_handle& ConsoleHandle, _In_ bool Elevated)\n{\n    ULONG ConsoleId;\n    wil::unique_handle ConhostHandle;\n    _GetConsoleInfo(ConsoleHandle, ConsoleId, ConhostHandle);\n    std::lock_guard<std::mutex> lock(m_mappingListLock);\n    const auto mapping = m_mappings.find(SessionLeaderKey{ConsoleId, Elevated});\n    if (mapping != m_mappings.end())\n    {\n        WI_VERIFY(m_lifetimeManager.RemoveCallback(mapping->second.clientCallbackId));\n        m_mappings.erase(mapping);\n    }\n}\n\nULONG ConsoleManager::s_GetConhostServerId(_In_ HANDLE ConsoleHandle)\n{\n    IO_STATUS_BLOCK IoStatus;\n    HANDLE ServerPid;\n\n    //\n    // N.B.: The ioctl for getting server pid requires a handle as its buffer,\n    //       but it isn't really a handle but a process id.\n    //\n\n    THROW_IF_NTSTATUS_FAILED(NtDeviceIoControlFile(\n        ConsoleHandle, NULL, NULL, NULL, &IoStatus, IOCTL_CONDRV_GET_SERVER_PID, NULL, 0, &ServerPid, sizeof(ServerPid)));\n\n    return HandleToUlong(ServerPid);\n}\n\nvoid ConsoleManager::_OnProcessDisconnect(_In_ ULONG ConsoleId, _In_ bool Elevated)\n{\n    wil::unique_handle firstClient;\n    std::shared_ptr<LxssPort> port;\n    {\n        std::lock_guard<std::mutex> lock(m_mappingListLock);\n        const auto mapping = m_mappings.find(SessionLeaderKey{ConsoleId, Elevated});\n        if (mapping != m_mappings.end())\n        {\n            if (!m_lifetimeManager.IsAnyProcessRegistered(mapping->second.clientCallbackId))\n            {\n                firstClient = std::move(mapping->second.firstClient);\n                port = mapping->second.port;\n                m_mappings.erase(mapping);\n            }\n        }\n    }\n\n    if (firstClient && port)\n    {\n        port->DisconnectConsole(firstClient.get());\n    }\n}\n\nvoid ConsoleManager::_GetConsoleInfo(_In_ const wil::unique_handle& ConsoleHandle, _Out_ ULONG& ConsoleId, _Out_ wil::unique_handle& ConhostHandle)\n{\n    FILE_FS_DEVICE_INFORMATION FsDeviceInformation;\n    IO_STATUS_BLOCK IoStatus;\n\n    //\n    // If no console handle was provided, use zero as the identifier.\n    //\n\n    if (!ConsoleHandle)\n    {\n        ConsoleId = 0;\n        return;\n    }\n\n    THROW_IF_NTSTATUS_FAILED(NtQueryVolumeInformationFile(\n        ConsoleHandle.get(), &IoStatus, &FsDeviceInformation, sizeof(FsDeviceInformation), FileFsDeviceInformation));\n\n    if (FsDeviceInformation.DeviceType != FILE_DEVICE_CONSOLE)\n    {\n        THROW_HR(E_UNEXPECTED);\n    }\n\n    ConsoleId = s_GetConhostServerId(ConsoleHandle.get());\n\n    //\n    // Open the conhost console process so it doesn't get closed and recycled while the process is\n    // running.\n    //\n\n    ConhostHandle.reset(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, ConsoleId));\n    THROW_LAST_ERROR_IF(!ConhostHandle);\n\n    //\n    // The conhost id needs to be queried again since it could get recycled between\n    // the query and the open.\n    //\n\n    if (ConsoleId != s_GetConhostServerId(ConsoleHandle.get()))\n    {\n        THROW_HR(E_UNEXPECTED);\n    }\n\n    WI_ASSERT(ConsoleId != 0);\n}\n\nbool ConsoleManager::s_OnProcessTerminated(_In_ ConsoleManager* Self, _In_ ULONG ConsoleId, _In_ bool Elevated)\n{\n    Self->_OnProcessDisconnect(ConsoleId, Elevated);\n    return true;\n}\n"
  },
  {
    "path": "src/windows/service/exe/LxssConsoleManager.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ConsoleManager.h\n\nAbstract:\n\n    This file contains function declarations around console management.\n\n--*/\n\n#pragma once\n#include \"precomp.h\"\n#include \"LxssCreateProcess.h\"\n#include \"Lifetime.h\"\n\nclass ConsoleManager : public std::enable_shared_from_this<ConsoleManager>\n{\npublic:\n    static std::shared_ptr<ConsoleManager> CreateConsoleManager(_In_ const std::shared_ptr<LxssPort>& Port);\n\n    ~ConsoleManager();\n\n    std::shared_ptr<LxssPort> GetSessionLeader(_In_ const CreateLxProcessConsoleData& ConsoleData, _In_ bool Elevated, _Out_opt_ bool* Created = nullptr);\n\nprivate:\n    ConsoleManager() = delete;\n    ConsoleManager(_In_ const std::shared_ptr<LxssPort>& Port);\n\n    std::shared_ptr<LxssPort> _RegisterProcess(_In_ const CreateLxProcessConsoleData& ConsoleData, _In_ bool Elevated, _Out_ ULONG* ConsoleId);\n\n    void _SetPort(_In_ ULONG ConsoleId, _In_ bool Elevated, _In_ std::shared_ptr<LxssPort>& Port);\n\n    void _UnregisterProcess(_In_ const wil::unique_handle& ConsoleHandle, _In_ bool Elevated);\n\n    struct SessionLeaderKey\n    {\n        ULONG ConsoleId;\n        bool Elevated;\n        bool operator<(const SessionLeaderKey& other) const\n        {\n            return std::tie(ConsoleId, Elevated) < std::tie(other.ConsoleId, other.Elevated);\n        }\n\n        bool operator==(const SessionLeaderKey& other) const\n        {\n            return ConsoleId == other.ConsoleId && Elevated == other.Elevated;\n        }\n    };\n\n    struct SessionLeaderMapping\n    {\n        wil::unique_handle console;\n        wil::unique_handle firstClient;\n        std::shared_ptr<LxssPort> port;\n        ULONG64 clientCallbackId;\n    };\n\n    static bool s_OnProcessTerminated(_In_ ConsoleManager* Self, _In_ ULONG ConsoleId, _In_ bool Elevated);\n\n    void _OnProcessDisconnect(_In_ ULONG ConsoleId, _In_ bool Elevated);\n\n    static void _GetConsoleInfo(_In_ const wil::unique_handle& ConsoleHandle, _Out_ ULONG& ConsoleId, _Out_ wil::unique_handle& ConhostHandle);\n\n    static ULONG s_GetConhostServerId(_In_ HANDLE ConsoleHandle);\n\n    std::mutex m_mappingListLock;\n\n    _Guarded_by_(m_mappingListLock) std::map<SessionLeaderKey, SessionLeaderMapping> m_mappings;\n    std::shared_ptr<LxssPort> m_initPort;\n    LifetimeManager m_lifetimeManager;\n};\n"
  },
  {
    "path": "src/windows/service/exe/LxssCreateProcess.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssCreateProcess.cpp\n\nAbstract:\n\n    This file contains process creation function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"LxssCreateProcess.h\"\n\nCreateLxProcessData LxssCreateProcess::ParseArguments(\n    _In_opt_ LPCSTR Filename,\n    _In_ ULONG CommandLineCount,\n    _In_reads_opt_(CommandLineCount) LPCSTR* CommandLine,\n    _In_opt_ LPCWSTR CurrentWorkingDirectory,\n    _In_opt_ LPCWSTR NtPath,\n    _In_reads_opt_(NtEnvironmentLength) PWCHAR NtEnvironment,\n    _In_ ULONG NtEnvironmentLength,\n    _In_opt_ LPCWSTR Username,\n    _In_ const std::vector<std::string>& DefaultEnvironment,\n    _In_ ULONG Flags)\n{\n    THROW_HR_IF(E_INVALIDARG, (!ARGUMENT_PRESENT(Filename) && (CommandLineCount > 1)));\n    THROW_HR_IF(E_INVALIDARG, ((CommandLineCount != 0) && !CommandLine) || (CommandLineCount > USHORT_MAX));\n\n    // Convert the input strings to counted strings that reuse the existing\n    // buffer so the length of the strings is only computed once.\n\n    CreateLxProcessData Parsed{};\n    if (ARGUMENT_PRESENT(Filename))\n    {\n        Parsed.Filename = Filename;\n        THROW_HR_IF(E_INVALIDARG, Parsed.Filename.empty());\n        Parsed.CommandLine.reserve(CommandLineCount);\n    }\n    else if (CommandLineCount > 0)\n    {\n        Parsed.CommandLine.reserve(CommandLineCount + 1);\n        Parsed.CommandLine.emplace_back(std::string(\"-c\"));\n    }\n\n    for (size_t Index = 0; Index < CommandLineCount; ++Index)\n    {\n        Parsed.CommandLine.emplace_back(std::string(CommandLine[Index]));\n    }\n\n    // Initialize the environment.\n\n    Parsed.Environment = DefaultEnvironment;\n\n    // Append the user's NT path if the configuration supports it.\n    //\n    // N.B. Failures to append user's NT path are non-fatal and errors are\n    //      logged internally.\n\n    if ((ARGUMENT_PRESENT(NtPath)) && (LXSS_INTEROP_ENABLED(Flags)) && (WI_IsFlagSet(Flags, LXSS_DISTRO_FLAGS_APPEND_NT_PATH)))\n    {\n        Parsed.NtPath = wsl::shared::string::WideToMultiByte(NtPath);\n    }\n\n    // Validate that the environment is a NUL-NUL-terminated string.\n\n    if (ARGUMENT_PRESENT(NtEnvironment))\n    {\n        for (size_t Index = 0;;)\n        {\n            const PWCHAR Current = NtEnvironment + Index;\n            const size_t Length = wcsnlen(Current, NtEnvironmentLength - Index);\n            THROW_HR_IF(E_INVALIDARG, Length == NtEnvironmentLength - Index);\n            if (Length == 0)\n            {\n                break;\n            }\n\n            Parsed.NtEnvironment.push_back(wsl::shared::string::WideToMultiByte(Current));\n            Index += Length + 1;\n        }\n    }\n\n    // Translate the username to UTF-8.\n\n    if (ARGUMENT_PRESENT(Username))\n    {\n        Parsed.Username = wsl::shared::string::WideToMultiByte(Username);\n    }\n\n    // Initialize the current working directory.\n    //\n    // N.B. An empty current working directory means the user's home path will\n    //      be used.\n\n    if (ARGUMENT_PRESENT(CurrentWorkingDirectory))\n    {\n        Parsed.CurrentWorkingDirectory = wsl::shared::string::WideToMultiByte(CurrentWorkingDirectory);\n    }\n\n    return Parsed;\n}\n\n// static function definitions\nstd::vector<gsl::byte> LxssCreateProcess::CreateMessage(_In_ LX_MESSAGE_TYPE MessageType, _In_ const CreateLxProcessData& CreateProcessData, _In_ ULONG DefaultUid)\n{\n    //\n    // Compute the size of the total message starting with the base fields and\n    // adding in the strings.\n    //\n    // N.B. The filename and command line are optional.\n    //\n\n    size_t MessageSize;\n    switch (MessageType)\n    {\n    case LxInitMessageCreateProcess:\n        MessageSize = offsetof(LX_INIT_CREATE_PROCESS, Common.Buffer);\n        break;\n\n    case LxInitMessageCreateProcessUtilityVm:\n        MessageSize = offsetof(LX_INIT_CREATE_PROCESS_UTILITY_VM, Common.Buffer);\n        break;\n\n    default:\n        THROW_HR(E_INVALIDARG);\n    }\n\n    THROW_IF_FAILED(SizeTAdd(CreateProcessData.Filename.length() + 1, MessageSize, &MessageSize));\n\n    THROW_IF_FAILED(SizeTAdd(CreateProcessData.CurrentWorkingDirectory.length(), MessageSize, &MessageSize));\n\n    THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));\n\n    if (CreateProcessData.CommandLine.size() > 0)\n    {\n        for (size_t Index = 0; Index < CreateProcessData.CommandLine.size(); ++Index)\n        {\n            THROW_IF_FAILED(SizeTAdd(CreateProcessData.CommandLine[Index].length() + 1, MessageSize, &MessageSize));\n        }\n    }\n    else\n    {\n        THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));\n    }\n\n    WI_ASSERT(CreateProcessData.Environment.size() > 0);\n\n    for (size_t Index = 0; Index < CreateProcessData.Environment.size(); ++Index)\n    {\n        WI_ASSERT(CreateProcessData.Environment[Index].length() > 0);\n\n        THROW_IF_FAILED(SizeTAdd(CreateProcessData.Environment[Index].length(), MessageSize, &MessageSize));\n\n        THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));\n    }\n\n    if (CreateProcessData.NtEnvironment.size() > 0)\n    {\n        for (size_t Index = 0; Index < CreateProcessData.NtEnvironment.size(); ++Index)\n        {\n            WI_ASSERT(CreateProcessData.NtEnvironment[Index].length() > 0);\n\n            THROW_IF_FAILED(SizeTAdd(CreateProcessData.NtEnvironment[Index].length(), MessageSize, &MessageSize));\n\n            THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));\n        }\n    }\n    else\n    {\n        THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));\n    }\n\n    THROW_IF_FAILED(SizeTAdd(CreateProcessData.NtPath.length(), MessageSize, &MessageSize));\n\n    THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));\n\n    THROW_IF_FAILED(SizeTAdd(CreateProcessData.Username.length(), MessageSize, &MessageSize));\n\n    THROW_IF_FAILED(SizeTAdd(1, MessageSize, &MessageSize));\n\n    //\n    // Allocate the zero initialized buffer and populate the base fields.\n    //\n\n    THROW_HR_IF(E_INVALIDARG, MessageSize > ULONG_MAX);\n\n    std::vector<gsl::byte> Message(MessageSize);\n    const auto MessageSpan = gsl::make_span(Message);\n    auto* MessageHeader = gslhelpers::get_struct<MESSAGE_HEADER>(MessageSpan);\n    MessageHeader->MessageType = MessageType;\n    MessageHeader->MessageSize = gsl::narrow_cast<ULONG>(MessageSize);\n    gsl::span<gsl::byte> CommonSpan;\n    if (MessageType == LxInitMessageCreateProcess)\n    {\n        CommonSpan = MessageSpan.subspan(offsetof(LX_INIT_CREATE_PROCESS, Common));\n    }\n    else\n    {\n        CommonSpan = MessageSpan.subspan(offsetof(LX_INIT_CREATE_PROCESS_UTILITY_VM, Common));\n    }\n\n    //\n    // Populate the default UID.\n    //\n\n    auto* Common = gslhelpers::get_struct<LX_INIT_CREATE_PROCESS_COMMON>(CommonSpan);\n    Common->DefaultUid = DefaultUid;\n\n    //\n    // Populate the Filename string.\n    //\n\n    size_t Offset = offsetof(LX_INIT_CREATE_PROCESS_COMMON, Buffer);\n    Common->FilenameOffset = wsl::shared::string::CopyToSpan(CreateProcessData.Filename, CommonSpan, Offset);\n\n    //\n    // Populate the CurrentWorkingDirectory string.\n    //\n    // N.B. Checks for overflow were done earlier in this function.\n    //\n\n    Common->CurrentWorkingDirectoryOffset = wsl::shared::string::CopyToSpan(CreateProcessData.CurrentWorkingDirectory, CommonSpan, Offset);\n\n    //\n    // Populate the CommandLine strings.\n    //\n\n    WI_ASSERT(CreateProcessData.CommandLine.size() <= USHORT_MAX);\n\n    Common->CommandLineOffset = gsl::narrow_cast<ULONG>(Offset);\n    Common->CommandLineCount = gsl::narrow_cast<USHORT>(CreateProcessData.CommandLine.size());\n    if (Common->CommandLineCount > 0)\n    {\n        for (USHORT Index = 0; Index < Common->CommandLineCount; ++Index)\n        {\n            wsl::shared::string::CopyToSpan(CreateProcessData.CommandLine[Index], CommonSpan, Offset);\n        }\n    }\n    else\n    {\n        Offset += 1;\n    }\n\n    //\n    // Populate the Environment strings.\n    //\n\n    WI_ASSERT(CreateProcessData.Environment.size() <= USHORT_MAX);\n\n    Common->EnvironmentOffset = gsl::narrow_cast<ULONG>(Offset);\n    Common->EnvironmentCount = gsl::narrow_cast<USHORT>(CreateProcessData.Environment.size());\n    for (size_t Index = 0; Index < CreateProcessData.Environment.size(); ++Index)\n    {\n        wsl::shared::string::CopyToSpan(CreateProcessData.Environment[Index], CommonSpan, Offset);\n    }\n\n    //\n    // Populate the NtEnvironment strings.\n    //\n\n    WI_ASSERT(CreateProcessData.NtEnvironment.size() <= USHORT_MAX);\n\n    Common->NtEnvironmentOffset = gsl::narrow_cast<ULONG>(Offset);\n    Common->NtEnvironmentCount = gsl::narrow_cast<USHORT>(CreateProcessData.NtEnvironment.size());\n    if (Common->NtEnvironmentCount > 0)\n    {\n        for (USHORT Index = 0; Index < Common->NtEnvironmentCount; ++Index)\n        {\n            wsl::shared::string::CopyToSpan(CreateProcessData.NtEnvironment[Index], CommonSpan, Offset);\n        }\n    }\n    else\n    {\n        Offset += 1;\n    }\n\n    //\n    // Populate the shell options.\n    //\n\n    Common->ShellOptions = CreateProcessData.ShellOptions;\n\n    //\n    // Populate the NtPath string.\n    //\n\n    Common->NtPathOffset = wsl::shared::string::CopyToSpan(CreateProcessData.NtPath, CommonSpan, Offset);\n\n    //\n    // Populate the Username string.\n    //\n\n    Common->UsernameOffset = wsl::shared::string::CopyToSpan(CreateProcessData.Username, CommonSpan, Offset);\n\n    WI_ASSERT(\n        ((MessageType == LxInitMessageCreateProcess) && (MessageSize == (Offset + offsetof(LX_INIT_CREATE_PROCESS, Common)))) ||\n        ((MessageType == LxInitMessageCreateProcessUtilityVm) &&\n         (MessageSize == (Offset + offsetof(LX_INIT_CREATE_PROCESS_UTILITY_VM, Common)))));\n\n    return Message;\n}"
  },
  {
    "path": "src/windows/service/exe/LxssCreateProcess.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssCreateProcess.h\n\nAbstract:\n\n    This file contains process creation function declarations.\n\n--*/\n\n#pragma once\n\n#include \"SocketChannel.h\"\n#include \"WslPluginApi.h\"\n\n// Macro to test if Windows interop is enabled.\n#define LXSS_INTEROP_FLAGS (LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING | LXSS_DISTRO_FLAGS_ENABLE_INTEROP)\n\n#define LXSS_INTEROP_ENABLED(_flags) (((_flags) & LXSS_INTEROP_FLAGS) == LXSS_INTEROP_FLAGS)\n\nusing CreateLxProcessConsoleData = struct\n{\n    wil::unique_handle ConsoleHandle;\n    wil::unique_handle ClientProcess;\n};\n\nusing CreateLxProcessContext = struct\n{\n    ULONG Flags;\n    std::vector<std::string> DefaultEnvironment;\n    wil::unique_handle UserToken;\n    bool Elevated;\n};\n\nusing CreateLxProcessData = struct\n{\n    std::string Filename;\n    std::vector<std::string> CommandLine;\n    std::vector<std::string> Environment;\n    std::vector<std::string> NtEnvironment;\n    CREATE_PROCESS_SHELL_OPTIONS ShellOptions;\n    std::string NtPath;\n    std::string CurrentWorkingDirectory;\n    std::string Username;\n};\n\nclass LxssCreateProcess\n{\npublic:\n    /// <summary>\n    /// Allocates and initializes a create process message.\n    /// </summary>\n    static std::vector<gsl::byte> CreateMessage(_In_ LX_MESSAGE_TYPE MessageType, _In_ const CreateLxProcessData& CreateProcessData, _In_ ULONG DefaultUid);\n\n    /// <summary>\n    /// Parses create process arguments.\n    /// </summary>\n    static CreateLxProcessData ParseArguments(\n        _In_opt_ LPCSTR Filename,\n        _In_ ULONG CommandLineCount,\n        _In_reads_opt_(CommandLineCount) LPCSTR* CommandLine,\n        _In_opt_ LPCWSTR CurrentWorkingDirectory,\n        _In_opt_ LPCWSTR NtPath,\n        _In_reads_opt_(NtEnvironmentLength) PWCHAR NtEnvironment,\n        _In_ ULONG NtEnvironmentLength,\n        _In_opt_ LPCWSTR Username,\n        _In_ const std::vector<std::string>& DefaultEnvironment,\n        _In_ ULONG Flags);\n\n    static inline wil::unique_socket CreateLinuxProcess(\n        _In_ LPCSTR Path, _In_ LPCSTR* Arguments, const GUID& RuntimeId, wsl::shared::SocketChannel& channel, HANDLE terminatingEvent, DWORD Timeout)\n    {\n        std::vector<char> ArgumentsData;\n        for (const auto* e = Arguments; *e != nullptr; e++)\n        {\n            ArgumentsData.insert(ArgumentsData.end(), *e, *e + strlen(*e) + 1);\n        }\n\n        ArgumentsData.emplace_back('\\0');\n\n        wsl::shared::MessageWriter<CREATE_PROCESS_MESSAGE> message(LxInitCreateProcess);\n        message.WriteString(message->PathIndex, Path);\n        gsl::copy(as_bytes(gsl::span(ArgumentsData)), message.InsertBuffer(message->CommandLineIndex, ArgumentsData.size()));\n        channel.SendMessage<CREATE_PROCESS_MESSAGE>(message.Span());\n\n        auto readResult = [&]() {\n            const auto& message = channel.ReceiveMessage<RESULT_MESSAGE<int32_t>>(nullptr, Timeout);\n            return message.Result;\n        };\n\n        auto processSocket = wsl::windows::common::hvsocket::Connect(RuntimeId, readResult(), terminatingEvent);\n\n        const auto execResult = readResult();\n        THROW_HR_IF_MSG(E_FAIL, execResult != 0, \"Failed to execute '%hs', error=%d\", Path, execResult);\n\n        return processSocket;\n    }\n};\n\ntypedef struct _LXSS_DISTRO_CONFIGURATION\n{\n    GUID DistroId;\n    DWORD State;\n    std::wstring Name;\n    DWORD Version;\n    std::filesystem::path BasePath;\n    std::wstring PackageFamilyName;\n    std::filesystem::path VhdFilePath;\n    ULONG Flags;\n    std::wstring Flavor;\n    std::wstring OsVersion;\n    std::optional<std::wstring> ShortcutPath;\n    bool RunOOBE;\n} LXSS_DISTRO_CONFIGURATION, *PLXSS_DISTRO_CONFIGURATION;\n\nclass LxssRunningInstance\n{\npublic:\n    LxssRunningInstance(int IdleTimeout) : m_idleTimeout(IdleTimeout)\n    {\n    }\n\n    LxssRunningInstance(const LxssRunningInstance&) = delete;\n    LxssRunningInstance(LxssRunningInstance&&) = delete;\n    void operator=(const LxssRunningInstance&) = delete;\n    void operator=(LxssRunningInstance&&) = delete;\n\n    virtual void CreateLxProcess(\n        _In_ const CreateLxProcessData& CreateProcessData,\n        _In_ const CreateLxProcessContext& CreateProcessContext,\n        _In_ const CreateLxProcessConsoleData& ConsoleData,\n        _In_ SHORT Columns,\n        _In_ SHORT Rows,\n        _In_ PLXSS_STD_HANDLES StdHandles,\n        _Out_ GUID* InstanceId,\n        _Out_ HANDLE* ProcessHandle,\n        _Out_ HANDLE* ServerHandle,\n        _Out_ HANDLE* StandardIn,\n        _Out_ HANDLE* StandardOut,\n        _Out_ HANDLE* StandardErr,\n        _Out_ HANDLE* CommunicationChannel,\n        _Out_ HANDLE* InteropSocket) = 0;\n\n    virtual GUID GetDistributionId() const = 0;\n    virtual std::shared_ptr<LxssPort> GetInitPort() = 0;\n    virtual ULONG64 GetLifetimeManagerId() const = 0;\n    virtual ULONG GetClientId() const = 0;\n    virtual void Initialize() = 0;\n    virtual bool RequestStop(_In_ bool Force) = 0;\n    virtual void Stop() = 0;\n    virtual void RegisterPlan9ConnectionTarget(_In_ HANDLE userToken) = 0;\n    virtual void UpdateTimezone() = 0;\n    virtual const WSLDistributionInformation* DistributionInformation() const noexcept = 0;\n\n    int GetIdleTimeout() const noexcept\n    {\n        return m_idleTimeout;\n    };\n\nprivate:\n    int m_idleTimeout;\n};\n"
  },
  {
    "path": "src/windows/service/exe/LxssHttpProxy.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssHttpProxy.cpp\n\nAbstract:\n\n    This file contains HTTP proxy related classes and helper functions for proxy queries.\n\n--*/\n\n#include \"precomp.h\"\n#include \"LxssHttpProxy.h\"\n\n#include <winhttp.h>\n#include <notifications.h>\n\n#include \"WslCoreNetworkingSupport.h\"\n\nusing namespace wsl::windows::common;\n\nstd::optional<LxssDynamicFunction<decltype(RegisterProxyChangeNotification)>> HttpProxyStateTracker::s_WinHttpRegisterProxyChangeNotification;\nstd::optional<LxssDynamicFunction<decltype(UnregisterProxyChangeNotification)>> HttpProxyStateTracker::s_WinHttpUnregisterProxyChangeNotification;\nstd::optional<LxssDynamicFunction<decltype(GetProxySettingsEx)>> HttpProxyStateTracker::s_WinHttpGetProxySettingsEx;\nstd::optional<LxssDynamicFunction<decltype(GetProxySettingsResultEx)>> HttpProxyStateTracker::s_WinHttpGetProxySettingsResultEx;\nstd::optional<LxssDynamicFunction<decltype(FreeProxySettingsEx)>> HttpProxyStateTracker::s_WinHttpFreeProxySettingsEx;\n\n// Helpers for using Winhttp's APIs\nHRESULT HttpProxyStateTracker::s_LoadWinHttpProxyMethods() noexcept\ntry\n{\n    static wil::shared_hmodule winHttpModule;\n    static std::once_flag winHttpLoadFlag;\n\n    // Load Winhttp dll only once\n    std::call_once(winHttpLoadFlag, [&]() {\n        winHttpModule.reset(LoadLibraryEx(c_winhttpModuleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));\n        THROW_LAST_ERROR_IF(!winHttpModule);\n    });\n\n    // Initialize dynamic functions for the WinHttp Proxy OS APIs\n    // Not using the throwing constructor\n    // as failures should not show up in the Error logs as they can falsely flag failure to start the container\n    LxssDynamicFunction<decltype(RegisterProxyChangeNotification)> local_WinHttpRegisterProxyChangeNotification{DynamicFunctionErrorLogs::None};\n    LxssDynamicFunction<decltype(UnregisterProxyChangeNotification)> local_WinHttpUnregisterProxyChangeNotification{DynamicFunctionErrorLogs::None};\n    LxssDynamicFunction<decltype(GetProxySettingsEx)> local_WinHttpGetProxySettingsEx{DynamicFunctionErrorLogs::None};\n    LxssDynamicFunction<decltype(GetProxySettingsResultEx)> local_WinHttpGetProxySettingsResultEx{DynamicFunctionErrorLogs::None};\n    LxssDynamicFunction<decltype(FreeProxySettingsEx)> local_WinHttpFreeProxySettingsEx{DynamicFunctionErrorLogs::None};\n\n    // try to load each function - only save if all succeed\n    RETURN_IF_FAILED_EXPECTED(\n        local_WinHttpRegisterProxyChangeNotification.load(winHttpModule, \"WinHttpRegisterProxyChangeNotification\"));\n    RETURN_IF_FAILED_EXPECTED(\n        local_WinHttpUnregisterProxyChangeNotification.load(winHttpModule, \"WinHttpUnregisterProxyChangeNotification\"));\n    RETURN_IF_FAILED_EXPECTED(local_WinHttpGetProxySettingsEx.load(winHttpModule, \"WinHttpGetProxySettingsEx\"));\n    RETURN_IF_FAILED_EXPECTED(local_WinHttpGetProxySettingsResultEx.load(winHttpModule, \"WinHttpGetProxySettingsResultEx\"));\n    RETURN_IF_FAILED_EXPECTED(local_WinHttpFreeProxySettingsEx.load(winHttpModule, \"WinHttpFreeProxySettingsEx\"));\n\n    s_WinHttpRegisterProxyChangeNotification.emplace(std::move(local_WinHttpRegisterProxyChangeNotification));\n    s_WinHttpUnregisterProxyChangeNotification.emplace(std::move(local_WinHttpUnregisterProxyChangeNotification));\n    s_WinHttpGetProxySettingsEx.emplace(std::move(local_WinHttpGetProxySettingsEx));\n    s_WinHttpGetProxySettingsResultEx.emplace(std::move(local_WinHttpGetProxySettingsResultEx));\n    s_WinHttpFreeProxySettingsEx.emplace(std::move(local_WinHttpFreeProxySettingsEx));\n    return S_OK;\n}\nCATCH_RETURN()\n\nvoid FreeHttpProxySettings(WINHTTP_PROXY_SETTINGS_EX* proxySettings) noexcept\ntry\n{\n    THROW_IF_WIN32_ERROR(HttpProxyStateTracker::s_WinHttpFreeProxySettingsEx.value()(WinHttpProxySettingsTypeWsl, proxySettings));\n}\nCATCH_LOG()\n\nauto CallbackStatusToString(DWORD internetStatus) noexcept\n{\n    switch (internetStatus)\n    {\n    case WINHTTP_CALLBACK_STATUS_GETPROXYSETTINGS_COMPLETE:\n        return \"WINHTTP_CALLBACK_STATUS_GETPROXYSETTINGS_COMPLETE\";\n    case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:\n        return \"WINHTTP_CALLBACK_STATUS_REQUEST_ERROR\";\n    case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:\n        return \" WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\";\n    default:\n        return \"Invalid status\";\n    }\n}\n\n// struct to contain proxy specific settings\n\nvoid LogHttpProxySettings(const HttpProxySettings& settings) noexcept\ntry\n{\n    WSL_LOG(\"OnProxyRequestComplete\", TraceLoggingValue(settings.ToString().c_str(), \"newProxySettings\"));\n}\nCATCH_LOG()\n\nHttpProxySettings::HttpProxySettings(const WINHTTP_PROXY_SETTINGS_EX& proxySettings)\n{\n    if (WI_IsFlagSet(proxySettings.ullFlags, WINHTTP_PROXY_TYPE_PROXY))\n    {\n        Proxy = wsl::shared::string::WideToMultiByte(proxySettings.pcwszProxy);\n        SecureProxy = wsl::shared::string::WideToMultiByte(proxySettings.pcwszSecureProxy);\n\n        const auto proxyBypasses = wil::make_range(proxySettings.rgpcwszProxyBypasses, proxySettings.cProxyBypasses);\n        std::transform(std::cbegin(proxyBypasses), std::cend(proxyBypasses), std::back_inserter(ProxyBypasses), [](const auto& proxyBypass) {\n            return wsl::shared::string::WideToMultiByte(proxyBypass);\n        });\n\n        if (!ProxyBypasses.empty())\n        {\n            ProxyBypassesComma = std::accumulate(\n                std::next(std::cbegin(ProxyBypasses)),\n                std::cend(ProxyBypasses),\n                ProxyBypasses.front(),\n                [](std::string previous, const std::string& proxyBypass) { return std::move(previous) + \",\" + proxyBypass; });\n        }\n    }\n\n    if (WI_IsFlagSet(proxySettings.ullFlags, WINHTTP_PROXY_TYPE_AUTO_PROXY_URL))\n    {\n        PacUrl = wsl::shared::string::WideToMultiByte(proxySettings.pcwszAutoconfigUrl);\n    }\n}\n\nstd::string HttpProxySettings::ToString() const\n{\n    std::ostringstream httpProxySettingsString{};\n    httpProxySettingsString << \"Proxy: \" << Proxy << \", SecureProxy: \" << SecureProxy << \", PacUrl: \" << PacUrl\n                            << \", ProxyBypasses: \" << ProxyBypassesComma;\n    return httpProxySettingsString.str();\n}\n\nbool HttpProxySettings::HasSettingsConfigured() const\n{\n    return !(Proxy.empty() && SecureProxy.empty() && PacUrl.empty());\n}\n\nvoid CALLBACK HttpProxyStateTracker::s_GetProxySettingsExCallback(\n    _In_ HINTERNET resolver, _In_ DWORD_PTR context, _In_ DWORD internetStatus, _In_ PVOID statusInformation, _In_ DWORD) noexcept\ntry\n{\n    HttpProxyStateTracker* proxyTracker = reinterpret_cast<HttpProxyStateTracker*>(context);\n    const WINHTTP_ASYNC_RESULT* pAsyncResult = static_cast<WINHTTP_ASYNC_RESULT*>(statusInformation);\n\n    if (!proxyTracker)\n    {\n        return;\n    }\n\n    WSL_LOG(\n        \"s_GetProxySettingsExCallback-CallbackInfo\", TraceLoggingValue(CallbackStatusToString(internetStatus), \"internetStatus\"));\n\n    // This is the last WinHttp callback for this request, received after the request handles were closed.\n    if (internetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING)\n    {\n        proxyTracker->m_callbackQueue.submit([proxyTracker] { proxyTracker->RequestClosed(); });\n        return;\n    }\n\n    DWORD error = ERROR_SUCCESS;\n    PCSTR executionStep = \"\";\n    unique_winhttp_proxy_settings proxySettings{};\n    switch (internetStatus)\n    {\n    case WINHTTP_CALLBACK_STATUS_GETPROXYSETTINGS_COMPLETE:\n    {\n        executionStep = \"WinHttpGetProxySettingsResultEx\";\n        error = s_WinHttpGetProxySettingsResultEx.value()(resolver, &proxySettings);\n        break;\n    }\n    case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:\n    {\n        executionStep = \"CallbackError\";\n        error = pAsyncResult->dwError;\n        break;\n    }\n\n    default:\n    {\n        error = ERROR_INVALID_PARAMETER;\n        break;\n    }\n    }\n\n    if (SUCCEEDED_WIN32(error))\n    {\n        WSL_LOG(\n            \"s_GetProxySettingsExCallback-Results\",\n            TraceLoggingValue(proxySettings.pcwszProxy, \"pcwszProxy\"),\n            TraceLoggingValue(proxySettings.pcwszSecureProxy, \"pcwszSecureProxy\"),\n            TraceLoggingValue(proxySettings.pcwszAutoconfigUrl, \"pcwszAutoconfigUrl\"),\n            TraceLoggingValue(proxySettings.cProxyBypasses, \"cProxyBypasses\"));\n    }\n    else\n    {\n        WSL_LOG(\n            \"WinHttpGetProxySettingsExCallbackFailed\",\n            TraceLoggingValue(error, \"result\"),\n            TraceLoggingValue(executionStep, \"executionStep\"));\n    }\n    LOG_IF_WIN32_ERROR(error);\n\n    HttpProxySettings newProxySettings{proxySettings};\n    proxyTracker->m_callbackQueue.submit([proxyTracker, error, movedProxySettings = std::move(newProxySettings)]() mutable {\n        proxyTracker->RequestCompleted(error, std::move(movedProxySettings));\n    });\n}\nCATCH_LOG()\n\nvoid HttpProxyStateTracker::RequestClosed() noexcept\ntry\n{\n    WI_ASSERT(m_callbackQueue.isRunningInQueue());\n    const auto requery = m_queryState == QueryState::PendingAndQueueAdditional;\n    m_queryState = QueryState::NoQuery;\n    if (requery)\n    {\n        LOG_IF_FAILED(wil::ResultFromException([&] { QueryProxySettingsAsync(); }));\n    }\n\n    if (m_queryState == QueryState::NoQuery)\n    {\n        m_requestFinished.SetEvent();\n    }\n}\nCATCH_LOG()\n\nbool HttpProxyStateTracker::AreProxyStringsIdentical(const HttpProxySettings& newSettings) const\n{\n    if (!m_proxySettings.has_value())\n    {\n        return false;\n    }\n    // note that we do not include the UnsupportedProxyDropReason intentionally here as if that is only change we don't want to trigger a toast\n    return (\n        newSettings.Proxy == m_proxySettings->Proxy && newSettings.SecureProxy == m_proxySettings->SecureProxy &&\n        newSettings.ProxyBypasses == m_proxySettings->ProxyBypasses && newSettings.PacUrl == m_proxySettings->PacUrl);\n}\n\nvoid HttpProxyStateTracker::RequestCompleted(_In_ DWORD error, _In_ HttpProxySettings&& newProxySettings) noexcept\ntry\n{\n    WI_ASSERT(m_callbackQueue.isRunningInQueue());\n    if (SUCCEEDED_WIN32(error))\n    {\n        auto dataLock = m_proxySettingsLock.lock();\n\n        FilterProxySettingsByNetworkConfiguration(newProxySettings, m_networkMode);\n\n        if (!AreProxyStringsIdentical(newProxySettings))\n        {\n            LogHttpProxySettings(newProxySettings);\n            m_proxySettings = std::move(newProxySettings);\n\n            // If there was a setting changes, and this is not the initial proxy query, notify the user to restart WSL to get new proxy changes.\n            if (m_initialProxyQueryCompleted.is_signaled())\n            {\n                notifications::DisplayProxyChangeNotification(m_localizedProxyChangeString);\n            }\n        }\n        else\n        {\n            // note that the DropReason is not included in AreProxyStringsIdentical as we don't want to toast if that is only change,\n            // but we still want to make sure the drop reason is updated; otherwise, we risk not reporting the correct drop reason to user\n            if (newProxySettings.UnsupportedProxyDropReason != m_proxySettings->UnsupportedProxyDropReason)\n            {\n                m_proxySettings->UnsupportedProxyDropReason = newProxySettings.UnsupportedProxyDropReason;\n            }\n        }\n        m_initialProxyQueryCompleted.SetEvent();\n    }\n\n    // It is guaranteed that after closing the handles, a callback with status WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n    // will be issued to indicate that the callback has been cleaned up.\n    m_resolver.reset();\n    m_session.reset();\n}\nCATCH_LOG()\n\n// Proxy query tracking.\nvoid CALLBACK HttpProxyStateTracker::s_OnProxyChange(_In_ ULONGLONG flags, _In_ void* pContext) noexcept\ntry\n{\n    WSL_LOG(\"OnProxyChange\", TraceLoggingValue(flags, \"flags\"));\n    const auto proxyStateTracking = static_cast<HttpProxyStateTracker*>(pContext);\n\n    // Ensure this is a change notification.\n    if (WI_IsFlagClear(flags, WINHTTP_PROXY_NOTIFY_CHANGE))\n    {\n        return;\n    }\n\n    proxyStateTracking->m_callbackQueue.submit([proxyStateTracking] { proxyStateTracking->QueryProxySettingsAsync(); });\n}\nCATCH_LOG()\n\nUnsupportedProxyReason HttpProxyStateTracker::IsUnsupportedProxy(LPCWSTR proxyString, wsl::core::NetworkingMode configuration) noexcept\ntry\n{\n    if (!proxyString)\n    {\n        return UnsupportedProxyReason::Supported;\n    }\n\n    URL_COMPONENTS url{};\n    url.dwStructSize = sizeof(url); // Required for WinHttpCrackUrl\n    url.dwHostNameLength = -1;      // Indicates what we want cracked.\n    const DWORD proxyLength = static_cast<DWORD>(wcslen(proxyString));\n\n    if (proxyLength == 0)\n    {\n        return UnsupportedProxyReason::Supported;\n    }\n\n    THROW_IF_WIN32_BOOL_FALSE(WinHttpCrackUrl(proxyString, proxyLength, 0, &url));\n\n    // lpszHostName name will still include <proxy>:port portion of proxy string http://<proxy>:port, but the hostNameLength truncates the port\n    std::wstring portRemoved{url.lpszHostName, url.dwHostNameLength};\n\n    // IPv6 strings can come in format http://[<IPv6 address>]:port\n    const auto openBracket = portRemoved.find_first_of(L\"[\");\n    if (openBracket != ::std::wstring::npos)\n    {\n        const auto closeBracket = portRemoved.find_first_of(L\"]\");\n        if (closeBracket == ::std::wstring::npos || (openBracket + 1 >= closeBracket))\n        {\n            // no other of below checks can contain brackets\n            return UnsupportedProxyReason::Supported;\n        }\n        portRemoved = portRemoved.substr(openBracket + 1, closeBracket - openBracket - 1);\n    }\n\n    in6_addr addrV6{};\n    PCWSTR pStringEnd{}; // not used by us but still required for *ToAddressW\n    if (SUCCEEDED_WIN32(RtlIpv6StringToAddressW(portRemoved.c_str(), &pStringEnd, &addrV6)))\n    {\n        if (configuration != wsl::core::NetworkingMode::Mirrored)\n        {\n            return UnsupportedProxyReason::Ipv6NotMirrored; // v6 is only supported in mirrored mode\n        }\n        if (IN6_IS_ADDR_LOOPBACK(&addrV6))\n        {\n            return UnsupportedProxyReason::LoopbackV6; // v6 loopback is not supported in any network configuration\n        }\n        return UnsupportedProxyReason::Supported;\n    }\n\n    // v4 loopback is only supported in mirrored mode\n    if (configuration != wsl::core::NetworkingMode::Mirrored)\n    {\n        in_addr addrV4{};\n        if (SUCCEEDED_WIN32(RtlIpv4StringToAddressW(portRemoved.c_str(), true, &pStringEnd, &addrV4)))\n        {\n            if (IN4_IS_ADDR_LOOPBACK(&addrV4))\n            {\n                return UnsupportedProxyReason::LoopbackNotMirrored;\n            }\n            return UnsupportedProxyReason::Supported;\n        }\n\n        if (wsl::shared::string::IsEqual(portRemoved, c_loopback, true) || wsl::shared::string::IsEqual(portRemoved, c_localhost, true))\n        {\n            return UnsupportedProxyReason::LoopbackNotMirrored;\n        }\n\n        DWORD size = 0;\n        std::wstring computerName{};\n\n        if (!GetComputerNameW(nullptr, &size))\n        {\n            const DWORD err = GetLastError();\n            THROW_WIN32_IF(err, err != ERROR_BUFFER_OVERFLOW);\n        }\n        computerName.resize(size, L'\\0');\n\n        THROW_IF_WIN32_BOOL_FALSE(GetComputerNameW(computerName.data(), &size));\n\n        // remove any embedded null characters\n        const auto offset = computerName.find_first_of(L'\\0');\n        if (offset != ::std::wstring::npos)\n        {\n            computerName.resize(offset);\n        }\n\n        if (wsl::shared::string::IsEqual(computerName, portRemoved, true))\n        {\n            return UnsupportedProxyReason::LoopbackNotMirrored;\n        }\n    }\n    return UnsupportedProxyReason::Supported;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return UnsupportedProxyReason::UnsupportedError;\n}\n\nvoid HttpProxyStateTracker::FilterProxySettingsByNetworkConfiguration(HttpProxySettings& settings, wsl::core::NetworkingMode mode) noexcept\ntry\n{\n    const auto proxySupportState = IsUnsupportedProxy(wsl::shared::string::MultiByteToWide(settings.Proxy).c_str(), mode);\n    const auto secureProxySupportState = IsUnsupportedProxy(wsl::shared::string::MultiByteToWide(settings.SecureProxy).c_str(), mode);\n    if (proxySupportState != UnsupportedProxyReason::Supported)\n    {\n        settings.Proxy.clear();\n        settings.UnsupportedProxyDropReason = proxySupportState;\n    }\n\n    if (secureProxySupportState != UnsupportedProxyReason::Supported)\n    {\n        settings.SecureProxy.clear();\n        settings.UnsupportedProxyDropReason = secureProxySupportState;\n    }\n\n    // If we now have no proxy settings configured, we should clear the proxy bypasses too.\n    // Note that if one setting was cleared, but other was not, the proxy bypasses are still valid.\n    if (settings.Proxy.empty() && settings.SecureProxy.empty())\n    {\n        settings.ProxyBypasses.clear();\n        settings.ProxyBypassesComma.clear();\n    }\n\n    if (proxySupportState != UnsupportedProxyReason::Supported || secureProxySupportState != UnsupportedProxyReason::Supported)\n    {\n        WSL_LOG(\n            \"AutoProxy-DropUnsupportedSetting\",\n            TraceLoggingValue(wsl::core::ToString(mode), \"InvalidNetworkConfiguration\"),\n            TraceLoggingValue(ToString(proxySupportState), \"DropHttpProxySetting\"),\n            TraceLoggingValue(ToString(secureProxySupportState), \"DropHttpsProxySetting\"));\n    }\n}\nCATCH_LOG()\n\nvoid HttpProxyStateTracker::QueryProxySettingsAsync()\n{\n    PCSTR executionStep = \"\";\n    try\n    {\n        WI_ASSERT(m_callbackQueue.isRunningInQueue());\n        if (m_queryState == QueryState::PendingAndQueueAdditional)\n        {\n            return;\n        }\n\n        if (m_queryState == QueryState::Pending)\n        {\n            m_queryState = QueryState::PendingAndQueueAdditional;\n            WSL_LOG(\"Run another http proxy query after current completes\");\n            return;\n        }\n\n        executionStep = \"impersonate_token\";\n        auto runAsUser = wil::impersonate_token(m_userToken.get());\n\n        executionStep = \"WinHttpOpen\";\n        //\n        // Open session and setup resolver handle\n        //\n        wil::unique_winhttp_hinternet session(WinHttpOpen(\n            nullptr, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC));\n        THROW_LAST_ERROR_IF(!session.is_valid());\n\n        executionStep = \"WinHttpCreateProxyResolver\";\n        wil::unique_winhttp_hinternet resolver{};\n        THROW_IF_WIN32_ERROR(WinHttpCreateProxyResolver(session.get(), &resolver));\n\n        executionStep = \"WinHttpSetStatusCallback\";\n        // We need to set flag WINHTTP_CALLBACK_FLAG_HANDLES in order to get the WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n        // status callback when a handle is closed.\n        // Using those flags will always result in 2 callbacks, 1 on success/failure, 1 when closing the requests.\n        // Without these we risk race conditions while deconstructing the ProxyTracker.\n        THROW_LAST_ERROR_IF(\n            WinHttpSetStatusCallback(\n                resolver.get(),\n                s_GetProxySettingsExCallback,\n                WINHTTP_CALLBACK_STATUS_GETPROXYSETTINGS_COMPLETE | WINHTTP_CALLBACK_STATUS_REQUEST_ERROR | WINHTTP_CALLBACK_FLAG_HANDLES,\n                0) == WINHTTP_INVALID_STATUS_CALLBACK);\n\n        WINHTTP_PROXY_SETTINGS_PARAM ProxySettingsParam{0, nullptr, nullptr};\n\n        executionStep = \"WinHttpGetProxySettingsEx\";\n        // Query the proxy settings\n        const DWORD dwError = s_WinHttpGetProxySettingsEx.value()(\n            resolver.get(), WinHttpProxySettingsTypeWsl, &ProxySettingsParam, reinterpret_cast<DWORD_PTR>(this));\n\n        if (dwError != ERROR_IO_PENDING && dwError != ERROR_SUCCESS)\n        {\n            THROW_WIN32(dwError);\n        }\n\n        // Transfer ownership of HTTP handles and track request\n        m_resolver = std::move(resolver);\n        m_session = std::move(session);\n        m_requestFinished.ResetEvent();\n        m_queryState = QueryState::Pending;\n    }\n    catch (...)\n    {\n        const auto hr = wil::ResultFromCaughtException();\n        WSL_LOG(\"QueryProxySettingsFailed\", TraceLoggingHResult(hr, \"result\"), TraceLoggingValue(executionStep, \"executionStep\"));\n\n        throw;\n    }\n}\n\nHttpProxyStateTracker::HttpProxyStateTracker(int ProxyTimeout, HANDLE UserToken, wsl::core::NetworkingMode mode) :\n    m_networkMode{mode},\n    m_initialQueryTimeout{ProxyTimeout},\n    m_localizedProxyChangeString{wsl::shared::Localization::MessageHttpProxyChangeDetected()}\n{\n    THROW_IF_WIN32_BOOL_FALSE(::DuplicateTokenEx(UserToken, MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenImpersonation, &m_userToken));\n    m_callbackQueue.submit([this] { QueryProxySettingsAsync(); });\n    THROW_IF_WIN32_ERROR(s_WinHttpRegisterProxyChangeNotification.value()(WINHTTP_PROXY_NOTIFY_CHANGE, s_OnProxyChange, this, &m_proxyRegistrationHandle));\n}\n\nHttpProxyStateTracker::~HttpProxyStateTracker()\n{\n    // cancel Proxy change notifications, preventing queries from being triggered.\n    if (m_proxyRegistrationHandle != nullptr)\n    {\n        try\n        {\n            THROW_IF_WIN32_ERROR(s_WinHttpUnregisterProxyChangeNotification.value()(m_proxyRegistrationHandle));\n        }\n        CATCH_LOG()\n    }\n    // It is guaranteed that after closing the handles, a callback with status WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n    // will be issued and it will be the last callback for this request, making it safe to delete the ProxyStateTracker.\n    m_resolver.reset();\n    m_session.reset();\n\n    // Wait for all requests to complete. At this point no new requests can be started since we unregistered for proxy change\n    // notifications. Without this we risk racing proxy callbacks and them calling into a cleaned up ProxyStateTracker.\n    m_requestFinished.wait();\n\n    // Cancel all pending work in the queue and prevent additional requests\n    m_callbackQueue.cancel();\n}\n\nstd::optional<HttpProxySettings> HttpProxyStateTracker::WaitForInitialProxySettings()\n{\n    m_initialProxyQueryCompleted.wait(m_initialQueryTimeout);\n    auto lock = m_proxySettingsLock.lock();\n    return m_proxySettings;\n}\n\nvoid HttpProxyStateTracker::ConfigureNetworkingMode(wsl::core::NetworkingMode mode) noexcept\n{\n    auto lock = m_proxySettingsLock.lock();\n    // if we fall back to NAT mode need to strip bad settings\n    if (m_proxySettings.has_value() && mode != m_networkMode)\n    {\n        FilterProxySettingsByNetworkConfiguration(m_proxySettings.value(), mode);\n    }\n    m_networkMode = mode;\n}\n"
  },
  {
    "path": "src/windows/service/exe/LxssHttpProxy.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    LxssHttpProxy.h\r\n\r\nAbstract:\r\n\r\n    This file contains HTTP proxy related classes and helper functions for proxy queries. Based on implementation in WSA.\r\n\r\n--*/\r\n\r\n#pragma once\r\n\r\n#include <windows.h>\r\n#include <winhttp.h>\r\n\r\n#include <wil/resource.h>\r\n\r\n#include <string>\r\n#include <vector>\r\n#include <optional>\r\n\r\n#include \"WslCoreConfig.h\"\r\n#include \"WslCoreMessageQueue.h\"\r\n\r\nstatic constexpr auto c_winhttpModuleName = L\"Winhttp.dll\";\r\nstatic constexpr auto c_httpProxyLower = \"http_proxy\";\r\nstatic constexpr auto c_httpProxyUpper = \"HTTP_PROXY\";\r\nstatic constexpr auto c_httpsProxyLower = \"https_proxy\";\r\nstatic constexpr auto c_httpsProxyUpper = \"HTTPS_PROXY\";\r\nstatic constexpr auto c_proxyBypassLower = \"no_proxy\";\r\nstatic constexpr auto c_proxyBypassUpper = \"NO_PROXY\";\r\nstatic constexpr auto c_pacProxy = \"WSL_PAC_URL\";\r\nstatic constexpr auto c_loopback = L\"loopback\";\r\nstatic constexpr auto c_localhost = L\"localhost\";\r\n\r\nvoid FreeHttpProxySettings(WINHTTP_PROXY_SETTINGS_EX* proxySettings) noexcept;\r\nusing unique_winhttp_proxy_settings = wil::unique_struct<WINHTTP_PROXY_SETTINGS_EX, decltype(&FreeHttpProxySettings), FreeHttpProxySettings>;\r\n\r\n// Function declarations used for dynamically loading in Winhttp APIs.\r\nWINHTTPAPI\r\nDWORD\r\nWINAPI\r\nRegisterProxyChangeNotification(_In_ ULONGLONG ullFlags, _In_ WINHTTP_PROXY_CHANGE_CALLBACK pfnCallback, _In_ PVOID pvContext, _Out_ WINHTTP_PROXY_CHANGE_REGISTRATION_HANDLE* hRegistration);\r\n\r\nWINHTTPAPI\r\nDWORD\r\nWINAPI\r\nUnregisterProxyChangeNotification(_In_ WINHTTP_PROXY_CHANGE_REGISTRATION_HANDLE hRegistration);\r\n\r\nWINHTTPAPI\r\nDWORD\r\nWINAPI\r\nGetProxySettingsEx(\r\n    _In_ HINTERNET hResolver,\r\n    _In_ WINHTTP_PROXY_SETTINGS_TYPE ProxySettingsType,\r\n    _In_opt_ PWINHTTP_PROXY_SETTINGS_PARAM pProxySettingsParam,\r\n    _In_opt_ DWORD_PTR pContext);\r\n\r\nWINHTTPAPI\r\nDWORD\r\nWINAPI\r\nGetProxySettingsResultEx(_In_ HINTERNET hResolver, _Out_ PVOID pProxySettingsEx);\r\n\r\nWINHTTPAPI\r\nDWORD\r\nWINAPI\r\nFreeProxySettingsEx(_In_ WINHTTP_PROXY_SETTINGS_TYPE ProxySettingsType, _In_ PVOID pProxySettingsEx);\r\n\r\nenum class UnsupportedProxyReason\r\n{\r\n    Supported,\r\n    LoopbackNotMirrored,\r\n    Ipv6NotMirrored,\r\n    LoopbackV6,\r\n    UnsupportedError\r\n};\r\n\r\nconstexpr auto ToString(UnsupportedProxyReason config) noexcept\r\n{\r\n    switch (config)\r\n    {\r\n    case UnsupportedProxyReason::Supported:\r\n        return \"Supported\";\r\n    case UnsupportedProxyReason::LoopbackNotMirrored:\r\n        return \"LoopbackNotMirrored\";\r\n    case UnsupportedProxyReason::Ipv6NotMirrored:\r\n        return \"Ipv6NotMirrored\";\r\n    case UnsupportedProxyReason::LoopbackV6:\r\n        return \"LoopbackV6\";\r\n    case UnsupportedProxyReason::UnsupportedError:\r\n        return \"UnsupportedError\";\r\n    default:\r\n        return \"<unknown UnsupportedProxyReason>\";\r\n    }\r\n}\r\n\r\nstruct HttpProxySettings\r\n{\r\n    HttpProxySettings() = default;\r\n    HttpProxySettings(const WINHTTP_PROXY_SETTINGS_EX& ProxySettings);\r\n    HttpProxySettings(const HttpProxySettings&) = default;\r\n    HttpProxySettings(HttpProxySettings&&) = default;\r\n    HttpProxySettings& operator=(const HttpProxySettings&) = default;\r\n    HttpProxySettings& operator=(HttpProxySettings&&) = default;\r\n\r\n    std::string PacUrl{};\r\n    std::string Proxy{};\r\n    std::string SecureProxy{};\r\n    std::vector<std::string> ProxyBypasses{};\r\n    std::string ProxyBypassesComma{};\r\n    UnsupportedProxyReason UnsupportedProxyDropReason = UnsupportedProxyReason::Supported;\r\n\r\n    std::string ToString() const;\r\n    bool HasSettingsConfigured() const;\r\n};\r\n\r\nclass HttpProxyStateTracker\r\n{\r\n    enum class QueryState\r\n    {\r\n        NoQuery,\r\n        Pending,\r\n        PendingAndQueueAdditional\r\n    };\r\n\r\npublic:\r\n    HttpProxyStateTracker(int ProxyTimeout, HANDLE UserToken, wsl::core::NetworkingMode configuration);\r\n    ~HttpProxyStateTracker();\r\n\r\n    HttpProxyStateTracker(const HttpProxyStateTracker&) = delete;\r\n    HttpProxyStateTracker(HttpProxyStateTracker&&) = delete;\r\n    HttpProxyStateTracker& operator=(const HttpProxyStateTracker&) = delete;\r\n    HttpProxyStateTracker& operator=(HttpProxyStateTracker&&) = delete;\r\n\r\n    /// <summary>\r\n    /// If no proxy queries have completed, wait for timeout for result.\r\n    /// Otherwise, return the proxy settings.\r\n    /// </summary>\r\n    std::optional<HttpProxySettings> WaitForInitialProxySettings();\r\n\r\n    /// <summary>\r\n    /// This needs to be called after the VM is created so actual selected configuration is set.\r\n    /// </summary>\r\n    void ConfigureNetworkingMode(wsl::core::NetworkingMode mode) noexcept;\r\n\r\n    /// <summary>\r\n    /// Loads necessary WinHttpProxy APIs into static dynamic functions from DLL if they exist.\r\n    /// </summary>\r\n    static HRESULT s_LoadWinHttpProxyMethods() noexcept;\r\n\r\n    /// Static dynamic function for loading in necessary WinHttpProxy APIs\r\n    static std::optional<LxssDynamicFunction<decltype(FreeProxySettingsEx)>> s_WinHttpFreeProxySettingsEx;\r\n\r\nprivate:\r\n    /// <summary>\r\n    /// Invoked via WslCoreMessageQueue. Uses WinHttpProxy APIs to start proxy query.\r\n    /// </summary>\r\n    void QueryProxySettingsAsync();\r\n\r\n    /// <summary>\r\n    /// Invoked via WslCoreMessageQueue when a proxy request completes.\r\n    /// </summary>\r\n    /// <param name=\"error\"> Error status of request. </param>\r\n    /// <param name=\"proxySettings\"> The current proxy settings. </param>\r\n    void RequestCompleted(_In_ DWORD error, _In_ HttpProxySettings&& newProxySettings) noexcept;\r\n\r\n    /// <summary>\r\n    ///  Invoked via WslCoreMessageQueue when a proxy request closes.\r\n    /// </summary>\r\n    void RequestClosed() noexcept;\r\n\r\n    /// <summary>\r\n    /// Checks if two proxy settings are identical.\r\n    /// </summary>\r\n    bool AreProxyStringsIdentical(const HttpProxySettings& newSettings) const;\r\n\r\n    /// <summary>\r\n    /// Memory barrier for reading/writing to m_proxySettings.\r\n    /// </summary>\r\n    wil::critical_section m_proxySettingsLock{};\r\n\r\n    /// <summary>\r\n    /// Current http proxy settings. If no queries have completed it is std::nullopt.\r\n    /// </summary>\r\n    _Guarded_by_(m_proxySettingsLock) std::optional<HttpProxySettings> m_proxySettings {};\r\n\r\n    /// <summary>\r\n    /// Current network mode. Used to determine some cases when we should send toast notification.\r\n    /// </summary>\r\n    _Guarded_by_(m_proxySettingsLock) wsl::core::NetworkingMode m_networkMode = wsl::core::NetworkingMode::Nat;\r\n\r\n    /// <summary>\r\n    /// Indicates if we need to start another query after current one.\r\n    /// </summary>\r\n    QueryState m_queryState{QueryState::NoQuery};\r\n\r\n    /// <summary>\r\n    /// Used to impersonate user, as it is required for the proxy queries to run as the user; otherwise, the results will be incorrect.\r\n    /// </summary>\r\n    wil::unique_handle m_userToken{};\r\n\r\n    /// <summary>\r\n    /// Handle for tracking http proxy setting changes.\r\n    /// </summary>\r\n    WINHTTP_PROXY_CHANGE_REGISTRATION_HANDLE m_proxyRegistrationHandle{};\r\n\r\n    /// <summary>\r\n    /// Amount of time WSL will wait for proxy settings if no proxy settings have been detected by time we attempt to launch process.\r\n    /// </summary>\r\n    const int m_initialQueryTimeout = 1000;\r\n\r\n    /// <summary>\r\n    /// We resolve and store the localized proxy change string for notifications to this object, as we can't resolve it in callback from proxy query.\r\n    /// </summary>\r\n    const std::wstring m_localizedProxyChangeString{};\r\n\r\n    /// <summary>\r\n    /// Event that is set when m_proxySettings has a value.\r\n    /// </summary>\r\n    wil::slim_event_manual_reset m_initialProxyQueryCompleted{false};\r\n\r\n    /// <summary>\r\n    /// Event that is set when all tracked requests have completed.\r\n    /// </summary>\r\n    wil::slim_event_manual_reset m_requestFinished{true};\r\n\r\n    // Handles associated with the request\r\n    wil::unique_winhttp_hinternet m_session{};\r\n    wil::unique_winhttp_hinternet m_resolver{};\r\n\r\n    /// <summary>\r\n    /// Single-threaded queue to trigger work from winhttp callbacks.\r\n    /// </summary>\r\n    wsl::core::WslCoreMessageQueue m_callbackQueue{};\r\n\r\n    /// Static dynamic functions for loading in necessary WinHttpProxy APIs\r\n    static std::optional<LxssDynamicFunction<decltype(GetProxySettingsEx)>> s_WinHttpGetProxySettingsEx;\r\n    static std::optional<LxssDynamicFunction<decltype(GetProxySettingsResultEx)>> s_WinHttpGetProxySettingsResultEx;\r\n    static std::optional<LxssDynamicFunction<decltype(RegisterProxyChangeNotification)>> s_WinHttpRegisterProxyChangeNotification;\r\n    static std::optional<LxssDynamicFunction<decltype(UnregisterProxyChangeNotification)>> s_WinHttpUnregisterProxyChangeNotification;\r\n\r\n    /// <summary>\r\n    /// Callback that returns results from proxy queries.\r\n    /// </summary>\r\n    /// <param name=\"resolver\"> Resolver associated with this callback. </param>\r\n    /// <param name=\"context\"> Pointer to ProxyCallbackContext associated with callback. </param>\r\n    /// <param name=\"internetStatus\"> Status of callback. </param>\r\n    /// <param name=\"statusInformation\"> Pointer to WINHTTP_ASYNC_RESULT used for error info. </param>\r\n    static void CALLBACK s_GetProxySettingsExCallback(\r\n        _In_ HINTERNET resolver, _In_ DWORD_PTR context, _In_ DWORD internetStatus, _In_ PVOID statusInformation, _In_ DWORD) noexcept;\r\n\r\n    /// <summary>\r\n    /// Callback that notifies that an Http proxy setting change has been detected.\r\n    /// </summary>\r\n    /// <param name=\"flags\"> Flags used to verify the type of callback received. </param>\r\n    /// <param name=\"pContext\"> Pointer to ProxyStateTracker associated with callback. </param>\r\n    static void CALLBACK s_OnProxyChange(_In_ ULONGLONG flags, _In_ void* pContext) noexcept;\r\n\r\n    /// <summary>\r\n    /// Determines if a proxy setting string is localhost.\r\n    /// </summary>\r\n    /// <param name=\"proxyString\"> ProxyString to check if it is localhost. </param>\r\n    /// <param name=\"configuration\"> Current network configuration. </param>\r\n    static UnsupportedProxyReason IsUnsupportedProxy(LPCWSTR proxyString, wsl::core::NetworkingMode mode) noexcept;\r\n\r\n    /// <summary>\r\n    /// Remove invalid proxy configurations depending on network mode.\r\n    /// </summary>\r\n    /// <param name=\"settings\"> HttpProxySettings to be filtered. </param>\r\n    /// <param name=\"configuration\"> Current network configuration. </param>\r\n    static void FilterProxySettingsByNetworkConfiguration(HttpProxySettings& settings, wsl::core::NetworkingMode mode) noexcept;\r\n};\r\n"
  },
  {
    "path": "src/windows/service/exe/LxssInstance.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssInstance.cpp\n\nAbstract:\n\n    This file contains lxss instance definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"LxssInstance.h\"\n#include \"LxssSecurity.h\"\n#include \"LxssUserSession.h\"\n\n// Number of milliseconds to wait for the init process to respond.\n#define LXSS_INIT_CONNECTION_TIMEOUT_MS (30 * 1000)\n\n// Registry keys related to integrity level checks.\n#define LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK L\"DisableMixedIntegrityLaunch\"\n#define LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK_DISABLED 0\n#define LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK_ENABLED 1\n\n// Legacy folder mount information.\n#define LXSS_CACHE_MOUNT_LXSS \"/cache\"\n#define LXSS_CACHE_MOUNT_NT L\"cache\"\n#define LXSS_CACHE_PERMISSIONS (0770)\n#define LXSS_DATA_MOUNT_LXSS \"/data\"\n#define LXSS_DATA_MOUNT_NT L\"data\"\n#define LXSS_DATA_PERMISSIONS (0771)\n#define LXSS_HOME_MOUNT_LXSS \"/home\"\n#define LXSS_HOME_MOUNT_NT L\"home\"\n#define LXSS_HOME_PERMISSIONS (0755)\n#define LXSS_MNT_MOUNT_LXSS \"/mnt\"\n#define LXSS_MNT_MOUNT_NT L\"mnt\"\n#define LXSS_MNT_PERMISSIONS (0755)\n#define LXSS_ROOT_HOME_MOUNT_LXSS \"/root\"\n#define LXSS_ROOT_HOME_MOUNT_NT L\"root\"\n#define LXSS_ROOT_PERMISSIONS (0700)\n#define LXSS_ROOTFS_PERMISSIONS (0755)\n\nextern bool g_lxcoreInitialized;\n\nusing namespace std::placeholders;\nusing namespace Microsoft::WRL;\nusing namespace wsl::windows::common::filesystem;\n\nLxssInstance::LxssInstance(\n    _In_ const GUID& InstanceId,\n    _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n    _In_ ULONG DefaultUid,\n    _In_ ULONG64 ClientLifetimeId,\n    _In_ const std::function<void()>& TerminationCallback,\n    _In_ const std::function<void()>& UpdateInitCallback,\n    _In_ ULONG Flags,\n    _In_ int IdleTimeout) :\n    LxssRunningInstance(IdleTimeout),\n    m_instanceId(InstanceId),\n    m_instanceHandle(),\n    m_terminationCallback(TerminationCallback),\n    m_initialized(false),\n    m_running(false),\n    m_defaultUid(DefaultUid),\n    m_configuration(Configuration),\n    m_ntClientLifetimeId(ClientLifetimeId),\n    m_instanceBasicIntegrityLevelCheckEnabled(false),\n    m_redirectorConnectionTargets{m_configuration.Name}\n{\n    // Running a WSL1 distro is not possible if the lxcore driver is not present.\n    THROW_HR_IF(WSL_E_WSL1_NOT_SUPPORTED, !g_lxcoreInitialized);\n\n    // Copy immutable distribution data into the info structure.\n    m_distributionInfo.Id = m_configuration.DistroId;\n    m_distributionInfo.Name = m_configuration.Name.c_str();\n    m_distributionInfo.PackageFamilyName = m_configuration.PackageFamilyName.c_str();\n    m_distributionInfo.InitPid = 1;\n\n    m_ServerPort = std::make_shared<LxssServerPort>();\n    {\n        // Create a job to hold pico processes from LXSS.\n        auto runAsSessionUser = wil::CoImpersonateClient();\n\n        m_instanceJob.reset(CreateJobObjectW(nullptr, nullptr));\n        THROW_LAST_ERROR_IF(!m_instanceJob);\n\n        Security::InitializeInstanceJob(m_instanceJob.get());\n\n        // Check if VPN detection is enabled.\n        const wil::unique_hkey LxssKey = wsl::windows::common::registry::OpenLxssUserKey();\n        m_enableVpnDetection = (wsl::windows::common::registry::ReadDword(LxssKey.get(), nullptr, L\"EnableVpnDetection\", 1)) != 0;\n    }\n\n    // Store the user token for access checks.\n    m_userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n\n    // Create manual reset event that is signaled on instance termination\n    m_instanceTerminatedEvent.reset(CreateEventW(nullptr, TRUE, FALSE, nullptr));\n    THROW_LAST_ERROR_IF(!m_instanceTerminatedEvent);\n\n    // Check if integrity level check is enabled.\n    //\n    // N.B. The registry value is only writable from high-IL and above.\n    if (wsl::windows::common::registry::ReadDword(\n            HKEY_LOCAL_MACHINE, LXSS_SERVICE_REGISTRY_PATH, LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK, LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK_DISABLED) ==\n        LXSS_SERVICE_REGISTRY_INTEGRITY_CHECK_ENABLED)\n    {\n        // Query the integrity level of the caller and store it in the instance.\n        m_instanceBasicIntegrityLevel = wsl::windows::common::security::GetUserBasicIntegrityLevel(m_userToken.get());\n        m_instanceBasicIntegrityLevelCheckEnabled = true;\n    }\n\n    // Initialize mount paths.\n    _ConfigureFilesystem(Flags);\n\n    // Update the init binary if needed.\n    UpdateInitCallback();\n\n    // Create the LXSS instance\n    _StartInstance(Configuration.Flags);\n\n    // Create a threadpool wait object to listen for instance termination\n    m_terminationWait.reset(CreateThreadpoolWait(\n        [](PTP_CALLBACK_INSTANCE, PVOID context, PTP_WAIT, TP_WAIT_RESULT) {\n            try\n            {\n                const auto instance = static_cast<LxssInstance*>(context);\n                instance->OnTerminated();\n            }\n            CATCH_LOG()\n        },\n        this,\n        nullptr));\n\n    THROW_LAST_ERROR_IF(!m_terminationWait);\n\n    SetThreadpoolWait(m_terminationWait.get(), m_instanceTerminatedEvent.get(), nullptr);\n\n    // Mark the instance as started.\n    m_running = true;\n}\n\nLxssInstance::~LxssInstance()\n{\n    Stop();\n}\nbool CreateLxProcessIsValidStdHandle(_In_ PLXSS_HANDLE StdHandle)\n{\n    bool IsValid = false;\n    switch (StdHandle->HandleType)\n    {\n    case LxssHandleConsole:\n        if (StdHandle->Handle == LXSS_HANDLE_USE_CONSOLE)\n        {\n            IsValid = true;\n        }\n\n        break;\n\n    case LxssHandleInput:\n    case LxssHandleOutput:\n        if (StdHandle->Handle != LXSS_HANDLE_USE_CONSOLE)\n        {\n            IsValid = true;\n        }\n\n        break;\n    }\n\n    return IsValid;\n}\n\nvoid LxssInstance::CreateLxProcess(\n    _In_ const CreateLxProcessData& CreateProcessData,\n    _In_ const CreateLxProcessContext& CreateProcessContext,\n    _In_ const CreateLxProcessConsoleData& ConsoleData,\n    _In_ SHORT Columns,\n    _In_ SHORT Rows,\n    _In_ PLXSS_STD_HANDLES StdHandles,\n    _Out_ GUID* InstanceId,\n    _Out_ HANDLE* ProcessHandle,\n    _Out_ HANDLE* ServerHandle,\n    _Out_ HANDLE* StandardIn,\n    _Out_ HANDLE* StandardOut,\n    _Out_ HANDLE* StandardErr,\n    _Out_ HANDLE* CommunicationChannel,\n    _Out_ HANDLE* InteropSocket)\n{\n    UNREFERENCED_PARAMETER(Columns);\n    UNREFERENCED_PARAMETER(Rows);\n\n    THROW_HR_IF(E_INVALIDARG, !CreateLxProcessIsValidStdHandle(&StdHandles->StdIn));\n    THROW_HR_IF(E_INVALIDARG, !CreateLxProcessIsValidStdHandle(&StdHandles->StdOut));\n    THROW_HR_IF(E_INVALIDARG, !CreateLxProcessIsValidStdHandle(&StdHandles->StdErr));\n\n    // Check that the process is at the same basic integrity level if\n    // needed.\n    if (m_instanceBasicIntegrityLevelCheckEnabled != false)\n    {\n        const DWORD BasicIntegrityLevel =\n            wsl::windows::common::security::GetUserBasicIntegrityLevel(CreateProcessContext.UserToken.get());\n        if (m_instanceBasicIntegrityLevel != BasicIntegrityLevel)\n        {\n            if (m_instanceBasicIntegrityLevel > BasicIntegrityLevel)\n            {\n                THROW_HR(WSL_E_LOWER_INTEGRITY);\n            }\n\n            THROW_HR(WSL_E_HIGHER_INTEGRITY);\n        }\n    }\n\n    if (m_oobeCompleteEvent && !m_oobeCompleteEvent.is_signaled())\n    {\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageWaitingForOobe(m_configuration.Name.c_str()));\n        m_oobeCompleteEvent.wait();\n    }\n\n    // Duplicate the handles from the calling process into the current process.\n    std::vector<wil::unique_handle> StdHandlesLocal(3);\n    if (StdHandles->StdIn.Handle != LXSS_HANDLE_USE_CONSOLE)\n    {\n        StdHandlesLocal[0].reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(StdHandles->StdIn.Handle)));\n    }\n\n    if (StdHandles->StdOut.Handle != LXSS_HANDLE_USE_CONSOLE)\n    {\n        StdHandlesLocal[1].reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(StdHandles->StdOut.Handle)));\n    }\n\n    if (StdHandles->StdErr.Handle != LXSS_HANDLE_USE_CONSOLE)\n    {\n        StdHandlesLocal[2].reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(StdHandles->StdErr.Handle)));\n    }\n\n    // Enable symlink creation privilege on the token, which is needed\n    // to allow DrvFs to create NT symlinks.\n    //\n    // N.B. This privilege is required to create NT symlinks only when\n    //      developer mode is disabled. When developer mode is enabled,\n    //      acquiring the privilege can fail, but creating the symlink\n    //      will succeed even without the privilege. Therefore, it does\n    //      not matter if this call fails.\n    const wil::unique_handle Token = wsl::windows::common::security::GetUserToken(TokenPrimary);\n    wsl::windows::common::security::EnableTokenPrivilege(Token.get(), SE_CREATE_SYMBOLIC_LINK_NAME);\n\n    // Create an unnamed server port if the caller provided a buffer and\n    // interop is enabled.\n    wil::unique_handle ServerPort;\n    PHANDLE ServerPortPointer = nullptr;\n    if (LXSS_INTEROP_ENABLED(CreateProcessContext.Flags))\n    {\n        ServerPortPointer = &ServerPort;\n    }\n\n    // Send the create process request to the session leader.\n    bool CreatedSessionLeader;\n    const auto SessionLeader = std::static_pointer_cast<LxssMessagePort>(\n        m_consoleManager->GetSessionLeader(ConsoleData, CreateProcessContext.Elevated, &CreatedSessionLeader));\n\n    // If the session leader was just created, ensure the networking information\n    // is up-to-date.\n    if (CreatedSessionLeader)\n    {\n        _UpdateNetworkConfigurationFiles(true);\n    }\n\n    wil::unique_handle ProcessHandleLocal =\n        _CreateLxProcess(SessionLeader, CreateProcessData, StdHandlesLocal, Token, m_defaultUid, ServerPortPointer);\n\n    *InstanceId = m_instanceId;\n    *ProcessHandle = ProcessHandleLocal.release();\n    *ServerHandle = ServerPort ? ServerPort.release() : nullptr;\n    *StandardIn = nullptr;\n    *StandardOut = nullptr;\n    *StandardErr = nullptr;\n    *CommunicationChannel = nullptr;\n    *InteropSocket = nullptr;\n}\n\nULONG LxssInstance::GetClientId() const\n{\n    return LXSS_CLIENT_ID_INVALID;\n}\n\nGUID LxssInstance::GetDistributionId() const\n{\n    return m_configuration.DistroId;\n}\n\nstd::shared_ptr<LxssPort> LxssInstance::GetInitPort()\n{\n    return m_InitMessagePort;\n}\n\nvoid LxssInstance::UpdateTimezone()\n{\n    const auto timezone = wsl::windows::common::helpers::GetLinuxTimezone(m_userToken.get());\n    auto message = wsl::windows::common::helpers::GenerateTimezoneUpdateMessage(timezone);\n    auto lock = m_InitMessagePort->Lock();\n    m_InitMessagePort->Send(message.data(), gsl::narrow_cast<ULONG>(message.size()));\n}\n\nULONG64 LxssInstance::GetLifetimeManagerId() const\n{\n    return m_ntClientLifetimeId;\n}\n\nvoid LxssInstance::Initialize()\n{\n    std::lock_guard<std::mutex> lock(m_stateLock);\n    if (m_initialized)\n    {\n        return;\n    }\n\n    const auto socketPath = m_configuration.BasePath / LXSS_PLAN9_UNIX_SOCKET;\n\n    // Open the communication channel with init.\n    _InitiateConnectionToInitProcess();\n\n    // Send initial configuration information to the init daemon.\n    _InitializeConfiguration(socketPath);\n\n    // Initialize networking for the instance, this will also register for network\n    // state change notifications and send the initial network information to the\n    // init process.\n    _InitializeNetworking();\n\n    // Initialization successful.\n    m_initialized = true;\n}\n\nvoid LxssInstance::OnTerminated() const\n{\n    m_terminationCallback();\n}\n\nbool LxssInstance::RequestStop(_In_ bool Force)\n{\n    std::lock_guard<std::mutex> lock(m_stateLock);\n\n    // Send the message to the init daemon to check if the instance can be terminated.\n    bool shutdown = true;\n    if (m_InitMessagePort)\n    {\n        try\n        {\n            auto lock = m_InitMessagePort->Lock();\n            LX_INIT_TERMINATE_INSTANCE terminateMessage{};\n            terminateMessage.Header.MessageType = LxInitMessageTerminateInstance;\n            terminateMessage.Header.MessageSize = sizeof(terminateMessage);\n            terminateMessage.Force = Force;\n            m_InitMessagePort->Send(&terminateMessage, sizeof(terminateMessage));\n            LX_INIT_TERMINATE_INSTANCE::TResponse terminateResponse{};\n            m_InitMessagePort->Receive(&terminateResponse, sizeof(terminateResponse));\n            shutdown = terminateResponse.Result;\n        }\n        CATCH_LOG()\n    }\n\n    return shutdown;\n}\n\nvoid LxssInstance::Stop()\n{\n    std::lock_guard<std::mutex> lock(m_stateLock);\n\n    // Do nothing if the instance is already terminated.\n    if (!m_running)\n    {\n        return;\n    }\n\n    // Logs when a distro is stopped\n    WSL_LOG_TELEMETRY(\n        \"StopInstance\",\n        PDT_ProductAndServiceUsage,\n        TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA),\n        TraceLoggingValue(m_configuration.Name.c_str(), \"distroName\"),\n        TraceLoggingValue(LXSS_WSL_VERSION_1, \"version\"),\n        TraceLoggingValue(m_instanceId, \"instanceId\"));\n\n    // Unregister the instance termination TP wait.\n    if (m_terminationWait)\n    {\n        SetThreadpoolWait(m_terminationWait.get(), nullptr, nullptr);\n    }\n\n    // Unregister from network change notifications.\n    m_networkNotificationHandle.reset();\n\n    // Unwind in the reverse order of creation. Stop the LXSS instance, delete it.\n    LOG_IF_NTSTATUS_FAILED(::LxssClientInstanceStop(m_instanceHandle.get()));\n    m_instanceTerminatedEvent.reset();\n\n    LOG_IF_NTSTATUS_FAILED(::LxssClientInstanceDestroy(m_instanceHandle.get()));\n    m_instanceHandle.reset();\n\n    // Wait on the oobe thread.\n    if (m_oobeThread.joinable())\n    {\n        m_oobeThread.join();\n    }\n\n    // Remove the instance's Plan 9 Redirector connection targets.\n    m_redirectorConnectionTargets.RemoveAll();\n\n    // Attempt to clean up the instance's temp folder.\n    if (!m_tempPath.empty())\n    {\n        auto runAsUser = wil::impersonate_token(m_userToken.get());\n        LOG_IF_FAILED(wil::RemoveDirectoryRecursiveNoThrow(m_tempPath.c_str()));\n    }\n\n    m_running = false;\n    m_tempPath.clear();\n    m_rootDirectory.reset();\n    m_tempDirectory.reset();\n    return;\n}\n\nvoid LxssInstance::RegisterPlan9ConnectionTarget(_In_ HANDLE userToken)\n{\n    const auto path = m_configuration.BasePath / LXSS_PLAN9_UNIX_SOCKET;\n    using unique_unicode_string = wil::unique_struct<UNICODE_STRING, decltype(RtlFreeUnicodeString), RtlFreeUnicodeString>;\n    unique_unicode_string socketPath;\n    THROW_IF_NTSTATUS_FAILED(RtlDosPathNameToNtPathName_U_WithStatus(path.c_str(), &socketPath, nullptr, nullptr));\n\n    m_redirectorConnectionTargets.AddConnectionTarget(\n        userToken, {}, m_defaultUid, std::wstring_view{socketPath.Buffer, socketPath.Length / sizeof(WCHAR)});\n}\n\nconst WSLDistributionInformation* LxssInstance::DistributionInformation() const noexcept\n{\n    return &m_distributionInfo;\n}\n\nvoid LxssInstance::_ConfigureFilesystem(_In_ ULONG Flags)\n{\n    // Part of this process will try to upgrade existing LxFs folders to\n    // enable the per-directory case sensitivity flag. To allow easy detection\n    // of already processed folders, and resumption in case the process is\n    // interrupted, directories are only marked case-sensitive after their\n    // children are processed.\n    //\n    // Paths for LXSS instances look like so:\n    //\n    // <root>\n    //      \\rootfs               <-- Where the file system is located\n    //      \\temp\\{Instance GUID} <-- Where temporary files go\n    auto runAsUser = wil::CoImpersonateClient();\n\n    // Ensure the parent temp folder exists and is empty.\n    const auto tempFolder = m_configuration.BasePath / LXSS_TEMP_DIRECTORY;\n    EnsureDirectory(tempFolder.c_str());\n    LOG_IF_FAILED(wil::RemoveDirectoryRecursiveNoThrow(tempFolder.c_str(), wil::RemoveDirectoryOptions::KeepRootDirectory));\n\n    // Create a subdirectory to be used as the temp folder for this instance.\n    const auto instanceIdString = wsl::shared::string::GuidToString<wchar_t>(m_instanceId);\n    m_tempPath = tempFolder / instanceIdString;\n\n    // Make sure the directories of interest exist. Attributes are added\n    // separately because on upgrade directories without attributes may\n    // already exist.\n    EnsureDirectory(m_configuration.BasePath.c_str());\n\n    auto ensureDirectoryWithAttributes = [&](LPCWSTR directory, ULONG permissions) {\n        EnsureDirectoryWithAttributes(\n            (m_configuration.BasePath / directory).c_str(), permissions, LX_UID_ROOT, LX_GID_ROOT, Flags, m_configuration.Version);\n    };\n\n    ensureDirectoryWithAttributes(LXSS_ROOTFS_DIRECTORY, LXSS_ROOTFS_PERMISSIONS);\n\n    // If this is the legacy distribution, ensure that the additional LxFs\n    // directories exist and have the correct attributes. Otherwise, ensure that\n    // the rootfs/mnt directory exists for DrvFs mounts.\n    switch (m_configuration.Version)\n    {\n    case LXSS_DISTRO_VERSION_LEGACY:\n\n        WI_ASSERT(IsEqualGUID(LXSS_LEGACY_DISTRO_GUID, m_configuration.DistroId));\n\n        ensureDirectoryWithAttributes(LXSS_MNT_MOUNT_NT, LXSS_MNT_PERMISSIONS);\n        ensureDirectoryWithAttributes(LXSS_CACHE_MOUNT_NT, LXSS_CACHE_PERMISSIONS);\n        ensureDirectoryWithAttributes(LXSS_DATA_MOUNT_NT, LXSS_DATA_PERMISSIONS);\n        ensureDirectoryWithAttributes(LXSS_ROOT_HOME_MOUNT_NT, LXSS_ROOTFS_PERMISSIONS);\n        ensureDirectoryWithAttributes(LXSS_HOME_MOUNT_NT, LXSS_HOME_PERMISSIONS);\n        break;\n\n    case LXSS_DISTRO_VERSION_1:\n    case LXSS_DISTRO_VERSION_CURRENT:\n        break;\n\n        DEFAULT_UNREACHABLE;\n    }\n\n    // Wipe out and then recreate the temporary directory.\n    m_tempDirectory = WipeAndOpenDirectory(m_tempPath.c_str());\n\n    // Open handle to rootfs directory for the instance.\n    m_rootDirectory = OpenDirectoryHandle(m_configuration.BasePath.c_str(), true);\n}\n\nwil::unique_handle LxssInstance::_CreateLxProcess(\n    _In_ const std::shared_ptr<LxssMessagePort>& MessagePort,\n    _In_ const CreateLxProcessData& CreateProcessData,\n    _In_ const std::vector<wil::unique_handle>& StdHandles,\n    _In_ const wil::unique_handle& Token,\n    _In_ ULONG DefaultUid,\n    _Out_opt_ PHANDLE ServerPortHandle)\n{\n    auto lock = MessagePort->Lock();\n\n    // Send a create process message for the session leader.\n    auto Message = _CreateLxProcessMarshalMessage(MessagePort, CreateProcessData, StdHandles, Token, DefaultUid);\n\n    WI_ASSERT(Message.size() <= ULONG_MAX);\n\n    const auto MessageLocal = (PLX_INIT_CREATE_PROCESS)Message.data();\n    const bool AllowOOBE = WI_IsFlagSet(MessageLocal->Common.Flags, LxInitCreateProcessFlagAllowOOBE);\n    auto HandleEraser =\n        wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ReleaseHandlesFromLxProcessMarshalMessage(MessagePort, MessageLocal); });\n\n    std::unique_ptr<LxssServerPort> ServerPort;\n    if (ARGUMENT_PRESENT(ServerPortHandle) || AllowOOBE)\n    {\n        ServerPort = std::make_unique<LxssServerPort>(MessagePort->CreateUnnamedServer(&MessageLocal->IpcServerId));\n    }\n\n    MessagePort->Send(Message.data(), gsl::narrow_cast<ULONG>(Message.size()));\n\n    if (AllowOOBE)\n    {\n        auto OobeMessagePort = ServerPort->WaitForConnection();\n\n        {\n            m_oobeCompleteEvent.create(wil::EventOptions::ManualReset);\n\n            auto impersonate = wil::CoImpersonateClient();\n            auto registration = wsl::windows::service::DistributionRegistration::Open(\n                wsl::windows::common::registry::OpenLxssUserKey().get(), m_configuration.DistroId);\n\n            // Wait for a potential previous oobe thread to complete before creating a new one.\n            if (m_oobeThread.joinable())\n            {\n                m_oobeThread.join();\n            }\n\n            m_oobeThread = std::thread([this, OobeMessagePort = std::move(OobeMessagePort), registration = std::move(registration)]() mutable {\n                try\n                {\n                    // N.B. The LX_INIT_OOBE_RESULT message is only sent once the OOBE process completes, which might be waiting on user input.\n                    // Do no set a timeout here otherwise the OOBE flow will fail if the OOBE process takes longer than expected.\n                    auto Message = OobeMessagePort->Receive(INFINITE);\n                    auto* OobeResult = gslhelpers::try_get_struct<LX_INIT_OOBE_RESULT>(gsl::make_span(Message));\n                    THROW_HR_IF(E_INVALIDARG, !OobeResult || (OobeResult->Header.MessageType != LxInitOobeResult));\n\n                    WSL_LOG(\n                        \"OOBEResult\",\n                        TraceLoggingValue(OobeResult->Result, \"Result\"),\n                        TraceLoggingValue(OobeResult->DefaultUid, \"DefaultUid\"),\n                        TraceLoggingValue(m_configuration.Name.c_str(), \"Name\"),\n                        TraceLoggingValue(1, \"Version\"));\n\n                    if (OobeResult->Result == 0)\n                    {\n                        // OOBE was successful, don't run it again.\n                        m_configuration.RunOOBE = false;\n                        registration.Write(wsl::windows::service::Property::RunOOBE, 0);\n\n                        if (OobeResult->DefaultUid != -1)\n                        {\n                            registration.Write(wsl::windows::service::Property::DefaultUid, static_cast<int>(OobeResult->DefaultUid));\n                            m_defaultUid = static_cast<int>(OobeResult->DefaultUid);\n                        }\n                    }\n                }\n                CATCH_LOG()\n\n                m_oobeCompleteEvent.SetEvent();\n            });\n        }\n    }\n\n    // Wait for the session leader to send the process identifier and unmarshal\n    // the process handle.\n    LXBUS_IPC_PROCESS_ID ProcessId = {};\n    MessagePort->Receive(&ProcessId, sizeof(ProcessId));\n    HandleEraser.release();\n\n    auto ProcessEraser = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n        // Reply to the init process that the process is not unmarshaled.\n        ProcessId = 0;\n        MessagePort->Send(&ProcessId, sizeof(ProcessId));\n    });\n\n    wil::unique_handle ProcessHandle = MessagePort->UnmarshalProcess(ProcessId);\n\n    // Reply to the init process that the process is unmarshaled.\n    ProcessId = 1;\n    MessagePort->Send(&ProcessId, sizeof(ProcessId));\n\n    ProcessEraser.release();\n\n    if (ARGUMENT_PRESENT(ServerPortHandle))\n    {\n        *ServerPortHandle = ServerPort->ReleaseServerPort();\n    }\n\n    return ProcessHandle;\n}\n\nstd::vector<gsl::byte> LxssInstance::_CreateLxProcessMarshalMessage(\n    _In_ const std::shared_ptr<LxssMessagePort>& MessagePort,\n    _In_ const CreateLxProcessData& CreateProcessData,\n    _In_ const std::vector<wil::unique_handle>& StdHandles,\n    _In_ const wil::unique_handle& Token,\n    _In_ ULONG DefaultUid) const\n{\n    // Allocate a message and initialize the common parameters.\n    auto Message = LxssCreateProcess::CreateMessage(LxInitMessageCreateProcess, CreateProcessData, DefaultUid);\n\n    const auto MessageLocal = (PLX_INIT_CREATE_PROCESS)Message.data();\n\n    {\n        static_assert(LX_INIT_CREATE_PROCESS_USE_CONSOLE == 0);\n    }\n\n    {\n        static_assert(LX_INIT_CREATE_PROCESS_USE_CONSOLE == LXSS_HANDLE_USE_CONSOLE);\n    }\n\n    // Marshal the standard handles.\n\n    auto Eraser =\n        wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ReleaseHandlesFromLxProcessMarshalMessage(MessagePort, MessageLocal); });\n\n    for (size_t Index = 0; Index < StdHandles.size(); ++Index)\n    {\n        if (StdHandles[Index])\n        {\n            LXBUS_IPC_MESSAGE_MARSHAL_HANDLE_DATA HandleData = {};\n            HandleData.Handle = HandleToUlong(StdHandles[Index].get());\n            if (Index == 0)\n            {\n                HandleData.HandleType = LxBusIpcMarshalHandleTypeInput;\n            }\n            else\n            {\n                HandleData.HandleType = LxBusIpcMarshalHandleTypeOutput;\n            }\n\n            MessageLocal->StdFdIds[Index] = MessagePort->MarshalHandle(&HandleData);\n        }\n    }\n\n    // Marshal the token.\n\n    {\n        // Acquire assign primary token in order to pass the primary token for the new process.\n        auto revertPriv = wsl::windows::common::security::AcquirePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME);\n        MessageLocal->ForkTokenId = MessagePort->MarshalForkToken(Token.get());\n    }\n\n    if (m_configuration.RunOOBE && CreateProcessData.Filename.empty() && CreateProcessData.CommandLine.empty())\n    {\n        WI_SetFlag(MessageLocal->Common.Flags, LxInitCreateProcessFlagAllowOOBE);\n    }\n\n    Eraser.release();\n    return Message;\n}\n\nvoid LxssInstance::_ReleaseHandlesFromLxProcessMarshalMessage(_In_ const std::shared_ptr<LxssMessagePort>& MessagePort, _In_ PLX_INIT_CREATE_PROCESS Message)\n{\n    if (Message->ForkTokenId != 0)\n    {\n        try\n        {\n            MessagePort->ReleaseForkToken(Message->ForkTokenId);\n        }\n        CATCH_LOG()\n    }\n\n    for (ULONG Index = 0; Index < RTL_NUMBER_OF(Message->StdFdIds); ++Index)\n    {\n        if (Message->StdFdIds[Index] != LXBUS_IPC_CONSOLE_ID_INVALID)\n        {\n            try\n            {\n                MessagePort->ReleaseHandle(Message->StdFdIds[Index]);\n            }\n            CATCH_LOG()\n        }\n    }\n}\n\nstd::vector<unique_lxss_addmount> LxssInstance::_InitializeMounts() const\n{\n    // Legacy distributions have more than one LxFs mount. If this is a legacy\n    // distribution add the /home, /root, /data, /cache, and /mnt LxFs mounts.\n    std::vector<unique_lxss_addmount> mounts;\n    auto addMount = [&](PCWSTR directory, PCWSTR source, LPCSTR target, ULONG mode) {\n        mounts.emplace_back(CreateMount((m_configuration.BasePath / directory).c_str(), source, target, LXSS_FS_TYPE_LXFS, mode));\n    };\n\n    switch (m_configuration.Version)\n    {\n    case LXSS_DISTRO_VERSION_LEGACY:\n\n        WI_ASSERT(IsEqualGUID(LXSS_LEGACY_DISTRO_GUID, m_configuration.DistroId));\n\n        addMount(LXSS_ROOT_HOME_MOUNT_NT, LXSS_ROOT_HOME_MOUNT_NT, LXSS_ROOT_HOME_MOUNT_LXSS, LXSS_ROOT_PERMISSIONS);\n        addMount(LXSS_HOME_MOUNT_NT, LXSS_HOME_MOUNT_NT, LXSS_HOME_MOUNT_LXSS, LXSS_HOME_PERMISSIONS);\n        addMount(LXSS_DATA_MOUNT_NT, LXSS_DATA_MOUNT_NT, LXSS_DATA_MOUNT_LXSS, LXSS_DATA_PERMISSIONS);\n        addMount(LXSS_CACHE_MOUNT_NT, LXSS_CACHE_MOUNT_NT, LXSS_CACHE_MOUNT_LXSS, LXSS_CACHE_PERMISSIONS);\n        addMount(LXSS_MNT_MOUNT_NT, LXSS_MNT_MOUNT_NT, LXSS_MNT_MOUNT_LXSS, LXSS_MNT_PERMISSIONS);\n\n    default:\n        break;\n    }\n\n    return mounts;\n}\n\nvoid LxssInstance::_StartInstance(_In_ ULONG DistributionFlags)\n{\n    std::vector<unique_lxss_addmount> mounts;\n    wil::unique_handle instanceToken;\n    {\n        // Be in the right session for creating the instance parameters.\n        const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n        auto runAsUser = wil::impersonate_token(userToken.get());\n\n        // Initialize mount points.\n        mounts = _InitializeMounts();\n\n        // Create token for the instance\n        instanceToken = wsl::windows::common::security::CreateRestrictedToken(userToken.get());\n    }\n\n    // Create a new instance.\n    LX_KINSTANCECREATESTART createParameters = {};\n    createParameters.RootFsType = LXSS_DISTRO_USES_WSL_FS(m_configuration.Version) ? LXSS_FS_TYPE_WSLFS : LXSS_FS_TYPE_LXFS;\n    WI_SetFlagIf(createParameters.Flags, LX_KINSTANCECREATESTART_FLAG_DISABLE_DRIVE_MOUNTING, WI_IsFlagClear(DistributionFlags, LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING));\n    createParameters.InstanceId = m_instanceId;\n    createParameters.RootDirectoryHandle = HandleToULong(m_rootDirectory.get());\n    createParameters.TempDirectoryHandle = HandleToULong(m_tempDirectory.get());\n    createParameters.JobHandle = HandleToULong(m_instanceJob.get());\n    createParameters.TokenHandle = HandleToULong(instanceToken.get());\n    createParameters.KernelCommandLine = LXSS_DISTRO_DEFAULT_KERNEL_COMMAND_LINE;\n    createParameters.NumPathsToMap = static_cast<ULONG>(mounts.size());\n    createParameters.PathsToMap = static_cast<PLX_KMAPPATHS_ADDMOUNT>(mounts.data());\n    createParameters.InstanceTerminatedEventHandle = HandleToULong(m_instanceTerminatedEvent.get());\n    const wil::unique_hfile nulDevice{OpenNulDevice(GENERIC_READ | GENERIC_WRITE)};\n    LX_KINIT_FILE_DESCRIPTOR initFileDescriptors[] = {\n        {nulDevice.get(), LX_O_RDONLY, 0}, {nulDevice.get(), LX_O_WRONLY, 0}, {nulDevice.get(), LX_O_WRONLY, 0}};\n\n    createParameters.NumInitFileDescriptors = RTL_NUMBER_OF(initFileDescriptors);\n    createParameters.InitFileDescriptors = initFileDescriptors;\n\n    {\n        // Acquire assign primary token in order to pass the primary token for init process.\n        auto revertPriv = wsl::windows::common::security::AcquirePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME);\n        THROW_IF_NTSTATUS_FAILED(::LxssClientInstanceCreate(&createParameters, &m_instanceHandle));\n    }\n\n    auto deleter = wil::scope_exit([&] { LOG_IF_NTSTATUS_FAILED(::LxssClientInstanceDestroy(m_instanceHandle.get())); });\n\n    // Launch the instance.\n    const wil::unique_handle clientProcess = wsl::windows::common::wslutil::OpenCallingProcess(PROCESS_CREATE_PROCESS | SYNCHRONIZE);\n    THROW_IF_NTSTATUS_FAILED(::LxssClientInstanceStart(m_instanceHandle.get(), clientProcess.get()));\n\n    deleter.release();\n}\n\nvoid LxssInstance::_UpdateNetworkInformation()\ntry\n{\n    // Impersonate the service.\n    auto runAsSelf = wil::run_as_self();\n\n    // Update the resolv.conf file if it has changed.\n    _UpdateNetworkConfigurationFiles(false);\n    return;\n}\nCATCH_LOG()\n\nvoid LxssInstance::_UpdateNetworkConfigurationFiles(_In_ bool UpdateAlways)\n{\n    // Generate contents of /etc/resolv.conf file\n    wsl::core::networking::DnsSettingsFlags flags = wsl::core::networking::DnsSettingsFlags::IncludeIpv6Servers;\n    WI_SetFlagIf(flags, wsl::core::networking::DnsSettingsFlags::IncludeVpn, m_enableVpnDetection);\n\n    const auto dnsSettings = wsl::core::networking::HostDnsInfo::GetDnsSettings(flags);\n    std::string fileContents = GenerateResolvConf(dnsSettings);\n    std::lock_guard<std::mutex> lock(m_resolvConfLock);\n    if (!UpdateAlways && (fileContents == m_lastResolvConfContents))\n    {\n        return;\n    }\n\n    // Construct the network information message.\n    wsl::shared::MessageWriter<LX_INIT_NETWORK_INFORMATION> message(LxInitMessageNetworkInformation);\n    message.WriteString(message->FileHeaderIndex, wsl::shared::string::WideToMultiByte(LX_INIT_RESOLVCONF_FULL_HEADER));\n    message.WriteString(message->FileContentsIndex, fileContents);\n    auto messageSpan = message.Span();\n\n    // Send the message.\n    auto messagePortLock = m_InitMessagePort->Lock();\n    m_InitMessagePort->Send(messageSpan.data(), gsl::narrow_cast<ULONG>(messageSpan.size()));\n    m_lastResolvConfContents = std::move(fileContents);\n}\n\nvoid LxssInstance::_InitializeNetworking()\n{\n    m_ipTables.EnableIpTablesSupport(m_instanceHandle);\n\n    // Register for network connectivity change notifications to update network information.\n    LOG_IF_WIN32_ERROR(::NotifyNetworkConnectivityHintChange(\n        [](PVOID context, NL_NETWORK_CONNECTIVITY_HINT) { static_cast<LxssInstance*>(context)->_UpdateNetworkInformation(); }, this, TRUE, &m_networkNotificationHandle));\n}\n\nvoid LxssInstance::_InitiateConnectionToInitProcess()\n{\n    // Register the server, wait for the init process to connect to the server,\n    // and create the message event.\n    m_ServerPort->RegisterLxBusServer(m_instanceHandle, LX_INIT_SERVER_NAME);\n    std::unique_ptr<LxssMessagePort> NewMessagePort = m_ServerPort->WaitForConnection(LXSS_INIT_CONNECTION_TIMEOUT_MS);\n\n    // Associate the server port with the new message port and create a console\n    // manager that will be used to manage session leaders.\n    NewMessagePort->SetServerPort(m_ServerPort);\n    m_InitMessagePort = std::make_shared<LxssMessagePort>(std::move(NewMessagePort));\n    m_consoleManager = ConsoleManager::CreateConsoleManager(m_InitMessagePort);\n}\n\nvoid LxssInstance::_InitializeConfiguration(_In_ const std::filesystem::path& Plan9SocketPath)\n{\n    // If DrvFs mounting is supported, initialize a bitmap of all fixed drives.\n    ULONG fixedDrives = 0;\n    if (WI_IsFlagSet(m_configuration.Flags, LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING))\n    {\n        fixedDrives = EnumerateFixedDrives().first;\n    }\n\n    const auto timezone = wsl::windows::common::helpers::GetLinuxTimezone(m_userToken.get());\n    ULONG featureFlags{};\n    WI_SetFlagIf(featureFlags, LxInitFeatureRootfsCompressed, WI_IsFlagSet(GetFileAttributesW(m_configuration.BasePath.c_str()), FILE_ATTRIBUTE_COMPRESSED));\n    auto message = wsl::windows::common::helpers::GenerateConfigurationMessage(\n        m_configuration.Name, fixedDrives, m_defaultUid, timezone, Plan9SocketPath.wstring(), featureFlags);\n\n    // Send the message to the init daemon.\n    auto lock = m_InitMessagePort->Lock();\n    m_InitMessagePort->Send(message.data(), static_cast<ULONG>(message.size()));\n\n    // Init replies with information about the distribution.\n    auto buffer = m_InitMessagePort->Receive();\n    const auto span = gsl::make_span(buffer);\n    const auto* response = gslhelpers::try_get_struct<LX_INIT_CONFIGURATION_INFORMATION_RESPONSE>(span);\n    THROW_HR_IF(E_UNEXPECTED, !response || response->Header.MessageType != LxInitMessageInitializeResponse);\n\n    m_defaultUid = response->DefaultUid;\n    if (response->VersionIndex > 0)\n    {\n        m_configuration.OsVersion = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(span, response->VersionIndex));\n        m_distributionInfo.Version = m_configuration.OsVersion.c_str();\n    }\n\n    if (response->FlavorIndex > 0)\n    {\n        m_configuration.Flavor = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(span, response->FlavorIndex));\n        m_distributionInfo.Flavor = m_configuration.Flavor.c_str();\n    }\n}\n"
  },
  {
    "path": "src/windows/service/exe/LxssInstance.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssInstance.h\n\nAbstract:\n\n    This file contains lxss instance declarations.\n\n--*/\n\n#pragma once\n\n#include \"LxssIpTables.h\"\n#include \"LxssConsoleManager.h\"\n#include \"helpers.hpp\"\n#include \"Lifetime.h\"\n#include \"LxssCreateProcess.h\"\n#include \"LxssServerPort.h\"\n#include \"filesystem.hpp\"\n#include \"WslCoreHostDnsInfo.h\"\n\nclass LxssInstance;\n\n/// <summary>\n/// Represents an instance.  Instances may or may not be running. This object is\n/// created via LxssUserSession::CreateInstance.\n/// </summary>\nclass LxssInstance : public LxssRunningInstance\n{\npublic:\n    /// <summary>\n    /// Sets up a new LxssInstance.\n    /// </summary>\n    LxssInstance(\n        _In_ const GUID& InstanceId,\n        _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n        _In_ ULONG DefaultUid,\n        _In_ ULONG64 ClientLifetimeId,\n        _In_ const std::function<void()>& TerminationCallback,\n        _In_ const std::function<void()>& UpdateInitCallback,\n        _In_ ULONG Flags,\n        _In_ int IdleTimeout);\n\n    virtual ~LxssInstance();\n\n    void CreateLxProcess(\n        _In_ const CreateLxProcessData& CreateProcessData,\n        _In_ const CreateLxProcessContext& CreateProcessContext,\n        _In_ const CreateLxProcessConsoleData& ConsoleData,\n        _In_ SHORT Columns,\n        _In_ SHORT Rows,\n        _In_ PLXSS_STD_HANDLES StdHandles,\n        _Out_ GUID* InstanceId,\n        _Out_ HANDLE* ProcessHandle,\n        _Out_ HANDLE* ServerHandle,\n        _Out_ HANDLE* StandardIn,\n        _Out_ HANDLE* StandardOut,\n        _Out_ HANDLE* StandardErr,\n        _Out_ HANDLE* CommunicationChannel,\n        _Out_ HANDLE* InteropSocket) override;\n\n    /// <returns>\n    /// The instance's client identifier.\n    /// </returns>\n    ULONG GetClientId() const override;\n\n    /// <returns>\n    /// The distribution id.\n    /// </returns>\n    GUID GetDistributionId() const override;\n\n    /// <returns>\n    /// The message port to the init daemon.\n    /// </returns>\n    std::shared_ptr<LxssPort> GetInitPort() override;\n\n    /// <returns>\n    /// Informs the instance that the timezone has changed.\n    /// </returns>\n    void UpdateTimezone() override;\n\n    /// <returns>\n    /// The unique lifetime manager identifier for the instance.\n    /// </returns>\n    ULONG64 GetLifetimeManagerId() const override;\n\n    /// <summary>\n    /// This routine initializes an instance.\n    /// </summary>\n    void Initialize() override;\n\n    /// <returns>\n    /// Calls the termination callback that was registered when the instance starts.\n    /// </returns>\n    void OnTerminated() const;\n\n    /// <summary>\n    /// Requests for the instance to stop.\n    /// </summary>\n    bool RequestStop(_In_ bool Force) override;\n\n    /// <summary>\n    /// Stops the instance. Terminates the LXSS instance, and potentially\n    /// deletes the directories created for runtime of the instance.\n    /// This method can be called at any time (started or stopped) and will do the right cleanup.\n    /// </summary>\n    void Stop() override;\n\n    /// <summary>\n    /// Registers connection targets with the Plan 9 Redirector for the calling user, if they're\n    /// not already registered.\n    /// </summary>\n    void RegisterPlan9ConnectionTarget(_In_ HANDLE userToken) override;\n\n    /// <summary>\n    /// Returns information about the distribution.\n    /// </summary>\n    const WSLDistributionInformation* DistributionInformation() const noexcept override;\n\nprotected:\n    /// <summary>\n    /// Configures the filesystem for this instance. Creates both \"RootFS\"\n    /// and a \"temp\" directory that LXSS requires to function.\n    /// </summary>\n    void _ConfigureFilesystem(_In_ ULONG Flags);\n\n    /// <summary>\n    /// Creates the backing LXSS instance and starts it.\n    /// </summary>\n    void _StartInstance(_In_ ULONG DistributionFlags);\n\n    /// <summary>\n    /// Initializes mount points to be passed during instance creation.\n    /// </summary>\n    std::vector<wsl::windows::common::filesystem::unique_lxss_addmount> _InitializeMounts() const;\n\n    /// <summary>\n    /// Server port for listening and accepting of new connections.\n    /// </summary>\n    std::shared_ptr<LxssServerPort> m_ServerPort;\n\n    /// <summary>\n    /// Message port to the init process.\n    /// </summary>\n    std::shared_ptr<LxssMessagePort> m_InitMessagePort;\n\n    /// <summary>\n    /// Creates a process in the instance.\n    /// </summary>\n    wil::unique_handle _CreateLxProcess(\n        _In_ const std::shared_ptr<LxssMessagePort>& MessagePort,\n        _In_ const CreateLxProcessData& CreateProcessData,\n        _In_ const std::vector<wil::unique_handle>& StdHandles,\n        _In_ const wil::unique_handle& Token,\n        _In_ ULONG DefaultUid,\n        _Out_opt_ PHANDLE ServerPortHandle);\n\n    /// <summary>\n    /// Creates a create process message.\n    /// </summary>\n    std::vector<gsl::byte> _CreateLxProcessMarshalMessage(\n        _In_ const std::shared_ptr<LxssMessagePort>& MessagePort,\n        _In_ const CreateLxProcessData& CreateProcessData,\n        _In_ const std::vector<wil::unique_handle>& StdHandles,\n        _In_ const wil::unique_handle& Token,\n        _In_ ULONG DefaultUid) const;\n\n    /// <summary>\n    /// Cleans up orphaned handles from a create process message.\n    /// </summary>\n    static void _ReleaseHandlesFromLxProcessMarshalMessage(_In_ const std::shared_ptr<LxssMessagePort>& MessagePort, _In_ PLX_INIT_CREATE_PROCESS MessageLocal);\n\n    /// <summary>\n    /// Initializes networking information and registers for WNF network state change notifications.\n    /// </summary>\n    void _InitializeNetworking();\n\n    /// <summary>\n    /// Updates networking information for the instance.\n    /// </summary>\n    void _UpdateNetworkInformation();\n\n    /// <summary>\n    /// Initializes communication channel to the init process.\n    /// </summary>\n    void _InitiateConnectionToInitProcess();\n\n    /// <summary>\n    /// Initializes configuration information for the instance. Updates host name\n    /// information for the instance and supplies the list of DrvFs volumes to mount.\n    /// </summary>\n    void _InitializeConfiguration(_In_ const std::filesystem::path& Plan9SocketPath);\n\n    /// <summary>\n    /// Writes out configuration files used by a running Lx instance.\n    /// </summary>\n    void _UpdateNetworkConfigurationFiles(_In_ bool UpdateAlways);\n\nprivate:\n    /// <summary>\n    /// Basic state of this object - instance identifier, instance handle,\n    /// instance terminated event, and termination callback state.\n    /// </summary>\n    GUID m_instanceId;\n    wil::unique_handle m_instanceHandle;\n    wil::unique_event m_instanceTerminatedEvent;\n    std::function<void()> m_terminationCallback;\n    wil::unique_threadpool_wait m_terminationWait;\n    wil::unique_handle m_userToken;\n\n    /// <summary>\n    /// Lock to protect instance state.\n    /// </summary>\n    std::mutex m_stateLock;\n    _Guarded_by_(m_stateLock) bool m_initialized;\n    _Guarded_by_(m_stateLock) bool m_running;\n\n    /// <summary>\n    /// Provides iptables emulation support.\n    /// </summary>\n    LxssIpTables m_ipTables;\n\n    /// <summary>\n    /// Settings for updating /etc/resolv.conf.\n    /// </summary>\n    bool m_enableVpnDetection;\n    std::mutex m_resolvConfLock;\n    _Guarded_by_(m_resolvConfLock) std::string m_lastResolvConfContents;\n\n    /// <summary>\n    /// Path and directory handles for this instance.  The base path is always available, the\n    /// handles are only valid while the instance is running.\n    /// </summary>\n    std::filesystem::path m_tempPath;\n    wil::unique_hfile m_tempDirectory;\n    wil::unique_hfile m_rootDirectory;\n\n    /// <summary>\n    /// Specifies the immutable settings for the instance. These are read from\n    /// the registry when the instance is created.\n    /// </summary>\n    ULONG m_defaultUid;\n    LXSS_DISTRO_CONFIGURATION m_configuration;\n\n    /// <summary>\n    /// Specifies information about the distro.\n    /// </summary>\n    WSLDistributionInformation m_distributionInfo{};\n\n    /// <summary>\n    /// This job object contains all pico processes within the instance.\n    /// </summary>\n    wil::unique_handle m_instanceJob;\n\n    /// <summary>\n    /// Handle for network state change notifications.\n    /// </summary>\n    wsl::core::networking::unique_notify_handle m_networkNotificationHandle;\n\n    /// <summary>\n    /// Lifetime manager ID for registering NT client termination callbacks.\n    /// </summary>\n    ULONG64 m_ntClientLifetimeId;\n\n    /// <summary>\n    /// Class to keep track of client process and consoles.\n    /// </summary>\n    std::shared_ptr<ConsoleManager> m_consoleManager;\n\n    /// <summary>\n    /// Stores if the basic integrity level check is enabled.\n    /// </summary>\n    bool m_instanceBasicIntegrityLevelCheckEnabled;\n\n    /// <summary>\n    /// The basic integrity level of the caller that created the instance.\n    /// </summary>\n    DWORD m_instanceBasicIntegrityLevel;\n\n    /// <summary>\n    /// The authentication IDs used for the Plan 9 redirector connection target.\n    /// </summary>\n    wsl::windows::common::redirector::ConnectionTargetManager m_redirectorConnectionTargets;\n\n    std::thread m_oobeThread;\n    wil::unique_event m_destroyingEvent{wil::EventOptions::ManualReset};\n    wil::unique_event m_oobeCompleteEvent;\n};\n"
  },
  {
    "path": "src/windows/service/exe/LxssIpTables.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssIptables.cpp\n\nAbstract:\n\n    This file contains iptables-related function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include <mi.h>\n#include \"LxssIpTables.h\"\n\nusing unique_safearray = wil::unique_any<SAFEARRAY*, decltype(&SafeArrayDestroy), SafeArrayDestroy>;\n\nusing namespace std::placeholders;\n\n// LxssIpTables class functions.\n\nLxssIpTables::LxssIpTables()\n{\n    return;\n}\n\nstd::wstring LxssIpTables::AddressStringFromAddress(const IP_ADDRESS_PREFIX& Address, bool AddPrefixLength)\n{\n    std::wstringstream addressStream;\n    addressStream << Address.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b1 << L\".\" << Address.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b2 << L\".\"\n                  << Address.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b3 << L\".\" << Address.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b4;\n\n    if (AddPrefixLength)\n    {\n        addressStream << L\"/\" << Address.PrefixLength;\n    }\n\n    return addressStream.str();\n}\n\nvoid LxssIpTables::CleanupRemnants()\n{\n    try\n    {\n        LxssNetworkingNat::CleanupRemnants();\n    }\n    CATCH_LOG()\n\n    try\n    {\n        LxssNetworkingFirewall::CleanupRemnants();\n    }\n    CATCH_LOG()\n}\n\nvoid LxssIpTables::EnableIpTablesSupport(_In_ const wil::unique_handle& InstanceHandle)\n{\n    //\n    // Passing 'this' is OK because unregistration will be done in the\n    // destructor.\n    //\n\n    auto callback = std::bind(KernelCallbackProxy, this, _1, _2);\n    m_kernelCallback =\n        LxssUserCallback::Register(InstanceHandle.get(), LxBusUserCallbackTypeIptables, callback, sizeof(LXBUS_USER_CALLBACK_NETWORK_DATA));\n}\n\nbool LxssIpTables::IsAllowedInputPrefix(_In_ CONST IP_ADDRESS_PREFIX& InputPrefix)\n{\n    if (InputPrefix.Prefix.si_family != AF_INET)\n    {\n        LOG_HR_MSG(E_NOTIMPL, \"IPv6 addresses for NAT not supported\");\n        return false;\n    }\n\n    if (InputPrefix.Prefix.Ipv4.sin_port != 0)\n    {\n        LOG_HR_MSG(E_NOTIMPL, \"Specific ports for NAT not supported\");\n        return false;\n    }\n\n    // TODO_LX: Currently there is an agreement in place with HNS to restrict\n    //          the NAT address range to 172.17.0.0/16.\n    if ((InputPrefix.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b1 != 172) || (InputPrefix.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b2 != 17) ||\n        (InputPrefix.PrefixLength < 16))\n    {\n        LOG_HR_MSG(E_NOTIMPL, \"Address not supported for NAT: %ls\", LxssIpTables::AddressStringFromAddress(InputPrefix, true).c_str());\n\n        return false;\n    }\n\n    return true;\n}\n\nNTSTATUS\nLxssIpTables::KernelCallback(_In_ PVOID CallbackBuffer, _In_ ULONG_PTR CallbackBufferSize)\n{\n    if (CallbackBufferSize < sizeof(LXBUS_USER_CALLBACK_IPTABLES_DATA))\n    {\n        WI_ASSERT_MSG(false, \"Kernel provided unexpected data for user-mode callback.\");\n\n        return STATUS_INVALID_PARAMETER;\n    }\n\n    const auto callbackData = static_cast<PLXBUS_USER_CALLBACK_IPTABLES_DATA>(CallbackBuffer);\n\n    switch (callbackData->IptablesDataType)\n    {\n    case LxBusUserCallbackIptablesDataTypeMasquerade:\n        return KernelCallbackMasquerade(callbackData);\n    case LxBusUserCallbackIptablesDataTypePort:\n        return KernelCallbackFirewallPort(callbackData);\n    default:\n        WI_ASSERT_MSG(false, \"Kernel provided unexpected data for user-mode callback.\");\n\n        return STATUS_INVALID_PARAMETER;\n    }\n}\n\nNTSTATUS\nLxssIpTables::KernelCallbackFirewallPort(_In_ PLXBUS_USER_CALLBACK_IPTABLES_DATA CallbackData)\n{\n    CONST IP_ADDRESS_PREFIX& inputPrefix = CallbackData->Data.Port.InputPrefix;\n\n    if (inputPrefix.Prefix.si_family != AF_INET)\n    {\n        LOG_HR_MSG(E_INVALIDARG, \"IPv6 addresses for firewall ports not supported\");\n\n        return STATUS_INVALID_PARAMETER;\n    }\n\n    if (inputPrefix.Prefix.Ipv4.sin_port == 0)\n    {\n        LOG_HR_MSG(E_INVALIDARG, \"No port specified\");\n        return STATUS_INVALID_PARAMETER;\n    }\n\n    NTSTATUS status;\n    std::lock_guard<std::mutex> lock(m_lock);\n    if (CallbackData->Data.Port.Enable == FALSE)\n    {\n        status = STATUS_NOT_FOUND;\n        const auto foundEntry = std::find_if(m_firewallPorts.begin(), m_firewallPorts.end(), [&](const auto& next) {\n            return (memcmp(&inputPrefix, &next->Address(), sizeof(inputPrefix)) == 0);\n        });\n\n        if (foundEntry != m_firewallPorts.end())\n        {\n            status = STATUS_INVALID_PARAMETER;\n            try\n            {\n                m_firewallPorts.erase(foundEntry);\n                status = STATUS_SUCCESS;\n            }\n            CATCH_LOG_MSG(\"Failed to remove firewall port rule.\")\n        }\n    }\n    else\n    {\n        status = STATUS_INVALID_PARAMETER;\n        try\n        {\n            std::shared_ptr<LxssNetworkingFirewall> firewall;\n            if (m_firewallPorts.empty())\n            {\n                firewall = std::make_shared<LxssNetworkingFirewall>();\n            }\n            else\n            {\n                firewall = m_firewallPorts.front()->Firewall();\n            }\n\n            auto newPortRule = std::make_unique<LxssNetworkingFirewallPort>(firewall, inputPrefix);\n\n            m_firewallPorts.emplace_back(std::move(newPortRule));\n            status = STATUS_SUCCESS;\n        }\n        CATCH_LOG_MSG(\"Failed to create new firewall port rule.\")\n    }\n\n    return status;\n}\n\nNTSTATUS\nLxssIpTables::KernelCallbackMasquerade(_In_ PLXBUS_USER_CALLBACK_IPTABLES_DATA CallbackData)\n{\n    CONST IP_ADDRESS_PREFIX& inputPrefix = CallbackData->Data.Masquerade.InputPrefix;\n\n    if (!IsAllowedInputPrefix(inputPrefix))\n    {\n        return STATUS_INVALID_PARAMETER;\n    }\n\n    NTSTATUS status;\n    std::lock_guard<std::mutex> lock(m_lock);\n    if (CallbackData->Data.Masquerade.Enable == FALSE)\n    {\n        status = STATUS_NOT_FOUND;\n        const auto foundEntry = std::find_if(m_networkTranslators.begin(), m_networkTranslators.end(), [&](const auto& next) {\n            return (memcmp(&inputPrefix, &next->Address(), sizeof(inputPrefix)) == 0);\n        });\n\n        if (foundEntry != m_networkTranslators.end())\n        {\n            status = STATUS_INVALID_PARAMETER;\n            try\n            {\n                m_networkTranslators.erase(foundEntry);\n                status = STATUS_SUCCESS;\n            }\n            CATCH_LOG_MSG(\"Failed to remove NAT.\")\n        }\n    }\n    else\n    {\n        status = STATUS_INVALID_PARAMETER;\n        try\n        {\n            auto newNat = std::make_unique<LxssNetworkingNat>(inputPrefix);\n            m_networkTranslators.emplace_back(std::move(newNat));\n            status = STATUS_SUCCESS;\n        }\n        CATCH_LOG_MSG(\"Failed to create new NAT.\")\n    }\n\n    return status;\n}\n\nNTSTATUS\nLxssIpTables::KernelCallbackProxy(_Inout_ LxssIpTables* Self, _In_ PVOID CallbackBuffer, _In_ ULONG_PTR CallbackBufferSize)\n{\n    return Self->KernelCallback(CallbackBuffer, CallbackBufferSize);\n}\n\n// LxssManagementInterface class functions.\n\nstd::weak_ptr<MI_Application> LxssManagementInterface::s_application;\nconst std::wstring LxssManagementInterface::s_localRoot(L\"ROOT/StandardCimv2\");\nstd::mutex LxssManagementInterface::s_lock;\n\nunique_mi_instance LxssManagementInterface::CloneInstance(_In_ const MI_Instance* InstanceToClone)\n{\n    MI_Instance* rawInstance;\n    const MI_Result result = MI_Instance_Clone(InstanceToClone, &rawInstance);\n    THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), \"Failed with error %d\", result);\n\n    return unique_mi_instance(rawInstance, std::bind(CloseInstance, GetGlobalApplication(), _1));\n}\n\nvoid LxssManagementInterface::CloseGlobalApplication(_Inout_ MI_Application* Application)\n{\n    WI_VERIFY(MI_Application_Close(Application) == MI_RESULT_OK);\n    delete Application;\n}\n\nvoid LxssManagementInterface::CloseInstance(_In_ std::shared_ptr<MI_Application>, _Inout_ MI_Instance* Instance)\n{\n    WI_VERIFY(MI_Instance_Delete(Instance) == MI_RESULT_OK);\n}\n\nvoid LxssManagementInterface::CloseOperation(_Inout_ MI_Operation* Operation)\n{\n    // If an operation is in progress, close will wait for it to complete.\n    // Always attempt to cancel the operation before closing it.\n    (void)MI_Operation_Cancel(Operation, MI_REASON_NONE);\n    WI_VERIFY(MI_Operation_Close(Operation) == MI_RESULT_OK);\n}\n\nvoid LxssManagementInterface::CloseSession(_In_ std::shared_ptr<MI_Application>, _Inout_ MI_Session* Session)\n{\n    WI_VERIFY(MI_Session_Close(Session, NULL, NULL) == MI_RESULT_OK);\n    delete Session;\n}\n\nstd::shared_ptr<MI_Application> LxssManagementInterface::GetGlobalApplication()\n{\n    std::shared_ptr<MI_Application> globalApplicationInstance;\n    std::lock_guard<std::mutex> lock(s_lock);\n    globalApplicationInstance = s_application.lock();\n    if (!globalApplicationInstance)\n    {\n        std::unique_ptr<MI_Application> rawApplication(new MI_Application);\n        const MI_Result result = MI_Application_Initialize(0, nullptr, nullptr, rawApplication.get());\n\n        THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), \"Failed with error %d\", result);\n\n        globalApplicationInstance = std::shared_ptr<MI_Application>(rawApplication.release(), CloseGlobalApplication);\n\n        s_application = globalApplicationInstance;\n    }\n\n    return globalApplicationInstance;\n}\n\nunique_mi_instance LxssManagementInterface::NewInstance(_In_ const std::wstring& ClassName, _In_opt_ const MI_Class* Class)\n{\n    auto application = GetGlobalApplication();\n    MI_Result result;\n    MI_Instance* rawInstance;\n    if (Class == nullptr)\n    {\n        result = MI_Application_NewInstance(application.get(), ClassName.c_str(), nullptr, &rawInstance);\n    }\n    else\n    {\n        result = MI_Application_NewInstanceFromClass(application.get(), ClassName.c_str(), Class, &rawInstance);\n    }\n\n    THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), \"Failed with error %d\", result);\n\n    WI_ASSERT(rawInstance != nullptr);\n\n    return unique_mi_instance(rawInstance, std::bind(CloseInstance, application, _1));\n}\n\nunique_mi_session LxssManagementInterface::NewSession()\n{\n    auto application = GetGlobalApplication();\n    std::unique_ptr<MI_Session> rawSession(new MI_Session());\n    const MI_Result result = MI_Application_NewSession(application.get(), nullptr, nullptr, nullptr, nullptr, nullptr, rawSession.get());\n\n    THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), \"Failed with error %d\", result);\n\n    return unique_mi_session(rawSession.release(), std::bind(CloseSession, application, _1));\n}\n\n// LxssNetworkingFirewall class functions\n\nconst wil::unique_bstr LxssNetworkingFirewall::s_DefaultRuleDescription(wil::make_bstr_failfast(L\"WSL iptables entry\"));\n\nconst std::wstring LxssNetworkingFirewall::s_FriendlyNamePrefix(L\"WSLRULE_17774471984f_\");\n\nLxssNetworkingFirewall::LxssNetworkingFirewall()\n{\n    m_firewall = wil::CoCreateInstance<NetFwPolicy2, INetFwPolicy2>(CLSCTX_INPROC_SERVER);\n}\n\nvoid LxssNetworkingFirewall::CopyPartialArray(SAFEARRAY* Destination, SAFEARRAY* Source, ULONG DestinationIndexStart, ULONG SourceIndexStart, ULONG ElementsToCopy)\n{\n    if (ElementsToCopy == 0)\n    {\n        return;\n    }\n\n    // Sanity check destination\n    THROW_HR_IF(E_INVALIDARG, SafeArrayGetDim(Destination) != 1);\n    LONG firstIndex;\n    // Only expecting arrays to start at 0, so enforce that for now.\n    THROW_IF_FAILED(SafeArrayGetLBound(Destination, 1, &firstIndex));\n    THROW_HR_IF(E_INVALIDARG, (firstIndex != 0));\n    ULONG lastIndex;\n    THROW_IF_FAILED(SafeArrayGetUBound(Destination, 1, (PLONG)&lastIndex));\n    ULONG requestedLastIndex;\n    THROW_IF_FAILED(ULongAdd(DestinationIndexStart, (ElementsToCopy - 1), &requestedLastIndex));\n\n    THROW_HR_IF(E_INVALIDARG, (requestedLastIndex > lastIndex));\n    // Sanity check source.\n    THROW_HR_IF(E_INVALIDARG, SafeArrayGetDim(Source) != 1);\n    // Only expecting arrays to start at 0, so enforce that for now.\n    THROW_IF_FAILED(SafeArrayGetLBound(Source, 1, &firstIndex));\n    THROW_HR_IF(E_INVALIDARG, (firstIndex != 0));\n    THROW_IF_FAILED(SafeArrayGetUBound(Source, 1, (PLONG)&lastIndex));\n    THROW_IF_FAILED(ULongAdd(SourceIndexStart, (ElementsToCopy - 1), &requestedLastIndex));\n\n    THROW_HR_IF(E_INVALIDARG, (requestedLastIndex > lastIndex));\n    // Perform the copy.\n    ULONG curSourceIndex = SourceIndexStart;\n    ULONG curDestIndex = DestinationIndexStart;\n    THROW_IF_FAILED(SafeArrayLock(Source));\n    auto releaseSource = wil::scope_exit([Source]() { WI_VERIFY(SUCCEEDED(SafeArrayUnlock(Source))); });\n\n    for (ULONG index = 0; index < ElementsToCopy; index += 1)\n    {\n        VARIANT* nextAdapter;\n        THROW_IF_FAILED(SafeArrayPtrOfIndex(Source, (PLONG)&curSourceIndex, (PVOID*)&nextAdapter));\n\n        curSourceIndex += 1;\n        THROW_IF_FAILED(SafeArrayPutElement(Destination, (PLONG)&curDestIndex, nextAdapter));\n\n        curDestIndex += 1;\n    }\n\n    return;\n}\n\nstd::wstring LxssNetworkingFirewall::AddPortRule(const IP_ADDRESS_PREFIX& Address) const\n{\n    auto newRule = wil::CoCreateInstance<NetFwRule, INetFwRule>(CLSCTX_INPROC_SERVER);\n\n    // Open a port via the firewall by creating a rule that specifies the local\n    // address and the local port to allow. Currently this rule only applies to\n    // the public profile as that is the one invoked with traffic between\n    // network compartments.\n    THROW_IF_FAILED(newRule->put_Action(NET_FW_ACTION_ALLOW));\n    THROW_IF_FAILED(newRule->put_Direction(NET_FW_RULE_DIR_IN));\n    THROW_IF_FAILED(newRule->put_Profiles(NET_FW_PROFILE2_PUBLIC));\n    THROW_IF_FAILED(newRule->put_Protocol(NET_FW_IP_PROTOCOL_TCP));\n    const std::wstring addressString = LxssIpTables::AddressStringFromAddress(Address, false);\n\n    const auto localAddress = wil::make_bstr_failfast(addressString.c_str());\n    THROW_IF_FAILED(newRule->put_LocalAddresses(localAddress.get()));\n    const auto localPort = wil::make_bstr_failfast(std::to_wstring(Address.Prefix.Ipv4.sin_port).c_str());\n\n    THROW_IF_FAILED(newRule->put_LocalPorts(localPort.get()));\n    std::wstring generatedName = GeneratePortRuleName(Address);\n    const auto friendlyName = wil::make_bstr_failfast(generatedName.c_str());\n    THROW_IF_FAILED(newRule->put_Name(friendlyName.get()));\n    THROW_IF_FAILED(newRule->put_Description(s_DefaultRuleDescription.get()));\n    THROW_IF_FAILED(newRule->put_Enabled(VARIANT_TRUE));\n    // Add the rule to the existing set.\n    wil::com_ptr<INetFwRules> rules;\n    THROW_IF_FAILED(m_firewall->get_Rules(&rules));\n    THROW_IF_FAILED(rules->Add(newRule.get()));\n    // Return the unique rule name to the caller.\n    return generatedName;\n}\n\nvoid LxssNetworkingFirewall::CleanupRemnants()\n{\n    auto firewall = std::make_shared<LxssNetworkingFirewall>();\n    THROW_HR_IF(E_OUTOFMEMORY, !firewall);\n    wil::com_ptr<INetFwRules> rules;\n    THROW_IF_FAILED(firewall->m_firewall->get_Rules(&rules));\n    wil::com_ptr<IUnknown> enumInterface;\n    THROW_IF_FAILED(rules->get__NewEnum(enumInterface.addressof()));\n    auto rulesEnum = enumInterface.query<IEnumVARIANT>();\n    // Find any rules with the unique WSL prefix and destroy them.\n    for (;;)\n    {\n        wil::unique_variant next;\n        ULONG numEntries;\n        THROW_IF_FAILED(rulesEnum->Next(1, &next, &numEntries));\n        if (numEntries == 0)\n        {\n            break;\n        }\n\n        wil::com_ptr<INetFwRule> nextRule;\n        THROW_IF_FAILED(next.pdispVal->QueryInterface(IID_PPV_ARGS(&nextRule)));\n        wil::unique_bstr nextRuleName;\n        THROW_IF_FAILED(nextRule->get_Name(nextRuleName.addressof()));\n        if (wsl::shared::string::StartsWith(nextRuleName.get(), s_FriendlyNamePrefix.c_str(), true))\n        {\n            // The firewall port rule will be destroyed when it goes out of\n            // scope.\n            LxssNetworkingFirewallPort tempPort(firewall, nextRule);\n        }\n    }\n}\n\nstd::wstring LxssNetworkingFirewall::GeneratePortRuleName(const IP_ADDRESS_PREFIX& Address)\n{\n    std::wstringstream nameStream;\n    nameStream << s_FriendlyNamePrefix << LxssIpTables::AddressStringFromAddress(Address, false) << L\":\"\n               << std::to_wstring(Address.Prefix.Ipv4.sin_port);\n\n    return nameStream.str();\n}\n\nwil::unique_variant LxssNetworkingFirewall::GetExcludedAdapters(_Out_opt_ ULONG* AdapterCount) const\n{\n    wil::unique_variant excludedResult;\n    THROW_IF_FAILED(m_firewall->get_ExcludedInterfaces(NET_FW_PROFILE2_PUBLIC, excludedResult.addressof()));\n\n    if (excludedResult.vt == VT_EMPTY)\n    {\n        // If there are no entries create a 0-element array so that callers can\n        // always assume the returned variant contains a safe array.\n        excludedResult.parray = SafeArrayCreateVector(VT_VARIANT, 0, 0);\n        THROW_HR_IF_NULL(E_OUTOFMEMORY, excludedResult.parray);\n        excludedResult.vt = (VT_ARRAY | VT_VARIANT);\n    }\n\n    THROW_HR_IF_MSG(E_UNEXPECTED, (excludedResult.vt != (VT_ARRAY | VT_VARIANT)), \"Unexpected type from get_ExcludedInterfaces\");\n\n    SAFEARRAY* existingAdapters = excludedResult.parray;\n    THROW_HR_IF_MSG(E_UNEXPECTED, (SafeArrayGetDim(existingAdapters) != 1), \"Unexpected array dim from get_ExcludedInterfaces\");\n\n    LONG firstIndex;\n    THROW_IF_FAILED(SafeArrayGetLBound(existingAdapters, 1, &firstIndex));\n\n    THROW_HR_IF_MSG(E_UNEXPECTED, (firstIndex != 0), \"Unexpected array (l) from get_ExcludedInterfaces\");\n\n    LONG lastIndex;\n    THROW_IF_FAILED(SafeArrayGetUBound(existingAdapters, 1, &lastIndex));\n\n    if (ARGUMENT_PRESENT(AdapterCount))\n    {\n        // A zero-element array reports the upper bound as -1 because it\n        // subtracts one from the element count (0) to get the upper bound.\n        *AdapterCount = (static_cast<ULONG>(lastIndex) + 1);\n    }\n\n    return excludedResult;\n}\n\nvoid LxssNetworkingFirewall::ExcludeAdapter(const std::wstring& AdapterName)\n{\n    ULONG adapterCount;\n    wil::unique_variant currentExcluded = GetExcludedAdapters(&adapterCount);\n    THROW_HR_IF_MSG(E_BOUNDS, (adapterCount >= ULONG_MAX), \"Unexpected array (u) from get_ExcludedInterfaces\");\n\n    SAFEARRAY* existingAdapters = currentExcluded.parray;\n    unique_safearray adapters(SafeArrayCreateVector(VT_VARIANT, 0, (adapterCount + 1)));\n\n    THROW_HR_IF(E_OUTOFMEMORY, !adapters);\n    // Add existing entries\n    CopyPartialArray(adapters.get(), existingAdapters, 0, 0, adapterCount);\n    // Create new entry matching device name.\n    wil::unique_variant interfaceName;\n    interfaceName.bstrVal = SysAllocString(AdapterName.c_str());\n    THROW_HR_IF_NULL(E_OUTOFMEMORY, interfaceName.bstrVal);\n    THROW_IF_FAILED(SafeArrayPutElement(adapters.get(), (PLONG)&adapterCount, interfaceName.addressof()));\n\n    wil::unique_variant excludedAdapters;\n    excludedAdapters.vt = (VT_ARRAY | VT_VARIANT);\n    excludedAdapters.parray = adapters.release();\n    THROW_IF_FAILED(m_firewall->put_ExcludedInterfaces(NET_FW_PROFILE2_PUBLIC, excludedAdapters));\n}\n\nvoid LxssNetworkingFirewall::RemoveExcludedAdapter(const std::wstring& AdapterName)\n{\n    ULONG adapterCount;\n    wil::unique_variant currentExcluded = GetExcludedAdapters(&adapterCount);\n    SAFEARRAY* existingAdapters = currentExcluded.parray;\n    ULONG index;\n    for (index = 0; index < adapterCount; index += 1)\n    {\n        wil::unique_variant nextAdapter;\n        THROW_IF_FAILED(SafeArrayGetElement(existingAdapters, (PLONG)&index, nextAdapter.addressof()));\n\n        THROW_HR_IF(E_UNEXPECTED, (nextAdapter.vt != VT_BSTR));\n        // Case-insensitive name comparison\n        if (wsl::shared::string::IsEqual(AdapterName, nextAdapter.bstrVal, true))\n        {\n            break;\n        }\n    }\n\n    THROW_HR_IF(E_INVALIDARG, (index >= adapterCount));\n    unique_safearray adapters(SafeArrayCreateVector(VT_VARIANT, 0, (adapterCount - 1)));\n\n    THROW_HR_IF(E_OUTOFMEMORY, !adapters);\n    // Copy all of the elements except the one being removed.\n    CopyPartialArray(adapters.get(), existingAdapters, 0, 0, index);\n    CopyPartialArray(adapters.get(), existingAdapters, index, (index + 1), (adapterCount - (index + 1)));\n\n    wil::unique_variant excludedAdapters;\n    excludedAdapters.vt = (VT_ARRAY | VT_VARIANT);\n    excludedAdapters.parray = adapters.release();\n    THROW_IF_FAILED(m_firewall->put_ExcludedInterfaces(NET_FW_PROFILE2_PUBLIC, excludedAdapters));\n}\n\nvoid LxssNetworkingFirewall::RemovePortRule(const std::wstring& RuleName) const\n{\n    wil::com_ptr<INetFwRules> rules;\n    THROW_IF_FAILED(m_firewall->get_Rules(&rules));\n    THROW_IF_FAILED(rules->Remove(wil::make_bstr_failfast(RuleName.c_str()).get()));\n}\n\n// LxssNetworkingFirewallPort class functions\n\nLxssNetworkingFirewallPort::LxssNetworkingFirewallPort(const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const IP_ADDRESS_PREFIX& Address) :\n    m_address(Address), m_firewall(Firewall)\n{\n    m_name = Firewall->AddPortRule(Address);\n    return;\n}\n\nLxssNetworkingFirewallPort::LxssNetworkingFirewallPort(const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const wil::com_ptr<INetFwRule>& Existing) :\n    m_firewall(Firewall)\n{\n    wil::unique_bstr ruleName;\n    THROW_IF_FAILED(Existing->get_Name(ruleName.addressof()));\n    m_name = ruleName.get();\n    return;\n}\n\nLxssNetworkingFirewallPort::~LxssNetworkingFirewallPort()\n{\n    try\n    {\n        m_firewall->RemovePortRule(m_name);\n    }\n    CATCH_LOG_MSG(\"Failed to remove firewall port rule.\")\n}\n\n// LxssNetworkingNat class functions\n\n// N.B. The name is internally limited by NAT to 39 characters.\nconst std::wstring LxssNetworkingNat::s_FriendlyNamePrefix(L\"WSLNAT_17774471984f_\");\n\nconst std::wstring LxssNetworkingNat::s_WmiNatInstanceId(L\"InstanceID\");\nconst std::wstring LxssNetworkingNat::s_WmiNatInternalIpAddress(L\"InternalIPInterfaceAddressPrefix\");\n\nconst std::wstring LxssNetworkingNat::s_WmiNatName(L\"Name\");\nconst std::wstring LxssNetworkingNat::s_WmiNatNamespace(L\"MSFT_NetNat\");\n\nLxssNetworkingNat::LxssNetworkingNat(const IP_ADDRESS_PREFIX& InputPrefix) : m_internalIpAddress(InputPrefix)\n{\n    // Convert the address into a string of the form \"172.17.0.0/16\"\n    const std::wstring inputAddress = LxssIpTables::AddressStringFromAddress(InputPrefix, true);\n\n    const std::wstring friendlyName = s_FriendlyNamePrefix + inputAddress;\n    m_session = LxssManagementInterface::NewSession();\n    const auto instance = GetNatWmiInstance(m_session);\n    MI_Value miValue;\n    miValue.string = const_cast<MI_Char*>(friendlyName.c_str());\n    MI_Result result = MI_Instance_SetElement(instance.get(), s_WmiNatName.c_str(), &miValue, MI_STRING, MI_FLAG_BORROW);\n\n    THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), \"Failed with error %d\", result);\n\n    miValue.string = const_cast<MI_Char*>(inputAddress.c_str());\n    result = MI_Instance_SetElement(instance.get(), s_WmiNatInternalIpAddress.c_str(), &miValue, MI_STRING, MI_FLAG_BORROW);\n\n    // TODO_LX: Might need a timeout value here, set via\n    //          MI_OperationOptions_SetTimeout()\n    unique_mi_operation operation;\n    MI_Session_CreateInstance(\n        m_session.get(), 0, nullptr, LxssManagementInterface::LocalRoot().c_str(), instance.get(), nullptr, operation.addressof());\n\n    MI_Boolean moreResults;\n    const MI_Instance* resultInstance;\n    MI_Result innerResult = MI_RESULT_OK;\n    result = MI_Operation_GetInstance(operation.addressof(), &resultInstance, &moreResults, &innerResult, nullptr, nullptr);\n\n    WI_ASSERT(moreResults != MI_TRUE);\n\n    THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), \"Failed with error %d\", result);\n\n    THROW_HR_IF_MSG(E_FAIL, (innerResult != MI_RESULT_OK), \"Operation failed with error %d\", innerResult);\n\n    m_natInstance = LxssManagementInterface::CloneInstance(resultInstance);\n    return;\n}\n\nLxssNetworkingNat::LxssNetworkingNat(const MI_Instance* ExistingInstance)\n{\n    m_session = LxssManagementInterface::NewSession();\n    m_natInstance = LxssManagementInterface::CloneInstance(ExistingInstance);\n    return;\n}\n\nLxssNetworkingNat::~LxssNetworkingNat()\n{\n    unique_mi_operation operation;\n    MI_Session_DeleteInstance(\n        m_session.get(), 0, nullptr, LxssManagementInterface::LocalRoot().c_str(), m_natInstance.get(), nullptr, operation.addressof());\n\n    MI_Boolean moreResults = MI_TRUE;\n    while (moreResults != MI_FALSE)\n    {\n        const MI_Instance* resultInstance;\n        MI_Result innerResult;\n        const MI_Result result =\n            MI_Operation_GetInstance(operation.addressof(), &resultInstance, &moreResults, &innerResult, nullptr, nullptr);\n\n        if (result != MI_RESULT_OK)\n        {\n            LOG_HR_MSG(E_FAIL, \"Failed with error %d\", result);\n\n            break;\n        }\n\n        LOG_HR_IF_MSG(E_FAIL, (innerResult != MI_RESULT_OK), \"Failed with error %d\", innerResult);\n    }\n}\n\nvoid LxssNetworkingNat::CleanupRemnants()\n{\n    // Search through all NATs in the system looking for those that match the\n    // WSL naming convention: WSL_<IP address>\n    const auto session = LxssManagementInterface::NewSession();\n    unique_mi_operation operation;\n    MI_Session_EnumerateInstances(\n        session.get(),\n        0,\n        nullptr,\n        LxssManagementInterface::LocalRoot().c_str(),\n        s_WmiNatNamespace.c_str(),\n        false,\n        nullptr,\n        operation.addressof());\n\n    MI_Boolean moreResults = MI_TRUE;\n    while (moreResults != MI_FALSE)\n    {\n        const MI_Instance* resultInstance{};\n        MI_Result innerResult{};\n        MI_Result result = MI_Operation_GetInstance(operation.addressof(), &resultInstance, &moreResults, &innerResult, nullptr, nullptr);\n        if (result != MI_RESULT_OK)\n        {\n            LOG_HR_MSG(E_FAIL, \"Failed with error %d\", result);\n            break;\n        }\n\n        if (innerResult != MI_RESULT_OK)\n        {\n            LOG_HR_MSG(E_FAIL, \"Failed with error %d\", innerResult);\n            continue;\n        }\n\n        if (resultInstance == nullptr)\n        {\n            // From: https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_operation_getinstance\n            // This value may be Null even if the operation succeeds.\n            continue;\n        }\n\n        MI_Value miValue{};\n        MI_Type miType{};\n        result = MI_Instance_GetElement(resultInstance, s_WmiNatName.c_str(), &miValue, &miType, nullptr, nullptr);\n\n        if (result != MI_RESULT_OK)\n        {\n            LOG_HR_MSG(E_FAIL, \"Failed with error %d\", result);\n            continue;\n        }\n\n        if (miType != MI_STRING)\n        {\n            LOG_HR_MSG(E_UNEXPECTED, \"Type is %d\", miType);\n            continue;\n        }\n\n        if (wsl::shared::string::StartsWith(miValue.string, s_FriendlyNamePrefix, true))\n        {\n            // Create a temporary NAT instance, to be immediately deleted when\n            // it goes out of scope.\n            LxssNetworkingNat natRemnant(resultInstance);\n        }\n    }\n}\n\nunique_mi_instance LxssNetworkingNat::GetNatWmiInstance(const unique_mi_session& Session)\n{\n    unique_mi_operation operation;\n    MI_Session_GetClass(\n        Session.get(), 0, nullptr, LxssManagementInterface::LocalRoot().c_str(), s_WmiNatNamespace.c_str(), nullptr, operation.addressof());\n\n    const MI_Class* miClass;\n    const MI_Result result = MI_Operation_GetClass(operation.addressof(), &miClass, nullptr, nullptr, nullptr, nullptr);\n\n    THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), \"Failed with error %d\", result);\n\n    return LxssManagementInterface::NewInstance(s_WmiNatNamespace.c_str(), miClass);\n}\n"
  },
  {
    "path": "src/windows/service/exe/LxssIpTables.h",
    "content": "/*++\n\n    Copyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssIptables.h\n\nAbstract:\n\n    This file contains iptables-related function declarations.\n\n--*/\n\n#pragma once\n\n#include <memory>\n#include <mutex>\n#include <mi.h>\n#include <netfw.h>\n#include \"LxssUserCallback.h\"\n\n/// <summary>\n/// Type to hold a MI_Session instance. Functions creating an instance of this\n/// type will construct it in such a way as to also hold an implicit reference\n/// to the global application instance.\n/// </summary>\nusing unique_mi_session = std::unique_ptr<MI_Session, std::function<void(MI_Session*)>>;\n\n/// <summary>\n/// Type to hold a MI_Instance instance. Functions creating an instance of this\n/// type will construct it in such a way as to also hold an implicit reference\n/// to the global application instance.\n/// </summary>\nusing unique_mi_instance = std::unique_ptr<MI_Instance, std::function<void(MI_Instance*)>>;\n\n/// <summary>\n/// Helper class for using Windows Management Interface.\n/// </summary>\nclass LxssManagementInterface\n{\npublic:\n    /// <summary>\n    /// Clone an instance.\n    /// </summary>\n    static unique_mi_instance CloneInstance(_In_ const MI_Instance* InstanceToClone);\n\n    /// <summary>\n    /// Closes a MI_Operation instance.\n    /// </summary>\n    static void CloseOperation(MI_Operation* Operation);\n\n    /// <summary>\n    /// Returns the global application instance.\n    /// </summary>\n    static std::shared_ptr<MI_Application> GetGlobalApplication();\n\n    /// <summary>\n    /// Create a new management interface MI_Instance instance.\n    /// </summary>\n    static unique_mi_instance NewInstance(_In_ const std::wstring& ClassName, _In_opt_ const MI_Class* Class);\n\n    /// <summary>\n    /// Create a new management interface session instance.\n    /// </summary>\n    static unique_mi_session NewSession();\n\n    /// <summary>\n    /// Local machine root instance.\n    /// </summary>\n    static const std::wstring& LocalRoot()\n    {\n        return s_localRoot;\n    }\n\nprivate:\n    /// <summary>\n    /// Called to destroy the global application instance. Called as a result\n    /// of the last shared reference being deleted.\n    /// </summary>\n    static void CloseGlobalApplication(_Inout_ MI_Application* Application);\n\n    /// <summary>\n    /// Close a MI_Instance. This is implicitly called via the destruction of\n    /// an unique_mi_instance instance.\n    /// </summary>\n    static void CloseInstance(_In_ std::shared_ptr<MI_Application>, _Inout_ MI_Instance* Instance);\n\n    /// <summary>\n    /// Close a session. This is implicitly called via the destruction of an\n    /// unique_mi_session instance.\n    /// </summary>\n    static void CloseSession(_In_ std::shared_ptr<MI_Application>, _Inout_ MI_Session* Session);\n\n    /// <summary>\n    /// Lock for static members.\n    /// </summary>\n    static std::mutex s_lock;\n\n    /// <summary>\n    /// Global MI_Application instance.\n    /// </summary>\n    _Guarded_by_(LxssManagementInterface::s_lock) static std::weak_ptr<MI_Application> s_application;\n\n    /// <summary>\n    /// Local machine root instance.\n    /// </summary>\n    static const std::wstring s_localRoot;\n};\n\n/// <summary>\n/// MI_Operation unique instance.\n/// </summary>\nusing unique_mi_operation =\n    wil::unique_struct<MI_Operation, decltype(LxssManagementInterface::CloseOperation), LxssManagementInterface::CloseOperation>;\n\nclass LxssNetworkingFirewallPort;\nclass LxssNetworkingNat;\n\n/// <summary>\n/// Emulate iptables functionality.\n/// </summary>\nclass LxssIpTables\n{\npublic:\n    /// <summary>\n    /// Constructor.\n    /// </summary>\n    LxssIpTables();\n\n    /// <summary>\n    /// Enable iptables emulation.\n    /// </summary>\n    void EnableIpTablesSupport(_In_ const wil::unique_handle& InstanceHandle);\n\n    /// <summary>\n    /// Cleanup any persistent data leftover from a non-clean shutdown.\n    /// </summary>\n    static void CleanupRemnants();\n\n    /// <summary>\n    /// Helper routine to convert an IP address into the string equivalent.\n    /// </summary>\n    static std::wstring AddressStringFromAddress(const IP_ADDRESS_PREFIX& Address, bool AddPrefixLength);\n\nprivate:\n    /// <summary>\n    /// No copy constructor.\n    /// </summary>\n    LxssIpTables(const LxssIpTables&) = delete;\n\n    /// <summary>\n    /// Verify the input prefix address is supported.\n    /// </summary>\n    static bool IsAllowedInputPrefix(_In_ CONST IP_ADDRESS_PREFIX& InputPrefix);\n\n    /// <summary>\n    /// Kernel-mode callback function for iptables operations.\n    /// </summary>\n    NTSTATUS\n    KernelCallback(_In_ PVOID CallbackBuffer, _In_ ULONG_PTR CallbackBufferSize);\n\n    /// <summary>\n    /// Kernel-mode callback function to configure a port rule via the Windows\n    /// firewall.\n    /// </summary>\n    NTSTATUS\n    KernelCallbackFirewallPort(_In_ PLXBUS_USER_CALLBACK_IPTABLES_DATA CallbackData);\n\n    /// <summary>\n    /// Kernel-mode callback function to add a new masquerade entry.\n    /// </summary>\n    NTSTATUS\n    KernelCallbackMasquerade(_In_ PLXBUS_USER_CALLBACK_IPTABLES_DATA CallbackData);\n\n    /// <summary>\n    /// Kernel-mode callback entrypoint function for iptables operations.\n    /// </summary>\n    static NTSTATUS KernelCallbackProxy(_Inout_ LxssIpTables* Self, _In_ PVOID CallbackBuffer, _In_ ULONG_PTR CallbackBufferSize);\n\n    /// <summary>\n    /// List of port rules.\n    /// </summary>\n    std::list<std::unique_ptr<LxssNetworkingFirewallPort>> m_firewallPorts;\n\n    /// <summary>\n    /// Lock to protect class members.\n    /// </summary>\n    std::mutex m_lock;\n\n    /// <summary>\n    /// List of NATs.\n    /// </summary>\n    std::list<std::unique_ptr<LxssNetworkingNat>> m_networkTranslators;\n\n    /// <summary>\n    /// Callback for the kernel-mode driver to make iptables requests.\n    /// </summary>\n    // N.B. This is the last member of the class because it needs to be\n    //      destructed early as the asynchronous callback may rely on other\n    //      members of the class being valid.\n    std::unique_ptr<LxssUserCallback> m_kernelCallback;\n};\n\n/// <summary>\n/// Class providing access to Windows firewall\n/// </summary>\nclass LxssNetworkingFirewall\n{\npublic:\n    /// <summary>\n    /// Default constructor.\n    /// </summary>\n    LxssNetworkingFirewall();\n\n    /// <summary>\n    /// Create a rule to allow the specified address and port combination.\n    /// </summary>\n    std::wstring AddPortRule(const IP_ADDRESS_PREFIX& Address) const;\n\n    /// <summary>\n    /// Cleanup any persistent data leftover from a non-clean shutdown.\n    /// </summary>\n    static void CleanupRemnants();\n\n    /// <summary>\n    /// Exclude a network adapter from the firewall's public profile.\n    /// </summary>\n    void ExcludeAdapter(const std::wstring& AdapterName);\n\n    /// <summary>\n    /// Remove a network adapter from the exclusion list.\n    /// </summary>\n    void RemoveExcludedAdapter(const std::wstring& AdapterName);\n\n    /// <summary>\n    /// Remove a port rule created by AddPortRule.\n    /// </summary>\n    void RemovePortRule(const std::wstring& RuleName) const;\n\nprivate:\n    /// <summary>\n    /// No copy constructor.\n    /// </summary>\n    LxssNetworkingFirewall(const LxssNetworkingFirewall&) = delete;\n\n    /// <summary>\n    /// Copies part of a source array to a destination array.\n    /// </summary>\n    static void CopyPartialArray(SAFEARRAY* Destination, SAFEARRAY* Source, ULONG DestinationIndexStart, ULONG SourceIndexStart, ULONG ElementsToCopy);\n\n    /// <summary>\n    /// Creates the unique friendly name of the firewall port rule.\n    /// </summary>\n    static std::wstring GeneratePortRuleName(const IP_ADDRESS_PREFIX& Address);\n\n    /// <summary>\n    /// Returns the existing array of excluded adapters.\n    /// </summary>\n    wil::unique_variant GetExcludedAdapters(_Out_opt_ ULONG* AdapterCount) const;\n\n    /// <summary>\n    /// COM firewall instance.\n    /// </summary>\n    wil::com_ptr<INetFwPolicy2> m_firewall;\n\n    /// <summary>\n    /// Lock to protect class members.\n    /// </summary>\n    std::mutex m_lock;\n\n    /// <summary>\n    /// Firewall rule description.\n    /// </summary>\n    static const wil::unique_bstr s_DefaultRuleDescription;\n\n    /// <summary>\n    /// Prefix to uniquely identify WSL firewall rules.\n    /// </summary>\n    static const std::wstring s_FriendlyNamePrefix;\n};\n\n/// <summary>\n/// Class representing a Windows firewall port open rule, removed on\n/// destruction.\n/// </summary>\nclass LxssNetworkingFirewallPort\n{\npublic:\n    /// <summary>\n    /// Constructor.\n    /// </summary>\n    LxssNetworkingFirewallPort(const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const IP_ADDRESS_PREFIX& Address);\n\n    /// <summary>\n    /// Constructor to take ownership of an existing rule.\n    /// </summary>\n    LxssNetworkingFirewallPort(const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const wil::com_ptr<INetFwRule>& Existing);\n\n    /// <summary>\n    /// Destructor.\n    /// </summary>\n    ~LxssNetworkingFirewallPort();\n\n    /// <summary>\n    /// Returns the address and port of the firewall rule.\n    /// </summary>\n    const IP_ADDRESS_PREFIX& Address() const\n    {\n        return m_address;\n    }\n\n    /// <summary>\n    /// Returns the underlying firewall instance.\n    /// </summary>\n    const std::shared_ptr<LxssNetworkingFirewall> Firewall() const\n    {\n        return m_firewall;\n    }\n\nprivate:\n    /// <summary>\n    /// No default constructor.\n    /// </summary>\n    LxssNetworkingFirewallPort() = delete;\n    /// <summary>\n    /// No copy constructor.\n    /// </summary>\n    LxssNetworkingFirewallPort(const LxssNetworkingFirewallPort&) = delete;\n\n    /// <summary>\n    /// Address information for the port rule.\n    /// </summary>\n    IP_ADDRESS_PREFIX m_address;\n\n    /// <summary>\n    /// Pointer to the firewall interface.\n    /// </summary>\n    std::shared_ptr<LxssNetworkingFirewall> m_firewall;\n\n    /// <summary>\n    /// The unique rule name.\n    /// </summary>\n    std::wstring m_name;\n};\n\n/// <summary>\n/// Class representing a Windows NAT instance.\n/// </summary>\nclass LxssNetworkingNat\n{\npublic:\n    /// <summary>\n    /// Constructor.\n    /// </summary>\n    LxssNetworkingNat(const IP_ADDRESS_PREFIX& InputPrefix);\n\n    /// <summary>\n    /// Constructor to take ownership of an existing NAT.\n    /// N.B. Not setting m_internalIpAddress as the only usage of this\n    ///      constructor is to wrap an existing instance for cleanup/deletion.\n    ///      If this constructor is to be used for other purposes, need to\n    ///      fetch the s_WmiNatInternalIpAddress property and convert it.\n    /// </summary>\n    LxssNetworkingNat(const MI_Instance* ExistingInstance);\n\n    /// <summary>\n    /// Destructor.\n    /// </summary>\n    ~LxssNetworkingNat();\n\n    /// <summary>\n    /// The address being NAT'd.\n    /// </summary>\n    const IP_ADDRESS_PREFIX& Address() const\n    {\n        return m_internalIpAddress;\n    }\n\n    /// <summary>\n    /// Cleanup any persistent data leftover from a non-clean shutdown.\n    /// </summary>\n    static void CleanupRemnants();\n\nprivate:\n    /// <summary>\n    /// No default constructor.\n    /// </summary>\n    LxssNetworkingNat() = delete;\n    /// <summary>\n    /// No copy constructor.\n    /// </summary>\n    LxssNetworkingNat(const LxssNetworkingNat&) = delete;\n\n    /// <summary>\n    /// The NAT instance.\n    /// </summary>\n    unique_mi_instance m_natInstance;\n\n    /// <summary>\n    /// The session used to create/destroy the NAT.\n    /// </summary>\n    unique_mi_session m_session;\n\n    /// <summary>\n    /// Create a new WMI instance of the NAT type.\n    /// </summary>\n    static unique_mi_instance GetNatWmiInstance(const unique_mi_session& Session);\n\n    /// <summary>\n    /// The IP address prefix to NAT.\n    /// </summary>\n    IP_ADDRESS_PREFIX m_internalIpAddress;\n\n    /// <summary>\n    /// The string prefix for the friendly NAT name.\n    /// </summary>\n    static const std::wstring s_FriendlyNamePrefix;\n\n    /// <summary>\n    /// The string representing the NAT instance ID in WMI.\n    /// </summary>\n    static const std::wstring s_WmiNatInstanceId;\n\n    /// <summary>\n    /// The string representing the NAT internal IP address prefix in WMI.\n    /// </summary>\n    static const std::wstring s_WmiNatInternalIpAddress;\n\n    /// <summary>\n    /// The string representing the NAT name property in WMI.\n    /// </summary>\n    static const std::wstring s_WmiNatName;\n\n    /// <summary>\n    /// The string representing the NAT namespace in WMI.\n    /// </summary>\n    static const std::wstring s_WmiNatNamespace;\n};\n"
  },
  {
    "path": "src/windows/service/exe/LxssUserCallback.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssUserCallback.cpp\n\nAbstract:\n\n    This file contains kernel->user callback function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"LxssUserCallback.h\"\n\nLxssUserCallback::LxssUserCallback(_In_ HANDLE Handle, _In_ LXBUS_USER_CALLBACK_TYPE CallbackType, _In_ const LXSS_USER_CALLBACK& Callback, _In_ ULONG OutputBufferSize) :\n    m_callback(Callback), m_exiting(false), m_callbackType(CallbackType), m_event(wil::EventOptions::ManualReset | wil::EventOptions::Signaled)\n{\n    // Keep a local copy of the handle so the request can be requeued.\n    THROW_IF_WIN32_BOOL_FALSE(::DuplicateHandle(GetCurrentProcess(), Handle, GetCurrentProcess(), &m_handle, 0, TRUE, DUPLICATE_SAME_ACCESS));\n\n    // All result buffers are derivatives of LXBUS_USER_CALLBACK_DATA.\n    WI_ASSERT(OutputBufferSize >= sizeof(LXBUS_USER_CALLBACK_DATA));\n\n    // Allocate a buffer of the requested size.\n    m_buffer.resize(OutputBufferSize);\n\n    // Set up the threadpool wait callback.\n    PTP_WAIT_CALLBACK ThreadpoolCallback;\n    ThreadpoolCallback = reinterpret_cast<PTP_WAIT_CALLBACK>(&ThreadpoolCallbackProxy);\n\n    // N.B. Using unreferenced 'this' as context parameter since the destructor\n    //      should wait for the threadpool thread to complete/unregister.\n    m_threadpoolWait.reset(CreateThreadpoolWait(ThreadpoolCallback, this, nullptr));\n\n    THROW_LAST_ERROR_IF(!m_threadpoolWait);\n    return;\n}\n\nLxssUserCallback::~LxssUserCallback()\n{\n    {\n        // synchronize with the callback thread to avoid a race where m_event\n        // is signalled but the callback is in the middle of queueing up\n        // another IO request.\n        std::lock_guard<std::mutex> lock(m_lock);\n        m_exiting = true;\n    }\n\n    SetThreadpoolWait(m_threadpoolWait.get(), nullptr, nullptr);\n    IO_STATUS_BLOCK ioCancelStatus{};\n    const NTSTATUS status = NtCancelIoFileEx(m_handle.get(), &m_ioStatus, &ioCancelStatus);\n\n    // If the instance has been terminated, the request may already have been\n    // cancelled.\n    if (status != STATUS_NOT_FOUND)\n    {\n        LOG_IF_NTSTATUS_FAILED_MSG(status, \"Failed to cancel user callback IO\");\n    }\n\n    // Wait for outstanding IO to complete since it references memory owned by\n    // this instance.\n    m_event.wait();\n}\n\nVOID LxssUserCallback::QueueRequest()\n{\n    auto setEventOnFailure = m_event.SetEvent_scope_exit();\n    m_event.ResetEvent();\n    LXBUS_REGISTER_USER_CALLBACK_PARAMETERS parameters;\n    parameters.Input.CallbackType = m_callbackType;\n    const ULONG outputBufferSize = std::min<ULONG>(static_cast<ULONG>(m_buffer.size()), ULONG_MAX);\n\n    THROW_IF_NTSTATUS_FAILED(LxBusClientRegisterUserCallbackAsync(\n        m_handle.get(), m_event.get(), &m_ioStatus, &parameters, &m_buffer.front(), outputBufferSize));\n\n    SetThreadpoolWait(m_threadpoolWait.get(), m_event.get(), nullptr);\n    setEventOnFailure.release();\n}\n\nstd::unique_ptr<LxssUserCallback> LxssUserCallback::Register(\n    _In_ HANDLE Handle, _In_ LXBUS_USER_CALLBACK_TYPE CallbackType, _In_ const LXSS_USER_CALLBACK& Callback, _In_ ULONG OutputBufferSize)\n{\n    std::unique_ptr<LxssUserCallback> userCallback(new LxssUserCallback(Handle, CallbackType, Callback, OutputBufferSize));\n\n    userCallback->QueueRequest();\n    return userCallback;\n}\n\nVOID LxssUserCallback::ThreadpoolCallback(_Inout_ PTP_CALLBACK_INSTANCE Instance, _Inout_ PTP_WAIT Wait, _In_ TP_WAIT_RESULT WaitResult)\n{\n    UNREFERENCED_PARAMETER(Instance);\n    UNREFERENCED_PARAMETER(Wait);\n    UNREFERENCED_PARAMETER(WaitResult);\n\n    WI_ASSERT(Wait == m_threadpoolWait.get());\n    WI_ASSERT(WaitResult == WAIT_OBJECT_0);\n\n    if (NT_SUCCESS(m_ioStatus.Status))\n    {\n        WI_ASSERT(m_ioStatus.Information >= sizeof(LXBUS_USER_CALLBACK_DATA));\n\n        const auto callbackData = static_cast<PLXBUS_USER_CALLBACK_DATA>(static_cast<PVOID>(&m_buffer.front()));\n\n        const unsigned long long callbackId = callbackData->CallbackId;\n        NTSTATUS status = STATUS_INTERNAL_ERROR;\n        try\n        {\n            status = m_callback(&m_buffer.front(), m_ioStatus.Information);\n        }\n        CATCH_LOG()\n\n        LXBUS_REGISTER_USER_CALLBACK_PARAMETERS parameters{};\n        parameters.Input.CallbackType = LxBusUserCallbackTypeResult;\n        parameters.Input.ResultData.CallbackId = callbackId;\n        parameters.Input.ResultData.Result = status;\n        LOG_IF_NTSTATUS_FAILED(LxBusClientUserCallbackSendResponse(m_handle.get(), &parameters));\n    }\n    else if (m_ioStatus.Status == STATUS_CANCELLED)\n    {\n        // Don't queue another request if the previous one was canceled.\n        // Cancel should only occur when the instance is shutting down or\n        // when the destructor runs.\n        return;\n    }\n    else\n    {\n        LOG_NTSTATUS_MSG(m_ioStatus.Status, \"User callback IO completed with failure\");\n    }\n\n    // Requeue another request.\n    //\n    // N.B. If queueing the request fails, the instance will no longer be able\n    //      to perform up-calls to the usermode service for this type of\n    //      operation. This is benign if the request fails because rundown\n    //      could not be acquired due to the instance terminating.\n    //\n    // TODO_LX: use telemetry to determine what other failures can occur so\n    //          can be handled gracefully.\n    {\n        std::lock_guard<std::mutex> lock(m_lock);\n        if (!m_exiting)\n        {\n            try\n            {\n                QueueRequest();\n            }\n            CATCH_LOG()\n        }\n    }\n}\n\nVOID CALLBACK LxssUserCallback::ThreadpoolCallbackProxy(\n    _Inout_ PTP_CALLBACK_INSTANCE Instance, _Inout_opt_ PVOID Context, _Inout_ PTP_WAIT Wait, _In_ TP_WAIT_RESULT WaitResult)\n{\n    const auto Self = static_cast<LxssUserCallback*>(Context);\n    Self->ThreadpoolCallback(Instance, Wait, WaitResult);\n}\n"
  },
  {
    "path": "src/windows/service/exe/LxssUserCallback.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssUserCallback.h\n\nAbstract:\n\n    This file contains kernel->user callback function declarations.\n\n--*/\n\n#pragma once\n#include <mutex>\n#include <wil/resource.h>\n\nusing LXSS_USER_CALLBACK = std::function<NTSTATUS(PVOID CallbackBuffer, ULONG_PTR CallbackBufferSize)>;\n\nclass LxssUserCallback\n{\npublic:\n    /// <summary>\n    /// Destructor.\n    /// </summary>\n    ~LxssUserCallback();\n\n    /// <summary>\n    /// Register a new user callback handler.\n    /// </summary>\n    static std::unique_ptr<LxssUserCallback> Register(\n        _In_ HANDLE Handle, _In_ LXBUS_USER_CALLBACK_TYPE CallbackType, _In_ const LXSS_USER_CALLBACK& Callback, _In_ ULONG OutputBufferSize);\n\nprivate:\n    /// <summary>\n    /// No default constructor.\n    /// </summary>\n    LxssUserCallback() = delete;\n    /// <summary>\n    /// No copy constructor.\n    /// </summary>\n    LxssUserCallback(const LxssUserCallback&) = delete;\n\n    /// <summary>\n    /// Private constructor.\n    /// </summary>\n    LxssUserCallback(_In_ HANDLE Handle, _In_ LXBUS_USER_CALLBACK_TYPE CallbackType, _In_ const LXSS_USER_CALLBACK& Callback, _In_ ULONG OutputBufferSize);\n\n    /// <summary>\n    /// Queue the request with the kernel driver.\n    /// </summary>\n    VOID QueueRequest();\n\n    /// <summary>\n    /// Threadpool callback handler.\n    /// </summary>\n    VOID ThreadpoolCallback(_Inout_ PTP_CALLBACK_INSTANCE Instance, _Inout_ PTP_WAIT Wait, _In_ TP_WAIT_RESULT WaitResult);\n\n    /// <summary>\n    /// Threadpool callback entry.\n    /// </summary>\n    static VOID CALLBACK ThreadpoolCallbackProxy(\n        _Inout_ PTP_CALLBACK_INSTANCE Instance, _Inout_opt_ PVOID Context, _Inout_ PTP_WAIT Wait, _In_ TP_WAIT_RESULT WaitResult);\n\n    /// <summary>\n    /// Output buffer.\n    /// </summary>\n    std::vector<BYTE> m_buffer;\n\n    /// <summary>\n    /// Reference to callback function.\n    /// </summary>\n    LXSS_USER_CALLBACK m_callback;\n\n    /// <summary>\n    /// Set when instance is destructing. Should be accessed holding m_lock.\n    /// </summary>\n    bool m_exiting;\n\n    /// <summary>\n    /// The type of callback to register.\n    /// </summary>\n    LXBUS_USER_CALLBACK_TYPE m_callbackType;\n\n    /// <summary>\n    /// Event triggered when asynchronous callback IOCTL is completed.\n    /// </summary>\n    wil::unique_event m_event;\n\n    /// <summary>\n    /// Stores handle to use to send IOCTL to kernel driver.\n    /// </summary>\n    wil::unique_handle m_handle;\n\n    /// <summary>\n    /// IO status.\n    /// </summary>\n    IO_STATUS_BLOCK m_ioStatus;\n\n    /// <summary>\n    /// Synchronize access to certain fields.\n    /// </summary>\n    std::mutex m_lock;\n\n    /// <summary>\n    /// Threadpool IO handle.\n    /// </summary>\n    // N.B. Keep this as the last member so that it is the first to be\n    //      destructed. This will ensure that if the threadpool callback\n    //      races with instance destruction, all of the fields will be valid.\n    //      At completion of this member's destruction, any outstanding\n    //      threadpool thread should have completed and the callback\n    //      will have been unregistered.\n    wil::unique_threadpool_wait m_threadpoolWait;\n};\n"
  },
  {
    "path": "src/windows/service/exe/LxssUserSession.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssUserSession.cpp\n\nAbstract:\n\n    This file contains session function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"Localization.h\"\n#include \"LxssUserSession.h\"\n#include \"LxssInstance.h\"\n#include \"LxssSecurity.h\"\n#include \"WslCoreInstance.h\"\n#include \"resource.h\"\n#include <winrt\\Windows.ApplicationModel.Background.h>\n#include <nlohmann\\json.hpp>\n\n// Registry keys for migrating legacy distro user config.\n#define LXSS_LEGACY_APPEND_NT_PATH L\"AppendNtPath\"\n#define LXSS_LEGACY_INTEROP_ENABLED L\"InteropEnabled\"\n\n#define LXSS_BSDTAR_PATH LXSS_TOOLS_MOUNT \"/bsdtar\"\n#define LXSS_BSDTAR_CREATE_ARGS \" -c --one-file-system --xattrs -f - .\"\n#define LXSS_BSDTAR_CREATE_ARGS_GZIP \" -cz --one-file-system --xattrs -f - .\"\n#define LXSS_BSDTAR_CREATE_ARGS_XZIP \" -cJ --one-file-system --xattrs -f - .\"\n#define LXSS_BSDTAR_EXTRACT_ARGS \" -x -p --xattrs --no-acls -f -\"\n#define LXSS_ROOTFS_MOUNT \"/rootfs\"\n#define LXSS_TOOLS_MOUNT \"/tools\"\n\nconstexpr auto c_shortIconName = L\"shortcut.ico\";\n\n// 16 MB buffer used for relaying tar contents via hvsocket.\n#define LXSS_RELAY_BUFFER_SIZE (0x1000000)\n\nextern bool g_lxcoreInitialized;\n\nusing namespace std::placeholders;\nusing namespace Microsoft::WRL;\nusing namespace wsl::windows::service;\nusing wsl::windows::common::Context;\nusing wsl::windows::common::ExecutionContext;\nusing wsl::windows::common::ServiceExecutionContext;\n\nLxssUserSession::LxssUserSession(_In_ const std::weak_ptr<LxssUserSessionImpl>& Session) : m_session(Session)\n{\n    return;\n}\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::ConfigureDistribution(_In_opt_ LPCGUID DistroGuid, _In_ ULONG DefaultUid, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->ConfigureDistribution(DistroGuid, DefaultUid, Flags);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::AttachDisk(_In_ LPCWSTR Disk, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    if constexpr (wsl::shared::Arm64)\n    {\n        // Pass-through disk support for ARM64 was added to Windows version 27653.\n        if (wsl::windows::common::helpers::GetWindowsVersion().BuildNumber < 27653)\n        {\n            return WSL_E_WSL_MOUNT_NOT_SUPPORTED;\n        }\n    }\n\n    RETURN_HR_IF(\n        WSL_E_DISK_MOUNT_DISABLED,\n        !wsl::windows::policies::IsFeatureAllowed(wsl::windows::policies::OpenPoliciesKey().get(), wsl::windows::policies::c_allowDiskMount));\n\n    RETURN_HR_IF(\n        E_INVALIDARG,\n        ((WI_IsFlagSet(Flags, LXSS_ATTACH_MOUNT_FLAGS_VHD) && WI_IsFlagSet(Flags, LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH)) ||\n         (WI_IsAnyFlagSet(Flags, ~(LXSS_ATTACH_MOUNT_FLAGS_VHD | LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH)))));\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->AttachDisk(Disk, Flags);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::CreateInstance(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->CreateInstance(DistroGuid, Flags);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::CreateInstance(_In_ LPCWSTR DistributionName, _In_ ULONG Flags)\ntry\n{\n    RETURN_HR_IF(E_INVALIDARG, WI_IsAnyFlagSet(Flags, ~WslSupportCreateInstanceFlags::IgnoreClient));\n\n    GUID distroGuid{};\n    RETURN_IF_FAILED(GetDistributionId(DistributionName, 0, nullptr, &distroGuid));\n\n    ULONG internalFlags = 0;\n    WI_SetFlagIf(internalFlags, LXSS_CREATE_INSTANCE_FLAGS_IGNORE_CLIENT, WI_IsFlagSet(Flags, WslSupportCreateInstanceFlags::IgnoreClient));\n\n    return CreateInstance(&distroGuid, internalFlags, nullptr);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::CreateLxProcess(\n    _In_opt_ LPCGUID DistroGuid,\n    _In_opt_ LPCSTR Filename,\n    _In_ ULONG CommandLineCount,\n    _In_reads_opt_(CommandLineCount) LPCSTR* CommandLine,\n    _In_opt_ LPCWSTR CurrentWorkingDirectory,\n    _In_opt_ LPCWSTR NtPath,\n    _In_reads_opt_(NtEnvironmentLength) PWCHAR NtEnvironment,\n    _In_ ULONG NtEnvironmentLength,\n    _In_opt_ LPCWSTR Username,\n    _In_ SHORT Columns,\n    _In_ SHORT Rows,\n    _In_ ULONG ConsoleHandle,\n    _In_ PLXSS_STD_HANDLES StdHandles,\n    _In_ ULONG Flags,\n    _Out_ GUID* DistributionId,\n    _Out_ GUID* InstanceId,\n    _Out_ HANDLE* ProcessHandle,\n    _Out_ HANDLE* ServerHandle,\n    _Out_ HANDLE* StandardIn,\n    _Out_ HANDLE* StandardOut,\n    _Out_ HANDLE* StandardErr,\n    _Out_ HANDLE* CommunicationChannel,\n    _Out_ HANDLE* InteropSocket,\n    _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->CreateLxProcess(\n        DistroGuid,\n        Filename,\n        CommandLineCount,\n        CommandLine,\n        CurrentWorkingDirectory,\n        NtPath,\n        NtEnvironment,\n        NtEnvironmentLength,\n        Username,\n        Columns,\n        Rows,\n        ULongToHandle(ConsoleHandle),\n        StdHandles,\n        Flags,\n        DistributionId,\n        InstanceId,\n        ProcessHandle,\n        ServerHandle,\n        StandardIn,\n        StandardOut,\n        StandardErr,\n        CommunicationChannel,\n        InteropSocket);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::DetachDisk(_In_ LPCWSTR Disk, _Out_ int* Result, _Out_ int* Step, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->DetachDisk(Disk, Result, Step);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::EnumerateDistributions(\n    _Out_ PULONG DistributionCount, _Out_ LXSS_ENUMERATE_INFO** Distributions, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->EnumerateDistributions(DistributionCount, Distributions);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::ExportDistribution(\n    _In_opt_ LPCGUID DistroGuid, _In_ HANDLE FileHandle, _In_ HANDLE ErrorHandle, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->ExportDistribution(DistroGuid, FileHandle, ErrorHandle, Flags);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::ExportDistributionPipe(\n    _In_opt_ LPCGUID DistroGuid, _In_ HANDLE PipeHandle, _In_ HANDLE ErrorHandle, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->ExportDistribution(DistroGuid, PipeHandle, ErrorHandle, Flags);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::GetDefaultDistribution(_Out_ LXSS_ERROR_INFO* Error, _Out_ LPGUID DefaultDistribution)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->GetDefaultDistribution(DefaultDistribution);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::GetDistributionConfiguration(\n    _In_opt_ LPCGUID DistroGuid,\n    _Out_ LPWSTR* DistributionName,\n    _Out_ ULONG* Version,\n    _Out_ ULONG* DefaultUid,\n    _Out_ ULONG* DefaultEnvironmentCount,\n    _Out_ LPSTR** DefaultEnvironment,\n    _Out_ ULONG* Flags,\n    _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->GetDistributionConfiguration(\n        DistroGuid, DistributionName, Version, DefaultUid, DefaultEnvironmentCount, DefaultEnvironment, Flags);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::GetDistributionConfiguration(\n    _In_ LPCWSTR DistributionName,\n    _Out_ ULONG* Version,\n    _Out_ ULONG* DefaultUid,\n    _Out_ ULONG* DefaultEnvironmentCount,\n    _Out_ LPSTR** DefaultEnvironment,\n    _Out_ ULONG* WslFlags)\ntry\n{\n    GUID distroGuid{};\n    RETURN_IF_FAILED(GetDistributionId(DistributionName, 0, nullptr, &distroGuid));\n\n    wil::unique_cotaskmem_string distroNameLocal;\n    const auto result = GetDistributionConfiguration(\n        &distroGuid, &distroNameLocal, Version, DefaultUid, DefaultEnvironmentCount, DefaultEnvironment, WslFlags, nullptr);\n\n    WI_ASSERT(FAILED(result) || wsl::shared::string::IsEqual(DistributionName, distroNameLocal.get(), true));\n\n    return result;\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::GetDistributionId(_In_ LPCWSTR DistributionName, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error, _Out_ GUID* pDistroGuid)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->GetDistributionId(DistributionName, Flags, pDistroGuid);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::ImportDistributionInplace(\n    _In_ LPCWSTR DistributionName, _In_ LPCWSTR VhdPath, _Out_ LXSS_ERROR_INFO* Error, _Out_ GUID* pDistroGuid)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->ImportDistributionInplace(DistributionName, VhdPath, pDistroGuid);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::ListDistributions(_Out_ ULONG* Count, _Out_ LPWSTR** Distributions)\ntry\n{\n\n    wil::unique_cotaskmem_array_ptr<LXSS_ENUMERATE_INFO> distributions;\n    RETURN_IF_FAILED(EnumerateDistributions(distributions.size_address<ULONG>(), &distributions, nullptr));\n\n    // Filter out distributions that are not in the installed or running state.\n    std::vector<wil::unique_cotaskmem_string> installedDistros{};\n    for (size_t index = 0; index < distributions.size(); index += 1)\n    {\n        if ((distributions[index].State == LxssDistributionStateInstalled) || (distributions[index].State == LxssDistributionStateRunning))\n        {\n            installedDistros.emplace_back(wil::make_unique_string<wil::unique_cotaskmem_string>(distributions[index].DistroName));\n        }\n    }\n\n    auto userDistributions(wil::make_unique_cotaskmem<LPWSTR[]>(installedDistros.size()));\n    for (size_t index = 0; index < installedDistros.size(); index += 1)\n    {\n        userDistributions.get()[index] = installedDistros[index].release();\n    }\n\n    *Count = gsl::narrow_cast<ULONG>(installedDistros.size());\n    *Distributions = userDistributions.release();\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::MountDisk(\n    _In_ LPCWSTR Disk,\n    _In_ ULONG Flags,\n    _In_ ULONG PartitionIndex,\n    _In_opt_ LPCWSTR Name,\n    _In_opt_ LPCWSTR Type,\n    _In_opt_ LPCWSTR Options,\n    _Out_ int* Result,\n    _Out_ int* Step,\n    _Out_ LPWSTR* MountName,\n    _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->MountDisk(Disk, Flags, PartitionIndex, Name, Type, Options, Result, Step, MountName);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::MoveDistribution(_In_ LPCGUID DistroGuid, _In_ LPCWSTR Location, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->MoveDistribution(DistroGuid, Location);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::RegisterDistribution(\n    _In_ LPCWSTR DistributionName,\n    _In_ ULONG Version,\n    _In_ HANDLE FileHandle,\n    _In_ HANDLE ErrorHandle,\n    _In_ LPCWSTR TargetDirectory,\n    _In_ ULONG Flags,\n    _In_ ULONG64 VhdSize,\n    _In_opt_ LPCWSTR PackageFamilyName,\n    _Out_ LPWSTR* InstalledDistributionName,\n    _Out_ LXSS_ERROR_INFO* Error,\n    _Out_ GUID* pDistroGuid)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->RegisterDistribution(\n        DistributionName, Version, FileHandle, ErrorHandle, TargetDirectory, Flags, VhdSize, PackageFamilyName, InstalledDistributionName, pDistroGuid);\n}\nCATCH_RETURN()\n\nHRESULT LxssUserSession::RegisterDistribution(\n    _In_ LPCWSTR DistributionName, _In_ ULONG Version, _In_opt_ HANDLE TarGzFile, _In_opt_ HANDLE TarGzPipe, _In_ LPCWSTR TargetDirectory)\ntry\n{\n    const auto clientProcess = wsl::windows::common::wslutil::OpenCallingProcess(PROCESS_QUERY_LIMITED_INFORMATION);\n    const auto packageFamilyName = wsl::windows::common::wslutil::GetPackageFamilyName(clientProcess.get());\n    GUID distroGuid{};\n\n    return RegisterDistribution(\n        DistributionName,\n        Version,\n        TarGzFile,\n        nullptr,\n        TargetDirectory,\n        0,\n        0,\n        packageFamilyName.empty() ? nullptr : packageFamilyName.c_str(),\n        nullptr,\n        nullptr,\n        &distroGuid);\n}\nCATCH_RETURN();\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::RegisterDistributionPipe(\n    _In_ LPCWSTR DistributionName,\n    _In_ ULONG Version,\n    _In_ HANDLE PipeHandle,\n    _In_ HANDLE ErrorHandle,\n    _In_ LPCWSTR TargetDirectory,\n    _In_ ULONG Flags,\n    _In_ ULONG64 VhdSize,\n    _In_opt_ LPCWSTR PackageFamilyName,\n    _Out_ LPWSTR* InstalledDistributionName,\n    _Out_ LXSS_ERROR_INFO* Error,\n    _Out_ GUID* pDistroGuid)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->RegisterDistribution(\n        DistributionName, Version, PipeHandle, ErrorHandle, TargetDirectory, Flags, VhdSize, PackageFamilyName, InstalledDistributionName, pDistroGuid);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::SetDefaultDistribution(_In_ LPCGUID DistroGuid, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->SetDefaultDistribution(DistroGuid);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::SetDistributionConfiguration(_In_ LPCWSTR DistributionName, _In_ ULONG DefaultUid, _In_ ULONG WslFlags)\ntry\n{\n    GUID distroGuid{};\n    RETURN_IF_FAILED(GetDistributionId(DistributionName, 0, nullptr, &distroGuid));\n\n    return ConfigureDistribution(&distroGuid, DefaultUid, WslFlags, nullptr);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::SetSparse(_In_ LPCGUID DistroGuid, _In_ BOOLEAN Sparse, _In_ BOOLEAN AllowUnsafe, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->SetSparse(DistroGuid, Sparse, AllowUnsafe);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::ResizeDistribution(_In_ LPCGUID DistroGuid, _In_ HANDLE OutputHandle, _In_ ULONG64 NewSize, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->ResizeDistribution(DistroGuid, OutputHandle, NewSize);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Version, _In_ HANDLE StdErrHandle, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->SetVersion(DistroGuid, Version, StdErrHandle);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::Shutdown(BOOL Force)\ntry\n{\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->Shutdown(false, Force ? ShutdownBehavior::Force : ShutdownBehavior::Wait);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::Shutdown()\ntry\n{\n    return Shutdown(false);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::TerminateDistribution(_In_opt_ LPCGUID DistroGuid, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->TerminateDistribution(DistroGuid);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::UnregisterDistribution(_In_ LPCGUID DistroGuid, _Out_ LXSS_ERROR_INFO* Error)\ntry\n{\n    ServiceExecutionContext context(Error);\n\n    const auto session = m_session.lock();\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    return session->UnregisterDistribution(DistroGuid);\n}\nCATCH_RETURN()\n\nHRESULT STDMETHODCALLTYPE LxssUserSession::UnregisterDistribution(_In_ LPCWSTR DistributionName)\ntry\n{\n    GUID distroGuid;\n    RETURN_IF_FAILED(GetDistributionId(DistributionName, 0, nullptr, &distroGuid));\n\n    return UnregisterDistribution(&distroGuid, nullptr);\n}\nCATCH_RETURN()\n\nLxssUserSessionImpl::LxssUserSessionImpl(_In_ PSID userSid, _In_ DWORD sessionId, _Inout_ wsl::windows::service::PluginManager& pluginManager) :\n    m_sessionId(sessionId), m_pluginManager(pluginManager)\n{\n    THROW_IF_WIN32_BOOL_FALSE(::CopySid(sizeof(m_userSid), &m_userSid.Sid, userSid));\n\n    try\n    {\n        wil::unique_hkey lxssKey;\n        wil::unique_handle userToken;\n        {\n            auto runAsUser = wil::CoImpersonateClient();\n            userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n            lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\n        }\n\n        static std::atomic<DWORD> sessionCookie;\n\n        m_session = {sessionCookie++, nullptr, &m_userSid.Sid};\n\n        // Detect existing legacy installs and convert them to the new format.\n        const DWORD state =\n            wsl::windows::common::registry::ReadDword(lxssKey.get(), nullptr, LXSS_LEGACY_INSTALL_VALUE, LxssDistributionStateInvalid);\n\n        if (state == LxssDistributionStateInstalled)\n        {\n            // Create a registration for legacy installs and delete legacy\n            // installed state.\n            std::lock_guard lock(m_instanceLock);\n            _CreateLegacyRegistration(lxssKey.get(), userToken.get());\n            wsl::windows::common::registry::DeleteKeyValue(lxssKey.get(), LXSS_LEGACY_INSTALL_VALUE);\n        }\n\n        // Create a threadpool timer to terminate a Linux utility VM that is idle.\n        m_vmTerminationTimer.reset(CreateThreadpoolTimer(s_VmIdleTerminate, this, nullptr));\n        THROW_IF_NULL_ALLOC(m_vmTerminationTimer);\n\n        // Register for timezone update notifications.\n\n        auto listenForTimeZoneChanges = [this] {\n            try\n            {\n                WNDCLASSEX windowClass{};\n                windowClass.cbSize = sizeof(windowClass);\n                windowClass.lpfnWndProc = s_TimezoneWindowProc;\n                windowClass.hInstance = nullptr;\n                windowClass.lpszClassName = L\"wslservice-timezone-notifications\";\n                THROW_LAST_ERROR_IF(RegisterClassExW(&windowClass) == 0);\n\n                // Note: HWND_MESSAGE cannot be used here because such windows don't receive broadcast messages like WM_TIMECHANGE\n                const wil::unique_hwnd windowHandle{CreateWindowExW(\n                    0,\n                    windowClass.lpszClassName,\n                    nullptr,\n                    WS_OVERLAPPEDWINDOW,\n                    CW_USEDEFAULT,\n                    CW_USEDEFAULT,\n                    CW_USEDEFAULT,\n                    CW_USEDEFAULT,\n                    nullptr,\n                    nullptr,\n                    windowClass.hInstance,\n                    nullptr)};\n\n                THROW_LAST_ERROR_IF(!windowHandle);\n                SetWindowLongPtr(windowHandle.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));\n\n                MSG windowMessage{};\n                while (GetMessageW(&windowMessage, nullptr, 0, 0))\n                {\n                    TranslateMessage(&windowMessage);\n                    DispatchMessage(&windowMessage);\n                }\n            }\n            CATCH_LOG();\n        };\n\n        m_timezoneThread = std::thread{listenForTimeZoneChanges};\n\n        // Shutdown the inbox session for the current user if needed, this is only required once after the\n        // lifted package is installed to ensure that the inbox service has released per-user resources.\n        LOG_IF_FAILED(wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&] {\n            // Open a handle to the service control manager and check if the inbox service is registered.\n            const wil::unique_schandle manager{OpenSCManager(nullptr, nullptr, SC_MANAGER_ENUMERATE_SERVICE)};\n            THROW_LAST_ERROR_IF(!manager);\n\n            const wil::unique_schandle service{OpenServiceW(manager.get(), LXSS_INBOX_SERVICE_NAME, SERVICE_QUERY_STATUS)};\n            if (!service)\n            {\n                return;\n            }\n\n            // Check if the service is already stopped.\n            SERVICE_STATUS status;\n            THROW_IF_WIN32_BOOL_FALSE(QueryServiceStatus(service.get(), &status));\n\n            if (status.dwCurrentState == SERVICE_STOPPED)\n            {\n                return;\n            }\n\n            // Shutdown the user's session.\n            auto runAsUser = wil::impersonate_token(userToken.get());\n            const auto wslSupport = wil::CoCreateInstance<LxssUserSessionInBox, IWslSupport>(CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING);\n            THROW_IF_FAILED(wslSupport->Shutdown());\n        }));\n    }\n    CATCH_LOG()\n}\n\nLxssUserSessionImpl::~LxssUserSessionImpl()\n{\n    if (m_timezoneThread.joinable())\n    {\n        LOG_IF_WIN32_BOOL_FALSE(PostThreadMessage(GetThreadId(m_timezoneThread.native_handle()), WM_QUIT, 0, 0));\n        m_timezoneThread.join();\n    }\n\n    m_lifetimeManager.ClearCallbacks();\n\n    // Ensure that if there are no running instances.\n    WI_ASSERT(m_runningInstances.empty());\n}\n\nHRESULT LxssUserSessionImpl::AttachDisk(_In_ LPCWSTR Disk, _In_ ULONG Flags)\n{\n    ExecutionContext context(Context::AttachDisk);\n\n    std::lock_guard lock(m_instanceLock);\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n\n    // Validate that at least one WSL2 distro is installed\n    auto pred = [&](const auto& e) { return WI_IsFlagSet(e.Read(Property::Flags), LXSS_DISTRO_FLAGS_VM_MODE); };\n\n    auto distributions = _EnumerateDistributions(lxssKey.get(), true);\n    RETURN_HR_IF(WSL_E_WSL2_NEEDED, !std::any_of(distributions.begin(), distributions.end(), pred));\n\n    return wil::ResultFromException([&]() {\n        _CreateVm();\n        const auto diskType = WI_IsFlagSet(Flags, LXSS_ATTACH_MOUNT_FLAGS_VHD) ? WslCoreVm::DiskType::VHD : WslCoreVm::DiskType::PassThrough;\n        const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n        m_utilityVm->AttachDisk(Disk, diskType, {}, true, userToken.get());\n    });\n}\n\nHRESULT LxssUserSessionImpl::ConfigureDistribution(_In_opt_ LPCGUID DistroGuid, _In_ ULONG DefaultUid, _In_ ULONG Flags)\ntry\n{\n    ExecutionContext context(Context::ConfigureDistro);\n\n    WSL_LOG(\"ConfigureDistribution\", TraceLoggingValue(DefaultUid, \"DefaultUid\"), TraceLoggingValue(Flags, \"Flags\"));\n\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    std::lock_guard lock(m_instanceLock);\n\n    // Ensure the distribution exists.\n    auto distribution = DistributionRegistration::OpenOrDefault(lxssKey.get(), DistroGuid);\n\n    auto configuration = s_GetDistributionConfiguration(distribution);\n\n    // Validate parameters.\n    RETURN_HR_IF(\n        E_INVALIDARG,\n        ((DefaultUid == LX_UID_INVALID) || (Flags != LXSS_DISTRO_FLAGS_UNCHANGED && WI_IsAnyFlagSet(Flags, ~LXSS_DISTRO_FLAGS_ALL))));\n\n    // If the configuration is changed, terminate the distribution so the new settings will take effect.\n    bool modified = false;\n    if (DefaultUid != distribution.Read(Property::DefaultUid))\n    {\n        distribution.Write(Property::DefaultUid, DefaultUid);\n        modified = true;\n    }\n\n    if (Flags != LXSS_DISTRO_FLAGS_UNCHANGED)\n    {\n        // The VM Mode flag is not configurable via this API.\n        if (WI_IsFlagSet(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE))\n        {\n            WI_SetFlag(Flags, LXSS_DISTRO_FLAGS_VM_MODE);\n        }\n        else\n        {\n            WI_ClearFlag(Flags, LXSS_DISTRO_FLAGS_VM_MODE);\n        }\n\n        if (Flags != configuration.Flags)\n        {\n            distribution.Write(Property::Flags, Flags);\n            modified = true;\n        }\n    }\n\n    if (modified)\n    {\n        _TerminateInstanceInternal(&distribution.Id(), false);\n    }\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT LxssUserSessionImpl::CreateInstance(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags)\ntry\n{\n    // Register the client process with the lifetime manager so when the last\n    // client goes away the instance is terminated (after a timeout).\n    _CreateInstance(DistroGuid, Flags);\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT LxssUserSessionImpl::CreateLxProcess(\n    _In_opt_ LPCGUID DistroGuid,\n    _In_opt_ LPCSTR Filename,\n    _In_ ULONG CommandLineCount,\n    _In_reads_opt_(CommandLineCount) LPCSTR* CommandLine,\n    _In_opt_ LPCWSTR CurrentWorkingDirectory,\n    _In_opt_ LPCWSTR NtPath,\n    _In_reads_opt_(NtEnvironmentLength) PWCHAR NtEnvironment,\n    _In_ ULONG NtEnvironmentLength,\n    _In_opt_ LPCWSTR Username,\n    _In_ SHORT Columns,\n    _In_ SHORT Rows,\n    _In_ HANDLE ConsoleHandle,\n    _In_ PLXSS_STD_HANDLES StdHandles,\n    _In_ ULONG Flags,\n    _Out_ GUID* DistributionId,\n    _Out_ GUID* InstanceId,\n    _Out_ HANDLE* ProcessHandle,\n    _Out_ HANDLE* ServerHandle,\n    _Out_ HANDLE* StandardIn,\n    _Out_ HANDLE* StandardOut,\n    _Out_ HANDLE* StandardErr,\n    _Out_ HANDLE* CommunicationChannel,\n    _Out_ HANDLE* InteropSocket)\ntry\n{\n    // This API handles launching processes three ways:\n    // 1. If Filename and CommandLine are both NULL, the user's default shell\n    //    is launched. The default shell is stored in /etc/passwd.\n    // 2. If Filename is NULL but CommandLine is not, the user's default shell\n    //    is used to invoke the specified command. For example:\n    //        /bin/bash -c \"command\"\n    // 3. If Filename and CommandLine are both non-NULL, they are passed along\n    //    as-is to the exec system call by the init daemon.\n\n    // Create an instance to run the process.\n    auto instance = _CreateInstance(DistroGuid, Flags);\n\n    // Query process creation context.\n    auto distributionId = instance->GetDistributionId();\n    auto context = s_GetCreateProcessContext(distributionId, WI_IsFlagSet(Flags, LXSS_CREATE_INSTANCE_FLAGS_USE_SYSTEM_DISTRO));\n\n    _SetHttpProxyInfo(context.DefaultEnvironment);\n\n    // Parse the create process params.\n    auto parsed = LxssCreateProcess::ParseArguments(\n        Filename,\n        CommandLineCount,\n        CommandLine,\n        CurrentWorkingDirectory,\n        NtPath,\n        NtEnvironment,\n        NtEnvironmentLength,\n        Username,\n        context.DefaultEnvironment,\n        context.Flags);\n\n    if (WI_IsFlagSet(Flags, LXSS_CREATE_INSTANCE_FLAGS_SHELL_LOGIN))\n    {\n        THROW_HR_IF(E_INVALIDARG, ARGUMENT_PRESENT(Filename));\n        parsed.ShellOptions = ShellOptionsLogin;\n    }\n\n    // Initialize console data and launch the process.\n    CreateLxProcessConsoleData consoleData;\n    if (ConsoleHandle)\n    {\n        consoleData.ConsoleHandle.reset(wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ConsoleHandle));\n    }\n\n    consoleData.ClientProcess = wsl::windows::common::wslutil::OpenCallingProcess(PROCESS_VM_READ | GENERIC_READ | SYNCHRONIZE);\n    instance->CreateLxProcess(\n        parsed, context, consoleData, Columns, Rows, StdHandles, InstanceId, ProcessHandle, ServerHandle, StandardIn, StandardOut, StandardErr, CommunicationChannel, InteropSocket);\n\n    *DistributionId = distributionId;\n    return S_OK;\n}\nCATCH_RETURN()\n\nvoid LxssUserSessionImpl::ClearDiskStateInRegistry(_In_ const LPCWSTR Disk)\n{\n    bool deleted = !ARGUMENT_PRESENT(Disk);\n\n    const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(&m_userSid.Sid);\n    for (const auto& e : wsl::windows::common::registry::EnumKeys(key.get(), KEY_READ))\n    {\n        if (Disk == nullptr || wsl::windows::common::registry::ReadString(e.second.get(), nullptr, c_diskValueName) == Disk)\n        {\n            wsl::windows::common::registry::DeleteKey(key.get(), e.first.c_str());\n            deleted = true;\n        }\n    }\n\n    THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), !deleted);\n}\n\nHRESULT LxssUserSessionImpl::DetachDisk(_In_ LPCWSTR Disk, _Out_ int* Result, _Out_ int* Step)\n{\n    ExecutionContext context(Context::DetachDisk);\n\n    std::lock_guard lock(m_instanceLock);\n\n    // If the UVM isn't running, simply clear the disk state in the registry, if any\n    if (!m_utilityVm)\n    {\n        return wil::ResultFromException([&]() {\n            ClearDiskStateInRegistry(Disk);\n            *Result = 0;\n            *Step = LxMiniInitMountStepNone;\n        });\n    }\n\n    return wil::ResultFromException([&]() { std::tie(*Result, *Step) = m_utilityVm->DetachDisk(Disk); });\n}\n\nHRESULT LxssUserSessionImpl::MountDisk(\n    _In_ LPCWSTR Disk,\n    _In_ ULONG Flags,\n    _In_ ULONG PartitionIndex,\n    _In_opt_ LPCWSTR Name,\n    _In_opt_ LPCWSTR Type,\n    _In_opt_ LPCWSTR Options,\n    _Out_ int* Result,\n    _Out_ int* Step,\n    _Out_ LPWSTR* MountName)\n{\n    ExecutionContext context(Context::DetachDisk);\n\n    std::lock_guard lock(m_instanceLock);\n    return wil::ResultFromException([&]() {\n        _CreateVm();\n        const auto MountDiskType = WI_IsFlagSet(Flags, LXSS_ATTACH_MOUNT_FLAGS_VHD) ? WslCoreVm::DiskType::VHD : WslCoreVm::DiskType::PassThrough;\n        const auto MountResult = m_utilityVm->MountDisk(Disk, MountDiskType, PartitionIndex, Name, Type, Options);\n        const auto MountNameWide = wsl::shared::string::MultiByteToWide(MountResult.MountPointName);\n        *Result = MountResult.Result;\n        *Step = MountResult.Step;\n        *MountName = wil::make_unique_string<wil::unique_cotaskmem_string>(MountNameWide.c_str()).release();\n    });\n}\n\nHRESULT LxssUserSessionImpl::MoveDistribution(_In_ LPCGUID DistroGuid, _In_ LPCWSTR Location)\n{\n    ExecutionContext context(Context::MoveDistro);\n\n    std::lock_guard lock(m_instanceLock);\n\n    // Fail if the distribution is running.\n    RETURN_HR_IF(WSL_E_DISTRO_NOT_STOPPED, m_runningInstances.contains(*DistroGuid));\n\n    // Lookup the distribution configuration\n    const auto lxssKey = s_OpenLxssUserKey();\n    _ValidateDistributionNameAndPathNotInUse(lxssKey.get(), Location, nullptr);\n\n    auto registration = DistributionRegistration::Open(lxssKey.get(), *DistroGuid);\n    auto distro = s_GetDistributionConfiguration(registration);\n\n    RETURN_HR_IF(E_NOTIMPL, WI_IsFlagClear(distro.Flags, LXSS_DISTRO_FLAGS_VM_MODE));\n\n    // Build the final vhd path.\n    std::filesystem::path newVhdPath = Location;\n    RETURN_HR_IF(E_INVALIDARG, newVhdPath.empty());\n\n    newVhdPath /= distro.VhdFilePath.filename();\n\n    auto impersonate = wil::CoImpersonateClient();\n\n    // Create the distribution base folder\n    std::error_code error;\n    std::filesystem::create_directories(Location, error);\n    if (error.value())\n    {\n        THROW_WIN32(error.value());\n    }\n\n    // Move the VHD to the new location.\n    THROW_IF_WIN32_BOOL_FALSE(MoveFileEx(distro.VhdFilePath.c_str(), newVhdPath.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH));\n\n    auto revert = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\n        THROW_IF_WIN32_BOOL_FALSE(MoveFileEx(\n            newVhdPath.c_str(), distro.VhdFilePath.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH));\n\n        // Write the location back to the original path in case the second registry write failed. Otherwise, this is a no-op.\n        registration.Write(Property::BasePath, distro.BasePath.c_str());\n    });\n\n    // Update the registry location\n    registration.Write(Property::BasePath, Location);\n    registration.Write(Property::VhdFileName, newVhdPath.filename().c_str());\n\n    revert.release();\n\n    return S_OK;\n}\n\nHRESULT LxssUserSessionImpl::EnumerateDistributions(_Out_ PULONG DistributionCount, _Out_ LXSS_ENUMERATE_INFO** Distributions)\n{\n    // Get a list of all registered distributions.\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    std::lock_guard lock(m_instanceLock);\n    const auto distributions = _EnumerateDistributions(lxssKey.get(), true);\n\n    // Get the default distribution.\n    //\n    // N.B. It is possible the default to not exist, for example if there is\n    //      a single distribution that is being installed.\n    GUID defaultGuid = GUID_NULL;\n    try\n    {\n        defaultGuid = _GetDefaultDistro(lxssKey.get());\n    }\n    CATCH_LOG()\n\n    const ULONG numberOfDistributions = gsl::narrow_cast<ULONG>(distributions.size());\n    auto userDistributions(wil::make_unique_cotaskmem<LXSS_ENUMERATE_INFO[]>(numberOfDistributions));\n\n    // Fill in information about each distribution.\n    for (ULONG index = 0; index < numberOfDistributions; index += 1)\n    {\n\n        auto configuration = s_GetDistributionConfiguration(distributions[index]);\n        auto state = static_cast<LxssDistributionState>(configuration.State);\n\n        if (m_runningInstances.contains(distributions[index].Id()))\n        {\n            state = LxssDistributionStateRunning;\n        }\n        else if (state == LxssDistributionStateInstalled)\n        {\n            auto distro = std::find_if(m_lockedDistributions.begin(), m_lockedDistributions.end(), [&](const auto& pair) {\n                return IsEqualGUID(distributions[index].Id(), pair.first);\n            });\n\n            if (distro != m_lockedDistributions.end())\n            {\n                state = distro->second;\n            }\n        }\n\n        const auto current = &userDistributions.get()[index];\n        current->DistroGuid = distributions[index].Id();\n        current->State = state;\n        current->Version = WI_IsFlagSet(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE) ? LXSS_WSL_VERSION_2 : LXSS_WSL_VERSION_1;\n        current->Flags = 0;\n        WI_SetFlagIf(current->Flags, LXSS_ENUMERATE_FLAGS_DEFAULT, IsEqualGUID(distributions[index].Id(), defaultGuid));\n\n        static_assert((RTL_NUMBER_OF(current->DistroName) - 1) == LX_INIT_DISTRO_NAME_MAX);\n\n        memset(current->DistroName, 0, sizeof(current->DistroName));\n        wcscpy_s(current->DistroName, RTL_NUMBER_OF(current->DistroName) - 1, configuration.Name.c_str());\n    }\n\n    *DistributionCount = numberOfDistributions;\n    *Distributions = userDistributions.release();\n    return S_OK;\n}\n\nHRESULT LxssUserSessionImpl::ExportDistribution(_In_opt_ LPCGUID DistroGuid, _In_ HANDLE FileHandle, _In_ HANDLE ErrorHandle, _In_ ULONG Flags)\n{\n    RETURN_HR_IF(E_INVALIDARG, (WI_IsAnyFlagSet(Flags, ~LXSS_EXPORT_DISTRO_FLAGS_ALL)));\n\n    LXSS_DISTRO_CONFIGURATION configuration;\n    wil::unique_hkey distroKey;\n    try\n    {\n        const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n        std::lock_guard lock(m_instanceLock);\n\n        const auto registration = DistributionRegistration::OpenOrDefault(lxssKey.get(), DistroGuid);\n\n        // Ensure the distribution is installed.\n        configuration = s_GetDistributionConfiguration(registration);\n        RETURN_HR_IF(E_ILLEGAL_STATE_CHANGE, (configuration.State != LxssDistributionStateInstalled));\n\n        // Exporting a WSL1 distro is not possible if the VHD flag is specified.\n        RETURN_HR_IF(WSL_E_WSL2_NEEDED, WI_IsFlagSet(Flags, LXSS_EXPORT_DISTRO_FLAGS_VHD) && WI_IsFlagClear(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE));\n\n        // Exporting a WSL1 distro is not possible if the lxcore driver is not present.\n        RETURN_HR_IF(WSL_E_WSL1_NOT_SUPPORTED, WI_IsFlagClear(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE) && !g_lxcoreInitialized);\n\n        // Add the distribution to the list of converting distributions.\n        _ConversionBegin(configuration.DistroId, LxssDistributionStateExporting);\n    }\n    CATCH_RETURN()\n\n    // Set up a scope exit member to remove the distribution from the converting list.\n    auto exportComplete = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ConversionComplete(configuration.DistroId); });\n\n    // Log telemetry to track how long exporting the distribution takes.\n    WSL_LOG_TELEMETRY(\n        \"ExportDistributionBegin\",\n        PDT_ProductAndServicePerformance,\n        TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n        TraceLoggingValue(Flags, \"flags\"));\n\n    HRESULT result;\n    auto enableExit = wil::scope_exit([&] {\n        WSL_LOG_TELEMETRY(\n            \"ExportDistributionEnd\",\n            PDT_ProductAndServicePerformance,\n            TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n            TraceLoggingValue(result, \"result\"),\n            TraceLoggingValue(Flags, \"flags\"));\n    });\n\n    // Export the distribution.\n    try\n    {\n        const wil::unique_handle clientProcess = wsl::windows::common::wslutil::OpenCallingProcess(GENERIC_READ | SYNCHRONIZE);\n        if (WI_IsFlagSet(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE))\n        {\n            if (WI_IsFlagSet(Flags, LXSS_EXPORT_DISTRO_FLAGS_VHD))\n            {\n                const wil::unique_handle userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n                auto runAsUser = wil::impersonate_token(userToken.get());\n\n                // Ensure the target file has the correct file extension.\n                if (GetFileType(FileHandle) == FILE_TYPE_DISK)\n                {\n                    std::wstring exportPath;\n                    THROW_IF_FAILED(wil::GetFinalPathNameByHandleW(FileHandle, exportPath));\n\n                    const auto sourceFileExtension = configuration.VhdFilePath.extension().native();\n                    const auto targetFileExtension = std::filesystem::path(exportPath).extension().native();\n                    if (!wsl::windows::common::string::IsPathComponentEqual(sourceFileExtension, targetFileExtension))\n                    {\n                        THROW_HR_WITH_USER_ERROR(\n                            WSL_E_EXPORT_FAILED, wsl::shared::Localization::MessageRequiresFileExtension(sourceFileExtension.c_str()));\n                    }\n                }\n\n                const wil::unique_hfile vhdFile(CreateFileW(\n                    configuration.VhdFilePath.c_str(), GENERIC_READ, (FILE_SHARE_READ | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));\n\n                RETURN_LAST_ERROR_IF(!vhdFile);\n\n                wsl::windows::common::relay::InterruptableRelay(vhdFile.get(), FileHandle, clientProcess.get(), LXSS_RELAY_BUFFER_SIZE);\n            }\n            else\n            {\n                auto vmContext = _RunUtilityVmSetup(configuration, LxMiniInitMessageExport, Flags);\n\n                wsl::windows::common::relay::ScopedRelay stdErrRelay(\n                    wil::unique_handle{reinterpret_cast<HANDLE>(vmContext.errorSocket.release())}, ErrorHandle);\n\n                // Relay the filesystem file contents to the tar.gz handle.\n                wsl::windows::common::relay::InterruptableRelay(\n                    reinterpret_cast<HANDLE>(vmContext.tarSocket.get()), FileHandle, clientProcess.get(), LXSS_RELAY_BUFFER_SIZE);\n\n                // Wait for the utility VM to finish expanding the tar and ensure that\n                // the operation was successful.\n                ULONG exitCode = 1;\n                vmContext.instance->GetInitPort()->Receive(&exitCode, sizeof(exitCode), clientProcess.get());\n\n                // Flush any pending IO on the error relay before exiting.\n                stdErrRelay.Sync();\n\n                THROW_HR_IF(WSL_E_EXPORT_FAILED, (exitCode != 0));\n            }\n        }\n        else\n        {\n            auto mounts = _CreateSetupMounts(configuration);\n\n            const char* formatArgs = nullptr;\n\n            if (WI_IsFlagSet(Flags, LXSS_EXPORT_DISTRO_FLAGS_GZIP))\n            {\n                THROW_HR_IF(E_INVALIDARG, WI_IsFlagSet(Flags, LXSS_EXPORT_DISTRO_FLAGS_XZIP));\n\n                formatArgs = LXSS_BSDTAR_CREATE_ARGS_GZIP;\n            }\n            else if (WI_IsFlagSet(Flags, LXSS_EXPORT_DISTRO_FLAGS_XZIP))\n            {\n                formatArgs = LXSS_BSDTAR_CREATE_ARGS_XZIP;\n            }\n            else\n            {\n                formatArgs = LXSS_BSDTAR_CREATE_ARGS;\n            }\n\n            const auto commandLine = std::format(\"{} -C {}{}\", LXSS_BSDTAR_PATH, LXSS_ROOTFS_MOUNT, formatArgs);\n\n            const auto elfContext = _RunElfBinary(\n                commandLine.c_str(),\n                configuration.BasePath.c_str(),\n                clientProcess.get(),\n                nullptr,\n                FileHandle,\n                ErrorHandle,\n                mounts.data(),\n                static_cast<ULONG>(mounts.size()));\n\n            const auto exitStatus = _GetElfExitStatus(elfContext);\n            THROW_HR_IF(WSL_E_EXPORT_FAILED, exitStatus != 0);\n        }\n\n        result = S_OK;\n    }\n    catch (...)\n    {\n        result = wil::ResultFromCaughtException();\n    }\n\n    return result;\n}\n\nHRESULT LxssUserSessionImpl::GetDefaultDistribution(_Out_ LPGUID DefaultDistribution)\ntry\n{\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    std::lock_guard lock(m_instanceLock);\n    *DefaultDistribution = _GetDefaultDistro(lxssKey.get());\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT LxssUserSessionImpl::GetDistributionConfiguration(\n    _In_opt_ LPCGUID DistroGuid,\n    _Out_ LPWSTR* DistributionName,\n    _Out_ ULONG* Version,\n    _Out_ ULONG* DefaultUid,\n    _Out_ ULONG* DefaultEnvironmentCount,\n    _Out_ LPSTR** DefaultEnvironment,\n    _Out_ ULONG* Flags)\ntry\n{\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    std::lock_guard lock(m_instanceLock);\n\n    const auto registration = DistributionRegistration::OpenOrDefault(lxssKey.get(), DistroGuid);\n    const auto configuration = s_GetDistributionConfiguration(registration);\n\n    // Write configuration information back to the calling process.\n    *DistributionName = wil::make_cotaskmem_string(configuration.Name.c_str()).release();\n    *Version = configuration.Version;\n    *DefaultUid = registration.Read(Property::DefaultUid);\n    *Flags = configuration.Flags;\n    const auto defaultEnvironment = registration.Read(Property::DefaultEnvironment);\n    *DefaultEnvironmentCount = gsl::narrow_cast<ULONG>(defaultEnvironment.size());\n    auto environment(wil::make_unique_cotaskmem<LPSTR[]>(defaultEnvironment.size()));\n    for (size_t index = 0; index < defaultEnvironment.size(); index += 1)\n    {\n        environment.get()[index] =\n            wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(defaultEnvironment[index].c_str()).release();\n    }\n\n    *DefaultEnvironment = environment.release();\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT LxssUserSessionImpl::GetDistributionId(_In_ LPCWSTR DistributionName, _In_ ULONG Flags, _Out_ GUID* pDistroGuid)\ntry\n{\n    // The client must provide a non-empty string.\n    //\n    // N.B. COM insures that the name buffer is non-NULL.\n    RETURN_HR_IF(E_INVALIDARG, (wcslen(DistributionName) == 0));\n\n    // Validate flags.\n    RETURN_HR_IF(E_INVALIDARG, (WI_IsAnyFlagSet(Flags, ~LXSS_GET_DISTRO_ID_LIST_ALL)));\n\n    // Open the user's lxss registry key.\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    const bool listAll = WI_IsFlagSet(Flags, LXSS_GET_DISTRO_ID_LIST_ALL);\n    bool distroFound = false;\n\n    // Lock the session and search for a distribution that has a matching name.\n    std::lock_guard lock(m_instanceLock);\n    const auto distros = _EnumerateDistributions(lxssKey.get(), listAll);\n    for (const auto& registration : distros)\n    {\n        if (wsl::shared::string::IsEqual(DistributionName, registration.Read(Property::Name), true))\n        {\n            distroFound = true;\n            *pDistroGuid = registration.Id();\n            break;\n        }\n    }\n\n    // Return an error if no distribution was found with a matching name.\n    RETURN_HR_IF(WSL_E_DISTRO_NOT_FOUND, !distroFound);\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nDWORD LxssUserSessionImpl::GetSessionCookie() const\n{\n    return m_session.SessionId;\n}\n\nDWORD LxssUserSessionImpl::GetSessionId() const\n{\n    return m_sessionId;\n}\n\nPSID LxssUserSessionImpl::GetUserSid()\n{\n    return &m_userSid.Sid;\n}\n\nHRESULT\nLxssUserSessionImpl::ImportDistributionInplace(_In_ LPCWSTR DistributionName, _In_ LPCWSTR VhdPath, _Out_ GUID* pDistroGuid)\n{\n    ExecutionContext context(Context::RegisterDistro);\n\n    s_ValidateDistroName(DistributionName);\n\n    // Return an error if the path is not absolute or does not have a valid VHD file extension.\n    const std::filesystem::path path{VhdPath};\n    RETURN_HR_IF(E_INVALIDARG, !path.is_absolute() || !wsl::windows::common::wslutil::IsVhdFile(path));\n\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    std::lock_guard lock(m_instanceLock);\n\n    // Create a registration for the distribution.\n    //\n    // N.B. Import inplace is always WSL2.\n    _ValidateDistributionNameAndPathNotInUse(lxssKey.get(), path.parent_path().c_str(), DistributionName);\n\n    constexpr ULONG flags = LXSS_DISTRO_FLAGS_DEFAULT | LXSS_DISTRO_FLAGS_VM_MODE;\n    auto registration = DistributionRegistration::Create(\n        lxssKey.get(),\n        {},\n        DistributionName,\n        LXSS_DISTRO_VERSION_CURRENT,\n        path.parent_path().c_str(),\n        flags,\n        LX_UID_ROOT,\n        nullptr,\n        path.filename().c_str(),\n        false);\n\n    auto configuration = s_GetDistributionConfiguration(registration);\n\n    // Declare a scope exit variable to clean up on failure.\n    const wil::unique_handle userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n        {\n            auto runAsUser = wil::impersonate_token(userToken.get());\n            _DeleteDistribution(configuration, LXSS_DELETE_DISTRO_FLAGS_UNMOUNT);\n        }\n\n        registration.Delete(lxssKey.get());\n    });\n\n    const auto vmContext = _RunUtilityVmSetup(configuration, LxMiniInitMessageImportInplace);\n    auto* channel = dynamic_cast<WslCoreInstance::WslCorePort*>(vmContext.instance->GetInitPort().get());\n\n    gsl::span<gsl::byte> span;\n    const auto& message = channel->GetChannel().ReceiveMessage<LX_MINI_INIT_IMPORT_RESULT>(&span);\n\n    // Process the import result message.\n    THROW_HR_IF(WSL_E_IMPORT_FAILED, (message.Result != 0));\n\n    _ProcessImportResultMessage(message, span, lxssKey.get(), configuration, registration);\n\n    // Set the distribution as installed.\n    _SetDistributionInstalled(lxssKey.get(), registration.Id());\n    cleanup.release();\n\n    _SendDistributionRegisteredEvent(configuration);\n\n    _LaunchOOBEIfNeeded();\n\n    // Log when a distro is imported in place\n    WSL_LOG_TELEMETRY(\n        \"ImportDistributionInplace\",\n        PDT_ProductAndServiceUsage,\n        TraceLoggingValue(DistributionName, \"distroName\"),\n        TraceLoggingValue(path.filename().c_str(), \"fileName\"));\n\n    *pDistroGuid = registration.Id();\n    return S_OK;\n}\n\nHRESULT LxssUserSessionImpl::RegisterDistribution(\n    _In_ LPCWSTR DistributionName,\n    _In_ ULONG Version,\n    _In_ HANDLE FileHandle,\n    _In_ HANDLE ErrorHandle,\n    _In_ LPCWSTR TargetDirectory,\n    _In_ ULONG Flags,\n    _In_ ULONG64 VhdSize,\n    _In_opt_ LPCWSTR PackageFamilyName,\n    _Out_opt_ LPWSTR* InstalledDistributionName,\n    _Out_ GUID* pDistroGuid)\n{\n    ExecutionContext context(Context::RegisterDistro);\n\n    RETURN_HR_IF(E_INVALIDARG, (WI_IsAnyFlagSet(Flags, ~LXSS_IMPORT_DISTRO_FLAGS_ALL)));\n\n    // Set up a scope exit member to log registration status.\n    HRESULT result = E_FAIL;\n    auto registerExit = wil::scope_exit([&] {\n        // Log when a distribution registration ends and its result\n        WSL_LOG_TELEMETRY(\n            \"RegisterDistributionEnd\",\n            PDT_ProductAndServiceUsage,\n            TraceLoggingValue(DistributionName, \"name\"),\n            TraceLoggingHexUInt32(result, \"result\"),\n            TraceLoggingValue(Version, \"version\"),\n            TraceLoggingValue(Flags, \"flags\"));\n    });\n\n    try\n    {\n        // Log when a distribution is being registered in WSL\n        WSL_LOG_TELEMETRY(\n            \"RegisterDistributionBegin\",\n            PDT_ProductAndServiceUsage,\n            TraceLoggingValue(DistributionName, \"name\"),\n            TraceLoggingValue(Version, \"version\"),\n            TraceLoggingValue(Flags, \"flags\"));\n\n        if (DistributionName != nullptr)\n        {\n            s_ValidateDistroName(DistributionName);\n        }\n\n        // Impersonate the user and open their lxss registry key.\n        wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n\n        // Determine the filesystem version. If WslFs is not enabled, downgrade\n        // the version.\n        ULONG FilesystemVersion = LXSS_DISTRO_VERSION_CURRENT;\n        if (wsl::windows::common::registry::ReadDword(lxssKey.get(), nullptr, WSL_NEW_DISTRO_LXFS, 0) != 0)\n        {\n            if (LXSS_DISTRO_USES_WSL_FS(FilesystemVersion) != FALSE)\n            {\n                FilesystemVersion = LXSS_DISTRO_VERSION_1;\n            }\n        }\n\n        // Validate the version number.\n        if (Version == LXSS_WSL_VERSION_DEFAULT)\n        {\n            Version = wsl::windows::common::registry::ReadDword(lxssKey.get(), nullptr, LXSS_WSL_DEFAULT_VERSION, LXSS_WSL_VERSION_2);\n        }\n\n        RETURN_HR_IF(E_INVALIDARG, ((Version != LXSS_WSL_VERSION_1) && (Version != LXSS_WSL_VERSION_2)));\n\n        // Registering a WSL1 distro is not possible if any VHD flags are specified.\n        RETURN_HR_IF(\n            WSL_E_WSL2_NEEDED,\n            WI_IsAnyFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_VHD | LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD) && (Version == LXSS_WSL_VERSION_1));\n\n        // Registering a vhd with the fixed vhd flag is not allowed.\n        if (WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_VHD))\n        {\n            RETURN_HR_IF(E_INVALIDARG, WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD));\n        }\n\n        // Registering a distro with a fixed VHD is only allowed if a size is specified.\n        RETURN_HR_IF(E_INVALIDARG, VhdSize == 0 && WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD));\n\n        // Registering a WSL1 distro is not possible if the lxcore driver is not present.\n        RETURN_HR_IF(WSL_E_WSL1_NOT_SUPPORTED, (Version == LXSS_WSL_VERSION_1) && !g_lxcoreInitialized);\n\n        DistributionRegistration registration;\n        LXSS_DISTRO_CONFIGURATION configuration;\n        std::filesystem::path distributionPath;\n        wil::unique_handle userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n        auto config = _GetResultantConfig(userToken.get());\n\n        {\n            std::lock_guard lock(m_instanceLock);\n\n            // Create a registration for the distribution and determine which version should be used.\n            ULONG flags = LXSS_DISTRO_FLAGS_DEFAULT;\n            WI_SetFlagIf(flags, LXSS_DISTRO_FLAGS_VM_MODE, (Version == LXSS_WSL_VERSION_2));\n\n            GUID DistributionId{};\n            THROW_IF_FAILED(CoCreateGuid(&DistributionId));\n\n            if (TargetDirectory == nullptr)\n            {\n                distributionPath = config.DefaultDistributionLocation / wsl::shared::string::GuidToString<wchar_t>(DistributionId);\n            }\n            else\n            {\n                distributionPath = TargetDirectory;\n            }\n\n            TargetDirectory = nullptr; // Make sure this isn't reused later.\n\n            _ValidateDistributionNameAndPathNotInUse(lxssKey.get(), distributionPath.c_str(), DistributionName);\n\n            if (!std::filesystem::exists(distributionPath))\n            {\n                auto impersonate = wil::CoImpersonateClient();\n                wil::CreateDirectoryDeep(distributionPath.c_str());\n            }\n\n            // If importing a vhd, determine if it is a .vhd or .vhdx.\n            std::wstring vhdName{LXSS_VM_MODE_VHD_NAME};\n            if ((WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_VHD)) && (GetFileType(FileHandle) == FILE_TYPE_DISK))\n            {\n                std::wstring pathBuffer;\n                THROW_IF_FAILED(wil::GetFinalPathNameByHandleW(FileHandle, pathBuffer));\n\n                std::filesystem::path vhdPath{std::move(pathBuffer)};\n                if (!wsl::windows::common::wslutil::IsVhdFile(vhdPath))\n                {\n                    using namespace wsl::windows::common::wslutil;\n                    THROW_HR_WITH_USER_ERROR(\n                        WSL_E_IMPORT_FAILED, wsl::shared::Localization::MessageRequiresFileExtensions(c_vhdFileExtension, c_vhdxFileExtension));\n                }\n\n                vhdName = vhdPath.filename();\n            }\n\n            registration = DistributionRegistration::Create(\n                lxssKey.get(),\n                DistributionId,\n                DistributionName,\n                FilesystemVersion,\n                distributionPath.c_str(),\n                flags,\n                LX_UID_ROOT,\n                PackageFamilyName,\n                vhdName.c_str(),\n                WI_IsFlagClear(Flags, LXSS_IMPORT_DISTRO_FLAGS_NO_OOBE));\n\n            configuration = s_GetDistributionConfiguration(registration, DistributionName == nullptr);\n\n            // Add the distribution to the list of converting distributions.\n            _ConversionBegin(configuration.DistroId, LxssDistributionStateInstalling);\n        }\n\n        // Set up a scope exit member to remove the distribution from the converting list.\n        auto installComplete = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ConversionComplete(configuration.DistroId); });\n\n        // Declare a scope exit variable to clean up on failure.\n        ULONG deleteFlags = 0;\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n            {\n                auto runAsUser = wil::impersonate_token(userToken.get());\n                _DeleteDistribution(configuration, deleteFlags);\n            }\n\n            registration.Delete(lxssKey.get());\n        });\n\n        // Initialize the filesystem.\n        wil::unique_handle clientProcess = wsl::windows::common::wslutil::OpenCallingProcess(GENERIC_READ | SYNCHRONIZE);\n        if (Version == LXSS_WSL_VERSION_2)\n        {\n            if (WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_VHD))\n            {\n                auto runAsUser = wil::impersonate_token(userToken.get());\n                auto vhdFile = wsl::core::filesystem::CreateFile(\n                    configuration.VhdFilePath.c_str(),\n                    GENERIC_WRITE,\n                    (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),\n                    CREATE_NEW,\n                    FILE_ATTRIBUTE_NORMAL,\n                    GetUserSid());\n\n                deleteFlags = LXSS_DELETE_DISTRO_FLAGS_VHD;\n                wsl::windows::common::relay::InterruptableRelay(FileHandle, vhdFile.get(), clientProcess.get(), LXSS_RELAY_BUFFER_SIZE);\n            }\n            else\n            {\n                // Create a vhd to store the root filesystem.\n                {\n                    auto runAsUser = wil::impersonate_token(userToken.get());\n                    if (VhdSize == 0)\n                    {\n                        VhdSize = config.VhdSizeBytes;\n                    }\n\n                    wsl::core::filesystem::CreateVhd(\n                        configuration.VhdFilePath.c_str(), VhdSize, GetUserSid(), config.EnableSparseVhd, WI_IsFlagSet(Flags, LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD));\n\n                    deleteFlags = LXSS_DELETE_DISTRO_FLAGS_VHD;\n                }\n\n                // Create a process in the utility VM to expand the tar file from a socket.\n                auto vmContext = _RunUtilityVmSetup(configuration, LxMiniInitMessageImport);\n\n                std::optional<wsl::windows::common::relay::ScopedRelay> errorRelay;\n                if (ErrorHandle != nullptr)\n                {\n                    errorRelay.emplace(std::move(vmContext.errorSocket), ErrorHandle);\n                }\n\n                // Relay the filesystem file contents to the tar.gz handle.\n                // Note: This is done in a separate thread because we can sometimes get stuck while writing the socket if tar exited without reading anything.\n                // Note: because the tarsSocket is moved, the relay owns it, meaning it will automatically close it when the relaying thread exits.\n                wsl::windows::common::relay::ScopedRelay dataRelay(FileHandle, std::move(vmContext.tarSocket));\n\n                // Wait for the utility VM to finish expanding the tar and ensure that\n                // the operation was successful.\n                auto* channel = dynamic_cast<WslCoreInstance::WslCorePort*>(vmContext.instance->GetInitPort().get());\n\n                gsl::span<gsl::byte> span;\n                const auto& message = channel->GetChannel().ReceiveMessage<LX_MINI_INIT_IMPORT_RESULT>(&span);\n\n                // Flush any pending IO on the error relay before exiting.\n                if (errorRelay.has_value())\n                {\n                    errorRelay->Sync();\n                }\n\n                // Process the import result message.\n                THROW_HR_IF(WSL_E_IMPORT_FAILED, (message.Result != 0));\n\n                _ProcessImportResultMessage(message, span, lxssKey.get(), configuration, registration);\n            }\n        }\n        else\n        {\n            // Create the directory to store the root filesystem.\n            const auto rootFsPath = configuration.BasePath / LXSS_ROOTFS_DIRECTORY;\n            wsl::windows::common::filesystem::CreateRootFs(rootFsPath.c_str(), configuration.Version);\n            deleteFlags = LXSS_DELETE_DISTRO_FLAGS_ROOTFS;\n\n            // Use bsdtar to extract the tar.gz file.\n            auto mounts = _CreateSetupMounts(configuration);\n            {\n                auto elfContext = _RunElfBinary(\n                    LXSS_BSDTAR_PATH \" -C \" LXSS_ROOTFS_MOUNT LXSS_BSDTAR_EXTRACT_ARGS,\n                    configuration.BasePath.c_str(),\n                    clientProcess.get(),\n                    FileHandle,\n                    nullptr,\n                    ErrorHandle,\n                    mounts.data(),\n                    static_cast<ULONG>(mounts.size()));\n\n                auto exitStatus = _GetElfExitStatus(elfContext);\n                THROW_HR_IF(WSL_E_IMPORT_FAILED, exitStatus != 0);\n            }\n\n            // Invoke the init binary with the option to export the distribution information via stdout.\n            {\n                std::pair<wil::unique_handle, wil::unique_handle> input;\n                THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&input.first, &input.second, nullptr, 0));\n\n                std::pair<wil::unique_handle, wil::unique_handle> output;\n                THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&output.first, &output.second, nullptr, 0));\n\n                auto elfContext = _RunElfBinary(\n                    LXSS_TOOLS_MOUNT \"/init \" LX_INIT_IMPORT_MESSAGE_ARG \" \" LXSS_ROOTFS_MOUNT,\n                    configuration.BasePath.c_str(),\n                    clientProcess.get(),\n                    input.first.get(),\n                    output.second.get(),\n                    ErrorHandle,\n                    mounts.data(),\n                    static_cast<ULONG>(mounts.size()));\n\n                // Close handles that were marshalled to WSL1.\n                input.first.reset();\n                output.second.reset();\n\n                // Read the import result message from stdout.\n                wil::unique_handle clientProcess = wsl::windows::common::wslutil::OpenCallingProcess(GENERIC_READ | SYNCHRONIZE);\n                MESSAGE_HEADER header{};\n                const auto headerSpan = gslhelpers::struct_as_writeable_bytes(header);\n                auto bytesRead = wsl::windows::common::relay::InterruptableRead(\n                    output.first.get(), gslhelpers::struct_as_writeable_bytes(header), {clientProcess.get()});\n\n                THROW_HR_IF(WSL_E_IMPORT_FAILED, bytesRead != headerSpan.size() || header.MessageSize <= headerSpan.size() || header.MessageType != LxMiniInitMessageImportResult);\n\n                std::vector<gsl::byte> buffer(header.MessageSize);\n                const auto span = gsl::make_span(buffer);\n                gsl::copy(headerSpan, span);\n\n                auto offset = headerSpan.size();\n                while (offset < span.size())\n                {\n                    bytesRead =\n                        wsl::windows::common::relay::InterruptableRead(output.first.get(), span.subspan(offset), {clientProcess.get()});\n                    if (bytesRead <= 0)\n                    {\n                        break;\n                    }\n\n                    offset += bytesRead;\n                }\n\n                THROW_HR_IF(WSL_E_IMPORT_FAILED, offset != buffer.size());\n\n                // Close the stdin write handle to let init exit and process the import result message.\n                input.second.reset();\n                auto exitStatus = _GetElfExitStatus(elfContext);\n                THROW_HR_IF(WSL_E_IMPORT_FAILED, exitStatus != 0);\n\n                const auto message = gslhelpers::try_get_struct<LX_MINI_INIT_IMPORT_RESULT>(span);\n                THROW_HR_IF(WSL_E_IMPORT_FAILED, !message);\n\n                _ProcessImportResultMessage(*message, span, lxssKey.get(), configuration, registration);\n            }\n        }\n\n        // Mark the distribution as installed and delete the scope exit variable\n        // so the registration is persisted.\n        {\n            std::lock_guard lock(m_instanceLock);\n            _SetDistributionInstalled(lxssKey.get(), registration.Id());\n            cleanup.release();\n        }\n\n        _SendDistributionRegisteredEvent(configuration);\n\n        _LaunchOOBEIfNeeded();\n\n        *pDistroGuid = registration.Id();\n        if (InstalledDistributionName != nullptr)\n        {\n            *InstalledDistributionName = wil::make_cotaskmem_string(configuration.Name.c_str()).release();\n        }\n\n        result = S_OK;\n    }\n    catch (...)\n    {\n        result = wil::ResultFromCaughtException();\n    }\n\n    return result;\n}\n\nHRESULT LxssUserSessionImpl::SetDefaultDistribution(_In_ LPCGUID DistroGuid)\ntry\n{\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n\n    // Ensure the distribution is in the installed state.\n    std::lock_guard lock(m_instanceLock);\n\n    const auto registration = DistributionRegistration::Open(lxssKey.get(), *DistroGuid);\n    const DWORD state = registration.Read(Property::State);\n\n    RETURN_HR_IF(WSL_E_DISTRO_NOT_FOUND, (state != LxssDistributionStateInstalled));\n\n    // Set the distribution to the default.\n    DistributionRegistration::SetDefault(lxssKey.get(), registration);\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT LxssUserSessionImpl::SetSparse(_In_ LPCGUID DistroGuid, _In_ BOOLEAN Sparse, _In_ BOOLEAN AllowUnsafe)\ntry\n{\n    auto runAsUser = wil::CoImpersonateClient();\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    std::lock_guard lock(m_instanceLock);\n\n    const auto registration = DistributionRegistration::Open(lxssKey.get(), *DistroGuid);\n    LXSS_DISTRO_CONFIGURATION configuration = s_GetDistributionConfiguration(registration);\n\n    // Don't attempt on V1\n    if (WI_IsFlagClear(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE))\n    {\n        THROW_HR_WITH_USER_ERROR(WSL_E_VM_MODE_INVALID_STATE, wsl::shared::Localization::MessageSparseVhdWsl2Only());\n    }\n\n    // Allow disabling sparse mode but not enabling until the data corruption issue has been resolved.\n    if (Sparse && !AllowUnsafe)\n    {\n        THROW_HR_WITH_USER_ERROR(E_INVALIDARG, wsl::shared::Localization::MessageSparseVhdDisabled());\n    }\n\n    // Don't attempt if running\n    RETURN_HR_IF(WSL_E_DISTRO_NOT_STOPPED, m_runningInstances.contains(*DistroGuid));\n\n    const wil::unique_hfile vhd{::CreateFileW(configuration.VhdFilePath.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr)};\n    if (!vhd)\n    {\n        const DWORD err = GetLastError();\n        if (err == ERROR_SHARING_VIOLATION)\n        {\n            THROW_HR_WITH_USER_ERROR(HRESULT_FROM_WIN32(err), wsl::shared::Localization::MessageVhdInUse());\n        }\n        THROW_WIN32(err);\n    }\n\n    FILE_SET_SPARSE_BUFFER buffer{\n        .SetSparse = Sparse,\n    };\n    THROW_IF_WIN32_BOOL_FALSE(::DeviceIoControl(vhd.get(), FSCTL_SET_SPARSE, &buffer, sizeof(buffer), nullptr, 0, nullptr, nullptr));\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT LxssUserSessionImpl::ResizeDistribution(_In_ LPCGUID DistroGuid, _In_ HANDLE OutputHandle, _In_ ULONG64 NewSize)\ntry\n{\n    std::lock_guard lock(m_instanceLock);\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    const auto registration = DistributionRegistration::Open(lxssKey.get(), *DistroGuid);\n    LXSS_DISTRO_CONFIGURATION configuration = s_GetDistributionConfiguration(registration);\n    RETURN_HR_IF(WSL_E_WSL2_NEEDED, WI_IsFlagClear(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE));\n\n    const auto vhdFilePath = configuration.VhdFilePath;\n    if (m_utilityVm && m_utilityVm->IsVhdAttached(vhdFilePath.c_str()))\n    {\n        THROW_HR_WITH_USER_ERROR(WSL_E_DISTRO_NOT_STOPPED, wsl::shared::Localization::MessageVhdInUse());\n    }\n\n    auto diskHandle = wsl::core::filesystem::OpenVhd(vhdFilePath.c_str(), VIRTUAL_DISK_ACCESS_GET_INFO | VIRTUAL_DISK_ACCESS_METAOPS);\n    const auto diskSize = wsl::core::filesystem::GetDiskSize(diskHandle.get());\n\n    const auto resizingLarger = NewSize > diskSize;\n    if (resizingLarger)\n    {\n        wsl::core::filesystem::ResizeExistingVhd(diskHandle.get(), NewSize, RESIZE_VIRTUAL_DISK_FLAG_NONE);\n    }\n\n    diskHandle.reset();\n\n    // Ensure VM exists and attach the VHD.\n    _CreateVm();\n    const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n    const auto lun = m_utilityVm->AttachDisk(vhdFilePath.c_str(), WslCoreVm::DiskType::VHD, {}, true, userToken.get());\n\n    // Resize the underlying filesystem.\n    //\n    // N.B. Passing zero as the size causes the resize to consume all available space on the block device.\n    {\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { m_utilityVm->EjectVhd(vhdFilePath.c_str()); });\n        m_utilityVm->ResizeDistribution(lun, OutputHandle, resizingLarger ? 0 : NewSize);\n    }\n\n    // If shrinking the VHD, resize the underlying VHD file. This is only supported for .vhdx files.\n    //\n    // N.B. RESIZE_VIRTUAL_DISK_FLAG_ALLOW_UNSAFE_VIRTUAL_SIZE is required because vhdmp can't validate that the minimum safe ext4 size.\n    if (!resizingLarger &&\n        wsl::shared::string::IsEqual(vhdFilePath.extension().c_str(), wsl::windows::common::wslutil::c_vhdxFileExtension, true))\n    {\n        const auto diskHandle =\n            wsl::core::filesystem::OpenVhd(vhdFilePath.c_str(), VIRTUAL_DISK_ACCESS_GET_INFO | VIRTUAL_DISK_ACCESS_METAOPS);\n        wsl::core::filesystem::ResizeExistingVhd(diskHandle.get(), NewSize, RESIZE_VIRTUAL_DISK_FLAG_ALLOW_UNSAFE_VIRTUAL_SIZE);\n    }\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT LxssUserSessionImpl::SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Version, _In_ HANDLE StderrHandle)\n{\n    RETURN_HR_IF(E_INVALIDARG, ((Version != LXSS_WSL_VERSION_1) && (Version != LXSS_WSL_VERSION_2)));\n\n    DistributionRegistration registration;\n    LXSS_DISTRO_CONFIGURATION configuration;\n    wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    try\n    {\n        // Ensure the distribution exists.\n        std::lock_guard lock(m_instanceLock);\n        registration = DistributionRegistration::Open(lxssKey.get(), *DistroGuid);\n        configuration = s_GetDistributionConfiguration(registration);\n\n        // The distro must be in the installed state.\n        RETURN_HR_IF(E_ILLEGAL_STATE_CHANGE, (configuration.State != LxssDistributionStateInstalled));\n\n        // Ensure distro is not already in the requested state.\n        if (Version == LXSS_WSL_VERSION_1)\n        {\n            RETURN_HR_IF(WSL_E_VM_MODE_INVALID_STATE, WI_IsFlagClear(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE));\n        }\n        else\n        {\n            // The legacy distribution does not support VM mode.\n            RETURN_HR_IF(WSL_E_VM_MODE_NOT_SUPPORTED, (configuration.Version == LXSS_DISTRO_VERSION_LEGACY));\n\n            RETURN_HR_IF(WSL_E_VM_MODE_INVALID_STATE, WI_IsFlagSet(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE));\n        }\n\n        // Conversion is not possible if the lxcore driver is not present.\n        RETURN_HR_IF(WSL_E_WSL1_NOT_SUPPORTED, !g_lxcoreInitialized);\n\n        // Add the distribution to the list of converting distributions.\n        _ConversionBegin(configuration.DistroId, LxssDistributionStateConverting);\n\n        // Remove the distribution ID from m_updatedInitDistros so init is updated on the next launch (in the case of a conversion to WSL1).\n        m_updatedInitDistros.erase(\n            std::remove(m_updatedInitDistros.begin(), m_updatedInitDistros.end(), configuration.DistroId), m_updatedInitDistros.end());\n    }\n    CATCH_RETURN()\n\n    // Set up a scope exit member to remove the distribution from the converting list.\n    auto conversionComplete = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ConversionComplete(configuration.DistroId); });\n\n    // Log telemetry to track how long enabling VM mode takes.\n    WSL_LOG_TELEMETRY(\n        \"SetVersionBegin\",\n        PDT_ProductAndServicePerformance,\n        TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n        TraceLoggingValue(Version, \"version\"));\n\n    HRESULT result;\n    auto setVersionComplete = wil::scope_exit([&] {\n        WSL_LOG_TELEMETRY(\n            \"SetVersionEnd\",\n            PDT_ProductAndServicePerformance,\n            TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n            TraceLoggingValue(Version, \"version\"),\n            TraceLoggingValue(result, \"result\"));\n    });\n\n    try\n    {\n        ULONG deleteFlags = 0;\n        wil::unique_handle userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n            auto runAsUser = wil::impersonate_token(userToken.get());\n            _DeleteDistribution(configuration, deleteFlags);\n        });\n\n        bool wroteLf = false;\n        size_t lastIndex = -1;\n        auto onTarOutput = [&StderrHandle, &wroteLf, &lastIndex](size_t Index, const gsl::span<gsl::byte>& Content) {\n            WI_ASSERT(Index == 0 || Index == 1);\n            auto it = Content.begin();\n\n            while (it != Content.end())\n            {\n                if (wroteLf || lastIndex != Index)\n                {\n                    if (*it == static_cast<std::byte>('\\n') && lastIndex != Index)\n                    {\n                        it++;\n                        continue;\n                    }\n\n                    // Add an extra newline if the input index changed to avoid mixing lines.\n                    if (lastIndex != Index && !wroteLf)\n                    {\n                        THROW_IF_WIN32_BOOL_FALSE(WriteFile(StderrHandle, \"\\n\", 1, nullptr, nullptr));\n                    }\n\n                    THROW_IF_WIN32_BOOL_FALSE(WriteFile(StderrHandle, Index == 0 ? \"wsl1: \" : \"wsl2: \", 6, nullptr, nullptr));\n                    wroteLf = false;\n                    lastIndex = Index;\n                }\n\n                auto lf = std::find(it, Content.end(), static_cast<std::byte>('\\n'));\n                if (lf != Content.end())\n                {\n                    lf++;\n                    wroteLf = true;\n                }\n\n                THROW_IF_WIN32_BOOL_FALSE(WriteFile(StderrHandle, &*it, static_cast<DWORD>(lf - it), nullptr, nullptr));\n\n                it = lf;\n            }\n        };\n\n        wil::unique_handle clientProcess = wsl::windows::common::wslutil::OpenCallingProcess(GENERIC_READ | SYNCHRONIZE);\n        std::string commandLine{LXSS_BSDTAR_PATH};\n        ULONG newFlags = configuration.Flags;\n        if (Version == LXSS_WSL_VERSION_1)\n        {\n            auto policiesKey = wsl::windows::policies::OpenPoliciesKey();\n            if (!wsl::windows::policies::IsFeatureAllowed(policiesKey.get(), wsl::windows::policies::c_allowWSL1))\n            {\n                THROW_HR_WITH_USER_ERROR(WSL_E_WSL1_DISABLED, wsl::shared::Localization::MessageWSL1Disabled());\n            }\n\n            auto rootfsPath = configuration.BasePath / LXSS_ROOTFS_DIRECTORY;\n\n            // Ensure the target directory is empty and create the root filesystem.\n            {\n                std::lock_guard lock(m_instanceLock);\n                {\n                    auto runAsUser = wil::impersonate_token(userToken.get());\n                    _DeleteDistributionLockHeld(configuration, LXSS_DELETE_DISTRO_FLAGS_ROOTFS);\n                }\n\n                wsl::windows::common::filesystem::CreateRootFs(rootfsPath.c_str(), configuration.Version);\n                deleteFlags = LXSS_DELETE_DISTRO_FLAGS_ROOTFS;\n            }\n\n            // Create a utility VM to create the tar file and output it via a\n            // socket.\n            auto vmContext = _RunUtilityVmSetup(configuration, LxMiniInitMessageExport, 0, true);\n\n            auto wsl1Pipe = wsl::windows::common::wslutil::OpenAnonymousPipe(LX_RELAY_BUFFER_SIZE, true, true);\n\n            wsl::windows::common::relay::ScopedMultiRelay stdErrRelay(\n                std::vector<HANDLE>{wsl1Pipe.first.get(), reinterpret_cast<HANDLE>(vmContext.errorSocket.get())}, onTarOutput);\n\n            // Add mounts for the rootfs and tools.\n            auto mounts = _CreateSetupMounts(configuration);\n\n            if (m_utilityVm->GetConfig().SetVersionDebug)\n            {\n                commandLine += \" -vv --totals\";\n            }\n\n            // Run the bsdtar elf binary expand the tar file using the socket as stdin.\n            commandLine += \" -C \" LXSS_ROOTFS_MOUNT LXSS_BSDTAR_EXTRACT_ARGS;\n            auto elfContext = _RunElfBinary(\n                commandLine.c_str(),\n                configuration.BasePath.c_str(),\n                clientProcess.get(),\n                reinterpret_cast<HANDLE>(vmContext.tarSocket.get()),\n                nullptr,\n                wsl1Pipe.second.get(),\n                mounts.data(),\n                static_cast<ULONG>(mounts.size()));\n\n            wsl1Pipe.second.reset();\n\n            // Wait for the utility VM to finish creating the tar and ensure that\n            // the export was successful.\n            LONG exitStatus = 1;\n            vmContext.instance->GetInitPort()->Receive(&exitStatus, sizeof(exitStatus), clientProcess.get());\n            THROW_HR_IF(WSL_E_EXPORT_FAILED, (exitStatus != 0));\n\n            // Wait for the elf binary to finish expanding the tar and ensure\n            // that it was successful.\n            exitStatus = _GetElfExitStatus(elfContext);\n            THROW_HR_IF(WSL_E_IMPORT_FAILED, exitStatus != 0);\n\n            // Import from the vhd was successful.\n            deleteFlags = LXSS_DELETE_DISTRO_FLAGS_VHD | LXSS_DELETE_DISTRO_FLAGS_WSLG_SHORTCUTS;\n            WI_ClearFlag(newFlags, LXSS_DISTRO_FLAGS_VM_MODE);\n        }\n        else\n        {\n            {\n                std::lock_guard lock(m_instanceLock);\n                _CreateVm();\n            }\n\n            // Create a vhd to store the root filesystem.\n            {\n                auto runAsUser = wil::impersonate_token(userToken.get());\n                wsl::core::filesystem::CreateVhd(\n                    configuration.VhdFilePath.c_str(),\n                    m_utilityVm->GetConfig().VhdSizeBytes,\n                    GetUserSid(),\n                    m_utilityVm->GetConfig().EnableSparseVhd,\n                    false);\n\n                deleteFlags = LXSS_DELETE_DISTRO_FLAGS_VHD;\n            }\n\n            // Create a process in the utility VM to expand the tar file from a socket.\n            auto vmContext = _RunUtilityVmSetup(configuration, LxMiniInitMessageImport, 0, true);\n\n            auto wsl1Pipe = wsl::windows::common::wslutil::OpenAnonymousPipe(LX_RELAY_BUFFER_SIZE, true, true);\n\n            wsl::windows::common::relay::ScopedMultiRelay stdErrRelay(\n                std::vector<HANDLE>{wsl1Pipe.first.get(), reinterpret_cast<HANDLE>(vmContext.errorSocket.get())}, onTarOutput);\n\n            // Add mounts for the rootfs and tools.\n            auto mounts = _CreateSetupMounts(configuration);\n\n            if (m_utilityVm->GetConfig().SetVersionDebug)\n            {\n                commandLine += \" -vv --totals\";\n            }\n\n            // Run the bsdtar elf binary to create the tar file using the socket as stdout.\n            commandLine += \" -C \" LXSS_ROOTFS_MOUNT LXSS_BSDTAR_CREATE_ARGS;\n            auto elfContext = _RunElfBinary(\n                commandLine.c_str(),\n                configuration.BasePath.c_str(),\n                clientProcess.get(),\n                nullptr,\n                reinterpret_cast<HANDLE>(vmContext.tarSocket.get()),\n                wsl1Pipe.second.get(),\n                mounts.data(),\n                static_cast<ULONG>(mounts.size()));\n\n            wsl1Pipe.second.reset();\n\n            LONG exitStatus = _GetElfExitStatus(elfContext);\n            THROW_HR_IF(WSL_E_IMPORT_FAILED, exitStatus != 0);\n\n            // Close the socket now that all data has been written.\n            vmContext.tarSocket.reset();\n\n            // Wait for the utility VM to finish expanding the tar and ensure that\n            // the export was successful.\n            auto* channel = dynamic_cast<WslCoreInstance::WslCorePort*>(vmContext.instance->GetInitPort().get());\n\n            gsl::span<gsl::byte> span;\n            const auto& message = channel->GetChannel().ReceiveMessage<LX_MINI_INIT_IMPORT_RESULT>(&span);\n            THROW_HR_IF(E_FAIL, (message.Result != 0));\n\n            if (message.FlavorIndex > 0)\n            {\n                configuration.Flavor = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(span, message.FlavorIndex));\n                registration.Write(Property::Flavor, configuration.Flavor.c_str());\n            }\n\n            if (message.VersionIndex > 0)\n            {\n                configuration.OsVersion = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(span, message.VersionIndex));\n                registration.Write(Property::OsVersion, configuration.OsVersion.c_str());\n            }\n\n            // Operation was successful.\n            deleteFlags = LXSS_DELETE_DISTRO_FLAGS_ROOTFS;\n            WI_SetFlag(newFlags, LXSS_DISTRO_FLAGS_VM_MODE);\n        }\n\n        // Record the new distribution state.\n        registration.Write(Property::Flags, newFlags);\n\n        result = S_OK;\n    }\n    catch (...)\n    {\n        result = wil::ResultFromCaughtException();\n    }\n\n    return result;\n}\n\nHRESULT LxssUserSessionImpl::Shutdown(_In_ bool PreventNewInstances, ShutdownBehavior Behavior)\n{\n    try\n    {\n        auto forceTerminate = [this]() {\n            auto vmId = m_vmId.load();\n            if (!IsEqualGUID(vmId, GUID_NULL))\n            {\n                auto vmIdStr = wsl::shared::string::GuidToString<wchar_t>(vmId, wsl::shared::string::GuidToStringFlags::Uppercase);\n\n                auto result = wil::ResultFromException([&]() {\n                    auto computeSystem = wsl::windows::common::hcs::OpenComputeSystem(vmIdStr.c_str(), GENERIC_ALL);\n                    wsl::windows::common::hcs::TerminateComputeSystem(computeSystem.get());\n                });\n\n                WSL_LOG(\"ForceTerminateVm\", TraceLoggingValue(result, \"Result\"));\n            }\n        };\n\n        // If the user asks for a forced termination, kill the VM\n        if (Behavior == ShutdownBehavior::Force)\n        {\n            forceTerminate();\n        }\n\n        {\n            bool locked = false;\n            auto unlock = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [this, &locked]() {\n                if (locked)\n                {\n                    m_instanceLock.unlock();\n                }\n            });\n\n            if (Behavior == ShutdownBehavior::ForceAfter30Seconds)\n            {\n                if (m_instanceLock.try_lock_for(std::chrono::seconds(30)))\n                {\n                    locked = true;\n                }\n                else\n                {\n                    WSL_LOG(\"VmShutdownLockTimedOut\");\n                    forceTerminate();\n                }\n            }\n\n            if (!locked)\n            {\n                m_instanceLock.lock();\n                locked = true;\n            }\n\n            // Stop each instance with the lock held.\n            while (!m_runningInstances.empty())\n            {\n                _TerminateInstanceInternal(&m_runningInstances.begin()->first, false);\n            }\n\n            // Terminate the utility VM.\n            _VmTerminate();\n\n            // Reset the proxy state.\n            // We don't clear it in _VMTerminate because we want to cache results if possible.\n            m_httpProxyStateTracker.reset();\n\n            // Clear any attached disk state.\n            // This is needed because wsl --shutdown might be called after the vm\n            // has timed out (and so the disks states would have been written in the registry)\n            const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(&m_userSid.Sid);\n            wsl::windows::common::registry::ClearSubkeys(key.get());\n\n            WI_ASSERT(!PreventNewInstances || !m_disableNewInstanceCreation);\n\n            // This is used when the session is being deleted.\n            // This is in place to prevent a CreateInstance() call from succeeding\n            // after the session is shut down since this would mean that the destructor,\n            // which could run on that thread (if the session already dropped its LxssUserSessionImpl reference)\n            // would have to do all the cleanup work.\n            m_disableNewInstanceCreation = PreventNewInstances;\n        }\n\n        auto lock = m_terminatedInstanceLock.lock_exclusive();\n        m_terminatedInstances.clear();\n    }\n    CATCH_LOG()\n\n    return S_OK;\n}\n\nvoid LxssUserSessionImpl::TelemetryWorker(_In_ wil::unique_socket&& socket) const\ntry\n{\n    wsl::windows::common::wslutil::SetThreadDescription(L\"Telemetry\");\n\n    wsl::shared::SocketChannel channel(std::move(socket), \"Telemetry\", m_vmTerminating.get());\n\n    // Check if drvfs notifications are enabled for the user.\n    bool drvFsNotifications{};\n    {\n        auto impersonate = wil::impersonate_token(m_userToken.get());\n        const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\n        drvFsNotifications =\n            wsl::windows::common::registry::ReadDword(lxssKey.get(), LXSS_NOTIFICATIONS_KEY, LXSS_NOTIFICATION_DRVFS_PERF_DISABLED, 0) == 0;\n    }\n\n    // Aggregate information about what is running inside the VM. This is logged\n    // periodically because logging each event individually would be too noisy.\n    for (;;)\n    {\n        auto [Message, Span] = channel.ReceiveMessageOrClosed<LX_MINI_INIT_TELEMETRY_MESSAGE>();\n        if (Message == nullptr)\n        {\n            break;\n        }\n\n        std::map<std::string, size_t> events{};\n\n        std::string content = wsl::shared::string::FromSpan(Span, offsetof(LX_MINI_INIT_TELEMETRY_MESSAGE, Buffer));\n        auto values = wsl::shared::string::Split<char>(content, '/');\n\n        THROW_HR_IF(E_UNEXPECTED, values.size() % 2 != 0);\n\n        // Periodically log an event to track active WSL usage. This event must be marked as\n        // 'MICROSOFT_KEYWORD_CRITICAL_DATA' and not MICROSOFT_KEYWORD_MEASURES.\n        //\n        // N.B. The count and imageName values are unused but required because they were present in the approved critical event.\n        WSL_LOG(\n            \"ExecCritical\",\n            TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),\n            TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA),\n            TraceLoggingValue(0, \"count\"),\n            TraceLoggingValue(\"\", \"imageName\"),\n            TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n        for (size_t i = 0; i < values.size(); i += 2)\n        {\n            // Log an aggregated account of the binary names run in WSL and their counts, used to determine popular use cases and prioritize support for issues\n            WSL_LOG_TELEMETRY(\n                \"Exec\",\n                PDT_ProductAndServiceUsage,\n                TraceLoggingValue(std::stoull(values[i + 1]), \"count\"),\n                TraceLoggingValue(values[i].c_str(), \"imageName\"),\n                TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n        }\n\n        if (drvFsNotifications && Message->ShowDrvFsNotification && !values.empty())\n        {\n            // If a drvfs notification is requested, the first entry is the executable that triggered it.\n            LOG_IF_FAILED(wsl::windows::common::notifications::DisplayFilesystemNotification(values[0].c_str()));\n            drvFsNotifications = false;\n        }\n    }\n}\nCATCH_LOG()\n\n_Requires_lock_not_held_(m_instanceLock)\nvoid LxssUserSessionImpl::TerminateByClientId(_In_ ULONG ClientId)\n{\n    if (ClientId == LXSS_CLIENT_ID_INVALID)\n    {\n        return;\n    }\n\n    std::lock_guard lock(m_instanceLock);\n    TerminateByClientIdLockHeld(ClientId);\n}\n\n_Requires_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::TerminateByClientIdLockHeld(_In_ ULONG ClientId)\n{\n    // Terminate any instances with a matching client ID.\n    std::vector<GUID> instances;\n    std::for_each(m_runningInstances.begin(), m_runningInstances.end(), [&](auto& pair) {\n        auto id = pair.second->GetClientId();\n        if ((id == ClientId) || ((ClientId == LXSS_CLIENT_ID_WILDCARD) && (id != LXSS_CLIENT_ID_INVALID)))\n        {\n            instances.push_back(pair.first);\n        }\n    });\n\n    std::for_each(instances.begin(), instances.end(), [&](auto& guid) { _TerminateInstanceInternal(&guid, false); });\n\n    // If the wildcard client ID was specified, the utility VM unexpectedly exited.\n    if (ClientId == LXSS_CLIENT_ID_WILDCARD)\n    {\n        _VmTerminate();\n    }\n}\n\nHRESULT LxssUserSessionImpl::TerminateDistribution(_In_opt_ LPCGUID DistroGuid)\ntry\n{\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    GUID defaultDistro;\n    {\n        std::lock_guard lock(m_instanceLock);\n\n        // If no distribution GUID was supplied, use the default.\n        if (ARGUMENT_PRESENT(DistroGuid) == FALSE)\n        {\n            defaultDistro = _GetDefaultDistro(lxssKey.get());\n            DistroGuid = &defaultDistro;\n        }\n\n        _TerminateInstanceInternal(DistroGuid);\n    }\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT LxssUserSessionImpl::UnregisterDistribution(_In_ LPCGUID DistroGuid)\n{\n    ExecutionContext context(Context::UnregisterDistro);\n\n    // Set up a scope exit member to log unregistration status.\n    DistributionRegistration registration;\n    LXSS_DISTRO_CONFIGURATION configuration{};\n    HRESULT result = E_FAIL;\n    auto unregisterExit = wil::scope_exit([&] {\n        // Only log the end event if a distro was found.\n        if (configuration.Name.size() > 0)\n        {\n            WSL_LOG(\n                \"UnregisterDistributionEnd\",\n                TraceLoggingValue(configuration.Name.c_str(), \"name\"),\n                TraceLoggingHexUInt32(result, \"result\"));\n        }\n    });\n\n    try\n    {\n        wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n\n        // Set up a scope exit lambda to delete the distribution registry key\n        // when the function exits.\n        auto removedDistroString = wsl::shared::string::GuidToString<wchar_t>(*DistroGuid);\n        bool removeDistro = false;\n        auto deleteDistroKey = wil::scope_exit([&] {\n            if (removeDistro)\n            {\n                wsl::windows::common::registry::DeleteKey(lxssKey.get(), removedDistroString.c_str());\n            }\n        });\n\n        {\n            std::lock_guard lock(m_instanceLock);\n\n            // Get the configuration information about the distribution.\n            registration = DistributionRegistration::Open(lxssKey.get(), *DistroGuid);\n            configuration = s_GetDistributionConfiguration(registration);\n\n            // Log telemetry about the distribution being removed.\n            WSL_LOG_TELEMETRY(\n                \"UnregisterDistributionBegin\", PDT_ProductAndServiceUsage, TraceLoggingValue(configuration.Name.c_str(), \"name\"));\n\n            // Ensure that a filesystem export is not in progress.\n            _EnsureNotLocked(DistroGuid);\n\n            // After this point the distribution registry key should be deleted.\n            removeDistro = true;\n\n            // Terminate the distribution and mark it as uninstalling.\n            _TerminateInstanceInternal(DistroGuid);\n            registration.Write(Property::State, LxssDistributionStateUninstalling);\n\n            // If the default distribution has been unregistered, search for another\n            // distribution to set as the new default.\n\n            auto defaultDistribution = DistributionRegistration::OpenDefault(lxssKey.get());\n            if (defaultDistribution.has_value() && IsEqualGUID(defaultDistribution->Id(), registration.Id()))\n            {\n                // Remove the old default.\n                DistributionRegistration::DeleteDefault(lxssKey.get());\n\n                // If there are any other registered distributions, set the first\n                // one found to the new default.\n                auto distributions = _EnumerateDistributions(lxssKey.get());\n                if (distributions.size() > 0)\n                {\n                    DistributionRegistration::SetDefault(lxssKey.get(), distributions[0]);\n                }\n            }\n\n            {\n                auto runAsUser = wil::CoImpersonateClient();\n                _DeleteDistributionLockHeld(configuration);\n            }\n\n            WslOfflineDistributionInformation distributionInfo;\n            distributionInfo.Id = configuration.DistroId;\n            distributionInfo.Name = configuration.Name.c_str();\n            distributionInfo.PackageFamilyName = configuration.PackageFamilyName.c_str();\n            distributionInfo.Flavor = configuration.Flavor.empty() ? nullptr : configuration.Flavor.c_str();\n            distributionInfo.Version = configuration.OsVersion.empty() ? nullptr : configuration.OsVersion.c_str();\n\n            m_pluginManager.OnDistributionUnregistered(&m_session, &distributionInfo);\n        }\n\n        result = S_OK;\n    }\n    catch (...)\n    {\n        result = wil::ResultFromCaughtException();\n    }\n\n    return result;\n}\n\n_Requires_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_ConversionBegin(_In_ GUID DistroGuid, _In_ LxssDistributionState State)\n{\n    _EnsureNotLocked(&DistroGuid);\n    _TerminateInstanceInternal(&DistroGuid);\n    m_lockedDistributions.emplace_back(DistroGuid, State);\n}\n\n_Requires_lock_not_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_ConversionComplete(_In_ GUID DistroGuid)\n{\n    std::lock_guard lock(m_instanceLock);\n    std::erase_if(m_lockedDistributions, [&](const auto& pair) { return (IsEqualGUID(pair.first, DistroGuid)); });\n\n    _VmCheckIdle();\n}\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_CreateLegacyRegistration(_In_ HKEY LxssKey, _In_ HANDLE UserToken)\n{\n    // Delete any existing legacy registration.\n    const auto distroGuidString = wsl::shared::string::GuidToString<wchar_t>(LXSS_LEGACY_DISTRO_GUID);\n    wsl::windows::common::registry::DeleteKey(LxssKey, distroGuidString.c_str());\n\n    // Migrate legacy default user configuration.\n    const ULONG defaultUid = wsl::windows::common::registry::ReadDword(LxssKey, nullptr, WSL_DISTRO_CONFIG_DEFAULT_UID, LX_UID_ROOT);\n    DWORD configFlags = LXSS_DISTRO_FLAGS_DEFAULT;\n    DWORD enabled = wsl::windows::common::registry::ReadDword(LxssKey, nullptr, LXSS_LEGACY_APPEND_NT_PATH, 1);\n    WI_ClearFlagIf(configFlags, LXSS_DISTRO_FLAGS_APPEND_NT_PATH, (enabled == 0));\n    enabled = wsl::windows::common::registry::ReadDword(LxssKey, nullptr, LXSS_LEGACY_INTEROP_ENABLED, 1);\n    WI_ClearFlagIf(configFlags, LXSS_DISTRO_FLAGS_ENABLE_INTEROP, (enabled == 0));\n\n    // Create a new registration for the legacy distro.\n    const auto basePath = wsl::windows::common::filesystem::GetLegacyBasePath(UserToken);\n\n    DistributionRegistration::Create(\n        LxssKey, LXSS_LEGACY_DISTRO_GUID, LXSS_LEGACY_INSTALL_NAME, LXSS_DISTRO_VERSION_LEGACY, basePath.c_str(), configFlags, defaultUid, nullptr, LXSS_VM_MODE_VHD_NAME, false);\n\n    _SetDistributionInstalled(LxssKey, LXSS_LEGACY_DISTRO_GUID);\n}\n\nstd::vector<wsl::windows::common::filesystem::unique_lxss_addmount> LxssUserSessionImpl::_CreateSetupMounts(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration)\n{\n    // Add a rootfs mount.\n    auto runAsUser = wil::CoImpersonateClient();\n    const auto rootFsPath = Configuration.BasePath / LXSS_ROOTFS_DIRECTORY;\n    std::vector<wsl::windows::common::filesystem::unique_lxss_addmount> mounts;\n    mounts.emplace_back(wsl::windows::common::filesystem::CreateMount(\n        rootFsPath.c_str(), LXSS_ROOTFS_DIRECTORY, LXSS_ROOTFS_MOUNT, LXSS_DISTRO_USES_WSL_FS(Configuration.Version) ? LXSS_FS_TYPE_WSLFS : LXSS_FS_TYPE_LXFS, 0755));\n\n    // Add a read only sharefs mount to the inbox tools directory which contains the bsdtar binary.\n    std::wstring systemDirectory;\n    THROW_IF_FAILED(wil::GetSystemDirectoryW(systemDirectory));\n\n    // Add a read only sharefs mount to the packaged tools directory which contains the init binary.\n    const auto initPath = wsl::windows::common::wslutil::GetBasePath() / L\"tools\";\n    mounts.emplace_back(wsl::windows::common::filesystem::CreateMount(\n        initPath.c_str(), initPath.c_str(), LXSS_TOOLS_MOUNT, LXSS_FS_TYPE_SHAREFS, 0755, false));\n\n    return mounts;\n}\n\n_Requires_lock_not_held_(m_instanceLock)\nstd::shared_ptr<LxssRunningInstance> LxssUserSessionImpl::_CreateInstance(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags)\n{\n    ExecutionContext context(Context::CreateInstance);\n\n    // Validate flags.\n    THROW_HR_IF(E_INVALIDARG, (WI_IsAnyFlagSet(Flags, ~LXSS_CREATE_INSTANCE_FLAGS_ALL)));\n\n    // Clear the list of terminated instances before acquiring the instance\n    // list lock.\n    {\n        auto lock = m_terminatedInstanceLock.lock_exclusive();\n        m_terminatedInstances.clear();\n    }\n\n    wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n    DistributionRegistration registration;\n    wil::unique_handle userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n\n    std::shared_ptr<LxssRunningInstance> instance;\n    {\n        std::lock_guard lock(m_instanceLock);\n\n        // m_disableNewInstanceCreation is set when the session is being deleted.\n        // In that code path, don't create a new session.\n        THROW_HR_IF(RPC_E_DISCONNECTED, m_disableNewInstanceCreation);\n\n        registration = DistributionRegistration::OpenOrDefault(lxssKey.get(), DistroGuid);\n\n        // Check if an instance is already running for this distribution, if\n        // not create one.\n        instance = _RunningInstance(&registration.Id());\n        if (!instance)\n        {\n            THROW_HR_IF(E_NOT_SET, WI_IsFlagSet(Flags, LXSS_CREATE_INSTANCE_FLAGS_OPEN_EXISTING));\n\n            // Query information about the distribution.\n            auto configuration = s_GetDistributionConfiguration(registration);\n            auto defaultUid = registration.Read(Property::DefaultUid);\n\n            THROW_HR_IF(E_ILLEGAL_STATE_CHANGE, (configuration.State != LxssDistributionStateInstalled));\n\n            // Determine the distribution version.\n            ULONG version = WI_IsFlagSet(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE) ? LXSS_WSL_VERSION_2 : LXSS_WSL_VERSION_1;\n\n            // Create a GUID for the instance.\n            GUID instanceId;\n            THROW_IF_FAILED(CoCreateGuid(&instanceId));\n\n            // Log telemetry to determine how long instance creation takes.\n            WSL_LOG_TELEMETRY(\n                \"CreateInstanceBegin\",\n                PDT_ProductAndServicePerformance,\n                TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n                TraceLoggingValue(version, \"version\"),\n                TraceLoggingValue(instanceId, \"instanceId\"));\n\n            HRESULT result = E_UNEXPECTED;\n\n            auto createEnd = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n                WSL_LOG_TELEMETRY(\n                    \"CreateInstanceEnd\",\n                    PDT_ProductAndServicePerformance,\n                    TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n                    TraceLoggingValue(version, \"version\"),\n                    TraceLoggingValue(instanceId, \"instanceId\"),\n                    TraceLoggingValue(SUCCEEDED(result), \"success\"),\n                    TraceLoggingValue(result, \"error\"));\n            });\n\n            try\n            {\n                auto clientKey = m_lifetimeManager.GetRegistrationId();\n                if (version == LXSS_WSL_VERSION_1)\n                {\n                    auto key = wsl::windows::policies::OpenPoliciesKey();\n                    if (!wsl::windows::policies::IsFeatureAllowed(key.get(), wsl::windows::policies::c_allowWSL1))\n                    {\n                        THROW_HR_WITH_USER_ERROR(\n                            WSL_E_WSL1_DISABLED,\n                            wsl::shared::Localization::MessageWSL1Disabled() + L\"\\n\" +\n                                wsl::shared::Localization::MessageUpgradeToWSL2(configuration.Name));\n                    }\n\n                    instance = std::make_shared<LxssInstance>(\n                        instanceId,\n                        configuration,\n                        defaultUid,\n                        clientKey,\n                        std::bind(s_TerminateInstance, this, registration.Id(), false),\n                        std::bind(s_UpdateInit, this, configuration),\n                        Flags,\n                        _GetResultantConfig(userToken.get()).InstanceIdleTimeout);\n                }\n                else\n                {\n                    // Ensure the VM has been created.\n                    _CreateVm();\n                    instance = m_utilityVm->CreateInstance(\n                        instanceId, configuration, LxMiniInitMessageLaunchInit, m_utilityVm->GetConfig().KernelBootTimeout, defaultUid, clientKey);\n                }\n\n                // Log telemetry to determine how long initialization takes.\n                WSL_LOG(\n                    \"InitializeInstanceBegin\",\n                    TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n                    TraceLoggingValue(version, \"version\"),\n                    TraceLoggingValue(instanceId, \"instanceId\"));\n\n                auto initializeEnd = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n                    WSL_LOG(\n                        \"InitializeInstanceEnd\",\n                        TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA),\n                        TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n                        TraceLoggingValue(version, \"version\"),\n                        TraceLoggingValue(instanceId, \"instanceId\"));\n                });\n\n                // Initialize the instance and add it to the list of running instances.\n                instance->Initialize();\n\n                const auto* distributionInfo = instance->DistributionInformation();\n                if (distributionInfo->Flavor != nullptr && distributionInfo->Flavor != configuration.Flavor)\n                {\n                    WSL_LOG(\n                        \"DistributionFlavorChange\",\n                        TraceLoggingValue(distributionInfo->Flavor, \"NewFlavor\"),\n                        TraceLoggingValue(configuration.Flavor.c_str(), \"OldFlavor\"),\n                        TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n                        TraceLoggingValue(instanceId, \"instanceId\"));\n\n                    registration.Write(Property::Flavor, distributionInfo->Flavor);\n                }\n\n                if (distributionInfo->Version != nullptr && distributionInfo->Version != configuration.OsVersion)\n                {\n                    WSL_LOG(\n                        \"DistributionVersionChange\",\n                        TraceLoggingValue(distributionInfo->Version, \"NewVersion\"),\n                        TraceLoggingValue(configuration.OsVersion.c_str(), \"OldVersion\"),\n                        TraceLoggingValue(configuration.Name.c_str(), \"distroName\"),\n                        TraceLoggingValue(instanceId, \"instanceId\"));\n\n                    registration.Write(Property::OsVersion, distributionInfo->Version);\n                }\n\n                // This needs to be done before plugins are notifed because they might try to run a command inside the distribution.\n                m_runningInstances[registration.Id()] = instance;\n\n                if (version == LXSS_WSL_VERSION_2)\n                {\n                    auto cleanupOnFailure =\n                        wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { m_runningInstances.erase(registration.Id()); });\n                    m_pluginManager.OnDistributionStarted(&m_session, instance->DistributionInformation());\n                    cleanupOnFailure.release();\n                }\n\n                result = S_OK;\n            }\n            catch (...)\n            {\n                result = wil::ResultFromCaughtException();\n                throw;\n            }\n        }\n    }\n\n    // Register the Plan 9 Redirector connection targets for the calling user if necessary.\n    // N.B. Normally this is only necessary when creating the instance, and every subsequent time\n    //      it's skipped because the user is already registered. However, in rare cases the\n    //      instance is created by the same user but under a different context, with a different\n    //      authentication ID, than the user's interactive session. For example, if the instance\n    //      was created by a scheduled task. For this reason, ensure that the calling user is\n    //      registered even for already running instances.\n    instance->RegisterPlan9ConnectionTarget(userToken.get());\n\n    // Determine the idle timeout for the instance. A value of less than zero indicates that the instance\n    // should never be idle-terminated.\n    if (instance->GetIdleTimeout() >= 0)\n    {\n        // Register a client termination callback with the lifetime manager. If the\n        // ignore client callback flag is specified and there are no other clients,\n        // the timer is immediately queued.\n        wil::unique_handle currentProcess{};\n        if (WI_IsFlagClear(Flags, LXSS_CREATE_INSTANCE_FLAGS_IGNORE_CLIENT))\n        {\n            currentProcess = wsl::windows::common::wslutil::OpenCallingProcess(GENERIC_READ | SYNCHRONIZE);\n        }\n\n        m_lifetimeManager.RegisterCallback(\n            instance->GetLifetimeManagerId(),\n            std::bind(s_TerminateInstance, this, registration.Id(), true),\n            currentProcess.get(),\n            instance->GetIdleTimeout());\n    }\n\n    // If the system distro flag was specified, return the system distro for the instance.\n    //\n    // N.B. The system distro is only supported for WSL2.\n    if (WI_IsFlagSet(Flags, LXSS_CREATE_INSTANCE_FLAGS_USE_SYSTEM_DISTRO))\n    {\n        auto wslCoreInstance = std::dynamic_pointer_cast<WslCoreInstance>(instance);\n        THROW_HR_IF(WSL_E_WSL2_NEEDED, !wslCoreInstance);\n\n        instance = wslCoreInstance->GetSystemDistro();\n        THROW_HR_IF(WSL_E_GUI_APPLICATIONS_DISABLED, !instance);\n    }\n\n    return instance;\n}\n\n// N.B. This methods expects the caller to impersonate the user.\nvoid LxssUserSessionImpl::_CreateDistributionShortcut(_In_ LPCWSTR DistributionName, LPCWSTR ShortcutIcon, LPCWSTR ExecutablePath, DistributionRegistration& registration)\ntry\n{\n    const auto shellLink = wil::CoCreateInstance<IShellLink>(CLSID_ShellLink);\n\n    auto shortcutPath = wsl::windows::common::filesystem::GetKnownFolderPath(FOLDERID_StartMenu, KF_FLAG_CREATE);\n    shortcutPath /= DistributionName + std::wstring(L\".lnk\");\n\n    THROW_IF_FAILED(shellLink->SetPath(ExecutablePath));\n\n    // Construct the command line to set the working directory to the user's home directory.\n    const auto commandLine = std::format(\n        L\"{} {} {} {}\", WSL_DISTRIBUTION_ID_ARG, wsl::shared::string::GuidToString<wchar_t>(registration.Id()), WSL_CHANGE_DIRECTORY_ARG, WSL_CWD_HOME);\n\n    THROW_IF_FAILED(shellLink->SetArguments(commandLine.c_str()));\n    THROW_IF_FAILED(shellLink->SetIconLocation(ShortcutIcon, 0));\n\n    auto storage = shellLink.query<IPersistFile>();\n    THROW_IF_FAILED(storage->Save(shortcutPath.c_str(), true));\n\n    registration.Write(Property::ShortcutPath, shortcutPath.c_str());\n}\nCATCH_LOG();\n\n// N.B. This methods expects the caller to impersonate the user.\nvoid LxssUserSessionImpl::_CreateTerminalProfile(\n    _In_ const std::string_view& Template,\n    _In_ _In_ const std::filesystem::path& IconPath,\n    _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n    wsl::windows::service::DistributionRegistration& Registration)\ntry\n{\n    using namespace wsl::windows::common::string;\n    using namespace wsl::windows::common::wslutil;\n    using wsl::shared::string::WideToMultiByte;\n\n    nlohmann::basic_json json;\n    nlohmann::json::iterator profiles;\n\n    try\n    {\n        json = nlohmann::json::parse(Template, nullptr, true, false);\n        THROW_HR_IF(E_UNEXPECTED, !json.is_object());\n\n        profiles = json.find(\"profiles\");\n        THROW_HR_IF(E_UNEXPECTED, profiles == json.end() || !profiles->is_array());\n    }\n    catch (const nlohmann::json::parse_error& e)\n    {\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageFailedToParseTerminalProfile(e.what()));\n        return;\n    }\n    catch (...)\n    {\n        auto error = WideToMultiByte(wsl::windows::common::wslutil::ErrorCodeToString(wil::ResultFromCaughtException()));\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageFailedToParseTerminalProfile(error));\n        return;\n    }\n\n    auto distributionIdString = wsl::shared::string::GuidToString<wchar_t>(Registration.Id());\n    auto distributionProfileId =\n        wsl::shared::string::GuidToString<wchar_t>(CreateV5Uuid(WslTerminalNamespace, std::as_bytes(std::span{distributionIdString})));\n\n    auto hideGeneratedProfileGuid = WideToMultiByte(wsl::shared::string::GuidToString<wchar_t>(\n        CreateV5Uuid(GeneratedProfilesTerminalNamespace, std::as_bytes(std::span{Configuration.Name}))));\n\n    bool foundHideProfile = false;\n\n    for (auto& e : *profiles)\n    {\n        auto updates = e.find(\"updates\");\n        if (updates != e.end() && (*updates) == hideGeneratedProfileGuid)\n        {\n            foundHideProfile = true;\n            continue;\n        }\n\n        std::wstring systemDirectory;\n        THROW_IF_FAILED(wil::GetSystemDirectory(systemDirectory));\n\n        e[\"commandline\"] =\n            WideToMultiByte(std::format(L\"{}\\\\{} {} {}\", systemDirectory, WSL_BINARY_NAME, WSL_DISTRIBUTION_ID_ARG, distributionIdString));\n\n        e[\"name\"] = WideToMultiByte(Configuration.Name);\n        e[\"guid\"] = WideToMultiByte(distributionProfileId);\n        e[\"icon\"] = WideToMultiByte(IconPath.native());\n\n        // Set default starting directory to home directory if not already specified\n        // This allows Windows Terminal to override with startingDirectory setting\n        if (e.find(\"startingDirectory\") == e.end())\n        {\n            e[\"startingDirectory\"] = \"~\";\n        }\n\n        // See https://github.com/microsoft/terminal/pull/18195. Supported in terminal >= 1.23\n        e[\"pathTranslationStyle\"] = \"wsl\";\n\n        if (!Configuration.Flavor.empty())\n        {\n            e[\"wsl.distribution-type\"] = WideToMultiByte(Configuration.Flavor);\n        }\n\n        if (!Configuration.OsVersion.empty())\n        {\n            e[\"wsl.distribution-version\"] = WideToMultiByte(Configuration.OsVersion);\n        }\n    }\n\n    // Add an entry to hide the autogenerated terminal profile, if not provided by the distribution.\n    if (!foundHideProfile)\n    {\n        nlohmann::json hideProfile{{\"updates\", hideGeneratedProfileGuid}, {\"hidden\", true}};\n\n        profiles->insert(profiles->begin(), hideProfile);\n    }\n\n    auto targetFolder = wsl::windows::common::filesystem::GetLocalAppDataPath(nullptr) / L\"Microsoft\" / L\"Windows Terminal\" /\n                        L\"Fragments\" / L\"Microsoft.WSL\";\n\n    wil::CreateDirectoryDeep(targetFolder.c_str());\n\n    auto tempFilePath = wsl::windows::common::filesystem::GetTempFilename();\n    auto targetPath = targetFolder / (distributionProfileId + L\".json\");\n\n    // Unfortunately creating & writing the file isn't atomic.\n    // Creating the file somewhere else and then moving it to 'targetPath' isn't an option either, because MoveFile\n    // will set its ownership to the Administrators group, which breaks terminal.\n    wil::unique_handle file{CreateFile(targetPath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)};\n    THROW_LAST_ERROR_IF(!file);\n\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\n        file.reset();\n        DeleteFile(targetPath.c_str());\n    });\n\n    auto content = json.dump(2);\n    THROW_IF_WIN32_BOOL_FALSE(WriteFile(file.get(), content.c_str(), gsl::narrow_cast<DWORD>(content.size()), nullptr, nullptr));\n    cleanup.release();\n\n    Registration.Write(Property::TerminalProfilePath, targetPath.c_str());\n}\nCATCH_LOG();\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_CreateVm()\n{\n    ExecutionContext context(Context::CreateVm);\n\n    if (!m_utilityVm)\n    {\n\n        // Return an error if a plugin failed to initialize or needs a newer WSL version.\n        // Note: It's better to do this here instead of CreateInstanceForCurrentUser() because we\n        // can return a proper error message with the plugin name since we have an execution context here.\n        m_pluginManager.ThrowIfFatalPluginError();\n\n        const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n        auto config = _GetResultantConfig(userToken.get());\n\n        // Initialize policies for the plugin interface.\n        WSLVmCreationSettings userSettings{};\n        WI_SetFlagIf(userSettings.CustomConfigurationFlags, WSLUserConfigurationCustomKernel, !config.KernelPath.empty());\n        WI_SetFlagIf(userSettings.CustomConfigurationFlags, WSLUserConfigurationCustomKernelCommandLine, !config.KernelCommandLine.empty());\n\n        // Duplicate the passed-in user token and pass it down to plugins.\n        THROW_IF_WIN32_BOOL_FALSE(\n            ::DuplicateTokenEx(userToken.get(), MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenImpersonation, &m_userToken));\n\n        m_session.UserToken = m_userToken.get();\n\n        GUID vmId{};\n        THROW_IF_FAILED(CoCreateGuid(&vmId));\n\n        m_vmId.store(vmId);\n\n        // Create the utility VM and register for callbacks.\n        m_utilityVm = WslCoreVm::Create(m_userToken, std::move(config), vmId);\n\n        if (m_httpProxyStateTracker)\n        {\n            // this needs to be done after the VM has finished in case we fell back to NAT mode\n            m_httpProxyStateTracker->ConfigureNetworkingMode(m_utilityVm->GetConfig().NetworkingMode);\n        }\n\n        try\n        {\n            // Mount disks after the system distro vhd is mounted in case filesystem detection is needed.\n            _LoadDiskMounts();\n\n            // Save the networking settings so they can be reused on the next instantiation.\n            m_utilityVm->GetConfig().SaveNetworkingSettings(m_userToken.get());\n\n            // If the telemetry is enabled, launch the telemetry agent inside the VM.\n            if (m_utilityVm->GetConfig().EnableTelemetry && TraceLoggingProviderEnabled(g_hTraceLoggingProvider, WINEVENT_LEVEL_INFO, 0))\n            {\n                LPCSTR Arguments[] = {LX_INIT_TELEMETRY_AGENT, nullptr};\n                auto socket = m_utilityVm->CreateRootNamespaceProcess(LX_INIT_PATH, Arguments);\n                m_telemetryThread = std::thread(&LxssUserSessionImpl::TelemetryWorker, this, std::move(socket));\n            }\n\n            m_pluginManager.OnVmStarted(&m_session, &userSettings);\n        }\n        catch (...)\n        {\n            LOG_CAUGHT_EXCEPTION_MSG(\"VM failed to start, shutting down.\");\n\n            _VmTerminate();\n            throw;\n        }\n\n        auto callback = [this](auto Pid) {\n            // If the vm is currently being destroyed, the instance lock might be held\n            // while WslCoreVm's destructor is waiting on this thread.\n            // Cancel the call if the vm destruction is signaled.\n            // Note: This is safe because m_instanceLock is always initialized\n            // and because WslCoreVm's destructor waits for this thread, the session can't be gone\n            // until this callback completes.\n\n            auto lock = m_instanceLock.try_lock();\n            while (!lock)\n            {\n                if (m_vmTerminating.wait(100))\n                {\n                    return;\n                }\n                lock = m_instanceLock.try_lock();\n            }\n\n            auto unlock = wil::scope_exit([&]() { m_instanceLock.unlock(); });\n            TerminateByClientIdLockHeld(Pid);\n        };\n\n        // N.B. The callbacks must be registered outside of the above try/catch.\n        // Otherwise if an exception is thrown, calling _VmTerminate() will trigger the 's_VmTerminated' termination callback\n        // Which can deadlock since this thread holds the instance lock and HCS can block until the VM termination callback returns before deleting the VM.\n\n        m_utilityVm->RegisterCallbacks(std::bind(callback, _1), std::bind(s_VmTerminated, this, _1));\n    }\n\n    _VmCheckIdle();\n}\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_DeleteDistribution(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration, _In_ ULONG Flags)\n{\n    std::lock_guard lock(m_instanceLock);\n    _DeleteDistributionLockHeld(Configuration, Flags);\n}\n\n// Function signature of the API to remove WSLg start menu shortcuts.\nHRESULT RemoveAppProvider(LPCWSTR);\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_DeleteDistributionLockHeld(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration, _In_ ULONG Flags) const\n{\n    THROW_HR_IF(E_UNEXPECTED, (WI_IsAnyFlagSet(Flags, ~LXSS_DELETE_DISTRO_FLAGS_ALL)));\n\n    // For WSL1 distributions delete rootfs, temp, and the 9p socket.\n    std::filesystem::path deletePath{};\n    if (WI_IsFlagSet(Flags, LXSS_DELETE_DISTRO_FLAGS_ROOTFS))\n    {\n        deletePath = Configuration.BasePath / LXSS_ROOTFS_DIRECTORY;\n        if (PathFileExistsW(deletePath.c_str()))\n        {\n            LOG_IF_FAILED(wil::RemoveDirectoryRecursiveNoThrow(deletePath.c_str()));\n        }\n\n        deletePath = Configuration.BasePath / LXSS_TEMP_DIRECTORY;\n        if (PathFileExistsW(deletePath.c_str()))\n        {\n            LOG_IF_FAILED(wil::RemoveDirectoryRecursiveNoThrow(deletePath.c_str()));\n        }\n\n        deletePath = Configuration.BasePath / LXSS_PLAN9_UNIX_SOCKET;\n        if (PathFileExistsW(deletePath.c_str()))\n        {\n            LOG_IF_WIN32_BOOL_FALSE(DeleteFileW(deletePath.c_str()));\n        }\n    }\n\n    // For WSL2 distributions, unmount and delete the VHD.\n    if (WI_IsFlagSet(Flags, LXSS_DELETE_DISTRO_FLAGS_VHD) || WI_IsFlagSet(Flags, LXSS_DELETE_DISTRO_FLAGS_UNMOUNT))\n    {\n        if (PathFileExistsW(Configuration.VhdFilePath.c_str()))\n        {\n            if (m_utilityVm)\n            {\n                try\n                {\n                    m_utilityVm->EjectVhd(Configuration.VhdFilePath.c_str());\n                }\n                CATCH_LOG()\n            }\n\n            if (WI_IsFlagSet(Flags, LXSS_DELETE_DISTRO_FLAGS_VHD))\n            {\n                LOG_IF_WIN32_BOOL_FALSE(DeleteFileW(Configuration.VhdFilePath.c_str()));\n            }\n        }\n    }\n\n    if (WI_IsFlagSet(Flags, LXSS_DELETE_DISTRO_FLAGS_SHORTCUTS))\n    {\n        // Delete the shortcut icon, if any\n        const auto shortcutIconPath = Configuration.BasePath / c_shortIconName;\n        if (std::filesystem::exists(shortcutIconPath))\n        {\n            LOG_IF_WIN32_BOOL_FALSE_MSG(DeleteFileW(shortcutIconPath.c_str()), \"Failed to delete %ls\", shortcutIconPath.c_str());\n        }\n\n        // Remove start menu entry for the distribution, if any.\n        if (Configuration.ShortcutPath.has_value())\n        {\n            LOG_IF_WIN32_BOOL_FALSE_MSG(\n                DeleteFileW(Configuration.ShortcutPath->c_str()), \"Failed to delete %ls\", Configuration.ShortcutPath->c_str());\n        }\n\n        // Remove the terminal profile, if any.\n        try\n        {\n            const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\n            const auto profile = DistributionRegistration::Open(lxssKey.get(), Configuration.DistroId).Read(Property::TerminalProfilePath);\n\n            if (profile.has_value())\n            {\n                LOG_IF_WIN32_BOOL_FALSE_MSG(DeleteFileW(profile->c_str()), \"Failed to delete %ls\", profile->c_str());\n            }\n        }\n        CATCH_LOG()\n    }\n\n    // Remove start menu shortcuts for WSLg applications.\n    if (WI_IsFlagSet(Flags, LXSS_DELETE_DISTRO_FLAGS_WSLG_SHORTCUTS))\n    {\n        try\n        {\n            const auto dllPath = wsl::windows::common::wslutil::GetBasePath() / WSLG_TS_PLUGIN_DLL;\n            static LxssDynamicFunction<decltype(RemoveAppProvider)> removeAppProvider(dllPath.c_str(), \"RemoveAppProvider\");\n            LOG_IF_FAILED(removeAppProvider(Configuration.Name.c_str()));\n        }\n        CATCH_LOG()\n    }\n\n    // If the basepath is empty, delete it.\n    try\n    {\n        if (std::filesystem::is_empty(Configuration.BasePath))\n        {\n            LOG_IF_WIN32_BOOL_FALSE_MSG(\n                RemoveDirectory(Configuration.BasePath.c_str()), \"Failed to delete %ls\", Configuration.BasePath.c_str());\n        }\n    }\n    CATCH_LOG();\n}\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nstd::vector<DistributionRegistration> LxssUserSessionImpl::_EnumerateDistributions(\n    _In_ HKEY LxssKey, _In_ bool ListAll, _In_ const std::optional<GUID>& Exclude)\n{\n    // Iterate through all subkeys looking for distributions.\n    std::vector<DistributionRegistration> distributions;\n    std::vector<GUID> orphanedDistributions;\n    for (const auto& distro : wsl::windows::common::registry::EnumGuidKeys(LxssKey))\n    {\n        if (Exclude.has_value() && IsEqualGUID(Exclude.value(), distro.first))\n        {\n            continue;\n        }\n\n        // Validate that the distribution's package is still installed.\n        if (!_ValidateDistro(LxssKey, &distro.first))\n        {\n            orphanedDistributions.push_back(distro.first);\n            continue;\n        }\n\n        auto registration = DistributionRegistration::Open(LxssKey, distro.first);\n\n        // Add the distribution to the list if the caller requested all, or if\n        // it is installed or upgrading.\n        const DWORD state = registration.Read(Property::State);\n        if ((ListAll) || (state == LxssDistributionStateInstalled))\n        {\n            distributions.push_back(std::move(registration));\n        }\n    }\n\n    // Unregister each orphaned distribution.\n    for (GUID Distro : orphanedDistributions)\n    {\n        // TODO: This can fail if the registration is broken.\n        auto configuration = s_GetDistributionConfiguration(DistributionRegistration::Open(LxssKey, Distro));\n        _UnregisterDistributionLockHeld(LxssKey, configuration);\n    }\n\n    // Ensure that the default distribution is still valid.\n    if (!orphanedDistributions.empty())\n    {\n        try\n        {\n            _GetDefaultDistro(LxssKey);\n        }\n        CATCH_LOG()\n    }\n\n    return distributions;\n}\n\n_Requires_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_EnsureNotLocked(_In_ LPCGUID DistroGuid, const std::source_location& location)\n{\n    const auto found = std::find_if(m_lockedDistributions.begin(), m_lockedDistributions.end(), [&DistroGuid](const auto& entry) {\n        return IsEqualGUID(entry.first, *DistroGuid);\n    });\n\n    THROW_HR_IF_MSG(\n        E_ILLEGAL_STATE_CHANGE,\n        (found != m_lockedDistributions.end()),\n        \"%hs, %hs:%u\",\n        location.function_name(),\n        location.file_name(),\n        location.line());\n}\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nGUID LxssUserSessionImpl::_GetDefaultDistro(_In_ HKEY LxssKey)\n{\n    ExecutionContext context(Context::GetDefaultDistro);\n\n    GUID defaultDistroId = {};\n    HRESULT result;\n    try\n    {\n        const auto defaultDistro = DistributionRegistration::OpenDefault(LxssKey);\n\n        THROW_HR_IF(WSL_E_DEFAULT_DISTRO_NOT_FOUND, !defaultDistro.has_value());\n\n        // Ensure that the default distribution is valid.\n        if (!_ValidateDistro(LxssKey, &defaultDistro->Id()))\n        {\n            // Delete the old default distribution.\n            DistributionRegistration::DeleteDefault(LxssKey);\n\n            const auto configuration = s_GetDistributionConfiguration(defaultDistro.value());\n            _UnregisterDistributionLockHeld(LxssKey, configuration);\n\n            // Validate remaining WSL distributions, if there are any remaining\n            // set the first one found to the new default.\n            const auto distros = _EnumerateDistributions(LxssKey);\n            THROW_HR_IF(WSL_E_DEFAULT_DISTRO_NOT_FOUND, (distros.size() == 0));\n\n            DistributionRegistration::SetDefault(LxssKey, distros[0]);\n            defaultDistroId = distros[0].Id();\n        }\n        else\n        {\n            defaultDistroId = defaultDistro->Id();\n        }\n\n        result = S_OK;\n    }\n    catch (...)\n    {\n        result = WSL_E_DEFAULT_DISTRO_NOT_FOUND;\n    }\n\n    THROW_IF_FAILED(result);\n\n    return defaultDistroId;\n}\n\nLONG LxssUserSessionImpl::_GetElfExitStatus(_In_ const LXSS_RUN_ELF_CONTEXT& Context)\n{\n    // Wait for the instance to terminate or the client process to exit.\n    const wil::unique_handle clientProcess = wsl::windows::common::wslutil::OpenCallingProcess(GENERIC_READ | SYNCHRONIZE);\n    THROW_HR_IF(E_ABORT, !wsl::windows::common::relay::InterruptableWait(Context.instanceTerminatedEvent.get(), {clientProcess.get()}));\n\n    // Ensure that the process exited successfully. If the process encountered\n    // an error, wait for the stderr worker thread and log the error message.\n    LONG exitStatus;\n    THROW_IF_NTSTATUS_FAILED(LxssClientInstanceGetExitStatus(Context.instanceHandle.get(), &exitStatus));\n\n    return exitStatus;\n}\n\nwsl::core::Config LxssUserSessionImpl::_GetResultantConfig(_In_ const HANDLE userToken)\n{\n    const auto configFilePath = wsl::windows::common::helpers::GetWslConfigPath(userToken);\n    // Open the config file (%userprofile%\\.wslconfig).\n    wsl::core::Config config(configFilePath.c_str(), userToken);\n\n    _LoadNetworkingSettings(config, userToken);\n    return config;\n}\n\nvoid LxssUserSessionImpl::_LoadDiskMount(_In_ HKEY Key, _In_ const std::wstring& LunStr) const\ntry\n{\n    // Get the disk path\n    const auto path = wsl::windows::common::registry::ReadString(Key, nullptr, c_diskValueName);\n\n    // Get the disk type; throw if unexpected type\n    const auto diskType = static_cast<WslCoreVm::DiskType>(wsl::windows::common::registry::ReadDword(\n        Key, nullptr, c_disktypeValueName, static_cast<DWORD>(WslCoreVm::DiskType::PassThrough)));\n\n    THROW_HR_IF(E_UNEXPECTED, (diskType != WslCoreVm::DiskType::VHD && diskType != WslCoreVm::DiskType::PassThrough));\n\n    // Attach the disk to the VM, reusing the same LUN if possible.\n    //\n    // N.B. The user token is not provided because the key that holds the disk\n    // state can only be written by elevated users.\n    auto lun = std::stoul(LunStr);\n    m_utilityVm->AttachDisk(path.c_str(), diskType, lun, true, nullptr);\n\n    // Restore each mount point.\n    for (const auto& e : wsl::windows::common::registry::EnumKeys(Key, KEY_READ))\n    {\n        auto optionalValue = [&](std::wstring& storage, LPCWSTR name) -> LPCWSTR {\n            try\n            {\n                storage = wsl::windows::common::registry::ReadString(e.second.get(), nullptr, name);\n                return storage.c_str();\n            }\n            catch (...)\n            {\n                LOG_CAUGHT_EXCEPTION();\n                return nullptr;\n            }\n        };\n\n        std::wstring options;\n        std::wstring type;\n\n        // Get the mount name\n        auto diskName = wsl::windows::common::registry::ReadString(e.second.get(), nullptr, c_mountNameValueName, L\"\");\n\n        // If there was not a disk name stored, set it to the default generated name when mounting\n        const auto result = m_utilityVm->MountDisk(\n            path.c_str(),\n            diskType,\n            std::stoul(e.first),\n            diskName.empty() ? nullptr : diskName.c_str(),\n            optionalValue(type, c_typeValueName),\n            optionalValue(options, c_optionsValueName));\n\n        LOG_HR_IF_MSG(\n            E_UNEXPECTED,\n            result.Result != 0,\n            \"Failed to restore disk mount. Device: '%ls', Partition: '%ls', error: %i, step: %i\",\n            path.c_str(),\n            e.first.c_str(),\n            result.Result,\n            result.Step);\n    }\n\n    return;\n}\nCATCH_LOG()\n\nvoid LxssUserSessionImpl::_LoadNetworkingSettings(_Inout_ wsl::core::Config& config, _In_ HANDLE userToken)\ntry\n{\n    const auto autoProxyRequested = config.EnableAutoProxy;\n    if (config.EnableAutoProxy)\n    {\n        if (SUCCEEDED(HttpProxyStateTracker::s_LoadWinHttpProxyMethods()))\n        {\n            if (!m_httpProxyStateTracker)\n            {\n                try\n                {\n                    m_httpProxyStateTracker =\n                        std::make_shared<HttpProxyStateTracker>(config.InitialAutoProxyTimeout, userToken, config.NetworkingMode);\n                }\n                catch (...)\n                {\n                    LOG_CAUGHT_EXCEPTION_MSG(\"autoProxy failed to start\");\n                    config.EnableAutoProxy = false;\n                }\n            }\n        }\n        else\n        {\n            config.EnableAutoProxy = false;\n        }\n    }\n\n    WSL_LOG(\n        \"AutoProxyEnabled\",\n        TraceLoggingValue(autoProxyRequested, \"autoProxyRequested\"),\n        TraceLoggingValue(config.EnableAutoProxy, \"autoProxyEnabled\"));\n}\nCATCH_LOG();\n\nvoid LxssUserSessionImpl::_LoadDiskMounts()\ntry\n{\n    const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(&m_userSid.Sid);\n    for (const auto& e : wsl::windows::common::registry::EnumKeys(key.get(), KEY_READ))\n    {\n        _LoadDiskMount(e.second.get(), e.first);\n    }\n\n    // Clear the state from the registry now that the mounts have been loaded\n    wsl::windows::common::registry::ClearSubkeys(key.get());\n    return;\n}\nCATCH_LOG()\n\nvoid LxssUserSessionImpl::_ProcessImportResultMessage(\n    const LX_MINI_INIT_IMPORT_RESULT& Message,\n    const gsl::span<gsl::byte> Span,\n    HKEY LxssKey,\n    LXSS_DISTRO_CONFIGURATION& Configuration,\n    wsl::windows::service::DistributionRegistration& Registration)\n{\n    THROW_HR_IF(WSL_E_NOT_A_LINUX_DISTRO, !Message.ValidDistribution);\n\n    if (Configuration.Name.empty())\n    {\n        THROW_HR_IF(WSL_E_DISTRIBUTION_NAME_NEEDED, Message.DefaultNameIndex <= 0);\n\n        auto distributionName = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(Span, Message.DefaultNameIndex));\n\n        // Validate that name is valid, and doesn't conflict with existing distributions.\n        s_ValidateDistroName(distributionName.c_str());\n        _ValidateDistributionNameAndPathNotInUse(LxssKey, nullptr, distributionName.c_str(), Registration.Id());\n\n        Configuration.Name = std::move(distributionName);\n        Registration.Write(Property::Name, Configuration.Name.c_str());\n    }\n\n    if (Message.FlavorIndex > 0)\n    {\n        Configuration.Flavor = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(Span, Message.FlavorIndex));\n        Registration.Write(Property::Flavor, Configuration.Flavor.c_str());\n    }\n\n    if (Message.VersionIndex != 0)\n    {\n        Configuration.OsVersion = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(Span, Message.VersionIndex));\n        Registration.Write(Property::OsVersion, Configuration.OsVersion.c_str());\n    }\n\n    // Do not create start menu shortcut or terminal profiles for appx based distributions.\n    if (Configuration.PackageFamilyName.empty())\n    {\n        auto impersonate = wil::CoImpersonateClient();\n\n        Registration.Write(Property::Modern, 1);\n\n        std::filesystem::path iconPath;\n        const auto basePath = wsl::windows::common::wslutil::GetBasePath();\n\n        if (Message.ShortcutIconIndex != 0)\n        {\n            iconPath = Configuration.BasePath / c_shortIconName;\n            const wil::unique_handle icon{CreateFileW(iconPath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)};\n            THROW_LAST_ERROR_IF(!icon);\n\n            const auto iconData = Span.subspan(Message.ShortcutIconIndex, Message.ShortcutIconSize);\n            THROW_IF_WIN32_BOOL_FALSE(WriteFile(icon.get(), iconData.data(), static_cast<DWORD>(iconData.size_bytes()), nullptr, nullptr));\n        }\n        else\n        {\n            iconPath = basePath / L\"wsl.exe\";\n        }\n\n        if (Message.GenerateShortcut)\n        {\n            _CreateDistributionShortcut(Configuration.Name.c_str(), iconPath.c_str(), (basePath / L\"wsl.exe\").c_str(), Registration);\n        }\n\n        // Generate a Windows Terminal profile, as long as the distribution didn't opt-out of it.\n        if (Message.GenerateTerminalProfile)\n        {\n            if (Message.TerminalProfileIndex != 0)\n            {\n                const auto terminalProfileSpan = Span.subspan(Message.TerminalProfileIndex);\n                const std::string_view terminalProfile(reinterpret_cast<const char*>(terminalProfileSpan.data()), Message.TerminalProfileSize);\n                _CreateTerminalProfile(terminalProfile, iconPath, Configuration, Registration);\n            }\n            else\n            {\n                constexpr auto defaultProfile = R\"(\n                            {\n                                \"profiles\": [{\n                                \"startingDirectory\": \"~\"\n                                }]\n                            })\";\n\n                _CreateTerminalProfile(defaultProfile, iconPath, Configuration, Registration);\n            }\n        }\n    }\n}\n\nLXSS_RUN_ELF_CONTEXT LxssUserSessionImpl::_RunElfBinary(\n    _In_ LPCSTR CommandLine,\n    _In_ LPCWSTR TargetDirectory,\n    _In_ HANDLE ClientProcess,\n    _In_opt_ HANDLE StdIn,\n    _In_opt_ HANDLE StdOut,\n    _In_opt_ HANDLE StdErr,\n    _In_opt_count_(NumMounts) PLX_KMAPPATHS_ADDMOUNT Mounts,\n    _In_opt_ ULONG NumMounts)\n{\n    GUID instanceId;\n    THROW_IF_FAILED(CoCreateGuid(&instanceId));\n\n    // If the caller did not provide stdin, stdout, or stderr handles use the nul device.\n    wil::unique_hfile stdInLocal;\n    if (!ARGUMENT_PRESENT(StdIn))\n    {\n        stdInLocal = wsl::windows::common::filesystem::OpenNulDevice(GENERIC_READ);\n        StdIn = stdInLocal.get();\n    }\n\n    wil::unique_hfile stdOutLocal;\n    if (!ARGUMENT_PRESENT(StdOut))\n    {\n        stdOutLocal = wsl::windows::common::filesystem::OpenNulDevice(GENERIC_WRITE);\n        StdOut = stdOutLocal.get();\n    }\n\n    wil::unique_hfile stdErrLocal;\n    if (!ARGUMENT_PRESENT(StdErr))\n    {\n        stdErrLocal = wsl::windows::common::filesystem::OpenNulDevice(GENERIC_WRITE);\n        StdErr = stdErrLocal.get();\n    }\n\n    // Get the user and instance tokens.\n    const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n    const wil::unique_handle instanceToken(wsl::windows::common::security::CreateRestrictedToken(userToken.get()));\n\n    // Open handles to the root directory and temp directory while impersonating\n    // the client.\n    wil::unique_hfile rootDirectory;\n    wil::unique_hfile tempDirectory;\n    {\n        auto runAsUser = wil::impersonate_token(userToken.get());\n        rootDirectory = wsl::windows::common::filesystem::OpenDirectoryHandle(TargetDirectory, true);\n        const auto tempFolder = std::filesystem::path(TargetDirectory) / LXSS_TEMP_DIRECTORY;\n        wsl::windows::common::filesystem::EnsureDirectory(tempFolder.c_str());\n        const auto instanceIdString = wsl::shared::string::GuidToString<wchar_t>(instanceId);\n        const auto tempPath = tempFolder / instanceIdString;\n        tempDirectory = wsl::windows::common::filesystem::WipeAndOpenDirectory(tempPath.c_str());\n    }\n\n    // Create manual reset event that is signaled on instance termination\n    LXSS_RUN_ELF_CONTEXT elfContext;\n    elfContext.instanceTerminatedEvent.create(wil::EventOptions::ManualReset);\n    THROW_LAST_ERROR_IF(!elfContext.instanceTerminatedEvent);\n\n    // Create and initialize a job object for the instance.\n    const wil::unique_handle instanceJob(CreateJobObjectW(nullptr, nullptr));\n    THROW_LAST_ERROR_IF(!instanceJob);\n\n    Security::InitializeInstanceJob(instanceJob.get());\n\n    // Create a new instance with bsdtar as the init process to perform the extraction.\n    LX_KINSTANCECREATESTART createParameters = {};\n    createParameters.InstanceId = instanceId;\n    createParameters.RootFsType = LXSS_FS_TYPE_TMPFS;\n    createParameters.RootDirectoryHandle = HandleToULong(rootDirectory.get());\n    createParameters.TempDirectoryHandle = HandleToULong(tempDirectory.get());\n    createParameters.JobHandle = HandleToULong(instanceJob.get());\n    createParameters.TokenHandle = HandleToULong(instanceToken.get());\n    createParameters.InstanceTerminatedEventHandle = HandleToULong(elfContext.instanceTerminatedEvent.get());\n    createParameters.NumPathsToMap = NumMounts;\n    createParameters.PathsToMap = Mounts;\n\n    // Format the kernel command line.\n    std::string kernelCommandLine(\"init=\");\n    kernelCommandLine += CommandLine;\n    createParameters.KernelCommandLine = kernelCommandLine.c_str();\n\n    // Set up the file descriptors that will be passed to the init process.\n    LX_KINIT_FILE_DESCRIPTOR initFileDescriptors[] = {\n        {StdIn, LX_O_RDONLY, LX_FD_CLOEXEC}, {StdOut, LX_O_WRONLY, LX_FD_CLOEXEC}, {StdErr, LX_O_WRONLY, LX_FD_CLOEXEC}};\n\n    createParameters.NumInitFileDescriptors = RTL_NUMBER_OF(initFileDescriptors);\n    createParameters.InitFileDescriptors = initFileDescriptors;\n\n    {\n        // Acquire assign primary token privilege in order to pass the primary token for init process.\n        auto revertPriv = wsl::windows::common::security::AcquirePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME);\n        THROW_IF_NTSTATUS_FAILED(LxssClientInstanceCreate(&createParameters, &elfContext.instanceHandle));\n    }\n\n    // Start the instance.\n    THROW_IF_NTSTATUS_FAILED(LxssClientInstanceStart(elfContext.instanceHandle.get(), ClientProcess));\n\n    return elfContext;\n}\n\n_Requires_lock_held_(m_instanceLock)\nstd::shared_ptr<LxssRunningInstance> LxssUserSessionImpl::_RunningInstance(_In_ LPCGUID DistroGuid)\n{\n    _EnsureNotLocked(DistroGuid);\n    const auto instance = m_runningInstances.find(*DistroGuid);\n    if (instance != m_runningInstances.end())\n    {\n        return instance->second;\n    }\n\n    return nullptr;\n}\n\nLXSS_VM_MODE_SETUP_CONTEXT\nLxssUserSessionImpl::_RunUtilityVmSetup(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration, _In_ LX_MESSAGE_TYPE MessageType, ULONG ExportFlags, bool SetVersion)\n{\n    THROW_HR_IF(E_INVALIDARG, ((MessageType != LxMiniInitMessageImport) && (MessageType != LxMiniInitMessageExport) && (MessageType != LxMiniInitMessageImportInplace)));\n\n    // Open the client process so the operation can be aborted if client exits.\n    wil::unique_handle clientProcess = wsl::windows::common::wslutil::OpenCallingProcess(GENERIC_READ | SYNCHRONIZE);\n\n    // Ensure that the Linux utility VM has been created.\n    std::lock_guard lock(m_instanceLock);\n    _CreateVm();\n\n    WI_SetFlagIf(ExportFlags, LXSS_EXPORT_DISTRO_FLAGS_VERBOSE, SetVersion && m_utilityVm->GetConfig().SetVersionDebug);\n\n    // Generate a GUID for the instance.\n    GUID instanceId;\n    THROW_IF_FAILED(CoCreateGuid(&instanceId));\n\n    LXSS_VM_MODE_SETUP_CONTEXT context{};\n    ULONG connectPort{};\n    context.instance = m_utilityVm->CreateInstance(instanceId, Configuration, MessageType, 0, 0, 0, ExportFlags, &connectPort);\n\n    // Establish the socket that will be used to transfer the tar file contents.\n    context.tarSocket = wsl::windows::common::hvsocket::Connect(m_utilityVm->GetRuntimeId(), connectPort);\n    context.errorSocket = wsl::windows::common::hvsocket::Connect(m_utilityVm->GetRuntimeId(), connectPort);\n    WI_ASSERT(context.tarSocket.is_valid());\n\n    return context;\n}\n\nvoid LxssUserSessionImpl::_SendDistributionRegisteredEvent(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration) const\n{\n    WslOfflineDistributionInformation distributionInfo{};\n    distributionInfo.Id = Configuration.DistroId;\n    distributionInfo.Name = Configuration.Name.c_str();\n    distributionInfo.PackageFamilyName = Configuration.PackageFamilyName.c_str();\n    distributionInfo.Flavor = Configuration.Flavor.c_str();\n    distributionInfo.Version = Configuration.OsVersion.c_str();\n    m_pluginManager.OnDistributionRegistered(&m_session, &distributionInfo);\n}\n\n_Requires_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_SetDistributionInstalled(_In_ HKEY LxssKey, _In_ const GUID& DistroGuid)\n{\n    // Mark the distribution as installed.\n    auto registration = DistributionRegistration::Open(LxssKey, DistroGuid);\n    registration.Write(Property::State, LxssDistributionStateInstalled);\n\n    // Set this distribution as the default if there is not already a default\n    // distribution.\n    const auto defaultDistro = DistributionRegistration::OpenDefault(LxssKey);\n    if (!defaultDistro.has_value())\n    {\n        DistributionRegistration::SetDefault(LxssKey, registration);\n    }\n}\n\n_Requires_lock_not_held_(m_instanceLock)\nbool LxssUserSessionImpl::_TerminateInstance(_In_ LPCGUID DistroGuid, _In_ bool CheckForClients)\n{\n    std::lock_guard lock(m_instanceLock);\n    return _TerminateInstanceInternal(DistroGuid, CheckForClients);\n}\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nbool LxssUserSessionImpl::_TerminateInstanceInternal(_In_ LPCGUID DistroGuid, _In_ bool CheckForClients)\n{\n    ExecutionContext context(Context::TerminateDistro);\n\n    // Look up an instance with the matching distro identifier. If the are no\n    // more active clients, it is stopped and removed from the list.\n    bool success = true;\n    const auto instance = m_runningInstances.find(*DistroGuid);\n\n    if (instance != m_runningInstances.end())\n    {\n        const auto clientKey = instance->second->GetLifetimeManagerId();\n        if ((CheckForClients == false) || (!m_lifetimeManager.IsAnyProcessRegistered(clientKey)))\n        {\n            // Stop the instance and move it to a list of terminated instances.\n            // This allows the instance destructor to run without the instance\n            // lock held, and allows in-flight termination callbacks to complete.\n            const bool force = !CheckForClients;\n            try\n            {\n                success = instance->second->RequestStop(force);\n            }\n            CATCH_LOG()\n\n            success = (success || force);\n            if (success)\n            {\n                if (const auto* wslcoreInstance = dynamic_cast<WslCoreInstance*>(instance->second.get()); wslcoreInstance != nullptr)\n                {\n                    m_pluginManager.OnDistributionStopping(&m_session, wslcoreInstance->DistributionInformation());\n                }\n\n                instance->second->Stop();\n\n                const auto clientId = instance->second->GetClientId();\n                {\n                    auto lock = m_terminatedInstanceLock.lock_exclusive();\n                    m_terminatedInstances.push_back(std::move(instance->second));\n                }\n\n                m_lifetimeManager.RemoveCallback(clientKey);\n\n                m_runningInstances.erase(instance);\n\n                // If the instance that was terminated was a WSL2 instance,\n                // check if the VM is now idle.\n                if (clientId != LXSS_CLIENT_ID_INVALID)\n                {\n                    _VmCheckIdle();\n                }\n            }\n        }\n    }\n\n    return success;\n}\n\nvoid LxssUserSessionImpl::_UpdateInit(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration)\n{\n    // Only update the init binary once per-distro, per-session.\n    auto lock = m_initUpdateLock.lock_exclusive();\n    if (std::find(m_updatedInitDistros.begin(), m_updatedInitDistros.end(), Configuration.DistroId) == m_updatedInitDistros.end())\n    {\n        wsl::windows::common::filesystem::UpdateInit(Configuration.BasePath.c_str(), Configuration.Version);\n        m_updatedInitDistros.emplace_back(Configuration.DistroId);\n    }\n}\n\nHRESULT LxssUserSessionImpl::MountRootNamespaceFolder(_In_ LPCWSTR HostPath, _In_ LPCWSTR GuestPath, _In_ bool ReadOnly, _In_ LPCWSTR Name)\n{\n    std::lock_guard lock(m_instanceLock);\n    RETURN_HR_IF(E_NOT_VALID_STATE, !m_utilityVm);\n\n    m_utilityVm->MountRootNamespaceFolder(HostPath, GuestPath, ReadOnly, Name);\n    return S_OK;\n}\n\nHRESULT LxssUserSessionImpl::CreateLinuxProcess(_In_opt_ const GUID* Distro, _In_ LPCSTR Path, _In_ LPCSTR* Arguments, _Out_ SOCKET* Socket)\n{\n    std::lock_guard lock(m_instanceLock);\n    RETURN_HR_IF(E_NOT_VALID_STATE, !m_utilityVm);\n\n    if (Distro == nullptr)\n    {\n        *Socket = m_utilityVm->CreateRootNamespaceProcess(Path, Arguments).release();\n    }\n    else\n    {\n        const auto distro = _RunningInstance(Distro);\n        THROW_HR_IF(WSL_E_VM_MODE_INVALID_STATE, !distro);\n\n        const auto wsl2Distro = dynamic_cast<WslCoreInstance*>(distro.get());\n        THROW_HR_IF(WSL_E_WSL2_NEEDED, !wsl2Distro);\n\n        *Socket = wsl2Distro->CreateLinuxProcess(Path, Arguments).release();\n    }\n    return S_OK;\n}\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_UnregisterDistributionLockHeld(_In_ HKEY LxssKey, _In_ const LXSS_DISTRO_CONFIGURATION& Configuration)\n{\n    ExecutionContext context(Context::UnregisterDistro);\n\n    try\n    {\n        const auto removedDistroString = wsl::shared::string::GuidToString<wchar_t>(Configuration.DistroId);\n\n        // Terminate any running instance of the distro and delete the distro.\n        _TerminateInstanceInternal(&Configuration.DistroId);\n\n        // Impersonate the user and delete the distro filesystem.\n        {\n            auto runAsUser = wil::CoImpersonateClient();\n            _DeleteDistributionLockHeld(Configuration);\n        }\n\n        // Delete the distro registry key.\n        wsl::windows::common::registry::DeleteKey(LxssKey, removedDistroString.c_str());\n    }\n    CATCH_LOG()\n}\n\nvoid LxssUserSessionImpl::_TimezoneUpdated()\ntry\n{\n    WSL_LOG(\"Received timezone change notification\");\n\n    // Update the timezone information for each running instance.\n    std::lock_guard lock(m_instanceLock);\n    std::for_each(m_runningInstances.begin(), m_runningInstances.end(), [&](auto& pair) { pair.second->UpdateTimezone(); });\n\n    return;\n}\nCATCH_LOG()\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nbool LxssUserSessionImpl::_ValidateDistro(_In_ HKEY LxssKey, _In_ LPCGUID DistroGuid)\n{\n    bool isValid = false;\n    std::wstring packageFamilyName;\n    try\n    {\n        // Ensure a subkey exists for the distribution.\n        auto configuration = s_GetDistributionConfiguration(DistributionRegistration::Open(LxssKey, *DistroGuid));\n        packageFamilyName = configuration.PackageFamilyName;\n\n        // If there is no package family name associated with the distribution,\n        // the user is responsible for unregistering the distribution.\n        // Otherwise, ensure that the package is still installed. If the\n        // package is installed ensure that the root file system is present.\n        //\n        // N.B. This covers the case where a package was uninstalled and\n        //      reinstalled without the service being invoked.\n        isValid = true;\n\n        // TODO: Below block needs test coverage\n        if (!packageFamilyName.empty())\n        {\n            std::filesystem::path localPath;\n            PCWSTR path;\n            if (WI_IsFlagClear(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE))\n            {\n                localPath = configuration.BasePath / LXSS_ROOTFS_DIRECTORY;\n                path = localPath.c_str();\n            }\n            else\n            {\n                path = configuration.VhdFilePath.c_str();\n            }\n\n            auto runAsUser = wil::CoImpersonateClient();\n\n            // If the path is not found and the package is removed, then the distro can be considered to be uninstalled.\n            // Only do this if the path is actually missing to prevent any accidental distro deletion if the store API\n            // can't find the package for transient reasons.\n            if (!PathFileExistsW(path) && !wsl::windows::common::helpers::IsPackageInstalled(packageFamilyName.c_str()))\n            {\n                isValid = false;\n            }\n        }\n    }\n    CATCH_LOG()\n\n    if (!isValid)\n    {\n        WSL_LOG(\"ValidateDistributionFailed\", TraceLoggingValue(packageFamilyName.c_str(), \"packageFamilyName\"));\n    }\n\n    return isValid;\n}\n\nvoid LxssUserSessionImpl::_ValidateDistributionNameAndPathNotInUse(\n    _In_ HKEY LxssKey, _In_opt_ LPCWSTR Path, _In_opt_ LPCWSTR Name, const std::optional<GUID>& Exclude)\n{\n    // Use the canonical path to compare distribution registration paths.\n    // The canonical path allows us to compare paths regardless of symlinks.\n    //\n    // Even with this, it's theoretically possible to use different drive mounts to have two paths\n    // that will point to the same underlying folder. To catch this, we'd need to use BY_HANDLE_FILE_INFORMATION and compare file & volume indexes.\n    // Unfortunately this is tricky because this doesn't work of the folder doesn't exist yet (or if a registered distribution's folder has been deleted).\n    // For the sake of simplicity, this isn't implemented given that trying to double register a distribution will fail at the VHD creation step regardless.\n\n    std::error_code error;\n    std::filesystem::path canonicalPath;\n\n    if (Path != nullptr)\n    {\n        canonicalPath = std::filesystem::weakly_canonical(Path, error);\n        if (error)\n        {\n            LOG_WIN32(error.value());\n        }\n        else\n        {\n            Path = canonicalPath.c_str();\n        }\n    }\n\n    // Ensure no existing distributions have the same name or install path.\n    for (const auto& distro : _EnumerateDistributions(LxssKey, true, Exclude))\n    {\n        // Return an appropriate failure code for the two possible\n        // conditions here:\n        //\n        //     1. The distribution is already registered successfully.\n        //     2. The distribution is currently being registered or unregistered by another thread.\n\n        LXSS_DISTRO_CONFIGURATION configuration{};\n        try\n        {\n            configuration = s_GetDistributionConfiguration(distro);\n        }\n        catch (...)\n        {\n            // Don't break registration of new distro if one registration is invalid.\n            LOG_CAUGHT_EXCEPTION();\n            continue;\n        }\n\n        if (Name != nullptr && wsl::shared::string::IsEqual(Name, configuration.Name, true))\n        {\n            THROW_HR_MSG(\n                (configuration.State == LxssDistributionStateInstalled) ? HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) : E_ILLEGAL_STATE_CHANGE,\n                \"%ls already registered (state = %d)\",\n                Name,\n                configuration.State);\n        }\n\n        if (Path != nullptr)\n        {\n            auto canonicalDistroPath = std::filesystem::weakly_canonical(configuration.BasePath, error);\n            if (error)\n            {\n                LOG_WIN32(error.value());\n            }\n\n            // Ensure another distribution by a different name is not already registered to the same location.\n            THROW_HR_IF(\n                HRESULT_FROM_WIN32(ERROR_FILE_EXISTS),\n                wsl::windows::common::string::IsPathComponentEqual(error ? configuration.BasePath.native() : canonicalDistroPath.native(), Path));\n        }\n    }\n}\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_VmCheckIdle()\n{\n    // If the VM is idle, queue a timer to terminate the VM.\n    // Otherwise, cancel any pending termination timers.\n    //\n    // N.B. A negative timeout means that the VM will continue running until it\n    //      is terminated via wsl.exe --shutdown, or the service is stopped.\n    if (_VmIsIdle())\n    {\n        const auto timeout = m_utilityVm->GetVmIdleTimeout();\n        if (timeout >= 0)\n        {\n            auto dueTime = wil::filetime::from_int64(-wil::filetime_duration::one_millisecond * timeout);\n            SetThreadpoolTimer(m_vmTerminationTimer.get(), &dueTime, 0, 0);\n        }\n    }\n    else\n    {\n        SetThreadpoolTimer(m_vmTerminationTimer.get(), nullptr, 0, 0);\n    }\n}\n\nvoid LxssUserSessionImpl::_VmIdleTerminate()\n{\n    std::lock_guard lock(m_instanceLock);\n    if (_VmIsIdle())\n    {\n        WSL_LOG(\"StopVm\");\n        m_utilityVm->SaveAttachedDisksState();\n        _VmTerminate();\n    }\n}\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nbool LxssUserSessionImpl::_VmIsIdle()\n{\n    const auto found = std::find_if(m_runningInstances.begin(), m_runningInstances.end(), [&](auto& pair) {\n        return (pair.second->GetClientId() != LXSS_CLIENT_ID_INVALID);\n    });\n\n    return (m_utilityVm && m_lockedDistributions.empty() && (found == m_runningInstances.end()));\n}\n\n_Requires_exclusive_lock_held_(m_instanceLock)\nvoid LxssUserSessionImpl::_VmTerminate()\n{\n    // Cancel any pending termination timers and terminate the system distro and VM.\n    SetThreadpoolTimer(m_vmTerminationTimer.get(), nullptr, 0, 0);\n\n    if (m_utilityVm != nullptr)\n    {\n        m_pluginManager.OnVmStopping(&m_session);\n    }\n\n    m_vmTerminating.SetEvent();\n    if (m_telemetryThread.joinable())\n    {\n        m_telemetryThread.join();\n    }\n\n    m_utilityVm.reset();\n    m_vmId.store(GUID_NULL);\n\n    // Reset the user's token since its lifetime is tied to the VM.\n    m_userToken.reset();\n    m_session.UserToken = nullptr;\n\n    // Reset the event since the VM can be recreated.\n    // This can done safely because WslCoreVm's destructor waits until\n    // its distro exit callback is done before returning, so at this point\n    // it's guaranteed that no one is waiting (or about to wait) on the event.\n    // Note: Using an auto-reset event wouldn't work since the callback can be invoked\n    // more than once while the vm is being destroyed.\n    m_vmTerminating.ResetEvent();\n}\n\nvoid LxssUserSessionImpl::_SetHttpProxyInfo(std::vector<std::string>& environment) const noexcept\ntry\n{\n    // Copy guarantees a ref is held on the original instance, or it's null.\n    const auto localTracker = m_httpProxyStateTracker;\n    if (localTracker)\n    {\n        WSL_LOG(\"_SetHttpProxyInfo: Attempting to set proxy info\");\n        const std::optional<HttpProxySettings> proxySettings = localTracker->WaitForInitialProxySettings();\n\n        if (proxySettings.has_value())\n        {\n            if (proxySettings->UnsupportedProxyDropReason != UnsupportedProxyReason::Supported)\n            {\n                switch (proxySettings->UnsupportedProxyDropReason)\n                {\n                case UnsupportedProxyReason::LoopbackNotMirrored:\n                    EMIT_USER_WARNING(wsl::shared::Localization::MessageProxyLocalhostSettingsDropped());\n                    break;\n                case UnsupportedProxyReason::Ipv6NotMirrored:\n                    EMIT_USER_WARNING(wsl::shared::Localization::MessageProxyV6SettingsDropped());\n                    break;\n                case UnsupportedProxyReason::LoopbackV6:\n                    EMIT_USER_WARNING(wsl::shared::Localization::MessageProxyLoopbackV6SettingsDropped());\n                    break;\n                case UnsupportedProxyReason::UnsupportedError:\n                    EMIT_USER_WARNING(wsl::shared::Localization::MessageProxyUnexpectedSettingsDropped());\n                    break;\n                case UnsupportedProxyReason::Supported:\n                default:\n                    WSL_LOG(\"_SetHttpProxyInfo: Unexpected UnsupportedProxyReason\");\n                }\n            }\n            if (proxySettings->HasSettingsConfigured())\n            {\n                s_AddHttpProxyToEnvironment(proxySettings.value(), environment);\n\n                WSL_LOG(\n                    \"AutoProxyConfiguration\",\n                    TraceLoggingValue(!proxySettings->Proxy.empty(), \"ProxySet\"),\n                    TraceLoggingValue(!proxySettings->SecureProxy.empty(), \"SecureProxySet\"),\n                    TraceLoggingValue(proxySettings->ProxyBypasses.size(), \"ProxyBypassesCount\"),\n                    TraceLoggingValue(!proxySettings->PacUrl.empty(), \"PacUrlSet\"));\n            }\n            else\n            {\n                WSL_LOG(\"_SetHttpProxyInfo: No HttpProxy settings detected so not configuring env vars.\");\n            }\n        }\n        else\n        {\n            // User will get a notification to restart WSL if proxy query completes later.\n            WSL_LOG(\"_SetHttpProxyInfo: Initial HttpProxy query timeout, start WSL process anyway.\");\n        }\n    }\n}\nCATCH_LOG()\n\nvoid LxssUserSessionImpl::_LaunchOOBEIfNeeded() noexcept\ntry\n{\n    // Impersonate the user and open their lxss registry key.\n    const wil::unique_hkey lxssKey = s_OpenLxssUserKey();\n\n    // OOBE hasn't run if the value is not present or set to 0.\n    if (wsl::windows::common::registry::ReadDword(lxssKey.get(), nullptr, LXSS_OOBE_COMPLETE_NAME, false) != false)\n    {\n        return;\n    }\n\n    // Don't run OOBE for existing users who already have a distro.\n    wil::unique_cotaskmem_array_ptr<LXSS_ENUMERATE_INFO> distributions;\n    THROW_IF_FAILED(EnumerateDistributions(distributions.size_address<ULONG>(), &distributions));\n    if (distributions.size() > 1)\n    {\n        wsl::windows::common::registry::WriteDword(lxssKey.get(), nullptr, LXSS_OOBE_COMPLETE_NAME, true);\n        return;\n    }\n\n    const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n    // This is needed to launch the OOBE process as the user.\n    wil::unique_handle userTokenCreateProcess;\n    THROW_IF_WIN32_BOOL_FALSE(::DuplicateTokenEx(\n        userToken.get(), MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenImpersonation, &userTokenCreateProcess));\n    wsl::windows::common::helpers::LaunchWslSettingsOOBE(userTokenCreateProcess.get());\n    wsl::windows::common::registry::WriteDword(lxssKey.get(), nullptr, LXSS_OOBE_COMPLETE_NAME, true);\n}\nCATCH_LOG()\n\nLXSS_DISTRO_CONFIGURATION\nLxssUserSessionImpl::s_GetDistributionConfiguration(const DistributionRegistration& Distro, bool skipName)\n{\n    ExecutionContext context(Context::ReadDistroConfig);\n\n    // Read information about the distribution from the distro key.\n    LXSS_DISTRO_CONFIGURATION configuration;\n    configuration.DistroId = Distro.Id();\n    configuration.State = Distro.Read(Property::State);\n    configuration.Version = Distro.Read(Property::Version);\n    configuration.BasePath = Distro.Read(Property::BasePath);\n    configuration.PackageFamilyName = Distro.Read(Property::PackageFamilyName);\n\n    // Read the vhd file name and append to the base path.\n    configuration.VhdFilePath = configuration.BasePath / Distro.Read(Property::VhdFileName);\n    configuration.Flags = Distro.Read(Property::Flags);\n\n    configuration.OsVersion = Distro.Read(Property::OsVersion).value_or(L\"\");\n    configuration.Flavor = Distro.Read(Property::Flavor).value_or(L\"\");\n    configuration.RunOOBE = Distro.Read(Property::RunOOBE);\n    configuration.ShortcutPath = Distro.Read(Property::ShortcutPath);\n\n    if (!skipName)\n    {\n        configuration.Name = Distro.Read(Property::Name);\n    }\n\n    return configuration;\n}\n\nCreateLxProcessContext LxssUserSessionImpl::s_GetCreateProcessContext(_In_ const GUID& DistroGuid, _In_ bool SystemDistro)\n{\n    CreateLxProcessContext context{};\n    std::vector<std::wstring> environment{};\n    if (!SystemDistro)\n    {\n        auto runAsUser = wil::CoImpersonateClient();\n        const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\n\n        const auto registration = DistributionRegistration::Open(lxssKey.get(), DistroGuid);\n\n        context.Flags = registration.Read(Property::Flags);\n        context.DefaultEnvironment = registration.Read(Property::DefaultEnvironment);\n    }\n    else\n    {\n        context.Flags = DistributionRegistration::ApplyGlobalFlagsOverride(LXSS_DISTRO_FLAGS_DEFAULT | LXSS_DISTRO_FLAGS_VM_MODE);\n        context.DefaultEnvironment = Property::DefaultEnvironment.DefaultValue;\n    }\n\n    context.UserToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n    context.Elevated = wsl::windows::common::security::IsTokenElevated(context.UserToken.get());\n    return context;\n}\n\n// Note that if the user defines proxy variables via WSLENV, these values will be overwritten by those when init spawns process\nvoid LxssUserSessionImpl::s_AddHttpProxyToEnvironment(_In_ const HttpProxySettings& proxySettings, _Inout_ std::vector<std::string>& environment) noexcept\ntry\n{\n    if (!proxySettings.Proxy.empty())\n    {\n        // Note that we add both lower and uppercase as some Linux apps use upper, others lower.\n        environment.emplace_back(std::format(\"{}={}\", c_httpProxyLower, proxySettings.Proxy));\n        environment.emplace_back(std::format(\"{}={}\", c_httpProxyUpper, proxySettings.Proxy));\n    }\n\n    if (!proxySettings.SecureProxy.empty())\n    {\n        environment.emplace_back(std::format(\"{}={}\", c_httpsProxyLower, proxySettings.SecureProxy));\n        environment.emplace_back(std::format(\"{}={}\", c_httpsProxyUpper, proxySettings.SecureProxy));\n    }\n\n    if (!proxySettings.ProxyBypassesComma.empty())\n    {\n        environment.emplace_back(std::format(\"{}={}\", c_proxyBypassLower, proxySettings.ProxyBypassesComma));\n        environment.emplace_back(std::format(\"{}={}\", c_proxyBypassUpper, proxySettings.ProxyBypassesComma));\n    }\n\n    if (!proxySettings.PacUrl.empty())\n    {\n        // We only add uppercase as there is no standard environment variable for PAC proxies.\n        // This at least makes the PAC url available to the user in case they wish to use it.\n        environment.emplace_back(std::format(\"{}={}\", c_pacProxy, proxySettings.PacUrl));\n    }\n}\nCATCH_LOG()\n\nwil::unique_hkey LxssUserSessionImpl::s_OpenLxssUserKey()\n{\n    auto runAsUser = wil::CoImpersonateClient();\n    return wsl::windows::common::registry::OpenLxssUserKey();\n}\n\nbool LxssUserSessionImpl::s_TerminateInstance(_Inout_ LxssUserSessionImpl* UserSession, _In_ GUID DistroGuid, _In_ bool CheckForClients)\n{\n    bool success = true;\n    try\n    {\n        success = UserSession->_TerminateInstance(&DistroGuid, CheckForClients);\n    }\n    CATCH_LOG()\n\n    return success;\n}\n\nvoid LxssUserSessionImpl::s_UpdateInit(_Inout_ LxssUserSessionImpl* UserSession, _In_ const LXSS_DISTRO_CONFIGURATION& Configuration)\ntry\n{\n    UserSession->_UpdateInit(Configuration);\n}\nCATCH_LOG()\n\nLRESULT\nCALLBACK\nLxssUserSessionImpl::s_TimezoneWindowProc(HWND windowHandle, UINT messageCode, WPARAM wParameter, LPARAM lParameter)\n{\n    if (messageCode == WM_TIMECHANGE)\n    {\n        auto* session = reinterpret_cast<LxssUserSessionImpl*>(GetWindowLongPtr(windowHandle, GWLP_USERDATA));\n        if (session != nullptr)\n        {\n            session->_TimezoneUpdated();\n        }\n    }\n\n    return DefWindowProc(windowHandle, messageCode, wParameter, lParameter);\n}\n\nvoid LxssUserSessionImpl::s_ValidateDistroName(_In_ LPCWSTR Name)\n{\n    // Validate the name string. The name must match the regular expression\n    // and cannot be the reserved legacy name.\n    std::wstring regex{L\"^[a-zA-Z0-9._-]{1,\"};\n    regex += std::to_wstring(LX_INIT_DISTRO_NAME_MAX);\n    regex += L\"}$\";\n    if ((!std::regex_match(Name, std::wregex(regex.c_str()))) || wsl::shared::string::IsEqual(Name, LXSS_LEGACY_INSTALL_NAME, true))\n    {\n        THROW_HR_WITH_USER_ERROR(E_INVALIDARG, wsl::shared::Localization::MessageInvalidInstallDistributionName(Name));\n    }\n}\n\nVOID CALLBACK LxssUserSessionImpl::s_VmIdleTerminate(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER)\n{\n    try\n    {\n        const auto userSession = reinterpret_cast<LxssUserSessionImpl*>(Context);\n        userSession->_VmIdleTerminate();\n    }\n    CATCH_LOG()\n}\n\nvoid LxssUserSessionImpl::s_VmTerminated(_Inout_ LxssUserSessionImpl* UserSession, _In_ const GUID& VmId)\ntry\n{\n    UNREFERENCED_PARAMETER(VmId);\n\n    UserSession->TerminateByClientId(LXSS_CLIENT_ID_WILDCARD);\n    return;\n}\nCATCH_LOG()\n"
  },
  {
    "path": "src/windows/service/exe/LxssUserSession.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssUserSession.h\n\nAbstract:\n\n    This file contains session function declarations.\n\n--*/\n\n#pragma once\n\n#include \"LxssPort.h\"\n#include \"LxssMessagePort.h\"\n#include \"LxssCreateProcess.h\"\n#include \"filesystem.hpp\"\n#include \"LxssHttpProxy.h\"\n#include \"WslCoreVm.h\"\n#include \"PluginManager.h\"\n#include \"Lifetime.h\"\n#include \"DistributionRegistration.h\"\n\n#define WSL_NEW_DISTRO_LXFS L\"NewDistributionLxFs\"\n#define WSL_DISTRO_CONFIG_DEFAULT_UID L\"DefaultUid\"\n\n#define LXSS_DELETE_DISTRO_FLAGS_ROOTFS 0x1\n#define LXSS_DELETE_DISTRO_FLAGS_VHD 0x2\n#define LXSS_DELETE_DISTRO_FLAGS_WSLG_SHORTCUTS 0x4\n#define LXSS_DELETE_DISTRO_FLAGS_SHORTCUTS 0x8\n#define LXSS_DELETE_DISTRO_FLAGS_UNMOUNT 0x10\n#define LXSS_DELETE_DISTRO_FLAGS_ALL \\\n    (LXSS_DELETE_DISTRO_FLAGS_ROOTFS | LXSS_DELETE_DISTRO_FLAGS_VHD | LXSS_DELETE_DISTRO_FLAGS_WSLG_SHORTCUTS | \\\n     LXSS_DELETE_DISTRO_FLAGS_SHORTCUTS | LXSS_DELETE_DISTRO_FLAGS_UNMOUNT)\n\nclass ConsoleManager;\nclass LxssRunningInstance;\nclass WslCoreVm;\nclass LxssUserSessionImpl;\n\ntypedef struct _LXSS_RUN_ELF_CONTEXT\n{\n    wil::unique_event instanceTerminatedEvent;\n    wil::unique_handle instanceHandle;\n} LXSS_RUN_ELF_CONTEXT, *PLXSS_RUN_ELF_CONTEXT;\n\ntypedef struct _LXSS_VM_MODE_SETUP_CONTEXT\n{\n    wil::unique_socket tarSocket;\n    wil::unique_socket errorSocket;\n    std::shared_ptr<LxssRunningInstance> instance;\n} LXSS_VM_MODE_SETUP_CONTEXT, *PLXSS_VM_MODE_SETUP_CONTEXT;\n\nenum class ShutdownBehavior\n{\n    Wait,\n    Force,\n    ForceAfter30Seconds\n};\n\n/// <summary>\n/// Each COM client gets a unique LxssUserSession object which contains a std::weak_ptr to a LxssUserSessionImpl for that user.\n/// </summary>\n\nclass DECLSPEC_UUID(\"a9b7a1b9-0671-405c-95f1-e0612cb4ce7e\") LxssUserSession\n    : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, ILxssUserSession, IWslSupport, IFastRundown>\n{\npublic:\n    LxssUserSession(_In_ const std::weak_ptr<LxssUserSessionImpl>& Session);\n    LxssUserSession(const LxssUserSession&) = delete;\n    LxssUserSession& operator=(const LxssUserSession&) = delete;\n\n    /// <summary>\n    /// Configures a distribution.\n    /// </summary>\n    IFACEMETHOD(ConfigureDistribution)(_In_opt_ LPCGUID DistroGuid, _In_ ULONG DefaultUid, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Creates an instance of the specified distro. The method blocks until the\n    /// new instance is started.\n    /// </summary>\n    IFACEMETHOD(CreateInstance)(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Create a Linux process.\n    /// </summary>\n    IFACEMETHOD(CreateLxProcess)(\n        _In_opt_ LPCGUID DistroGuid,\n        _In_opt_ LPCSTR Filename,\n        _In_ ULONG CommandLineCount,\n        _In_reads_opt_(CommandLineCount) LPCSTR* CommandLine,\n        _In_opt_ LPCWSTR CurrentWorkingDirectory,\n        _In_opt_ LPCWSTR NtPath,\n        _In_reads_opt_(NtEnvironmentLength) PWCHAR NtEnvironment,\n        _In_ ULONG NtEnvironmentLength,\n        _In_opt_ LPCWSTR Username,\n        _In_ SHORT Columns,\n        _In_ SHORT Rows,\n        _In_ ULONG ConsoleHandle,\n        _In_ PLXSS_STD_HANDLES StdHandles,\n        _In_ ULONG Flags,\n        _Out_ GUID* DistributionId,\n        _Out_ GUID* InstanceId,\n        _Out_ HANDLE* ProcessHandle,\n        _Out_ HANDLE* ServerHandle,\n        _Out_ HANDLE* StandardIn,\n        _Out_ HANDLE* StandardOut,\n        _Out_ HANDLE* StandardErr,\n        _Out_ HANDLE* CommunicationChannel,\n        _Out_ HANDLE* InteropSocket,\n        _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Enumerates all registered distributions.\n    /// </summary>\n    IFACEMETHOD(EnumerateDistributions)(_Out_ PULONG DistributionCount, _Out_ LXSS_ENUMERATE_INFO** Distributions, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Exports a distribution from to tar file.\n    /// </summary>\n    IFACEMETHOD(ExportDistribution)(\n        _In_opt_ LPCGUID DistroGuid, _In_ HANDLE FileHandle, _In_ HANDLE ErrorHandle, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Exports a distribution to a pipe.\n    /// </summary>\n    IFACEMETHOD(ExportDistributionPipe)(\n        _In_opt_ LPCGUID DistroGuid, _In_ HANDLE PipeHandle, _In_ HANDLE ErrorHandle, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Queries the default distribution.\n    /// </summary>\n    IFACEMETHOD(GetDefaultDistribution)(_Out_ LXSS_ERROR_INFO* Error, _Out_ LPGUID DefaultDistribution) override;\n\n    /// <summary>\n    /// Returns the configuration for the specified distribution.\n    /// </summary>\n    IFACEMETHOD(GetDistributionConfiguration)(\n        _In_opt_ LPCGUID DistroGuid,\n        _Out_ LPWSTR* DistributionName,\n        _Out_ ULONG* Version,\n        _Out_ ULONG* DefaultUid,\n        _Out_ ULONG* DefaultEnvironmentCount,\n        _Out_ LPSTR** DefaultEnvironment,\n        _Out_ ULONG* Flags,\n        _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Returns the GUID of a distribution with the specified name.\n    /// </summary>\n    IFACEMETHOD(GetDistributionId)(_In_ LPCWSTR DistributionName, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error, _Out_ GUID* pDistroGuid) override;\n\n    /// <summary>\n    /// Registers a distribution from a tar file.\n    /// </summary>\n    IFACEMETHOD(RegisterDistribution)(\n        _In_ LPCWSTR DistributionName,\n        _In_ ULONG Version,\n        _In_ HANDLE FileHandle,\n        _In_ HANDLE ErrorHandle,\n        _In_ LPCWSTR TargetDirectory,\n        _In_ ULONG Flags,\n        _In_ ULONG64 VhdSize,\n        _In_opt_ LPCWSTR PackageFamilyName,\n        _Out_ LPWSTR* InstalledDistributionName,\n        _Out_ LXSS_ERROR_INFO* Error,\n        _Out_ GUID* pDistroGuid) override;\n\n    /// <summary>\n    /// Registers a distribution from a pipe.\n    /// </summary>\n    IFACEMETHOD(RegisterDistributionPipe)(\n        _In_ LPCWSTR DistributionName,\n        _In_ ULONG Version,\n        _In_ HANDLE PipeHandle,\n        _In_ HANDLE ErrorHandle,\n        _In_ LPCWSTR TargetDirectory,\n        _In_ ULONG Flags,\n        _In_ ULONG64 VhdSize,\n        _In_opt_ LPCWSTR PackageFamilyName,\n        _Out_ LPWSTR* InstalledDistributionName,\n        _Out_ LXSS_ERROR_INFO* Error,\n        _Out_ GUID* pDistroGuid) override;\n\n    /// <summary>\n    /// Resizes the virtual disk of a distribution.\n    /// </summary>\n    IFACEMETHOD(ResizeDistribution)(_In_ LPCGUID DistroGuid, _In_ HANDLE OutputHandle, _In_ ULONG64 NewSize, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Sets the default distribution.\n    /// </summary>\n    IFACEMETHOD(SetDefaultDistribution)(_In_ LPCGUID DistroGuid, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Sets or unsets the sparse flag for a distribution.\n    /// </summary>\n    IFACEMETHOD(SetSparse)(_In_ LPCGUID DistroGuid, _In_ BOOLEAN Sparse, _In_ BOOLEAN AllowUnsafe, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Sets the version for a distribution.\n    /// </summary>\n    IFACEMETHOD(SetVersion)(_In_ LPCGUID DistroGuid, _In_ ULONG Version, _In_ HANDLE StdErrHandle, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Pass through a disk to the utility VM.\n    /// </summary>\n    IFACEMETHOD(AttachDisk)(_In_ LPCWSTR Disk, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Detach a passthrough disk from the utility VM.\n    /// </summary>\n    IFACEMETHOD(DetachDisk)(_In_ LPCWSTR Disk, _Out_ int* Result, _Out_ int* Step, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Mount a disk.\n    /// </summary>\n    IFACEMETHOD(MountDisk)(\n        _In_ LPCWSTR Disk,\n        _In_ ULONG Flags,\n        _In_ ULONG PartitionIndex,\n        _In_opt_ LPCWSTR Name,\n        _In_opt_ LPCWSTR Type,\n        _In_opt_ LPCWSTR Options,\n        _Out_ int* Result,\n        _Out_ int* Step,\n        _Out_ LPWSTR* MountName,\n        _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Move a distribution to a new location\n    /// </summary>\n    IFACEMETHOD(MoveDistribution)(_In_ LPCGUID DistroGuid, _In_ LPCWSTR Location, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Terminates all running instances and the Linux utility vm.\n    /// </summary>\n    IFACEMETHOD(Shutdown)() override;\n    IFACEMETHOD(Shutdown)(_In_ BOOL Force);\n\n    /// <summary>\n    /// Imports a distribution inplace.\n    /// </summary>\n    IFACEMETHOD(ImportDistributionInplace)(_In_ LPCWSTR DistributionName, _In_ LPCWSTR VhdPath, _Out_ LXSS_ERROR_INFO* Error, _Out_ GUID* pDistroGuid) override;\n\n    /// <summary>\n    /// Terminates a distribution by it's client identifier.\n    /// </summary>\n    void TerminateByClientId(_In_ ULONG ClientId);\n\n    /// <summary>\n    /// Sets the execution state of this instance.\n    /// </summary>\n    IFACEMETHOD(TerminateDistribution)(_In_opt_ LPCGUID DistroGuid, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    /// <summary>\n    /// Unregisters a distribution.\n    /// </summary>\n    IFACEMETHOD(UnregisterDistribution)(_In_ LPCGUID DistroGuid, _Out_ LXSS_ERROR_INFO* Error) override;\n\n    // IWslSupport methods.\n\n    /// <summary>\n    /// Registers a distribution.\n    /// </summary>\n    HRESULT RegisterDistribution(\n        _Inout_ LPCWSTR DistributionName, _In_ ULONG Version, _In_opt_ HANDLE TarGzFile, _In_opt_ HANDLE TarGzPipe, _In_ LPCWSTR TargetDirectory) override;\n\n    /// <summary>\n    /// Unregisters a distribution.\n    /// </summary>\n    HRESULT\n    UnregisterDistribution(_In_ LPCWSTR DistributionName) override;\n\n    /// <summary>\n    /// Returns the configuration for the specified distribution.\n    /// </summary>\n    HRESULT GetDistributionConfiguration(\n        _In_ LPCWSTR DistributionName,\n        _Out_ ULONG* Version,\n        _Out_ ULONG* DefaultUid,\n        _Out_ ULONG* DefaultEnvironmentCount,\n        _Out_ LPSTR** DefaultEnvironment,\n        _Out_ ULONG* WslFlags) override;\n\n    /// <summary>\n    /// Configures a distribution.\n    /// </summary>\n    HRESULT SetDistributionConfiguration(_In_ LPCWSTR DistributionName, _In_ ULONG DefaultUid, _In_ ULONG WslFlags) override;\n\n    /// <summary>\n    /// Returns a list of runnable distributions.\n    /// </summary>\n    HRESULT ListDistributions(_Out_ ULONG* Count, _Out_ LPWSTR** Distributions) override;\n\n    /// <summary>\n    /// Creates an instance of the specified distro.\n    /// </summary>\n    HRESULT CreateInstance(_In_ LPCWSTR DistributionName, _In_ ULONG Flags) override;\n\nprivate:\n    std::weak_ptr<LxssUserSessionImpl> m_session;\n};\n\n/// <summary>\n/// Each user gets its own LxssUserSessionImpl object, This object manages the lifetime of running instances.\n/// </summary>\nclass LxssUserSessionImpl\n{\npublic:\n    LxssUserSessionImpl(_In_ PSID userSid, _In_ DWORD sessionId, _Inout_ wsl::windows::service::PluginManager& pluginManager);\n    virtual ~LxssUserSessionImpl();\n    LxssUserSessionImpl(const LxssUserSessionImpl&) = delete;\n    LxssUserSessionImpl& operator=(const LxssUserSessionImpl&) = delete;\n\n    /// <summary>\n    /// Configures a distribution.\n    /// </summary>\n    HRESULT\n    ConfigureDistribution(_In_opt_ LPCGUID DistroGuid, _In_ ULONG DefaultUid, _In_ ULONG Flags);\n\n    /// <summary>\n    /// Creates an instance of the specified distro. The method blocks until the\n    /// new instance is started.\n    /// </summary>\n    HRESULT\n    CreateInstance(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags);\n\n    /// <summary>\n    /// Create a Linux process.\n    /// </summary>\n    HRESULT\n    CreateLxProcess(\n        _In_opt_ LPCGUID DistroGuid,\n        _In_opt_ LPCSTR Filename,\n        _In_ ULONG CommandLineCount,\n        _In_reads_opt_(CommandLineCount) LPCSTR* CommandLine,\n        _In_opt_ LPCWSTR CurrentWorkingDirectory,\n        _In_opt_ LPCWSTR NtPath,\n        _In_reads_opt_(NtEnvironmentLength) PWCHAR NtEnvironment,\n        _In_ ULONG NtEnvironmentLength,\n        _In_opt_ LPCWSTR Username,\n        _In_ SHORT Columns,\n        _In_ SHORT Rows,\n        _In_ HANDLE ConsoleHandle,\n        _In_ PLXSS_STD_HANDLES StdHandles,\n        _In_ ULONG Flags,\n        _Out_ GUID* DistributionId,\n        _Out_ GUID* InstanceId,\n        _Out_ HANDLE* ProcessHandle,\n        _Out_ HANDLE* ServerHandle,\n        _Out_ HANDLE* StandardIn,\n        _Out_ HANDLE* StandardOut,\n        _Out_ HANDLE* StandardErr,\n        _Out_ HANDLE* CommunicationChannel,\n        _Out_ HANDLE* InteropSocket);\n\n    /// <summary>\n    /// Clears the state of an attached disk in the registry\n    /// </summary>\n    void ClearDiskStateInRegistry(_In_opt_ LPCWSTR Disk);\n\n    /// <summary>\n    /// Start a process in the root namespace or in a user distribution.\n    /// </summary>\n    HRESULT CreateLinuxProcess(_In_opt_ const GUID* Distro, _In_ LPCSTR Path, _In_ LPCSTR* Arguments, _Out_ SOCKET* socket);\n\n    /// <summary>\n    /// Enumerates registered distributions, optionally including ones that are\n    /// currently being registered, unregistered, or converted.\n    /// </summary>\n    HRESULT\n    EnumerateDistributions(_Out_ PULONG DistributionCount, _Out_ LXSS_ENUMERATE_INFO** Distributions);\n\n    /// <summary>\n    /// Exports a distribution.\n    /// </summary>\n    HRESULT\n    ExportDistribution(_In_opt_ LPCGUID DistroGuid, _In_ HANDLE FileHandle, _In_ HANDLE ErrorHandle, _In_ ULONG Flags);\n\n    /// <summary>\n    /// Queries the default distribution.\n    /// </summary>\n    HRESULT\n    GetDefaultDistribution(_Out_ LPGUID DefaultDistribution);\n\n    /// <summary>\n    /// Returns the configuration for the specified distribution.\n    /// </summary>\n    HRESULT\n    GetDistributionConfiguration(\n        _In_opt_ LPCGUID DistroGuid,\n        _Out_ LPWSTR* DistributionName,\n        _Out_ ULONG* Version,\n        _Out_ ULONG* DefaultUid,\n        _Out_ ULONG* DefaultEnvironmentCount,\n        _Out_ LPSTR** DefaultEnvironment,\n        _Out_ ULONG* Flags);\n\n    /// <summary>\n    /// Returns the GUID of a distribution with the specified name.\n    /// </summary>\n    HRESULT\n    GetDistributionId(_In_ LPCWSTR DistributionName, _In_ ULONG Flags, _Out_ GUID* pDistroGuid);\n\n    /// <summary>\n    /// Returns the session cookie\n    /// </summary>\n    DWORD GetSessionCookie() const;\n\n    /// <summary>\n    /// Returns the session ID of the user.\n    /// </summary>\n    DWORD GetSessionId() const;\n\n    /// <summary>\n    /// Returns the sid for the user session.\n    /// </summary>\n    PSID GetUserSid();\n\n    /// <summary>\n    /// Imports a distribution inplace.\n    /// </summary>\n    HRESULT\n    ImportDistributionInplace(_In_ LPCWSTR DistributionName, _In_ LPCWSTR VhdPath, _Out_ GUID* pDistroGuid);\n\n    /// <summary>\n    /// Mount a disk.\n    /// </summary>\n    HRESULT MountDisk(\n        _In_ LPCWSTR Disk,\n        _In_ ULONG Flags,\n        _In_ ULONG PartitionIndex,\n        _In_opt_ LPCWSTR Name,\n        _In_opt_ LPCWSTR Type,\n        _In_opt_ LPCWSTR Options,\n        _Out_ int* Result,\n        _Out_ int* Step,\n        _Out_ LPWSTR* MountName);\n\n    HRESULT MoveDistribution(_In_ LPCGUID DistroGuid, _In_ LPCWSTR Location);\n\n    HRESULT MountRootNamespaceFolder(_In_ LPCWSTR HostPath, _In_ LPCWSTR GuestPath, _In_ bool ReadOnly, _In_ LPCWSTR Name);\n\n    /// <summary>\n    /// Registers a distribution.\n    /// </summary>\n    HRESULT\n    RegisterDistribution(\n        _In_ LPCWSTR DistributionName,\n        _In_ ULONG Version,\n        _In_ HANDLE FileHandle,\n        _In_ HANDLE ErrorHandle,\n        _In_ LPCWSTR TargetDirectory,\n        _In_ ULONG Flags,\n        _In_ ULONG64 VhdSize,\n        _In_opt_ LPCWSTR PackageFamilyName,\n        _Out_opt_ LPWSTR* InstalledDistributionName,\n        _Out_ GUID* pDistroGuid);\n\n    /// <summary>\n    /// Resizes the disk of a distribution.\n    /// </summary>\n    HRESULT\n    ResizeDistribution(_In_ LPCGUID DistroGuid, _In_ HANDLE OutputHandle, _In_ ULONG64 NewSize);\n\n    /// <summary>\n    /// Sets the default distribution.\n    /// </summary>\n    HRESULT\n    SetDefaultDistribution(_In_ LPCGUID DistroGuid);\n\n    /// <summary>\n    /// Marks/unmarks the backing vhdx as sparse.\n    /// </summary>\n    HRESULT\n    SetSparse(_In_ LPCGUID DistroGuid, _In_ BOOLEAN Sparse, _In_ BOOLEAN AllowUnsafe);\n\n    /// <summary>\n    /// Sets the version for a distribution.\n    /// </summary>\n    HRESULT\n    SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Version, _In_ HANDLE StdErrHandle);\n\n    /// <summary>\n    /// Pass through a disk to the utility VM.\n    /// </summary>\n    HRESULT AttachDisk(_In_ LPCWSTR Disk, _In_ ULONG Flags);\n\n    /// <summary>\n    /// Detach a passthrough disk from the utility VM.\n    /// </summary>\n    HRESULT DetachDisk(_In_ LPCWSTR Disk, _Out_ int* Result, _Out_ int* Step);\n\n    /// <summary>\n    /// Terminates all running instances and the Linux utility vm.\n    /// </summary>\n    HRESULT Shutdown(_In_ bool PreventNewInstances = false, ShutdownBehavior Behavior = ShutdownBehavior::Wait);\n\n    /// <summary>\n    /// Worker thread for logging telemetry about processes running inside of WSL.\n    /// </summary>\n    void TelemetryWorker(_In_ wil::unique_socket&& socket) const;\n\n    /// <summary>\n    /// Terminates a distribution by it's client identifier.\n    /// </summary>\n    void TerminateByClientId(_In_ ULONG ClientId);\n\n    /// <summary>\n    /// Terminates a distribution by it's client identifier (assumes lock is held).\n    /// </summary>\n    void TerminateByClientIdLockHeld(_In_ ULONG ClientId);\n\n    /// <summary>\n    /// Sets the execution state of this instance.\n    /// </summary>\n    HRESULT\n    TerminateDistribution(_In_opt_ LPCGUID DistroGuid);\n\n    /// <summary>\n    /// Unregisters a distribution.\n    /// </summary>\n    HRESULT\n    UnregisterDistribution(_In_ LPCGUID DistroGuid);\n\n    /// <summary>\n    /// Queries a distribution's default UID, default environment, and flags.\n    /// </summary>\n    static CreateLxProcessContext s_GetCreateProcessContext(_In_ const GUID& DistroGuid, _In_ bool SystemDistro);\n\nprivate:\n    /// <summary>\n    /// Adds a distro to the list of converting distros.\n    /// </summary>\n    _Requires_lock_held_(m_instanceLock)\n    void _ConversionBegin(_In_ GUID DistroGuid, _In_ LxssDistributionState State);\n\n    /// <summary>\n    /// Removes a distro from the list of converting distros and checks if the\n    /// Linux utility VM is idle.\n    /// </summary>\n    _Requires_lock_not_held_(m_instanceLock)\n    void _ConversionComplete(_In_ GUID DistroGuid);\n\n    /// <summary>\n    /// Creates a distribution registration for legacy installs.\n    /// </summary>\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    void _CreateLegacyRegistration(_In_ HKEY LxssKey, _In_ HANDLE UserToken);\n\n    /// <summary>\n    /// Creates the set of WSL mounts required for setup and ext4 conversion.\n    /// </summary>\n    static std::vector<wsl::windows::common::filesystem::unique_lxss_addmount> _CreateSetupMounts(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration);\n\n    /// <summary>\n    /// Creates and initializes a utility VM. If a VM is already running, the\n    /// running VM is returned.\n    /// </summary>\n    _Requires_lock_not_held_(m_instanceLock)\n    std::shared_ptr<LxssRunningInstance> _CreateInstance(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags = LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE);\n\n    static void _CreateDistributionShortcut(\n        _In_ LPCWSTR DistributionName, LPCWSTR Shortcut, LPCWSTR ExecutablePath, wsl::windows::service::DistributionRegistration& registration);\n\n    static void _CreateTerminalProfile(\n        _In_ const std::string_view& Template,\n        _In_ const std::filesystem::path& IconPath,\n        _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n        wsl::windows::service::DistributionRegistration& Registration);\n\n    /// <summary>\n    /// Ensures that the utility VM has been created.\n    /// </summary>\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    void _CreateVm();\n\n    /// <summary>\n    /// Deletes distribution filesystem.\n    /// </summary>\n    void _DeleteDistribution(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration, _In_ ULONG Flags = LXSS_DELETE_DISTRO_FLAGS_ALL);\n\n    /// <summary>\n    /// Deletes distribution filesystem.\n    /// </summary>\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    void _DeleteDistributionLockHeld(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration, _In_ ULONG Flags = LXSS_DELETE_DISTRO_FLAGS_ALL) const;\n\n    /// <summary>\n    /// Enumerates and validates all registered distributions for the calling\n    /// process. If ListAll is true this will return all distributions including\n    /// those that are in the progress of being installed or uninstalled.\n    /// </summary>\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    std::vector<wsl::windows::service::DistributionRegistration> _EnumerateDistributions(\n        _In_ HKEY LxssKey, _In_ bool ListAll = false, _In_ const std::optional<GUID>& Exclude = {});\n\n    /// <summary>\n    /// Validates that the specified distribution is not currently performing\n    /// a filesystem conversion.\n    /// </summary>\n    _Requires_lock_held_(m_instanceLock)\n    void _EnsureNotLocked(_In_ LPCGUID DistroGuid, const std::source_location& location = std::source_location::current());\n\n    /// <summary>\n    /// Queries the GUID of the default distribution for the calling process.\n    /// </summary>\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    GUID _GetDefaultDistro(_In_ HKEY LxssKey);\n\n    /// <summary>\n    /// Waits for the elf binary to exit and returns the exit status.\n    /// </summary>\n    static LONG _GetElfExitStatus(_In_ const LXSS_RUN_ELF_CONTEXT& Context);\n\n    /// <summary>\n    /// Return a new config after policies have been applied.\n    /// </summary>\n    wsl::core::Config _GetResultantConfig(_In_ const HANDLE userToken);\n\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    void _LoadDiskMount(_In_ HKEY Key, _In_ const std::wstring& LunStr) const;\n\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    void _LoadDiskMounts();\n\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    void _LoadNetworkingSettings(_Inout_ wsl::core::Config& config, _In_ HANDLE userToken);\n\n    void _ProcessImportResultMessage(\n        const LX_MINI_INIT_IMPORT_RESULT& message,\n        const gsl::span<gsl::byte> span,\n        HKEY LxssKey,\n        LXSS_DISTRO_CONFIGURATION& configuration,\n        wsl::windows::service::DistributionRegistration& registration);\n\n    /// <summary>\n    /// Runs a single ELF binary without using the init daemon.\n    /// </summary>\n    static LXSS_RUN_ELF_CONTEXT _RunElfBinary(\n        _In_ LPCSTR CommandLine,\n        _In_ LPCWSTR TargetDirectory,\n        _In_ HANDLE ClientProcess,\n        _In_opt_ HANDLE StdIn = nullptr,\n        _In_opt_ HANDLE StdOut = nullptr,\n        _In_opt_ HANDLE StdErr = nullptr,\n        _In_opt_count_(NumMounts) PLX_KMAPPATHS_ADDMOUNT Mounts = nullptr,\n        _In_opt_ ULONG NumMounts = 0);\n\n    /// <summary>\n    /// Returns the currently running utility vm, if one exists.\n    /// </summary>\n    _Requires_lock_held_(m_instanceLock)\n    std::shared_ptr<LxssRunningInstance> _RunningInstance(_In_ LPCGUID DistroGuid);\n\n    /// <summary>\n    /// Creates a utility VM to perform a setup operation.\n    /// </summary>\n    LXSS_VM_MODE_SETUP_CONTEXT\n    _RunUtilityVmSetup(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration, _In_ LX_MESSAGE_TYPE MessageType, _In_ ULONG ExportFlags = 0, _In_ bool SetVersion = false);\n\n    void _SendDistributionRegisteredEvent(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration) const;\n\n    /// <summary>\n    /// Set the specified distribution as installed. If there is no default distribution\n    /// this routine also marks this distribution as the default.\n    /// </summary>\n    _Requires_lock_held_(m_instanceLock)\n    static void _SetDistributionInstalled(_In_ HKEY LxssKey, _In_ const GUID& DistroGuid);\n\n    /// <summary>\n    /// Removes a utility vm from the list.\n    /// </summary>\n    _Requires_lock_not_held_(m_instanceLock)\n    bool _TerminateInstance(_In_ LPCGUID DistroGuid, _In_ bool CheckForClients);\n\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    bool _TerminateInstanceInternal(_In_ LPCGUID DistroGuid, _In_ bool CheckForClients = false);\n\n    /// <summary>\n    /// Ensures the WSL1 init binary is up-to-date.\n    /// </summary>\n    wil::srwlock m_initUpdateLock;\n    _Guarded_by_(m_initUpdateLock) std::vector<GUID> m_updatedInitDistros;\n    void _UpdateInit(_In_ const LXSS_DISTRO_CONFIGURATION& Configuration);\n\n    /// <summary>\n    /// Unregisters the specified distribution.\n    /// </summary>\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    void _UnregisterDistributionLockHeld(_In_ HKEY LxssKey, _In_ const LXSS_DISTRO_CONFIGURATION& Configuration);\n\n    /// <summary>\n    /// Updates timezone information for each running instance and utility VM.\n    /// </summary>\n    void _TimezoneUpdated();\n\n    /// <summary>\n    /// Validates if the package for a specified distribution is still installed.\n    /// </summary>\n    _Requires_lock_held_(m_instanceLock)\n    static bool _ValidateDistro(_In_ HKEY LxssKey, _In_ LPCGUID DistroGuid);\n\n    /// <summary>\n    /// Validates that the given path or name is not already in use by a registered distribution.\n    /// </summary>\n    _Requires_lock_held_(m_instanceLock)\n    void _ValidateDistributionNameAndPathNotInUse(\n        _In_ HKEY LxssKey, _In_opt_ LPCWSTR Path, _In_opt_ LPCWSTR Name, const std::optional<GUID>& Exclude = {});\n\n    /// <summary>\n    /// Queues a threadpool timer to terminate an idle utility VM.\n    /// </summary>\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    void _VmCheckIdle();\n\n    /// <summary>\n    /// Terminate the Linux utility VM if there are no running distros.\n    /// </summary>\n    _Requires_lock_not_held_(m_instanceLock)\n    void _VmIdleTerminate();\n\n    /// <summary>\n    /// Queries if the Linux utility VM has any running distros.\n    /// </summary>\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    bool _VmIsIdle();\n\n    /// <summary>\n    /// Terminates the Linux utility VM.\n    /// </summary>\n    _Requires_exclusive_lock_held_(m_instanceLock)\n    void _VmTerminate();\n\n    /// <summary>\n    /// Configures HttpProxy info for the process\n    /// </summary>\n    void _SetHttpProxyInfo(_Inout_ std::vector<std::string>& environment) const noexcept;\n\n    /// <summary>\n    /// Launch OOBE for the first time or skip.\n    /// </summary>\n    void _LaunchOOBEIfNeeded() noexcept;\n\n    /// <summary>\n    /// Inputs proxy environment values into an environment if they're not already present.\n    /// </summary>\n    static void s_AddHttpProxyToEnvironment(_In_ const HttpProxySettings& proxySettings, _Inout_ std::vector<std::string>& environment) noexcept;\n\n    /// <summary>\n    /// Query information about a distribution config.\n    /// </summary>\n    static LXSS_DISTRO_CONFIGURATION s_GetDistributionConfiguration(const wsl::windows::service::DistributionRegistration& Distro, bool skipName = false);\n\n    /// <summary>\n    /// Impersonate the user and open the lxss registry key\n    /// </summary>\n    static wil::unique_hkey s_OpenLxssUserKey();\n\n    /// <summary>\n    /// Ensures the distribution name is valid.\n    /// </summary>\n    static void s_ValidateDistroName(_In_ LPCWSTR Name);\n\n    /// <summary>\n    /// Callback for when a VM unexpectedly exits (crashes).\n    /// </summary>\n    static void s_VmTerminated(_Inout_ LxssUserSessionImpl* UserSession, _In_ const GUID& VmId);\n\n    /// <summary>\n    /// Callback to terminate a utility VM.\n    /// </summary>\n    static bool s_TerminateInstance(_Inout_ LxssUserSessionImpl* UserSession, _In_ GUID DistroGuid, _In_ bool CheckForClients);\n\n    /// <summary>\n    /// Ensures that the init binary for the specified distribution is up-to-date.\n    /// </summary>\n    static void s_UpdateInit(_Inout_ LxssUserSessionImpl* UserSession, _In_ const LXSS_DISTRO_CONFIGURATION& Configuration);\n\n    /// <summary>\n    /// Callback to determine if the Linux VM can terminate.\n    /// </summary>\n    static VOID CALLBACK s_VmIdleTerminate(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER Timer);\n\n    static LRESULT CALLBACK s_TimezoneWindowProc(HWND windowHandle, UINT messageCode, WPARAM wParameter, LPARAM lParameter);\n\n    /// <summary>\n    /// Lock for protecting various lists.\n    /// </summary>\n    std::recursive_timed_mutex m_instanceLock;\n\n    /// <summary>\n    /// Contains the currently running utility VM's.\n    /// </summary>\n    _Guarded_by_(m_instanceLock) std::map<GUID, std::shared_ptr<LxssRunningInstance>, wsl::windows::common::helpers::GuidLess> m_runningInstances;\n\n    /// <summary>\n    /// Contains a list of instances that have been terminated.\n    /// </summary>\n    wil::srwlock m_terminatedInstanceLock;\n    _Guarded_by_(m_terminatedInstanceLock) std::list<std::shared_ptr<LxssRunningInstance>> m_terminatedInstances;\n\n    /// <summary>\n    /// Contains a list of distribution are toggling VM mode.\n    /// </summary>\n    _Guarded_by_(m_instanceLock) std::list<std::pair<GUID, LxssDistributionState>> m_lockedDistributions;\n\n    /// <summary>\n    /// The running utility vm for WSL2 distributions.\n    ///\n    _Guarded_by_(m_instanceLock) std::unique_ptr<WslCoreVm> m_utilityVm;\n\n    std::atomic<GUID> m_vmId{GUID_NULL};\n\n    /// <summary>\n    /// Contains the user sid for the session.\n    /// </summary>\n    SE_SID m_userSid;\n\n    /// <summary>\n    /// Contains the session ID.\n    /// </summary>\n    DWORD m_sessionId;\n\n    /// <summary>\n    /// Class to keep track of any client processes.\n    /// </summary>\n    LifetimeManager m_lifetimeManager;\n\n    /// <summary>\n    /// Timer to control terminating the Linux utility VM.\n    /// </summary>\n    wil::unique_threadpool_timer m_vmTerminationTimer;\n\n    /// <summary>\n    /// Signaled when the utility vm is terminating.\n    /// </summary>\n    wil::unique_event m_vmTerminating{wil::EventOptions::ManualReset};\n\n    /// <summary>\n    /// Thread for logging usage telemetry from the WSL VM.\n    /// </summary>\n    std::thread m_telemetryThread;\n\n    /// <summary>\n    /// True when this session shouldn't allow creating new instances.\n    /// (Used when the session is being deleted).\n    /// </summary>\n    bool m_disableNewInstanceCreation = false;\n\n    /// <summary>\n    /// The user's token. Shared with WslCoreVm and plugins.\n    /// </summary>\n    wil::shared_handle m_userToken;\n\n    WSLSessionInformation m_session{};\n\n    wsl::windows::service::PluginManager& m_pluginManager;\n\n    /// <summary>\n    /// Listens to host http proxy setting changes, tracks ongoing proxy queries, and stores current settings.\n    /// Information used to configure Linux proxy environment variables.\n    /// </summary>\n    std::shared_ptr<HttpProxyStateTracker> m_httpProxyStateTracker{};\n\n    std::thread m_timezoneThread;\n};\n\n#define LXSS_CLIENT_ID_WILDCARD ((ULONG)0)\n#define LXSS_CLIENT_ID_INVALID ((ULONG) - 1)\n"
  },
  {
    "path": "src/windows/service/exe/LxssUserSessionFactory.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssUserSessionFactory.cpp\n\nAbstract:\n\n    This file contains user session factory function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"LxssSecurity.h\"\n#include \"LxssUserSessionFactory.h\"\n#include \"PluginManager.h\"\n\nusing namespace Microsoft::WRL;\nusing namespace Security;\nusing namespace wil;\n\nbool g_disabledByPolicy{false};\n\n// Note: g_sessionTerminationLock must always be acquired before g_sessionLock\nstd::recursive_mutex g_sessionTerminationLock;\nsrwlock g_sessionLock;\n\nstd::optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>> g_sessions =\n    std::make_optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>>();\n\nstd::optional<wsl::windows::service::PluginManager> g_pluginManager;\n\nextern unique_event g_networkingReady;\nextern bool g_lxcoreInitialized;\n\n_Requires_lock_held_(g_sessionLock)\nvoid ClearSessionsAndBlockNewInstancesLockHeld(std::optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>>& sessions)\n{\n    std::lock_guard lock(g_sessionTerminationLock);\n\n    if (sessions)\n    {\n        // Shutdown the session and prevent new session creation.\n        for (const auto& session : sessions.value())\n        {\n            // Because Shutdown() acquires the session inner lock, it shouldn't called while g_sessionLock is held,\n            // since that could lead to a deadlock if FindSessionByCookie is called since that would try to lock g_sessionLock\n            // while holding the session inner lock\n\n            session->Shutdown(true, ShutdownBehavior::ForceAfter30Seconds);\n        }\n\n        sessions.reset();\n    }\n\n    // Unload plugins\n    g_pluginManager.reset();\n}\n\nvoid ClearSessionsAndBlockNewInstances()\n{\n    std::optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>> sessions;\n\n    {\n        auto sessionsLock = g_sessionLock.lock_exclusive();\n        sessions = std::move(g_sessions);\n\n        // This is required because the moved-from std::optional<T> isn't made empty, so this needs to be done explicitly.\n        g_sessions.reset();\n    }\n\n    ClearSessionsAndBlockNewInstancesLockHeld(sessions);\n}\n\nvoid SetSessionPolicy(_In_ bool enabled)\n{\n    std::lock_guard lock(g_sessionTerminationLock);\n\n    if (enabled)\n    {\n        auto sessionsLock = g_sessionLock.lock_exclusive();\n        if (!g_sessions)\n        {\n            g_sessions = std::make_optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>>();\n        }\n\n        if (!g_pluginManager.has_value())\n        {\n            g_pluginManager.emplace();\n            g_pluginManager->LoadPlugins();\n        }\n    }\n    else\n    {\n        ClearSessionsAndBlockNewInstances();\n    }\n\n    g_disabledByPolicy = !enabled;\n}\n\nstd::shared_ptr<LxssUserSessionImpl> FindSessionByCookie(_In_ DWORD Cookie)\n{\n    // Find a session with a matching session ID and terminate it.\n    //\n    // N.B. Sessions launched from session zero will only be terminated when the\n    //      service is stopped.\n    auto lock = g_sessionLock.lock_exclusive();\n    if (!g_sessions.has_value())\n    {\n        return {};\n    }\n\n    const auto found = std::find_if(std::begin(g_sessions.value()), std::end(g_sessions.value()), [Cookie](const auto& session) {\n        return (session->GetSessionCookie() == Cookie);\n    });\n\n    return found == g_sessions->end() ? std::shared_ptr<LxssUserSessionImpl>() : *found;\n}\n\nvoid TerminateSession(_In_ DWORD sessionId)\n{\n    // Find a session with a matching session ID and terminate it.\n    //\n    // N.B. Sessions launched from session zero will only be terminated when the\n    //      service is stopped.\n\n    std::lock_guard lock(g_sessionTerminationLock);\n\n    std::shared_ptr<LxssUserSessionImpl> session;\n\n    {\n        auto lock = g_sessionLock.lock_exclusive();\n        if (!g_sessions.has_value())\n        {\n            return;\n        }\n\n        const auto found = std::find_if(std::begin(g_sessions.value()), std::end(g_sessions.value()), [&sessionId](const auto& session) {\n            return (session->GetSessionId() == sessionId);\n        });\n\n        if (found != g_sessions->end())\n        {\n            // Shutdown the session and prevent new instance creation.\n            session = std::move(*found);\n            g_sessions->erase(found);\n        }\n    }\n\n    if (session)\n    {\n        session->Shutdown(true);\n    }\n}\n\nCoCreatableClassWithFactory(LxssUserSession, LxssUserSessionFactory);\n\nHRESULT LxssUserSessionFactory::CreateInstance(_In_ IUnknown* pUnkOuter, _In_ REFIID riid, _Out_ void** ppCreated)\n{\n    RETURN_HR_IF_NULL(E_POINTER, ppCreated);\n    *ppCreated = nullptr;\n\n    RETURN_HR_IF(CLASS_E_NOAGGREGATION, pUnkOuter != nullptr);\n\n    WSL_LOG(\"LxssUserSessionCreateInstanceBegin\", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));\n\n    // Wait for the network cleanup to be done before continuing.\n    g_networkingReady.wait();\n\n    try\n    {\n        auto instance = CreateInstanceForCurrentUser();\n        const auto userSession = wil::MakeOrThrow<LxssUserSession>(instance);\n        THROW_IF_FAILED(userSession.CopyTo(riid, ppCreated));\n    }\n    catch (...)\n    {\n        const auto result = wil::ResultFromCaughtException();\n\n        // Note: S_FALSE will cause COM to retry if the service is stopping.\n        return result == CO_E_SERVER_STOPPING ? S_FALSE : result;\n    }\n\n    WSL_LOG(\"LxssUserSessionCreateInstanceEnd\", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));\n\n    return S_OK;\n}\n\n_Requires_lock_held_(g_sessionLock)\nstd::shared_ptr<LxssUserSessionImpl> FindSessionLockHeld(PSID User)\n{\n    // Fail if the service is stopping (see ClearSessionsAndBlockNewInstances()).\n    THROW_HR_IF(CO_E_SERVER_STOPPING, !g_sessions.has_value());\n\n    const auto found = std::find_if(std::begin(g_sessions.value()), std::end(g_sessions.value()), [&](const auto& session) {\n        return (::EqualSid(User, session->GetUserSid()));\n    });\n\n    if (found != g_sessions->end())\n    {\n        return *found;\n    }\n    else\n    {\n        return {};\n    }\n}\n\nstd::weak_ptr<LxssUserSessionImpl> CreateInstanceForCurrentUser()\n{\n    // Do not create sessions for localsystem.\n    const unique_handle userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);\n    THROW_HR_IF(WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED, wsl::windows::common::security::IsTokenLocalSystem(userToken.get()));\n\n    // Get the session ID and SID of the client process.\n    DWORD sessionId{};\n    DWORD length = 0;\n    THROW_IF_WIN32_BOOL_FALSE(::GetTokenInformation(userToken.get(), TokenSessionId, &sessionId, sizeof(sessionId), &length));\n\n    const auto tokenInfo = wil::get_token_information<TOKEN_USER>(userToken.get());\n\n    // Find an existing session or create a new one.\n    std::shared_ptr<LxssUserSessionImpl> userSession;\n    {\n        std::lock_guard sessionLock(g_sessionTerminationLock);\n        auto lock = g_sessionLock.lock_exclusive();\n\n        // Do not allow session creation if WSL is disabled via policy.\n        THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY), g_disabledByPolicy);\n\n        // Builds prior to Windows 10 require the WSL optional component.\n        THROW_HR_IF(WSL_E_WSL_OPTIONAL_COMPONENT_REQUIRED, !g_lxcoreInitialized && !wsl::windows::common::helpers::IsWindows11OrAbove());\n\n        userSession = FindSessionLockHeld(tokenInfo->User.Sid);\n\n        if (!userSession)\n        {\n            userSession.reset(new LxssUserSessionImpl(tokenInfo->User.Sid, sessionId, *g_pluginManager));\n            g_sessions->emplace_back(userSession);\n        }\n    }\n\n    return userSession;\n}\n"
  },
  {
    "path": "src/windows/service/exe/LxssUserSessionFactory.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    LxssUserSessionFactory.h\n\nAbstract:\n\n    This file contains user session factory function declarations.\n\n--*/\n\n#pragma once\n#include <wil/resource.h>\n#include \"LxssUserSession.h\"\n\nclass LxssUserSessionImpl;\n\n/// <summary>\n/// WRL supports caching of class factories, but doesn't have a notion of a \"singleton\" COM object.\n/// By providing our own class factory for creating LxssUserSession objects, we can control the lifetime\n/// of the handed-out session object and ensure there's only one.\n/// </summary>\nclass LxssUserSessionFactory : public Microsoft::WRL::ClassFactory<>\n{\npublic:\n    LxssUserSessionFactory() = default;\n\n    /// <summary>\n    /// IClassFactory::CreateInstance - creates or hands out the LxssUserSession object to use.\n    /// </summary>\n    STDMETHODIMP CreateInstance(_In_ IUnknown* pUnkOuter, _In_ REFIID riid, _Out_ void** ppCreated) override;\n};\n\n/// <summary>\n/// Clean shutdown - clear all active sessions and prevent new sessions from being created.\n/// </summary>\nvoid ClearSessionsAndBlockNewInstances();\n\n/// <summary>\n/// Create a new session for the current user.\n/// </summary>\nstd::weak_ptr<LxssUserSessionImpl> CreateInstanceForCurrentUser();\n\n/// <summary>\n/// Set session creation policy. This is controlled by WSL enterprise policy keys.\n/// </summary>\nvoid SetSessionPolicy(_In_ bool enabled);\n\n/// <summary>\n/// Clean shutdown - terminate a specific session.\n/// </summary>\nvoid TerminateSession(_In_ DWORD sessionId);\n\n/// <summary>\n/// Find a session for a given user.\n/// </summary>\nstd::shared_ptr<LxssUserSessionImpl> FindSession(PSID User);\n\n/// <summary>\n/// Find a session for a given cookie.\n/// </summary>\nstd::shared_ptr<LxssUserSessionImpl> FindSessionByCookie(DWORD Cookie);"
  },
  {
    "path": "src/windows/service/exe/MirroredNetworking.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\n#include \"MirroredNetworking.h\"\n#include \"Stringify.h\"\n#include \"WslCoreFirewallSupport.h\"\n#include \"WslCoreNetworkingSupport.h\"\n#include \"WslMirroredNetworking.h\"\n#include \"WslCoreVm.h\"\n\nusing wsl::core::MirroredNetworking;\nusing wsl::core::networking::NetworkEndpoint;\nusing namespace wsl::shared;\n\nMirroredNetworking::MirroredNetworking(HCS_SYSTEM system, GnsChannel&& gnsChannel, const Config& config, GUID runtimeId, wil::unique_socket&& dnsHvsocket) :\n    m_system(system), m_runtimeId(runtimeId), m_config(config), m_gnsChannel(std::move(gnsChannel))\n{\n    // ensure the MTA apartment stays alive for the lifetime of this object in this process for our callback\n    THROW_IF_FAILED(CoIncrementMTAUsage(&m_mtaCookie));\n\n    // Create the DNS resolver used for DNS tunneling\n    if (dnsHvsocket)\n    {\n        networking::DnsResolverFlags resolverFlags{};\n        WI_SetFlagIf(resolverFlags, networking::DnsResolverFlags::BestEffortDnsParsing, m_config.BestEffortDnsParsing);\n\n        m_dnsTunnelingResolver.emplace(std::move(dnsHvsocket), resolverFlags);\n    }\n}\n\nMirroredNetworking::~MirroredNetworking()\n{\n    // Unblock GNSChannel if any calls are pended to unblock all the threadpools\n    // will also unblock m_networkManager, if that's waiting for calls through the GNS channel into Linux\n    m_gnsChannel.Stop();\n\n    // Stop DNS suffix change notifications before stopping m_networkManager and m_networkingQueue, as they can call into those objects.\n    m_dnsSuffixRegistryWatcher.reset();\n\n    // Gns must unregister all callbacks first (which could call into m_networkManager)\n    // Then we must shutdown the entire m_networkManager\n    // Accessing m_gnsRpcServer here is safe because it's only written to in the\n    // constructor, which is protected by m_instanceLock in LxssUserSessionImpl\n    if (m_gnsRpcServer)\n    {\n        // Unregister for GNS notifications\n        m_guestNetworkService.Stop();\n\n        if (m_networkManager)\n        {\n            m_networkManager->Stop();\n        }\n    }\n\n    // stop the TCPIP network change notifications - then stop all queued network work\n    m_addressNotificationHandle.reset();\n    m_routeNotificationHandle.reset();\n    m_interfaceNotificationHandle.reset();\n    m_networkNotificationHandle.reset();\n\n    m_gnsPortTrackerChannel.reset();\n    m_networkingQueue.cancel();\n    m_gnsMessageQueue.cancel();\n}\n\n// static\nbool MirroredNetworking::IsHyperVFirewallSupported(const wsl::core::Config& vmConfig) noexcept\n{\n    PCSTR executionStep = \"\";\n    try\n    {\n        const auto hyperVFirewallSupport = wsl::core::networking::GetHyperVFirewallSupportVersion(vmConfig.FirewallConfig);\n\n        if (hyperVFirewallSupport == wsl::core::networking::HyperVFirewallSupport::None)\n        {\n            WSL_LOG(\"IsHyperVFirewallSupported returning false: No Hyper-V Firewall API present\");\n            return false;\n        }\n\n        if (hyperVFirewallSupport == wsl::core::networking::HyperVFirewallSupport::Version1)\n        {\n            // not allowing Hyper-V Firewall support with WSL with just the Version1 Hyper-V Firewall API\n            WSL_LOG(\"IsHyperVFirewallSupported returning false: WSL requires Hyper-V Firewall version2 but version1 is present\");\n            return false;\n        }\n\n        executionStep = \"HcnEnumerateNetworks\";\n        // Check to see if the network is already created without Hyper-V Firewall.\n        // HNS only supports one networking configuration per boot cycle, so if it was configured with the\n        // Mirrored flag but without the Hyper-V Firewall flag, then we MUST NOT attempt to enable Hyper-V Firewall.\n        for (const auto& id : wsl::core::networking::EnumerateNetworks())\n        {\n            executionStep = \"HcnOpenNetwork\";\n            auto network = wsl::core::networking::OpenNetwork(id);\n\n            executionStep = \"HcnQueryNetworkProperties\";\n            auto [networkProperties, propertiesString] = wsl::core::networking::QueryNetworkProperties(network.get());\n            if (WI_IsFlagSet(static_cast<uint32_t>(networkProperties.Flags), WI_EnumValue(wsl::shared::hns::NetworkFlags::EnableFlowSteering)) &&\n                !WI_IsFlagSet(static_cast<uint32_t>(networkProperties.Flags), WI_EnumValue(wsl::shared::hns::NetworkFlags::EnableFirewall)))\n            {\n                WSL_LOG(\n                    \"IsHyperVFirewallSupported returning false: HNS Mirrored-network already created without Hyper-V Firewall \"\n                    \"support, cannot enable Hyper-V Firewall\");\n                return false;\n            }\n        }\n\n        return true;\n    }\n    catch (...)\n    {\n        const auto hr = wil::ResultFromCaughtException();\n        WSL_LOG(\n            \"IsHyperVFirewallSupportedFailed\",\n            TraceLoggingHResult(hr, \"result\"),\n            TraceLoggingValue(executionStep, \"executionStep\"),\n            TraceLoggingValue(\"Mirrored\", \"networkingMode\"));\n\n        return false;\n    }\n}\n\n// static\nbool MirroredNetworking::IsExternalInterfaceConstrained(const HCN_NETWORK network) noexcept\n{\n    try\n    {\n        // Read interface constraint\n        const auto lxssKey = windows::common::registry::OpenLxssMachineKey(KEY_READ);\n        const auto interfaceConstraint =\n            windows::common::registry::ReadString(lxssKey.get(), nullptr, networking::c_interfaceConstraintKey, L\"\");\n\n        if (!interfaceConstraint.empty())\n        {\n            // The user has configured an ExternalInterfaceConstraint\n\n            // Use GetAdapterAddresses to obtain the InterfaceGuid of the interface corresponding to the constraint\n            constexpr auto GET_ADAPTER_ADDRESSES_BUFFER_SIZE_INITIAL = (15 * 1024);\n            constexpr auto GAA_FLAGS = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_DNS_SERVER;\n\n            ULONG result = ERROR_SUCCESS;\n            ULONG bufferSize = GET_ADAPTER_ADDRESSES_BUFFER_SIZE_INITIAL;\n            std::vector<std::byte> buffer;\n            PIP_ADAPTER_ADDRESSES adapter;\n\n            do\n            {\n                buffer.resize(bufferSize);\n                adapter = gslhelpers::get_struct<IP_ADAPTER_ADDRESSES>(gsl::make_span(buffer));\n                result = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAGS, nullptr, adapter, &bufferSize);\n            } while (result == ERROR_BUFFER_OVERFLOW);\n\n            THROW_LAST_ERROR_IF_MSG(result != ERROR_SUCCESS, \"GetAdaptersAddresses\");\n\n            // Find the external interface constraint adapter (i.e. the adapter which has its friendly name matching the regkey value)\n            bool interfaceConstraintPresent = false;\n            while (adapter != nullptr)\n            {\n                if (wsl::shared::string::IsEqual(interfaceConstraint, adapter->FriendlyName, true))\n                {\n                    interfaceConstraintPresent = true;\n                    break;\n                }\n                adapter = adapter->Next;\n            }\n\n            if (interfaceConstraintPresent)\n            {\n                // Retrieve the interfaceGuid corresponding to this endpoint by querying the HNS network\n                GUID endpointInterfaceGuid{};\n                wil::unique_cotaskmem_string error;\n                wil::unique_cotaskmem_string networkPropertiesString;\n                wsl::shared::hns::HNSNetwork networkProperties;\n                try\n                {\n                    std::tie(networkProperties, networkPropertiesString) = wsl::core::networking::QueryNetworkProperties(network);\n                }\n                catch (...)\n                {\n                    WSL_LOG(\n                        \"IsExternalInterfaceConstrainedFailed\",\n                        TraceLoggingHResult(wil::ResultFromCaughtException(), \"result\"),\n                        TraceLoggingValue(\"HcnQueryNetworkProperties\", \"executionStep\"),\n                        TraceLoggingValue(\"Mirrored\", \"networkingMode\"));\n\n                    return false;\n                }\n\n                // Successfully read the interfaceGuid for this endpoint\n                endpointInterfaceGuid = networkProperties.InterfaceConstraint.InterfaceGuid;\n\n                // Obtain ExternalInterfaceConstraint's interfaceGuid to compare against the endpoint's interfaceGuid\n                GUID externalInterfaceConstraintGuid{};\n                THROW_IF_WIN32_ERROR(ConvertInterfaceLuidToGuid(&(adapter->Luid), &externalInterfaceConstraintGuid));\n\n                if (externalInterfaceConstraintGuid == endpointInterfaceGuid)\n                {\n                    // This interface matches the one we are looking for.\n                    // There is an external interface constraint configured, the constrained\n                    // interface is present, and the interface in question is the ExternalInterfaceConstraint.\n                    // This interface is allowed to operate normally and must not be constrained.\n                    WSL_LOG(\n                        \"IsExternalInterfaceConstrainedInterface\",\n                        TraceLoggingValue(endpointInterfaceGuid, \"InterfaceGuid\"),\n                        TraceLoggingValue(\n                            \"ExternalInterfaceConstraint is configured and this interface is the \"\n                            \"ExternalInterfaceConstraint. This interface must NOT be constrained\",\n                            \"state\"));\n                    return false;\n                }\n\n                // There is an external interface constraint configured and the constrained\n                // interface is present, but this is not the ExternalInterfaceConstraint.\n                // Thus, this interface must be constrained.\n                WSL_LOG(\n                    \"IsExternalInterfaceConstrainedInterface\",\n                    TraceLoggingValue(endpointInterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(\n                        \"ExternalInterfaceConstraint is configured and the ExternalInterfaceConstraint is \"\n                        \"found. This interface must be constrained.\",\n                        \"state\"));\n                return true;\n            }\n            // There is an ExternalInterfaceConstraint configured, but it is not present/up.\n            // Thus, this interface must be constrained.\n            WSL_LOG(\n                \"IsExternalInterfaceConstrainedInterface\",\n                TraceLoggingValue(\n                    \"ExternalInterfaceConstraint is configured and the ExternalInterfaceConstraint is NOT \"\n                    \"found. All interfaces must be constrained.\",\n                    \"state\"));\n            return true;\n        }\n\n        // There is no ExternalInterfaceConstraint configured.\n        // This, this interface must NOT be constrained.\n        WSL_LOG(\n            \"IsExternalInterfaceConstrainedInterface\",\n            TraceLoggingValue(\"ExternalInterfaceConstraint is not configured. All interfaces must NOT be constrained.\", \"state\"));\n        return false;\n    }\n    CATCH_LOG()\n\n    // If we reached here, we hit caught an unexpected error. Default to non-constrained\n    return false;\n}\n\nvoid MirroredNetworking::Initialize()\n{\n    // Configure IPV6 before anything else happens (IPV6 configuration needs to be done early).\n    m_networkingQueue.submit([this] {\n        return wil::ResultFromException([&]() { m_gnsChannel.SendNetworkDeviceMessage(LxGnsMessageSetupIpv6, L\"{}\"); });\n    });\n\n    m_gnsRpcServer = GnsRpcServer::GetOrCreate();\n    m_guestNetworkService.CreateGuestNetworkService(\n        m_config.FirewallConfig.Enabled(), m_config.IgnoredPorts, m_runtimeId, m_gnsRpcServer->GetServerUuid(), s_GuestNetworkServiceCallback, this);\n    m_ephemeralPortRange = m_guestNetworkService.AllocateEphemeralPortRange();\n\n    networking::ConfigureHyperVFirewall(m_config.FirewallConfig, wsl::windows::common::wslutil::c_vmOwner);\n\n    // must keep all m_networkManager interactions (including) creation queued\n    // also must queue GNS callbacks to keep them serialized\n    // the queue also prevents losing change notifications while we are still processing add notifications\n    // calling submit_with_results to get a WslThreadPoolWaitableResult so we can conditionally wait for this workitem to complete to determine if it succeeded\n    const auto workItemTracker = m_networkingQueue.submit_with_results<HRESULT>([this] {\n        try\n        {\n            auto addNetworkEndpointCallback = [this](const GUID& networkId) {\n                m_networkingQueue.submit([this, networkId] { this->AddNetworkEndpoint(networkId); });\n            };\n\n            // Create and start the network manager.\n            //\n            // N.B. Mirrored networks may not yet exist and the NetworkManager c'tor will cause HCS to create them asynchronously.\n            //      This is done by the query submitted to HcnEnumerateNetworks.\n            //      Once the networks are created, the network change callback will be invoked and endpoints will be hot-added.\n            // implement wsl::core::networking::GnsMessageCallbackWithCallbackResult so WSL can serialize messages to Linux\n            auto networkManagerGnsMessageCallbackWithCallbackResult = [this](\n                                                                          LX_MESSAGE_TYPE messageType,\n                                                                          const std::wstring& notificationString,\n                                                                          networking::GnsCallbackFlags callbackFlags,\n                                                                          _Out_opt_ int* returnedResult) -> HRESULT {\n                // NetworkManagerGnsMessageCallback queues the actual work to the m_gnsMessageQueue\n                return NetworkManagerGnsMessageCallback(messageType, notificationString, callbackFlags, returnedResult);\n            };\n\n            m_networkManager = std::make_unique<wsl::core::networking::WslMirroredNetworkManager>(\n                m_system, m_config, std::move(networkManagerGnsMessageCallbackWithCallbackResult), std::move(addNetworkEndpointCallback), m_ephemeralPortRange);\n\n            // Register notifications for DNS suffix changes\n            m_dnsSuffixRegistryWatcher.emplace(\n                [this] { m_networkingQueue.submit([this] { m_networkManager->OnDnsSuffixChange(); }); });\n\n            // Send the requisite notifications for the required network devices\n            m_networkManager->SendCreateNotificationsForInitialEndpoints();\n\n            // HNS now has all host interfaces that will be mirrored mapped into NetworkIds\n            std::vector<GUID> networkIds;\n            THROW_IF_FAILED(m_networkManager->EnumerateNetworks(networkIds));\n\n            // Create an endpoint on each mirrored network.\n            for (auto& networkId : networkIds)\n            {\n                AddNetworkEndpoint(networkId);\n            }\n\n            // At this point all endpoints are configured, mark the GuestNetworkService as 'Synchronized'\n            m_guestNetworkService.SetGuestNetworkServiceState(hns::GuestNetworkServiceState::Synchronized);\n        }\n        catch (...)\n        {\n            const auto hr = wil::ResultFromCaughtException();\n            WSL_LOG(\n                \"FailedToStartNetworkManager\",\n                TraceLoggingValue(m_runtimeId, \"vmId\"),\n                TraceLoggingValue(hr, \"error\"),\n                TraceLoggingValue(ToString(m_config.NetworkingMode), \"networkConfiguration\"));\n\n            return hr;\n        }\n        return S_OK;\n    });\n\n    // Wait for initial mirroring to give users a consistent experience.\n    // the wait should not timeout - we are waiting infinite\n    const auto waitResult = workItemTracker->wait(INFINITE);\n    WI_ASSERT(ERROR_SUCCESS == waitResult);\n    // now we can read the HRESULT returned from the work item\n    const auto hr = workItemTracker->read_result();\n    if (SUCCEEDED(hr))\n    {\n        // We must wait for the goal state to be reached outside of the queue, since operations\n        // required to reach the goal state require processing in the queue.\n        const auto goalStateHr = m_networkManager->WaitForMirroredGoalState();\n        if (FAILED(goalStateHr))\n        {\n            WSL_LOG(\n                \"WaitForMirroredGoalStateFailed\",\n                TraceLoggingHResult(goalStateHr, \"hr\"),\n                TraceLoggingValue(m_config.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                TraceLoggingValue(m_config.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                TraceLoggingValue(m_config.EnableAutoProxy, \"AutoProxyFeatureEnabled\"));\n        }\n    }\n\n    THROW_IF_FAILED(hr);\n    // else we don't need to wait on the result\n    // it can safely go out of scope and we can exit (it's a shared_ptr)\n}\n\nvoid MirroredNetworking::FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message)\n{\n    message.NetworkingMode = LxMiniInitNetworkingModeMirrored;\n\n    std::tie(message.EphemeralPortRangeStart, message.EphemeralPortRangeEnd) = m_ephemeralPortRange;\n    message.PortTrackerType = LxMiniInitPortTrackerTypeMirrored;\n    message.EnableDhcpClient = false;\n    message.DisableIpv6 = false;\n}\n\nvoid MirroredNetworking::StartPortTracker(wil::unique_socket&& socket)\n{\n    WI_ASSERT(!m_gnsPortTrackerChannel.has_value());\n\n    m_gnsPortTrackerChannel.emplace(\n        std::move(socket),\n        [&](const SOCKADDR_INET& Address, int Protocol, bool Allocate) {\n            return m_guestNetworkService.OnPortAllocationRequest(Address, Protocol, Allocate);\n        },\n        [&](_In_ const std::string& InterfaceName, _In_ bool Up) {\n            m_networkingQueue.submit([=, this] {\n                if (m_networkManager)\n                {\n                    m_networkManager->TunAdapterStateChanged(InterfaceName, Up);\n                }\n            });\n        });\n}\n\nvoid MirroredNetworking::TraceLoggingRundown() noexcept\n{\n    m_networkingQueue.submit([this] {\n        if (m_networkManager)\n        {\n            m_networkManager->TraceLoggingRundown();\n        }\n    });\n}\n\n// must be called from m_networkingQueue - m_networkManager must be called only from that queue\nvoid MirroredNetworking::AddNetworkEndpoint(const GUID& NetworkId) noexcept\n{\n    PCSTR executionStep = \"\";\n    try\n    {\n        WI_ASSERT(m_networkingQueue.isRunningInQueue());\n        WI_ASSERT(m_networkManager);\n\n        if (m_networkManager->DoesEndpointExist(NetworkId))\n        {\n            WSL_LOG(\n                \"MirroredNetworking::AddNetworkEndpoint - NetworkId already exists\", TraceLoggingValue(NetworkId, \"networkId\"));\n            return;\n        }\n\n        executionStep = \"HcnOpenNetwork\";\n        auto network = wsl::core::networking::OpenNetwork(NetworkId);\n        WSL_LOG(\"MirroredNetworking::AddNetworkEndpoint [HcnOpenNetwork]\", TraceLoggingValue(NetworkId, \"networkId\"));\n\n        // Query the network properties for diagnostic purposes only.\n        wsl::shared::hns::HNSNetwork properties;\n        wil::unique_cotaskmem_string networkProperties;\n        executionStep = \"HcnQueryNetworkProperties\";\n        std::tie(properties, networkProperties) = wsl::core::networking::QueryNetworkProperties(network.get());\n        WSL_LOG(\n            \"MirroredNetworking::AddNetworkEndpoint [HcnQueryNetworkProperties]\",\n            TraceLoggingValue(NetworkId, \"networkId\"),\n            TraceLoggingValue(networkProperties.get(), \"networkProperties\"));\n\n        // Create a network endpoint.\n        // first see if we have cached a prior endpoint-id that matches this network-id\n        GUID endpointId{};\n        const auto existingEndpointValue = m_networkIdMappings.find(NetworkId);\n        if (existingEndpointValue != m_networkIdMappings.end())\n        {\n            endpointId = existingEndpointValue->second;\n            WSL_LOG(\n                \"MirroredNetworking::AddNetworkEndpoint [using existing endpoint id]\",\n                TraceLoggingValue(NetworkId, \"networkId\"),\n                TraceLoggingValue(endpointId, \"endpointId\"));\n        }\n        else\n        {\n            executionStep = \"CoCreateGuid\";\n            THROW_IF_FAILED(CoCreateGuid(&endpointId));\n        }\n\n        std::wstring endpointSettings;\n        NetworkEndpoint endpointInfo{};\n        endpointInfo.NetworkId = NetworkId;\n        endpointInfo.EndpointId = endpointId;\n\n        if (m_config.FirewallConfig.Enabled())\n        {\n            // Create HNS firewall policy object for the endpoint\n            hns::HostComputeEndpoint hnsEndpoint{};\n            hns::EndpointPolicy<hns::PortnameEndpointPolicySetting> endpointPortNamePolicy{};\n            hns::EndpointPolicy<hns::FirewallPolicySetting> endpointFirewallPolicy{};\n\n            // Assemble the endpoint\n            hnsEndpoint.HostComputeNetwork = NetworkId;\n            hnsEndpoint.SchemaVersion.Major = 2;\n            hnsEndpoint.SchemaVersion.Minor = 16;\n\n            // Port name policy\n            endpointPortNamePolicy.Type = hns::EndpointPolicyType::PortName;\n            hnsEndpoint.Policies.emplace_back(std::move(endpointPortNamePolicy));\n\n            // Firewall policy\n            hns::FirewallPolicySetting firewallPolicyObject{};\n            firewallPolicyObject.VmCreatorId = m_config.FirewallConfig.VmCreatorId.value();\n\n            // Set firewall policy flags\n            // Currently, only the ConstrainedInterface flag is supported, which is set based on the user configuring an ExternalInterfaceConstraint.\n            firewallPolicyObject.PolicyFlags = IsExternalInterfaceConstrained(network.get()) ? hns::FirewallPolicyFlags::ConstrainedInterface\n                                                                                             : hns::FirewallPolicyFlags::None;\n\n            endpointFirewallPolicy.Settings = std::move(firewallPolicyObject);\n            endpointFirewallPolicy.Type = hns::EndpointPolicyType::Firewall;\n            hnsEndpoint.Policies.emplace_back(std::move(endpointFirewallPolicy));\n            endpointSettings = ToJsonW(hnsEndpoint);\n        }\n        else\n        {\n            // If Hyper-V Firewall is not supported for this scenario, only configure the basic HNS endpoint object\n            wsl::shared::hns::HNSEndpoint settings{};\n            settings.VirtualNetwork = NetworkId;\n            endpointSettings = ToJsonW(settings);\n        }\n\n        // Create the endpoint\n        executionStep = \"HcnCreateEndpoint\";\n        wil::unique_cotaskmem_string error;\n        auto result = HcnCreateEndpoint(network.get(), endpointInfo.EndpointId, endpointSettings.c_str(), &endpointInfo.Endpoint, &error);\n\n        WSL_LOG(\n            \"MirroredNetworking::AddNetworkEndpoint [HcnCreateEndpoint]\",\n            TraceLoggingValue(NetworkId, \"HNSEndpoint::NetworkId\"),\n            TraceLoggingValue(result, \"result\"),\n            TraceLoggingValue(error.is_valid() ? error.get() : L\"\", \"errorString\"));\n        THROW_IF_FAILED_MSG(result, \"HcnCreateEndpoint %ls\", error.get());\n\n        wil::unique_cotaskmem_string propertiesString;\n        executionStep = \"HcnQueryEndpointProperties\";\n        result = HcnQueryEndpointProperties(endpointInfo.Endpoint.get(), nullptr, &propertiesString, &error);\n        WSL_LOG(\n            \"MirroredNetworking::AddNetworkEndpoint [HcnQueryEndpointProperties]\",\n            TraceLoggingValue(endpointInfo.EndpointId, \"endpointId\"),\n            TraceLoggingValue(result, \"result\"),\n            TraceLoggingValue(error.is_valid() ? error.get() : L\"\", \"errorString\"),\n            TraceLoggingValue(propertiesString.is_valid() ? propertiesString.get() : L\"\", \"propertiesString\"));\n        THROW_IF_FAILED_MSG(result, \"HcnQueryEndpointProperties %ls\", error.get());\n\n        executionStep = \"ParsingHcnQueryEndpointProperties\";\n        auto endpointProperties = FromJson<hns::HNSEndpoint>(propertiesString.get());\n\n        endpointInfo.Network = m_networkManager->GetEndpointSettings(endpointProperties);\n        endpointInfo.InterfaceGuid = endpointProperties.InterfaceConstraint.InterfaceGuid;\n\n        WSL_LOG(\n            \"MirroredNetworking::AddNetworkEndpoint\",\n            TraceLoggingValue(endpointInfo.EndpointId, \"endpointId\"),\n            TraceLoggingValue(endpointInfo.InterfaceGuid, \"endpointInterfaceGuid\"),\n            TraceLoggingValue(endpointInfo.InterfaceLuid.Value, \"endpointInterfaceLuid\"),\n            TraceLoggingValue(endpointProperties.IPAddress.c_str(), \"endpointIpAddress\"),\n            TraceLoggingValue(endpointProperties.PortFriendlyName.c_str(), \"endpointPortFriendlyName\"),\n            TraceLoggingValue(endpointProperties.Name.c_str(), \"endpointName\"),\n            TraceLoggingValue(endpointProperties.VirtualNetwork, \"endpointVirtualNetwork\"),\n            TraceLoggingValue(endpointProperties.VirtualNetworkName.c_str(), \"endpointVirtualNetworkName\"));\n\n        m_networkManager->AddEndpoint(std::move(endpointInfo), std::move(endpointProperties));\n\n        if (!m_networkNotificationHandle)\n        {\n            // Register for network connectivity change notifications to update the MTU.\n            LOG_IF_WIN32_ERROR(NotifyNetworkConnectivityHintChange(\n                [](PVOID context, NL_NETWORK_CONNECTIVITY_HINT hint) {\n                    WSL_LOG(\n                        \"MirroredNetworking::NotifyNetworkConnectivityHintChange fired\",\n                        TraceLoggingValue(static_cast<uint32_t>(hint.ConnectivityLevel), \"connectivityLevel\"),\n                        TraceLoggingValue(static_cast<uint32_t>(hint.ConnectivityCost), \"connectivityCost\"));\n\n                    auto* thisPtr = static_cast<MirroredNetworking*>(context);\n                    thisPtr->m_networkingQueue.submit([thisPtr] {\n                        if (thisPtr->m_networkManager)\n                        {\n                            thisPtr->m_networkManager->OnNetworkConnectivityHintChange();\n                        }\n                    });\n                },\n                this,\n                TRUE,\n                &m_networkNotificationHandle));\n        }\n        if (!m_interfaceNotificationHandle)\n        {\n            LOG_IF_WIN32_ERROR(NotifyIpInterfaceChange(\n                AF_UNSPEC,\n                [](PVOID context, PMIB_IPINTERFACE_ROW row, MIB_NOTIFICATION_TYPE) {\n                    WSL_LOG(\n                        \"MirroredNetworking::NotifyIpInterfaceChange fired\",\n                        TraceLoggingValue(row->Family, \"family\"),\n                        TraceLoggingValue(row->InterfaceIndex, \"ifIndex\"));\n\n                    auto* thisPtr = static_cast<MirroredNetworking*>(context);\n                    thisPtr->m_networkingQueue.submit([thisPtr] {\n                        if (thisPtr->m_networkManager)\n                        {\n                            thisPtr->m_networkManager->OnNetworkConnectivityHintChange();\n                        }\n                    });\n                },\n                this,\n                FALSE,\n                &m_interfaceNotificationHandle));\n        }\n        if (!m_routeNotificationHandle)\n        {\n            LOG_IF_WIN32_ERROR(NotifyRouteChange2(\n                AF_UNSPEC,\n                [](PVOID context, PMIB_IPFORWARD_ROW2 row, MIB_NOTIFICATION_TYPE) {\n                    WSL_LOG(\"MirroredNetworking::NotifyRouteChange2 fired\", TraceLoggingValue(row->InterfaceIndex, \"ifIndex\"));\n\n                    auto* thisPtr = static_cast<MirroredNetworking*>(context);\n                    thisPtr->m_networkingQueue.submit([thisPtr] {\n                        if (thisPtr->m_networkManager)\n                        {\n                            thisPtr->m_networkManager->OnNetworkConnectivityHintChange();\n                        }\n                    });\n                },\n                this,\n                FALSE,\n                &m_routeNotificationHandle));\n        }\n        if (!m_addressNotificationHandle)\n        {\n            LOG_IF_WIN32_ERROR(NotifyUnicastIpAddressChange(\n                AF_UNSPEC,\n                [](PVOID context, PMIB_UNICASTIPADDRESS_ROW row, MIB_NOTIFICATION_TYPE) {\n                    WSL_LOG(\n                        \"MirroredNetworking::NotifyUnicastIpAddressChange fired\",\n                        TraceLoggingValue(row->InterfaceIndex, \"ifIndex\"));\n\n                    auto* thisPtr = static_cast<MirroredNetworking*>(context);\n                    thisPtr->m_networkingQueue.submit([thisPtr] {\n                        if (thisPtr->m_networkManager)\n                        {\n                            thisPtr->m_networkManager->OnNetworkConnectivityHintChange();\n                        }\n                    });\n                },\n                this,\n                FALSE,\n                &m_addressNotificationHandle));\n        }\n\n        // we've successfully added a new endpoint - track that Id\n        if (existingEndpointValue == m_networkIdMappings.end())\n        {\n            WSL_LOG(\n                \"MirroredNetworking::AddNetworkEndpoint [tracking new endpoint]\",\n                TraceLoggingValue(NetworkId, \"networkId\"),\n                TraceLoggingValue(endpointId, \"endpointId\"));\n            m_networkIdMappings[NetworkId] = endpointId;\n        }\n    }\n    catch (...)\n    {\n        WSL_LOG(\n            \"AddNetworkEndpointFailure\",\n            TraceLoggingHResult(wil::ResultFromCaughtException(), \"result\"),\n            TraceLoggingValue(executionStep, \"executionStep\"),\n            TraceLoggingValue(\"Mirrored\", \"networkingMode\"),\n            TraceLoggingValue(m_config.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n            TraceLoggingValue(m_config.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n            TraceLoggingValue(m_config.EnableAutoProxy, \"AutoProxyFeatureEnabled\") // the feature is enabled, but we don't know if proxy settings are actually configured\n        );\n    }\n}\n\n// must be called from m_networkingQueue so all GNS interactions are correctly serialized\n// OnNetworkEndpointChange is called from GNS\nHRESULT MirroredNetworking::OnNetworkEndpointChange(const GUID& EndpointId, _In_ LPCWSTR Settings) const noexcept\ntry\n{\n    WI_ASSERT(m_networkingQueue.isRunningInQueue());\n\n    const auto notification = FromJson<hns::ModifyGuestEndpointSettingRequest<void>>(Settings);\n\n    // not sending Neighbor updates into the container\n    if (notification.ResourceType == hns::GuestEndpointResourceType::Neighbor)\n    {\n        return E_NOTIMPL;\n    }\n\n    // a network property changed on some interface that HNS is tracking\n    // we're using this notification as a trigger to rediscover the preferred interface\n    WSL_LOG(\n        \"MirroredNetworking::OnNetworkEndpointChange [GNS server notification]\",\n        TraceLoggingValue(wsl::shared::string::GuidToString<wchar_t>(EndpointId).c_str(), \"Endpoint\"),\n        TraceLoggingValue(Settings, \"Payload\"));\n    m_networkManager->OnNetworkEndpointChange();\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nHRESULT MirroredNetworking::NetworkManagerGnsMessageCallback(\n    LX_MESSAGE_TYPE messageType, std::wstring notificationString, networking::GnsCallbackFlags callbackFlags, _Out_opt_ int* returnedValueFromGns) noexcept\ntry\n{\n    // only pass the OUT returnedValueFromGns int* if the callback flags are set to wait\n    if (returnedValueFromGns)\n    {\n        *returnedValueFromGns = ERROR_FATAL_APP_EXIT;\n        WI_ASSERT(WI_IsFlagSet(callbackFlags, wsl::core::networking::GnsCallbackFlags::Wait));\n    }\n\n    auto sendGnsMessage =\n        [this, messageType, capturedNotificationString = std::move(notificationString), callbackFlags, returnedValueFromGns]() mutable {\n            try\n            {\n                auto retryCount = 0ul;\n                // RetryWithTimeout throws if fails after the timeout has elapsed - which is caught and returned by m_gnsMessageQueue below\n                return wsl::shared::retry::RetryWithTimeout<HRESULT>(\n                    [&]() {\n                        const auto hr = wil::ResultFromException([&] {\n                            if (returnedValueFromGns && WI_IsFlagSet(callbackFlags, wsl::core::networking::GnsCallbackFlags::Wait))\n                            {\n                                *returnedValueFromGns =\n                                    m_gnsChannel.SendNetworkDeviceMessageReturnResult(messageType, capturedNotificationString.c_str());\n                            }\n                            else\n                            {\n                                m_gnsChannel.SendNetworkDeviceMessage(messageType, capturedNotificationString.c_str());\n                            }\n                        });\n                        WSL_LOG(\n                            \"MirroredNetworking::NetworkManagerGnsMessageCallback\",\n                            TraceLoggingValue(ToString(messageType), \"messageType\"),\n                            TraceLoggingValue(capturedNotificationString.c_str(), \"notificationString\"),\n                            TraceLoggingValue(hr, \"hr\"),\n                            TraceLoggingValue(returnedValueFromGns ? *returnedValueFromGns : 0xFFFFFFFF, \"returnedValueFromGns\"),\n                            TraceLoggingValue(retryCount, \"retryCount\"));\n\n                        ++retryCount;\n                        return hr;\n                    },\n                    std::chrono::milliseconds(100),\n                    std::chrono::seconds(3));\n            }\n            CATCH_RETURN()\n        };\n\n    if (WI_IsFlagSet(callbackFlags, wsl::core::networking::GnsCallbackFlags::Wait))\n    {\n        return m_gnsMessageQueue.submit_and_wait(std::move(sendGnsMessage));\n    }\n\n    m_gnsMessageQueue.submit(std::move(sendGnsMessage));\n    return S_OK;\n}\nCATCH_RETURN()\n\nvoid MirroredNetworking::GuestNetworkServiceCallback(DWORD NotificationType, HRESULT NotificationStatus, _In_opt_ PCWSTR NotificationData) noexcept\ntry\n{\n    WSL_LOG(\n        \"MirroredNetworking::GuestNetworkServiceCallback\",\n        TraceLoggingValue(wsl::windows::common::stringify::HcnNotificationsToString(NotificationType), \"NotificationType\"),\n        TraceLoggingValue(NotificationStatus, \"NotificationStatus\"),\n        TraceLoggingValue(NotificationData, \"NotificationData\"));\n\n    WI_ASSERT(SUCCEEDED(NotificationStatus));\n\n    hns::NotificationBase data{};\n    if (ARGUMENT_PRESENT(NotificationData))\n    {\n        data = FromJson<hns::NotificationBase>(NotificationData);\n    }\n\n    switch (NotificationType)\n    {\n    case HcnNotificationServiceDisconnect:\n        break;\n\n    case HcnNotificationGuestNetworkServiceStateChanged:\n        break;\n\n    case HcnNotificationGuestNetworkServiceInterfaceStateChanged:\n        break;\n\n    default:\n        WI_ASSERT(false);\n    }\n\n    return;\n}\nCATCH_LOG()\n\nvoid CALLBACK MirroredNetworking::s_GuestNetworkServiceCallback(DWORD NotificationType, _In_ void* Context, HRESULT NotificationStatus, _In_opt_ PCWSTR NotificationData)\n{\n    static_cast<MirroredNetworking*>(Context)->GuestNetworkServiceCallback(NotificationType, NotificationStatus, NotificationData);\n}\n"
  },
  {
    "path": "src/windows/service/exe/MirroredNetworking.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n\r\n#include \"precomp.h\"\r\n#include \"INetworkingEngine.h\"\r\n#include \"GnsChannel.h\"\r\n#include \"DnsResolver.h\"\r\n#include \"WslCoreConfig.h\"\r\n#include \"WslCoreNetworkEndpointSettings.h\"\r\n#include \"WslCoreMessageQueue.h\"\r\n#include \"GnsPortTrackerChannel.h\"\r\n#include \"GnsRpcServer.h\"\r\n#include \"WslCoreGuestNetworkService.h\"\r\n#include \"IMirroredNetworkManager.h\"\r\n\r\nnamespace wsl::core {\r\n\r\nclass MirroredNetworking : public INetworkingEngine\r\n{\r\npublic:\r\n    MirroredNetworking(HCS_SYSTEM system, GnsChannel&& gnsChannel, const Config& config, GUID runtimeId, wil::unique_socket&& dnsHvsocket);\r\n    ~MirroredNetworking() override;\r\n\r\n    MirroredNetworking(const MirroredNetworking&) = delete;\r\n    MirroredNetworking& operator=(const MirroredNetworking) = delete;\r\n    MirroredNetworking(MirroredNetworking&&) = delete;\r\n    MirroredNetworking& operator=(MirroredNetworking&&) = delete;\r\n\r\n    void Initialize() override;\r\n\r\n    void TraceLoggingRundown() noexcept override;\r\n\r\n    void FillInitialConfiguration(LX_MINI_INIT_NETWORKING_CONFIGURATION& message) override;\r\n\r\n    void StartPortTracker(wil::unique_socket&& socket) override;\r\n\r\n    /// <summary>\r\n    /// Returns true if the interface should be constrained, false otherwise.\r\n    ///\r\n    /// This function determines if the input InterfaceGuid corresponds to an interface\r\n    /// that should be constrained. One can configure the ExternalInterfaceConstraint, which means\r\n    /// that all interfaces OTHER than the ExternalInterfaceConstraint will have its traffic constrained\r\n    /// (i.e restricted to only local subnet access).\r\n    ///\r\n    /// This function returns TRUE if there is an ExternalInterfaceConstraint configured AND\r\n    /// this interface does not match the ExternalInterfaceConstraint (which means that this interface is\r\n    /// restricted to communicate ONLY over the local subnet)\r\n    /// This function returns FALSE otherwise (which means that this interface has no restrictions on it)\r\n    ///\r\n    /// If any errors occur while trying to determine the ExternalInterfaceConstraint, this function will\r\n    /// default to returning FALSE (i.e non-constrained, normal traffic allowed interface)\r\n    /// </summary>\r\n    static bool IsExternalInterfaceConstrained(const HCN_NETWORK network) noexcept;\r\n\r\n    static bool IsHyperVFirewallSupported(const wsl::core::Config& vmConfig) noexcept;\r\n\r\nprivate:\r\n    void AddNetworkEndpoint(const GUID& NetworkId) noexcept;\r\n\r\n    HRESULT OnNetworkEndpointChange(const GUID& Endpoint, _In_ LPCWSTR Settings) const noexcept;\r\n\r\n    // callbacks\r\n    HRESULT NetworkManagerGnsMessageCallback(\r\n        LX_MESSAGE_TYPE messageType, std::wstring notificationString, networking::GnsCallbackFlags callbackFlags, _Out_opt_ int* returnedValueFromGns) noexcept;\r\n    static void GuestNetworkServiceCallback(DWORD NotificationType, HRESULT NotificationStatus, _In_opt_ PCWSTR NotificationData) noexcept;\r\n    static void CALLBACK s_GuestNetworkServiceCallback(DWORD NotificationType, _In_ void* Context, HRESULT NotificationStatus, _In_opt_ PCWSTR NotificationData);\r\n\r\n    // Handle owned by WslCoreVm\r\n    const HCS_SYSTEM m_system{};\r\n    const GUID m_runtimeId;\r\n    const Config& m_config;\r\n\r\n    // holding the MTA for our COM callback\r\n    wsl::windows::common::helpers::unique_mta_cookie m_mtaCookie{};\r\n    std::optional<GnsPortTrackerChannel> m_gnsPortTrackerChannel;\r\n    std::shared_ptr<GnsRpcServer> m_gnsRpcServer;\r\n    // mutable allows m_gnsMessageQueue to submit from const methods\r\n    mutable WslCoreMessageQueue m_gnsMessageQueue;\r\n    networking::GuestNetworkService m_guestNetworkService;\r\n\r\n    // m_network* and m_gnsChannel must be accessed only from within the m_networkingQueue\r\n    // which serializes all workitems through a single-threaded queue\r\n    // This unwinds the locking/dependencies with the GNS channel (and its callbacks) and HNS APIs (Hcn*)\r\n    GnsChannel m_gnsChannel;\r\n    std::unique_ptr<networking::IMirroredNetworkManager> m_networkManager;\r\n    mutable WslCoreMessageQueue m_networkingQueue;\r\n\r\n    // Optional DNS resolver used for DNS tunneling\r\n    std::optional<networking::DnsResolver> m_dnsTunnelingResolver;\r\n\r\n    std::optional<networking::DnsSuffixRegistryWatcher> m_dnsSuffixRegistryWatcher;\r\n\r\n    std::optional<networking::NetworkSettings> m_networkPreferredSettings;\r\n    ULONG m_networkNatMtu = ULONG_MAX;\r\n    networking::unique_notify_handle m_networkNotificationHandle{};\r\n    networking::unique_notify_handle m_interfaceNotificationHandle{};\r\n    networking::unique_notify_handle m_routeNotificationHandle{};\r\n    networking::unique_notify_handle m_addressNotificationHandle{};\r\n\r\n    // track network-id to endpoint-id\r\n    // we can avoid recreating vmNICs by reusing the same endpoint-id values\r\n    std::map<GUID, GUID, wsl::windows::common::helpers::GuidLess> m_networkIdMappings;\r\n\r\n    // Ephemeral port range allocated for the VM.\r\n    std::pair<uint16_t, uint16_t> m_ephemeralPortRange;\r\n};\r\n\r\n} // namespace wsl::core\r\n"
  },
  {
    "path": "src/windows/service/exe/PluginManager.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    PluginManager.cpp\n\nAbstract:\n\n    This file contains the PluginManager helper class implementation.\n\n--*/\n\n#include \"precomp.h\"\n#include \"install.h\"\n#include \"PluginManager.h\"\n#include \"WslPluginApi.h\"\n#include \"LxssUserSessionFactory.h\"\n\nusing wsl::windows::common::Context;\nusing wsl::windows::common::ExecutionContext;\nusing wsl::windows::service::PluginManager;\n\nconstexpr auto c_pluginPath = L\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\Plugins\";\n\nconstexpr WSLVersion Version = {wsl::shared::VersionMajor, wsl::shared::VersionMinor, wsl::shared::VersionRevision};\n\nthread_local std::optional<std::wstring> g_pluginErrorMessage;\n\nextern \"C\" {\nHRESULT MountFolder(WSLSessionId Session, LPCWSTR WindowsPath, LPCWSTR LinuxPath, BOOL ReadOnly, LPCWSTR Name)\ntry\n{\n    const auto session = FindSessionByCookie(Session);\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    auto result = session->MountRootNamespaceFolder(WindowsPath, LinuxPath, ReadOnly, Name);\n\n    WSL_LOG(\n        \"PluginMountFolderCall\",\n        TraceLoggingValue(WindowsPath, \"WindowsPath\"),\n        TraceLoggingValue(LinuxPath, \"LinuxPath\"),\n        TraceLoggingValue(ReadOnly, \"ReadOnly\"),\n        TraceLoggingValue(Name, \"Name\"),\n        TraceLoggingValue(result, \"Result\"));\n\n    return result;\n}\nCATCH_RETURN();\n\nHRESULT ExecuteBinary(WSLSessionId Session, LPCSTR Path, LPCSTR* Arguments, SOCKET* Socket)\ntry\n{\n\n    const auto session = FindSessionByCookie(Session);\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    auto result = session->CreateLinuxProcess(nullptr, Path, Arguments, Socket);\n\n    WSL_LOG(\"PluginExecuteBinaryCall\", TraceLoggingValue(Path, \"Path\"), TraceLoggingValue(result, \"Result\"));\n    return result;\n}\nCATCH_RETURN();\n\nHRESULT PluginError(LPCWSTR UserMessage)\ntry\n{\n    const auto* context = ExecutionContext::Current();\n    THROW_HR_IF(E_INVALIDARG, UserMessage == nullptr);\n    THROW_HR_IF_MSG(\n        E_ILLEGAL_METHOD_CALL, context == nullptr || WI_IsFlagClear(context->CurrentContext(), Context::Plugin), \"Message: %ls\", UserMessage);\n\n    // Logs when a WSL plugin hits an error and what that error message is\n    WSL_LOG_TELEMETRY(\"PluginError\", PDT_ProductAndServicePerformance, TraceLoggingValue(UserMessage, \"Message\"));\n\n    THROW_HR_IF(E_ILLEGAL_STATE_CHANGE, g_pluginErrorMessage.has_value());\n\n    g_pluginErrorMessage.emplace(UserMessage);\n\n    return S_OK;\n}\nCATCH_RETURN();\n\nHRESULT ExecuteBinaryInDistribution(WSLSessionId Session, const GUID* Distro, LPCSTR Path, LPCSTR* Arguments, SOCKET* Socket)\ntry\n{\n    THROW_HR_IF(E_INVALIDARG, Distro == nullptr);\n\n    const auto session = FindSessionByCookie(Session);\n    RETURN_HR_IF(RPC_E_DISCONNECTED, !session);\n\n    auto result = session->CreateLinuxProcess(Distro, Path, Arguments, Socket);\n\n    WSL_LOG(\"PluginExecuteBinaryInDistributionCall\", TraceLoggingValue(Path, \"Path\"), TraceLoggingValue(result, \"Result\"));\n\n    return result;\n}\nCATCH_RETURN();\n}\n\nstatic constexpr WSLPluginAPIV1 ApiV1 = {Version, &MountFolder, &ExecuteBinary, &PluginError, &ExecuteBinaryInDistribution};\n\nvoid PluginManager::LoadPlugins()\n{\n    ExecutionContext context(Context::Plugin);\n\n    const auto key = common::registry::CreateKey(HKEY_LOCAL_MACHINE, c_pluginPath, KEY_READ);\n    const auto values = common::registry::EnumValues(key.get());\n\n    std::set<std::wstring, wsl::shared::string::CaseInsensitiveCompare> loaded;\n    for (const auto& e : values)\n    {\n        if (e.second != REG_SZ)\n        {\n            LOG_HR_MSG(E_UNEXPECTED, \"Plugin value: '%ls' has incorrect type: %lu, skipping\", e.first.c_str(), e.second);\n            continue;\n        }\n\n        auto path = common::registry::ReadString(key.get(), nullptr, e.first.c_str());\n\n        if (!loaded.insert(path).second)\n        {\n            LOG_HR_MSG(E_UNEXPECTED, \"Module '%ls' has already been loaded, skipping plugin '%ls'\", path.c_str(), e.first.c_str());\n            continue;\n        }\n\n        auto loadResult = wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&]() { LoadPlugin(e.first.c_str(), path.c_str()); });\n\n        // Logs when a WSL plugin is loaded, used for evaluating plugin populations\n        WSL_LOG_TELEMETRY(\n            \"PluginLoad\",\n            PDT_ProductAndServiceUsage,\n            TraceLoggingValue(e.first.c_str(), \"Name\"),\n            TraceLoggingValue(path.c_str(), \"Path\"),\n            TraceLoggingValue(loadResult, \"Result\"));\n\n        if (FAILED(loadResult))\n        {\n            // If this plugin reported an error, record it to display it to the user\n            m_pluginError.emplace(PluginError{e.first, loadResult});\n        }\n    }\n}\n\nvoid PluginManager::LoadPlugin(LPCWSTR Name, LPCWSTR ModulePath)\n{\n    // Validate the plugin signature before loading it.\n    // The handle to the module is kept open after validating the signature so the file can't be written to\n    // after the signature check.\n    wil::unique_hfile pluginHandle;\n    if constexpr (wsl::shared::OfficialBuild)\n    {\n        pluginHandle = wsl::windows::common::install::ValidateFileSignature(ModulePath);\n        WI_ASSERT(pluginHandle.is_valid());\n    }\n\n    LoadedPlugin plugin{};\n    plugin.name = Name;\n\n    plugin.module.reset(LoadLibrary(ModulePath));\n    THROW_LAST_ERROR_IF_NULL(plugin.module);\n\n    const WSLPluginAPI_EntryPointV1 entryPoint =\n        reinterpret_cast<WSLPluginAPI_EntryPointV1>(GetProcAddress(plugin.module.get(), GSL_STRINGIFY(WSLPLUGINAPI_ENTRYPOINTV1)));\n\n    THROW_LAST_ERROR_IF_NULL(entryPoint);\n    THROW_IF_FAILED_MSG(entryPoint(&ApiV1, &plugin.hooks), \"Error returned by plugin: '%ls'\", ModulePath);\n\n    m_plugins.emplace_back(std::move(plugin));\n}\n\nvoid PluginManager::OnVmStarted(const WSLSessionInformation* Session, const WSLVmCreationSettings* Settings)\n{\n    ExecutionContext context(Context::Plugin);\n\n    for (const auto& e : m_plugins)\n    {\n        if (e.hooks.OnVMStarted != nullptr)\n        {\n            WSL_LOG(\n                \"PluginOnVmStartedCall\", TraceLoggingValue(e.name.c_str(), \"Plugin\"), TraceLoggingValue(Session->UserSid, \"Sid\"));\n\n            ThrowIfPluginError(e.hooks.OnVMStarted(Session, Settings), Session->SessionId, e.name.c_str());\n        }\n    }\n}\n\nvoid PluginManager::OnVmStopping(const WSLSessionInformation* Session) const\n{\n    ExecutionContext context(Context::Plugin);\n\n    for (const auto& e : m_plugins)\n    {\n        if (e.hooks.OnVMStopping != nullptr)\n        {\n            WSL_LOG(\n                \"PluginOnVmStoppingCall\", TraceLoggingValue(e.name.c_str(), \"Plugin\"), TraceLoggingValue(Session->UserSid, \"Sid\"));\n\n            const auto result = e.hooks.OnVMStopping(Session);\n            LOG_IF_FAILED_MSG(result, \"Error thrown from plugin: '%ls'\", e.name.c_str());\n        }\n    }\n}\n\nvoid PluginManager::OnDistributionStarted(const WSLSessionInformation* Session, const WSLDistributionInformation* Distribution)\n{\n    ExecutionContext context(Context::Plugin);\n\n    for (const auto& e : m_plugins)\n    {\n        if (e.hooks.OnDistributionStarted != nullptr)\n        {\n            WSL_LOG(\n                \"PluginOnDistroStartedCall\",\n                TraceLoggingValue(e.name.c_str(), \"Plugin\"),\n                TraceLoggingValue(Session->UserSid, \"Sid\"),\n                TraceLoggingValue(Distribution->Id, \"DistributionId\"));\n\n            ThrowIfPluginError(e.hooks.OnDistributionStarted(Session, Distribution), Session->SessionId, e.name.c_str());\n        }\n    }\n}\n\nvoid PluginManager::OnDistributionStopping(const WSLSessionInformation* Session, const WSLDistributionInformation* Distribution) const\n{\n    ExecutionContext context(Context::Plugin);\n\n    for (const auto& e : m_plugins)\n    {\n        if (e.hooks.OnDistributionStopping != nullptr)\n        {\n            WSL_LOG(\n                \"PluginOnDistroStoppingCall\",\n                TraceLoggingValue(e.name.c_str(), \"Plugin\"),\n                TraceLoggingValue(Session->UserSid, \"Sid\"),\n                TraceLoggingValue(Distribution->Id, \"DistributionId\"));\n\n            const auto result = e.hooks.OnDistributionStopping(Session, Distribution);\n            LOG_IF_FAILED_MSG(result, \"Error thrown from plugin: '%ls'\", e.name.c_str());\n        }\n    }\n}\n\nvoid PluginManager::OnDistributionRegistered(const WSLSessionInformation* Session, const WslOfflineDistributionInformation* Distribution) const\n{\n    ExecutionContext context(Context::Plugin);\n\n    for (const auto& e : m_plugins)\n    {\n        if (e.hooks.OnDistributionRegistered != nullptr)\n        {\n            WSL_LOG(\n                \"PluginOnDistributionRegisteredCall\",\n                TraceLoggingValue(e.name.c_str(), \"Plugin\"),\n                TraceLoggingValue(Session->UserSid, \"Sid\"),\n                TraceLoggingValue(Distribution->Id, \"DistributionId\"));\n\n            const auto result = e.hooks.OnDistributionRegistered(Session, Distribution);\n            LOG_IF_FAILED_MSG(result, \"Error thrown from plugin: '%ls'\", e.name.c_str());\n        }\n    }\n}\n\nvoid PluginManager::OnDistributionUnregistered(const WSLSessionInformation* Session, const WslOfflineDistributionInformation* Distribution) const\n{\n    ExecutionContext context(Context::Plugin);\n\n    for (const auto& e : m_plugins)\n    {\n        if (e.hooks.OnDistributionUnregistered != nullptr)\n        {\n            WSL_LOG(\n                \"PluginOnDistributionUnregisteredCall\",\n                TraceLoggingValue(e.name.c_str(), \"Plugin\"),\n                TraceLoggingValue(Session->UserSid, \"Sid\"),\n                TraceLoggingValue(Distribution->Id, \"DistributionId\"));\n\n            const auto result = e.hooks.OnDistributionUnregistered(Session, Distribution);\n            LOG_IF_FAILED_MSG(result, \"Error thrown from plugin: '%ls'\", e.name.c_str());\n        }\n    }\n}\n\nvoid PluginManager::ThrowIfPluginError(HRESULT Result, WSLSessionId Session, LPCWSTR Plugin)\n{\n    const auto message = std::move(g_pluginErrorMessage);\n    g_pluginErrorMessage.reset(); // std::move() doesn't clear the previous std::optional\n\n    if (FAILED(Result))\n    {\n        if (message.has_value())\n        {\n            THROW_HR_WITH_USER_ERROR(Result, wsl::shared::Localization::MessageFatalPluginErrorWithMessage(Plugin, message->c_str()));\n        }\n        else\n        {\n            THROW_HR_WITH_USER_ERROR(Result, wsl::shared::Localization::MessageFatalPluginError(Plugin));\n        }\n    }\n    else if (message.has_value())\n    {\n        THROW_HR_MSG(E_ILLEGAL_STATE_CHANGE, \"Plugin '%ls' emitted an error message but returned success\", Plugin);\n    }\n}\n\nvoid PluginManager::ThrowIfFatalPluginError() const\n{\n    ExecutionContext context(Context::Plugin);\n\n    if (!m_pluginError.has_value())\n    {\n        return;\n    }\n    else if (m_pluginError->error == WSL_E_PLUGIN_REQUIRES_UPDATE)\n    {\n        THROW_HR_WITH_USER_ERROR(\n            WSL_E_PLUGIN_REQUIRES_UPDATE, wsl::shared::Localization::MessagePluginRequiresUpdate(m_pluginError->plugin));\n    }\n    else\n    {\n        THROW_HR_WITH_USER_ERROR(m_pluginError->error, wsl::shared::Localization::MessageFatalPluginError(m_pluginError->plugin));\n    }\n}\n"
  },
  {
    "path": "src/windows/service/exe/PluginManager.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    PluginManager.h\n\nAbstract:\n\n    This file contains the PluginManager class definition.\n\n--*/\n\n#pragma once\n\n#include <wil/resource.h>\n#include <string>\n#include <vector>\n#include \"WslPluginApi.h\"\n\nnamespace wsl::windows::service {\nclass PluginManager\n{\npublic:\n    struct PluginError\n    {\n        std::wstring plugin;\n        HRESULT error;\n    };\n\n    PluginManager() = default;\n\n    PluginManager(const PluginManager&) = delete;\n    PluginManager& operator=(const PluginManager&) = delete;\n    PluginManager(PluginManager&&) = delete;\n    PluginManager& operator=(PluginManager&&) = delete;\n\n    void LoadPlugins();\n    void OnVmStarted(const WSLSessionInformation* Session, const WSLVmCreationSettings* Settings);\n    void OnVmStopping(const WSLSessionInformation* Session) const;\n    void OnDistributionStarted(const WSLSessionInformation* Session, const WSLDistributionInformation* distro);\n    void OnDistributionStopping(const WSLSessionInformation* Session, const WSLDistributionInformation* distro) const;\n    void OnDistributionRegistered(const WSLSessionInformation* Session, const WslOfflineDistributionInformation* distro) const;\n    void OnDistributionUnregistered(const WSLSessionInformation* Session, const WslOfflineDistributionInformation* distro) const;\n    void ThrowIfFatalPluginError() const;\n\nprivate:\n    void LoadPlugin(LPCWSTR Name, LPCWSTR Path);\n    static void ThrowIfPluginError(HRESULT Result, WSLSessionId session, LPCWSTR Plugin);\n\n    struct LoadedPlugin\n    {\n        wil::unique_hmodule module;\n        std::wstring name;\n        WSLPluginHooksV1 hooks{};\n    };\n\n    std::vector<LoadedPlugin> m_plugins;\n    std::optional<PluginError> m_pluginError;\n};\n\n} // namespace wsl::windows::service"
  },
  {
    "path": "src/windows/service/exe/ServiceMain.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ServiceMain.cpp\n\nAbstract:\n\n    This file contains the entrypoint for the Lxss Manager service.\n\n--*/\n\n#include \"precomp.h\"\n#include \"comservicehelper.h\"\n#include \"LxssSecurity.h\"\n#include \"WslCoreFilesystem.h\"\n#include \"LxssIpTables.h\"\n#include \"LxssUserSessionFactory.h\"\n#include <ctime>\n\nusing namespace wsl::windows::common::registry;\nusing namespace wsl::windows::common::string;\nusing namespace wsl::windows::common::wslutil;\nusing namespace wsl::windows::policies;\n\nbool g_lxcoreInitialized{false};\nwil::unique_event g_networkingReady{wil::EventOptions::ManualReset};\n\n// Declare the LxssUserSession COM class.\nCoCreatableClassWrlCreatorMapInclude(LxssUserSession);\n\nstruct WslServiceSecurityPolicy\n{\n    static LPCWSTR GetSDDLText()\n    {\n        // COM Access and Launch permissions allowed for authenticated user, principal self, and system.\n        // 0xB = (COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL | COM_RIGHTS_ACTIVATE_LOCAL)\n        // N.B. This should be kept in sync with the security descriptors in the appxmanifest and package.wix.\n        return L\"O:BAG:BAD:(A;;0xB;;;AU)(A;;0xB;;;PS)(A;;0xB;;;SY)\";\n    }\n};\n\nclass WslService : public Windows::Internal::Service<WslService, Windows::Internal::ContinueRunningWithNoObjects, WslServiceSecurityPolicy>\n{\npublic:\n    static wchar_t* GetName()\n    {\n        return const_cast<LPWSTR>(L\"WslService\");\n    }\n\n    static void OnSessionChanged(DWORD eventType, DWORD sessionId);\n    HRESULT OnServiceStarting();\n    HRESULT ServiceStarted();\n    void ServiceStopped();\n\nprivate:\n    static void __stdcall CheckForUpdates(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_ PVOID Context, _Inout_ PTP_TIMER);\n    static void ApplyProcessPolicies();\n\n    void CreateExplorerExtensions() noexcept;\n    void EvaluateWslPolicy();\n    void Initialize();\n    static void InitializePlan9Redirector();\n    void RegisterEventSource();\n    void StartCheckingForUpdates();\n\n    wil::unique_couninitialize_call m_coInit{false};\n    wil::unique_registry_watcher m_watcher;\n    wil::unique_threadpool_timer m_updateCheckTimer;\n    wil::unique_any_handle_null<decltype(&::DeregisterEventSource), ::DeregisterEventSource> m_eventLog;\n};\n\nvoid WslService::EvaluateWslPolicy()\n{\n    // If WSL is disabled, terminate any sessions and block future sessions from being created.\n    //\n    // N.B. This is done instead of failing service start so a proper error can be returned to the user.\n    const auto policiesKey = OpenPoliciesKey();\n    const auto enabled = IsFeatureAllowed(policiesKey.get(), c_allowWSL);\n    if (enabled)\n    {\n        Initialize();\n    }\n\n    SetSessionPolicy(enabled);\n}\n\nvoid WslService::Initialize()\n{\n    static std::once_flag flag{};\n    std::call_once(flag, [&]() {\n        // Initialize the connection to the LxCore driver.\n        //\n        // N.B. The WSL optional component is required on Windows 10. On Windows 11 and later,\n        //      the lifted WSL service can run but will only support WSL2 distros.\n        g_lxcoreInitialized = NT_SUCCESS(::LxssClientInitialize());\n\n        try\n        {\n            // Initialize the Plan 9 redirector (can fail iff the OC is not enabled on Win10).\n            // Failures here are silently ignored because we don't want the service to fail to start in that case\n            // so it can return WSL_E_WSL_OPTIONAL_COMPONENT_REQUIRED in LxssUserSession\n            InitializePlan9Redirector();\n        }\n        CATCH_LOG()\n\n        RegisterEventSource();\n    });\n}\n\nvoid WslService::InitializePlan9Redirector()\n{\n    // Make sure that the Plan 9 redirector trigger start prefix is correct.\n    try\n    {\n        // Acquire backup and restore privileges to modify the P9NP trigger start registry key.\n        auto restore = wsl::windows::common::security::AcquirePrivileges({SE_BACKUP_NAME, SE_RESTORE_NAME});\n\n        // Read the P9NP registry key and ensure it contains the correct value.\n        constexpr auto* keyName = L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\P9NP\\\\NetworkProvider\";\n        const auto key = CreateKey(HKEY_LOCAL_MACHINE, keyName, (KEY_READ | KEY_SET_VALUE), nullptr, REG_OPTION_BACKUP_RESTORE);\n        constexpr auto* valueName = L\"TriggerStartPrefix\";\n        DWORD valueType{};\n        THROW_IF_WIN32_ERROR(RegGetValueW(key.get(), nullptr, valueName, (RRF_RT_ANY | RRF_NOEXPAND), &valueType, nullptr, nullptr));\n        if (valueType != REG_MULTI_SZ)\n        {\n            // Because older Windows 10 builds won't have the p9rdr changes to support TriggerStartPrefix being a REG_MULTI_SZ,\n            // make sure that this build has the updated AppIdFlags value (added to support vp9fs being called from packaged context),\n            // which was added in the same commit.\n            // This theoretically shouldn't happen since the package shouldn't install on Windows 10 builds that are too old to\n            // support lifted, but if this block ran on such a build it would completely break p9rdr, so better safe than sorry.\n            if (!wsl::windows::common::helpers::IsWindows11OrAbove())\n            {\n                auto appIdFlags = ReadDword(HKEY_CLASSES_ROOT, L\"AppID\\\\{DFB65C4C-B34F-435D-AFE9-A86218684AA8}\", L\"AppIdFlags\", 0);\n                THROW_HR_IF_MSG(\n                    E_UNEXPECTED,\n                    WI_IsFlagClear(appIdFlags, APPIDREGFLAGS_AAA_NO_IMPLICIT_ACTIVATE_AS_IU),\n                    \"TriggerStartPrefix needs update, but AppIdFlags isn't up to date\");\n            }\n\n            WSL_LOG(\"Updating TriggerStartPrefix\", TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n            constexpr wchar_t newValue[] = L\"wsl.localhost\\0wsl$\\0\";\n            THROW_IF_WIN32_ERROR(RegSetValueEx(key.get(), valueName, 0, REG_MULTI_SZ, (BYTE*)newValue, sizeof(newValue)));\n        }\n    }\n    CATCH_LOG()\n\n    // Make sure the Plan 9 redirector driver is loaded.\n    wsl::windows::common::redirector::EnsureRedirectorStarted();\n}\n\nHRESULT WslService::OnServiceStarting()\ntry\n{\n    ConfigureCrt();\n\n    // Enable contextualized errors\n    wsl::windows::common::EnableContextualizedErrors(true);\n\n    // Initialize telemetry.\n    WslTraceLoggingInitialize(WslServiceTelemetryProvider, !wsl::shared::OfficialBuild);\n\n    WSL_LOG(\"Service starting\", TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n    // Don't kill the process on unknown C++ exceptions.\n    wil::g_fResultFailFastUnknownExceptions = false;\n\n    wsl::windows::common::security::ApplyProcessMitigationPolicies();\n\n    // Ensure that the OS has support for running lifted WSL.\n    THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED), !wsl::windows::common::helpers::IsWslSupportInterfacePresent());\n\n    // Initialize Winsock.\n    WSADATA Data;\n    THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &Data));\n\n    // Check if WSL is disabled via policy and set up a registry watcher to watch for changes.\n    //\n    // N.B. The registry watcher must be created before checking the policy to avoid missing notifications.\n    m_watcher = wil::make_registry_watcher(HKEY_LOCAL_MACHINE, ROOT_POLICIES_KEY, true, [this](wil::RegistryChangeKind) {\n        try\n        {\n            EvaluateWslPolicy();\n        }\n        CATCH_LOG()\n    });\n\n    EvaluateWslPolicy();\n    return S_OK;\n}\nCATCH_RETURN()\n\nvoid WslService::RegisterEventSource()\ntry\n{\n    m_eventLog.reset(::RegisterEventSource(nullptr, L\"WSL\"));\n    THROW_LAST_ERROR_IF(!m_eventLog);\n\n    wsl::windows::common::SetEventLog(m_eventLog.get());\n}\nCATCH_LOG();\n\nHRESULT WslService::ServiceStarted()\n{\n    m_coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);\n\n    // Cleanup any data from a previously aborted session (crash, power loss, etc).\n    LxssIpTables::CleanupRemnants();\n    g_networkingReady.SetEvent();\n\n    if constexpr (wsl::shared::OfficialBuild)\n    {\n        StartCheckingForUpdates();\n    }\n\n    return S_OK;\n}\n\nvoid WslService::OnSessionChanged(DWORD eventType, DWORD sessionId)\n{\n    if (eventType == WTS_SESSION_LOGOFF)\n    {\n        TerminateSession(sessionId);\n    }\n}\n\nvoid WslService::ServiceStopped()\n{\n    WSL_LOG(\"Service stopping\", TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n    // Stop checking for updates.\n    m_updateCheckTimer.reset();\n\n    // Stop watching the WSL policy registry keys.\n    m_watcher.reset();\n\n    // Terminate all user sessions.\n    ClearSessionsAndBlockNewInstances();\n\n    // Disconnect from the LxCore driver.\n    if (g_lxcoreInitialized)\n    {\n        LxssClientUninitialize();\n    }\n\n    // There is a potential deadlock if CoUninitialize() is called before the LanguageChangeNotifyThread\n    // isn't done initializing. Clearing the COM objects before calling CoUninitialize() works around the issue.\n    winrt::clear_factory_cache();\n\n    // Tear down telemetry.\n    WslTraceLoggingUninitialize();\n\n    // uninitialize COM. This must be done here because this call can cause cleanups that will be fail\n    // if the CRT is shutting down.\n    m_coInit.reset();\n}\n\nvoid WslService::StartCheckingForUpdates()\ntry\n{\n    const auto lxssKey = OpenLxssMachineKey(KEY_QUERY_VALUE);\n    constexpr std::uint64_t c_updateCheckPeriodDefaultMs = 24 * 60 * 60 * 1000; // 24h\n    const auto period =\n        wsl::windows::common::registry::ReadDword(lxssKey.get(), nullptr, L\"UpdateCheckPeriodMs\", c_updateCheckPeriodDefaultMs);\n\n    if (period <= 0)\n    {\n        WSL_LOG(\"Update check is disabled via the registry\", TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n        return;\n    }\n\n    m_updateCheckTimer.reset(CreateThreadpoolTimer(WslService::CheckForUpdates, this, nullptr));\n    THROW_LAST_ERROR_IF_NULL(m_updateCheckTimer);\n\n    // Check for updates at the configured period, starting one minute after the service starts.\n    auto dueTime = wil::filetime::from_int64(static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_minute));\n    SetThreadpoolTimer(m_updateCheckTimer.get(), &dueTime, period, 60 * 1000);\n}\nCATCH_LOG()\n\nvoid WslService::CheckForUpdates(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_ PVOID Context, _Inout_ PTP_TIMER)\ntry\n{\n    auto [version, _] = GetLatestGitHubRelease(false);\n    if (ParseWslPackageVersion(version) > ParseWslPackageVersion(TEXT(WSL_PACKAGE_VERSION)))\n    {\n        WSL_LOG(\"WSL Package update is available\", TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n        // Reset the timer since there's no reason to check for updates anymore.\n        SetThreadpoolTimer(static_cast<WslService*>(Context)->m_updateCheckTimer.get(), nullptr, 0, 0);\n\n        // Get current release date\n        std::wstring currentReleaseCreatedAtDate = GetGitHubReleaseByTag(TEXT(WSL_PACKAGE_VERSION)).created_at;\n\n        std::tm tm = {};\n        std::wstring dateTimeFormat = L\"%Y-%m-%dT%H:%M:%SZ\";\n        std::wistringstream ss(currentReleaseCreatedAtDate);\n        ss >> std::get_time(&tm, dateTimeFormat.c_str());\n        auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));\n\n        // If their release of WSL is older than 30 days, then show a notification to update\n        if (std::chrono::system_clock::now() - std::chrono::days(30) > tp)\n        {\n            // Create a notification to inform the user that an update is available\n            THROW_IF_FAILED(wsl::windows::common::notifications::DisplayUpdateNotification(version));\n\n            WSL_LOG(\"WSL Package update notification displayed\", TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n        }\n    }\n}\nCATCH_LOG()\n\nint __cdecl wmain()\n{\n    WslService::ProcessMain();\n    return 0;\n}\n"
  },
  {
    "path": "src/windows/service/exe/WslCoreAdviseHandler.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    WslCoreAdviseHandler.h\r\n\r\nAbstract:\r\n\r\n    This file contains WSL Core COM Advise/Unadvise functions.\r\n\r\n--*/\r\n\r\n#pragma once\r\n#include <vector>\r\n#include <objbase.h>\r\n#include <wil/com.h>\r\n#include <wil/resource.h>\r\n\r\nnamespace wsl::core {\r\n// This class encapsulates the non-obvious and sometimes non-trivial calls to register for COM callbacks\r\n// using the fairly standardized Advise ConnectionPoint interface\r\nclass WslCoreAdviseHandler\r\n{\r\npublic:\r\n    WslCoreAdviseHandler() = default;\r\n    ~WslCoreAdviseHandler() = default;\r\n\r\n    WslCoreAdviseHandler(const WslCoreAdviseHandler&) = delete;\r\n    WslCoreAdviseHandler& operator=(const WslCoreAdviseHandler&) = delete;\r\n    WslCoreAdviseHandler(WslCoreAdviseHandler&&) = delete;\r\n    WslCoreAdviseHandler& operator=(WslCoreAdviseHandler&&) = delete;\r\n\r\n    // typename T is the connection point interface which is implemented\r\n    // typename C is the server object implementing IConnectionPoint to Advise()\r\n    // typename S is the client's sink object (must implement type T)\r\n    template <typename T, typename C, typename S>\r\n    void AdviseInProcObject(wil::com_ptr<C> sourceObject, _In_ S* connectionSink)\r\n    {\r\n        AdviseInstance newInstance;\r\n\r\n        const wil::com_ptr<IConnectionPointContainer> pointContainer = sourceObject.template query<IConnectionPointContainer>();\r\n        THROW_IF_FAILED(pointContainer->FindConnectionPoint(__uuidof(T), newInstance.m_connectionPoint.addressof()));\r\n        THROW_IF_FAILED(newInstance.m_connectionPoint->Advise(connectionSink, &newInstance.m_cookie));\r\n\r\n        m_adviseInstances.emplace_back(std::move(newInstance));\r\n    }\r\n\r\n    // typename C is the instantiated object implementing IConnectionPoint to Advise()\r\n    // typename S is the interface to register with Advise()\r\n    // Also sets the authentication information that will be used to make calls on the specified proxy.\r\n    template <typename T, typename C, typename S>\r\n    void AdviseProxyObject(wil::com_ptr<C> sourceObject, _In_ S* connectionSink)\r\n    {\r\n        AdviseInstance newInstance;\r\n\r\n        const wil::com_ptr<IConnectionPointContainer> pointContainer = sourceObject.template query<IConnectionPointContainer>();\r\n        THROW_IF_FAILED(CoSetProxyBlanket(\r\n            pointContainer.get(), RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_NONE, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_STATIC_CLOAKING));\r\n\r\n        THROW_IF_FAILED(pointContainer->FindConnectionPoint(__uuidof(T), newInstance.m_connectionPoint.addressof()));\r\n        THROW_IF_FAILED(CoSetProxyBlanket(\r\n            newInstance.m_connectionPoint.get(), RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_NONE, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_STATIC_CLOAKING));\r\n\r\n        THROW_IF_FAILED(newInstance.m_connectionPoint->Advise(static_cast<IUnknown*>(connectionSink), &newInstance.m_cookie));\r\n        m_adviseInstances.emplace_back(std::move(newInstance));\r\n    }\r\n\r\n    void Reset() noexcept\r\n    {\r\n        m_adviseInstances.clear();\r\n    }\r\n\r\nprivate:\r\n    struct AdviseInstance\r\n    {\r\n        wil::com_ptr<IConnectionPoint> m_connectionPoint{};\r\n        DWORD m_cookie{0};\r\n\r\n        AdviseInstance() noexcept = default;\r\n        ~AdviseInstance() noexcept\r\n        {\r\n            if (m_connectionPoint && m_cookie != 0)\r\n            {\r\n                m_connectionPoint->Unadvise(m_cookie);\r\n            }\r\n        }\r\n\r\n        AdviseInstance(const AdviseInstance&) = delete;\r\n        AdviseInstance& operator=(const AdviseInstance&) = delete;\r\n        AdviseInstance(AdviseInstance&&) = default;\r\n        AdviseInstance& operator=(AdviseInstance&&) = default;\r\n    };\r\n\r\n    std::vector<AdviseInstance> m_adviseInstances;\r\n};\r\n} // namespace wsl::core"
  },
  {
    "path": "src/windows/service/exe/WslCoreGuestNetworkService.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include \"precomp.h\"\r\n\r\n#include \"WslCoreGuestNetworkService.h\"\r\n#include \"WslCoreNetworkingSupport.h\"\r\n\r\n#include <TraceLoggingProvider.h>\r\n\r\n#include \"Stringify.h\"\r\n#include \"WslTelemetry.h\"\r\n#include \"hns_schema.h\"\r\n\r\nstatic constexpr auto c_computeNetworkModuleName = L\"ComputeNetwork.dll\";\r\nstatic constexpr auto c_dnsPortNumber = 53;\r\nstatic constexpr auto c_mdnsPortNumber = 5353;\r\nstatic constexpr auto c_llmnrPortNumber = 5355;\r\n\r\nusing namespace wsl::shared;\r\n\r\nconstexpr IN_ADDR c_ipv4LoopbackAddr = IN4ADDR_LOOPBACK_INIT;\r\n\r\nstd::optional<LxssDynamicFunction<decltype(HcnReserveGuestNetworkServicePortRange)>> wsl::core::networking::GuestNetworkService::m_allocatePortRange;\r\nstd::optional<LxssDynamicFunction<decltype(HcnReserveGuestNetworkServicePort)>> wsl::core::networking::GuestNetworkService::m_allocatePort;\r\nstd::optional<LxssDynamicFunction<decltype(HcnReleaseGuestNetworkServicePortReservationHandle)>> wsl::core::networking::GuestNetworkService::m_releasePort;\r\n\r\nwsl::core::networking::GuestNetworkService::GuestNetworkService() noexcept\r\n{\r\n    if (wsl::core::networking::IsFlowSteeringSupportedByHns())\r\n    {\r\n\r\n        static std::once_flag flag;\r\n        std::call_once(flag, [&]() {\r\n            try\r\n            {\r\n                m_allocatePortRange.emplace(c_computeNetworkModuleName, \"HcnReserveGuestNetworkServicePortRange\");\r\n                m_allocatePort.emplace(c_computeNetworkModuleName, \"HcnReserveGuestNetworkServicePort\");\r\n                m_releasePort.emplace(c_computeNetworkModuleName, \"HcnReleaseGuestNetworkServicePortReservationHandle\");\r\n            }\r\n            CATCH_LOG()\r\n        });\r\n    }\r\n}\r\n\r\nvoid wsl::core::networking::GuestNetworkService::CreateGuestNetworkService(\r\n    const bool firewallEnabled, const std::set<uint16_t>& IgnoredPorts, const GUID& VmId, const UUID& ServerUuid, HCN_NOTIFICATION_CALLBACK Callback, void* CallbackContext)\r\n{\r\n    // we must first enable mirrored networking - which must by done by indirectly issuing a query with these special flags\r\n    wsl::core::networking::EnumerateMirroredNetworksAndHyperVFirewall(firewallEnabled);\r\n\r\n    m_ignoredPorts = IgnoredPorts;\r\n    // Always allow binds for 53. This is a workaround to unblock Docker Desktop and needs to be revisited in the future.\r\n    m_ignoredPorts.insert(c_dnsPortNumber);\r\n\r\n    hns::GuestNetworkService request{};\r\n    request.VirtualMachineId = VmId;\r\n    request.MirrorHostNetworking = true;\r\n    request.SchemaVersion = {2, 0};\r\n\r\n    request.GnsRpcServerInformation.EndpointType = hns::RpcEndpointType::LRpc;\r\n    request.GnsRpcServerInformation.ObjectUuid = ServerUuid;\r\n    WI_SetFlag(request.Flags, hns::GuestNetworkServiceFlags::IsFlowsteered);\r\n    WI_SetFlag(request.Flags, hns::GuestNetworkServiceFlags::IsFlowsteeredSelfManaged);\r\n\r\n    wil::unique_cotaskmem_string error;\r\n    const auto result = ::HcnCreateGuestNetworkService(VmId, ToJsonW(request).c_str(), &m_service, &error);\r\n    WSL_LOG(\r\n        \"GuestNetworkService::CreateGuestNetworkService [HcnCreateGuestNetworkService]\",\r\n        TraceLoggingValue(request.VirtualMachineId, \"virtualMachineId\"),\r\n        TraceLoggingValue(request.MirrorHostNetworking, \"mirrorHostNetworking\"),\r\n        TraceLoggingValue(request.SchemaVersion.Major, \"schemaMajorVersion\"),\r\n        TraceLoggingValue(request.SchemaVersion.Minor, \"schemaMinorVersion\"),\r\n        TraceLoggingValue(JsonEnumToString(request.GnsRpcServerInformation.EndpointType).c_str(), \"endpointType\"),\r\n        TraceLoggingValue(request.GnsRpcServerInformation.ObjectUuid, \"objectUuid\"),\r\n        TraceLoggingValue(static_cast<uint32_t>(request.Flags), \"flags-value\"),\r\n        TraceLoggingHResult(result, \"result\"),\r\n        TraceLoggingValue(error.is_valid() ? error.get() : L\"null\", \"errorString\"));\r\n    THROW_IF_FAILED_MSG(result, \"%ls\", error.get());\r\n\r\n    m_guestNetworkServiceCallback = windows::common::hcs::RegisterGuestNetworkServiceCallback(m_service, Callback, CallbackContext);\r\n    SetGuestNetworkServiceState(hns::GuestNetworkServiceState::Bootstrapping);\r\n}\r\n\r\nvoid wsl::core::networking::GuestNetworkService::SetGuestNetworkServiceState(_In_ hns::GuestNetworkServiceState State) const\r\n{\r\n    hns::ModifyGuestNetworkServiceSettingRequest modifyRequest{};\r\n    modifyRequest.RequestType = hns::ModifyRequestType::Update;\r\n    modifyRequest.ResourceType = hns::GuestNetworkServiceResourceType::State;\r\n    modifyRequest.Settings.State = State;\r\n\r\n    const auto result = ::HcnModifyGuestNetworkService(m_service.get(), ToJsonW(modifyRequest).c_str(), nullptr);\r\n    WSL_LOG(\r\n        \"GuestNetworkService::SetGuestNetworkServiceState [HcnModifyGuestNetworkService]\",\r\n        TraceLoggingValue(JsonEnumToString(modifyRequest.Settings.State).c_str(), \"state\"));\r\n    THROW_IF_FAILED(result);\r\n}\r\n\r\nstd::pair<uint16_t, uint16_t> wsl::core::networking::GuestNetworkService::AllocateEphemeralPortRange()\r\n{\r\n    FAIL_FAST_IF(!IsFlowSteeringSupportedByHns());\r\n\r\n    const auto lock = m_dataLock.lock_exclusive();\r\n\r\n    HANDLE port{nullptr};\r\n    auto releasePortOnError = wil::scope_exit([&] {\r\n        if (port)\r\n        {\r\n            m_releasePort.value()(port);\r\n        }\r\n    });\r\n\r\n    // N.B. Use an odd number of ports to avoid Linux kernel warning about preferring different parity for start / end values.\r\n    static constexpr auto c_ephemeralPortRangeSize = 4095;\r\n    THROW_IF_FAILED(m_allocatePortRange.value()(m_service.get(), c_ephemeralPortRangeSize, &m_reservedPortRange, &port));\r\n\r\n    WI_ASSERT(m_reservedPortRange.endingPort - m_reservedPortRange.startingPort == c_ephemeralPortRangeSize);\r\n\r\n    // setting the port to zero as we do not expect any bind requests to be sent to wslcore for ports in this range\r\n    m_reservedPorts.emplace(std::make_pair(HCN_PORT_PROTOCOL_TCP, static_cast<uint16_t>(0)), HcnPortReservation{port, 1});\r\n\r\n    // ownership of the port was transferred successfully\r\n    releasePortOnError.release();\r\n\r\n    WSL_LOG(\r\n        \"GuestNetworkService::AllocateEphemeralPortRange\",\r\n        TraceLoggingValue(m_reservedPortRange.startingPort, \"startingPort\"),\r\n        TraceLoggingValue(m_reservedPortRange.endingPort, \"endingPort\"));\r\n\r\n    return std::make_pair(m_reservedPortRange.startingPort, m_reservedPortRange.endingPort);\r\n}\r\n\r\nbool wsl::core::networking::GuestNetworkService::IsPortAllocationLoopbackException(const SOCKADDR_INET& Address) noexcept\r\n{\r\n    // Out of IPv4 loopback address range 127.0.0.0/8, only 127.0.0.1 is used by host<->guest loopback networking scenarios.\r\n    // FSE needs to be aware of binds using address 127.0.0.1, but can ignore binds for other IPv4 loopback addresses.\r\n    //\r\n    // Loopback traffic from the guest to the other IPv4 loopback addresses will stay in the guest.\r\n    //\r\n    // This also solves the issue of someone wanting to bind on the host to port 53 (known scenario is ICS)\r\n    // at the same time with someone binding to port 53 in the guest - known scenarios are:\r\n    // - DNS tunneling server that uses IP 127.0.0.42, port 53\r\n    // - systemd DNS resolver that uses IP 127.0.0.53, port 53\r\n    return (Address.si_family == AF_INET && IN4_IS_ADDR_LOOPBACK(&Address.Ipv4.sin_addr) && !IN4_ADDR_EQUAL(&Address.Ipv4.sin_addr, &c_ipv4LoopbackAddr));\r\n}\r\n\r\nbool wsl::core::networking::GuestNetworkService::IsPortAllocationMulticast(const SOCKADDR_INET& Address, _In_ int Protocol) noexcept\r\n{\r\n    const auto PortNumber = SS_PORT(&Address);\r\n\r\n    if ((Address.si_family == AF_INET && IN4_IS_ADDR_MULTICAST(&Address.Ipv4.sin_addr)) ||\r\n        (Address.si_family == AF_INET6 && IN6_IS_ADDR_MULTICAST(&Address.Ipv6.sin6_addr)))\r\n    {\r\n        return true;\r\n    }\r\n    // multicast DNS (mDNS)\r\n    else if (Protocol == IPPROTO_UDP && PortNumber == c_mdnsPortNumber)\r\n    {\r\n        return true;\r\n    }\r\n    // LLMNR DNS\r\n    else if (Protocol == IPPROTO_UDP && PortNumber == c_llmnrPortNumber)\r\n    {\r\n        return true;\r\n    }\r\n\r\n    return false;\r\n}\r\n\r\nint wsl::core::networking::GuestNetworkService::OnPortAllocationRequest(const SOCKADDR_INET& Address, _In_ int Protocol, _In_ bool Allocate) noexcept\r\ntry\r\n{\r\n    // The Linux and Windows constants conveniently have the same values for TCP & UDP.\r\n    WI_ASSERT(Protocol == IPPROTO_TCP || Protocol == IPPROTO_UDP);\r\n    WI_ASSERT(m_allocatePort.has_value() && m_releasePort.has_value());\r\n    auto HnsProtocol = Protocol == IPPROTO_TCP ? HCN_PORT_PROTOCOL_TCP : HCN_PORT_PROTOCOL_UDP;\r\n\r\n    const auto PortNumber = SS_PORT(&Address);\r\n    const auto StringAddress = wsl::windows::common::string::SockAddrInetToString(Address);\r\n\r\n    if (IsPortAllocationLoopbackException(Address))\r\n    {\r\n        WSL_LOG(\r\n            \"GuestNetworkService::OnPortAllocationRequest - allowing port allocation for loopback without asking FSE\",\r\n            TraceLoggingValue(StringAddress.c_str(), \"IP address\"),\r\n            TraceLoggingValue(Protocol == IPPROTO_TCP ? \"TCP\" : \"UDP\", \"protocol\"),\r\n            TraceLoggingValue(PortNumber, \"portNumber\"),\r\n            TraceLoggingValue(Address.si_family == AF_INET ? \"IPv4\" : \"IPv6\", \"address family\"),\r\n            TraceLoggingValue(Allocate, \"Allocate\"));\r\n        return 0;\r\n    }\r\n\r\n    if (m_ignoredPorts.find(PortNumber) != m_ignoredPorts.end())\r\n    {\r\n\r\n        WSL_LOG(\r\n            \"GuestNetworkService::OnPortAllocationRequest - allowing port allocation for ignored port without asking FSE\",\r\n            TraceLoggingValue(StringAddress.c_str(), \"IP address\"),\r\n            TraceLoggingValue(Protocol == IPPROTO_TCP ? \"TCP\" : \"UDP\", \"protocol\"),\r\n            TraceLoggingValue(PortNumber, \"portNumber\"),\r\n            TraceLoggingValue(Address.si_family == AF_INET ? \"IPv4\" : \"IPv6\", \"address family\"),\r\n            TraceLoggingValue(Allocate, \"Allocate\"));\r\n        return 0;\r\n    }\r\n\r\n    const auto lock = m_dataLock.lock_exclusive();\r\n\r\n    if (PortNumber >= m_reservedPortRange.startingPort && PortNumber <= m_reservedPortRange.endingPort)\r\n    {\r\n        WSL_LOG(\r\n            \"GuestNetworkService::OnPortAllocationRequest\",\r\n            TraceLoggingValue(\r\n                \"Guest attempted to allocate a port but it was already allocated through port reservations\", \"status\"),\r\n            TraceLoggingValue(HnsProtocol == HCN_PORT_PROTOCOL_TCP ? \"TCP\" : \"UDP\", \"protocol\"),\r\n            TraceLoggingValue(PortNumber, \"portNumber\"),\r\n            TraceLoggingValue(StringAddress.c_str(), \"IP address\"));\r\n        return 0;\r\n    }\r\n\r\n    HRESULT result = E_UNEXPECTED;\r\n    const auto it = m_reservedPorts.find(std::make_pair(HnsProtocol, PortNumber));\r\n    if (Allocate)\r\n    {\r\n        if (it != m_reservedPorts.end())\r\n        {\r\n            it->second.ReferenceCount++;\r\n            WSL_LOG(\r\n                \"GuestNetworkService::OnPortAllocationRequest - incremented reference\",\r\n                TraceLoggingValue(PortNumber, \"Port\"),\r\n                TraceLoggingValue(Address.si_family, \"Family\"),\r\n                TraceLoggingValue(StringAddress.c_str(), \"IP address\"),\r\n                TraceLoggingValue(Protocol, \"Protocol\"),\r\n                TraceLoggingValue(it->second.ReferenceCount, \"ReferenceCount\"));\r\n            return 0;\r\n        }\r\n\r\n        HANDLE port{nullptr};\r\n        auto releasePortOnError = wil::scope_exit([&] {\r\n            if (port)\r\n            {\r\n                m_releasePort.value()(port);\r\n            }\r\n        });\r\n\r\n        bool isMulticast = IsPortAllocationMulticast(Address, Protocol);\r\n\r\n        // Multicast port allocations are requested using the \"shared\" flag.\r\n        result = m_allocatePort.value()(\r\n            m_service.get(), HnsProtocol, isMulticast ? HCN_PORT_ACCESS_SHARED : HCN_PORT_ACCESS_EXCLUSIVE, PortNumber, &port);\r\n\r\n        if (SUCCEEDED(result))\r\n        {\r\n            m_reservedPorts.emplace(std::make_pair(HnsProtocol, PortNumber), HcnPortReservation{port, 1});\r\n        }\r\n        // if the port was reserved, we successfully handed over ownership\r\n        releasePortOnError.release();\r\n\r\n        WSL_LOG(\r\n            \"GuestNetworkService::OnPortAllocationRequest [HcnReserveGuestNetworkServicePort]\",\r\n            TraceLoggingValue(HnsProtocol == HCN_PORT_PROTOCOL_TCP ? \"TCP\" : \"UDP\", \"protocol\"),\r\n            TraceLoggingValue(PortNumber, \"portNumber\"),\r\n            TraceLoggingValue(StringAddress.c_str(), \"IP address\"),\r\n            TraceLoggingValue(isMulticast, \"isMulticast\"),\r\n            TraceLoggingValue(result, \"result\"));\r\n    }\r\n    else\r\n    {\r\n        if (it == m_reservedPorts.end())\r\n        {\r\n            RETURN_HR_MSG(E_UNEXPECTED, \"Guest attempted to deallocate port (%i, %i), but it's not allocated\", Protocol, PortNumber);\r\n        }\r\n\r\n        if (it->second.ReferenceCount == 1)\r\n        {\r\n            result = m_releasePort.value()(it->second.Handle);\r\n            m_reservedPorts.erase(it);\r\n            WSL_LOG(\r\n                \"GuestNetworkService::OnPortAllocationRequest - released port\",\r\n                TraceLoggingValue(PortNumber, \"Port\"),\r\n                TraceLoggingValue(Address.si_family, \"Family\"),\r\n                TraceLoggingValue(StringAddress.c_str(), \"IP address\"),\r\n                TraceLoggingValue(Protocol, \"Protocol\"));\r\n        }\r\n        else\r\n        {\r\n            it->second.ReferenceCount--;\r\n            WSL_LOG(\r\n                \"GuestNetworkService::OnPortAllocationRequest - decremented reference\",\r\n                TraceLoggingValue(PortNumber, \"Port\"),\r\n                TraceLoggingValue(Address.si_family, \"Family\"),\r\n                TraceLoggingValue(StringAddress.c_str(), \"IP address\"),\r\n                TraceLoggingValue(Protocol, \"Protocol\"),\r\n                TraceLoggingValue(it->second.ReferenceCount, \"ReferenceCount\"));\r\n            return 0;\r\n        }\r\n    }\r\n\r\n    return SUCCEEDED(result) ? 0 : -LX_EADDRINUSE;\r\n}\r\ncatch (...)\r\n{\r\n    LOG_CAUGHT_EXCEPTION();\r\n    return -LX_ENOBUFS;\r\n}\r\n\r\nvoid wsl::core::networking::GuestNetworkService::Stop() noexcept\r\n{\r\n    if (m_releasePort)\r\n    {\r\n        const auto lock = m_dataLock.lock_exclusive();\r\n\r\n        for (const auto& reservedPort : m_reservedPorts)\r\n        {\r\n            m_releasePort.value()(reservedPort.second.Handle);\r\n        }\r\n        m_reservedPorts.clear();\r\n    }\r\n\r\n    m_guestNetworkServiceCallback.reset();\r\n\r\n    if (m_service)\r\n    {\r\n        wil::unique_cotaskmem_string error;\r\n        const auto result = ::HcnDeleteGuestNetworkService(m_id, &error);\r\n        LOG_IF_FAILED_MSG(result, \"HcnDeleteGuestNetworkService failed, %ls\", error.get());\r\n        m_service.reset();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/service/exe/WslCoreGuestNetworkService.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n#include <optional>\r\n#include <map>\r\n#include <utility>\r\n\r\n#include <ComputeNetwork.h>\r\n#include <LxssDynamicFunction.h>\r\n\r\n#include \"hcs.hpp\"\r\n\r\n#include <wil/resource.h>\r\n\r\nnamespace wsl::core::networking {\r\nclass GuestNetworkService\r\n{\r\npublic:\r\n    GuestNetworkService() noexcept;\r\n\r\n    ~GuestNetworkService() noexcept\r\n    {\r\n        Stop();\r\n    }\r\n\r\n    GuestNetworkService(const GuestNetworkService& other) = delete;\r\n    GuestNetworkService(GuestNetworkService&& other) = delete;\r\n    GuestNetworkService& operator=(const GuestNetworkService& other) = delete;\r\n    GuestNetworkService& operator=(GuestNetworkService&& other) = delete;\r\n\r\n    void CreateGuestNetworkService(\r\n        const bool firewallEnabled,\r\n        const std::set<USHORT>& IgnoredPorts,\r\n        const GUID& VmId,\r\n        const UUID& ServerUuid,\r\n        HCN_NOTIFICATION_CALLBACK Callback,\r\n        void* CallbackContext);\r\n\r\n    void SetGuestNetworkServiceState(_In_ wsl::shared::hns::GuestNetworkServiceState State) const;\r\n\r\n    std::pair<uint16_t, uint16_t> AllocateEphemeralPortRange();\r\n\r\n    int OnPortAllocationRequest(const SOCKADDR_INET& Address, _In_ int Protocol, _In_ bool Allocate) noexcept;\r\n\r\n    void Stop() noexcept;\r\n\r\nprivate:\r\n    struct HcnPortReservation\r\n    {\r\n        // The consumer of the port reservations requests reservations at a {SOCKADDR_INET, Protocol} granularity.\r\n        // The HCN port reservation API allows reservations at a {PortNumber, Protocol} granularity.\r\n        // Using a reference count to coalesce consumer requests to their appropriate HCN requests.\r\n        HANDLE Handle;\r\n        ULONG ReferenceCount;\r\n    };\r\n\r\n    // Returns true if the port allocation should be always allowed, without Windows.\r\n    static bool IsPortAllocationLoopbackException(const SOCKADDR_INET& Address) noexcept;\r\n\r\n    static bool IsPortAllocationMulticast(const SOCKADDR_INET& Address, _In_ int Protocol) noexcept;\r\n\r\n    static std::optional<LxssDynamicFunction<decltype(HcnReserveGuestNetworkServicePortRange)>> m_allocatePortRange;\r\n    static std::optional<LxssDynamicFunction<decltype(HcnReserveGuestNetworkServicePort)>> m_allocatePort;\r\n    static std::optional<LxssDynamicFunction<decltype(HcnReleaseGuestNetworkServicePortReservationHandle)>> m_releasePort;\r\n\r\n    wsl::windows::common::hcs::unique_hcn_guest_network_service m_service;\r\n    wsl::windows::common::hcs::unique_hcn_guest_network_service_callback m_guestNetworkServiceCallback;\r\n    GUID m_id{};\r\n    wil::srwlock m_dataLock;\r\n    _Guarded_by_(m_dataLock) std::set<uint16_t> m_ignoredPorts;\r\n    _Guarded_by_(m_dataLock) std::map<std::pair<HCN_PORT_PROTOCOL, USHORT>, HcnPortReservation> m_reservedPorts;\r\n    _Guarded_by_(m_dataLock) HCN_PORT_RANGE_RESERVATION m_reservedPortRange {};\r\n};\r\n} // namespace wsl::core::networking\r\n"
  },
  {
    "path": "src/windows/service/exe/WslCoreInstance.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreInstance.cpp\n\nAbstract:\n\n    This file contains WSL Core Instance function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"WslCoreInstance.h\"\n\nWslCoreInstance::WslCoreInstance(\n    _In_ HANDLE UserToken,\n    _In_ wil::unique_socket& InitSocket,\n    _In_ wil::unique_socket& SystemDistroSocket,\n    _In_ const GUID& InstanceId,\n    _In_ const GUID& RuntimeId,\n    _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n    _In_ ULONG DefaultUid,\n    _In_ ULONG64 ClientLifetimeId,\n    _In_ const std::function<LX_INIT_DRVFS_MOUNT(HANDLE)>& DrvFsCallback,\n    _In_ ULONG FeatureFlags,\n    _In_ DWORD SocketTimeout,\n    _In_ int IdleTimeout,\n    _Out_opt_ ULONG* ConnectPort) :\n    LxssRunningInstance(IdleTimeout),\n    m_featureFlags(FeatureFlags),\n    m_instanceId(InstanceId),\n    m_runtimeId(RuntimeId),\n    m_configuration(Configuration),\n    m_defaultUid(DefaultUid),\n    m_initializeDrvFs(DrvFsCallback),\n    m_ntClientLifetimeId(ClientLifetimeId),\n    m_redirectorConnectionTargets{m_configuration.Name},\n    m_socketTimeout(SocketTimeout)\n{\n    // Establish a communication channel with the init daemon.\n    m_initChannel = std::make_shared<WslCorePort>(InitSocket.release(), m_runtimeId, m_socketTimeout);\n\n    // Read a message from the init daemon. This will let us know if anything failed during startup.\n    gsl::span<gsl::byte> span;\n    const auto& result = m_initChannel->GetChannel().ReceiveMessage<LX_MINI_INIT_CREATE_INSTANCE_RESULT>(&span, m_socketTimeout);\n    if (result.WarningsOffset != 0)\n    {\n        for (const auto& e : wsl::shared::string::Split<char>(wsl::shared::string::FromSpan(span, result.WarningsOffset), '\\n'))\n        {\n            if (!e.empty())\n            {\n                EMIT_USER_WARNING(wsl::shared::string::MultiByteToWide(e));\n            }\n        }\n    }\n\n    if (result.Result != 0)\n    {\n        // N.B. EUCLEAN (117) can be returned if the disk's journal is corrupted.\n        if ((result.Result == EINVAL || result.Result == 117) && result.FailureStep == LxInitCreateInstanceStepMountDisk)\n        {\n            THROW_HR(WSL_E_DISK_CORRUPTED);\n        }\n        else\n        {\n            THROW_HR_WITH_USER_ERROR(\n                E_FAIL, wsl::shared::Localization::MessageDistributionFailedToStart(result.Result, static_cast<int>(result.FailureStep)));\n        }\n    }\n\n    m_clientId = static_cast<ULONG>(result.Pid);\n    if (ConnectPort != nullptr)\n    {\n        *ConnectPort = result.ConnectPort;\n    }\n\n    // Set a flag if the rootfs folder is compressed.\n    //\n    // N.B. The system distro has an empty base path.\n    if (!m_configuration.BasePath.empty())\n    {\n        WI_SetFlagIf(m_featureFlags, LxInitFeatureRootfsCompressed, WI_IsFlagSet(GetFileAttributesW(m_configuration.BasePath.c_str()), FILE_ATTRIBUTE_COMPRESSED));\n    }\n\n    // Copy immutable distribution data into the info structure.\n    m_distributionInfo.Id = m_configuration.DistroId;\n    m_distributionInfo.Name = m_configuration.Name.c_str();\n    m_distributionInfo.PackageFamilyName = m_configuration.PackageFamilyName.c_str();\n    m_distributionInfo.InitPid = m_clientId;\n\n    // Duplicate the passed-in user token.\n    THROW_IF_WIN32_BOOL_FALSE(::DuplicateTokenEx(UserToken, MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenImpersonation, &m_userToken));\n\n    // If a system distro socket was provided, create a system distro for this instance.\n    if (SystemDistroSocket)\n    {\n        LXSS_DISTRO_CONFIGURATION systemDistroConfig{};\n        systemDistroConfig.DistroId = Configuration.DistroId;\n        systemDistroConfig.State = LxssDistributionStateInstalled;\n        systemDistroConfig.Version = LXSS_DISTRO_VERSION_2;\n        systemDistroConfig.Flags = (LXSS_DISTRO_FLAGS_DEFAULT | LXSS_DISTRO_FLAGS_VM_MODE);\n\n        // Allow interop requests from init (pid 1) and disable the 9p server.\n        ULONG systemDistroFeatureFlags = m_featureFlags;\n        WI_SetFlag(systemDistroFeatureFlags, LxInitFeatureDisable9pServer);\n        WI_SetFlag(systemDistroFeatureFlags, LxInitFeatureSystemDistro);\n\n        // Create an instance for the system distro, this will fail if the distro has opted-out of\n        // GUI applications via /etc/wsl.conf.\n        try\n        {\n            wil::unique_socket empty{};\n            m_systemDistro = std::make_shared<WslCoreInstance>(\n                UserToken,\n                SystemDistroSocket,\n                empty,\n                WSL2_SYSTEM_DISTRO_GUID,\n                RuntimeId,\n                systemDistroConfig,\n                LX_UID_ROOT,\n                ClientLifetimeId,\n                DrvFsCallback,\n                systemDistroFeatureFlags,\n                m_socketTimeout,\n                IdleTimeout);\n        }\n        CATCH_LOG()\n    }\n}\n\nWslCoreInstance::~WslCoreInstance()\n{\n    if (m_oobeThread.joinable())\n    {\n        m_destroyingEvent.SetEvent();\n        m_oobeThread.join();\n    }\n}\n\nvoid WslCoreInstance::CreateLxProcess(\n    _In_ const CreateLxProcessData& CreateProcessData,\n    _In_ const CreateLxProcessContext& CreateProcessContext,\n    _In_ const CreateLxProcessConsoleData& ConsoleData,\n    _In_ SHORT Columns,\n    _In_ SHORT Rows,\n    _In_ PLXSS_STD_HANDLES StdHandles,\n    _Out_ GUID* InstanceId,\n    _Out_ HANDLE* ProcessHandle,\n    _Out_ HANDLE* ServerHandle,\n    _Out_ HANDLE* StandardIn,\n    _Out_ HANDLE* StandardOut,\n    _Out_ HANDLE* StandardErr,\n    _Out_ HANDLE* CommunicationChannel,\n    _Out_ HANDLE* InteropSocket)\n{\n    LX_INIT_DRVFS_MOUNT drvfsMount = LxInitDrvfsMountNone;\n    // If drive mounting is supported, ensure that DrvFs has been initialized.\n    if (WI_IsFlagSet(m_configuration.Flags, LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING))\n    {\n        drvfsMount = m_initializeDrvFs(CreateProcessContext.UserToken.get());\n    }\n\n    // Ensure the instance is still running.\n\n    std::lock_guard lock(m_lock);\n    THROW_HR_IF(HCS_E_TERMINATED, (!m_initChannel || !m_consoleManager));\n\n    if (m_oobeCompleteEvent && !m_oobeCompleteEvent.is_signaled())\n    {\n        EMIT_USER_WARNING(wsl::shared::Localization::MessageWaitingForOobe(m_configuration.Name.c_str()));\n        m_oobeCompleteEvent.wait();\n    }\n\n    // Initialize the create process message.\n    // N.B. m_defaultUid can only be read after m_oobeCompleteEvent is signaled since OOBE can change the default UID.\n    auto messageBuffer = LxssCreateProcess::CreateMessage(LxInitMessageCreateProcessUtilityVm, CreateProcessData, m_defaultUid);\n\n    const auto messageSpan = gsl::make_span(messageBuffer);\n    const auto message = gslhelpers::get_struct<LX_INIT_CREATE_PROCESS_UTILITY_VM>(messageSpan);\n\n    // m_initializeDrvFs returns true if admin share should be used\n    if (drvfsMount == LxInitDrvfsMountElevated && !m_adminMountNamespaceCreated)\n    {\n        MountDrvfs(true);\n        m_adminMountNamespaceCreated = true;\n    }\n    else if (drvfsMount == LxInitDrvfsMountNonElevated && !m_nonAdminMountNamespaceCreated)\n    {\n        MountDrvfs(false);\n        m_nonAdminMountNamespaceCreated = true;\n    }\n\n    message->Columns = Columns;\n    message->Rows = Rows;\n    WI_SetFlagIf(message->Common.Flags, LxInitCreateProcessFlagsStdInConsole, (StdHandles->StdIn.HandleType == LxssHandleConsole));\n    WI_SetFlagIf(message->Common.Flags, LxInitCreateProcessFlagsStdOutConsole, (StdHandles->StdOut.HandleType == LxssHandleConsole));\n    WI_SetFlagIf(message->Common.Flags, LxInitCreateProcessFlagsStdErrConsole, (StdHandles->StdErr.HandleType == LxssHandleConsole));\n    WI_SetFlagIf(message->Common.Flags, LxInitCreateProcessFlagsElevated, (drvfsMount == LxInitDrvfsMountElevated));\n    WI_SetFlagIf(message->Common.Flags, LxInitCreateProcessFlagsInteropEnabled, LXSS_INTEROP_ENABLED(CreateProcessContext.Flags));\n\n    if (m_configuration.RunOOBE && CreateProcessData.Filename.empty() && CreateProcessData.CommandLine.empty())\n    {\n        WI_SetFlag(message->Common.Flags, LxInitCreateProcessFlagAllowOOBE);\n    }\n\n    // Create a session leader if needed.\n    const auto sessionLeader =\n        std::static_pointer_cast<WslCorePort>(m_consoleManager->GetSessionLeader(ConsoleData, CreateProcessContext.Elevated));\n\n    // Lock the session leader connection and send a create process message.\n    //\n    // N.B. The session leader must be locked to ensure that the create process\n    //      message and response are received by the correct endpoints.\n    ULONG port;\n    {\n        auto sessionLock = sessionLeader->Lock();\n        port = sessionLeader->GetChannel().Transaction<LX_INIT_CREATE_PROCESS_UTILITY_VM>(messageSpan).Result;\n    }\n\n    // Connect to the port specified by the session leader.\n    std::vector<wil::unique_socket> sockets(LX_INIT_UTILITY_VM_CREATE_PROCESS_SOCKET_COUNT);\n    if (WI_IsFlagSet(message->Common.Flags, LxInitCreateProcessFlagAllowOOBE))\n    {\n        sockets.emplace_back();\n    }\n\n    for (auto& socket : sockets)\n    {\n        socket = wsl::windows::common::hvsocket::Connect(m_runtimeId, port);\n    }\n\n    *InstanceId = m_runtimeId;\n    *ProcessHandle = nullptr;\n    *ServerHandle = nullptr;\n    *StandardIn = reinterpret_cast<HANDLE>(sockets[0].release());\n    *StandardOut = reinterpret_cast<HANDLE>(sockets[1].release());\n    *StandardErr = reinterpret_cast<HANDLE>(sockets[2].release());\n    *CommunicationChannel = reinterpret_cast<HANDLE>(sockets[3].release());\n    *InteropSocket = reinterpret_cast<HANDLE>(sockets[4].release());\n\n    if (WI_IsFlagSet(message->Common.Flags, LxInitCreateProcessFlagAllowOOBE))\n    {\n        {\n            m_oobeCompleteEvent.create(wil::EventOptions::ManualReset);\n\n            auto impersonate = wil::CoImpersonateClient();\n            auto registration = wsl::windows::service::DistributionRegistration::Open(\n                wsl::windows::common::registry::OpenLxssUserKey().get(), m_configuration.DistroId);\n\n            // Wait for a potential previous oobe thread to complete before creating a new one.\n            if (m_oobeThread.joinable())\n            {\n                m_oobeThread.join();\n            }\n\n            m_oobeThread = std::thread([this, socket = std::move(sockets[5]), registration = std::move(registration)]() mutable {\n                try\n                {\n                    ReadOOBEResult(std::move(socket), std::move(registration));\n                }\n                CATCH_LOG()\n\n                m_oobeCompleteEvent.SetEvent();\n            });\n        }\n    }\n}\n\nvoid WslCoreInstance::ReadOOBEResult(wil::unique_socket&& Socket, wsl::windows::service::DistributionRegistration&& registration)\n{\n    wsl::shared::SocketChannel channel(std::move(Socket), \"OOBE\", m_destroyingEvent.get());\n\n    const auto* oobeResult = channel.ReceiveMessageOrClosed<LX_INIT_OOBE_RESULT>().first;\n\n    if (oobeResult == nullptr)\n    {\n        LOG_HR_MSG(E_FAIL, \"OOBE channel closed\");\n        return;\n    }\n\n    // Logs the result of the OOBE process\n    WSL_LOG_TELEMETRY(\n        \"OOBEResult\",\n        PDT_ProductAndServicePerformance,\n        TraceLoggingValue(oobeResult->Result, \"Result\"),\n        TraceLoggingValue(oobeResult->DefaultUid, \"DefaultUid\"),\n        TraceLoggingValue(m_configuration.Name.c_str(), \"Name\"),\n        TraceLoggingValue(2, \"Version\"));\n\n    if (oobeResult->Result == 0)\n    {\n        // OOBE was successful, don't run it again.\n        m_configuration.RunOOBE = false;\n        registration.Write(wsl::windows::service::Property::RunOOBE, 0);\n\n        if (oobeResult->DefaultUid != -1)\n        {\n            registration.Write(wsl::windows::service::Property::DefaultUid, static_cast<int>(oobeResult->DefaultUid));\n            m_defaultUid = static_cast<int>(oobeResult->DefaultUid);\n        }\n    }\n}\n\nULONG WslCoreInstance::GetClientId() const\n{\n    // Return the system distro ClientId if any so that this distribution is correctly\n    // identified if the system distro init process terminates.\n    if (m_systemDistro)\n    {\n        return m_systemDistro->GetClientId();\n    }\n\n    return m_clientId;\n}\n\nGUID WslCoreInstance::GetDistributionId() const\n{\n    return m_configuration.DistroId;\n}\n\nstd::shared_ptr<LxssPort> WslCoreInstance::GetInitPort()\n{\n    THROW_HR_IF(HCS_E_TERMINATED, !m_initChannel);\n\n    return m_initChannel;\n}\n\nstd::shared_ptr<LxssRunningInstance> WslCoreInstance::GetSystemDistro()\n{\n    return m_systemDistro;\n}\n\nvoid WslCoreInstance::UpdateTimezone()\n{\n    if (m_systemDistro)\n    {\n        m_systemDistro->UpdateTimezone();\n    }\n\n    auto message =\n        wsl::windows::common::helpers::GenerateTimezoneUpdateMessage(wsl::windows::common::helpers::GetLinuxTimezone(m_userToken.get()));\n\n    auto lock = m_initChannel->Lock();\n    m_initChannel->GetChannel().SendMessage<LX_INIT_TIMEZONE_INFORMATION>(gsl::make_span(message));\n}\n\nULONG64 WslCoreInstance::GetLifetimeManagerId() const\n{\n    return m_ntClientLifetimeId;\n}\n\nvoid WslCoreInstance::Initialize()\n{\n    // Check if the instance has already been initialized.\n    std::lock_guard lock(m_lock);\n    if (m_initialized)\n    {\n        return;\n    }\n\n    // If a system distro was created, initialize it first.\n    if (m_systemDistro)\n    {\n        m_systemDistro->Initialize();\n    }\n\n    LX_INIT_DRVFS_MOUNT drvfsMount = LxInitDrvfsMountNone;\n\n    // If drive mounting is supported, ensure that DrvFs has been initialized.\n    if (WI_IsFlagSet(m_configuration.Flags, LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING))\n    {\n        drvfsMount = m_initializeDrvFs(m_userToken.get());\n    }\n\n    // Create a console manager that will be used to manage session leaders.\n    m_consoleManager = ConsoleManager::CreateConsoleManager(m_initChannel);\n\n    // Send the initial configuration information to the init daemon.\n    ULONG fixedDrives = 0;\n    if (WI_IsFlagSet(m_configuration.Flags, LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING))\n    {\n        fixedDrives = wsl::windows::common::filesystem::EnumerateFixedDrives().first;\n    }\n\n    const auto timezone = wsl::windows::common::helpers::GetLinuxTimezone();\n    auto config = wsl::windows::common::helpers::GenerateConfigurationMessage(\n        m_configuration.Name, fixedDrives, m_defaultUid, timezone, {}, m_featureFlags, drvfsMount);\n\n    m_initChannel->GetChannel().SendMessage<LX_INIT_CONFIGURATION_INFORMATION>(gsl::span(config));\n\n    // Init replies with information about the distribution.\n    gsl::span<gsl::byte> span;\n    const auto& response = m_initChannel->GetChannel().ReceiveMessage<LX_INIT_CONFIGURATION_INFORMATION_RESPONSE>(&span);\n    m_defaultUid = response.DefaultUid;\n    m_plan9Port = response.Plan9Port;\n    m_distributionInfo.PidNamespace = response.PidNamespace;\n\n    if (response.VersionIndex > 0)\n    {\n        m_configuration.OsVersion = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(span, response.VersionIndex));\n        m_distributionInfo.Version = m_configuration.OsVersion.c_str();\n    }\n\n    if (response.FlavorIndex > 0)\n    {\n        m_configuration.Flavor = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(span, response.FlavorIndex));\n        m_distributionInfo.Flavor = m_configuration.Flavor.c_str();\n    }\n\n    // Launch the interop server with the user's token.\n    if (response.InteropPort != LX_INIT_UTILITY_VM_INVALID_PORT)\n    {\n        try\n        {\n            const wil::unique_socket socket{wsl::windows::common::hvsocket::Connect(m_runtimeId, response.InteropPort)};\n            wil::unique_handle info{wsl::windows::common::helpers::LaunchInteropServer(\n                nullptr, reinterpret_cast<HANDLE>(socket.get()), nullptr, nullptr, &m_runtimeId, m_userToken.get())};\n        }\n        CATCH_LOG()\n    }\n\n    // Initialization was successful.\n    m_initialized = true;\n\n    // The initialization message mounts the drvfs drives, so make sure we don't try it again.\n    if (drvfsMount == LxInitDrvfsMountElevated)\n    {\n        m_adminMountNamespaceCreated = true;\n    }\n    else if (drvfsMount == LxInitDrvfsMountNonElevated)\n    {\n        m_nonAdminMountNamespaceCreated = true;\n    }\n\n    WSL_LOG(\n        \"WslCoreInstanceInitialize\",\n        TraceLoggingValue(m_configuration.Name.c_str(), \"distroName\"),\n        TraceLoggingValue(LXSS_WSL_VERSION_2, \"version\"),\n        TraceLoggingValue(m_instanceId, \"instanceId\"),\n        TraceLoggingValue(m_configuration.DistroId, \"distroId\"),\n        TraceLoggingValue(response.DefaultUid, \"defaultUid\"),\n        TraceLoggingValue(response.SystemdEnabled, \"systemdEnabled\"));\n}\n\nvoid WslCoreInstance::MountDrvfs(bool Admin) const\n{\n    auto [drives, nonReadableDrives] = wsl::windows::common::filesystem::EnumerateFixedDrives();\n    LX_INIT_MOUNT_DRVFS Message{{LxInitMessageRemountDrvfs, sizeof(Message)}, Admin, drives, nonReadableDrives, static_cast<int>(m_defaultUid)};\n\n    const auto& Result = m_initChannel->GetChannel().Transaction(Message, nullptr, m_socketTimeout);\n\n    LOG_HR_IF_MSG(E_UNEXPECTED, Result.Result != 0, \"Failed to mount the drvfs shares, %i\", Result.Result);\n}\n\nconst WSLDistributionInformation* WslCoreInstance::DistributionInformation() const noexcept\n{\n    return &m_distributionInfo;\n}\n\nbool WslCoreInstance::RequestStop(_In_ bool Force)\n{\n    bool shutdown = true;\n    std::lock_guard lock(m_lock);\n    if (m_initChannel)\n    {\n        try\n        {\n            LX_INIT_TERMINATE_INSTANCE terminateMessage{};\n            terminateMessage.Header.MessageType = LxInitMessageTerminateInstance;\n            terminateMessage.Header.MessageSize = sizeof(terminateMessage);\n            terminateMessage.Force = Force;\n\n            m_initChannel->GetChannel().SendMessage(terminateMessage);\n            auto [message, span] = m_initChannel->GetChannel().ReceiveMessageOrClosed<RESULT_MESSAGE<bool>>(m_socketTimeout);\n            if (message)\n            {\n                shutdown = message->Result;\n            }\n        }\n        CATCH_LOG()\n    }\n\n    return shutdown;\n}\n\nvoid WslCoreInstance::Stop()\n{\n    std::lock_guard lock(m_lock);\n\n    WSL_LOG_TELEMETRY(\n        \"StopInstance\",\n        PDT_ProductAndServiceUsage,\n        TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA),\n        TraceLoggingValue(m_configuration.Name.c_str(), \"distroName\"),\n        TraceLoggingValue(LXSS_WSL_VERSION_2, \"version\"),\n        TraceLoggingValue(m_instanceId, \"instanceId\"),\n        TraceLoggingValue(m_configuration.DistroId, \"distroId\"));\n\n    m_destroyingEvent.SetEvent();\n    m_initChannel.reset();\n    m_consoleManager.reset();\n\n    // Remove the instance's Plan 9 Redirector connection targets.\n    m_redirectorConnectionTargets.RemoveAll();\n\n    // If the instance was terminated, terminate the associated system distro.\n    m_systemDistro.reset();\n\n    return;\n}\n\nvoid WslCoreInstance::RegisterPlan9ConnectionTarget(_In_ HANDLE userToken)\n{\n    // If Plan 9 is running, add a connection target to the P9Rdr driver.\n    if (m_plan9Port != LX_INIT_UTILITY_VM_INVALID_PORT)\n    {\n        m_redirectorConnectionTargets.AddConnectionTarget(userToken, {}, m_defaultUid, {}, m_runtimeId, m_plan9Port);\n    }\n}\n\nwil::unique_socket WslCoreInstance::CreateLinuxProcess(_In_ LPCSTR Path, _In_ LPCSTR* Arguments)\n{\n    std::lock_guard lock(m_lock);\n\n    return LxssCreateProcess::CreateLinuxProcess(Path, Arguments, m_runtimeId, m_initChannel->GetChannel(), nullptr, m_socketTimeout);\n}\n\nWslCoreInstance::WslCorePort::WslCorePort(_In_ SOCKET Socket, _In_ const GUID& RuntimeId, DWORD SocketTimeout) :\n    m_channel(wil::unique_socket{Socket}, \"WslCorePort\"), m_runtimeId(RuntimeId), m_socketTimeout(SocketTimeout)\n\n{\n    // N.B. The class takes ownership of the socket.\n}\n\nstd::shared_ptr<LxssPort> WslCoreInstance::WslCorePort::CreateSessionLeader(_In_ HANDLE)\n{\n    // Send a create session message to the init daemon.\n    // N.B. A lock is held while this method is called.\n\n    LX_INIT_CREATE_SESSION message{{LxInitMessageCreateSession, sizeof(message)}};\n    const auto& response = m_channel.Transaction(message, nullptr, m_socketTimeout);\n\n    wil::unique_socket socket = wsl::windows::common::hvsocket::Connect(m_runtimeId, response.Port);\n    return std::make_shared<WslCorePort>(socket.release(), m_runtimeId, m_socketTimeout);\n}\n\nvoid WslCoreInstance::WslCorePort::DisconnectConsole(_In_ HANDLE)\n{\n}\n\nwsl::shared::SocketChannel& WslCoreInstance::WslCorePort::GetChannel()\n{\n    return m_channel;\n}\n\nwil::cs_leave_scope_exit WslCoreInstance::WslCorePort::Lock()\n{\n    return m_lock.lock();\n}\n\nvoid WslCoreInstance::WslCorePort::Receive(_Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_opt_ HANDLE ClientProcess, _In_ DWORD Timeout)\n{\n    const auto span = gsl::make_span(reinterpret_cast<gsl::byte*>(Buffer), Length);\n    const ULONG bytesRead = wsl::windows::common::socket::Receive(m_channel.Socket(), span, ClientProcess, MSG_WAITALL, Timeout);\n\n    THROW_HR_IF_MSG(E_UNEXPECTED, bytesRead < Length, \"Expected %lu bytes, but received %lu\", Length, bytesRead);\n}\n\nvoid WslCoreInstance::WslCorePort::Send(_In_reads_bytes_(Length) PVOID Buffer, _In_ ULONG Length)\n{\n    wsl::windows::common::socket::Send(m_channel.Socket(), gsl::make_span(reinterpret_cast<gsl::byte*>(Buffer), Length));\n}\n"
  },
  {
    "path": "src/windows/service/exe/WslCoreInstance.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreInstance.h\n\nAbstract:\n\n    This file contains WSL Core Instance function declarations.\n\n--*/\n\n#pragma once\n\n#include \"precomp.h\"\n#include \"hcs.hpp\"\n#include \"LxssPort.h\"\n#include \"LxssCreateProcess.h\"\n#include \"LxssConsoleManager.h\"\n#include \"WslPluginApi.h\"\n#include \"DistributionRegistration.h\"\n\nclass WslCoreInstance : public LxssRunningInstance\n{\npublic:\n    class WslCorePort final : public LxssPort\n    {\n    public:\n        WslCorePort(_In_ SOCKET Socket, _In_ const GUID& RuntimeId, _In_ DWORD SocketTimeout);\n\n        WslCorePort(const WslCorePort&) = delete;\n        WslCorePort& operator=(const WslCorePort&) = delete;\n        WslCorePort(WslCorePort&& Source) = delete;\n        WslCorePort& operator=(WslCorePort&& Source) = delete;\n\n        std::shared_ptr<LxssPort> CreateSessionLeader(_In_ HANDLE ClientProcess) override;\n        void DisconnectConsole(_In_ HANDLE ClientProcess) override;\n        wsl::shared::SocketChannel& GetChannel();\n        wil::cs_leave_scope_exit Lock() override;\n        void Receive(_Out_writes_bytes_(Length) PVOID Buffer, _In_ ULONG Length, _In_opt_ HANDLE ClientProcess = nullptr, _In_ DWORD Timeout = INFINITE) override;\n        void Send(_In_reads_bytes_(Length) PVOID Buffer, _In_ ULONG Length) override;\n\n    private:\n        wil::critical_section m_lock;\n        wsl::shared::SocketChannel m_channel;\n        GUID m_runtimeId{};\n        DWORD m_socketTimeout{};\n    };\n\n    WslCoreInstance(const WslCoreInstance&) = delete;\n    WslCoreInstance(WslCoreInstance&&) = delete;\n    WslCoreInstance& operator=(const WslCoreInstance&) = delete;\n    WslCoreInstance& operator=(WslCoreInstance&&) = delete;\n\n    WslCoreInstance(\n        _In_ HANDLE UserToken,\n        _In_ wil::unique_socket& InitSocket,\n        _In_ wil::unique_socket& SystemDistroSocket,\n        _In_ const GUID& InstanceId,\n        _In_ const GUID& RuntimeId,\n        _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n        _In_ ULONG DefaultUid,\n        _In_ ULONG64 ClientLifetimeId,\n        _In_ const std::function<LX_INIT_DRVFS_MOUNT(HANDLE)>& DrvFsCallback,\n        _In_ ULONG FeatureFlags,\n        _In_ DWORD SocketTimeout,\n        _In_ int IdleTimeout,\n        _Out_opt_ ULONG* ConnectPort = nullptr);\n\n    virtual ~WslCoreInstance();\n\n    void CreateLxProcess(\n        _In_ const CreateLxProcessData& CreateProcessData,\n        _In_ const CreateLxProcessContext& CreateProcessContext,\n        _In_ const CreateLxProcessConsoleData& ConsoleData,\n        _In_ SHORT Columns,\n        _In_ SHORT Rows,\n        _In_ PLXSS_STD_HANDLES StdHandles,\n        _Out_ GUID* InstanceId,\n        _Out_ HANDLE* ProcessHandle,\n        _Out_ HANDLE* ServerHandle,\n        _Out_ HANDLE* StandardIn,\n        _Out_ HANDLE* StandardOut,\n        _Out_ HANDLE* StandardErr,\n        _Out_ HANDLE* CommunicationChannel,\n        _Out_ HANDLE* InteropSocket) override;\n\n    ULONG GetClientId() const override;\n\n    GUID GetDistributionId() const override;\n\n    std::shared_ptr<LxssPort> GetInitPort() override;\n\n    std::shared_ptr<LxssRunningInstance> GetSystemDistro();\n\n    void UpdateTimezone() override;\n\n    ULONG64 GetLifetimeManagerId() const override;\n\n    void Initialize() override;\n\n    bool RequestStop(_In_ bool Force) override;\n\n    void Stop() override;\n\n    void RegisterPlan9ConnectionTarget(_In_ HANDLE userToken) override;\n\n    const WSLDistributionInformation* DistributionInformation() const noexcept override;\n\n    wil::unique_socket CreateLinuxProcess(_In_ LPCSTR Path, _In_ LPCSTR* Arguments);\n\nprivate:\n    void MountDrvfs(bool Admin) const;\n\n    void ReadOOBEResult(wil::unique_socket&& Socket, wsl::windows::service::DistributionRegistration&& registration);\n\n    std::recursive_mutex m_lock;\n    wil::unique_handle m_userToken;\n    bool m_initialized = false;\n    bool m_adminMountNamespaceCreated = false;\n    bool m_nonAdminMountNamespaceCreated = false;\n    ULONG m_featureFlags{};\n    GUID m_instanceId{};\n    GUID m_runtimeId{};\n    LXSS_DISTRO_CONFIGURATION m_configuration{};\n    ULONG m_clientId{};\n    ULONG m_defaultUid{};\n    std::function<LX_INIT_DRVFS_MOUNT(HANDLE)> m_initializeDrvFs;\n    std::shared_ptr<WslCorePort> m_initChannel;\n    std::shared_ptr<ConsoleManager> m_consoleManager;\n    ULONG64 m_ntClientLifetimeId{};\n    wsl::windows::common::redirector::ConnectionTargetManager m_redirectorConnectionTargets;\n    ULONG m_plan9Port{LX_INIT_UTILITY_VM_INVALID_PORT};\n    std::shared_ptr<WslCoreInstance> m_systemDistro;\n    WSLDistributionInformation m_distributionInfo{};\n    DWORD m_socketTimeout{};\n    std::thread m_oobeThread;\n    wil::unique_event m_destroyingEvent{wil::EventOptions::ManualReset};\n    wil::unique_event m_oobeCompleteEvent;\n};\n"
  },
  {
    "path": "src/windows/service/exe/WslCoreNetworkEndpoint.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n#include <memory>\r\n#include <optional>\r\n#include <hcs.hpp>\r\n\r\n#include \"WslCoreNetworkEndpointSettings.h\"\r\n#include \"WslCoreTcpIpStateTracking.h\"\r\n\r\nnamespace wsl::core::networking {\r\n\r\nstruct NetworkEndpoint\r\n{\r\n    NetworkEndpoint() = default;\r\n\r\n    ~NetworkEndpoint() noexcept\r\n    {\r\n        if (Endpoint)\r\n        {\r\n            wil::unique_cotaskmem_string error;\r\n            LOG_IF_FAILED_MSG(::HcnDeleteEndpoint(EndpointId, &error), \"error message: %ls\", error.get());\r\n        }\r\n    }\r\n\r\n    NetworkEndpoint(NetworkEndpoint&&) = default;\r\n    NetworkEndpoint& operator=(NetworkEndpoint&& source) = default;\r\n    NetworkEndpoint(const NetworkEndpoint&) = delete;\r\n    NetworkEndpoint& operator=(const NetworkEndpoint&) = delete;\r\n\r\n    std::shared_ptr<NetworkSettings> Network;\r\n    GUID NetworkId{};\r\n    GUID EndpointId{};\r\n    GUID InterfaceGuid{};\r\n    NET_LUID InterfaceLuid{};\r\n    windows::common::hcs::unique_hcn_endpoint Endpoint{};\r\n    std::optional<IpStateTracking> StateTracking;\r\n\r\n    void TraceLoggingRundown() const\r\n    {\r\n        if (Network)\r\n        {\r\n            WSL_LOG(\r\n                \"NetworkEndpoint::TraceLoggingRundown\",\r\n                TraceLoggingValue(NetworkId, \"networkId\"),\r\n                TraceLoggingValue(EndpointId, \"endpointId\"),\r\n                TraceLoggingValue(InterfaceGuid, \"interfaceGuid\"),\r\n                TraceLoggingValue(InterfaceLuid.Value, \"interfaceLuid\"),\r\n                TRACE_NETWORKSETTINGS_OBJECT(Network));\r\n        }\r\n        else\r\n        {\r\n            WSL_LOG(\r\n                \"NetworkEndpoint::TraceLoggingRundown\",\r\n                TraceLoggingValue(NetworkId, \"networkId\"),\r\n                TraceLoggingValue(EndpointId, \"endpointId\"),\r\n                TraceLoggingValue(InterfaceGuid, \"interfaceGuid\"),\r\n                TraceLoggingValue(InterfaceLuid.Value, \"interfaceLuid\"),\r\n                TraceLoggingValue(\"null\", \"Network\"));\r\n        }\r\n    }\r\n};\r\n} // namespace wsl::core::networking\r\n"
  },
  {
    "path": "src/windows/service/exe/WslCoreTcpIpStateTracking.cpp",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#include \"precomp.h\"\r\n#include \"WslCoreTcpIpStateTracking.h\"\r\n\r\n#include \"WslCoreConfig.h\"\r\n#include \"WslCoreFirewallSupport.h\"\r\n\r\n// SyncFirewallState is only needed when running on a Windows build with the original Hyper-V Firewall API (shipped with Windows\r\n// 11 22H2) later updates to the Hyper-V Firewall solve the below automatically\r\nvoid wsl::core::networking::IpStateTracking::SyncFirewallState(const NetworkSettings& preferredNetwork) noexcept\r\ntry\r\n{\r\n    // This function is used to update rules which are IP address based. These\r\n    // rules are updated whenever we detect IP address changes.\r\n    // If we have tracked IP addresses, we add or update the rules.\r\n    // If we no longer have any tracked IP addresses, we remove the rules.\r\n    // Currently, we add the following rules:\r\n    //     -My IP loopback rule - allows traffic from my IP addresses\r\n    //     -Local subnet rule - allows traffic from the local subnet\r\n\r\n    if (FirewallVmCreatorId.has_value())\r\n    {\r\n        // Obtain current set of IP addresses\r\n        std::set<EndpointIpAddress> currentIpAddresses;\r\n        if (!preferredNetwork.PreferredIpAddress.AddressString.empty())\r\n        {\r\n            currentIpAddresses.insert(preferredNetwork.PreferredIpAddress);\r\n        }\r\n        for (const auto& ipAddress : preferredNetwork.IpAddresses)\r\n        {\r\n            currentIpAddresses.insert(ipAddress);\r\n        }\r\n\r\n        // Only perform firewall update if the IP addresses have changed\r\n        if (currentIpAddresses != FirewallTrackedIpAddresses)\r\n        {\r\n            // Ensure COM state is properly initialized\r\n            const auto coInit = InitializeCOMState();\r\n            const auto loopbackRuleId = MakeLoopbackFirewallRuleId(FirewallVmCreatorId.value());\r\n            const auto localSubnetRuleId = MakeLocalSubnetFirewallRuleId(FirewallVmCreatorId.value());\r\n\r\n            // If we have no IP addresses, remove any existing rules\r\n            if (currentIpAddresses.empty())\r\n            {\r\n                WSL_LOG(\"IpStateTracking::SyncFirewallState removing rules\");\r\n\r\n                // Remove loopback rule\r\n                RemoveHyperVFirewallRule(loopbackRuleId);\r\n\r\n                // Remove local subnet rule\r\n                RemoveHyperVFirewallRule(localSubnetRuleId);\r\n            }\r\n            else\r\n            {\r\n                // We have IP addresses - update the firewall rules\r\n                FirewallRuleConfiguration myIpLoopbackRule{MakeLoopbackFirewallRuleConfiguration(loopbackRuleId)};\r\n                FirewallRuleConfiguration localSubnetRule{MakeLocalSubnetFirewallRuleConfiguration(localSubnetRuleId)};\r\n\r\n                // Iterate through IP Addresses to populate myIP loopback addresses and local subnet addresses\r\n                std::set<std::wstring> localSubnetPrefixes;\r\n                for (const auto& ipAddress : currentIpAddresses)\r\n                {\r\n                    myIpLoopbackRule.RemoteAddresses.emplace_back(wil::make_bstr(ipAddress.AddressString.c_str()));\r\n                    localSubnetPrefixes.insert(ipAddress.GetPrefix());\r\n                }\r\n\r\n                // Convert from set of wstring to vector of unique_bstr\r\n                for (const auto& subnet : localSubnetPrefixes)\r\n                {\r\n                    localSubnetRule.RemoteAddresses.emplace_back(wil::make_bstr(subnet.c_str()));\r\n                }\r\n\r\n                // Add my IP loopback rule\r\n                WSL_LOG(\"IpStateTracking::SyncFirewallState Adding my IP loopback rule\");\r\n                AddHyperVFirewallRule(FirewallVmCreatorId.value(), myIpLoopbackRule);\r\n\r\n                // Add local subnet rule\r\n                WSL_LOG(\"IpStateTracking::SyncFirewallState Adding local subnet rule\");\r\n                AddHyperVFirewallRule(FirewallVmCreatorId.value(), localSubnetRule);\r\n            }\r\n\r\n            // Swap to the tracked set of IP addresses after we have performed all of the updates\r\n            std::swap(FirewallTrackedIpAddresses, currentIpAddresses);\r\n        }\r\n        else\r\n        {\r\n            WSL_LOG(\r\n                \"IpStateTracking::SyncFirewallState - FirewallTrackedIpAddresses is synced with the preferredNetwork\",\r\n                TraceLoggingValue(FirewallTrackedIpAddresses.size(), \"FirewallTrackedIpAddresses.size\"));\r\n        }\r\n    }\r\n    else\r\n    {\r\n        WSL_LOG(\"IpStateTracking::SyncFirewallState - no FirewallVmCreatorId\");\r\n    }\r\n}\r\nCATCH_LOG()\r\n"
  },
  {
    "path": "src/windows/service/exe/WslCoreTcpIpStateTracking.h",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\n#pragma once\r\n#include \"WslCoreNetworkEndpointSettings.h\"\r\n#include \"WslCoreHostDnsInfo.h\"\r\n\r\nnamespace wsl::core::networking {\r\nenum class TrackedIpStateSyncStatus\r\n{\r\n    PendingAdd,\r\n    PendingUpdate,\r\n    PendingRemoval,\r\n    Synced\r\n};\r\nconstexpr auto ToString(networking::TrackedIpStateSyncStatus status) noexcept\r\n{\r\n    switch (status)\r\n    {\r\n    case networking::TrackedIpStateSyncStatus::PendingAdd:\r\n        return \"PendingAdd\";\r\n    case networking::TrackedIpStateSyncStatus::PendingUpdate:\r\n        return \"PendingUpdate\";\r\n    case networking::TrackedIpStateSyncStatus::PendingRemoval:\r\n        return \"PendingRemoval\";\r\n    case networking::TrackedIpStateSyncStatus::Synced:\r\n        return \"Synced\";\r\n    default:\r\n        return \"Unknown\";\r\n    }\r\n}\r\n\r\nstruct TrackedIpAddress\r\n{\r\n    EndpointIpAddress Address{};\r\n\r\n    // The following fields need to be changed from a std::set iterator (which is always const)\r\n    // in SyncIpStateWithLinux - that's why they're marked mutable.\r\n    mutable TrackedIpStateSyncStatus SyncStatus = TrackedIpStateSyncStatus::PendingAdd;\r\n    mutable uint32_t SyncRetryCount = MaxSyncRetryCount;\r\n    mutable uint32_t LoopbackSyncRetryCount = MaxLoopbackSyncRetryCount;\r\n\r\n    static constexpr uint32_t MaxSyncRetryCount = 15;\r\n    static constexpr uint32_t MaxLoopbackSyncRetryCount = 5;\r\n\r\n    TrackedIpAddress() = default;\r\n    ~TrackedIpAddress() noexcept = default;\r\n\r\n    // not copyable to avoid subtle bugs where 2 objects are trying to track state of the same address\r\n    TrackedIpAddress(const TrackedIpAddress&) = delete;\r\n    TrackedIpAddress& operator=(const TrackedIpAddress&) = delete;\r\n    TrackedIpAddress(TrackedIpAddress&&) = default;\r\n    TrackedIpAddress& operator=(TrackedIpAddress&&) = default;\r\n\r\n    explicit TrackedIpAddress(const EndpointIpAddress& other)\r\n    {\r\n        Address = other;\r\n    }\r\n\r\n    TrackedIpAddress& operator=(const EndpointIpAddress& other)\r\n    {\r\n        Address = other;\r\n        SyncStatus = TrackedIpStateSyncStatus::PendingAdd;\r\n        SyncRetryCount = MaxSyncRetryCount;\r\n        LoopbackSyncRetryCount = MaxLoopbackSyncRetryCount;\r\n        return *this;\r\n    }\r\n\r\n    wsl::shared::hns::IPAddress ConvertToHnsSettingsMsg() const\r\n    {\r\n        wsl::shared::hns::IPAddress addr{};\r\n        addr.Family = Address.Address.si_family;\r\n        addr.Address = Address.AddressString;\r\n        addr.OnLinkPrefixLength = Address.PrefixLength;\r\n        addr.PreferredLifetime = Address.PreferredLifetime;\r\n        addr.PrefixOrigin = Address.PrefixOrigin;\r\n        addr.SuffixOrigin = Address.SuffixOrigin;\r\n        return addr;\r\n    }\r\n\r\n    bool operator==(const TrackedIpAddress& other) const noexcept\r\n    {\r\n        return Address == other.Address;\r\n    }\r\n\r\n    bool operator!=(const TrackedIpAddress& other) const noexcept\r\n    {\r\n        return !(*this == other);\r\n    }\r\n\r\n    bool operator<(const TrackedIpAddress& other) const noexcept\r\n    {\r\n        return Address < other.Address;\r\n    }\r\n};\r\n\r\nstruct TrackedRoute\r\n{\r\n    EndpointRoute Route{};\r\n\r\n    // The following fields need to be changed from a std::set iterator (which is always const)\r\n    // in SyncIpStateWithLinux - that's why they're marked mutable.\r\n    mutable TrackedIpStateSyncStatus SyncStatus = TrackedIpStateSyncStatus::PendingAdd;\r\n    mutable uint32_t SyncRetryCount = MaxSyncRetryCount;\r\n    mutable bool LinuxConflictRemoved = false; // only used for prefix routes\r\n\r\n    static constexpr uint32_t MaxSyncRetryCount = 15;\r\n\r\n    TrackedRoute() = default;\r\n    ~TrackedRoute() noexcept = default;\r\n\r\n    // not copyable to avoid subtle bugs where 2 objects are trying to track state of the same route\r\n    TrackedRoute(const TrackedRoute&) = delete;\r\n    TrackedRoute& operator=(const TrackedRoute&) = delete;\r\n    TrackedRoute(TrackedRoute&&) = default;\r\n    TrackedRoute& operator=(TrackedRoute&&) = default;\r\n\r\n    explicit TrackedRoute(const EndpointRoute& other)\r\n    {\r\n        Route = other;\r\n    }\r\n\r\n    TrackedRoute& operator=(const EndpointRoute& other)\r\n    {\r\n        Route = other;\r\n        SyncStatus = TrackedIpStateSyncStatus::PendingAdd;\r\n        SyncRetryCount = MaxSyncRetryCount;\r\n        LinuxConflictRemoved = false;\r\n        return *this;\r\n    }\r\n\r\n    unsigned int LinuxAutoGenRouteMetric() const\r\n    {\r\n        return (Route.Family == AF_INET6) ? 1024 : 0;\r\n    }\r\n\r\n    bool CanConflictWithLinuxAutoGenRoute() const\r\n    {\r\n        return Route.IsAutoGeneratedPrefixRoute && (Route.Metric != LinuxAutoGenRouteMetric());\r\n    }\r\n\r\n    wsl::shared::hns::Route ConvertToHnsSettingsMsg() const\r\n    {\r\n        wsl::shared::hns::Route route{};\r\n        route.Family = Route.Family;\r\n        route.DestinationPrefix = Route.GetFullDestinationPrefix();\r\n        route.SitePrefixLength = Route.SitePrefixLength;\r\n        route.NextHop = Route.NextHopString;\r\n        route.Metric = Route.Metric;\r\n        return route;\r\n    }\r\n\r\n    bool operator==(const TrackedRoute& other) const noexcept\r\n    {\r\n        return Route == other.Route;\r\n    }\r\n\r\n    bool operator!=(const TrackedRoute& other) const noexcept\r\n    {\r\n        return !(*this == other);\r\n    }\r\n\r\n    bool operator<(const TrackedRoute& other) const noexcept\r\n    {\r\n        // return true if 'this' is less than (i.e. is ordered before) the input argument\r\n        if (Route.IsAutoGeneratedPrefixRoute || other.Route.IsAutoGeneratedPrefixRoute)\r\n        {\r\n            if (Route.IsAutoGeneratedPrefixRoute && other.Route.IsAutoGeneratedPrefixRoute)\r\n            {\r\n                // if both are effectively equivalent, sort by their addresses\r\n                if (Route.DestinationPrefixString == other.Route.DestinationPrefixString)\r\n                {\r\n                    return Route.Metric < other.Route.Metric;\r\n                }\r\n                return Route.DestinationPrefixString < other.Route.DestinationPrefixString;\r\n            }\r\n            // else return true if it's the left that's IsAutoGeneratedPrefixRoute\r\n            return Route.IsAutoGeneratedPrefixRoute;\r\n        }\r\n\r\n        if (Route.IsNextHopOnlink() || other.Route.IsNextHopOnlink())\r\n        {\r\n            if (Route.IsNextHopOnlink() && other.Route.IsNextHopOnlink())\r\n            {\r\n                // if both are effectively equivalent, sort by their addresses\r\n                if (Route.DestinationPrefixString == other.Route.DestinationPrefixString)\r\n                {\r\n                    return Route.Metric < other.Route.Metric;\r\n                }\r\n                return Route.DestinationPrefixString < other.Route.DestinationPrefixString;\r\n            }\r\n            // else return true if it's the left that's IsNextHopOnlink()\r\n            return Route.IsNextHopOnlink();\r\n        }\r\n\r\n        // else it's an Add or Update for a route that's not an auto-generated route\r\n        // and whose next-hop address is not on-link\r\n        if (Route.DestinationPrefixString == other.Route.DestinationPrefixString)\r\n        {\r\n            return Route.Metric < other.Route.Metric;\r\n        }\r\n        return Route.DestinationPrefixString < other.Route.DestinationPrefixString;\r\n    }\r\n};\r\n\r\nstruct IpStateTracking\r\n{\r\n    bool InitialSyncComplete = false;\r\n\r\n    GUID InterfaceGuid{};\r\n    std::set<TrackedIpAddress> IpAddresses{};\r\n    std::set<TrackedRoute> Routes{};\r\n    std::vector<std::wstring> DnsServers{};\r\n    // currently DnsServersSyncStatus is only tracked for tracing purposes - it's not being set through Linux\r\n    TrackedIpStateSyncStatus DnsServersSyncStatus = TrackedIpStateSyncStatus::PendingAdd;\r\n    DnsInfo DnsInfo{};\r\n    ULONG InterfaceMtu = 0;\r\n    bool IsMetered = false;\r\n\r\n    IpStateTracking() = default;\r\n    IpStateTracking(const std::optional<GUID>& vmCreatorId) : FirewallVmCreatorId(vmCreatorId)\r\n    {\r\n    }\r\n\r\n    ~IpStateTracking() noexcept\r\n    {\r\n        if (FirewallVmCreatorId)\r\n        {\r\n            ResetState();\r\n        }\r\n    }\r\n\r\n    // cannot copy - the d'tor clears FW rules so move-operators must track when moved-from\r\n    // it does this by clearing the std::optional FirewallVmCreatorId when moved-from\r\n    IpStateTracking(const IpStateTracking&) = delete;\r\n    IpStateTracking& operator=(const IpStateTracking&) = delete;\r\n\r\n    IpStateTracking(IpStateTracking&& lhs) noexcept :\r\n        InitialSyncComplete(std::move(lhs.InitialSyncComplete)),\r\n        InterfaceGuid(std::move(lhs.InterfaceGuid)),\r\n        IpAddresses(std::move(lhs.IpAddresses)),\r\n        Routes(std::move(lhs.Routes)),\r\n        InterfaceMtu(std::move(lhs.InterfaceMtu)),\r\n        IsMetered(std::move(lhs.IsMetered)),\r\n        FirewallVmCreatorId(std::move(lhs.FirewallVmCreatorId)),\r\n        FirewallTrackedIpAddresses(std::move(lhs.FirewallTrackedIpAddresses))\r\n    {\r\n        // must reset FirewallVmCreatorId so the d'tor won't reset FW state from the moved-from object\r\n        lhs.FirewallVmCreatorId.reset();\r\n    }\r\n\r\n    IpStateTracking& operator=(IpStateTracking&& lhs) noexcept\r\n    {\r\n        InterfaceGuid = std::move(lhs.InterfaceGuid);\r\n        IpAddresses = std::move(lhs.IpAddresses);\r\n        Routes = std::move(lhs.Routes);\r\n        InterfaceMtu = std::move(lhs.InterfaceMtu);\r\n        IsMetered = std::move(lhs.IsMetered);\r\n        InitialSyncComplete = std::move(lhs.InitialSyncComplete);\r\n        FirewallVmCreatorId = std::move(lhs.FirewallVmCreatorId);\r\n        FirewallTrackedIpAddresses = std::move(lhs.FirewallTrackedIpAddresses);\r\n\r\n        // must reset FirewallVmCreatorId so the d'tor won't reset FW state from the moved-from object\r\n        lhs.FirewallVmCreatorId.reset();\r\n\r\n        return *this;\r\n    }\r\n\r\n    void ResetState() noexcept\r\n    {\r\n        WSL_LOG(\"IpStateTracking::ResetState\");\r\n        InitialSyncComplete = false;\r\n        InterfaceGuid = {};\r\n        IpAddresses.clear();\r\n        Routes.clear();\r\n        DnsServers.clear();\r\n        DnsServersSyncStatus = TrackedIpStateSyncStatus::PendingAdd;\r\n        InterfaceMtu = 0;\r\n        IsMetered = false;\r\n        SyncFirewallState({});\r\n    }\r\n\r\n    void SeedInitialState(const NetworkSettings& settings) noexcept\r\n    {\r\n        InterfaceGuid = settings.InterfaceGuid;\r\n        InterfaceMtu = settings.GetEffectiveMtu();\r\n        IsMetered = settings.IsMetered;\r\n\r\n        WSL_LOG(\r\n            \"IpStateTracking::SeedInitialState\",\r\n            TraceLoggingValue(InterfaceGuid, \"InterfaceGuid\"),\r\n            TraceLoggingValue(InterfaceMtu, \"InterfaceMtu\"),\r\n            TraceLoggingValue(IsMetered, \"IsMetered\"),\r\n            TraceLoggingValue(IpAddresses.size(), \"IpAddresses.size()\"),\r\n            TraceLoggingValue(Routes.size(), \"Routes.size()\"),\r\n            TraceLoggingValue(FirewallVmCreatorId.value_or(GUID{}), \"FirewallVmCreatorId\"));\r\n\r\n        // not updating any other fields in this SeedInitialState\r\n        // Address/Route/DnsServer objects are cleared when disconnected\r\n        //   and updated as we confirm they are pushed to the container\r\n    }\r\n\r\n    void SyncFirewallState(const NetworkSettings& preferredNetwork) noexcept;\r\n\r\nprivate:\r\n    std::optional<GUID> FirewallVmCreatorId{};\r\n    std::set<EndpointIpAddress> FirewallTrackedIpAddresses{};\r\n};\r\n} // namespace wsl::core::networking\r\n"
  },
  {
    "path": "src/windows/service/exe/WslCoreVm.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreVm.cpp\n\nAbstract:\n\n    This file contains utility VM function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"WslCoreVm.h\"\n#include \"WslCoreNetworkingSupport.h\"\n#include <lxfsshares.h>\n#include \"disk.hpp\"\n#include \"WslCoreInstance.h\"\n#include \"NatNetworking.h\"\n#include \"BridgedNetworking.h\"\n#include \"MirroredNetworking.h\"\n#include \"WslCoreFirewallSupport.h\"\n#include \"DnsResolver.h\"\n#include \"VirtioNetworking.h\"\n\n#include <TraceLoggingProvider.h>\n\nusing msl::utilities::SafeInt;\nusing wsl::windows::common::helpers::WindowsBuildNumbers;\nusing namespace wsl::windows::common::registry;\nusing namespace wsl::windows::common::string;\nusing namespace std::string_literals;\n\n// The default high-gap MMIO space is 16GB\n#define DEFAULT_HIGH_MMIO_GAP_IN_MB (16 * _1KB)\n\n// Start of unaddressable memory if guest only supports the minimum 36-bit addressing.\n#define MAX_36_BIT_PAGE_IN_MB (0x1000000000 / _1MB)\n\n// Version numbers for various functionality that was backported.\n#define NICKEL_BUILD_FLOOR 22350\n#define VIRTIO_SERIAL_CONSOLE_COBALT_RELEASE_UBR 40\n#define VMEMM_SUFFIX_COBALT_REFRESH_BUILD_NUMBER 22138\n#define VMMEM_SUFFIX_COBALT_RELEASE_UBR 71\n#define VMMEM_SUFFIX_NICKEL_BUILD_NUMBER 22420\n\n#define WSLG_SHARED_MEMORY_SIZE_MB 8192\n#define PAGE_SIZE 0x1000\n\nstatic constexpr size_t c_bootEntropy = 0x1000;\nstatic constexpr auto c_localDevicesKey = L\"SOFTWARE\\\\Microsoft\\\\Terminal Server Client\\\\LocalDevices\";\nstatic constexpr std::pair<uint32_t, uint32_t> c_schemaVersionNickel{2, 7};\n\n#define LXSS_ENABLE_GUI_APPS() (m_vmConfig.EnableGuiApps && (m_systemDistroDeviceId != ULONG_MAX))\n\nusing namespace wsl::windows::common;\nusing wsl::core::NetworkingMode;\nusing wsl::core::networking::NetworkEndpoint;\nusing wsl::core::networking::NetworkSettings;\nusing wsl::shared::Localization;\nusing wsl::windows::common::Context;\nusing wsl::windows::common::ExecutionContext;\n\nnamespace {\nINT64\nRequiredExtraMmioSpaceForPmemFileInMb(_In_ PCWSTR FilePath)\n{\n    // Open the file and retrieve the file's size.\n    const wil::unique_hfile fileHandle{CreateFile(FilePath, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)};\n    THROW_LAST_ERROR_IF(!fileHandle);\n\n    LARGE_INTEGER fileSizeBytes;\n    THROW_IF_WIN32_BOOL_FALSE(GetFileSizeEx(fileHandle.get(), &fileSizeBytes));\n\n    // The file is mapped to the VM using PCI BARs, which can only be a power of two. Therefore,\n    // round the file size up to the nearest power of two.\n    fileSizeBytes.QuadPart = wsl::windows::common::helpers::RoundUpToNearestPowerOfTwo(fileSizeBytes.QuadPart);\n\n    // Convert from bytes to megabytes. Ensure that we don't truncate a 512kb file to 0mb.\n    return std::max(fileSizeBytes.QuadPart / static_cast<INT64>(_1MB), 1i64);\n}\n} // namespace\n\nWslCoreVm::WslCoreVm(_In_ wsl::core::Config&& VmConfig) :\n    m_vmConfig(std::move(VmConfig)), m_traceClient(m_vmConfig.EnableTelemetry)\n{\n}\n\nstd::unique_ptr<WslCoreVm> WslCoreVm::Create(_In_ const wil::shared_handle& UserToken, _In_ wsl::core::Config&& VmConfig, _In_ const GUID& VmId)\n{\n    auto newInstance = std::unique_ptr<WslCoreVm>{new WslCoreVm{std::move(VmConfig)}};\n    try\n    {\n        const auto startTimeMs = GetTickCount64();\n        auto privateKernel = !newInstance->m_vmConfig.KernelPath.empty();\n        // Log telemetry on how long it took to create the VM\n        WSL_LOG_TELEMETRY(\n            \"CreateVmBegin\", PDT_ProductAndServicePerformance, TraceLoggingValue(VmId, \"vmId\"), CONFIG_TELEMETRY(newInstance->m_vmConfig));\n\n        newInstance->Initialize(VmId, UserToken);\n\n        const auto timeToCreateVmMs = GetTickCount64() - startTimeMs;\n        WSL_LOG_TELEMETRY(\n            \"CreateVmEnd\",\n            PDT_ProductAndServicePerformance,\n            TraceLoggingValue(privateKernel, \"privateKernel\"),\n            TraceLoggingValue(newInstance->m_kernelVersionString.c_str(), \"kernelVersion\"),\n            TraceLoggingValue(newInstance->m_runtimeId, \"vmId\"),\n            TraceLoggingValue(timeToCreateVmMs, \"timeToCreateVmMs\"),\n            CONFIG_TELEMETRY(newInstance->m_vmConfig));\n    }\n    catch (...)\n    {\n        const auto hr = wil::ResultFromCaughtException();\n\n        // Log telemetry when the WSL VM fails to start including the error\n        WSL_LOG_TELEMETRY(\n            \"FailedToStartVm\",\n            PDT_ProductAndServicePerformance,\n            TraceLoggingValue(VmId, \"vmId\"),\n            TraceLoggingValue(hr, \"error\"),\n            CONFIG_TELEMETRY(newInstance->m_vmConfig));\n\n        if (hr == HRESULT_FROM_WIN32(WSAENOTCONN) || hr == HRESULT_FROM_WIN32(WSAECONNRESET) || hr == HRESULT_FROM_WIN32(WSAETIMEDOUT))\n        {\n            // A kernel panic can cause an hvsocket error. If we hit this, wait one second for an HCS notification to give a better error for the user.\n            if (newInstance->m_vmCrashEvent.wait(1000))\n            {\n                if (newInstance->m_vmCrashLogFile.has_value())\n                {\n                    THROW_HR_WITH_USER_ERROR(\n                        WSL_E_VM_CRASHED,\n                        wsl::shared::Localization::MessageWSL2Crashed() + L\"\\r\\n\" +\n                            Localization::MessageWSL2CrashedStackTrace(newInstance->m_vmCrashLogFile.value()));\n                }\n                else\n                {\n                    THROW_HR_WITH_USER_ERROR(WSL_E_VM_CRASHED, wsl::shared::Localization::MessageWSL2Crashed());\n                }\n            }\n        }\n\n        throw;\n    }\n\n    return newInstance;\n}\n\nvoid WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken)\n{\n    auto signalEarlyTermination = wil::scope_exit([&] { m_terminatingEvent.SetEvent(); });\n\n    // create a restricted version of the token.\n    m_userToken = UserToken;\n    m_restrictedToken = wsl::windows::common::security::CreateRestrictedToken(m_userToken.get());\n\n    // Make a copy of the user sid.\n    auto tokenUser = wil::get_token_information<TOKEN_USER>(m_userToken.get());\n    THROW_IF_WIN32_BOOL_FALSE(::CopySid(sizeof(m_userSid), &m_userSid.Sid, tokenUser->User.Sid));\n\n    // Generate a machine ID string based on the VM ID. This is used for some HCS APIs.\n    m_machineId = wsl::shared::string::GuidToString<wchar_t>(VmId, wsl::shared::string::GuidToStringFlags::Uppercase);\n\n    // Set the install path of the package.\n    m_installPath = wsl::windows::common::wslutil::GetBasePath();\n\n    // Initialize the path to the tools folder which also serves as the default rootfs path.\n    m_rootFsPath = m_installPath / LXSS_TOOLS_DIRECTORY;\n\n    // Store the path of the user profile.\n    m_userProfile = wsl::windows::common::helpers::GetUserProfilePath(m_userToken.get());\n\n    // Query the Windows version.\n    m_windowsVersion = wsl::windows::common::helpers::GetWindowsVersion();\n\n    // Create a temporary folder for the VM.\n    try\n    {\n        const auto runAsUser = wil::impersonate_token(m_userToken.get());\n        m_tempPath = wsl::windows::common::filesystem::GetTempFolderPath(m_userToken.get()) / m_machineId;\n\n        wil::CreateDirectoryDeep(m_tempPath.c_str());\n        m_tempDirectoryCreated = true;\n    }\n    CATCH_LOG();\n\n    // If a private kernel was not specified, use the default.\n    m_defaultKernel = m_vmConfig.KernelPath.empty();\n    if (m_defaultKernel)\n    {\n#ifdef WSL_KERNEL_PATH\n\n        m_vmConfig.KernelPath = TEXT(WSL_KERNEL_PATH);\n\n#else\n\n        m_vmConfig.KernelPath = m_rootFsPath / LXSS_VM_MODE_KERNEL_NAME;\n\n#endif\n    }\n    else\n    {\n        if (!wsl::windows::common::filesystem::FileExists(m_vmConfig.KernelPath.c_str()))\n        {\n            THROW_HR_WITH_USER_ERROR(\n                WSL_E_CUSTOM_KERNEL_NOT_FOUND,\n                Localization::MessageCustomKernelNotFound(\n                    wsl::windows::common::helpers::GetWslConfigPath(m_userToken.get()), m_vmConfig.KernelPath.c_str()));\n        }\n\n        // Direct boot is not supported on ARM64. Modify the rootfs directory to be a temporary directory that contains\n        // copies of the initrd file and private kernel.\n        if constexpr (wsl::shared::Arm64)\n        {\n            auto impersonate = wil::impersonate_token(m_userToken.get());\n\n            m_rootFsPath = m_tempPath / LXSS_ROOTFS_DIRECTORY;\n            wil::CreateDirectoryDeep(m_rootFsPath.c_str());\n            auto initRdPath = m_installPath / LXSS_TOOLS_DIRECTORY / LXSS_VM_MODE_INITRD_NAME;\n\n            auto targetPath = m_rootFsPath / LXSS_VM_MODE_INITRD_NAME;\n            THROW_IF_WIN32_BOOL_FALSE(CopyFileW(initRdPath.c_str(), targetPath.c_str(), TRUE));\n\n            targetPath = m_rootFsPath / LXSS_VM_MODE_KERNEL_NAME;\n            THROW_IF_WIN32_BOOL_FALSE(CopyFileW(m_vmConfig.KernelPath.c_str(), targetPath.c_str(), TRUE));\n        }\n    }\n\n    // If the user did not specify custom modules, use the default modules only if using the default kernel.\n    if (m_vmConfig.KernelModulesPath.empty())\n    {\n        if (m_defaultKernel)\n        {\n#ifdef WSL_KERNEL_MODULES_PATH\n\n            m_vmConfig.KernelModulesPath = std::wstring(TEXT(WSL_KERNEL_MODULES_PATH));\n\n#else\n\n            m_vmConfig.KernelModulesPath = m_rootFsPath / L\"modules.vhd\";\n\n#endif\n        }\n    }\n    else\n    {\n        if (!wsl::windows::common::filesystem::FileExists(m_vmConfig.KernelModulesPath.c_str()))\n        {\n            THROW_HR_WITH_USER_ERROR(\n                WSL_E_CUSTOM_KERNEL_NOT_FOUND,\n                Localization::MessageCustomKernelModulesNotFound(\n                    wsl::windows::common::helpers::GetWslConfigPath(m_userToken.get()), m_vmConfig.KernelModulesPath.c_str()));\n        }\n\n        if (m_defaultKernel)\n        {\n            THROW_HR_WITH_USER_ERROR(WSL_E_CUSTOM_KERNEL_NOT_FOUND, Localization::MessageMismatchedKernelModulesError());\n        }\n    }\n\n    // If debug console was requested, create a randomly-named pipe and spawn a wslhost process to read from the pipe.\n    //\n    // N.B. wslhost.exe is launched at medium integrity level and its lifetime\n    //      is tied to the lifetime of the utility VM.\n    if (m_vmConfig.EnableDebugConsole || !m_vmConfig.DebugConsoleLogFile.empty())\n    {\n        try\n        {\n            m_vmConfig.EnableDebugConsole = true;\n            m_comPipe0 = wsl::windows::common::helpers::GetUniquePipeName();\n        }\n        CATCH_LOG()\n    }\n\n    // If the system supports virtio console serial ports, use dmesg capture for telemetry and/or debug output.\n    // Legacy serial is much slower, so this is not enabled without virtio console support.\n    m_vmConfig.EnableDebugShell &= IsVirtioSerialConsoleSupported();\n    if (IsVirtioSerialConsoleSupported())\n    {\n        try\n        {\n            bool enableTelemetry = TraceLoggingProviderEnabled(g_hTraceLoggingProvider, WINEVENT_LEVEL_INFO, 0);\n            m_dmesgCollector = DmesgCollector::Create(\n                VmId, m_vmExitEvent, enableTelemetry, m_vmConfig.EnableDebugConsole, m_comPipe0, m_vmConfig.EnableEarlyBootLogging);\n\n            WSL_LOG(\"DMESG collector created\");\n\n            if (m_vmConfig.EnableDebugShell)\n            {\n                m_debugShellPipe = wsl::windows::common::wslutil::GetDebugShellPipeName(&m_userSid.Sid);\n            }\n\n            // Initialize the guest telemetry logger.\n            m_gnsTelemetryLogger = GuestTelemetryLogger::Create(VmId, m_vmExitEvent);\n        }\n        CATCH_LOG()\n    }\n\n    if (m_vmConfig.EnableDebugConsole)\n    {\n        try\n        {\n            // If specified, create a file to log the debug console output.\n            wil::unique_hfile logFile;\n            if (!m_vmConfig.DebugConsoleLogFile.empty())\n            {\n                auto impersonate = wil::impersonate_token(m_userToken.get());\n                logFile.reset(CreateFileW(\n                    m_vmConfig.DebugConsoleLogFile.c_str(), FILE_APPEND_DATA, (FILE_SHARE_READ | FILE_SHARE_WRITE), nullptr, OPEN_ALWAYS, 0, nullptr));\n\n                LOG_LAST_ERROR_IF(!logFile);\n            }\n\n            wsl::windows::common::helpers::LaunchDebugConsole(\n                m_comPipe0.c_str(), !!m_dmesgCollector, m_restrictedToken.get(), logFile ? logFile.get() : nullptr, !m_vmConfig.EnableTelemetry);\n        }\n        CATCH_LOG()\n    }\n\n    // Create the utility VM and store the runtime ID.\n    std::wstring json = GenerateConfigJson();\n    m_system = wsl::windows::common::hcs::CreateComputeSystem(m_machineId.c_str(), json.c_str());\n    m_runtimeId = wsl::windows::common::hcs::GetRuntimeId(m_system.get());\n    WI_ASSERT(IsEqualGUID(VmId, m_runtimeId));\n\n    // Initialize the guest device manager.\n    m_guestDeviceManager = std::make_shared<GuestDeviceManager>(m_machineId, m_runtimeId);\n\n    // Create a socket listening for connections from mini_init.\n    m_listenSocket = wsl::windows::common::hvsocket::Listen(m_runtimeId, LX_INIT_UTILITY_VM_INIT_PORT);\n\n    if (m_vmConfig.MaxCrashDumpCount >= 0)\n    {\n        auto crashDumpSocket = wsl::windows::common::hvsocket::Listen(m_runtimeId, LX_INIT_UTILITY_VM_CRASH_DUMP_PORT);\n        THROW_LAST_ERROR_IF(!crashDumpSocket);\n        m_crashDumpCollectionThread =\n            std::thread{[this, socket = std::move(crashDumpSocket)]() mutable { CollectCrashDumps(std::move(socket)); }};\n    }\n\n    // Register a callback to detect if the utility VM exits unexpectedly.\n    wsl::windows::common::hcs::RegisterCallback(m_system.get(), s_OnExit, this);\n    signalEarlyTermination.release();\n\n    // Start the utility VM.\n    try\n    {\n        wsl::windows::common::hcs::StartComputeSystem(m_system.get(), json.c_str());\n    }\n    catch (...)\n    {\n        // Reset m_system so we don't try to wait for termination in the destructor, since the VM isn't even running.\n        m_system.reset();\n        throw;\n    }\n\n    // Add GPUs to the utility VM.\n    if (m_vmConfig.EnableGpuSupport)\n    {\n        ExecutionContext context(Context::ConfigureGpu);\n\n        hcs::ModifySettingRequest<hcs::GpuConfiguration> gpuRequest{};\n        gpuRequest.ResourcePath = L\"VirtualMachine/ComputeTopology/Gpu\";\n        gpuRequest.RequestType = hcs::ModifyRequestType::Update;\n        gpuRequest.Settings.AssignmentMode = hcs::GpuAssignmentMode::Mirror;\n        gpuRequest.Settings.AllowVendorExtension = true;\n        if (IsDisableVgpuSettingsSupported())\n        {\n            gpuRequest.Settings.DisableGdiAcceleration = true;\n            gpuRequest.Settings.DisablePresentation = true;\n        }\n\n        wsl::windows::common::hcs::ModifyComputeSystem(m_system.get(), wsl::shared::ToJsonW(gpuRequest).c_str());\n\n        // Also add 9p shares for the library directories.\n        // N.B. These are not hosted by the out-of-proc drvfs 9p server because the GPU shares\n        //      should work even if drvfs is disabled.\n        auto addShare = [&](PCWSTR name, PCWSTR path) {\n            constexpr auto flags = (hcs::Plan9ShareFlags::ReadOnly | hcs::Plan9ShareFlags::AllowOptions);\n            wsl::windows::common::hcs::AddPlan9Share(m_system.get(), name, name, path, LX_INIT_UTILITY_VM_PLAN9_PORT, flags);\n        };\n\n        std::wstring path;\n        THROW_IF_FAILED(wil::ExpandEnvironmentStringsW(L\"%SystemRoot%\\\\System32\\\\DriverStore\\\\FileRepository\", path));\n        addShare(TEXT(LXSS_GPU_DRIVERS_SHARE), path.c_str());\n\n        // N.B. There are inbox and packaged versions of the Direct 3D libraries. The packaged\n        //      versions take presidence by using overlayfs in the guest.\n        THROW_IF_FAILED(wil::ExpandEnvironmentStringsW(L\"%SystemRoot%\\\\System32\\\\lxss\\\\lib\", path));\n\n        if (wsl::windows::common::filesystem::FileExists(path.c_str()))\n        {\n            try\n            {\n                addShare(TEXT(LXSS_GPU_INBOX_LIB_SHARE), path.c_str());\n                m_enableInboxGpuLibs = true;\n            }\n            CATCH_LOG()\n        }\n\n#ifdef WSL_GPU_LIB_PATH\n\n        path = TEXT(WSL_GPU_LIB_PATH);\n\n#else\n\n        path = m_installPath / L\"lib\";\n\n#endif\n\n        addShare(TEXT(LXSS_GPU_PACKAGED_LIB_SHARE), path.c_str());\n    }\n\n    // Asynchronously add drvfs devices if supported.\n    if (m_vmConfig.EnableHostFileSystemAccess)\n    {\n        std::promise<bool> initialResult;\n        m_drvfsInitialResult = initialResult.get_future();\n        auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();\n        std::thread([this, guestDeviceLock = std::move(guestDeviceLock), initialResult = std::move(initialResult)]() mutable {\n            try\n            {\n                wsl::windows::common::wslutil::SetThreadDescription(L\"InitializeDrvfs\");\n                initialResult.set_value(InitializeDrvFsLockHeld(m_userToken.get()));\n            }\n            catch (...)\n            {\n                try\n                {\n                    initialResult.set_exception(std::current_exception());\n                }\n                CATCH_LOG()\n            }\n        }).detach();\n    }\n\n    // Accept a connection from mini_init with a receive timeout so the service does not get stuck waiting for a response from the VM.\n    m_miniInitChannel = wsl::shared::SocketChannel{AcceptConnection(m_vmConfig.KernelBootTimeout), \"mini_init\", m_terminatingEvent.get()};\n\n    // Accept the connection from the Linux guest for notifications.\n    m_notifyChannel = AcceptConnection(m_vmConfig.KernelBootTimeout);\n\n    // Receive and parse the guest kernel version\n    ReadGuestCapabilities();\n\n    // Mount the system distro.\n    // N.B. If using SCSI, the system distro is added during VM creation.\n    switch (m_systemDistroDeviceType)\n    {\n    case LxMiniInitMountDeviceTypePmem:\n        m_systemDistroDeviceId = MountFileAsPersistentMemory(m_vmConfig.SystemDistroPath.c_str(), true);\n        break;\n    }\n\n    // Attempt to create and mount the swap vhd.\n    //\n    // N.B. This can fail if the target directory is compressed, encrypted, or if\n    //      the user does not have write access.\n    ULONG swapLun = ULONG_MAX;\n    if ((m_systemDistroDeviceId != ULONG_MAX) && (m_vmConfig.SwapSizeBytes > 0))\n    {\n        try\n        {\n            {\n                // If no user-specified swap vhd file path was specified, use a\n                // path in the temp directory.\n                auto runAsUser = wil::impersonate_token(m_userToken.get());\n                if (m_vmConfig.SwapFilePath.empty())\n                {\n                    m_vmConfig.SwapFilePath = m_tempPath / L\"swap\";\n                }\n\n                // Ensure the swap vhd ends with the vhdx file extension.\n                if (!wsl::windows::common::string::IsPathComponentEqual(\n                        m_vmConfig.SwapFilePath.extension().native(), wsl::windows::common::wslutil::c_vhdxFileExtension))\n                {\n                    m_vmConfig.SwapFilePath += wsl::windows::common::wslutil::c_vhdxFileExtension;\n                }\n\n                // Create the VHD with an additional page for swap overhead.\n                m_vmConfig.SwapSizeBytes += PAGE_SIZE;\n                auto result = wil::ResultFromException([&]() {\n                    wsl::core::filesystem::CreateVhd(m_vmConfig.SwapFilePath.c_str(), m_vmConfig.SwapSizeBytes, &m_userSid.Sid, false, false);\n                    m_swapFileCreated = true;\n                });\n\n                if (result == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS))\n                {\n                    auto handle = wsl::core::filesystem::OpenVhd(\n                        m_vmConfig.SwapFilePath.c_str(), VIRTUAL_DISK_ACCESS_CREATE | VIRTUAL_DISK_ACCESS_METAOPS | VIRTUAL_DISK_ACCESS_GET_INFO);\n                    wsl::core::filesystem::ResizeExistingVhd(handle.get(), m_vmConfig.SwapSizeBytes, RESIZE_VIRTUAL_DISK_FLAG_ALLOW_UNSAFE_VIRTUAL_SIZE);\n                }\n                else if (FAILED(result))\n                {\n                    EMIT_USER_WARNING(wsl::shared::Localization::MessagedFailedToCreateSwapVhd(\n                        m_vmConfig.SwapFilePath.c_str(), wsl::windows::common::wslutil::GetSystemErrorString(result).c_str()));\n\n                    THROW_HR(result);\n                }\n            }\n\n            swapLun = AttachDiskLockHeld(m_vmConfig.SwapFilePath.c_str(), DiskType::VHD, MountFlags::None, {}, false, m_userToken.get());\n        }\n        CATCH_LOG()\n    }\n\n    // Validate that the requesting network mode is supported.\n    //\n    // N.B. This must be done before sending the initial configuration message because some guest\n    //      behavior is determined by the networking mode.\n    ValidateNetworkingMode();\n\n    // Send the early configuration message.\n    wsl::shared::MessageWriter<LX_MINI_INIT_EARLY_CONFIG_MESSAGE> message(LxMiniInitMessageEarlyConfig);\n    message->SwapLun = swapLun;\n    message->SystemDistroDeviceType = m_systemDistroDeviceType;\n    message->SystemDistroDeviceId = m_systemDistroDeviceId;\n    message->PageReportingOrder = m_coldDiscardShiftSize;\n    message->MemoryReclaimMode = static_cast<LX_MINI_INIT_MEMORY_RECLAIM_MODE>(m_vmConfig.MemoryReclaim);\n    message->EnableDebugShell = m_vmConfig.EnableDebugShell;\n    message->EnableSafeMode = m_vmConfig.EnableSafeMode;\n    message->EnableDnsTunneling = m_vmConfig.EnableDnsTunneling && m_vmConfig.NetworkingMode != NetworkingMode::VirtioProxy;\n    message->DefaultKernel = m_defaultKernel;\n    message->KernelModulesDeviceId = m_kernelModulesDeviceId;\n    message.WriteString(message->HostnameOffset, wsl::windows::common::filesystem::GetLinuxHostName());\n    message.WriteString(message->KernelModulesListOffset, m_vmConfig.KernelModulesList);\n    message->DnsTunnelingIpAddress = m_vmConfig.DnsTunnelingIpAddress.value_or(0);\n\n    m_miniInitChannel.SendMessage<LX_MINI_INIT_EARLY_CONFIG_MESSAGE>(message.Span());\n\n    {\n        ExecutionContext context(Context::ConfigureNetworking);\n\n        // Accept the connection from the guest network service and create the channel.\n        wsl::core::GnsChannel gnsChannel(AcceptConnection(m_vmConfig.KernelBootTimeout));\n\n        // Create hvsocket connection for DNS tunneling if enabled.\n        wil::unique_socket dnsTunnelingSocket;\n        if (message->EnableDnsTunneling)\n        {\n            dnsTunnelingSocket = AcceptConnection(m_vmConfig.KernelBootTimeout);\n        }\n\n        // Record the start time of the networking engine initialization so the duration can be logged.\n        const auto startTime = std::chrono::steady_clock::now();\n\n        // For NAT networking, ensure the network can be created. If creating the network fails, fall back to\n        // virtio proxy networking mode.\n        wsl::windows::common::hcs::unique_hcn_network natNetwork;\n        if (m_vmConfig.NetworkingMode == NetworkingMode::Nat)\n        {\n            natNetwork = wsl::core::NatNetworking::CreateNetwork(m_vmConfig);\n            if (!natNetwork)\n            {\n                EMIT_USER_WARNING(wsl::shared::Localization::MessageNetworkInitializationFailedFallback2(\n                    ToString(m_vmConfig.NetworkingMode), ToString(NetworkingMode::VirtioProxy)));\n\n                m_vmConfig.NetworkingMode = NetworkingMode::VirtioProxy;\n            }\n        }\n\n        // Create and initialize the networking engine.\n        const auto result = wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&] {\n            if (m_vmConfig.NetworkingMode == NetworkingMode::Mirrored)\n            {\n                m_networkingEngine = std::make_unique<wsl::core::MirroredNetworking>(\n                    m_system.get(), std::move(gnsChannel), m_vmConfig, m_runtimeId, std::move(dnsTunnelingSocket));\n            }\n            else if (m_vmConfig.NetworkingMode == NetworkingMode::Nat)\n            {\n                WI_ASSERT(natNetwork);\n\n                m_networkingEngine = std::make_unique<wsl::core::NatNetworking>(\n                    m_system.get(), std::move(natNetwork), std::move(gnsChannel), m_vmConfig, std::move(dnsTunnelingSocket));\n            }\n            else if (m_vmConfig.NetworkingMode == NetworkingMode::VirtioProxy)\n            {\n                wsl::core::VirtioNetworkingFlags flags = wsl::core::VirtioNetworkingFlags::Ipv6;\n                WI_SetFlagIf(flags, wsl::core::VirtioNetworkingFlags::LocalhostRelay, m_vmConfig.EnableLocalhostRelay);\n                WI_SetFlagIf(flags, wsl::core::VirtioNetworkingFlags::DnsTunneling, m_vmConfig.EnableDnsTunneling);\n                m_networkingEngine = std::make_unique<wsl::core::VirtioNetworking>(\n                    std::move(gnsChannel), flags, LX_INIT_RESOLVCONF_FULL_HEADER, m_guestDeviceManager, m_userToken);\n            }\n            else if (m_vmConfig.NetworkingMode == NetworkingMode::Bridged)\n            {\n                m_networkingEngine = std::make_unique<wsl::core::BridgedNetworking>(m_system.get(), m_vmConfig);\n            }\n            else\n            {\n                WI_ASSERT(m_vmConfig.NetworkingMode == NetworkingMode::None);\n            }\n\n            if (m_networkingEngine)\n            {\n                m_networkingEngine->Initialize();\n            }\n        });\n\n        // Find the interface type of the host interface that is most likely to give Internet connectivity\n        const auto bestInterfaceIndex = wsl::core::networking::GetBestInterface();\n        MIB_IFROW row{};\n        row.dwIndex = bestInterfaceIndex;\n        IFTYPE bestInterfaceType{};\n        // Ignore failures\n        if (row.dwIndex != 0 && SUCCEEDED_WIN32(GetIfEntry(&row)))\n        {\n            bestInterfaceType = row.dwType;\n        }\n\n        const auto endTime = std::chrono::steady_clock::now();\n\n        // Log telemetry on the VM initialization including some of its key settings\n        WSL_LOG_TELEMETRY(\n            \"WslCoreVmInitialize\",\n            PDT_ProductAndServicePerformance,\n            TraceLoggingValue(m_runtimeId, \"vmId\"),\n            TraceLoggingValue(ToString(m_vmConfig.NetworkingMode), \"networkingMode\"),\n            TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"firewallEnabled\"),\n            TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"dnsTunnelingEnabled\"),\n            TraceLoggingValue(\n                m_vmConfig.DnsTunnelingIpAddress.has_value()\n                    ? wsl::windows::common::string::IntegerIpv4ToWstring(m_vmConfig.DnsTunnelingIpAddress.value()).c_str()\n                    : L\"\",\n                \"dnsTunnelingIpAddress\"),\n            TraceLoggingValue(bestInterfaceType, \"bestInterfaceType\"),\n            TraceLoggingValue(result, \"result\"),\n            TraceLoggingValue((std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime)).count(), \"durationMs\"));\n\n        if (FAILED(result))\n        {\n            const auto* context = ExecutionContext::Current();\n            if (context != nullptr)\n            {\n                // We already have a specialized error message, display it to the user.\n                const auto& currentError = context->ReportedError();\n                if (currentError.has_value())\n                {\n                    auto strings = wsl::windows::common::wslutil::ErrorToString(currentError.value());\n                    EMIT_USER_WARNING(Localization::MessageErrorCode(strings.Message, strings.Code));\n                }\n            }\n\n            // If something failed during initialization that indicates a dependent service is not running,\n            // inform the user to install the Virtual Machine Platform optional component.\n            if (wsl::core::networking::IsNetworkErrorForMissingServices(result) &&\n                !wsl::windows::common::wslutil::IsVirtualMachinePlatformInstalled())\n            {\n                wsl::windows::common::notifications::DisplayOptionalComponentsNotification();\n                EMIT_USER_WARNING(Localization::MessageVirtualMachinePlatformNotInstalled());\n            }\n\n            // Fall back to no networking.\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageNetworkInitializationFailedFallback2(\n                ToString(m_vmConfig.NetworkingMode), ToString(NetworkingMode::None)));\n\n            m_vmConfig.NetworkingMode = NetworkingMode::None;\n            m_networkingEngine.reset();\n        }\n    }\n\n    // Perform additional initialization.\n    InitializeGuest();\n}\n\nWslCoreVm::~WslCoreVm() noexcept\n{\n    TraceLoggingActivity<g_hTraceLoggingProvider, MICROSOFT_KEYWORD_MEASURES> activity;\n    TraceLoggingWriteStart(\n        activity,\n        \"TerminateVmStart\",\n        TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance),\n        TraceLoggingValue(m_runtimeId, \"vmId\"));\n\n    m_networkingEngine.reset();\n\n    auto lock = m_lock.lock_exclusive();\n\n    if (m_drvfsInitialResult.valid())\n    {\n        try\n        {\n            m_drvfsInitialResult.get();\n        }\n        CATCH_LOG()\n    }\n\n    // Clear out the exit callback.\n    {\n        auto exitLock = m_exitCallbackLock.lock_exclusive();\n        m_onExit = nullptr;\n\n        // Signal that the vm is terminating\n        // N.B. This might have already been signaled if the VM exited abnormally.\n        m_terminatingEvent.SetEvent();\n    }\n\n    if (m_system)\n    {\n        bool unexpectedTerminate = m_vmExitEvent.is_signaled();\n        bool forcedTerminate = false;\n\n        // Close the socket to mini_init. This will cause mini_init to break out\n        // of its message processing loop and perform a clean shutdown.\n        m_miniInitChannel.Close();\n\n        if (!unexpectedTerminate)\n        {\n            // Wait to receive the notification that the VM has exited.\n            forcedTerminate = !m_vmExitEvent.wait(UTILITY_VM_SHUTDOWN_TIMEOUT);\n\n            // If the notification did not arrive within the timeout, the VM is\n            // forcefully terminated.\n            if (forcedTerminate)\n            {\n                try\n                {\n                    wsl::windows::common::hcs::TerminateComputeSystem(m_system.get());\n                }\n                CATCH_LOG()\n            }\n        }\n\n        m_vmExitEvent.wait(UTILITY_VM_TERMINATE_TIMEOUT);\n\n        TraceLoggingWriteTagged(\n            activity,\n            \"TerminateVm\",\n            TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),\n            TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance),\n            TraceLoggingValue(WSL_PACKAGE_VERSION, \"wslVersion\"),\n            TraceLoggingValue(m_runtimeId, \"vmId\"),\n            TraceLoggingValue(forcedTerminate, \"forceTerminate\"),\n            TraceLoggingValue(unexpectedTerminate, \"unexpectedTerminate\"),\n            TraceLoggingValue(m_vmExitEvent.is_signaled(), \"terminationCallbackReceived\"),\n            TraceLoggingValue(m_exitDetails.c_str(), \"exitDetails\"));\n    }\n\n    // Wait for the distro exit callback thread to exit.\n    // The thread might not have been started, in that case joinable() returns false.\n    if (m_distroExitThread.joinable())\n    {\n        m_distroExitThread.join();\n    }\n\n    if (m_virtioFsThread.joinable())\n    {\n        m_virtioFsThread.join();\n    }\n\n    if (m_crashDumpCollectionThread.joinable())\n    {\n        m_crashDumpCollectionThread.join();\n    }\n\n    // Close the handle to the VM. This will wait for any outstanding callbacks.\n    m_system.reset();\n\n    // This loops helps against a potential crash in build <= Windows 11 22H2.\n    for (const auto& e : m_plan9Servers)\n    {\n        LOG_IF_FAILED(e.second->Teardown());\n    }\n\n    // Shutdown virtio device hosts.\n    if (m_guestDeviceManager)\n    {\n        m_guestDeviceManager->Shutdown();\n    }\n\n    // Call RevokeVmAccess on each VHD that was added to the utility VM. This\n    // ensures that the ACL on the VHD does not grow unbounded.\n    std::for_each(m_attachedDisks.begin(), m_attachedDisks.end(), [&](const auto& Entry) {\n        if ((Entry.first.Type == DiskType::PassThrough) && (WI_IsFlagSet(Entry.second.Flags, DiskStateFlags::Online)))\n        {\n            RestorePassthroughDiskState(Entry.first.Path.c_str());\n        }\n\n        if (WI_IsFlagSet(Entry.second.Flags, DiskStateFlags::AccessGranted))\n        {\n            try\n            {\n                wsl::windows::common::hcs::RevokeVmAccess(m_machineId.c_str(), Entry.first.Path.c_str());\n            }\n            CATCH_LOG()\n        }\n    });\n\n    // Delete the swap vhd if one was created.\n    if (m_swapFileCreated)\n    {\n        try\n        {\n            const auto runAsUser = wil::impersonate_token(m_userToken.get());\n            LOG_IF_WIN32_BOOL_FALSE(DeleteFileW(m_vmConfig.SwapFilePath.c_str()));\n        }\n        CATCH_LOG()\n    }\n\n    // Delete the temp folder if it was created.\n    if (m_tempDirectoryCreated)\n    {\n        try\n        {\n            const auto runAsUser = wil::impersonate_token(m_userToken.get());\n            wil::RemoveDirectoryRecursive(m_tempPath.c_str());\n        }\n        CATCH_LOG()\n    }\n\n    // Delete the mstsc.exe local devices key if one was created.\n    if (m_localDevicesKeyCreated)\n    {\n        try\n        {\n            const auto runAsUser = wil::impersonate_token(m_userToken.get());\n            const auto userKey = wsl::windows::common::registry::OpenCurrentUser();\n            const auto key = wsl::windows::common::registry::CreateKey(userKey.get(), c_localDevicesKey, KEY_SET_VALUE);\n            THROW_IF_WIN32_ERROR(::RegDeleteKeyValueW(key.get(), nullptr, m_machineId.c_str()));\n        }\n        CATCH_LOG()\n    }\n\n    WSL_LOG(\"TerminateVmStop\");\n}\n\nwil::unique_socket WslCoreVm::AcceptConnection(_In_ DWORD ReceiveTimeout, _In_ const std::source_location& Location) const\n{\n    auto socket = hvsocket::CancellableAccept(m_listenSocket.get(), m_vmConfig.KernelBootTimeout, m_terminatingEvent.get(), Location);\n    THROW_HR_IF(E_ABORT, !socket.has_value());\n\n    if (ReceiveTimeout != 0)\n    {\n        THROW_LAST_ERROR_IF(setsockopt(socket->get(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&ReceiveTimeout, sizeof(ReceiveTimeout)) == SOCKET_ERROR);\n    }\n\n    return std::move(socket.value());\n}\n\n_Requires_lock_held_(m_guestDeviceLock)\nvoid WslCoreVm::AddDrvFsShare(_In_ bool Admin, _In_ HANDLE UserToken)\n{\n    THROW_HR_IF(HCS_E_TERMINATED, !m_system);\n\n    // Allow the Plan 9 server to create NT symlinks.\n    //\n    // N.B. This may fail for unelevated users, however symlink creation will\n    //      succeed even without this privilege if developer mode is enabled.\n    wsl::windows::common::security::EnableTokenPrivilege(UserToken, SE_CREATE_SYMBOLIC_LINK_NAME);\n\n    // Set the 9p port and virtio tag.\n    const UINT32 port = Admin ? LX_INIT_UTILITY_VM_PLAN9_DRVFS_ADMIN_PORT : LX_INIT_UTILITY_VM_PLAN9_DRVFS_PORT;\n    const PCWSTR tag = Admin ? TEXT(LX_INIT_DRVFS_ADMIN_VIRTIO_TAG) : TEXT(LX_INIT_DRVFS_VIRTIO_TAG);\n    AddPlan9Share(\n        TEXT(LX_INIT_UTILITY_VM_DRVFS_SHARE_NAME), L\"\\\\\\\\?\", port, (hcs::Plan9ShareFlags::AllowOptions | hcs::Plan9ShareFlags::AllowSubPaths), UserToken, tag);\n\n    const auto virtiofsInitialized = Admin ? m_adminDrvfsToken.is_valid() : m_drvfsToken.is_valid();\n    if (m_vmConfig.EnableVirtioFs && !virtiofsInitialized)\n    {\n        // Add virtiofs devices associating indices with paths from the fixed drive bitmap. These devices support\n        // multiple mounts in the guest, so this only needs to be done once.\n        auto fixedDrives = wsl::windows::common::filesystem::EnumerateFixedDrives(UserToken).first;\n        while (fixedDrives != 0)\n        {\n            ULONG index;\n            WI_VERIFY(_BitScanForward(&index, fixedDrives) != FALSE);\n            const wchar_t fixedDrivePath[] = {gsl::narrow_cast<wchar_t>(L'A' + index), L':', L'\\\\', L'\\0'};\n            AddVirtioFsShare(Admin, fixedDrivePath, TEXT(LX_INIT_DEFAULT_PLAN9_MOUNT_OPTIONS), UserToken);\n            fixedDrives ^= (1 << index);\n        }\n    }\n}\n\nbool WslCoreVm::IsDisableVgpuSettingsSupported() const\n{\n    // See if the Windows version has the required platform change.\n    return ((wsl::windows::common::hcs::GetSchemaVersion() >= c_schemaVersionNickel) && (m_windowsVersion.BuildNumber >= 22545));\n}\n\nbool WslCoreVm::IsVirtioSerialConsoleSupported() const\n{\n    if (!m_vmConfig.EnableVirtio)\n    {\n        return false;\n    }\n\n    // See if the Windows version has the required platform change.\n    //\n    // N.B. If the package is running on a vibranium or iron build, then it means that lifted\n    //      support is available, so virtio serial is available as well (since it was done in the same LCU).\n    return m_windowsVersion.BuildNumber != WindowsBuildNumbers::Cobalt ||\n           m_windowsVersion.UpdateBuildRevision >= VIRTIO_SERIAL_CONSOLE_COBALT_RELEASE_UBR;\n}\n\nbool WslCoreVm::IsVmemmSuffixSupported() const\n{\n    // See if the Windows version has the required platform change.\n    return (\n        (m_windowsVersion.BuildNumber >= VMMEM_SUFFIX_NICKEL_BUILD_NUMBER) ||\n        ((m_windowsVersion.BuildNumber < NICKEL_BUILD_FLOOR) && (m_windowsVersion.BuildNumber >= VMEMM_SUFFIX_COBALT_REFRESH_BUILD_NUMBER)) ||\n        ((m_windowsVersion.BuildNumber == WindowsBuildNumbers::Cobalt) &&\n         (m_windowsVersion.UpdateBuildRevision >= VMMEM_SUFFIX_COBALT_RELEASE_UBR)));\n}\n\n_Requires_lock_held_(m_guestDeviceLock)\nvoid WslCoreVm::AddPlan9Share(\n    _In_ PCWSTR AccessName, _In_ PCWSTR Path, [[maybe_unused]] _In_ UINT32 Port, _In_ hcs::Plan9ShareFlags Flags, _In_ HANDLE UserToken, _In_opt_ PCWSTR VirtIoTag)\n{\n    bool addNewDevice = false;\n    wil::com_ptr<IPlan9FileSystem> server;\n\n    {\n        auto revert = wil::impersonate_token(UserToken);\n\n        // This is called from AddDrvFsShare, which is called from InitializeDrvFs, so m_guestDeviceLock is\n        // already held.\n\n        if (m_vmConfig.EnableVirtio9p)\n        {\n            server = m_guestDeviceManager->GetRemoteFileSystem(__uuidof(p9fs::Plan9FileSystem), VirtIoTag);\n        }\n        else\n        {\n            const auto existingServer = m_plan9Servers.find(Port);\n            if (existingServer != m_plan9Servers.end())\n            {\n                server = existingServer->second;\n            }\n        }\n\n        if (!server)\n        {\n            server = wsl::windows::common::wslutil::CreateComServerAsUser<p9fs::Plan9FileSystem, IPlan9FileSystem>(UserToken);\n            if (m_vmConfig.EnableVirtio9p)\n            {\n                m_guestDeviceManager->AddRemoteFileSystem(__uuidof(p9fs::Plan9FileSystem), VirtIoTag, server);\n\n                // Start with one device to handle the first mount request. After\n                // each mount, the Plan9 file-system will request additional\n                // devices via the IPlan9FileSystemHost::NotifyAllDevicesInUse\n                // callback.\n                addNewDevice = true;\n            }\n            else\n            {\n                THROW_IF_FAILED(server->Init(&m_runtimeId, Port));\n                THROW_IF_FAILED(server->Resume());\n                m_plan9Servers.insert(std::make_pair(Port, server));\n            }\n        }\n\n        HRESULT result = server->AddSharePath(AccessName, Path, static_cast<UINT32>(Flags));\n        if (result == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))\n        {\n            result = S_OK;\n        }\n\n        THROW_IF_FAILED(result);\n    }\n\n    if (addNewDevice)\n    {\n        // This requires more privileges than the user may have, so impersonation is disabled.\n        (void)m_guestDeviceManager->AddNewDevice(VIRTIO_PLAN9_DEVICE_ID, server, VirtIoTag);\n    }\n}\n\nULONG WslCoreVm::AttachDisk(_In_ PCWSTR Disk, _In_ DiskType Type, _In_ std::optional<ULONG> Lun, _In_ bool IsUserDisk, _In_ HANDLE UserToken)\n{\n    auto lock = m_lock.lock_exclusive();\n    return AttachDiskLockHeld(Disk, Type, MountFlags::None, Lun, IsUserDisk, UserToken);\n}\n\nULONG WslCoreVm::AttachDiskLockHeld(\n    _In_ PCWSTR Disk, _In_ DiskType Type, _In_ MountFlags Flags, _In_ std::optional<ULONG> Lun, _In_ bool IsUserDisk, _In_opt_ HANDLE UserToken)\n{\n    ExecutionContext context(Context::MountDisk);\n\n    Lun = ReserveLun(Lun);\n\n    // Set a scope exit variable to perform cleanup if attaching the disk fails.\n    DiskStateFlags diskFlags{};\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\n        FreeLun(Lun.value());\n        if (WI_IsFlagSet(diskFlags, DiskStateFlags::AccessGranted))\n        {\n            wsl::windows::common::hcs::RevokeVmAccess(m_machineId.c_str(), Disk);\n        }\n\n        if (WI_IsFlagSet(diskFlags, DiskStateFlags::Online))\n        {\n            const auto diskHandle = wsl::windows::common::disk::OpenDevice(Disk, GENERIC_READ | GENERIC_WRITE, m_vmConfig.MountDeviceTimeout);\n            wsl::windows::common::disk::SetOnline(diskHandle.get(), false, m_vmConfig.MountDeviceTimeout);\n        }\n    });\n\n    try\n    {\n        // Check if the disk is already attached.\n        const auto found = m_attachedDisks.find({Type, Disk});\n\n        if (Type == DiskType::PassThrough)\n        {\n            if (found != m_attachedDisks.end())\n            {\n                THROW_HR_WITH_USER_ERROR(WSL_E_DISK_ALREADY_ATTACHED, Localization::MessageDiskAlreadyAttached(Disk));\n            }\n\n            // Grant the VM access to the disk.\n            GrantVmWorkerProcessAccessToDisk(Disk, UserToken);\n            WI_SetFlag(diskFlags, DiskStateFlags::AccessGranted);\n\n            // Set the disk online if needed.\n            //\n            // N.B. The disk handle must be closed prior to adding the disk to the VM.\n            {\n                const auto diskHandle =\n                    wsl::windows::common::disk::OpenDevice(Disk, GENERIC_READ | GENERIC_WRITE, m_vmConfig.MountDeviceTimeout);\n                if (wsl::windows::common::disk::IsDiskOnline(diskHandle.get()))\n                {\n                    wsl::windows::common::disk::SetOnline(diskHandle.get(), false, m_vmConfig.MountDeviceTimeout);\n                    WI_SetFlag(diskFlags, DiskStateFlags::Online);\n                }\n            }\n\n            // Add the disk to the VM.\n            wsl::shared::retry::RetryWithTimeout<void>(\n                std::bind(wsl::windows::common::hcs::AddPassThroughDisk, m_system.get(), Disk, Lun.value()),\n                wsl::windows::common::disk::c_diskOperationRetry,\n                std::chrono::milliseconds(m_vmConfig.MountDeviceTimeout),\n                []() { return wil::ResultFromCaughtException() == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION); });\n        }\n        else\n        {\n            if (found != m_attachedDisks.end())\n            {\n                // Prevent user from launching a distro vhd after manually mounting it; otherwise, return the LUN of the mounted disk.\n                THROW_HR_IF(WSL_E_USER_VHD_ALREADY_ATTACHED, found->first.User);\n\n                return found->second.Lun;\n            }\n\n            auto grantDiskAccess = [&]() {\n                auto runAsUser = wil::impersonate_token(UserToken);\n                wsl::windows::common::hcs::GrantVmAccess(m_machineId.c_str(), Disk);\n                WI_SetFlag(diskFlags, DiskStateFlags::AccessGranted);\n            };\n\n            // Grant the VM access to the disk.\n            if (WI_IsFlagClear(Flags, MountFlags::ReadOnly))\n            {\n                grantDiskAccess();\n            }\n\n            auto result = wil::ResultFromException([&]() {\n                wsl::windows::common::hcs::AddVhd(m_system.get(), Disk, Lun.value(), WI_IsFlagSet(Flags, MountFlags::ReadOnly));\n            });\n\n            if (result == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) && WI_IsFlagClear(diskFlags, DiskStateFlags::AccessGranted))\n            {\n                grantDiskAccess();\n                wsl::windows::common::hcs::AddVhd(m_system.get(), Disk, Lun.value(), WI_IsFlagSet(Flags, MountFlags::ReadOnly));\n            }\n            else\n            {\n                THROW_IF_FAILED(result);\n            }\n        }\n    }\n    catch (...)\n    {\n        const auto result = wil::ResultFromCaughtException();\n        THROW_HR_WITH_USER_ERROR(\n            result, Localization::MessageFailedToAttachDisk(Disk, wsl::windows::common::wslutil::GetSystemErrorString(result)));\n    }\n\n    m_attachedDisks.emplace(AttachedDisk{Type, Disk, IsUserDisk}, DiskState{Lun.value(), {}, diskFlags});\n    cleanup.release();\n\n    return Lun.value();\n}\n\nvoid WslCoreVm::CollectCrashDumps(wil::unique_socket&& listenSocket) const\n{\n    wsl::windows::common::wslutil::SetThreadDescription(L\"CrashDumpCollection\");\n\n    while (!m_terminatingEvent.is_signaled())\n    {\n        try\n        {\n            auto socket = hvsocket::CancellableAccept(listenSocket.get(), INFINITE, m_terminatingEvent.get());\n            if (!socket.has_value())\n            {\n                break; // VM is exiting.\n            }\n\n            DWORD receiveTimeout = m_vmConfig.KernelBootTimeout;\n            THROW_LAST_ERROR_IF(setsockopt(socket->get(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&receiveTimeout, sizeof(receiveTimeout)) == SOCKET_ERROR);\n\n            auto channel = wsl::shared::SocketChannel{std::move(socket.value()), \"crash_dump\", m_terminatingEvent.get()};\n\n            const auto& message = channel.ReceiveMessage<LX_PROCESS_CRASH>();\n            const char* process = reinterpret_cast<const char*>(&message.Buffer);\n\n            constexpr auto dumpExtension = \".dmp\";\n            constexpr auto dumpPrefix = \"wsl-crash\";\n\n            auto filename = std::format(\"{}-{}-{}-{}-{}{}\", dumpPrefix, message.Timestamp, message.Pid, process, message.Signal, dumpExtension);\n\n            std::replace_if(filename.begin(), filename.end(), [](auto e) { return !std::isalnum(e) && e != '.' && e != '-'; }, '_');\n\n            auto fullPath = m_vmConfig.CrashDumpFolder / filename;\n\n            // Log telemetry when there is a crash within the WSL VM\n            WSL_LOG_TELEMETRY(\n                \"LinuxCrash\",\n                PDT_ProductAndServicePerformance,\n                TraceLoggingValue(fullPath.c_str(), \"FullPath\"),\n                TraceLoggingValue(message.Pid, \"Pid\"),\n                TraceLoggingValue(message.Signal, \"Signal\"),\n                TraceLoggingValue(process, \"process\"));\n\n            auto runAsUser = wil::impersonate_token(m_userToken.get());\n\n            std::error_code error;\n            std::filesystem::create_directories(m_vmConfig.CrashDumpFolder, error);\n            if (error.value())\n            {\n                THROW_WIN32_MSG(error.value(), \"Failed to create folder: %ls\", m_vmConfig.CrashDumpFolder.c_str());\n            }\n\n            // Only delete files that:\n            // - have the temporary flag set\n            // - start with 'wsl-crash'\n            // - end in .dmp\n            //\n            // This logic is here to prevent accidental user file deletion\n\n            auto pred = [&dumpExtension, &dumpPrefix](const auto& e) {\n                return WI_IsFlagSet(GetFileAttributes(e.path().c_str()), FILE_ATTRIBUTE_TEMPORARY) && e.path().has_extension() &&\n                       e.path().extension() == dumpExtension && e.path().has_filename() &&\n                       e.path().filename().string().find(dumpPrefix) == 0;\n            };\n\n            wsl::windows::common::wslutil::EnforceFileLimit(m_vmConfig.CrashDumpFolder.c_str(), m_vmConfig.MaxCrashDumpCount, pred);\n\n            wil::unique_hfile file{CreateFileW(fullPath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, nullptr)};\n            THROW_LAST_ERROR_IF(!file);\n\n            channel.SendResultMessage<std::int32_t>(0);\n\n            wsl::windows::common::relay::InterruptableRelay(reinterpret_cast<HANDLE>(channel.Socket()), file.get(), nullptr);\n        }\n        CATCH_LOG();\n    }\n}\n\nstd::shared_ptr<LxssRunningInstance> WslCoreVm::CreateInstance(\n    _In_ const GUID& InstanceId,\n    _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n    _In_ LX_MESSAGE_TYPE MessageType,\n    _In_ DWORD ReceiveTimeout,\n    _In_ ULONG DefaultUid,\n    _In_ ULONG64 ClientLifetimeId,\n    _In_ ULONG ExportFlags,\n    _Out_opt_ ULONG* ConnectPort)\n{\n    // Add the VHD to the machine.\n    auto lock = m_lock.lock_exclusive();\n    const auto lun = AttachDiskLockHeld(Configuration.VhdFilePath.c_str(), DiskType::VHD, MountFlags::None, {}, false, m_userToken.get());\n\n    // Launch the init daemon and create the instance.\n    int flags = LxMiniInitMessageFlagNone;\n    std::wstring sharedMemoryRoot{};\n\n#ifdef WSL_DEV_INSTALL_PATH\n\n    std::wstring installPath = TEXT(WSL_DEV_INSTALL_PATH);\n\n#else\n\n    std::wstring installPath = m_installPath.wstring();\n\n#endif\n\n    std::wstring userProfile{};\n    if (LXSS_ENABLE_GUI_APPS() && (MessageType == LxMiniInitMessageLaunchInit))\n    {\n        WI_SetFlag(flags, LxMiniInitMessageFlagLaunchSystemDistro);\n        sharedMemoryRoot = m_sharedMemoryRoot;\n\n        userProfile = m_userProfile;\n    }\n\n    WI_SetFlagIf(flags, LxMiniInitMessageFlagExportCompressGzip, WI_IsFlagSet(ExportFlags, LXSS_EXPORT_DISTRO_FLAGS_GZIP));\n    WI_SetFlagIf(flags, LxMiniInitMessageFlagExportCompressXzip, WI_IsFlagSet(ExportFlags, LXSS_EXPORT_DISTRO_FLAGS_XZIP));\n    WI_SetFlagIf(flags, LxMiniInitMessageFlagVerbose, WI_IsFlagSet(ExportFlags, LXSS_EXPORT_DISTRO_FLAGS_VERBOSE));\n\n    wsl::shared::MessageWriter<LX_MINI_INIT_MESSAGE> message(MessageType);\n    message->MountDeviceType = LxMiniInitMountDeviceTypeLun;\n    message->DeviceId = lun;\n    message->Flags = flags;\n    message.WriteString(message->FsTypeOffset, \"ext4\");\n    message.WriteString(message->MountOptionsOffset, \"discard,errors=remount-ro,data=ordered\");\n    message.WriteString(message->VmIdOffset, m_machineId);\n    message.WriteString(message->DistributionNameOffset, Configuration.Name);\n    message.WriteString(message->SharedMemoryRootOffset, sharedMemoryRoot);\n    message.WriteString(message->InstallPathOffset, installPath);\n    message.WriteString(message->UserProfileOffset, userProfile);\n    m_miniInitChannel.SendMessage<LX_MINI_INIT_MESSAGE>(message.Span());\n\n    return CreateInstanceInternal(\n        InstanceId, Configuration, ReceiveTimeout, DefaultUid, ClientLifetimeId, WI_IsFlagSet(flags, LxMiniInitMessageFlagLaunchSystemDistro), ConnectPort);\n}\n\nstd::shared_ptr<LxssRunningInstance> WslCoreVm::CreateInstanceInternal(\n    _In_ const GUID& InstanceId,\n    _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n    _In_ DWORD ReceiveTimeout,\n    _In_ ULONG DefaultUid,\n    _In_ ULONG64 ClientLifetimeId,\n    _In_ bool LaunchSystemDistro,\n    _Out_opt_ ULONG* ConnectPort)\n{\n    // Clear the drive mounting flag if support is disabled at the VM level.\n    //\n    // N.B. If the system distro is enabled the share will still be created since\n    //      GUI apps require access to the Windows file system in order to launch mstsc.\n    LXSS_DISTRO_CONFIGURATION localConfig = Configuration;\n    WI_ClearFlagIf(localConfig.Flags, LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING, !m_vmConfig.EnableHostFileSystemAccess);\n\n    // Establish a communication channel with the init daemon.\n    auto initSocket = AcceptConnection(ReceiveTimeout);\n\n    // If the system distro is enabled, establish a communication channel with its init daemon.\n    wil::unique_socket systemDistroSocket;\n    if (LaunchSystemDistro)\n    {\n        WI_ASSERT(m_vmConfig.EnableGuiApps);\n        systemDistroSocket = AcceptConnection(ReceiveTimeout);\n    }\n\n    // Set feature flags for the instance.\n    ULONG featureFlags{};\n    WI_SetFlagIf(featureFlags, LxInitFeatureVirtIo9p, m_vmConfig.EnableVirtio9p);\n    WI_SetFlagIf(featureFlags, LxInitFeatureVirtIoFs, m_vmConfig.EnableVirtioFs);\n    WI_SetFlagIf(featureFlags, LxInitFeatureDnsTunneling, m_vmConfig.EnableDnsTunneling);\n\n    // Create an instance, this takes ownership of the sockets.\n    auto instance = std::make_shared<WslCoreInstance>(\n        m_userToken.get(),\n        initSocket,\n        systemDistroSocket,\n        InstanceId,\n        m_runtimeId,\n        localConfig,\n        DefaultUid,\n        ClientLifetimeId,\n        std::bind(s_InitializeDrvFs, this, std::placeholders::_1),\n        featureFlags,\n        m_vmConfig.DistributionStartTimeout,\n        m_vmConfig.InstanceIdleTimeout,\n        ConnectPort);\n\n    WI_ASSERT(!initSocket && !systemDistroSocket);\n\n    return instance;\n}\n\nwil::unique_socket WslCoreVm::CreateListeningSocket() const\n{\n    return wsl::windows::common::hvsocket::Listen(m_runtimeId, 0);\n}\n\nstd::pair<int, LX_MINI_MOUNT_STEP> WslCoreVm::DetachDisk(_In_opt_ PCWSTR Disk)\n{\n    bool deleted = !ARGUMENT_PRESENT(Disk);\n    std::vector<AttachedDisk> selectedDisks;\n\n    auto diskMatches = [TargetPath = Disk](const AttachedDisk& disk) {\n        if (!disk.User)\n        {\n            // Only user mounted disks can be detached\n            return false;\n        }\n\n        if (disk.Type == DiskType::VHD)\n        {\n            // N.B. std::filesystem::equivalent can throw if the path is malformed so use the noexcept variant.\n            std::error_code error{};\n            return TargetPath == nullptr || std::filesystem::equivalent(disk.Path, TargetPath, error);\n        }\n        else if (disk.Type == DiskType::PassThrough)\n        {\n            return TargetPath == nullptr || wsl::windows::common::string::IsPathComponentEqual(disk.Path, TargetPath);\n        }\n\n        return false;\n    };\n\n    auto lock = m_lock.lock_exclusive();\n    for (auto it = m_attachedDisks.begin(); it != m_attachedDisks.end();)\n    {\n        if (diskMatches(it->first))\n        {\n            // Unmount any mounted volumes inside the utility VM.\n            const auto result = UnmountDisk(it->first, it->second);\n            if (result.first != 0)\n            {\n                return result;\n            }\n\n            // Detach the disk from the VM.\n            wsl::windows::common::hcs::RemoveScsiDisk(m_system.get(), it->second.Lun);\n            if (WI_VERIFY(WI_IsFlagSet(it->second.Flags, DiskStateFlags::AccessGranted)))\n            {\n                wsl::windows::common::hcs::RevokeVmAccess(m_machineId.c_str(), it->first.Path.c_str());\n            }\n\n            FreeLun(it->second.Lun);\n\n            // If the disk was online before being attached, revert to that state.\n            if (WI_IsFlagSet(it->second.Flags, DiskStateFlags::Online))\n            {\n                RestorePassthroughDiskState(it->first.Path.c_str());\n            }\n\n            deleted = true;\n            it = m_attachedDisks.erase(it);\n        }\n        else\n        {\n            ++it;\n        }\n    }\n\n    THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), !deleted);\n\n    return std::make_pair(0, LxMiniInitMountStepNone);\n}\n\nvoid WslCoreVm::EjectVhd(_In_ PCWSTR VhdPath)\n{\n    auto lock = m_lock.lock_exclusive();\n    return EjectVhdLockHeld(VhdPath);\n}\n\n_Requires_lock_held_(m_lock)\nvoid WslCoreVm::EjectVhdLockHeld(_In_ PCWSTR VhdPath)\n{\n    const auto search = m_attachedDisks.find({DiskType::VHD, VhdPath});\n    if (search != m_attachedDisks.end())\n    {\n        EJECT_VHD_MESSAGE message;\n        message.Header.MessageSize = sizeof(message);\n        message.Header.MessageType = LxMiniInitMessageEjectVhd;\n        message.Lun = search->second.Lun;\n        const auto& result = m_miniInitChannel.Transaction(message);\n        LOG_HR_IF_MSG(E_UNEXPECTED, result.Result != 0, \"VHD eject failed: %u\", result.Result);\n\n        // Impersonate the session manager and remove the vhd.\n        {\n            auto runAsSelf = wil::run_as_self();\n            wsl::windows::common::hcs::RemoveScsiDisk(m_system.get(), search->second.Lun);\n            if (WI_IsFlagSet(search->second.Flags, DiskStateFlags::AccessGranted))\n            {\n                wsl::windows::common::hcs::RevokeVmAccess(m_machineId.c_str(), VhdPath);\n            }\n        }\n\n        m_attachedDisks.erase(search);\n        FreeLun(message.Lun);\n    }\n}\n\n_Requires_lock_held_(m_guestDeviceLock)\nstd::optional<WslCoreVm::VirtioFsShare> WslCoreVm::FindVirtioFsShare(_In_ PCWSTR tag, _In_ std::optional<bool> Admin) const\n{\n    for (const auto& share : m_virtioFsShares)\n    {\n        if ((share.second == tag) && (!Admin.has_value() || Admin.value() == share.first.Admin))\n        {\n            return share.first;\n        }\n    }\n\n    return {};\n}\n\nvoid WslCoreVm::FreeLun(_In_ ULONG lun)\n{\n    WI_ASSERT(m_lunBitmap[lun]);\n    m_lunBitmap.set(lun, false);\n}\n\nstd::wstring WslCoreVm::GenerateConfigJson()\n{\n    hcs::ComputeSystem systemSettings{};\n    systemSettings.Owner = wsl::windows::common::wslutil::c_vmOwner;\n    systemSettings.ShouldTerminateOnLastHandleClosed = true;\n    systemSettings.SchemaVersion.Major = 2;\n    systemSettings.SchemaVersion.Minor = 3;\n    hcs::VirtualMachine vmSettings{};\n    vmSettings.StopOnReset = true;\n    vmSettings.Chipset.UseUtc = true;\n\n    // Ensure the 2MB granularity enforced by HCS.\n    vmSettings.ComputeTopology.Memory.SizeInMB = ((m_vmConfig.MemorySizeBytes / _1MB) & ~0x1);\n    vmSettings.ComputeTopology.Memory.AllowOvercommit = true;\n    vmSettings.ComputeTopology.Memory.EnableDeferredCommit = true;\n    vmSettings.ComputeTopology.Memory.EnableColdDiscardHint = true;\n\n    // Configure backing page size, fault cluster shift size, and cold discard hint size to favor density (lower vmmem usage).\n    //\n    // N.B. Cold discard hint size should be a multiple of the fault cluster shift size.\n    //\n    // N.B. This is only done on builds that have the fix for the VID deadlock on partition teardown.\n    if ((m_windowsVersion.BuildNumber >= WindowsBuildNumbers::Germanium) ||\n        (m_windowsVersion.BuildNumber >= WindowsBuildNumbers::Cobalt && m_windowsVersion.UpdateBuildRevision >= 2360) ||\n        (m_windowsVersion.BuildNumber >= WindowsBuildNumbers::Iron && m_windowsVersion.UpdateBuildRevision >= 1970) ||\n        (m_windowsVersion.BuildNumber >= WindowsBuildNumbers::Vibranium_22H2 && m_windowsVersion.UpdateBuildRevision >= 3393))\n    {\n        vmSettings.ComputeTopology.Memory.BackingPageSize = hcs::MemoryBackingPageSize::Small;\n        vmSettings.ComputeTopology.Memory.FaultClusterSizeShift = 4;          // 64k\n        vmSettings.ComputeTopology.Memory.DirectMapFaultClusterSizeShift = 4; // 64k\n        m_coldDiscardShiftSize = 5;                                           // 128k\n    }\n    else\n    {\n        m_coldDiscardShiftSize = 9; // 2MB\n    }\n\n    // May need more MMIO than the default 16GB. WSL uses a vpci device per Plan9 share, WSLg adds a GPU device,\n    // and a pmem device, and each shared memory virtiofs device needs more than 8GB of MMIO.\n    SafeInt<INT64> highMmioGapInMB = DEFAULT_HIGH_MMIO_GAP_IN_MB;\n\n    // Add additional MMIO space for the system distro and WSLg.\n    bool privateSystemDistro = !m_vmConfig.SystemDistroPath.empty();\n    if (!privateSystemDistro)\n    {\n#ifdef WSL_SYSTEM_DISTRO_PATH\n\n        m_vmConfig.SystemDistroPath = TEXT(WSL_SYSTEM_DISTRO_PATH);\n        privateSystemDistro = true;\n\n#else\n\n        m_systemDistroDeviceType = LxMiniInitMountDeviceTypeLun;\n        m_vmConfig.SystemDistroPath = (m_installPath / L\"system.vhd\").wstring();\n        WI_ASSERT(wsl::windows::common::filesystem::FileExists(m_vmConfig.SystemDistroPath.c_str()));\n\n#endif\n    }\n\n    // Ensure the system distro exists and ends with a img or vhd file extension.\n    if (privateSystemDistro)\n    {\n        if (wsl::windows::common::string::IsPathComponentEqual(m_vmConfig.SystemDistroPath.extension().native(), L\".img\"))\n        {\n            m_systemDistroDeviceType = LxMiniInitMountDeviceTypePmem;\n        }\n        else if (wsl::windows::common::string::IsPathComponentEqual(m_vmConfig.SystemDistroPath.extension().native(), L\".vhd\"))\n        {\n            m_systemDistroDeviceType = LxMiniInitMountDeviceTypeLun;\n        }\n\n        THROW_HR_IF(\n            WSL_E_CUSTOM_SYSTEM_DISTRO_ERROR,\n            (m_systemDistroDeviceType == LxMiniInitMountDeviceTypeInvalid) ||\n                (!wsl::windows::common::filesystem::FileExists(m_vmConfig.SystemDistroPath.c_str())));\n    }\n\n    // Add MMIO space for the WSLg virtio shared memory device.\n    if (m_vmConfig.EnableGuiApps && m_vmConfig.EnableVirtio)\n    {\n        highMmioGapInMB += WSLG_SHARED_MEMORY_SIZE_MB + EXTRA_MMIO_SIZE_PER_VIRTIOFS_DEVICE_IN_MB;\n    }\n\n    // If using pmem for the system distro, add MMIO space for the device.\n    if (m_systemDistroDeviceType == LxMiniInitMountDeviceTypePmem)\n    {\n        highMmioGapInMB += RequiredExtraMmioSpaceForPmemFileInMb(m_vmConfig.SystemDistroPath.c_str());\n    }\n\n    // Log telemetry to measure system distro usage.\n    WSL_LOG(\n        \"InitializeSystemDistro\",\n        TraceLoggingValue(static_cast<INT64>(highMmioGapInMB), \"highMmioGapInMB\"),\n        TraceLoggingValue(privateSystemDistro, \"privateSystemDistro\"),\n        TraceLoggingValue(static_cast<DWORD>(m_systemDistroDeviceType), \"systemDistroDeviceType\"),\n        TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n    vmSettings.ComputeTopology.Memory.HighMmioGapInMB = highMmioGapInMB;\n\n    // The guest may only be able to access 36-bits of address space (minimum supported), so shift the high MMIO base\n    // down such that all addresses are accessible. The default starting point is 16G below the maximum 36-bit address,\n    // so for guests that support larger address spaces, the default base should suffice.\n    vmSettings.ComputeTopology.Memory.HighMmioBaseInMB = MAX_36_BIT_PAGE_IN_MB - highMmioGapInMB;\n\n    // Configure the number of processors.\n    vmSettings.ComputeTopology.Processor.Count = m_vmConfig.ProcessorCount;\n\n    // Set the vmmem suffix which will change the process name in task manager.\n    if (IsVmemmSuffixSupported())\n    {\n        vmSettings.ComputeTopology.Memory.HostingProcessNameSuffix = wsl::windows::common::wslutil::c_vmOwner;\n    }\n\n    // If nested virtualization was requested, ensure the platform supports it.\n    //\n    // N.B. This is done because arm64 and some older amd64 processors do not support nested virtualization.\n    //      Nested virtualization not supported on Windows 10.\n    if (m_vmConfig.EnableNestedVirtualization)\n    {\n        try\n        {\n            if (wsl::windows::common::helpers::IsWindows11OrAbove())\n            {\n                const auto& processorFeatures = wsl::windows::common::hcs::GetProcessorFeatures();\n                auto feature = std::find(processorFeatures.begin(), processorFeatures.end(), \"NestedVirt\");\n                m_vmConfig.EnableNestedVirtualization = (feature != processorFeatures.end());\n            }\n            else\n            {\n                m_vmConfig.EnableNestedVirtualization = false;\n            }\n\n            vmSettings.ComputeTopology.Processor.ExposeVirtualizationExtensions = m_vmConfig.EnableNestedVirtualization;\n            if (!m_vmConfig.EnableNestedVirtualization)\n            {\n                EMIT_USER_WARNING(wsl::shared::Localization::MessageNestedVirtualizationNotSupported());\n            }\n        }\n        CATCH_LOG()\n    }\n\n#ifdef _AMD64_\n\n    // Enable hardware performance counters if they are supported.\n    if (m_vmConfig.EnableHardwarePerformanceCounters)\n    {\n        HV_X64_HYPERVISOR_HARDWARE_FEATURES hardwareFeatures{};\n        __cpuid(reinterpret_cast<int*>(&hardwareFeatures), HvCpuIdFunctionMsHvHardwareFeatures);\n        vmSettings.ComputeTopology.Processor.EnablePerfmonPmu = hardwareFeatures.ChildPerfmonPmuSupported != 0;\n        vmSettings.ComputeTopology.Processor.EnablePerfmonLbr = hardwareFeatures.ChildPerfmonLbrSupported != 0;\n    }\n\n#endif\n\n    // Initialize kernel command line.\n    std::wstring kernelCmdLine = L\"initrd=\\\\\" LXSS_VM_MODE_INITRD_NAME L\" \" TEXT(WSL_ROOT_INIT_ENV) L\"=1 panic=-1\";\n\n    // Set number of processors.\n    kernelCmdLine += std::format(L\" nr_cpus={}\", m_vmConfig.ProcessorCount);\n\n    // Enable timesync workaround to sync on resume from sleep in modern standby.\n    kernelCmdLine += L\" hv_utils.timesync_implicit=1\";\n\n    // If using virtio features, enable SWIOTLB as a perf optimization (will cause VM to consume 64MB more memory).\n    if (m_vmConfig.EnableVirtio9p || m_vmConfig.EnableVirtioFs || m_vmConfig.NetworkingMode == NetworkingMode::VirtioProxy)\n    {\n        kernelCmdLine += L\" swiotlb=force\";\n    }\n\n    if (IsVirtioSerialConsoleSupported())\n    {\n        vmSettings.Devices.VirtioSerial.emplace();\n    }\n\n    if (m_dmesgCollector)\n    {\n        if (m_vmConfig.EnableEarlyBootLogging)\n        {\n            // Capture using the very slow legacy serial port up until the point that the virtio device is started.\n            if constexpr (!wsl::shared::Arm64)\n            {\n                kernelCmdLine += L\" earlycon=uart8250,io,0x3f8,115200\";\n            }\n            else\n            {\n                kernelCmdLine += L\" earlycon=pl011,0xeffec000,115200\";\n            }\n\n            vmSettings.Devices.ComPorts[\"0\"] = hcs::ComPort{m_dmesgCollector->EarlyConsoleName()};\n        }\n\n        // The primary \"console\" will be a virtio serial device.\n        kernelCmdLine += L\" console=hvc0 debug\";\n        hcs::VirtioSerialPort virtioPort{};\n        virtioPort.Name = L\"hvc0\";\n        virtioPort.NamedPipe = m_dmesgCollector->VirtioConsoleName();\n        virtioPort.ConsoleSupport = true;\n        vmSettings.Devices.VirtioSerial->Ports[\"0\"] = std::move(virtioPort);\n    }\n    else if (m_vmConfig.EnableDebugConsole)\n    {\n        // If a debug console was requested, add required kernel command line options.\n        if constexpr (!wsl::shared::Arm64)\n        {\n            kernelCmdLine += L\" console=ttyS0,115200 debug\";\n        }\n        else\n        {\n            kernelCmdLine += L\" console=ttyAMA0 debug\";\n        }\n    }\n\n    //\n    // N.B. The ordering of these devices is important because it determines the order they show up as\n    //      /dev/hvc devices in the guest.\n    //\n\n    if (m_gnsTelemetryLogger)\n    {\n        hcs::VirtioSerialPort virtioPort;\n        virtioPort.Name = TEXT(LX_INIT_HVC_TELEMETRY);\n        virtioPort.NamedPipe = m_gnsTelemetryLogger->GetPipeName();\n        virtioPort.ConsoleSupport = true;\n        vmSettings.Devices.VirtioSerial->Ports[\"1\"] = std::move(virtioPort);\n    }\n\n    if (!m_debugShellPipe.empty())\n    {\n        hcs::VirtioSerialPort virtioPort;\n        virtioPort.Name = TEXT(LX_INIT_HVC_DEBUG_SHELL);\n        virtioPort.NamedPipe = m_debugShellPipe;\n        virtioPort.ConsoleSupport = true;\n        vmSettings.Devices.VirtioSerial->Ports[\"2\"] = std::move(virtioPort);\n    }\n\n    // Ensure that virtio serial devices have unique names.\n    if constexpr (wsl::shared::Debug)\n    {\n        if (vmSettings.Devices.VirtioSerial)\n        {\n            std::set<std::wstring_view> uniqueNames;\n            for (const auto& device : vmSettings.Devices.VirtioSerial->Ports)\n            {\n                uniqueNames.emplace(device.second.Name);\n            }\n\n            WI_ASSERT_MSG(\n                uniqueNames.size() == vmSettings.Devices.VirtioSerial->Ports.size(), \"Serial device names must be unique.\");\n        }\n    }\n\n    // If a kernel debugger was requested, add required kernel command line options and\n    // generate the name of the pipe.\n    if (m_vmConfig.KernelDebugPort != 0)\n    {\n        PCWSTR debugDeviceName = nullptr;\n        if constexpr (wsl::shared::Arm64)\n        {\n            debugDeviceName = L\"ttyAMA1\";\n        }\n        else\n        {\n            debugDeviceName = L\"ttyS1\";\n        }\n\n        kernelCmdLine += std::format(L\" pty.legacy_count=2 kgdboc={},115200\", debugDeviceName);\n\n        m_comPipe1 = wsl::windows::common::helpers::GetUniquePipeName();\n        wsl::windows::common::helpers::LaunchKdRelay(\n            m_comPipe1.c_str(), m_restrictedToken.get(), m_vmConfig.KernelDebugPort, m_terminatingEvent.get(), !m_vmConfig.EnableTelemetry);\n    }\n    else\n    {\n        kernelCmdLine += L\" pty.legacy_count=0\";\n    }\n\n    if (!m_comPipe0.empty() && (!m_dmesgCollector || !m_vmConfig.EnableEarlyBootLogging))\n    {\n        vmSettings.Devices.ComPorts[\"0\"] = hcs::ComPort{m_comPipe0};\n    }\n\n    if (!m_comPipe1.empty())\n    {\n        vmSettings.Devices.ComPorts[\"1\"] = hcs::ComPort{m_comPipe1};\n    }\n\n    if (m_vmConfig.MaxCrashDumpCount >= 0)\n    {\n        kernelCmdLine += L\" \" WSL_ENABLE_CRASH_DUMP_ENV L\"=1\";\n    }\n\n    // Add user-specified kernel command line options at the end.\n    if (!m_vmConfig.KernelCommandLine.empty())\n    {\n        kernelCmdLine += L\" \";\n        kernelCmdLine += m_vmConfig.KernelCommandLine;\n    }\n\n    // Set up boot params.\n    //\n    // N.B. Linux kernel direct boot is not yet supported on ARM64.\n    if constexpr (!wsl::shared::Arm64)\n    {\n        auto linuxKernelDirect = hcs::LinuxKernelDirect{};\n        linuxKernelDirect.KernelFilePath = m_vmConfig.KernelPath.c_str();\n        linuxKernelDirect.InitRdPath = (m_rootFsPath / LXSS_VM_MODE_INITRD_NAME).c_str();\n        linuxKernelDirect.KernelCmdLine = kernelCmdLine;\n        vmSettings.Chipset.LinuxKernelDirect = std::move(linuxKernelDirect);\n    }\n    else\n    {\n        auto bootThis = hcs::UefiBootEntry{};\n        bootThis.DeviceType = hcs::UefiBootDevice::VmbFs;\n        bootThis.VmbFsRootPath = m_rootFsPath.c_str();\n        bootThis.DevicePath = L\"\\\\\" LXSS_VM_MODE_KERNEL_NAME;\n        bootThis.OptionalData = kernelCmdLine;\n        hcs::Uefi uefiSettings{};\n        uefiSettings.BootThis = std::move(bootThis);\n        vmSettings.Chipset.Uefi = std::move(uefiSettings);\n    }\n\n    // Initialize SCSI devices.\n    hcs::Scsi scsiController{};\n    auto attachDisk = [&](PCWSTR path) {\n        auto lun = ReserveLun();\n        hcs::Attachment disk{};\n        disk.Type = hcs::AttachmentType::VirtualDisk;\n        disk.Path = path;\n        disk.ReadOnly = true;\n        disk.SupportCompressedVolumes = true;\n        disk.AlwaysAllowSparseFiles = true;\n        disk.SupportEncryptedFiles = true;\n        scsiController.Attachments[std::to_string(lun)] = std::move(disk);\n        m_attachedDisks.emplace(AttachedDisk{DiskType::VHD, path, false}, DiskState{lun, {}, {}});\n        return lun;\n    };\n\n    if (m_systemDistroDeviceType == LxMiniInitMountDeviceTypeLun)\n    {\n        m_systemDistroDeviceId = attachDisk(m_vmConfig.SystemDistroPath.c_str());\n    }\n\n    if (!m_vmConfig.KernelModulesPath.empty())\n    {\n        m_kernelModulesDeviceId = attachDisk(m_vmConfig.KernelModulesPath.c_str());\n    }\n\n    vmSettings.Devices.Scsi[\"0\"] = std::move(scsiController);\n\n    // Construct a security descriptor that allows system and the current user.\n    wil::unique_hlocal_string userSidString;\n    THROW_LAST_ERROR_IF(!ConvertSidToStringSidW(&m_userSid.Sid, &userSidString));\n\n    std::wstring securityDescriptor{L\"D:P(A;;FA;;;SY)(A;;FA;;;\"};\n    securityDescriptor += userSidString.get();\n    securityDescriptor += L\")\";\n    hcs::HvSocket hvSocketConfig{};\n    hvSocketConfig.HvSocketConfig.DefaultBindSecurityDescriptor = securityDescriptor;\n    hvSocketConfig.HvSocketConfig.DefaultConnectSecurityDescriptor = securityDescriptor;\n    vmSettings.Devices.HvSocket = std::move(hvSocketConfig);\n\n    // N.B. Plan9 device is always added during serialization\n\n    systemSettings.VirtualMachine = std::move(vmSettings);\n    return wsl::shared::ToJsonW(systemSettings);\n}\n\nstd::pair<int, LX_MINI_MOUNT_STEP> WslCoreVm::GetMountResult(_In_ wsl::shared::SocketChannel& Channel)\n{\n    // Read the response from mini_init.\n    const auto& Message = Channel.ReceiveMessage<LX_MINI_INIT_MOUNT_RESULT_MESSAGE>();\n    return std::make_pair(Message.Result, Message.FailureStep);\n}\n\nconst wsl::core::Config& WslCoreVm::GetConfig() const noexcept\n{\n    return m_vmConfig;\n}\n\nGUID WslCoreVm::GetRuntimeId() const\n{\n    return m_runtimeId;\n}\n\nint WslCoreVm::GetVmIdleTimeout() const\n{\n    return m_vmConfig.VmIdleTimeout;\n}\n\nvoid WslCoreVm::GrantVmWorkerProcessAccessToDisk(_In_ PCWSTR Disk, _In_opt_ HANDLE UserToken) const\n{\n    if (ARGUMENT_PRESENT(UserToken))\n    {\n        // Impersonating the user doesn't let us access a block device,\n        // check for an elevated token instead.\n        THROW_HR_IF(WSL_E_ELEVATION_NEEDED_TO_MOUNT_DISK, ((!wsl::windows::common::security::IsTokenElevated(UserToken))));\n    }\n\n    wsl::windows::common::hcs::GrantVmAccess(m_machineId.c_str(), Disk);\n}\n\nvoid WslCoreVm::InitializeGuest()\n{\n    // If GUI apps are enabled, mount the shared memory device and write a registry key to suppress mstsc.exe security warnings.\n    if (LXSS_ENABLE_GUI_APPS())\n    {\n        if (m_vmConfig.EnableVirtio)\n        {\n            try\n            {\n                // Use the appropriate virtiofs class ID based on m_userToken elevation.\n                const bool admin = wsl::windows::common::security::IsTokenElevated(m_userToken.get());\n                const GUID classId = admin ? VIRTIO_FS_ADMIN_CLASS_ID : VIRTIO_FS_CLASS_ID;\n                m_guestDeviceManager->AddSharedMemoryDevice(classId, L\"wslg\", L\"wslg\", WSLG_SHARED_MEMORY_SIZE_MB, m_userToken.get());\n                m_sharedMemoryRoot = std::format(L\"WSL\\\\{}\\\\wslg\", m_machineId);\n            }\n            CATCH_LOG()\n        }\n\n        try\n        {\n            auto runAsUser = wil::impersonate_token(m_userToken.get());\n            const auto userKey = wsl::windows::common::registry::OpenCurrentUser();\n            const auto devicesKey = wsl::windows::common::registry::CreateKey(userKey.get(), c_localDevicesKey, KEY_SET_VALUE);\n            constexpr DWORD flags = 0xC4; // Allow clipboard, microphone, and printer access.\n            wsl::windows::common::registry::WriteDword(devicesKey.get(), nullptr, m_machineId.c_str(), flags);\n            m_localDevicesKeyCreated = true;\n        }\n        CATCH_LOG()\n    }\n\n    // Calculate the size of the configuration message.\n    wsl::shared::MessageWriter<LX_MINI_INIT_CONFIG_MESSAGE> message(LxMiniInitMessageInitialConfig);\n    message->EntropySize = c_bootEntropy;\n    message->EnableGuiApps = LXSS_ENABLE_GUI_APPS();\n    message->MountGpuShares = m_vmConfig.EnableGpuSupport;\n    message->EnableInboxGpuLibs = m_enableInboxGpuLibs;\n    if (m_networkingEngine)\n    {\n        m_networkingEngine->FillInitialConfiguration(message->NetworkingConfiguration);\n    }\n\n    WI_ASSERT(message->NetworkingConfiguration.NetworkingMode == static_cast<LX_MINI_INIT_NETWORKING_MODE>(m_vmConfig.NetworkingMode));\n\n    // Generate additional entropy to be injected.\n    if (message->EntropySize > 0)\n    {\n        THROW_IF_NTSTATUS_FAILED(BCryptGenRandom(\n            nullptr, (PUCHAR)message.InsertBuffer(message->EntropyOffset, message->EntropySize).data(), message->EntropySize, BCRYPT_USE_SYSTEM_PREFERRED_RNG));\n    }\n\n    // Send the message.\n    m_miniInitChannel.SendMessage<LX_MINI_INIT_CONFIG_MESSAGE>(message.Span());\n\n    // If port tracker or localhost relay are enabled, establish a connection with the guest and start processing messages.\n    switch (message->NetworkingConfiguration.PortTrackerType)\n    {\n    case LxMiniInitPortTrackerTypeMirrored:\n    {\n        auto socket = AcceptConnection(m_vmConfig.KernelBootTimeout);\n        m_networkingEngine->StartPortTracker(std::move(socket));\n        break;\n    }\n    case LxMiniInitPortTrackerTypeRelay:\n    {\n        // If localhost relay is enabled, create a relay process.\n        //\n        // N.B. The relay process is launched at medium integrity level, and its lifetime is tied to the lifetime of the utility VM.\n        const auto result = wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&]() {\n            const auto socket = AcceptConnection(m_vmConfig.KernelBootTimeout);\n            wsl::windows::common::helpers::LaunchPortRelay(socket.get(), m_runtimeId, m_restrictedToken.get(), !m_vmConfig.EnableTelemetry);\n        });\n\n        if (FAILED(result))\n        {\n            const auto errorString = wsl::windows::common::wslutil::GetSystemErrorString(result);\n            EMIT_USER_WARNING(wsl::shared::Localization::MessageLocalhostRelayFailed(errorString));\n        }\n    }\n\n    default:\n        break;\n    }\n}\n\n// Returns true if the admin drvfs share should be used,\n// false if the non-elevated share should be used\nbool WslCoreVm::InitializeDrvFs(_In_ HANDLE UserToken)\n{\n    auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();\n    WI_ASSERT(m_vmConfig.EnableHostFileSystemAccess);\n    if (m_drvfsInitialResult.valid())\n    {\n        // The drvfs drives might have been initialized with a different token.\n        // Make sure the elevation status matches before returning the cached value.\n        const auto elevated = wsl::windows::common::security::IsTokenElevated(UserToken);\n        if (m_drvfsInitialResult.get() == elevated)\n        {\n            return elevated;\n        }\n    }\n\n    return InitializeDrvFsLockHeld(UserToken);\n}\n\n// Returns true if the admin drvfs share should be used,\n// false if the non-elevated share should be used\n_Requires_lock_held_(m_guestDeviceLock)\nbool WslCoreVm::InitializeDrvFsLockHeld(_In_ HANDLE UserToken)\n{\n    // Before checking whether DrvFs is already initialized, make sure any existing Plan 9 servers\n    // are usable.\n    VerifyPlan9Servers();\n\n    const auto elevated = wsl::windows::common::security::IsTokenElevated(UserToken);\n    if (elevated)\n    {\n        if (!m_adminDrvfsToken)\n        {\n            AddDrvFsShare(true, UserToken);\n            THROW_IF_WIN32_BOOL_FALSE(\n                ::DuplicateTokenEx(UserToken, MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenImpersonation, &m_adminDrvfsToken));\n        }\n    }\n    else\n    {\n        if (!m_drvfsToken)\n        {\n            AddDrvFsShare(false, UserToken);\n            THROW_IF_WIN32_BOOL_FALSE(\n                ::DuplicateTokenEx(UserToken, MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenImpersonation, &m_drvfsToken));\n        }\n    }\n\n    return elevated;\n}\n\nbool WslCoreVm::IsDnsTunnelingSupported() const\n{\n    WI_ASSERT(\n        m_vmConfig.NetworkingMode == NetworkingMode::Nat || m_vmConfig.NetworkingMode == NetworkingMode::Mirrored ||\n        m_vmConfig.NetworkingMode == NetworkingMode::VirtioProxy);\n\n    return SUCCEEDED_LOG(wsl::core::networking::DnsResolver::LoadDnsResolverMethods());\n}\n\nbool WslCoreVm::IsVhdAttached(_In_ PCWSTR VhdPath)\n{\n    auto lock = m_lock.lock_exclusive();\n    return m_attachedDisks.contains({DiskType::VHD, VhdPath});\n}\n\nWslCoreVm::DiskMountResult WslCoreVm::MountDisk(\n    _In_ PCWSTR Disk, _In_ DiskType MountDiskType, _In_ ULONG PartitionIndex, _In_opt_ PCWSTR Name, _In_opt_ PCWSTR Type, _In_opt_ PCWSTR Options)\n{\n    auto lock = m_lock.lock_exclusive();\n    return MountDiskLockHeld(Disk, MountDiskType, PartitionIndex, Name, Type, Options);\n}\n\nWslCoreVm::DiskMountResult WslCoreVm::MountDiskLockHeld(\n    _In_ PCWSTR Disk, _In_ DiskType MountDiskType, _In_ ULONG PartitionIndex, _In_opt_ PCWSTR Name, _In_opt_ PCWSTR Type, _In_opt_ PCWSTR Options)\n{\n    const auto it = m_attachedDisks.find({MountDiskType, Disk});\n    THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), (it == m_attachedDisks.end()));\n    THROW_HR_IF(WSL_E_DISK_ALREADY_MOUNTED, it->second.Mounts.find(PartitionIndex) != it->second.Mounts.end());\n\n    // Get the name for the mountpoint\n    auto targetName = s_GetMountTargetName(Disk, Name, PartitionIndex);\n    auto targetNameWide = wsl::shared::string::MultiByteToWide(targetName);\n    // For each attachedDisk pair\n    const auto nameCollision = std::any_of(m_attachedDisks.begin(), m_attachedDisks.end(), [&](const auto& diskEntry) {\n        // Check if the targetName matches the name of any Mount already present\n        return (std::any_of(diskEntry.second.Mounts.begin(), diskEntry.second.Mounts.end(), [&](const auto& mountEntry) {\n            return wsl::shared::string::IsEqual(mountEntry.second.Name, targetNameWide, false);\n        }));\n    });\n\n    // Throw error if the specified name was already used\n    THROW_HR_IF(WSL_E_VM_MODE_MOUNT_NAME_ALREADY_EXISTS, nameCollision);\n\n    wsl::shared::MessageWriter<LX_MINI_INIT_MOUNT_MESSAGE> message(LxMiniInitMessageMount);\n    message->PartitionIndex = PartitionIndex;\n    message->ScsiLun = it->second.Lun;\n    message.WriteString(message->TypeOffset, Type);\n    message.WriteString(message->TargetNameOffset, targetName);\n    message.WriteString(message->OptionsOffset, Options);\n\n    // Send the message.\n    m_miniInitChannel.SendMessage<LX_MINI_INIT_MOUNT_MESSAGE>(message.Span());\n\n    // Accept a connection from mini_init\n    wsl::shared::SocketChannel channel{AcceptConnection(m_vmConfig.KernelBootTimeout), \"MountResult\", m_terminatingEvent.get()};\n\n    // Get the mount result from mini_init\n    auto [mountResult, step] = GetMountResult(channel);\n    if (mountResult == 0)\n    {\n        Mount mount;\n\n        // Always set the Name attribute; use generated one as default\n        mount.Name = std::move(targetNameWide);\n\n        if (Type != nullptr)\n        {\n            mount.Type = Type;\n        }\n\n        if (Options != nullptr)\n        {\n            mount.Options = Options;\n        }\n\n        it->second.Mounts.emplace(PartitionIndex, std::move(mount));\n    }\n\n    return {std::move(targetName), mountResult, step};\n}\n\nwil::unique_socket WslCoreVm::CreateRootNamespaceProcess(_In_ LPCSTR Path, _In_ LPCSTR* Arguments)\n{\n    auto lock = m_lock.lock_exclusive();\n\n    return LxssCreateProcess::CreateLinuxProcess(\n        Path, Arguments, m_runtimeId, m_miniInitChannel, m_terminatingEvent.get(), m_vmConfig.DistributionStartTimeout);\n}\n\nvoid WslCoreVm::MountRootNamespaceFolder(_In_ LPCWSTR HostPath, _In_ LPCWSTR GuestPath, _In_ bool ReadOnly, _In_ LPCWSTR Name)\n{\n    auto lock = m_lock.lock_exclusive();\n\n    const auto flags = (ReadOnly ? hcs::Plan9ShareFlags::ReadOnly : hcs::Plan9ShareFlags::None) | hcs::Plan9ShareFlags::AllowOptions;\n    wsl::windows::common::hcs::AddPlan9Share(m_system.get(), Name, Name, HostPath, LX_INIT_UTILITY_VM_PLAN9_PORT, flags);\n\n    wsl::shared::MessageWriter<LX_MINI_INIT_MOUNT_FOLDER_MESSAGE> message(LxMiniInitMountFolder);\n    message.WriteString(message->PathIndex, GuestPath);\n    message.WriteString(message->NameIndex, Name);\n    message->ReadOnly = ReadOnly;\n\n    const auto& ResultMessage = m_miniInitChannel.Transaction<LX_MINI_INIT_MOUNT_FOLDER_MESSAGE>(message.Span());\n\n    THROW_HR_IF_MSG(\n        E_FAIL,\n        ResultMessage.Result != 0,\n        \"Failed to mount folder. HostPath=%ls, GuestPath=%ls, Name=%ls, ReadOnly=%d, Result=%d\",\n        HostPath,\n        GuestPath,\n        Name,\n        ReadOnly,\n        ResultMessage.Result);\n}\n\nULONG\nWslCoreVm::MountFileAsPersistentMemory(_In_ PCWSTR FilePath, _In_ bool ReadOnly)\n{\n    hcs::Plan9ShareFlags flags{};\n\n    WI_SetFlagIf(flags, hcs::Plan9ShareFlags::ReadOnly, ReadOnly);\n\n    // Serialize calls to mount pmem devices to the VM. Some quick background on why we do this.\n    // The problem stems from the fact that our caller needs to know the dev path where the pmem\n    // device will be mounted (i.e. /dev/pmem0). We could dynamically discover the device path and\n    // return that to our caller. However, some callers statically declare the dev paths in their\n    // fstabs. Therefore, we must wait for each device to finish initializing before allowing the\n    // next to proceed, so that they appear in the expected predefined order.\n    //\n    // Ideally callers wouldn't rely on the dev path, and would setup their fstabs using names. If\n    // callers are ever updated, we could update this code to allow pmem devices to be added in\n    // parallel and dynamically discover their dev path (which we would then use if the caller\n    // asked us to mount the pmem device, instead of them doing it in their fstabs). To dynamically\n    // discover the dev path, we'd have to poll /sys/class/block. Eventually a path such as\n    // /sys/class/block/pmemX will appear. Once it appears, /sys/class/block/pmemX/device will be\n    // a symlink that points to a path like:\n    // /sys/devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0004:00/VMBUS:00/<GUID>/pcicceb:00//cceb:00:00.0/virtio1/ndbus0/region0/namespace0.0/block/pmem0\n    // Notice the GUID in the middle of that path. That GUID is the instance ID, which is randomly\n    // generated by AddGuestDevice. So once we find a path with the instance ID, we know that\n    // eventually /dev/pmemX will appear in the guest.\n    auto persistentMemoryLock = m_persistentMemoryLock.lock_exclusive();\n\n    // Add the pmem device to the VM.\n    // N.B. If this succeeds, technically we'd need to remove the device if we later encounter any\n    //      failures. Otherwise, we'd potentially leave the VM in a torn state. However, HCS\n    //      doesn't currently support this. For now, we rely on the fact that all pmem devices are\n    //      added as part of VM creation and therefore any failure will result in VM termination\n    //      (in which case there's no need to remove the device).\n    {\n        (void)m_guestDeviceManager->AddGuestDevice(\n            VIRTIO_PMEM_DEVICE_ID, VIRTIO_PMEM_CLASS_ID, L\"\", nullptr, FilePath, static_cast<UINT32>(flags), m_userToken.get());\n    }\n\n    // Wait for the pmem device to appear in the VM at /dev/pmemX. Guess the value of X given the\n    // number of pmem devices that have been exposed to the VM. See above for more details why.\n    // N.B. If hot remove of pmem devices is ever added, this logic will need to be updated.\n    //      Similarly, if nvdimm devices are ever passed through to the VM, this logic will need\n    //      to be updated.\n    const ULONG persistentMemoryId = m_nextPersistentMemoryId;\n    WaitForPmemDeviceInVm(persistentMemoryId);\n\n    // The pmem device was successfully found in the VM. Increment the next expected pmem device ID.\n    m_nextPersistentMemoryId += 1;\n\n    return persistentMemoryId;\n}\n\nvoid WslCoreVm::WaitForPmemDeviceInVm(_In_ ULONG PmemId)\n{\n    // Construct the mini_init message.\n    LX_MINI_INIT_WAIT_FOR_PMEM_DEVICE_MESSAGE message;\n    message.Header.MessageType = LxMiniInitMessageWaitForPmemDevice;\n    message.Header.MessageSize = sizeof(message);\n    message.PmemId = PmemId;\n\n    // Send the message to mini_init.\n    wsl::shared::SocketChannel channel;\n    {\n        auto lock = m_lock.lock_exclusive();\n\n        m_miniInitChannel.SendMessage(message);\n        channel = {\n            AcceptConnection(m_vmConfig.KernelBootTimeout),\n            \"WaitForPmem\",\n            m_terminatingEvent.get(),\n        };\n    }\n\n    // Wait for mini_init to respond.\n\n    const auto& resultMessage = channel.ReceiveMessage<LX_MINI_INIT_WAIT_FOR_PMEM_DEVICE_MESSAGE::TResponse>();\n\n    // Check if the device was found in the VM.\n    if (resultMessage.Result != 0)\n    {\n        THROW_WIN32_MSG(ERROR_NOT_FOUND, \"Failed to find /dev/pmem%u with result %d\", PmemId, resultMessage.Result);\n    }\n}\n\n_Requires_lock_held_(m_guestDeviceLock)\nstd::pair<std::wstring, std::wstring> WslCoreVm::AddVirtioFsShare(_In_ bool Admin, _In_ PCWSTR Path, _In_ PCWSTR Options, _In_opt_ HANDLE UserToken)\n{\n    WI_ASSERT(m_vmConfig.EnableVirtioFs);\n\n    if (!ARGUMENT_PRESENT(UserToken))\n    {\n        UserToken = Admin ? m_adminDrvfsToken.get() : m_drvfsToken.get();\n        THROW_HR_IF_MSG(E_UNEXPECTED, !UserToken, \"UserToken not set for supplied context (Admin = %d)\", Admin);\n    }\n\n    WI_ASSERT(Admin == wsl::windows::common::security::IsTokenElevated(UserToken));\n\n    // Ensure that the path has a trailing path separator.\n    std::wstring sharePath(Path);\n    if (!sharePath.ends_with(L'\\\\') && !sharePath.ends_with(L'/'))\n    {\n        sharePath.push_back(L'\\\\');\n    }\n\n    sharePath = std::filesystem::weakly_canonical(sharePath).wstring();\n\n    // Check if a matching share already exists.\n    bool created = false;\n    std::wstring tag;\n    VirtioFsShare key(sharePath.c_str(), Options, Admin);\n    if (!m_virtioFsShares.contains(key))\n    {\n        // Generate a new unique tag for the share.\n        //\n        // N.B. The tag can be maximum 36 characters long so a GUID without braces fits perfectly.\n        GUID tagGuid{};\n        THROW_IF_FAILED(CoCreateGuid(&tagGuid));\n\n        tag = wsl::shared::string::GuidToString<wchar_t>(tagGuid, wsl::shared::string::None);\n        WI_ASSERT(!FindVirtioFsShare(tag.c_str(), Admin));\n\n        (void)m_guestDeviceManager->AddGuestDevice(\n            VIRTIO_FS_DEVICE_ID,\n            Admin ? VIRTIO_FS_ADMIN_CLASS_ID : VIRTIO_FS_CLASS_ID,\n            tag.c_str(),\n            key.OptionsString().c_str(),\n            sharePath.c_str(),\n            VIRTIO_FS_FLAGS_TYPE_FILES,\n            UserToken);\n\n        m_virtioFsShares.emplace(std::move(key), tag);\n        created = true;\n    }\n    else\n    {\n        tag = m_virtioFsShares[key];\n    }\n\n    WSL_LOG(\n        \"WslCoreVmAddVirtioFsShare\",\n        TraceLoggingValue(Admin, \"admin\"),\n        TraceLoggingValue(sharePath.c_str(), \"path\"),\n        TraceLoggingValue(Options, \"options\"),\n        TraceLoggingValue(tag.c_str(), \"tag\"),\n        TraceLoggingValue(created, \"created\"),\n        TraceLoggingValue(m_virtioFsShares.size(), \"shareCount\"));\n\n    return {tag, sharePath};\n}\n\nvoid WslCoreVm::OnCrash(_In_ LPCWSTR Details)\n{\n    if (m_vmCrashEvent.is_signaled())\n    {\n        return; // Crash information has already been collected\n    }\n\n    WSL_LOG(\"GuestCrash\", TraceLoggingValue(Details, \"Data\"));\n    const auto crashInformation = wsl::shared::FromJson<wsl::windows::common::hcs::CrashReport>(Details);\n\n    if (m_vmConfig.MaxCrashDumpCount >= 0)\n    {\n        constexpr auto c_extension = L\".txt\";\n        constexpr auto c_prefix = L\"kernel-panic-\";\n        const auto filename = std::format(L\"{}{}-{}{}\", c_prefix, std::time(nullptr), m_runtimeId, c_extension);\n        auto tracePath = m_vmConfig.CrashDumpFolder / filename;\n\n        auto runAsUser = wil::impersonate_token(m_userToken.get());\n\n        std::error_code error;\n        std::filesystem::create_directories(m_vmConfig.CrashDumpFolder, error);\n        if (error.value())\n        {\n            THROW_WIN32_MSG(error.value(), \"Failed to create folder: %ls\", m_vmConfig.CrashDumpFolder.c_str());\n        }\n\n        auto pred = [&c_extension, &c_prefix](const auto& e) {\n            return WI_IsFlagSet(GetFileAttributes(e.path().c_str()), FILE_ATTRIBUTE_TEMPORARY) && e.path().has_extension() &&\n                   e.path().extension() == c_extension && e.path().has_filename() && e.path().filename().wstring().find(c_prefix) == 0;\n        };\n\n        wsl::windows::common::wslutil::EnforceFileLimit(m_vmConfig.CrashDumpFolder.c_str(), m_vmConfig.MaxCrashDumpCount, pred);\n\n        {\n            std::wofstream outputFile(tracePath.wstring());\n            THROW_HR_IF(E_UNEXPECTED, !outputFile || !(outputFile << crashInformation.CrashLog));\n        }\n\n        m_vmCrashLogFile = std::move(tracePath);\n    }\n\n    m_vmCrashEvent.SetEvent();\n}\n\nvoid WslCoreVm::OnExit(_In_opt_ PCWSTR ExitDetails)\n{\n    // Indicate that the VM has exited, and wake any waiting threads. The instance may be in its destructor at this\n    // point but closing m_system will wait for any outstanding callbacks, so this function will complete before the\n    // destructor continues.\n    std::function<void(GUID)> terminationCallback{};\n    {\n        auto exitLock = m_exitCallbackLock.lock_exclusive();\n        if (ARGUMENT_PRESENT(ExitDetails))\n        {\n            m_exitDetails = ExitDetails;\n        }\n\n        m_vmExitEvent.SetEvent();\n\n        // If we reach this block and 'm_terminatingEvent' is not signaled, then this is abnormal shutdown.\n        // If that happens, set m_terminatingEvent so all pending socket operations can be properly cancelled.\n        if (!m_terminatingEvent.is_signaled())\n        {\n            WSL_LOG(\"AbnormalVmExit\", TraceLoggingValue(ExitDetails, \"Details\"));\n            m_terminatingEvent.SetEvent();\n        }\n\n        terminationCallback = std::move(m_onExit);\n    }\n\n    if (terminationCallback)\n    {\n        terminationCallback(m_runtimeId);\n    }\n}\n\nvoid WslCoreVm::ReadGuestCapabilities()\n{\n    const auto& info = m_miniInitChannel.ReceiveMessage<LX_INIT_GUEST_CAPABILITIES>();\n\n    m_kernelVersionString = wsl::shared::string::MultiByteToWide(info.Buffer);\n\n    // Parse the version string.\n    const std::regex pattern(\"(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+).*\");\n    std::smatch match;\n    const std::string input = info.Buffer;\n    if (!std::regex_match(input, match, pattern) || match.size() != 4)\n    {\n        THROW_HR_MSG(E_UNEXPECTED, \"Failed to parse kernel version: '%hs'\", input.c_str());\n    }\n\n    auto get = [&](int position) { return std::stoul(match.str(position)); };\n\n    try\n    {\n        m_kernelVersion = std::make_tuple(get(1), get(2), get(3));\n    }\n    catch (const std::exception& e)\n    {\n        THROW_HR_MSG(E_UNEXPECTED, \"Failed to parse kernel version: '%hs', %hs\", info.Buffer, e.what());\n    }\n\n    m_seccompAvailable = info.SeccompAvailable;\n    WSL_LOG(\n        \"GuestKernelInfo\",\n        TraceLoggingValue(m_seccompAvailable, \"SeccompAvailable\"),\n        TraceLoggingValue(std::get<0>(m_kernelVersion), \"Version\"),\n        TraceLoggingValue(std::get<1>(m_kernelVersion), \"Revision\"),\n        TraceLoggingValue(std::get<2>(m_kernelVersion), \"Minor\"));\n}\n\nULONG WslCoreVm::ReserveLun(_In_ std::optional<ULONG> Lun)\n{\n    if (Lun.has_value() && !m_lunBitmap[Lun.value()])\n    {\n        m_lunBitmap[Lun.value()] = true;\n        return Lun.value();\n    }\n\n    for (ULONG index = 0; index < gsl::narrow_cast<ULONG>(m_lunBitmap.size()); index += 1)\n    {\n        if (!m_lunBitmap[index])\n        {\n            m_lunBitmap[index] = true;\n            return index;\n        }\n    }\n\n    THROW_HR(WSL_E_TOO_MANY_DISKS_ATTACHED);\n}\n\nvoid WslCoreVm::RestorePassthroughDiskState(_In_ LPCWSTR Disk) const\ntry\n{\n    const auto diskHandle = wsl::windows::common::disk::OpenDevice(Disk, GENERIC_READ | GENERIC_WRITE, m_vmConfig.MountDeviceTimeout);\n    wsl::windows::common::disk::SetOnline(diskHandle.get(), true, m_vmConfig.MountDeviceTimeout);\n    return;\n}\nCATCH_LOG()\n\nvoid WslCoreVm::RegisterCallbacks(_In_ const std::function<void(ULONG)>& DistroExitCallback, _In_ const std::function<void(GUID)>& TerminationCallback)\n{\n    WSL_LOG(\n        \"WslCoreVm::RegisterCallbacks\",\n        TraceLoggingValue(static_cast<bool>(DistroExitCallback), \"DistroExitCallback\"),\n        TraceLoggingValue(static_cast<bool>(TerminationCallback), \"TerminationCallback\"));\n\n    if (DistroExitCallback)\n    {\n        auto lock = m_lock.lock_exclusive();\n        THROW_HR_IF(E_INVALIDARG, !m_notifyChannel);\n        m_distroExitThread = std::thread([exitCallback = std::move(DistroExitCallback),\n                                          notifyChannel = std::move(m_notifyChannel),\n                                          terminationEvent = m_terminatingEvent.get()]() {\n            try\n            {\n                wsl::windows::common::wslutil::SetThreadDescription(L\"DistroExitCallback\");\n\n                std::vector<gsl::byte> buffer;\n                for (;;)\n                {\n                    // Read the message.\n                    auto message = wsl::shared::socket::RecvMessage(notifyChannel.get(), buffer, terminationEvent);\n                    if (message.empty())\n                    {\n                        break;\n                    }\n\n                    const auto* header = gslhelpers::get_struct<MESSAGE_HEADER>(message);\n                    if (header->MessageType == LxMiniInitMessageChildExit)\n                    {\n                        const auto* exitMessage = gslhelpers::try_get_struct<LX_MINI_INIT_CHILD_EXIT_MESSAGE>(message);\n                        if (exitMessage)\n                        {\n                            exitCallback(exitMessage->ChildPid);\n                        }\n                    }\n                    else\n                    {\n                        LOG_HR_MSG(E_UNEXPECTED, \"Unexpected MessageType %d\", header->MessageType);\n                    }\n                }\n            }\n            CATCH_LOG()\n        });\n    }\n\n    if (TerminationCallback)\n    {\n        // Register the callback if the VM has not been terminated.\n        auto exitLock = m_exitCallbackLock.lock_exclusive();\n        THROW_HR_IF(E_INVALIDARG, m_onExit);\n        if (!m_terminatingEvent.is_signaled())\n        {\n            m_onExit = std::move(TerminationCallback);\n        }\n        else\n        {\n            // The VM has already been terminated, invoke the callback on a separate thread.\n            std::thread([terminationCallback = std::move(TerminationCallback), runtimeId = m_runtimeId]() {\n                wsl::windows::common::wslutil::SetThreadDescription(L\"TerminationCallback\");\n                terminationCallback(runtimeId);\n            }).detach();\n        }\n    }\n\n    if (m_vmConfig.EnableHostFileSystemAccess && m_vmConfig.EnableVirtioFs)\n    {\n        // Create a thread listening for handling virtiofs requests.\n        auto listenSocket = wsl::windows::common::hvsocket::Listen(m_runtimeId, LX_INIT_UTILITY_VM_VIRTIOFS_PORT);\n        m_virtioFsThread = std::thread(&WslCoreVm::VirtioFsWorker, this, std::move(listenSocket));\n    }\n}\n\nvoid WslCoreVm::ResizeDistribution(_In_ ULONG Lun, _In_ HANDLE OutputHandle, _In_ ULONG64 NewSize)\n{\n    auto lock = m_lock.lock_exclusive();\n\n    LX_MINI_INIT_RESIZE_DISTRIBUTION_MESSAGE message;\n    message.Header.MessageSize = sizeof(message);\n    message.Header.MessageType = LxMiniInitMessageResizeDistribution;\n    message.ScsiLun = Lun;\n    message.NewSize = NewSize;\n\n    m_miniInitChannel.SendMessage(message);\n\n    wsl::shared::SocketChannel channel{AcceptConnection(m_vmConfig.KernelBootTimeout), \"ResizeDistribution\", m_terminatingEvent.get()};\n    auto outputChannel = AcceptConnection(m_vmConfig.KernelBootTimeout);\n\n    wsl::windows::common::relay::ScopedRelay outputRelay(std::move(outputChannel), OutputHandle);\n\n    const auto& resultMessage = channel.ReceiveMessage<LX_MINI_INIT_RESIZE_DISTRIBUTION_RESPONSE>();\n    if (resultMessage.ResponseCode != 0)\n    {\n        THROW_HR_WITH_USER_ERROR(E_FAIL, wsl::shared::Localization::MessageFailedToResizeDisk());\n    }\n}\n\nvoid WslCoreVm::SaveAttachedDisksState()\ntry\n{\n    auto lock = m_lock.lock_exclusive();\n    const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(&m_userSid.Sid);\n    for (const auto& e : m_attachedDisks)\n    {\n        if (e.first.User)\n        {\n            SaveDiskState(key.get(), e.first, e.second, e.first.Type);\n        }\n    }\n\n    return;\n}\nCATCH_LOG()\n\nvoid WslCoreVm::SaveDiskState(_In_ HKEY Key, _In_ const AttachedDisk& Disk, _In_ const DiskState& State, _In_ const DiskType& SaveDiskType)\n{\n    const auto keyPath = std::to_wstring(State.Lun);\n    const auto diskKey = wsl::windows::common::registry::CreateKey(Key, keyPath.c_str(), KEY_ALL_ACCESS, nullptr, REG_OPTION_VOLATILE);\n\n    wsl::windows::common::registry::WriteString(diskKey.get(), nullptr, c_diskValueName, Disk.Path.c_str());\n\n    wsl::windows::common::registry::WriteDword(diskKey.get(), nullptr, c_disktypeValueName, static_cast<DWORD>(SaveDiskType));\n\n    for (const auto& e : State.Mounts)\n    {\n        auto partition = std::to_wstring(e.first);\n        auto mountKey = wsl::windows::common::registry::CreateKey(diskKey.get(), partition.c_str(), KEY_ALL_ACCESS, nullptr, REG_OPTION_VOLATILE);\n\n        wsl::windows::common::registry::WriteString(mountKey.get(), nullptr, c_mountNameValueName, e.second.Name.c_str());\n\n        if (e.second.Options.has_value())\n        {\n            wsl::windows::common::registry::WriteString(mountKey.get(), nullptr, c_optionsValueName, e.second.Options.value().c_str());\n        }\n\n        if (e.second.Type.has_value())\n        {\n            wsl::windows::common::registry::WriteString(mountKey.get(), nullptr, c_typeValueName, e.second.Type.value().c_str());\n        }\n    }\n}\n\nstd::pair<int, LX_MINI_MOUNT_STEP> WslCoreVm::UnmountDisk(_In_ const AttachedDisk& Disk, _Inout_ DiskState& State)\n{\n    // Iterate through the mountpoints to unmount and delete them\n    for (auto it = State.Mounts.begin(); it != State.Mounts.end(); it = State.Mounts.erase(it))\n    {\n        const auto result = UnmountVolume(Disk, it->first, it->second.Name.c_str());\n        if (result.first != 0)\n        {\n            return result;\n        }\n    }\n\n    // Tell the guest to flush its IO caches and stop using the disk.\n    LX_MINI_INIT_DETACH_MESSAGE message;\n    message.Header.MessageType = LxMiniInitMessageDetach;\n    message.Header.MessageSize = sizeof(message);\n    message.ScsiLun = State.Lun;\n\n    m_miniInitChannel.SendMessage(message);\n\n    // Accept a connection from mini_init.\n    wsl::shared::SocketChannel channel{AcceptConnection(m_vmConfig.KernelBootTimeout), \"MountResult\", m_terminatingEvent.get()};\n\n    // Get the unmount result from mini_init\n    return GetMountResult(channel);\n}\n\nstd::pair<int, LX_MINI_MOUNT_STEP> WslCoreVm::UnmountVolume(_In_ const AttachedDisk& Disk, _In_ ULONG PartitionIndex, _In_ PCWSTR Name)\n{\n    wsl::shared::MessageWriter<LX_MINI_INIT_UNMOUNT_MESSAGE> message(LxMiniInitMessageUnmount);\n    message.WriteString(Name);\n\n    // Send the message.\n    m_miniInitChannel.SendMessage<LX_MINI_INIT_UNMOUNT_MESSAGE>(message.Span());\n\n    // Accept a connection from mini_init.\n    wsl::shared::SocketChannel channel{AcceptConnection(m_vmConfig.KernelBootTimeout), \"MountResult\", m_terminatingEvent.get()};\n\n    // Get the unmount result from mini_init.\n    return GetMountResult(channel);\n}\n\n_Requires_lock_held_(m_guestDeviceLock)\nvoid WslCoreVm::VerifyPlan9Servers()\n{\n    for (auto it = m_plan9Servers.begin(); it != m_plan9Servers.end();)\n    {\n        const HRESULT result = it->second->IsRunning();\n\n        // If the server process was terminated (which can happen e.g. if the user logged out and\n        // back in), attempting to make a COM call will return\n        // HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE). For this and other errors, remove the\n        // server from the list and mark DrvFs for that port uninitialized.\n        // N.B. The call will return S_FALSE if the server is not running. That should never\n        //      happen since this service never calls Pause(), but in case it does that is also\n        //      treated as an error.\n        if (result != S_OK)\n        {\n            if (it->first == LX_INIT_UTILITY_VM_PLAN9_DRVFS_ADMIN_PORT)\n            {\n                m_adminDrvfsToken.reset();\n            }\n            else\n            {\n                WI_ASSERT(it->first == LX_INIT_UTILITY_VM_PLAN9_DRVFS_PORT);\n\n                m_drvfsToken.reset();\n            }\n\n            it = m_plan9Servers.erase(it);\n        }\n        else\n        {\n            ++it;\n        }\n    }\n}\n\nvoid WslCoreVm::VirtioFsWorker(_In_ const wil::unique_socket& listenSocket)\ntry\n{\n    wsl::windows::common::wslutil::SetThreadDescription(L\"VirtioFs - Worker\");\n\n    for (;;)\n    {\n        // Create a worker thread to handle each request.\n\n        auto socket = hvsocket::CancellableAccept(listenSocket.get(), INFINITE, m_terminatingEvent.get());\n        if (!socket.has_value())\n        {\n            break;\n        }\n\n        wsl::shared::SocketChannel channel{std::move(socket.value()), \"VirtioFs\", m_terminatingEvent.get()};\n        std::thread([this, channel = std::move(channel)]() mutable {\n            try\n            {\n                wsl::windows::common::wslutil::SetThreadDescription(L\"VirtioFs - Request\");\n\n                auto [message, span] = channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\n                if (message == nullptr)\n                {\n                    return;\n                }\n\n                auto respondWithTag = [&](const std::wstring& tag, const std::wstring& source, HRESULT result) {\n                    // Respond to the guest with the tag that should be used to mount the device.\n\n                    wsl::shared::MessageWriter<LX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE> response(LxInitMessageAddVirtioFsDeviceResponse);\n                    response->Result = SUCCEEDED(result) ? 0 : EINVAL; // TODO: Improved HRESULT -> errno mapping.\n                    response.WriteString(response->TagOffset, tag);\n                    response.WriteString(response->SourceOffset, source);\n\n                    channel.SendMessage<LX_INIT_ADD_VIRTIOFS_SHARE_RESPONSE_MESSAGE>(response.Span());\n                };\n\n                if (message->MessageType == LxInitMessageAddVirtioFsDevice)\n                {\n                    std::wstring tag;\n                    std::wstring source;\n                    const auto result = wil::ResultFromException([this, span, &tag, &source]() {\n                        const auto* addShare = gslhelpers::try_get_struct<LX_INIT_ADD_VIRTIOFS_SHARE_MESSAGE>(span);\n                        THROW_HR_IF(E_UNEXPECTED, !addShare);\n\n                        const auto path = wsl::shared::string::FromSpan(span, addShare->PathOffset);\n                        const auto pathWide = wsl::shared::string::MultiByteToWide(path);\n                        const auto options = wsl::shared::string::FromSpan(span, addShare->OptionsOffset);\n                        const auto optionsWide = wsl::shared::string::MultiByteToWide(options);\n\n                        // Acquire the lock and attempt to add the device.\n                        auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();\n                        std::tie(tag, source) = AddVirtioFsShare(addShare->Admin, pathWide.c_str(), optionsWide.c_str());\n                    });\n\n                    respondWithTag(tag, source, result);\n                }\n                else if (message->MessageType == LxInitMessageRemountVirtioFsDevice)\n                {\n                    std::wstring newTag;\n                    std::wstring source;\n                    const auto result = wil::ResultFromException([this, span, &newTag, &source]() {\n                        const auto* remountShare = gslhelpers::try_get_struct<LX_INIT_REMOUNT_VIRTIOFS_SHARE_MESSAGE>(span);\n                        THROW_HR_IF(E_UNEXPECTED, !remountShare);\n\n                        const std::string tag = wsl::shared::string::FromSpan(span, remountShare->TagOffset);\n                        const auto tagWide = wsl::shared::string::MultiByteToWide(tag);\n                        auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();\n                        const auto foundShare = FindVirtioFsShare(tagWide.c_str(), !remountShare->Admin);\n                        THROW_HR_IF_MSG(E_UNEXPECTED, !foundShare.has_value(), \"Unknown tag %ls\", tagWide.c_str());\n\n                        std::tie(newTag, source) =\n                            AddVirtioFsShare(remountShare->Admin, foundShare->Path.c_str(), foundShare->OptionsString().c_str());\n\n                        WI_ASSERT(source == foundShare->Path);\n                    });\n\n                    respondWithTag(newTag, source, result);\n                }\n                else\n                {\n                    THROW_HR_MSG(E_UNEXPECTED, \"Unexpected MessageType %d\", message->MessageType);\n                }\n            }\n            CATCH_LOG()\n        }).detach();\n    }\n}\nCATCH_LOG()\n\nstd::string WslCoreVm::s_GetMountTargetName(_In_ PCWSTR Disk, _In_opt_ PCWSTR Name, _In_ int PartitionIndex)\n{\n    // Derive the mount target from the disk and partition names.\n    // The format is <Disk>p[partition]\n    // For Example: PhysicalDisk1p2\n    // If user has specified the name, ensure proper formatting and use it instead\n    if (ARGUMENT_PRESENT(Name))\n    {\n        auto mountName = wsl::shared::string::WideToMultiByte(Name);\n        // Throw if the name contains '/' since it is a linux path separator\n        THROW_HR_IF(WSL_E_VM_MODE_INVALID_MOUNT_NAME, mountName.find('/') != std::string::npos);\n        return mountName;\n    }\n\n    std::string target{};\n    auto mountName = wsl::shared::string::WideToMultiByte(Disk);\n    std::copy_if(mountName.begin(), mountName.end(), std::back_inserter(target), &isalnum);\n    if (PartitionIndex != 0)\n    {\n        target += std::format(\"p{}\", PartitionIndex);\n    }\n\n    return target;\n}\n\nLX_INIT_DRVFS_MOUNT WslCoreVm::s_InitializeDrvFs(_Inout_ WslCoreVm* VmContext, _In_ HANDLE UserToken)\n{\n    try\n    {\n        return VmContext->InitializeDrvFs(UserToken) ? LxInitDrvfsMountElevated : LxInitDrvfsMountNonElevated;\n    }\n    catch (...)\n    {\n        LOG_CAUGHT_EXCEPTION();\n\n        return LxInitDrvfsMountNone;\n    }\n}\n\nvoid CALLBACK WslCoreVm::s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context)\ntry\n{\n    const auto utilityVm = static_cast<WslCoreVm*>(Context);\n    if (Event->Type == HcsEventSystemCrashInitiated || Event->Type == HcsEventSystemCrashReport)\n    {\n        utilityVm->OnCrash(Event->EventData);\n    }\n    else if ((Event->Type == HcsEventSystemExited) || (Event->Type == HcsEventServiceDisconnect))\n    {\n        utilityVm->OnExit(Event->EventData);\n    }\n}\nCATCH_LOG();\n\nbool WslCoreVm::AttachedDisk::operator<(const AttachedDisk& other) const\n{\n    if (Type < other.Type)\n    {\n        return true;\n    }\n\n    if (Type == other.Type)\n    {\n        return _wcsicmp(Path.c_str(), other.Path.c_str()) < 0;\n    }\n\n    return false;\n}\n\nbool WslCoreVm::AttachedDisk::operator==(const AttachedDisk& other) const\n{\n    return Type == other.Type && wsl::windows::common::string::IsPathComponentEqual(Path, other.Path);\n}\n\nWslCoreVm::VirtioFsShare::VirtioFsShare(PCWSTR Path, PCWSTR Options, bool Admin) : Path(Path), Admin(Admin)\n{\n    // Parse the options string into a map representing mount options to ensure that shares with functionally\n    // identical options can share a single device.\n    // For example: \"uid=1000;gid=1000\" and \"gid=1000;uid=1000\"\n    auto optionsVector = wsl::shared::string::Split(std::wstring{Options}, L';');\n    for (const auto& option : optionsVector)\n    {\n        std::wstring key;\n        std::wstring value;\n        const auto pos = option.find_first_of(L'=');\n        if (pos == option.npos)\n        {\n            key = option;\n        }\n        else\n        {\n            key = option.substr(0, pos);\n            value = option.substr(pos + 1);\n        }\n\n        if (!key.empty())\n        {\n            this->Options.insert({std::move(key), std::move(value)});\n        }\n    }\n\n    if constexpr (wsl::shared::Debug)\n    {\n        const auto originalSet = std::set<std::wstring>(optionsVector.begin(), optionsVector.end());\n        auto newVector = wsl::shared::string::Split(OptionsString(), L';');\n        const auto newSet = std::set<std::wstring>(newVector.begin(), newVector.end());\n        WI_ASSERT_MSG(originalSet == newSet, \"mount options do not match\");\n    }\n}\n\nstd::wstring WslCoreVm::VirtioFsShare::OptionsString() const\n{\n    std::wstring optionsString;\n    for (const auto& option : Options)\n    {\n        if (!optionsString.empty())\n        {\n            optionsString += L';';\n        }\n\n        optionsString += option.first;\n        if (!option.second.empty())\n        {\n            optionsString += L'=';\n            optionsString += option.second;\n        }\n    }\n\n    return optionsString;\n}\n\nbool WslCoreVm::VirtioFsShare::operator<(const VirtioFsShare& other) const\n{\n    return std::tie(Path, Options, Admin) < std::tie(other.Path, other.Options, other.Admin);\n}\n\nbool WslCoreVm::VirtioFsShare::operator==(const VirtioFsShare& other) const\n{\n    return Path == other.Path && Options == other.Options && Admin == other.Admin;\n}\n\nvoid WslCoreVm::TraceLoggingRundown() const noexcept\ntry\n{\n    WSL_LOG(\n        \"WslCoreVm::Rundown\",\n        TraceLoggingValue(\"Machine Config\"),\n        TraceLoggingValue(m_machineId.c_str(), \"machineId\"),\n        TraceLoggingValue(ToString(m_vmConfig.NetworkingMode), \"networkingMode\"));\n\n    if (m_networkingEngine)\n    {\n        m_networkingEngine->TraceLoggingRundown();\n    }\n}\nCATCH_LOG()\n\nvoid WslCoreVm::ValidateNetworkingMode()\n{\n    using namespace wsl::core;\n    using namespace wsl::windows::common;\n\n    ExecutionContext context(Context::ConfigureNetworking);\n\n    // Cache requested networking features to be logged via telemetry.\n    const auto networkingModeRequested = m_vmConfig.NetworkingMode;\n    auto firewallRequested = m_vmConfig.FirewallConfig.Enabled();\n    auto dnsTunnelingRequested = m_vmConfig.EnableDnsTunneling;\n\n    // If Hyper-V firewall was requested, ensure it is supported by the OS.\n    if (m_vmConfig.FirewallConfig.Enabled())\n    {\n        if (m_vmConfig.NetworkingMode == NetworkingMode::Mirrored || m_vmConfig.NetworkingMode == NetworkingMode::Nat)\n        {\n            if (!wsl::core::MirroredNetworking::IsHyperVFirewallSupported(m_vmConfig))\n            {\n                // Since hyper-V firewall is enabled by default, only show the warning if the user explicitly asked for it.\n                if (m_vmConfig.FirewallConfigPresence == ConfigKeyPresence::Present)\n                {\n                    EMIT_USER_WARNING(Localization::MessageHyperVFirewallNotSupported());\n                }\n\n                m_vmConfig.FirewallConfig.reset();\n            }\n        }\n    }\n\n    // If mirrored networking was requested, ensure it is supported by the OS and guest kernel.\n    if (m_vmConfig.NetworkingMode == NetworkingMode::Mirrored)\n    {\n        if ((m_kernelVersion < std::make_tuple(5u, 10u, 0u)) || !m_seccompAvailable)\n        {\n            m_vmConfig.NetworkingMode = NetworkingMode::Nat;\n            EMIT_USER_WARNING(Localization::MessageMirroredNetworkingNotSupportedReason(\n                Localization::MessageMirroredNetworkingNotSupportedKernel()));\n        }\n        else if (!wsl::core::networking::IsFlowSteeringSupportedByHns() || !m_vmConfig.FirewallConfig.Enabled())\n        {\n            m_vmConfig.NetworkingMode = NetworkingMode::Nat;\n            EMIT_USER_WARNING(Localization::MessageMirroredNetworkingNotSupportedReason(Localization::MessageMirroredNetworkingNotSupportedWindowsVersion(\n                m_windowsVersion.BuildNumber, m_windowsVersion.UpdateBuildRevision)));\n        }\n    }\n\n    // Localhost relay is not supported in mirrored mode. Generate a warning if the user configures localhost relay\n    // together with mirrored mode.\n    // N.B. Mirrored mode already provides a way to communicate between Windows and Linux using localhost.\n    if (m_vmConfig.NetworkingMode == NetworkingMode::Mirrored && m_vmConfig.LocalhostRelayConfigPresence == ConfigKeyPresence::Present)\n    {\n        EMIT_USER_WARNING(Localization::MessageLocalhostForwardingNotSupportedMirroredMode());\n    }\n\n    // If DNS tunneling was requested, ensure it is supported by Windows.\n    if (m_vmConfig.EnableDnsTunneling && !IsDnsTunnelingSupported())\n    {\n        // Since DNS tunneling is enabled by default, only show the warning if the user explicitly asked for it.\n        if (m_vmConfig.DnsTunnelingConfigPresence == ConfigKeyPresence::Present)\n        {\n            EMIT_USER_WARNING(Localization::MessageDnsTunnelingNotSupported());\n        }\n\n        m_vmConfig.EnableDnsTunneling = false;\n    }\n\n    // Gives information about the requested networking settings and whether they were enabled or not\n    WSL_LOG_TELEMETRY(\n        \"WslCoreVmValidateNetworkingMode\",\n        PDT_ProductAndServicePerformance,\n        TraceLoggingValue(m_runtimeId, \"vmId\"),\n        TraceLoggingValue(ToString(networkingModeRequested), \"networkingModeRequested\"),\n        TraceLoggingValue(ToString(m_vmConfig.NetworkingMode), \"networkingMode\"),\n        TraceLoggingValue(m_vmConfig.NetworkingModePresence == ConfigKeyPresence::Present, \"networkingModePresent\"),\n        TraceLoggingValue(firewallRequested, \"firewallRequested\"),\n        TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"firewall\"),\n        TraceLoggingValue(dnsTunnelingRequested, \"dnsTunnelingRequested\"),\n        TraceLoggingValue(m_vmConfig.DnsTunnelingConfigPresence == ConfigKeyPresence::Present, \"dnsTunnelingConfigPresent\"),\n        TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"dnsTunneling\"));\n}\n"
  },
  {
    "path": "src/windows/service/exe/WslCoreVm.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslCoreVm.h\n\nAbstract:\n\n    This file contains WSL Core vm function declarations.\n\n--*/\n\n#pragma once\n\n#include \"hcs.hpp\"\n#include \"GnsChannel.h\"\n#include \"LxssPort.h\"\n#include \"GnsRpcServer.h\"\n#include \"GnsPortTrackerChannel.h\"\n#include \"Dmesg.h\"\n#include \"GuestTelemetryLogger.h\"\n#include \"WslCoreConfig.h\"\n#include \"LxssCreateProcess.h\"\n#include \"WslCoreNetworkEndpointSettings.h\"\n#include \"WslSecurity.h\"\n#include \"WslCoreFilesystem.h\"\n#include \"INetworkingEngine.h\"\n#include \"SocketChannel.h\"\n#include \"DeviceHostProxy.h\"\n#include \"GuestDeviceManager.h\"\n\n#define UTILITY_VM_SHUTDOWN_TIMEOUT (30 * 1000)\n#define UTILITY_VM_TERMINATE_TIMEOUT (30 * 1000)\n\ninline constexpr auto c_diskValueName = L\"Disk\";\ninline constexpr auto c_disktypeValueName = L\"DiskType\";\ninline constexpr auto c_optionsValueName = L\"Options\";\ninline constexpr auto c_typeValueName = L\"Type\";\ninline constexpr auto c_mountNameValueName = L\"Name\";\n\nnamespace wrl = Microsoft::WRL;\n\n/// <summary>\n/// This object tracks a running WSL Core VM.\n/// </summary>\nclass WslCoreVm\n{\n    WslCoreVm(const WslCoreVm&) = delete;\n    void operator=(const WslCoreVm&) = delete;\n\npublic:\n    static std::unique_ptr<WslCoreVm> Create(_In_ const wil::shared_handle& UserToken, _In_ wsl::core::Config&& VmConfig, _In_ const GUID& VmId);\n\n    ~WslCoreVm() noexcept;\n\n    wil::unique_socket AcceptConnection(_In_ DWORD ReceiveTimeout = 0, _In_ const std::source_location& Location = std::source_location::current()) const;\n\n    enum class DiskType\n    {\n        Invalid = 0x0,\n        VHD = 0x1,\n        PassThrough = 0x2\n    };\n\n    ULONG AttachDisk(_In_ PCWSTR Disk, _In_ DiskType Type, _In_ std::optional<ULONG> Lun, _In_ bool IsUserDisk, _In_ HANDLE UserToken);\n\n    std::shared_ptr<LxssRunningInstance> CreateInstance(\n        _In_ const GUID& InstanceId,\n        _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n        _In_ LX_MESSAGE_TYPE MessageType,\n        _In_ DWORD ReceiveTimeout = 0,\n        _In_ ULONG DefaultUid = LX_UID_ROOT,\n        _In_ ULONG64 ClientLifetimeId = 0,\n        _In_ ULONG ExportFlags = 0,\n        _Out_opt_ ULONG* ConnectPort = nullptr);\n\n    wil::unique_socket CreateListeningSocket() const;\n\n    wil::unique_socket CreateRootNamespaceProcess(_In_ LPCSTR Path, _In_ LPCSTR* Arguments);\n\n    std::pair<int, LX_MINI_MOUNT_STEP> DetachDisk(_In_opt_ PCWSTR Disk);\n\n    struct DiskMountResult\n    {\n        std::string MountPointName;\n        int Result;\n        LX_MINI_MOUNT_STEP Step;\n    };\n\n    void EjectVhd(_In_ PCWSTR VhdPath);\n\n    const wsl::core::Config& GetConfig() const noexcept;\n\n    GUID GetRuntimeId() const;\n\n    int GetVmIdleTimeout() const;\n\n    bool InitializeDrvFs(_In_ HANDLE UserToken);\n\n    bool IsVhdAttached(_In_ PCWSTR VhdPath);\n\n    DiskMountResult MountDisk(\n        _In_ PCWSTR Disk, _In_ DiskType MountDiskType, _In_ ULONG PartitionIndex, _In_opt_ PCWSTR Name, _In_opt_ PCWSTR Type, _In_opt_ PCWSTR Options);\n\n    enum MountFlags\n    {\n        None = 0x0,\n        ReadOnly = 0x1\n    };\n\n    ULONG\n    MountFileAsPersistentMemory(_In_ PCWSTR FilePath, _In_ bool ReadOnly);\n\n    void MountRootNamespaceFolder(_In_ LPCWSTR HostPath, _In_ LPCWSTR GuestPath, _In_ bool ReadOnly, _In_ LPCWSTR Name);\n\n    void RegisterCallbacks(_In_ const std::function<void(ULONG)>& DistroExitCallback = {}, _In_ const std::function<void(GUID)>& TerminationCallback = {});\n\n    void ResizeDistribution(_In_ ULONG Lun, _In_ HANDLE OutputHandle, _In_ ULONG64 NewSize);\n\n    _Requires_lock_not_held_(m_lock)\n    void SaveAttachedDisksState();\n\n    _Requires_lock_held_(m_guestDeviceLock)\n    void VerifyPlan9Servers();\n\n    enum DiskStateFlags\n    {\n        Online = 0x1,\n        AccessGranted = 0x2\n    };\n\n    void TraceLoggingRundown() const noexcept;\n\n    void ValidateNetworkingMode();\n\nprivate:\n    struct AttachedDisk\n    {\n        DiskType Type;\n        std::wstring Path;\n        bool User;\n\n        bool operator<(const AttachedDisk& other) const;\n        bool operator==(const AttachedDisk& other) const;\n    };\n\n    struct Mount\n    {\n        std::wstring Name;\n        std::optional<std::wstring> Options;\n        std::optional<std::wstring> Type;\n    };\n\n    struct DiskState\n    {\n        ULONG Lun;\n        std::map<ULONG, Mount> Mounts;\n        DiskStateFlags Flags;\n    };\n\n    struct VirtioFsShare\n    {\n        VirtioFsShare(PCWSTR Path, PCWSTR Options, bool Admin);\n\n        std::wstring Path;\n        // Mount options are stored as a map so mounts that specify mount options in different orders can be shared.\n        std::map<std::wstring, std::wstring> Options;\n        bool Admin;\n\n        std::wstring OptionsString() const;\n        bool operator<(const VirtioFsShare& other) const;\n        bool operator==(const VirtioFsShare& other) const;\n    };\n\n    WslCoreVm(_In_ wsl::core::Config&& VmConfig);\n\n    _Requires_lock_held_(m_guestDeviceLock)\n    void AddDrvFsShare(_In_ bool Admin, _In_ HANDLE UserToken);\n\n    _Requires_lock_held_(m_guestDeviceLock)\n    void AddPlan9Share(_In_ PCWSTR AccessName, _In_ PCWSTR Path, _In_ UINT32 Port, _In_ wsl::windows::common::hcs::Plan9ShareFlags Flags, _In_ HANDLE UserToken, _In_ PCWSTR VirtIoTag);\n\n    _Requires_lock_held_(m_guestDeviceLock)\n    std::pair<std::wstring, std::wstring> AddVirtioFsShare(_In_ bool Admin, _In_ PCWSTR Path, _In_ PCWSTR Options, _In_opt_ HANDLE UserToken = nullptr);\n\n    _Requires_lock_held_(m_lock)\n    ULONG AttachDiskLockHeld(_In_ PCWSTR Disk, _In_ DiskType Type, _In_ MountFlags Flags, _In_ std::optional<ULONG> Lun, _In_ bool IsUserDisk, _In_ HANDLE UserToken);\n\n    void CollectCrashDumps(wil::unique_socket&& socket) const;\n\n    std::shared_ptr<LxssRunningInstance> CreateInstanceInternal(\n        _In_ const GUID& InstanceId,\n        _In_ const LXSS_DISTRO_CONFIGURATION& Configuration,\n        _In_ DWORD ReceiveTimeout = 0,\n        _In_ ULONG DefaultUid = LX_UID_ROOT,\n        _In_ ULONG64 ClientLifetimeId = 0,\n        _In_ bool LaunchSystemDistro = false,\n        _Out_opt_ ULONG* ConnectPort = nullptr);\n\n    _Requires_lock_held_(m_lock)\n    void EjectVhdLockHeld(_In_ PCWSTR VhdPath);\n\n    _Requires_lock_held_(m_guestDeviceLock)\n    std::optional<VirtioFsShare> FindVirtioFsShare(_In_ PCWSTR tag, _In_ std::optional<bool> Admin = {}) const;\n\n    _Requires_lock_held_(m_lock)\n    void FreeLun(_In_ ULONG Lun);\n\n    std::wstring GenerateConfigJson();\n\n    static std::pair<int, LX_MINI_MOUNT_STEP> GetMountResult(_In_ wsl::shared::SocketChannel& Channel);\n\n    void GrantVmWorkerProcessAccessToDisk(_In_ PCWSTR Disk, _In_opt_ HANDLE UserToken) const;\n\n    void Initialize(const GUID& VmId, const wil::shared_handle& UserToken);\n\n    void InitializeGuest();\n\n    _Requires_lock_held_(m_guestDeviceLock)\n    bool InitializeDrvFsLockHeld(_In_ HANDLE UserToken);\n\n    bool IsDnsTunnelingSupported() const;\n\n    bool IsDisableVgpuSettingsSupported() const;\n\n    bool IsVirtioSerialConsoleSupported() const;\n\n    bool IsVmemmSuffixSupported() const;\n\n    _Requires_lock_held_(m_lock)\n    DiskMountResult MountDiskLockHeld(\n        _In_ PCWSTR Disk, _In_ DiskType MountDiskType, _In_ ULONG PartitionIndex, _In_opt_ PCWSTR Name, _In_opt_ PCWSTR Type, _In_opt_ PCWSTR Options);\n\n    void WaitForPmemDeviceInVm(_In_ ULONG PmemId);\n\n    void OnCrash(_In_ LPCWSTR Details);\n\n    void OnExit(_In_opt_ PCWSTR ExitDetails);\n\n    void ReadGuestCapabilities();\n\n    _Requires_lock_held_(m_lock)\n    ULONG ReserveLun(_In_ std::optional<ULONG> Lun = {});\n\n    void RestorePassthroughDiskState(_In_ LPCWSTR Disk) const;\n\n    _Requires_lock_held_(m_lock)\n    static void SaveDiskState(_In_ HKEY Key, _In_ const AttachedDisk& Disk, _In_ const DiskState& State, _In_ const DiskType& DiskType);\n\n    _Requires_lock_held_(m_lock)\n    std::pair<int, LX_MINI_MOUNT_STEP> UnmountDisk(_In_ const AttachedDisk& Disk, _Inout_ DiskState& State);\n\n    _Requires_lock_held_(m_lock)\n    std::pair<int, LX_MINI_MOUNT_STEP> UnmountVolume(_In_ const AttachedDisk& Disk, _In_ ULONG PartitionIndex, _In_ PCWSTR Name);\n\n    void VirtioFsWorker(_In_ const wil::unique_socket& socket);\n\n    static std::string s_GetMountTargetName(_In_ PCWSTR Disk, _In_opt_ PCWSTR Name, _In_ int PartitionIndex);\n\n    static LX_INIT_DRVFS_MOUNT s_InitializeDrvFs(_Inout_ WslCoreVm* VmContext, _In_ HANDLE UserToken);\n\n    static void CALLBACK s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context);\n\n    wil::srwlock m_guestDeviceLock;\n    std::shared_ptr<GuestDeviceManager> m_guestDeviceManager;\n    _Guarded_by_(m_guestDeviceLock) std::future<bool> m_drvfsInitialResult;\n    _Guarded_by_(m_guestDeviceLock) wil::unique_handle m_drvfsToken;\n    _Guarded_by_(m_guestDeviceLock) wil::unique_handle m_adminDrvfsToken;\n    _Guarded_by_(m_guestDeviceLock) std::map<VirtioFsShare, std::wstring> m_virtioFsShares;\n    _Guarded_by_(m_guestDeviceLock) std::map<UINT32, wil::com_ptr<IPlan9FileSystem>> m_plan9Servers;\n    wil::srwlock m_lock;\n    _Guarded_by_(m_lock) wil::unique_event m_terminatingEvent { wil::EventOptions::ManualReset };\n    _Guarded_by_(m_lock) wil::unique_event m_vmExitEvent { wil::EventOptions::ManualReset };\n    wil::unique_event m_vmCrashEvent{wil::EventOptions::ManualReset};\n    std::optional<std::filesystem::path> m_vmCrashLogFile;\n\n    wil::srwlock m_exitCallbackLock;\n    std::wstring m_exitDetails;\n    std::wstring m_machineId;\n    GUID m_runtimeId;\n    wsl::core::Config m_vmConfig;\n    std::wstring m_comPipe0;\n    std::wstring m_comPipe1;\n    int m_coldDiscardShiftSize;\n    WslTraceLoggingClient m_traceClient;\n    std::filesystem::path m_rootFsPath;\n    std::filesystem::path m_tempPath;\n    wil::shared_handle m_userToken;\n    wil::unique_handle m_restrictedToken;\n    bool m_swapFileCreated;\n    bool m_localDevicesKeyCreated;\n    bool m_tempDirectoryCreated;\n    bool m_enableInboxGpuLibs;\n    bool m_defaultKernel = true;\n    LX_MINI_INIT_MOUNT_DEVICE_TYPE m_systemDistroDeviceType = LxMiniInitMountDeviceTypeInvalid;\n    ULONG m_systemDistroDeviceId = ULONG_MAX;\n    ULONG m_kernelModulesDeviceId = ULONG_MAX;\n    wsl::windows::common::hcs::unique_hcs_system m_system;\n    wil::unique_socket m_listenSocket;\n    std::function<void(GUID)> m_onExit;\n    wsl::shared::SocketChannel m_miniInitChannel;\n    wil::unique_socket m_notifyChannel;\n    SE_SID m_userSid;\n    std::shared_ptr<LxssRunningInstance> m_systemDistro;\n    _Guarded_by_(m_lock) std::bitset<MAX_VHD_COUNT> m_lunBitmap;\n    _Guarded_by_(m_lock) std::map<AttachedDisk, DiskState> m_attachedDisks;\n    std::tuple<std::uint32_t, std::uint32_t, std::uint32_t> m_kernelVersion;\n    std::wstring m_kernelVersionString;\n    bool m_seccompAvailable;\n    std::wstring m_sharedMemoryRoot;\n    std::filesystem::path m_installPath;\n    std::wstring m_userProfile;\n    wsl::windows::common::helpers::WindowsVersion m_windowsVersion;\n    std::shared_ptr<DmesgCollector> m_dmesgCollector;\n    std::shared_ptr<GuestTelemetryLogger> m_gnsTelemetryLogger;\n    std::wstring m_debugShellPipe;\n    std::thread m_distroExitThread;\n    std::thread m_virtioFsThread;\n    std::thread m_crashDumpCollectionThread;\n\n    wil::srwlock m_persistentMemoryLock;\n    _Guarded_by_(m_persistentMemoryLock) ULONG m_nextPersistentMemoryId = 0;\n\n    std::unique_ptr<wsl::core::INetworkingEngine> m_networkingEngine;\n};\n\nDEFINE_ENUM_FLAG_OPERATORS(WslCoreVm::DiskStateFlags);\n\nDEFINE_ENUM_FLAG_OPERATORS(WslCoreVm::MountFlags);\n"
  },
  {
    "path": "src/windows/service/exe/WslMirroredNetworking.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslMirroredNetworking.cpp\n\nAbstract:\n\n    This file contains WSL mirrored networking function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"WslMirroredNetworking.h\"\n#include \"WslCoreMessageQueue.h\"\n#include \"Stringify.h\"\n#include \"WslCoreNetworkingSupport.h\"\n#include \"WslCoreNetworkEndpointSettings.h\"\n#include \"WslCoreHostDnsInfo.h\"\n#include \"hcs.hpp\"\n#include \"hns_schema.h\"\n\nstatic constexpr auto c_loopbackDeviceName = TEXT(LX_INIT_LOOPBACK_DEVICE_NAME);\nstatic constexpr auto c_initialMirroredGoalStateWaitTimeoutMs = 5 * 1000;\n\nusing namespace wsl::windows::common;\nusing namespace wsl::shared;\nusing wsl::core::networking::EndpointIpAddress;\nusing wsl::core::networking::EndpointRoute;\n\nnamespace {\ninline const auto HnsModifyRequestTypeToString(const hns::ModifyRequestType requestType)\n{\n    return JsonEnumToString<hns::ModifyRequestType>(requestType);\n}\n} // namespace\n\n_Requires_lock_held_(m_networkLock)\nvoid wsl::core::networking::WslMirroredNetworkManager::ProcessConnectivityChange()\n{\n    const std::set<GUID, wsl::windows::common::helpers::GuidLess> initialConnectedInterfaces{std::move(m_hostConnectedInterfaces)};\n    m_hostConnectedInterfaces.clear();\n\n    const auto coInit = wil::CoInitializeEx();\n    const wil::com_ptr<INetworkListManager> networkListManager = wil::CoCreateInstance<NetworkListManager, INetworkListManager>();\n\n    wil::com_ptr<IEnumNetworks> networksEnumerator;\n    THROW_IF_FAILED(networkListManager->GetNetworks(NLM_ENUM_NETWORK_CONNECTED, &networksEnumerator));\n\n    for (;;)\n    {\n        ULONG fetched{};\n        wil::com_ptr<INetwork> networkInstance;\n        auto hr = networksEnumerator->Next(1, &networkInstance, &fetched);\n        THROW_IF_FAILED(hr);\n        if (hr == S_FALSE || fetched == 0)\n        {\n            break;\n        }\n\n        // each NLM network could have multiple interfaces - walk through each\n        // if we fail trying to access an individual interface, continue the loop for the other interfaces\n\n        wil::com_ptr<IEnumNetworkConnections> enumNetworkConnections;\n        hr = networkInstance->GetNetworkConnections(&enumNetworkConnections);\n        if (FAILED(hr))\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::ProcessConnectivityChange - ignoring interface after processing \"\n                \"INetworkConnection::GetAdapterId\",\n                TraceLoggingValue(hr, \"hr\"));\n            continue;\n        }\n\n        for (;;)\n        {\n            ULONG fetchedNetworkConnections{};\n            wil::com_ptr<INetworkConnection> networkConnection;\n            hr = enumNetworkConnections->Next(1, &networkConnection, &fetchedNetworkConnections);\n            if (FAILED(hr) || hr == S_FALSE || fetchedNetworkConnections == 0)\n            {\n                break;\n            }\n\n            GUID interfaceGuid{};\n            hr = networkConnection->GetAdapterId(&interfaceGuid);\n            if (FAILED(hr))\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::ProcessConnectivityChange - ignoring interface INetworkConnection::GetAdapterId \"\n                    \"failed\",\n                    TraceLoggingValue(hr, \"hr\"));\n                continue;\n            }\n\n            NLM_CONNECTIVITY connectivity{};\n            hr = networkConnection->GetConnectivity(&connectivity);\n            if (FAILED(hr) || connectivity == NLM_CONNECTIVITY_DISCONNECTED)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::ProcessConnectivityChange - ignoring interface after processing \"\n                    \"INetworkConnection::GetConnectivity\",\n                    TraceLoggingValue(wsl::shared::string::GuidToString<wchar_t>(interfaceGuid).c_str(), \"interfaceGuid\"),\n                    TraceLoggingValue(connectivity == NLM_CONNECTIVITY_DISCONNECTED, \"is_NLM_CONNECTIVITY_DISCONNECTED\"),\n                    TraceLoggingValue(hr, \"hr\"));\n                continue;\n            }\n\n            m_hostConnectedInterfaces.insert(interfaceGuid);\n        }\n    }\n\n    if (initialConnectedInterfaces != m_hostConnectedInterfaces)\n    {\n        WSL_LOG(\n            \"WslMirroredNetworkManager::ProcessConnectivityChange - reset goal state\",\n            TraceLoggingValue(initialConnectedInterfaces.size(), \"previous_interfaces_size\"),\n            TraceLoggingValue(m_hostConnectedInterfaces.size(), \"updated_interfaces_size\"));\n\n        m_inMirroredGoalState.ResetEvent();\n        m_connectivityTelemetry.UpdateTimer();\n\n        std::wstring guids;\n        for (const auto& connectedInterface : initialConnectedInterfaces)\n        {\n            guids.append(wsl::shared::string::GuidToString<wchar_t>(connectedInterface) + L\",\");\n        }\n\n        WSL_LOG(\n            \"WslMirroredNetworkManager::ProcessConnectivityChange [previous]\",\n            TraceLoggingValue(guids.c_str(), \"connectedInterfaces\"));\n\n        guids.clear();\n        for (const auto& connectedInterface : m_hostConnectedInterfaces)\n        {\n            guids.append(wsl::shared::string::GuidToString<wchar_t>(connectedInterface) + L\",\");\n        }\n\n        WSL_LOG(\n            \"WslMirroredNetworkManager::ProcessConnectivityChange [updated]\",\n            TraceLoggingValue(guids.c_str(), \"connectedInterfaces\"));\n    }\n}\n\n_Requires_lock_held_(m_networkLock)\nvoid wsl::core::networking::WslMirroredNetworkManager::ProcessIpAddressChange()\n{\n    wsl::core::networking::unique_address_table addressTable{};\n    THROW_IF_WIN32_ERROR(GetUnicastIpAddressTable(AF_UNSPEC, &addressTable));\n\n    for (auto& endpoint : m_networkEndpoints)\n    {\n        const auto initialAddresses{std::move(endpoint.Network->IpAddresses)};\n        endpoint.Network->IpAddresses.clear();\n\n        // if the interface isn't connected, ensure we always track zero addresses\n        if (!endpoint.Network->IsConnected)\n        {\n            continue;\n        }\n\n        for (const auto& address : wil::make_range(addressTable.get()->Table, addressTable.get()->NumEntries))\n        {\n            if (address.InterfaceIndex != endpoint.Network->InterfaceIndex)\n            {\n                continue;\n            }\n\n            const auto endpointAddress = EndpointIpAddress(address);\n            if (endpointAddress.IsPreferred())\n            {\n                endpoint.Network->IpAddresses.insert(endpointAddress);\n            }\n        }\n\n        if (initialAddresses != endpoint.Network->IpAddresses)\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::ProcessIpAddressChange - reset goal state\",\n                TraceLoggingValue(endpoint.EndpointId, \"endpointId\"),\n                TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                TraceLoggingValue(initialAddresses.size(), \"previous_addresses_size\"),\n                TraceLoggingValue(endpoint.Network->IpAddresses.size(), \"updated_addresses_size\"));\n\n            m_inMirroredGoalState.ResetEvent();\n            m_connectivityTelemetry.UpdateTimer();\n\n            for (const auto& address : initialAddresses)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::ProcessIpAddressChange [previous]\",\n                    TraceLoggingValue(endpoint.EndpointId, \"endpointId\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                    TraceLoggingValue(address.AddressString.c_str(), \"address\"),\n                    TraceLoggingValue(address.PrefixLength, \"prefixLength\"));\n            }\n            for (const auto& address : endpoint.Network->IpAddresses)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::ProcessIpAddressChange [updated]\",\n                    TraceLoggingValue(endpoint.EndpointId, \"endpointId\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                    TraceLoggingValue(address.AddressString.c_str(), \"address\"),\n                    TraceLoggingValue(address.PrefixLength, \"prefixLength\"));\n            }\n        }\n    }\n}\n\n_Requires_lock_held_(m_networkLock)\nvoid wsl::core::networking::WslMirroredNetworkManager::ProcessRouteChange()\n{\n    wsl::core::networking::unique_address_table addressTable{};\n    wsl::core::networking::unique_forward_table routeTable{};\n    THROW_IF_WIN32_ERROR(GetIpForwardTable2(AF_UNSPEC, &routeTable));\n\n    for (auto& endpoint : m_networkEndpoints)\n    {\n        const auto initialRoutes = endpoint.Network->Routes;\n        endpoint.Network->Routes.clear();\n\n        // if the interface isn't connected, ensure we always track zero routes\n        // Windows can have routes assigned on disconnected interfaces, Linux cannot\n        if (!endpoint.Network->IsConnected)\n        {\n            continue;\n        }\n\n        // Gather endpoint address prefixes and raw address\n        std::unordered_set<std::wstring> addressPrefixes{};\n        std::unordered_set<std::wstring> addresses{};\n        std::unordered_set<std::wstring> ipv4broadcastAddresses{};\n\n        for (const auto& endpointAddress : endpoint.Network->IpAddresses)\n        {\n            addresses.insert(endpointAddress.AddressString);\n\n            auto addressPrefix = endpointAddress.GetPrefix();\n            WI_ASSERT(!addressPrefix.empty());\n            if (!addressPrefix.empty())\n            {\n                addressPrefixes.insert(std::move(addressPrefix));\n            }\n\n            if (endpointAddress.Address.si_family == AF_INET)\n            {\n                auto v4BroadcastMaskAddress = endpointAddress.GetIpv4BroadcastMask();\n                WI_ASSERT(!v4BroadcastMaskAddress.empty());\n                if (!v4BroadcastMaskAddress.empty())\n                {\n                    ipv4broadcastAddresses.emplace(std::move(v4BroadcastMaskAddress));\n                }\n            }\n        }\n\n        for (const auto& route : wil::make_range(routeTable.get()->Table, routeTable.get()->NumEntries))\n        {\n            if (route.InterfaceIndex == endpoint.Network->InterfaceIndex)\n            {\n                auto endpointRoute = EndpointRoute(route);\n\n                endpointRoute.IsAutoGeneratedPrefixRoute =\n                    endpointRoute.IsNextHopOnlink() && addressPrefixes.contains(endpointRoute.GetFullDestinationPrefix());\n\n                // Ignore host IPv4 routes, e.g. 192.168.5.2/32 -> 0.0.0.0\n                if (addresses.contains(endpointRoute.DestinationPrefixString))\n                {\n                    continue;\n                }\n\n                // ignore host routes for deprecated addresses\n                // the address will not be in the 'addresses' variable above since it's deprecated\n                // e.g. a route 2001:0:d5b:9458:1ceb:518b:7c94:609e/128, but the matching local IP address is deprecated\n                bool shouldIgnoreUnicastAddressRoute = false;\n                if (endpointRoute.IsUnicastAddressRoute())\n                {\n                    if (!addressTable)\n                    {\n                        THROW_IF_WIN32_ERROR(GetUnicastIpAddressTable(AF_UNSPEC, &addressTable));\n                    }\n                    // find the address matching this destination prefix\n                    for (const auto& address : wil::make_range(addressTable.get()->Table, addressTable.get()->NumEntries))\n                    {\n                        if (address.InterfaceIndex != endpoint.Network->InterfaceIndex)\n                        {\n                            continue;\n                        }\n\n                        const auto endpointAddress = EndpointIpAddress(address);\n                        if (endpointAddress.Address == route.DestinationPrefix.Prefix)\n                        {\n                            if (!endpointAddress.IsPreferred())\n                            {\n                                shouldIgnoreUnicastAddressRoute = true;\n                                break;\n                            }\n                        }\n                    }\n                }\n                if (shouldIgnoreUnicastAddressRoute)\n                {\n                    continue;\n                }\n\n                if (endpointRoute.DestinationPrefix.Prefix.si_family == AF_INET)\n                {\n                    if (endpoint.Network->DisableIpv4DefaultRoutes && endpointRoute.IsDefault())\n                    {\n                        continue;\n                    }\n\n                    const auto addressType =\n                        Ipv4AddressType(reinterpret_cast<const UCHAR*>(&endpointRoute.DestinationPrefix.Prefix.Ipv4.sin_addr));\n                    if (addressType != NlatUnspecified && addressType != NlatUnicast)\n                    {\n                        // ignore broadcast and multicast routes - Linux doesn't seem to create those like Windows\n                        continue;\n                    }\n\n                    if (ipv4broadcastAddresses.contains(endpointRoute.DestinationPrefixString))\n                    {\n                        continue;\n                    }\n                }\n                else if (endpointRoute.DestinationPrefix.Prefix.si_family == AF_INET6)\n                {\n                    if (endpoint.Network->DisableIpv6DefaultRoutes && endpointRoute.IsDefault())\n                    {\n                        continue;\n                    }\n\n                    const auto addressType =\n                        Ipv6AddressType(reinterpret_cast<const UCHAR*>(&endpointRoute.DestinationPrefix.Prefix.Ipv6.sin6_addr));\n                    if (addressType != NlatUnspecified && addressType != NlatUnicast)\n                    {\n                        // ignore broadcast and multicast routes - Linux doesn't seem to create those like Windows\n                        continue;\n                    }\n                }\n\n                // update the route metric for Linux - which to be equivalent to Windows must be the sum of the interface metric and route metric\n                endpointRoute.Metric += (endpointRoute.Family == AF_INET) ? endpoint.Network->IPv4InterfaceMetric.value_or(0)\n                                                                          : endpoint.Network->IPv6InterfaceMetric.value_or(0);\n                if (endpointRoute.Metric > UINT16_MAX)\n                {\n                    endpointRoute.Metric = UINT16_MAX;\n                }\n                endpoint.Network->Routes.insert(endpointRoute);\n            }\n        }\n\n        // Linux requires that there's an onlink route for any route with a NextHop address that's not all-zeros (on-link)\n        // \"normal\" network deployments with Windows creates an address prefix route that includes that next hop\n        // but some deployments, like some VPNs, do not include a prefix route that includes the nexthop\n        // While that works in Windows (all nexthop addresses in a route *must* be on-link), it won't work in Linux\n        // thus we must guarantee an onlink route for all routes with a non-zero nexthop\n        std::vector<EndpointRoute> newRoutes;\n        for (const auto& route : endpoint.Network->Routes)\n        {\n            if (!route.IsNextHopOnlink())\n            {\n                EndpointRoute newRoute;\n                newRoute.Family = route.Family;\n                newRoute.Metric = route.Metric;\n                newRoute.SitePrefixLength = route.GetMaxPrefixLength();\n\n                // update the destination prefix to the nexthop address /32 (for ipv4) or /128 (for ipv6)\n                newRoute.DestinationPrefix.Prefix = route.NextHop;\n                newRoute.DestinationPrefix.PrefixLength = route.GetMaxPrefixLength();\n                newRoute.DestinationPrefixString = windows::common::string::SockAddrInetToWstring(newRoute.DestinationPrefix.Prefix);\n\n                // update the destination prefix to be all zeros (on-link)\n                ZeroMemory(&newRoute.NextHop, sizeof newRoute.NextHop);\n                newRoute.NextHop.si_family = route.NextHop.si_family;\n                newRoute.NextHopString = windows::common::string::SockAddrInetToWstring(newRoute.NextHop);\n\n                // force a copy so the route strings are re-calculated in the new EndpointRoute object\n                newRoutes.emplace_back(std::move(newRoute));\n            }\n        }\n        for (const auto& route : newRoutes)\n        {\n            endpoint.Network->Routes.insert(route);\n        }\n\n        if (initialRoutes != endpoint.Network->Routes)\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::ProcessRouteChange - reset goal state\",\n                TraceLoggingValue(endpoint.EndpointId, \"endpointId\"),\n                TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                TraceLoggingValue(initialRoutes.size(), \"previous_routes_size\"),\n                TraceLoggingValue(endpoint.Network->Routes.size(), \"updated_routes_size\"));\n\n            m_inMirroredGoalState.ResetEvent();\n            m_connectivityTelemetry.UpdateTimer();\n\n            for (const auto& route : initialRoutes)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::ProcessRouteChange [previous]\",\n                    TraceLoggingValue(endpoint.EndpointId, \"endpointId\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                    TraceLoggingValue(route.Metric, \"metric\"),\n                    TraceLoggingValue(route.NextHopString.c_str(), \"nextHop\"),\n                    TraceLoggingValue(route.DestinationPrefixString.c_str(), \"destinationPrefix\"),\n                    TraceLoggingValue(route.DestinationPrefix.PrefixLength, \"destinationPrefixLength\"));\n            }\n            for (const auto& route : endpoint.Network->Routes)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::ProcessRouteChange [updated]\",\n                    TraceLoggingValue(endpoint.EndpointId, \"endpointId\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                    TraceLoggingValue(route.Metric, \"metric\"),\n                    TraceLoggingValue(route.NextHopString.c_str(), \"nextHop\"),\n                    TraceLoggingValue(route.DestinationPrefixString.c_str(), \"destinationPrefix\"),\n                    TraceLoggingValue(route.DestinationPrefix.PrefixLength, \"destinationPrefixLength\"));\n            }\n        }\n    }\n}\n\n_Requires_lock_held_(m_networkLock)\nvoid wsl::core::networking::WslMirroredNetworkManager::ProcessDNSChange()\n{\n    const auto initialDnsInfo = m_dnsInfo;\n\n    if (m_vmConfig.EnableDnsTunneling)\n    {\n        m_dnsInfo = wsl::core::networking::HostDnsInfo::GetDnsTunnelingSettings(m_dnsTunnelingIpAddress);\n    }\n    else\n    {\n        m_dnsInfo = wsl::core::networking::HostDnsInfo::GetDnsSettings(\n            wsl::core::networking::DnsSettingsFlags::IncludeVpn | wsl::core::networking::DnsSettingsFlags::IncludeIpv6Servers |\n            wsl::core::networking::DnsSettingsFlags::IncludeAllSuffixes);\n    }\n\n    if (initialDnsInfo != m_dnsInfo)\n    {\n        WSL_LOG(\"WslMirroredNetworkManager::ProcessDNSChange - reset goal state\");\n        m_inMirroredGoalState.ResetEvent();\n        m_connectivityTelemetry.UpdateTimer();\n\n        WSL_LOG(\n            \"WslMirroredNetworkManager::ProcessDNSChange [previous]\",\n            TraceLoggingValue(wsl::shared::string::Join(initialDnsInfo.Domains, ',').c_str(), \"domainList\"),\n            TraceLoggingValue(wsl::shared::string::Join(initialDnsInfo.Servers, ',').c_str(), \"dnsServerList\"));\n\n        WSL_LOG(\n            \"WslMirroredNetworkManager::ProcessDNSChange [updated]\",\n            TraceLoggingValue(wsl::shared::string::Join(m_dnsInfo.Domains, ',').c_str(), \"domainList\"),\n            TraceLoggingValue(wsl::shared::string::Join(m_dnsInfo.Servers, ',').c_str(), \"dnsServerList\"));\n    }\n}\n\n_Requires_lock_held_(m_networkLock)\nvoid wsl::core::networking::WslMirroredNetworkManager::ProcessInterfaceChange()\n{\n    wsl::core::networking::unique_interface_table interfaceTable{};\n    THROW_IF_WIN32_ERROR(::GetIpInterfaceTable(AF_UNSPEC, &interfaceTable));\n\n    for (auto& endpoint : m_networkEndpoints)\n    {\n        const auto originalIPv4DisableDefaultRoutes = endpoint.Network->DisableIpv4DefaultRoutes;\n        const auto originalIPv6DisableDefaultRoutes = endpoint.Network->DisableIpv6DefaultRoutes;\n        const auto originallyConnected = endpoint.Network->IsConnected;\n        const auto originalMinimumMtu = endpoint.Network->GetEffectiveMtu();\n        const auto originalMinimumMetric = endpoint.Network->GetMinimumMetric();\n\n        endpoint.Network->IsConnected = false;\n\n        auto interfaceFoundCount = 0;\n        for (const auto& ipInterface : wil::make_range(interfaceTable.get()->Table, interfaceTable.get()->NumEntries))\n        {\n            if (ipInterface.InterfaceIndex != endpoint.Network->InterfaceIndex ||\n                (ipInterface.Family != AF_INET && ipInterface.Family != AF_INET6))\n            {\n                continue;\n            }\n\n            // Endpoint is marked as connected if either IPv4 or IPv6 interface is connected\n            endpoint.Network->IsConnected = endpoint.Network->IsConnected || !!ipInterface.Connected;\n\n            if (ipInterface.Family == AF_INET)\n            {\n                endpoint.Network->IPv4InterfaceMtu = ipInterface.NlMtu;\n                endpoint.Network->IPv4InterfaceMetric = ipInterface.Metric;\n                endpoint.Network->DisableIpv4DefaultRoutes = ipInterface.DisableDefaultRoutes;\n            }\n            else\n            {\n                endpoint.Network->IPv6InterfaceMtu = ipInterface.NlMtu;\n                endpoint.Network->IPv6InterfaceMetric = ipInterface.Metric;\n                endpoint.Network->DisableIpv6DefaultRoutes = ipInterface.DisableDefaultRoutes;\n            }\n\n            ++interfaceFoundCount;\n            if (interfaceFoundCount > 1)\n            {\n                // we already found both v4 and v6\n                break;\n            }\n        }\n\n        const auto disableDefaultRoutesUpdated = originalIPv4DisableDefaultRoutes != endpoint.Network->DisableIpv4DefaultRoutes ||\n                                                 originalIPv6DisableDefaultRoutes != endpoint.Network->DisableIpv6DefaultRoutes;\n        const auto connectedStateUpdated = originallyConnected != endpoint.Network->IsConnected;\n        const auto minimumMtu = endpoint.Network->GetEffectiveMtu();\n        const auto mtuUpdated = originalMinimumMtu != minimumMtu;\n        const auto minimumMetric = endpoint.Network->GetMinimumMetric();\n        const auto metricUpdate = originalMinimumMetric != minimumMetric;\n\n        endpoint.Network->PendingIPInterfaceUpdate |= connectedStateUpdated || mtuUpdated || metricUpdate;\n\n        if (disableDefaultRoutesUpdated || connectedStateUpdated || mtuUpdated || metricUpdate)\n        {\n            // we want to trace when disableDefaultRoutesUpdated, but that won't trigger resetting the goal-state\n            // if disableDefaultRoutesUpdated affects routes, then ProcessRouteChange will reset the goal-state accordingly\n            // but we do want to trace when disableDefaultRoutes get updated - to greatly help debugging\n            if (connectedStateUpdated || mtuUpdated || metricUpdate)\n            {\n                WSL_LOG(\"WslMirroredNetworkManager::ProcessInterfaceChange - reset goal state\");\n                m_inMirroredGoalState.ResetEvent();\n                m_connectivityTelemetry.UpdateTimer();\n            }\n\n            WSL_LOG(\n                \"WslMirroredNetworkManager::ProcessInterfaceChange [previous]\",\n                TraceLoggingValue(endpoint.EndpointId, \"endpointId\"),\n                TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                TraceLoggingValue(originallyConnected, \"isConnected\"),\n                TraceLoggingValue(originalMinimumMtu, \"EffectiveMtu\"),\n                TraceLoggingValue(originalMinimumMetric, \"MinimumMetric\"),\n                TraceLoggingValue(originalIPv4DisableDefaultRoutes, \"disableIpv4DefaultRoutes\"),\n                TraceLoggingValue(originalIPv6DisableDefaultRoutes, \"disableIpv6DefaultRoutes\"));\n\n            WSL_LOG(\n                \"WslMirroredNetworkManager::ProcessInterfaceChange [updated]\",\n                TraceLoggingValue(endpoint.EndpointId, \"endpointId\"),\n                TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                TraceLoggingValue(endpoint.Network->IPv4InterfaceMtu, \"ipv4InterfaceMtu\"),\n                TraceLoggingValue(endpoint.Network->IPv6InterfaceMtu, \"ipv6InterfaceMtu\"),\n                TraceLoggingValue(endpoint.Network->IPv4InterfaceMetric.value_or(0xffffffff), \"IPv4InterfaceMetric\"),\n                TraceLoggingValue(endpoint.Network->IPv6InterfaceMetric.value_or(0xffffffff), \"IPv6InterfaceMetric\"),\n                TraceLoggingValue(endpoint.Network->DisableIpv4DefaultRoutes, \"disableIpv4DefaultRoutes\"),\n                TraceLoggingValue(endpoint.Network->DisableIpv6DefaultRoutes, \"disableIpv6DefaultRoutes\"));\n        }\n    }\n}\n\nwsl::core::networking::WslMirroredNetworkManager::WslMirroredNetworkManager(\n    HCS_SYSTEM hcsSystem,\n    const Config& config,\n    GnsMessageCallbackWithCallbackResult&& GnsMessageCallbackWithCallbackResult,\n    AddNetworkEndpointCallback&& addNetworkEndpointCallback,\n    const std::pair<uint16_t, uint16_t>& ephemeralPortRange) :\n    m_callbackForGnsMessage(std::move(GnsMessageCallbackWithCallbackResult)),\n    m_addNetworkEndpointCallback(std::move(addNetworkEndpointCallback)),\n    m_hcsSystem{hcsSystem},\n    m_vmConfig{config},\n    m_ephemeralPortRange(ephemeralPortRange),\n    m_state(State::Starting)\n{\n    // ensure the MTA apartment stays alive for the lifetime of this object in this process\n    // we do not want to risk COM unloading / reloading when we need to make our WinRT API calls\n    // which by default will be in the MTA\n    LOG_IF_FAILED(CoIncrementMTAUsage(&m_mtaCookie));\n\n    // locking in the c'tor in case any of the below callbacks fire before this object is fully constructed\n    const auto lock = m_networkLock.lock_exclusive();\n\n    // keep the WinRT DLL loaded for the lifetime of this instance. we instantiate it repeatedly,\n    // and today we are loading and unloading 7 dll's over and over again - each time we call it.\n    // this also circumvents many performance optimizations we made with our WinRT API\n    const auto roInit = wil::RoInitialize();\n    m_networkInformationStatics = wil::GetActivationFactory<ABI::Windows::Networking::Connectivity::INetworkInformationStatics>(\n        RuntimeClass_Windows_Networking_Connectivity_NetworkInformation);\n\n    m_netListManager = wil::CoCreateInstance<NetworkListManager, INetworkListManager>();\n    // create an event sink for NLM Network change notifications, then register (Advise) with NLM\n    m_netListManagerEventSink = wil::com_ptr<INetworkEvents>(Microsoft::WRL::Make<PublicNLMSink>(this));\n    // INetworkListManager is actually an inproc COM API - it just calls private COM APIs which are hosted in a service\n    m_netListManagerAdviseHandler.AdviseInProcObject<INetworkEvents>(m_netListManager, m_netListManagerEventSink.get());\n\n    // Subscribe for network change notifications. This is done before\n    // obtaining the initial list of networks to connect to, in order to\n    // avoid a race condition between the initial enumeration and any network\n    // changes that may be occurring at the same time. The subscription will\n    // receive network change events, but will not be able to react to them\n    // the lock is released.\n    m_hcnCallback = windows::common::hcs::RegisterServiceCallback(HcnCallback, this);\n\n    // Create the timer used to retry the HNS service connection.\n    m_retryHcnServiceConnectionTimer.reset(CreateThreadpoolTimer(HcnServiceConnectionTimerCallback, this, nullptr));\n    THROW_IF_NULL_ALLOC(m_retryHcnServiceConnectionTimer);\n\n    // Create the timer used to retry syncing pending IP state with Linux.\n    m_retryLinuxIpStateSyncTimer.reset(CreateThreadpoolTimer(RetryLinuxIpStateSyncTimerCallback, this, nullptr));\n    THROW_IF_NULL_ALLOC(m_retryLinuxIpStateSyncTimer);\n\n    m_debounceUpdateAllEndpointsDefaultTimer.reset(CreateThreadpoolTimer(DebounceUpdateAllEndpointsDefaultTimerFired, this, nullptr));\n    THROW_IF_NULL_ALLOC(m_debounceUpdateAllEndpointsDefaultTimer);\n\n    m_debounceCreateEndpointFailureTimer.reset(CreateThreadpoolTimer(DebounceCreateEndpointFailureTimerFired, this, nullptr));\n    THROW_IF_NULL_ALLOC(m_debounceCreateEndpointFailureTimer);\n\n    // Populate the initial list of networks. The list will then be kept\n    // up to date by the above subscription notifications.\n    for (const auto& networkId : EnumerateMirroredNetworks())\n    {\n        // Must call back through MirroredNetworking to create a new Endpoint\n        // note that the callback will not block - it just queues the work in MirroredNetworking\n        LOG_IF_FAILED(AddNetwork(networkId));\n    }\n\n    // once HNS has started creating networks, start our telemetry timer\n    if (config.EnableTelemetry && !WslTraceLoggingShouldDisableTelemetry())\n    {\n        m_connectivityTelemetry.StartTimer([&](NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) {\n            TelemetryConnectionCallback(hostConnectivity, telemetryCounter);\n        });\n    }\n\n    if (config.DnsTunnelingIpAddress.has_value())\n    {\n        m_dnsTunnelingIpAddress = wsl::windows::common::string::IntegerIpv4ToWstring(config.DnsTunnelingIpAddress.value());\n    }\n\n    m_state = State::Started;\n}\n\nwsl::core::networking::WslMirroredNetworkManager::~WslMirroredNetworkManager() noexcept\n{\n    Stop();\n}\n\nwsl::core::networking::WslMirroredNetworkManager::HnsStatus wsl::core::networking::WslMirroredNetworkManager::Stop() noexcept\n{\n    HnsStatus returnStatus{};\n    try\n    {\n        // scope to the lock to flip the bit that we are stopping\n        {\n            const auto lock = m_networkLock.lock_exclusive();\n            m_state = State::Stopped;\n            returnStatus = m_latestHnsStatus;\n        }\n\n        // must set state first so all other threads won't make forward progress\n        // since we are about to stop all timers and callbacks\n        // which must be stopped not holding our lock\n\n        // Next stop the telemetry timer which could queue work to linux (through m_gnsCallbackQueue)\n        m_connectivityTelemetry.Reset();\n\n        // Next stop the timer which could reset the hcnCallback\n        m_retryHcnServiceConnectionTimer.reset();\n\n        // Next stop the Hcn callback, which could add/remove networks\n        m_hcnCallback.reset();\n\n        m_debounceUpdateAllEndpointsDefaultTimer.reset();\n\n        m_debounceCreateEndpointFailureTimer.reset();\n\n        // Stop the linux ip state sync timer\n        m_retryLinuxIpStateSyncTimer.reset();\n\n        // canceling the callback queue only after stopping all sources that could queue a callback\n        m_gnsCallbackQueue.cancel();\n        m_hnsQueue.cancel();\n\n        // all of the above must be done outside holding a lock to avoid deadlocks\n        const auto lock = m_networkLock.lock_exclusive();\n        m_networkEndpoints.clear();\n    }\n    CATCH_LOG()\n\n    return returnStatus;\n}\n\nvoid wsl::core::networking::WslMirroredNetworkManager::DebounceUpdateAllEndpointsDefaultTimerFired(\n    _Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER)\ntry\n{\n    auto* const instance = static_cast<WslMirroredNetworkManager*>(Context);\n\n    const auto lock = instance->m_networkLock.lock_exclusive();\n    instance->m_IsDebounceUpdateAllEndpointsDefaultTimerSet = false;\n    if (instance->m_state == State::Stopped)\n    {\n        return;\n    }\n\n    instance->UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, \"DebounceUpdateAllEndpointsDefaultTimerFired\");\n}\nCATCH_LOG()\n\nvoid wsl::core::networking::WslMirroredNetworkManager::DebounceCreateEndpointFailureTimerFired(\n    _Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER)\ntry\n{\n    auto* const instance = static_cast<WslMirroredNetworkManager*>(Context);\n\n    const auto lock = instance->m_networkLock.lock_exclusive();\n    if (instance->m_state == State::Stopped)\n    {\n        return;\n    }\n\n    if (!instance->m_failedEndpointProperties.empty())\n    {\n        // AddEndpointImpl will update m_failedEndpointProperties if any re-attempts to add the endpoint fail\n        // thus we must first move everything out\n        auto failedEndpointProperties = std::move(instance->m_failedEndpointProperties);\n        instance->m_failedEndpointProperties.clear();\n        for (auto& endpointProperties : failedEndpointProperties)\n        {\n            instance->AddEndpointImpl(std::move(endpointProperties));\n        }\n    }\n}\nCATCH_LOG()\n\n_Requires_lock_held_(m_networkLock)\nstd::vector<GUID> wsl::core::networking::WslMirroredNetworkManager::EnumerateMirroredNetworks() const noexcept\ntry\n{\n    WI_ASSERT(m_state == State::Started || m_state == State::Starting);\n\n    return EnumerateMirroredNetworksAndHyperVFirewall(m_vmConfig.FirewallConfig.Enabled());\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::AddNetwork(const GUID& networkId) noexcept\ntry\n{\n    WSL_LOG(\"WslMirroredNetworkManager::AddNetwork\", TraceLoggingValue(networkId, \"networkId\"));\n\n    // Inform the parent class to create a new endpoint object which we can then connect into the container\n    m_hnsQueue.submit([this, networkId] { m_addNetworkEndpointCallback(networkId); });\n\n    return S_OK;\n}\nCATCH_RETURN()\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::RemoveNetwork(const GUID& networkId) noexcept\ntry\n{\n    WSL_LOG(\"WslMirroredNetworkManager::RemoveNetwork\", TraceLoggingValue(networkId, \"networkId\"));\n\n    const auto foundEndpoint =\n        std::ranges::find_if(m_networkEndpoints, [&](const auto& endpoint) { return endpoint.NetworkId == networkId; });\n    if (foundEndpoint == std::end(m_networkEndpoints))\n    {\n        WSL_LOG(\"WslMirroredNetworkManager::RemoveNetwork - Network not found\", TraceLoggingValue(networkId, \"networkId\"));\n        return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);\n    }\n\n    // RemoveEndpoint will remove this endpoint from m_networkEndpoints\n    return RemoveEndpoint(foundEndpoint->EndpointId);\n}\nCATCH_RETURN()\n\nvoid __stdcall wsl::core::networking::WslMirroredNetworkManager::RetryLinuxIpStateSyncTimerCallback(\n    _Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER) noexcept\n{\n    auto* const manager = static_cast<WslMirroredNetworkManager*>(Context);\n    const auto lock = manager->m_networkLock.lock_exclusive();\n    if (manager->m_state == State::Stopped)\n    {\n        return;\n    }\n\n    manager->UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, \"RetryLinuxIpStateSyncTimerCallback\");\n}\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendAddressRequestToGns(\n    const NetworkEndpoint& endpoint, const TrackedIpAddress& address, hns::ModifyRequestType requestType) noexcept\ntry\n{\n    hns::ModifyGuestEndpointSettingRequest<hns::IPAddress> modifyRequest;\n    modifyRequest.ResourceType = hns::GuestEndpointResourceType::IPAddress;\n    modifyRequest.RequestType = requestType;\n    modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);\n    modifyRequest.Settings = address.ConvertToHnsSettingsMsg();\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendAddressRequestToGns\",\n        TraceLoggingValue(\"ModifyGuestDeviceSettingRequest - set address [queued]\", \"GnsMessage\"),\n        TraceLoggingValue(HnsModifyRequestTypeToString(requestType).c_str(), \"requestType\"),\n        TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n        TraceLoggingValue(address.Address.AddressString.c_str(), \"ipAddress\"),\n        TraceLoggingValue(address.Address.PrefixLength, \"prefixLength\"),\n        TraceLoggingValue(address.Address.IsPreferred(), \"isPreferred\"));\n\n    int linuxResultCode{};\n    // can safely capture by ref since we are waiting\n    const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {\n        return m_callbackForGnsMessage(LxGnsMessageDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);\n    });\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendAddressRequestToGns\",\n        TraceLoggingValue(\"ModifyGuestDeviceSettingRequest - set address [completed]\", \"GnsMessage\"),\n        TraceLoggingHResult(hr, \"hr\"),\n        TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n\n    address.SyncRetryCount = (address.SyncRetryCount > 0) ? address.SyncRetryCount - 1 : 0;\n    return hr;\n}\nCATCH_RETURN()\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendRouteRequestToGns(\n    const NetworkEndpoint& endpoint, const TrackedRoute& route, hns::ModifyRequestType requestType) noexcept\ntry\n{\n    hns::ModifyGuestEndpointSettingRequest<hns::Route> modifyRequest;\n    modifyRequest.ResourceType = hns::GuestEndpointResourceType::Route;\n    modifyRequest.RequestType = requestType;\n    modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);\n    modifyRequest.Settings = route.ConvertToHnsSettingsMsg();\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendRouteRequestToGns\",\n        TraceLoggingValue(\"ModifyGuestDeviceSettingRequest : set route [queued]\", \"GnsMessage\"),\n        TraceLoggingValue(HnsModifyRequestTypeToString(requestType).c_str(), \"requestType\"),\n        TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n        TraceLoggingValue(route.Route.DestinationPrefixString.c_str(), \"destinationPrefix\"),\n        TraceLoggingValue(route.Route.DestinationPrefix.PrefixLength, \"prefixLength\"),\n        TraceLoggingValue(route.Route.NextHopString.c_str(), \"nextHop\"),\n        TraceLoggingValue(route.Route.Metric, \"metric\"));\n\n    int linuxResultCode{};\n    // can safely capture by ref since we are waiting\n    const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {\n        return m_callbackForGnsMessage(LxGnsMessageDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);\n    });\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendRouteRequestToGns\",\n        TraceLoggingValue(\"ModifyGuestDeviceSettingRequest : set route [completed]\", \"GnsMessage\"),\n        TraceLoggingHResult(hr, \"hr\"),\n        TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n\n    route.SyncRetryCount = (route.SyncRetryCount > 0) ? route.SyncRetryCount - 1 : 0;\n    return hr;\n}\nCATCH_RETURN()\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendLoopbackRequestToGns(\n    const NetworkEndpoint& endpoint, const TrackedIpAddress& address, hns::OperationType operation) noexcept\ntry\n{\n    hns::LoopbackRoutesRequest loopbackRequest;\n    loopbackRequest.operation = operation;\n    loopbackRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);\n    loopbackRequest.family = address.Address.Address.si_family;\n    loopbackRequest.ipAddress = address.Address.AddressString;\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendLoopbackRequestToGns\",\n        TraceLoggingValue(\"LoopbackRoutesRequest [queued]\", \"GnsMessage\"),\n        TraceLoggingValue(JsonEnumToString(operation).c_str(), \"requestType\"),\n        TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n        TraceLoggingValue(address.Address.AddressString.c_str(), \"ipAddress\"));\n\n    int linuxResultCode{};\n    // can safely capture by ref since we are waiting\n    const auto hr = m_gnsCallbackQueue.submit_and_wait([&]() {\n        return m_callbackForGnsMessage(LxGnsMessageLoopbackRoutesRequest, ToJsonW(loopbackRequest), GnsCallbackFlags::Wait, &linuxResultCode);\n    });\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendLoopbackRequestToGns\",\n        TraceLoggingValue(\"LoopbackRoutesRequest [completed]\", \"GnsMessage\"),\n        TraceLoggingHResult(hr, \"hr\"),\n        TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n\n    return hr;\n}\nCATCH_RETURN()\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendDnsRequestToGns(\n    const NetworkEndpoint& endpoint, const DnsInfo& dnsInfo, hns::ModifyRequestType requestType) noexcept\ntry\n{\n    hns::ModifyGuestEndpointSettingRequest<hns::DNS> modifyRequest;\n    modifyRequest.ResourceType = hns::GuestEndpointResourceType::DNS;\n    modifyRequest.RequestType = requestType;\n    modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);\n    modifyRequest.Settings = BuildDnsNotification(dnsInfo);\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendDnsRequestToGns\",\n        TraceLoggingValue(\"ModifyGuestDeviceSettingRequest : set DNS [queued]\", \"GnsMessage\"),\n        TraceLoggingValue(HnsModifyRequestTypeToString(requestType).c_str(), \"requestType\"),\n        TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n        TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"m_vmConfig.EnableDnsTunneling\"),\n        TraceLoggingValue(wsl::shared::string::Join(dnsInfo.Servers, ',').c_str(), \"server list\"),\n        TraceLoggingValue(wsl::shared::string::Join(dnsInfo.Domains, ',').c_str(), \"suffix list\"));\n\n    int linuxResultCode{};\n    // can safely capture by ref since we are waiting\n    const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {\n        return m_callbackForGnsMessage(LxGnsMessageDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);\n    });\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendDnsRequestToGns\",\n        TraceLoggingValue(\"ModifyGuestDeviceSettingRequest : set DNS [completed]\", \"GnsMessage\"),\n        TraceLoggingHResult(hr, \"hr\"),\n        TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n\n    return hr;\n}\nCATCH_RETURN()\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendInterfaceRequestToGns(const NetworkEndpoint& endpoint) noexcept\ntry\n{\n    const auto interfaceConnected = endpoint.Network->IsConnected;\n    const auto interfaceMtu = endpoint.Network->GetEffectiveMtu();\n    const auto interfaceMetric = endpoint.Network->GetMinimumMetric();\n\n    hns::ModifyGuestEndpointSettingRequest<hns::NetworkInterface> modifyRequest;\n    modifyRequest.Settings.Connected = interfaceConnected;\n    modifyRequest.Settings.NlMtu = interfaceMtu;\n    modifyRequest.Settings.Metric = interfaceMetric;\n    modifyRequest.ResourceType = hns::GuestEndpointResourceType::Interface;\n    modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendInterfaceRequestToGns\",\n        TraceLoggingValue(\"ModifyGuestDeviceSettingRequest : update interface properties [queued]\", \"GnsMessage\"),\n        TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n        TraceLoggingValue(interfaceConnected, \"connected\"),\n        TraceLoggingValue(interfaceMtu, \"mtu\"),\n        TraceLoggingValue(interfaceMetric, \"metric\"));\n\n    int linuxResultCode{};\n    // can safely capture by ref since we are waiting\n    const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {\n        return m_callbackForGnsMessage(LxGnsMessageModifyGuestDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);\n    });\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendInterfaceRequestToGns\",\n        TraceLoggingValue(\"ModifyGuestDeviceSettingRequest : update interface properties [completed]\", \"GnsMessage\"),\n        TraceLoggingHResult(hr, \"hr\"),\n        TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n\n    return hr;\n}\nCATCH_RETURN()\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ bool wsl::core::networking::WslMirroredNetworkManager::SyncIpStateWithLinux(NetworkEndpoint& endpoint)\n{\n    using hns::GuestEndpointResourceType;\n    using hns::IPAddress;\n    using hns::Route;\n    using TrackedIpStateSyncStatus::PendingAdd;\n    using TrackedIpStateSyncStatus::PendingRemoval;\n    using TrackedIpStateSyncStatus::PendingUpdate;\n    using TrackedIpStateSyncStatus::Synced;\n\n    bool syncSuccessful = true;\n\n    if (!endpoint.StateTracking->InitialSyncComplete)\n    {\n        // Tell GNS that we're ready to start pushing addresses and routes to Linux on this interface.\n        hns::InitialIpConfigurationNotification notification{};\n        notification.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);\n        WI_SetAllFlags(\n            notification.flags,\n            (hns::InitialIpConfigurationNotificationFlags::SkipPrimaryRoutingTableUpdate |\n             hns::InitialIpConfigurationNotificationFlags::SkipLoopbackRouteReset));\n\n        WSL_LOG(\n            \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n            TraceLoggingValue(\"InitialIpConfigurationNotification [queued]\", \"GnsMessage\"),\n            TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"));\n\n        int linuxResultCode{};\n        // can safely capture by ref since we are waiting\n        const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {\n            return m_callbackForGnsMessage(\n                LxGnsMessageInitialIpConfigurationNotification, ToJsonW(notification), GnsCallbackFlags::Wait, &linuxResultCode);\n        });\n\n        WSL_LOG(\n            \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n            TraceLoggingValue(\"InitialIpConfigurationNotification [completed]\", \"GnsMessage\"),\n            TraceLoggingHResult(hr, \"hr\"),\n            TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n    }\n\n    const auto makingIpInterfaceUpdate = endpoint.Network->PendingIPInterfaceUpdate;\n    // Linux may delete routes behind us when making interface, address, and route changes\n    // will track when to refresh v4 and v6 routes to ensure routes are still present after changes\n    // a few customers have seen this when we update temporary v6 addresses, for example\n    auto refreshAllRoutes = false;\n\n    // First: update Linux with any interface updates\n    // If IsHidden is set, then also indicate to Linux that the interface should be disconnected\n    if (endpoint.Network->PendingIPInterfaceUpdate || endpoint.Network->IsHidden)\n    {\n        const auto originalConnectValue = endpoint.Network->IsConnected;\n        if (endpoint.Network->IsHidden)\n        {\n            endpoint.Network->IsConnected = false;\n        }\n\n        if (FAILED(SendInterfaceRequestToGns(endpoint)))\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                TraceLoggingValue(\"Failed to update Interface properties\", \"message\"),\n                TraceLoggingValue(endpoint.Network->IsConnected, \"connected\"),\n                TraceLoggingValue(endpoint.Network->GetEffectiveMtu(), \"mtu\"),\n                TraceLoggingValue(endpoint.Network->GetMinimumMetric(), \"metric\"),\n                TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"));\n\n            syncSuccessful = false;\n            // interfaces are in an unknown state - push route updates in case Linux deleted routes behind us\n            refreshAllRoutes = true;\n        }\n        else\n        {\n            endpoint.Network->PendingIPInterfaceUpdate = false;\n        }\n\n        endpoint.Network->IsConnected = originalConnectValue;\n        if (originalConnectValue)\n        {\n            // interface potentially just moved from disconnected -> connected\n            // push route updates in case Linux deleted routes behind us\n            refreshAllRoutes = true;\n        }\n    }\n\n    // Second: update Linux with any addresses to remove\n    auto addressIt = endpoint.StateTracking->IpAddresses.begin();\n    while (addressIt != endpoint.StateTracking->IpAddresses.end())\n    {\n        auto address = addressIt++;\n\n        // if the interface is hidden, we need to remove addresses\n        // 'continue' to keep the address\n        if (!endpoint.Network->IsHidden)\n        {\n            if (endpoint.Network->IpAddresses.contains(address->Address))\n            {\n                if (address->SyncStatus == PendingRemoval)\n                {\n                    // This address was slated for removal but still exists on the host.  It should be kept around instead.\n                    // We'll send an update just in case.\n                    address->SyncStatus = PendingUpdate;\n                    address->SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;\n                }\n                else if (makingIpInterfaceUpdate)\n                {\n                    // if we pushed an interface update, ensure our addresses are up to date\n                    address->SyncStatus = PendingUpdate;\n                    address->SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;\n                }\n\n                continue;\n            }\n        }\n\n        // We found an address that should be removed from the guest.\n\n        if (address->SyncStatus != PendingRemoval)\n        {\n            // We've never attempted to remove this address before, so reset the sync retry count.\n            address->SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;\n        }\n        address->SyncStatus = PendingRemoval;\n\n        if (m_vmConfig.EnableHostAddressLoopback)\n        {\n            if (FAILED(SendLoopbackRequestToGns(endpoint, *address, hns::OperationType::Remove)))\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                    TraceLoggingValue(\"Failed to remove loopback routes for local address\", \"message\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                    TraceLoggingValue(address->Address.AddressString.c_str(), \"address\"),\n                    TraceLoggingValue(address->Address.PrefixLength, \"prefixLength\"));\n            }\n        }\n\n        if (FAILED(SendAddressRequestToGns(endpoint, *address, hns::ModifyRequestType::Remove)))\n        {\n            if (address->SyncRetryCount == 0)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                    TraceLoggingValue(\n                        \"Reached maximum retries to remove an address - we will no longer schedule the retry timer\", \"message\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                    TraceLoggingValue(address->Address.AddressString.c_str(), \"address\"),\n                    TraceLoggingValue(address->Address.PrefixLength, \"prefixLength\"));\n            }\n            else\n            {\n                syncSuccessful = false;\n            }\n        }\n        else\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                TraceLoggingValue(\"Address synced (removed)\", \"message\"),\n                TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                TraceLoggingValue(address->Address.AddressString.c_str(), \"address\"),\n                TraceLoggingValue(address->Address.PrefixLength, \"prefixLength\"));\n            endpoint.StateTracking->IpAddresses.erase(address);\n        }\n        // push route updates in case Linux deleted routes behind us after removing addresses\n        refreshAllRoutes = true;\n    }\n\n    // Third: update Linux with any routes to remove\n    auto routeIt = endpoint.StateTracking->Routes.begin();\n    while (routeIt != endpoint.StateTracking->Routes.end())\n    {\n        auto route = routeIt++;\n\n        // if the interface is hidden, we need to remove routes\n        // 'continue' to keep the route\n        if (!endpoint.Network->IsHidden)\n        {\n            if (endpoint.Network->Routes.contains(route->Route))\n            {\n                if (route->SyncStatus == PendingRemoval)\n                {\n                    // This route was slated for removal but still exists on the host.  It should be kept around instead.\n                    // We'll send an update just in case.\n                    route->SyncStatus = PendingUpdate;\n                    route->SyncRetryCount = TrackedRoute::MaxSyncRetryCount;\n                }\n                else if (makingIpInterfaceUpdate)\n                {\n                    // if we pushed an interface update, ensure our routes are up to date\n                    route->SyncStatus = PendingUpdate;\n                    route->SyncRetryCount = TrackedRoute::MaxSyncRetryCount;\n                }\n\n                continue;\n            }\n        }\n\n        // We found a route that should be removed from the guest.\n\n        if (route->SyncStatus != PendingRemoval)\n        {\n            // We've never attempted to remove this route before, so reset the sync retry count.\n            route->SyncRetryCount = TrackedRoute::MaxSyncRetryCount;\n        }\n        route->SyncStatus = PendingRemoval;\n\n        if (FAILED(SendRouteRequestToGns(endpoint, *route, hns::ModifyRequestType::Remove)))\n        {\n            if (route->SyncRetryCount == 0)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                    TraceLoggingValue(\n                        \"Reached maximum retries to remove a route - we will no longer schedule the retry timer\", \"message\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                    TraceLoggingValue(route->Route.DestinationPrefixString.c_str(), \"destinationPrefix\"),\n                    TraceLoggingValue(route->Route.DestinationPrefix.PrefixLength, \"prefixLength\"),\n                    TraceLoggingValue(route->Route.NextHopString.c_str(), \"nextHop\"),\n                    TraceLoggingValue(route->Route.Metric, \"metric\"));\n            }\n            else\n            {\n                syncSuccessful = false;\n            }\n        }\n        else\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                TraceLoggingValue(\"Route synced (removed) succeeded\", \"message\"),\n                TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                TraceLoggingValue(route->Route.DestinationPrefixString.c_str(), \"destinationPrefix\"),\n                TraceLoggingValue(route->Route.DestinationPrefix.PrefixLength, \"prefixLength\"),\n                TraceLoggingValue(route->Route.NextHopString.c_str(), \"nextHop\"),\n                TraceLoggingValue(route->Route.Metric, \"metric\"));\n            endpoint.StateTracking->Routes.erase(route);\n        }\n        // push route updates in case Linux deleted routes behind us after removing other various routes\n        refreshAllRoutes = true;\n    }\n\n    // Fourth: update Linux with any addresses to add\n    if (!endpoint.Network->IsHidden && endpoint.Network->IsConnected)\n    {\n        bool shouldRefreshAllAddresses = false;\n        for (auto& hostAddress : endpoint.Network->IpAddresses)\n        {\n            auto trackedAddress = endpoint.StateTracking->IpAddresses.emplace(TrackedIpAddress(hostAddress)).first;\n            // detect if previously sync'd addresses need to be updated\n            // this addresses issues we've seen where addresses were removed from Linux without our knowledge\n            shouldRefreshAllAddresses |= trackedAddress->SyncStatus == PendingAdd || trackedAddress->SyncStatus == PendingUpdate;\n        }\n\n        for (auto& trackedAddress : endpoint.StateTracking->IpAddresses)\n        {\n            std::optional<HRESULT> hr{};\n            switch (trackedAddress.SyncStatus)\n            {\n            case PendingAdd:\n            {\n                hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Add);\n                if (FAILED(hr.value()))\n                {\n                    // try to update it instead if it already exists\n                    hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Update);\n                }\n\n                if (SUCCEEDED(hr.value()) && m_vmConfig.EnableHostAddressLoopback)\n                {\n                    // Add a special loopback route so that loopback packets flow through the host and back.\n                    hr = SendLoopbackRequestToGns(endpoint, trackedAddress, hns::OperationType::Create);\n                    if (FAILED(hr.value()))\n                    {\n                        WSL_LOG(\n                            \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                            TraceLoggingValue(\"Failed to create loopback routes for local address\", \"message\"),\n                            TraceLoggingValue(wsl::core::networking::ToString(trackedAddress.SyncStatus), \"AddressSyncStatus\"),\n                            TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                            TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                            TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), \"address\"),\n                            TraceLoggingValue(trackedAddress.Address.PrefixLength, \"prefixLength\"));\n                    }\n                }\n                // push route updates in case Linux deleted routes behind us after refreshing addresses\n                refreshAllRoutes = true;\n                break;\n            }\n\n            case Synced:\n            {\n                auto fallThroughToUpdateAddress = shouldRefreshAllAddresses;\n\n                // Check if this address needs to be updated (i.e., its PreferredLifetime / DAD state needs to be updated)\n                auto hostAddress = endpoint.Network->IpAddresses.find(trackedAddress.Address);\n                if (hostAddress != endpoint.Network->IpAddresses.end())\n                {\n                    if (trackedAddress.Address.IsPreferred() != hostAddress->IsPreferred())\n                    {\n                        trackedAddress.Address.PreferredLifetime = hostAddress->PreferredLifetime;\n                        fallThroughToUpdateAddress = true;\n                    }\n                }\n\n                if (!fallThroughToUpdateAddress)\n                {\n                    break;\n                }\n\n                trackedAddress.SyncStatus = PendingUpdate;\n                trackedAddress.SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;\n                __fallthrough;\n            }\n\n            case PendingUpdate:\n                hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Update);\n                if (FAILED(hr.value()))\n                {\n                    // try to add it if it was removed in Linux\n                    hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Add);\n                }\n\n                if (SUCCEEDED(hr.value()) && m_vmConfig.EnableHostAddressLoopback)\n                {\n                    // Add a special loopback route so that loopback packets flow through the host and back.\n                    hr = SendLoopbackRequestToGns(endpoint, trackedAddress, hns::OperationType::Create);\n                    if (FAILED(hr.value()))\n                    {\n                        WSL_LOG(\n                            \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                            TraceLoggingValue(\"Failed to create loopback routes for local address\", \"message\"),\n                            TraceLoggingValue(wsl::core::networking::ToString(trackedAddress.SyncStatus), \"AddressSyncStatus\"),\n                            TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                            TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                            TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), \"address\"),\n                            TraceLoggingValue(trackedAddress.Address.PrefixLength, \"prefixLength\"));\n                    }\n                }\n\n                // push route updates in case Linux deleted routes behind us after refreshing addresses\n                refreshAllRoutes = true;\n                break;\n\n            case PendingRemoval:\n                // This address is still slated for removal, which we'll try again later.\n                continue;\n\n            default:\n                WI_ASSERT(false);\n                continue;\n            }\n\n            if (SUCCEEDED(hr.value_or(E_FAIL)))\n            {\n                trackedAddress.SyncStatus = Synced;\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                    TraceLoggingValue(\"Address synced\", \"message\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                    TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), \"address\"),\n                    TraceLoggingValue(trackedAddress.Address.PrefixLength, \"prefixLength\"));\n            }\n\n            if (trackedAddress.SyncRetryCount == 0)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                    TraceLoggingValue(\n                        \"Reached maximum retries to sync an address - we will no longer schedule the retry timer\", \"message\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                    TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), \"address\"),\n                    TraceLoggingValue(trackedAddress.Address.PrefixLength, \"prefixLength\"));\n            }\n\n            syncSuccessful &= (trackedAddress.SyncStatus == Synced || trackedAddress.SyncRetryCount == 0);\n        }\n    }\n    else\n    {\n        WSL_LOG(\n            \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n            TraceLoggingValue(\"Not adding addresses for hidden or disconnected interface\", \"message\"),\n            TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n            TraceLoggingValue(endpoint.Network->IsHidden, \"isHidden\"),\n            TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"));\n    }\n\n    // Fourth: update Linux with any routes to add\n    if (!endpoint.Network->IsHidden && endpoint.Network->IsConnected)\n    {\n        for (auto& hostRoute : endpoint.Network->Routes)\n        {\n            const auto trackedRoute = endpoint.StateTracking->Routes.emplace(TrackedRoute(hostRoute)).first;\n            // detect if previously sync'd routes need to be updated\n            // this addresses issues we've seen where routes were removed from Linux without our knowledge\n            // and routes couldn't be updated later because required routes, like the prefix route, wasn't there\n            refreshAllRoutes |= trackedRoute->SyncStatus == PendingAdd || trackedRoute->SyncStatus == PendingUpdate;\n        }\n\n        if (refreshAllRoutes)\n        {\n            WSL_LOG(\"WslMirroredNetworkManager::SyncIpStateWithLinux\", TraceLoggingValue(\"Refreshing all routes\", \"message\"));\n        }\n\n        for (auto& trackedRoute : endpoint.StateTracking->Routes)\n        {\n            std::optional<HRESULT> hr{};\n            switch (trackedRoute.SyncStatus)\n            {\n            case PendingAdd:\n                hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Add);\n                if (FAILED(hr.value()))\n                {\n                    // try to update it instead if it already exists\n                    hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Update);\n                }\n                break;\n\n            case Synced:\n                if (refreshAllRoutes)\n                {\n                    hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Update);\n                    if (FAILED(hr.value()))\n                    {\n                        // try to add it\n                        hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Add);\n                    }\n                    if (FAILED(hr.value()))\n                    {\n                        trackedRoute.SyncStatus = PendingUpdate;\n                        trackedRoute.SyncRetryCount = TrackedRoute::MaxSyncRetryCount;\n                    }\n                }\n                break;\n\n            case PendingUpdate:\n                hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Update);\n                if (FAILED(hr.value()))\n                {\n                    // try to add it\n                    hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Add);\n                }\n                break;\n\n            case PendingRemoval:\n                // This route is still slated for removal, which we'll try again later.\n                continue;\n\n            default:\n                WI_ASSERT(false);\n                continue;\n            }\n\n            if (SUCCEEDED(hr.value_or(E_FAIL)))\n            {\n                trackedRoute.SyncStatus = Synced;\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                    TraceLoggingValue(\"Route synced\", \"message\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                    TraceLoggingValue(trackedRoute.Route.DestinationPrefixString.c_str(), \"destinationPrefix\"),\n                    TraceLoggingValue(trackedRoute.Route.DestinationPrefix.PrefixLength, \"prefixLength\"),\n                    TraceLoggingValue(trackedRoute.Route.NextHopString.c_str(), \"nextHop\"),\n                    TraceLoggingValue(trackedRoute.Route.Metric, \"metric\"));\n            }\n\n            if (trackedRoute.SyncRetryCount == 0)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n                    TraceLoggingValue(\n                        \"Reached maximum amount of retries to sync a route. This can happen if the route's next hop is not \"\n                        \"reachable, as Linux does not allow such routes to be plumbed. Failure to sync the route will no longer \"\n                        \"schedule the retry timer.\",\n                        \"message\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"),\n                    TraceLoggingValue(trackedRoute.Route.DestinationPrefixString.c_str(), \"destinationPrefix\"),\n                    TraceLoggingValue(trackedRoute.Route.DestinationPrefix.PrefixLength, \"prefixLength\"),\n                    TraceLoggingValue(trackedRoute.Route.NextHopString.c_str(), \"nextHop\"),\n                    TraceLoggingValue(trackedRoute.Route.Metric, \"metric\"));\n            }\n\n            syncSuccessful &= (trackedRoute.SyncStatus == Synced || trackedRoute.SyncRetryCount == 0);\n        }\n    }\n    else\n    {\n        WSL_LOG(\n            \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n            TraceLoggingValue(\"Not adding routes for hidden or disconnected interface\", \"message\"),\n            TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n            TraceLoggingValue(endpoint.Network->IsHidden, \"isHidden\"),\n            TraceLoggingValue(endpoint.Network->IsConnected, \"isConnected\"));\n    }\n\n    // Fifth: update Linux with updated DNS information\n    if (m_dnsInfo != m_trackedDnsInfo)\n    {\n        if (FAILED(SendDnsRequestToGns(endpoint, m_dnsInfo, hns::ModifyRequestType::Update)))\n        {\n            syncSuccessful = false;\n        }\n        else\n        {\n            m_trackedDnsInfo = m_dnsInfo;\n        }\n    }\n\n    endpoint.StateTracking->InitialSyncComplete = true;\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SyncIpStateWithLinux\",\n        TraceLoggingValue(endpoint.InterfaceGuid, \"InterfaceGuid\"),\n        TraceLoggingValue(syncSuccessful, \"syncSuccessful\"));\n    return syncSuccessful;\n}\n\n// We must determine what IP changes to push to Linux\n_Requires_lock_held_(m_networkLock)\nvoid wsl::core::networking::WslMirroredNetworkManager::UpdateAllEndpointsImpl(UpdateEndpointFlag updateFlag, _In_ PCSTR callingSource) noexcept\ntry\n{\n    static long s_updateAllEndpointsCounter = 0;\n    const auto instanceCounter = InterlockedIncrement(&s_updateAllEndpointsCounter);\n\n    if (updateFlag == UpdateEndpointFlag::None)\n    {\n        WSL_LOG(\n            \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n            TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n            TraceLoggingValue(callingSource, \"callingSource\"),\n            TraceLoggingValue(\"None [exiting early]\", \"updateFlag\"));\n        return;\n    }\n\n    if (updateFlag == UpdateEndpointFlag::Default)\n    {\n        const auto currentTickCount = GetTickCount64();\n        const auto timeFromLastUpdate = currentTickCount - m_lastUpdateAllEndpointsDefaultTime;\n\n        if (timeFromLastUpdate >= m_debounceUpdateAllEndpointsTimerMs)\n        {\n            // It's been >= m_debounceUpdateAllEndpointsTimerMs since we last attempted an update, so go ahead and process it.\n            WSL_LOG(\n                \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n                TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n                TraceLoggingValue(callingSource, \"callingSource\"),\n                TraceLoggingValue(wsl::core::networking::ToString(updateFlag), \"updateFlag\"),\n                TraceLoggingValue(\"Debounce time reset - continuing update\", \"state\"),\n                TraceLoggingValue(timeFromLastUpdate, \"timeFromLastUpdate\"),\n                TraceLoggingValue(m_debounceUpdateAllEndpointsTimerMs, \"m_debounceUpdateAllEndpointsTimerMs\"));\n            m_lastUpdateAllEndpointsDefaultTime = currentTickCount;\n        }\n        else if (!m_IsDebounceUpdateAllEndpointsDefaultTimerSet)\n        {\n            // The debounce timer is not already scheduled, so schedule it.\n            WSL_LOG(\n                \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n                TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n                TraceLoggingValue(callingSource, \"callingSource\"),\n                TraceLoggingValue(wsl::core::networking::ToString(updateFlag), \"updateFlag\"),\n                TraceLoggingValue(\"Debouncing Notification - setting timer\", \"state\"),\n                TraceLoggingValue(timeFromLastUpdate, \"timeFromLastUpdate\"),\n                TraceLoggingValue(m_debounceUpdateAllEndpointsTimerMs, \"m_debounceUpdateAllEndpointsTimerMs\"));\n\n            // Set the due time just past the debounce timer duration, relative to the last update time.\n            m_IsDebounceUpdateAllEndpointsDefaultTimerSet = true;\n            FILETIME dueTime = wil::filetime::from_int64(static_cast<ULONGLONG>(\n                -1 * (wil::filetime_duration::one_millisecond * (20 + m_debounceUpdateAllEndpointsTimerMs - timeFromLastUpdate))));\n            SetThreadpoolTimer(m_debounceUpdateAllEndpointsDefaultTimer.get(), &dueTime, 0, 0);\n            return;\n        }\n        else\n        {\n            // The debounce timer is already scheduled, so ignore this update.\n            WSL_LOG(\n                \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n                TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n                TraceLoggingValue(callingSource, \"callingSource\"),\n                TraceLoggingValue(wsl::core::networking::ToString(updateFlag), \"updateFlag\"),\n                TraceLoggingValue(\"Debouncing Notification - timer already set\", \"state\"),\n                TraceLoggingValue(timeFromLastUpdate, \"timeFromLastUpdate\"),\n                TraceLoggingValue(m_debounceUpdateAllEndpointsTimerMs, \"m_debounceUpdateAllEndpointsTimerMs\"));\n            return;\n        }\n    }\n    else\n    {\n        WSL_LOG(\n            \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n            TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n            TraceLoggingValue(callingSource, \"callingSource\"),\n            TraceLoggingValue(wsl::core::networking::ToString(updateFlag), \"updateFlag\"));\n    }\n\n    m_latestHnsStatus = HnsStatus::NetworkConnectedWithHnsNotification;\n\n    // Update IP properties on all interfaces on the host\n    // N.B. We must process the DisableDefaultRoutes property of each host interface before we\n    // process the host routes, as this property might impact the set of routes we choose to mirror.\n    ProcessConnectivityChange();\n    ProcessInterfaceChange();\n    ProcessIpAddressChange();\n    ProcessRouteChange();\n    ProcessDNSChange();\n\n    // Push IP state to Linux\n    bool syncSuccessful = true;\n    std::set<GUID, wsl::windows::common::helpers::GuidLess> mirroredConnectedInterfaces;\n    for (auto& endpoint : m_networkEndpoints)\n    {\n        if (IsInterfaceIndexOfGelnic(endpoint.Network->InterfaceIndex))\n        {\n            continue;\n        }\n\n        // there may be more mirrored interfaces than 'host-connected' interfaces\n        // e.g. network adapters which are disconnected or hidden\n        // track all host-connected interfaces that have been successfully mirrored\n        if (m_hostConnectedInterfaces.contains(endpoint.InterfaceGuid))\n        {\n            if (endpoint.Network->IsHidden)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n                    TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n                    TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                    TraceLoggingValue(\n                        \"Resetting IsHidden to false and PendingIPInterfaceUpdate to true to update the Interface\", \"message\"));\n                endpoint.Network->IsHidden = false;\n                // setting PendingIPInterfaceUpdate to tell SyncIpStateWithLinux to update the Interface state\n                endpoint.Network->PendingIPInterfaceUpdate = true;\n            }\n            mirroredConnectedInterfaces.insert(endpoint.InterfaceGuid);\n        }\n        else\n        {\n            // if the host has hidden the interface that was mirrored by HNS\n            // ensure the interface is not connected in Linux\n            // we are deliberately overriding the endpoint state in this case\n            WSL_LOG(\n                \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n                TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n                TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                TraceLoggingValue(\n                    \"Setting IsHidden to true - this interface is hidden on the host and must not be connected in the container\",\n                    \"message\"));\n            endpoint.Network->IsHidden = true;\n            // setting PendingIPInterfaceUpdate to tell SyncIpStateWithLinux to update the Interface state\n            endpoint.Network->PendingIPInterfaceUpdate = true;\n        }\n\n        if (!SyncIpStateWithLinux(endpoint))\n        {\n            // We failed to sync some bit of state.  Let's schedule a timer to try again in a bit.\n            WSL_LOG(\n                \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n                TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n                TraceLoggingValue(endpoint.InterfaceGuid, \"interfaceGuid\"),\n                TraceLoggingValue(\"Some IP state did not sync with Linux - scheduling a retry attempt\", \"message\"),\n                TraceLoggingValue(m_linuxIpStateRetryDebounceTimerMilliseconds, \"m_linuxIpStateRetryDebounceTimerMilliseconds\"));\n\n            FILETIME dueTime = wil::filetime::from_int64(\n                static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_linuxIpStateRetryDebounceTimerMilliseconds));\n\n            SetThreadpoolTimer(m_retryLinuxIpStateSyncTimer.get(), &dueTime, 0, 1000);\n\n            if (syncSuccessful)\n            {\n                // set to false the first pass through the for loop\n                syncSuccessful = false;\n\n                // Increase the IP state retry timer according to exponential back-off, capping at a maximum value.\n                m_linuxIpStateRetryDebounceTimerMilliseconds =\n                    std::min(m_linuxIpStateRetryDebounceTimerMilliseconds * 2, m_linuxIpStateRetryDebounceTimerMaxMilliseconds);\n            }\n        }\n    }\n\n    // If all of the following occurs, then we have entered the goal state.\n    // 1) Mirrored all usable host interfaces\n    // 2) Successfully sync'd all settings on those interfaces\n    // 3) Not currently in the goal state\n    if (syncSuccessful)\n    {\n        // Reset the IP state retry timer back to the minimum value.\n        m_linuxIpStateRetryDebounceTimerMilliseconds = m_linuxIpStateRetryDebounceTimerMinMilliseconds;\n\n        // if any host-connected interfaces are not yet mirrored, don't indicate we are in sync\n        bool hnsMirroredInSyncWithHost = mirroredConnectedInterfaces == m_hostConnectedInterfaces;\n        if (mirroredConnectedInterfaces != m_hostConnectedInterfaces)\n        {\n            // mirroredConnectedInterfaces won't equal m_hostConnectedInterfaces when:\n            // - there are hidden host interfaces\n            //   i.e., interfaces are in m_networkEndpoints but not in m_hostConnectedInterfaces\n            // - when HNS hasn't yet mirrored a connected host interface\n            //   i.e. interfaces are in m_hostConnectedInterfaces but not in m_networkEndpoints\n            //\n            // if HNS has not yet mirrored a host interface, we should not indicate we are in sync\n            // but if hidden interfaces should not block being in sync\n\n            // reset to true until we see if HNS hasn't yet mirrored a connected host interface\n            hnsMirroredInSyncWithHost = true;\n            // verify that HNS has mirrored all host-connected interfaces\n            for (const auto& connectedHostInterface : m_hostConnectedInterfaces)\n            {\n                bool interfaceMatched = false;\n                for (const auto& hnsEndpoint : m_networkEndpoints)\n                {\n                    if (connectedHostInterface == hnsEndpoint.InterfaceGuid)\n                    {\n                        interfaceMatched = true;\n                        break;\n                    }\n                }\n\n                if (!interfaceMatched)\n                {\n                    WSL_LOG(\n                        \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n                        TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n                        TraceLoggingValue(\"HNS has not yet mirrored a host connected Interface\", \"message\"),\n                        TraceLoggingValue(connectedHostInterface, \"interfaceGuid\"));\n                    hnsMirroredInSyncWithHost = false;\n                }\n            }\n        }\n\n        if (hnsMirroredInSyncWithHost && !m_inMirroredGoalState.is_signaled())\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::UpdateAllEndpointsImpl\",\n                TraceLoggingValue(instanceCounter, \"instanceCounter\"),\n                TraceLoggingValue(\"Reached goal state\", \"message\"));\n            m_inMirroredGoalState.SetEvent();\n\n            // Telemetry to see how long it takes to reach the mirrored goal state for the first time.\n            if (std::chrono::duration_cast<std::chrono::milliseconds>(m_initialMirroredGoalStateEndTime.time_since_epoch()) ==\n                std::chrono::milliseconds::zero())\n            {\n                m_initialMirroredGoalStateEndTime = std::chrono::steady_clock::now();\n\n                const auto waitTime = m_initialMirroredGoalStateEndTime - m_objectCreationTime;\n                WSL_LOG(\n                    \"WslMirroringInitialGoalStateWait\",\n                    TraceLoggingValue((std::chrono::duration_cast<std::chrono::milliseconds>(waitTime)).count(), \"waitTimeMs\"),\n                    TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                    TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                    TraceLoggingValue(m_vmConfig.EnableAutoProxy, \"AutoProxyFeatureEnabled\")); // the feature is enabled, but we don't know if proxy settings are actually configured\n            }\n        }\n    }\n}\nCATCH_LOG()\n\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::EnumerateNetworks(_Out_ std::vector<GUID>& NetworkIds) const noexcept\ntry\n{\n    auto lock = m_networkLock.lock_shared();\n    WI_ASSERT(m_state == State::Started);\n    if (m_state == State::Stopped)\n    {\n        return E_ABORT;\n    }\n\n    NetworkIds = EnumerateMirroredNetworks();\n    return S_OK;\n}\nCATCH_RETURN()\n\nvoid wsl::core::networking::WslMirroredNetworkManager::AddEndpoint(NetworkEndpoint&& newEndpoint, hns::HNSEndpoint&& endpointProperties) noexcept\n{\n    const auto lock = m_networkLock.lock_exclusive();\n    if (m_state == State::Stopped)\n    {\n        return;\n    }\n\n    constexpr uint32_t defaultRetryCount = 0ul;\n    AddEndpointImpl({std::move(newEndpoint), std::move(endpointProperties), defaultRetryCount});\n}\n\n_Requires_lock_held_(m_networkLock)\nvoid wsl::core::networking::WslMirroredNetworkManager::AddEndpointImpl(EndpointTracking&& endpointTrackingObject) noexcept\n{\n    PCSTR executionStep = \"\";\n    try\n    {\n        // Hot-add the network endpoint to the utility VM.\n        hcs::NetworkAdapter networkEndpoint{};\n\n        networkEndpoint.MacAddress = wsl::shared::string::ParseMacAddress(endpointTrackingObject.m_hnsEndpoint.MacAddress);\n\n        // Set the instance id to the mirrored interfaceGuid so HNS -> netvsc can optimally use the same vmNIC constructs when the InterfaceGuid is the same\n        hcs::ModifySettingRequest<hcs::NetworkAdapter> addEndpointRequest{};\n        addEndpointRequest.ResourcePath =\n            c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(endpointTrackingObject.m_networkEndpoint.InterfaceGuid);\n        addEndpointRequest.RequestType = hcs::ModifyRequestType::Add;\n        addEndpointRequest.Settings.EndpointId = endpointTrackingObject.m_hnsEndpoint.ID;\n        addEndpointRequest.Settings.InstanceId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;\n\n        addEndpointRequest.Settings.MacAddress = wsl::shared::string::ParseMacAddress(endpointTrackingObject.m_hnsEndpoint.MacAddress);\n        auto addEndpointRequestString = wsl::shared::ToJsonW(addEndpointRequest);\n\n        WSL_LOG(\n            \"WslMirroredNetworkManager::AddEndpoint [Creating HCS endpoint]\",\n            TraceLoggingValue(addEndpointRequestString.c_str(), \"networkRequestString\"));\n\n        executionStep = \"AddHcsEndpoint\";\n        auto hr = m_hnsQueue.submit_and_wait([&] {\n            // RetryWithTimeout throws if fails every attempt - which is caught and returned by m_gnsMessageQueue\n            auto retryCount = 0ul;\n            return wsl::shared::retry::RetryWithTimeout<HRESULT>(\n                [&] {\n                    const auto retryHr = wil::ResultFromException(\n                        [&] { wsl::windows::common::hcs::ModifyComputeSystem(m_hcsSystem, addEndpointRequestString.c_str()); });\n\n                    WSL_LOG(\n                        \"WslMirroredNetworkManager::AddEndpoint [ModifyComputeSystem(ModifyRequestType::Add)]\",\n                        TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, \"endpointId\"),\n                        TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"instanceId\"),\n                        TraceLoggingValue(retryHr, \"retryHr\"),\n                        TraceLoggingValue(retryCount, \"retryCount\"));\n\n                    ++retryCount;\n                    return THROW_IF_FAILED(retryHr);\n                },\n                wsl::core::networking::AddEndpointRetryPeriod,\n                wsl::core::networking::AddEndpointRetryTimeout,\n                wsl::core::networking::AddEndpointRetryPredicate);\n        });\n\n        if (hr == HCN_E_ENDPOINT_ALREADY_ATTACHED)\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::AddEndpoint [Adding the endpoint returned HCN_E_ENDPOINT_ALREADY_ATTACHED - \"\n                \"continuing]\",\n                TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, \"endpointId\"));\n\n            hr = S_OK;\n        }\n        else if (FAILED(hr))\n        {\n            THROW_HR(hr);\n        }\n\n        auto removeEndpointOnError = wil::scope_exit([&] {\n            // try to delete the endpoint in HCS if anything failed\n            // Set the instance id to the mirrored interfaceGuid so HNS -> netvsc can optimally use the same vmNIC constructs when the InterfaceGuid is the same\n            hcs::ModifySettingRequest<hcs::NetworkAdapter> networkRequest{};\n            networkRequest.ResourcePath =\n                c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(endpointTrackingObject.m_networkEndpoint.InterfaceGuid);\n            networkRequest.RequestType = hcs::ModifyRequestType::Remove;\n            networkRequest.Settings.EndpointId = endpointTrackingObject.m_hnsEndpoint.ID;\n            networkRequest.Settings.InstanceId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;\n\n            const auto networkRequestString = wsl::shared::ToJsonW(std::move(networkRequest));\n\n            // capturing by ref because we wait for the workitem to complete\n            const auto modifyResult = m_hnsQueue.submit_and_wait([&] {\n                windows::common::hcs::ModifyComputeSystem(m_hcsSystem, networkRequestString.c_str());\n                return S_OK; // ModifyComputeSystem throws errors, caught by m_gnsMessageQueue\n            });\n            WSL_LOG(\n                \"WslMirroredNetworkManager::AddEndpoint [Removing the HCS mirrored endpoint after failure to Add]\",\n                TraceLoggingHResult(modifyResult, \"hr\"),\n                TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, \"endpointId\"),\n                TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"instanceId\"));\n\n            if (FAILED(modifyResult))\n            {\n                WSL_LOG(\n                    \"AddMirroredEndpointFailed\",\n                    TraceLoggingHResult(modifyResult, \"result\"),\n                    TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(\n                        endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,\n                        \"InterfaceType\"),\n                    TraceLoggingValue(\"RemoveHcsEndpointOnFailure\", \"executionStep\"),\n                    TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                    TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                    TraceLoggingValue(m_vmConfig.EnableAutoProxy, \"AutoProxyFeatureEnabled\"), // the feature is enabled, but we don't know if proxy settings are actually configured\n                    TraceLoggingValue(endpointTrackingObject.m_retryCount, \"retryCount\"));\n            }\n\n            // Inform the parent class to remove the endpoint object from GNS registration since we couldn't add the endpoint\n        });\n\n        // Refreshing the endpoint causes it to reach the GNSInterfaceState::Synchronized state in HNS\n        // which is required to receive notifications.\n        // When HcnModifyEndpoint returns, all GNS notifications have been processed and the interface is fully configured.\n        hns::ModifyGuestEndpointSettingRequest<void> refreshRequest{};\n        refreshRequest.RequestType = hns::ModifyRequestType::Refresh;\n        refreshRequest.ResourceType = hns::GuestEndpointResourceType::Port;\n\n        const auto refreshEndpointRequestString = ToJsonW(refreshRequest);\n        WSL_LOG(\n            \"WslMirroredNetworkManager::AddEndpoint [Synchronizing HNS state]\",\n            TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, \"endpointId\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"instanceId\"));\n\n        executionStep = \"RefreshHcsEndpoint\";\n        THROW_IF_FAILED(m_hnsQueue.submit_and_wait([&] {\n            // Don't retry if HcnModifyEndpoint fails with HCN_E_ENDPOINT_NOT_FOUND which indicates that the underlying network object was deleted.\n            constexpr auto retryPredicate = [] { return wil::ResultFromCaughtException() != HCN_E_ENDPOINT_NOT_FOUND; };\n            auto retryCount = 0ul;\n            // RetryWithTimeout throws if fails every attempt - which is caught and returned by m_hnsQueue\n            return wsl::shared::retry::RetryWithTimeout<HRESULT>(\n                [&] {\n                    wil::unique_cotaskmem_string error;\n                    const auto retryHr = HcnModifyEndpoint(\n                        endpointTrackingObject.m_networkEndpoint.Endpoint.get(), refreshEndpointRequestString.c_str(), &error);\n\n                    WSL_LOG(\n                        \"WslMirroredNetworkManager::AddEndpoint [HcnModifyEndpoint(ModifyRequestType::Refresh)]\",\n                        TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, \"endpointId\"),\n                        TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"instanceId\"),\n                        TraceLoggingValue(refreshEndpointRequestString.c_str(), \"json\"),\n                        TraceLoggingHResult(retryHr, \"retryHr\"),\n                        TraceLoggingValue(error.is_valid() ? error.get() : L\"\", \"errorString\"),\n                        TraceLoggingValue(retryCount, \"retryCount\"));\n\n                    ++retryCount;\n                    return THROW_IF_FAILED(retryHr);\n                },\n                wsl::core::networking::AddEndpointRetryPeriod,\n                wsl::core::networking::AddEndpointRetryTimeout,\n                retryPredicate);\n        }));\n\n        // Notify GNS of the new adapter\n        hns::VmNicCreatedNotification newAdapterNotification;\n        // Set the adapterId == instanceId of the created Endpoint == the mirrored interfaceGuid\n        newAdapterNotification.adapterId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;\n\n        constexpr auto type = GnsMessageType(newAdapterNotification);\n        const auto jsonString = ToJsonW(newAdapterNotification);\n        WSL_LOG(\n            \"WslMirroredNetworkManager::AddEndpoint\",\n            TraceLoggingValue(\"VmNicCreatedNotification [queued]\", \"GnsMessage\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"adapterId\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.NetworkId, \"networkId\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, \"endpointId\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"interfaceGuid\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.Network->InterfaceIndex, \"interfaceIndex\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.Network->InterfaceType, \"interfaceType\"),\n            TraceLoggingValue(jsonString.c_str(), \"jsonString\"));\n\n        int linuxResultCode{};\n        // can safely capture by ref since we are waiting\n        hr = m_gnsCallbackQueue.submit_and_wait(\n            [&] { return m_callbackForGnsMessage(type, jsonString, GnsCallbackFlags::Wait, &linuxResultCode); });\n        WSL_LOG(\n            \"WslMirroredNetworkManager::AddEndpoint\",\n            TraceLoggingValue(\"VmNicCreatedNotification [completed]\", \"GnsMessage\"),\n            TraceLoggingHResult(hr, \"hr\"),\n            TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n\n        // Send the endpoint state (link status) to gns.\n        // Also set the loopback device name to allow configuration by name.\n        //\n        // Temporarily set endpoint ID and PortFriendlyName to what LxGnsMessageInterfaceConfiguration expects.\n        GUID originalEndpointId = endpointTrackingObject.m_hnsEndpoint.ID;\n        std::wstring originalPortFriendlyName = endpointTrackingObject.m_hnsEndpoint.PortFriendlyName;\n        endpointTrackingObject.m_hnsEndpoint.ID = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;\n        if (IsInterfaceIndexOfGelnic(endpointTrackingObject.m_networkEndpoint.Network->InterfaceIndex))\n        {\n            endpointTrackingObject.m_hnsEndpoint.PortFriendlyName = c_loopbackDeviceName;\n        }\n        WI_ASSERT(endpointTrackingObject.m_hnsEndpoint.IPAddress.empty());\n\n        executionStep = \"SendEndpointStateToGns\";\n        linuxResultCode = {};\n        // can safely capture by ref since we are waiting\n        hr = m_gnsCallbackQueue.submit_and_wait([&] {\n            return m_callbackForGnsMessage(\n                LxGnsMessageInterfaceConfiguration, ToJsonW(endpointTrackingObject.m_hnsEndpoint), GnsCallbackFlags::Wait, &linuxResultCode);\n        });\n        // restore the Endpoint ID GUID and PortFriendlyName\n        endpointTrackingObject.m_hnsEndpoint.ID = originalEndpointId;\n        endpointTrackingObject.m_hnsEndpoint.PortFriendlyName = originalPortFriendlyName;\n        WSL_LOG(\n            \"WslMirroredNetworkManager::AddEndpoint [Update link status]\",\n            TraceLoggingHResult(hr, \"hr\"),\n            TraceLoggingValue(linuxResultCode, \"linuxResultCode\"),\n            TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, \"endpointId\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"instanceId\"),\n            TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.PortFriendlyName.c_str(), \"PortFriendlyName\"));\n        THROW_IF_FAILED(hr);\n\n        endpointTrackingObject.m_networkEndpoint.Network->MacAddress = endpointTrackingObject.m_hnsEndpoint.MacAddress;\n\n        if (IsInterfaceIndexOfGelnic(endpointTrackingObject.m_networkEndpoint.Network->InterfaceIndex))\n        {\n            // Create loopback device in the container which will also set up loopback communication with the host.\n            hns::CreateDeviceRequest createLoopbackDevice;\n            createLoopbackDevice.deviceName = c_loopbackDeviceName;\n            createLoopbackDevice.type = hns::DeviceType::Loopback;\n            // Set the lowerEdgeAdapterId == the InstanceId of the Endpoint == the mirrored interfaceGuid\n            createLoopbackDevice.lowerEdgeAdapterId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;\n\n            WSL_LOG(\n                \"WslMirroredNetworkManager::AddEndpoint\",\n                TraceLoggingValue(\"CreateDeviceRequest - loopback [queued]\", \"GnsMessage\"),\n                TraceLoggingValue(c_loopbackDeviceName, \"deviceName\"),\n                TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"lowerEdgeAdapterId\"),\n                TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, \"endpointId\"),\n                TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"interfaceGuid\"));\n            constexpr auto gnsMessageType = GnsMessageType(createLoopbackDevice);\n\n            linuxResultCode = {};\n            // can safely capture by ref since we are waiting\n            hr = m_gnsCallbackQueue.submit_and_wait([&] {\n                return m_callbackForGnsMessage(gnsMessageType, ToJsonW(createLoopbackDevice), GnsCallbackFlags::Wait, &linuxResultCode);\n            });\n            WSL_LOG(\n                \"WslMirroredNetworkManager::AddEndpoint\",\n                TraceLoggingValue(\"CreateDeviceRequest - loopback [completed]\", \"GnsMessage\"),\n                TraceLoggingHResult(hr, \"hr\"),\n                TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n        }\n        else\n        {\n            // Perform per-interface configuration of net filter rules.\n            hns::InterfaceNetFilterRequest interfaceNetFilterRequest;\n            interfaceNetFilterRequest.targetDeviceName =\n                wsl::shared::string::GuidToString<wchar_t>(endpointTrackingObject.m_networkEndpoint.InterfaceGuid);\n            interfaceNetFilterRequest.operation = hns::OperationType::Create;\n            interfaceNetFilterRequest.ephemeralPortRangeStart = m_ephemeralPortRange.first;\n            interfaceNetFilterRequest.ephemeralPortRangeEnd = m_ephemeralPortRange.second;\n\n            linuxResultCode = {};\n            // can safely capture by ref since we are waiting\n            hr = m_gnsCallbackQueue.submit_and_wait([&] {\n                return m_callbackForGnsMessage(\n                    LxGnsMessageInterfaceNetFilter, ToJsonW(interfaceNetFilterRequest), GnsCallbackFlags::Wait, &linuxResultCode);\n            });\n            LOG_IF_FAILED(hr);\n            WSL_LOG(\n                \"WslMirroredNetworkManager::AddEndpoint [InterfaceNetFilterRequest]\",\n                TraceLoggingHResult(hr, \"hr\"),\n                TraceLoggingValue(linuxResultCode, \"linuxResultCode\"),\n                TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, \"endpointId\"),\n                TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"interfaceGuid\"),\n                TraceLoggingValue(m_ephemeralPortRange.first, \"ephemeralPortRangeStart\"),\n                TraceLoggingValue(m_ephemeralPortRange.second, \"ephemeralPortRangeEnd\"));\n        }\n\n        // WSL will track state for every endpoint (interface)\n        endpointTrackingObject.m_networkEndpoint.StateTracking.emplace(m_vmConfig.FirewallConfig.VmCreatorId);\n        endpointTrackingObject.m_networkEndpoint.StateTracking->SeedInitialState(*endpointTrackingObject.m_networkEndpoint.Network);\n\n        m_networkEndpoints.emplace_back(std::move(endpointTrackingObject.m_networkEndpoint));\n\n        // successfully tracked the added endpoint - release the scope guards\n        removeEndpointOnError.release();\n\n        // after added, we must determine what is the preferred interface to indicate to bond to connect\n        UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, \"AddEndpoint\");\n\n        WSL_LOG(\n            \"CreateMirroredEndpointEnd\",\n            TraceLoggingHResult(S_OK, \"result\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"InterfaceGuid\"),\n            TraceLoggingValue(\n                endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,\n                \"InterfaceType\"),\n            TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n            TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n            TraceLoggingValue(m_vmConfig.EnableAutoProxy, \"AutoProxyFeatureEnabled\"), // the feature is enabled, but we don't know if proxy settings are actually configured\n            TraceLoggingValue(endpointTrackingObject.m_retryCount, \"retryCount\"));\n    }\n    catch (...)\n    {\n        const auto hr = wil::ResultFromCaughtException();\n\n        WSL_LOG(\n            \"AddMirroredEndpointFailed\",\n            TraceLoggingHResult(hr, \"result\"),\n            TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"InterfaceGuid\"),\n            TraceLoggingValue(\n                endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,\n                \"InterfaceType\"),\n            TraceLoggingValue(executionStep, \"executionStep\"),\n            TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n            TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n            TraceLoggingValue(m_vmConfig.EnableAutoProxy, \"AutoProxyFeatureEnabled\"), // the feature is enabled, but we don't know if proxy settings are actually configured\n            TraceLoggingValue(endpointTrackingObject.m_retryCount, \"retryCount\"));\n\n        if (hr == HCN_E_ENDPOINT_NOT_FOUND)\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::AddEndpoint\",\n                TraceLoggingValue(\"HCN/HCS returned HCN_E_ENDPOINT_NOT_FOUND - not retrying\", \"GnsMessage\"),\n                TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.NetworkId, \"networkId\"),\n                TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, \"endpointId\"),\n                TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"interfaceGuid\"),\n                TraceLoggingHResult(hr, \"hr\"));\n            return;\n        }\n\n        try\n        {\n            ++endpointTrackingObject.m_retryCount;\n\n            if (endpointTrackingObject.m_retryCount > m_maxAddEndpointRetryCount)\n            {\n                WSL_LOG(\n                    \"BlockedNetworkEndpoint\",\n                    TraceLoggingValue(\"WslMirroredNetworkManager::AddEndpoint\", \"where\"),\n                    TraceLoggingHResult(hr, \"result\"),\n                    TraceLoggingValue(executionStep, \"executionStep\"),\n                    TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, \"InterfaceGuid\"),\n                    TraceLoggingValue(\n                        endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,\n                        \"InterfaceType\"),\n                    TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                    TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                    TraceLoggingValue(m_vmConfig.EnableAutoProxy, \"AutoProxyFeatureEnabled\")); // the feature is enabled, but we don't know if proxy settings are actually configured\n\n                // we now need to guarantee that Update* gets called again - but we can't do it from this thread\n                // will update our debounce-timer to fire soon to invoke Update - which will trigger the Blocked* path\n                // since we are now blocked on this interface\n                FILETIME dueTime = wil::filetime::from_int64(\n                    static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_debounceUpdateAllEndpointsTimerMs));\n                SetThreadpoolTimer(m_debounceUpdateAllEndpointsDefaultTimer.get(), &dueTime, 0, 0);\n                return;\n            }\n\n            m_failedEndpointProperties.emplace_back(\n                std::move(endpointTrackingObject.m_networkEndpoint),\n                std::move(endpointTrackingObject.m_hnsEndpoint),\n                endpointTrackingObject.m_retryCount);\n\n            FILETIME dueTime = wil::filetime::from_int64(\n                static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_debounceCreateEndpointFailureTimerMs));\n            SetThreadpoolTimer(m_debounceCreateEndpointFailureTimer.get(), &dueTime, 0, 0);\n        }\n        CATCH_LOG()\n    }\n}\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::RemoveEndpoint(const GUID& endpointId) noexcept\ntry\n{\n    const auto removedFailedEndpointCount = std::erase_if(m_failedEndpointProperties, [&](const auto& endpointTracking) {\n        return endpointTracking.m_networkEndpoint.EndpointId == endpointId;\n    });\n    if (removedFailedEndpointCount > 0)\n    {\n        WSL_LOG(\n            \"WslMirroredNetworkManager::RemoveEndpoint - Endpoint removed from m_failedEndpointProperties\",\n            TraceLoggingValue(endpointId, \"endpointId\"));\n    }\n\n    WSL_LOG(\"WslMirroredNetworkManager::RemoveEndpoint\", TraceLoggingValue(endpointId, \"endpointId\"));\n\n    std::vector<NetworkEndpoint>::const_iterator foundEndpoint;\n\n    try\n    {\n        foundEndpoint = std::find_if(m_networkEndpoints.cbegin(), m_networkEndpoints.cend(), [&](const auto& endpoint) {\n            return endpoint.EndpointId == endpointId;\n        });\n\n        if (foundEndpoint == m_networkEndpoints.cend())\n        {\n            WSL_LOG(\"WslMirroredNetworkManager::RemoveEndpoint - Endpoint not found\", TraceLoggingValue(endpointId, \"endpointId\"));\n            return S_OK;\n        }\n\n        // Perform per-interface configuration of net filter rules.\n        hns::InterfaceNetFilterRequest interfaceNetFilterRequest;\n        interfaceNetFilterRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(foundEndpoint->InterfaceGuid);\n        interfaceNetFilterRequest.operation = hns::OperationType::Remove;\n\n        int linuxResultCode{};\n        // can safely capture by ref since we are waiting\n        auto hr = m_gnsCallbackQueue.submit_and_wait([&] {\n            return m_callbackForGnsMessage(\n                LxGnsMessageInterfaceNetFilter, ToJsonW(std::move(interfaceNetFilterRequest)), GnsCallbackFlags::Wait, &linuxResultCode);\n        });\n        LOG_IF_FAILED(hr);\n        WSL_LOG(\n            \"WslMirroredNetworkManager::RemoveEndpoint [InterfaceNetFilterRequest]\",\n            TraceLoggingHResult(hr, \"hr\"),\n            TraceLoggingValue(linuxResultCode, \"linuxResultCode\"),\n            TraceLoggingValue(endpointId, \"endpointId\"),\n            TraceLoggingValue(foundEndpoint->InterfaceGuid, \"interfaceGuid\"));\n\n        // A race exists between already queued operations for this interface on the GNS queue and HNS endpoint removal.\n        // In order to resolve the race, while holding the m_networkLock, flush the GNS queue then delete the endpoint in HCS.\n        WSL_LOG(\"WslMirroredNetworkManager::RemoveEndpoint\", TraceLoggingValue(\"Flush GNS queue [queued]\", \"message\"));\n\n        linuxResultCode = {};\n        // can safely capture by ref since we are waiting\n        hr = m_gnsCallbackQueue.submit_and_wait([&] {\n            return m_callbackForGnsMessage(LxGnsMessageNoOp, std::wstring(L\"\"), GnsCallbackFlags::Wait, &linuxResultCode);\n        });\n        WSL_LOG(\n            \"WslMirroredNetworkManager::RemoveEndpoint\",\n            TraceLoggingValue(\"Flush GNS queue [completed]\", \"message\"),\n            TraceLoggingHResult(hr, \"hr\"),\n            TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n\n        // try to delete the endpoint in HCS\n        // Set the instance id to the mirrored interfaceGuid so HNS -> netvsc can optimally use the same vmNIC constructs when the InterfaceGuid is the same\n\n        hcs::ModifySettingRequest<hcs::NetworkAdapter> networkRequest{};\n        networkRequest.ResourcePath = c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(foundEndpoint->InterfaceGuid);\n        networkRequest.RequestType = hcs::ModifyRequestType::Remove;\n        networkRequest.Settings.InstanceId = foundEndpoint->InterfaceGuid;\n        networkRequest.Settings.EndpointId = endpointId;\n\n        const auto networkRequestString = wsl::shared::ToJsonW(networkRequest);\n\n        WSL_LOG(\n            \"WslMirroredNetworkManager::RemoveEndpoint : Removing the HCS mirrored endpoint [queued]\",\n            TraceLoggingValue(networkRequestString.c_str(), \"networkRequest\"),\n            TraceLoggingValue(endpointId, \"endpointId\"));\n        // capturing by ref because we wait for the workitem to complete\n        hr = m_hnsQueue.submit_and_wait([&] {\n            windows::common::hcs::ModifyComputeSystem(m_hcsSystem, networkRequestString.c_str());\n            return S_OK;\n        });\n        WSL_LOG(\n            \"WslMirroredNetworkManager::RemoveEndpoint : Removing the HCS mirrored endpoint [completed]\",\n            TraceLoggingHResult(hr, \"hr\"));\n\n        if (FAILED(hr))\n        {\n            WSL_LOG(\n                \"RemoveMirroredEndpointFailed\",\n                TraceLoggingHResult(hr, \"result\"),\n                TraceLoggingValue(\"RemoveHcsEndpoint\", \"executionStep\"),\n                TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                TraceLoggingValue(m_vmConfig.EnableAutoProxy, \"AutoProxyFeatureEnabled\")); // the feature is enabled, but we don't know if proxy settings are actually configured\n        }\n    }\n    CATCH_LOG()\n\n    // Remove the endpoint and its tracked state\n    // Linux will delete any addresses and routes associated with the interface\n    m_networkEndpoints.erase(foundEndpoint);\n    WSL_LOG(\n        \"WslMirroredNetworkManager::RemoveEndpoint - Endpoint removed from m_networkEndpoints\",\n        TraceLoggingValue(endpointId, \"endpointId\"));\n\n    // Is this necessary?\n    UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, \"RemoveEndpoint\");\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nvoid wsl::core::networking::WslMirroredNetworkManager::SendCreateNotificationsForInitialEndpoints() noexcept\n{\n    WSL_LOG(\"WslMirroredNetworkManager::SendCreateNotificationsForInitialEndpoints\");\n    const auto lock = m_networkLock.lock_shared();\n    if (m_state == State::Stopped)\n    {\n        return;\n    }\n\n    // Perform global configuration of net filter rules.\n    int linuxResultCode{};\n    // can safely capture by ref since we are waiting\n    const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {\n        return m_callbackForGnsMessage(LxGnsMessageGlobalNetFilter, std::wstring(L\"\"), GnsCallbackFlags::Wait, &linuxResultCode);\n    });\n    WSL_LOG(\n        \"WslMirroredNetworkManager::SendCreateNotificationsForInitialEndpoints\",\n        TraceLoggingValue(\"Sent message to perform global configuration of net filter rules\", \"message\"),\n        TraceLoggingHResult(hr, \"hr\"),\n        TraceLoggingValue(linuxResultCode, \"linuxResultCode\"));\n    LOG_IF_FAILED(hr);\n}\n\nHRESULT wsl::core::networking::WslMirroredNetworkManager::WaitForMirroredGoalState() noexcept\n{\n    WSL_LOG(\"WslMirroredNetworkManager::WaitForMirroredGoalState\");\n\n    return (m_inMirroredGoalState.wait(c_initialMirroredGoalStateWaitTimeoutMs)) ? S_OK : HRESULT_FROM_WIN32(ERROR_TIMEOUT);\n}\n\n_Check_return_ bool wsl::core::networking::WslMirroredNetworkManager::DoesEndpointExist(GUID networkId) const noexcept\ntry\n{\n    const auto lock = m_networkLock.lock_shared();\n    if (m_state == State::Stopped)\n    {\n        return false;\n    }\n\n    return std::ranges::any_of(m_networkEndpoints, [&](const NetworkEndpoint& endpoint) { return endpoint.NetworkId == networkId; });\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return false;\n}\n\n_Requires_lock_not_held_(m_networkLock)\nvoid wsl::core::networking::WslMirroredNetworkManager::UpdateAllEndpoints(_In_ PCSTR sourceName) noexcept\n{\n    const auto lock = m_networkLock.lock_exclusive();\n    if (m_state == State::Stopped)\n    {\n        return;\n    }\n\n    UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, sourceName);\n}\n\nvoid wsl::core::networking::WslMirroredNetworkManager::OnNetworkConnectivityHintChange() noexcept\n{\n    const auto lock = m_networkLock.lock_exclusive();\n    if (m_state == State::Stopped)\n    {\n        return;\n    }\n\n    UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, \"OnNetworkConnectivityHintChange\");\n}\n\n// Strategy for handling notifications from HNS:\n// 1) Always consume the data immediately.\n// 2) If UpdateAllEndpointsImpl hasn't run for >= m_debounceUpdateAllEndpointsTimerMs then run it.\n// 3) If UpdateAllEndpointsImpl has run < m_debounceUpdateAllEndpointsTimerMs ago, schedule the timer.\nvoid wsl::core::networking::WslMirroredNetworkManager::OnNetworkEndpointChange() noexcept\n{\n    const auto lock = m_networkLock.lock_exclusive();\n    if (m_state == State::Stopped)\n    {\n        return;\n    }\n\n    UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, \"OnNetworkEndpointChange\");\n}\n\nvoid wsl::core::networking::WslMirroredNetworkManager::OnDnsSuffixChange() noexcept\ntry\n{\n    const auto lock = m_networkLock.lock_exclusive();\n    if (m_state == State::Stopped)\n    {\n        return;\n    }\n\n    UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, \"OnDnsSuffixChange\");\n}\nCATCH_LOG();\n\nvoid wsl::core::networking::WslMirroredNetworkManager::TunAdapterStateChanged(_In_ const std::string& interfaceName, _In_ bool up) noexcept\n{\n}\n\nvoid wsl::core::networking::WslMirroredNetworkManager::ReconnectGuestNetwork()\n{\n    auto lock = m_networkLock.lock_exclusive();\n    if (m_state == State::Stopped)\n    {\n        return;\n    }\n\n    WSL_LOG(\"WslMirroredNetworkManager::ReconnectGuestNetwork\");\n    UpdateAllEndpointsImpl(UpdateEndpointFlag::ForceUpdate, \"ReconnectGuestNetwork\");\n}\n\n_Requires_lock_held_(m_networkLock)\nwsl::core::networking::NetworkSettings wsl::core::networking::WslMirroredNetworkManager::GetNetworkSettingsOfInterface(DWORD ifIndex) const\n{\n    const auto matchingEndpoint =\n        std::ranges::find_if(m_networkEndpoints, [&](const auto& endpoint) { return endpoint.Network->InterfaceIndex == ifIndex; });\n    if (matchingEndpoint == std::end(m_networkEndpoints))\n    {\n        WSL_LOG(\"GetNetworkSettingsOfInterface - Network not found\", TraceLoggingValue(ifIndex, \"ifIndex\"));\n        return {};\n    }\n    else\n    {\n        WSL_LOG(\"GetNetworkSettingsOfInterface\", TRACE_NETWORKSETTINGS_OBJECT(matchingEndpoint->Network.get()));\n        return *matchingEndpoint->Network;\n    }\n}\n\nstd::shared_ptr<wsl::core::networking::NetworkSettings> wsl::core::networking::WslMirroredNetworkManager::GetEndpointSettings(\n    const hns::HNSEndpoint& endpointProperties) const\n{\n    return wsl::core::networking::GetEndpointSettings(endpointProperties);\n}\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::UpdateHcnServiceTimer() noexcept\ntry\n{\n    // These values are chosen so that the connection will be retried 5 times.\n    static constexpr DWORD INITIAL_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS = 1000; // 1 second.\n    static constexpr DWORD MAX_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS = 80000;    // ~1.3 minute.\n\n    // Check if the maximum retry attempt count has been reached.\n    if (m_retryHcnServiceConnectionDurationMs <= MAX_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS)\n    {\n        // Determine how long until the timer should fire.\n        if (m_retryHcnServiceConnectionDurationMs == 0)\n        {\n            // Use the initial duration value as this is the first time the timer is being armed.\n            m_retryHcnServiceConnectionDurationMs = INITIAL_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS;\n        }\n        else\n        {\n            // Make sure that the timer duration can't overflow.\n            static_assert(MAX_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS < (DWORD_MAX / 2));\n\n            // Apply an exponential backoff.\n            m_retryHcnServiceConnectionDurationMs *= 2;\n        }\n\n        WSL_LOG(\n            \"WslMirroredNetworkManager::UpdateHcnServiceTimer\",\n            TraceLoggingValue(m_retryHcnServiceConnectionDurationMs, \"m_retryHcnServiceConnectionDurationMs\"));\n\n        FILETIME dueTime = wil::filetime::from_int64(\n            static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_retryHcnServiceConnectionDurationMs));\n        SetThreadpoolTimer(m_retryHcnServiceConnectionTimer.get(), &dueTime, 0, 1000);\n    }\n    else\n    {\n        WSL_LOG(\n            \"WslMirroredNetworkManager::UpdateHcnServiceTimer\",\n            TraceLoggingValue(0, \"retryHcnServiceConnectionDurationMs (service is not active)\"));\n        THROW_WIN32(ERROR_SERVICE_NOT_ACTIVE);\n    }\n\n    return S_OK;\n}\nCATCH_RETURN()\n\n_Requires_lock_held_(m_networkLock)\n_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::ResetHcnServiceSession() noexcept\ntry\n{\n    if (!m_hcnCallback)\n    {\n        WSL_LOG(\"WslMirroredNetworkManager::ResetHcnServiceSession - attempting to re-register\"); // Attempt to resubscribe to HNS notifications.\n        m_hcnCallback = windows::common::hcs::RegisterServiceCallback(HcnCallback, this);\n\n        // if we can reregister, reset the retry timer.\n        m_retryHcnServiceConnectionDurationMs = 0;\n        SetThreadpoolTimer(m_retryHcnServiceConnectionTimer.get(), nullptr, 0, 0);\n\n        std::vector<GUID> enumeratedNetworkIds;\n        try\n        {\n            // Refresh the current list of networks. The list will then be kept\n            // up to date by the subscription notifications.\n            enumeratedNetworkIds = EnumerateMirroredNetworks();\n        }\n        catch (...)\n        {\n            const auto hr = wil::ResultFromCaughtException();\n            WSL_LOG(\n                \"ResetHcnServiceSessionFailed\",\n                TraceLoggingValue(hr, \"result\"),\n                TraceLoggingValue(\"HcnEnumerateNetworks\", \"executionStep\"),\n                TraceLoggingValue(\"Mirrored\", \"networkingMode\"),\n                TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                TraceLoggingValue(m_vmConfig.EnableAutoProxy, \"AutoProxyFeatureEnabled\")); // the feature is enabled, but we don't know if proxy settings are actually configured\n\n            throw;\n        }\n\n        wil::unique_cotaskmem_string response;\n        wil::unique_cotaskmem_string error;\n        const auto enumEndpointsHr = HcnEnumerateEndpoints(nullptr, &response, &error);\n        if (FAILED(enumEndpointsHr))\n        {\n            WSL_LOG(\n                \"ResetHcnServiceSessionFailed\",\n                TraceLoggingValue(enumEndpointsHr, \"result\"),\n                TraceLoggingValue(\"HcnEnumerateEndpoints\", \"executionStep\"),\n                TraceLoggingValue(\"Mirrored\", \"networkingMode\"),\n                TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n                TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n                TraceLoggingValue(m_vmConfig.EnableAutoProxy, \"AutoProxyFeatureEnabled\")); // the feature is enabled, but we don't know if proxy settings are actually configured\n        }\n        else\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::ResetHcnServiceSession - HcnEnumerateEndpoints\",\n                TraceLoggingValue(response.get(), \"response\"));\n        }\n\n        for (const auto& networkId : enumeratedNetworkIds)\n        {\n            // Must call back through MirroredNetworking to create a new Endpoint\n            // note that the callback will not block - it just queues the work in MirroredNetworking\n            LOG_IF_FAILED(AddNetwork(networkId));\n        }\n    }\n    else\n    {\n        WSL_LOG(\"WslMirroredNetworkManager::ResetHcnServiceSession - already re-registered\");\n    }\n\n    return S_OK;\n}\nCATCH_RETURN()\n\nvoid wsl::core::networking::WslMirroredNetworkManager::TelemetryConnectionCallback(NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) noexcept\ntry\n{\n    WSL_LOG(\"WslMirroredNetworkManager::TelemetryConnectionCallback\");\n\n    const auto lock = m_networkLock.lock_exclusive();\n    if (m_state == State::Stopped)\n    {\n        return;\n    }\n\n    // if this is the inital callback for checking container connectivity, push this through as telemetry, so we can observe the time-to-connect\n    if ((telemetryCounter > 1) && !(hostConnectivity & NLM_CONNECTIVITY_IPV4_INTERNET) && !(hostConnectivity & NLM_CONNECTIVITY_IPV6_INTERNET))\n    {\n        WSL_LOG(\n            \"WslMirroredNetworkManager::TelemetryConnectionCallback - not testing connectivity - host is not connected\",\n            TraceLoggingValue(telemetryCounter, \"telemetryCounter\"),\n            TraceLoggingValue(wsl::core::networking::ToString(hostConnectivity).c_str(), \"HostConnectivityLevel\"));\n        return;\n    }\n\n    int returnedIPv4Value{};\n    LOG_IF_FAILED(m_gnsCallbackQueue.submit_and_wait([&] {\n        return m_callbackForGnsMessage(LxGnsMessageConnectTestRequest, c_ipv4TestRequestTarget, GnsCallbackFlags::Wait, &returnedIPv4Value);\n    }));\n\n    int returnedIPv6Value{};\n    LOG_IF_FAILED(m_gnsCallbackQueue.submit_and_wait([&] {\n        return m_callbackForGnsMessage(LxGnsMessageConnectTestRequest, c_ipv6TestRequestTarget, GnsCallbackFlags::Wait, &returnedIPv6Value);\n    }));\n\n    // make the same connect requests as we just requested from the container\n    const auto hostConnectivityCheck =\n        wsl::shared::conncheck::CheckConnection(c_ipv4TestRequestTargetA, c_ipv6TestRequestTargetA, \"80\");\n    const auto WindowsIpv4ConnCheckStatus = static_cast<uint32_t>(hostConnectivityCheck.Ipv4Status);\n    const auto WindowsIpv6ConnCheckStatus = static_cast<uint32_t>(hostConnectivityCheck.Ipv6Status);\n    const auto WindowsIPv4NlmConnectivityLevel = ConnectivityTelemetry::WindowsIPv4NlmConnectivityLevel(hostConnectivity);\n    const auto WindowsIPv6NlmConnectivityLevel = ConnectivityTelemetry::WindowsIPv6NlmConnectivityLevel(hostConnectivity);\n    const auto LinuxIPv4ConnCheckStatus = ConnectivityTelemetry::LinuxIPv4ConnCheckResult(returnedIPv4Value);\n    const auto LinuxIPv6ConnCheckStatus = ConnectivityTelemetry::LinuxIPv6ConnCheckResult(returnedIPv6Value);\n\n    const auto timeFromObjectCreation = std::chrono::steady_clock::now() - m_objectCreationTime;\n\n    // Logs when network connectivity changes, used to compare network connectivity in the guest to the host to determine networking health\n    WSL_LOG_TELEMETRY(\n        \"TelemetryConnectionCallback\",\n        PDT_ProductAndServicePerformance,\n        TraceLoggingValue(\"Mirrored\", \"networkingMode\"),\n        TraceLoggingValue(telemetryCounter, \"telemetryCounter\"),\n        TraceLoggingValue(\n            (std::chrono::duration_cast<std::chrono::milliseconds>(timeFromObjectCreation)).count(), \"timeFromObjectCreationMs\"),\n        TraceLoggingValue(wsl::core::networking::ToString(hostConnectivity).c_str(), \"HostConnectivityLevel\"),\n        TraceLoggingValue(WindowsIPv4NlmConnectivityLevel, \"WindowsIPv4ConnectivityLevel\"),\n        TraceLoggingValue(WindowsIPv6NlmConnectivityLevel, \"WindowsIPv6ConnectivityLevel\"),\n        TraceLoggingValue(LinuxIPv4ConnCheckStatus, \"LinuxIPv4ConnCheckStatus\"),\n        TraceLoggingValue(LinuxIPv6ConnCheckStatus, \"LinuxIPv6ConnCheckStatus\"),\n        TraceLoggingValue(WindowsIpv4ConnCheckStatus, \"WindowsIpv4ConnCheckStatus\"),\n        TraceLoggingValue(WindowsIpv6ConnCheckStatus, \"WindowsIpv6ConnCheckStatus\"),\n        TraceLoggingValue(m_vmConfig.EnableDnsTunneling, \"DnsTunnelingEnabled\"),\n        TraceLoggingValue(m_dnsTunnelingIpAddress.c_str(), \"DnsTunnelingIpAddress\"),\n        TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), \"HyperVFirewallEnabled\"),\n        TraceLoggingValue(m_vmConfig.EnableAutoProxy, \"AutoProxyFeatureEnabled\")); // the feature is enabled, but we don't know if proxy settings are actually configured\n}\nCATCH_LOG()\n\nvoid __stdcall wsl::core::networking::WslMirroredNetworkManager::HcnServiceConnectionTimerCallback(\n    _Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER) noexcept\n{\n    WSL_LOG(\"WslMirroredNetworkManager::HcnServiceConnectionTimerCallback\");\n\n    auto* const manager = static_cast<WslMirroredNetworkManager*>(Context);\n\n    const auto lock = manager->m_networkLock.lock_exclusive();\n    if (manager->m_state == State::Stopped)\n    {\n        return;\n    }\n\n    if (FAILED(manager->ResetHcnServiceSession()))\n    {\n        // The retry attempt was unsuccessful, re-arm the timer to try again.\n        LOG_IF_FAILED(manager->UpdateHcnServiceTimer());\n    }\n}\n\nvoid CALLBACK wsl::core::networking::WslMirroredNetworkManager::HcnCallback(\n    _In_ DWORD NotificationType, _In_opt_ void* Context, _In_ HRESULT, _In_opt_ PCWSTR NotificationData) noexcept\ntry\n{\n    hns::NotificationBase data = {};\n    if (NotificationType == HcnNotificationNetworkCreate || NotificationType == HcnNotificationNetworkPreDelete)\n    {\n        data = FromJson<hns::NotificationBase>(NotificationData);\n    }\n\n    auto* const manager = static_cast<WslMirroredNetworkManager*>(Context);\n\n    const auto lock = manager->m_networkLock.lock_exclusive();\n    if (manager->m_state == State::Stopped)\n    {\n        return;\n    }\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::HcnCallback [HcnRegisterServiceCallback]\",\n        TraceLoggingValue(NotificationType, \"notificationType\"),\n        TraceLoggingValue(wsl::windows::common::stringify::HcnNotificationsToString(NotificationType), \"notificationTypeString\"),\n        TraceLoggingValue(data.ID, \"networkId\"),\n        TraceLoggingValue(data.Flags, \"flags\"),\n        TraceLoggingValue(NotificationData, \"notificationData\"));\n\n    switch (NotificationType)\n    {\n    case HcnNotificationNetworkCreate:\n    {\n        // convert the enum to integer to allow for bitmap comparisons\n        if (!WI_IsFlagSet(data.Flags, WI_EnumValue(hns::NetworkFlags::EnableFlowSteering)))\n        {\n            WSL_LOG(\"WslMirroredNetworkManager::HcnCallback [HcnRegisterServiceCallback] - not a mirrored network\");\n            return;\n        }\n\n        LOG_IF_FAILED(manager->AddNetwork(data.ID));\n        break;\n    }\n\n    case HcnNotificationNetworkPreDelete:\n    {\n        // This notification is fired off right before HNS network deletion.\n        // Ensure Containers release endpoints whether network deletion\n        // is successful or not.\n        LOG_IF_FAILED(manager->RemoveNetwork(data.ID));\n        break;\n    }\n\n    case HcnNotificationServiceDisconnect:\n    {\n        // This notification indicates that the subscription has become invalid due to a loss\n        // of connection to the server. This typically means that the HNS service has been\n        // stopped or restarted.\n        manager->m_hcnCallback.reset();\n\n        manager->m_networkEndpoints.clear();\n\n        LOG_IF_FAILED(manager->UpdateHcnServiceTimer());\n        break;\n    }\n    }\n    manager->UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, \"HcnCallback\");\n}\nCATCH_LOG()\n\nconst char* wsl::core::networking::WslMirroredNetworkManager::StateToString(State state) noexcept\n{\n    switch (state)\n    {\n    case State::Stopped:\n        return \"Stopped\";\n    case State::Started:\n        return \"Started\";\n    case State::Starting:\n        return \"Starting\";\n    default:\n        return \"Unknown\";\n    }\n}\n\nvoid wsl::core::networking::WslMirroredNetworkManager::TraceLoggingRundown() const\n{\n    auto lock = m_networkLock.lock_shared();\n\n    WSL_LOG(\n        \"WslMirroredNetworkManager::TraceLoggingRundown\",\n        TraceLoggingValue(\"Global State\"),\n        TraceLoggingValue(StateToString(m_state), \"state\"),\n        TraceLoggingValue(GenerateResolvConf(m_trackedDnsInfo).c_str(), \"dnsInfo\"));\n\n    for (const auto& network : m_networkEndpoints)\n    {\n        WSL_LOG(\"WslMirroredNetworkManager::TraceLoggingRundown\", TRACE_NETWORKSETTINGS_OBJECT(network.Network));\n\n        if (network.StateTracking)\n        {\n            WSL_LOG(\n                \"WslMirroredNetworkManager::TraceLoggingRundown\",\n                TraceLoggingValue(\"IpStateTracking Interface Info\"),\n                TraceLoggingValue(network.StateTracking->InterfaceGuid, \"interfaceGuid\"),\n                TraceLoggingValue(network.StateTracking->InterfaceMtu, \"mtu\"));\n\n            for (const auto& address : network.StateTracking->IpAddresses)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::TraceLoggingRundown\",\n                    TraceLoggingValue(\"IpStateTracking::IpAddresses\"),\n                    TraceLoggingValue(address.Address.AddressString.c_str(), \"address\"),\n                    TraceLoggingValue(address.Address.PrefixLength, \"prefixLength\"),\n                    TraceLoggingValue(wsl::core::networking::ToString(address.SyncStatus), \"syncStatus\"),\n                    TraceLoggingValue(address.SyncRetryCount, \"syncRetryCount\"),\n                    TraceLoggingValue(address.LoopbackSyncRetryCount, \"loopbackSyncRetryCount\"));\n            }\n\n            for (const auto& route : network.StateTracking->Routes)\n            {\n                WSL_LOG(\n                    \"WslMirroredNetworkManager::TraceLoggingRundown\",\n                    TraceLoggingValue(\"IpStateTracking::Routes\"),\n                    TraceLoggingValue(route.Route.ToString().c_str(), \"route\"),\n                    TraceLoggingValue(route.Route.Metric, \"metric\"),\n                    TraceLoggingValue(\n                        !route.CanConflictWithLinuxAutoGenRoute() || route.LinuxConflictRemoved,\n                        \"linuxConflictRemovedOrDoesntExist\"),\n                    TraceLoggingValue(wsl::core::networking::ToString(route.SyncStatus), \"syncStatus\"),\n                    TraceLoggingValue(route.SyncRetryCount, \"syncRetryCount\"));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/windows/service/exe/WslMirroredNetworking.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslMirroredNetworking.h\n\nAbstract:\n\n    This file contains WSL mirrored networking function declarations.\n\n--*/\n\n#pragma once\n#include <chrono>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include <mstcpip.h>\n#include <ws2ipdef.h>\n#include <netlistmgr.h>\n#include <ComputeNetwork.h>\n#include <wil/winrt.h>\n\n#include \"WslCoreMessageQueue.h\"\n#include \"WslCoreAdviseHandler.h\"\n#include \"WslCoreNetworkEndpoint.h\"\n#include \"WslCoreNetworkEndpointSettings.h\"\n#include \"WslCoreNetworkingSupport.h\"\n#include \"WslCoreTcpIpStateTracking.h\"\n#include \"WslCoreHostDnsInfo.h\"\n#include \"hcs.hpp\"\n#include \"IMirroredNetworkManager.h\"\n\n/// <summary>\n/// Creates network-related information for WSL.\n/// </summary>\nnamespace wsl::core::networking {\n\nclass WslMirroredNetworkManager final : public wsl::core::networking::IMirroredNetworkManager\n{\npublic:\n    WslMirroredNetworkManager(\n        HCS_SYSTEM hcsSystem,\n        const Config& config,\n        GnsMessageCallbackWithCallbackResult&& GnsMessageCallbackWithCallbackResult,\n        AddNetworkEndpointCallback&& addNetworkEndpointCallback,\n        const std::pair<uint16_t, uint16_t>& ephemeralPortRange);\n\n    ~WslMirroredNetworkManager() noexcept override;\n\n    // Disable copy and assign.\n    WslMirroredNetworkManager(const WslMirroredNetworkManager&) = delete;\n    WslMirroredNetworkManager& operator=(const WslMirroredNetworkManager&) = delete;\n\n    // Disable move semantics.\n    WslMirroredNetworkManager(WslMirroredNetworkManager&&) noexcept = delete;\n    WslMirroredNetworkManager& operator=(WslMirroredNetworkManager&&) = delete;\n\n    HnsStatus Stop() noexcept override;\n\n    _Check_return_ HRESULT EnumerateNetworks(_Out_ std::vector<GUID>& NetworkIds) const noexcept override;\n\n    void AddEndpoint(NetworkEndpoint&& newEndpoint, wsl::shared::hns::HNSEndpoint&& endpointProperties) noexcept override;\n\n    void SendCreateNotificationsForInitialEndpoints() noexcept override;\n\n    HRESULT WaitForMirroredGoalState() noexcept override;\n\n    _Check_return_ bool DoesEndpointExist(GUID networkId) const noexcept override;\n\n    void OnNetworkConnectivityHintChange() noexcept override;\n    void OnNetworkEndpointChange() noexcept override;\n    void OnDnsSuffixChange() noexcept override;\n\n    void TunAdapterStateChanged(_In_ const std::string& interfaceName, _In_ bool up) noexcept override;\n\n    // Client should call this if they detect the network is in a bad state and needs to be reconnected\n    void ReconnectGuestNetwork() override;\n\n    std::shared_ptr<NetworkSettings> GetEndpointSettings(const wsl::shared::hns::HNSEndpoint& endpointProperties) const override;\n\n    void TraceLoggingRundown() const override;\n\nprivate:\n    enum class State\n    {\n        Stopped = 0,\n        Started,\n        Starting,\n    };\n\n    static const char* StateToString(State state) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    std::vector<GUID> EnumerateMirroredNetworks() const noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT AddNetwork(const GUID& networkId) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT RemoveNetwork(const GUID& networkId) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT RemoveEndpoint(const GUID& endpointId) noexcept;\n\n    struct EndpointTracking\n    {\n        EndpointTracking(NetworkEndpoint&& networkEndpoint, wsl::shared::hns::HNSEndpoint&& hnsEndpoint, uint32_t retryCount) :\n            m_networkEndpoint{std::move(networkEndpoint)}, m_hnsEndpoint{std::move(hnsEndpoint)}, m_retryCount{retryCount}\n        {\n        }\n        ~EndpointTracking() noexcept = default;\n        EndpointTracking(const EndpointTracking&) = delete;\n        EndpointTracking& operator=(const EndpointTracking&) = delete;\n        EndpointTracking(EndpointTracking&&) = default;\n        EndpointTracking& operator=(EndpointTracking&&) = default;\n\n        NetworkEndpoint m_networkEndpoint;\n        wsl::shared::hns::HNSEndpoint m_hnsEndpoint;\n        uint32_t m_retryCount = 0;\n    };\n\n    _Requires_lock_held_(m_networkLock)\n    void AddEndpointImpl(EndpointTracking&& endpointTrackingObject) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    void ProcessConnectivityChange();\n\n    _Requires_lock_held_(m_networkLock)\n    void ProcessInterfaceChange();\n\n    _Requires_lock_held_(m_networkLock)\n    void ProcessIpAddressChange();\n\n    _Requires_lock_held_(m_networkLock)\n    void ProcessRouteChange();\n\n    _Requires_lock_held_(m_networkLock)\n    void ProcessDNSChange();\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT SendAddressRequestToGns(\n        const NetworkEndpoint& endpoint, const TrackedIpAddress& address, wsl::shared::hns::ModifyRequestType requestType) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT SendRouteRequestToGns(const NetworkEndpoint& endpoint, const TrackedRoute& route, wsl::shared::hns::ModifyRequestType requestType) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT SendLoopbackRequestToGns(\n        const NetworkEndpoint& endpoint, const TrackedIpAddress& address, wsl::shared::hns::OperationType operation) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT SendDnsRequestToGns(const NetworkEndpoint& endpoint, const DnsInfo& dnsInfo, wsl::shared::hns::ModifyRequestType requestType) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT SendInterfaceRequestToGns(const NetworkEndpoint& endpoint) noexcept;\n\n    _Requires_lock_not_held_(m_networkLock)\n    void UpdateAllEndpoints(_In_ PCSTR sourceName) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    void UpdateAllEndpointsImpl(UpdateEndpointFlag updateFlag, _In_ PCSTR callingSource) noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT UpdateHcnServiceTimer() noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ HRESULT ResetHcnServiceSession() noexcept;\n\n    _Requires_lock_held_(m_networkLock)\n    _Check_return_ bool SyncIpStateWithLinux(NetworkEndpoint& endpoint);\n\n    _Requires_lock_held_(m_networkLock)\n    NetworkSettings GetNetworkSettingsOfInterface(DWORD ifIndex) const;\n\n    void TelemetryConnectionCallback(NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) noexcept;\n\n    // protects access to member variables as well as operations that generate callback messages\n    // methods which lead to GNS messages being sent must maintain the order in which the caller invoked them\n    // thus exclusive access will be guaranteed for these methods, even if we don't need write-protection to member variables\n    mutable wil::srwlock m_networkLock;\n\n    // Member variables used to limit calls through UpdatePreferredEndpoint(UpdateEndpointFlag::Default) to every 350ms.\n    // This is because WslMirroredNetworkManager uses an eventing model where we will often see many 10s of events fired back-to-back\n    static constexpr uint32_t m_debounceUpdateAllEndpointsTimerMs = 350;\n    _Guarded_by_(m_networkLock) uint64_t m_lastUpdateAllEndpointsDefaultTime = 0;\n    bool m_IsDebounceUpdateAllEndpointsDefaultTimerSet = false;\n    _Requires_lock_held_(m_networkLock)\n    wil::unique_threadpool_timer m_debounceUpdateAllEndpointsDefaultTimer;\n    static void __stdcall DebounceUpdateAllEndpointsDefaultTimerFired(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER);\n\n    // Member variables tracking resiliency attempts to create endpoints in the container for indicated networkIds from HNS\n    static constexpr uint32_t m_maxAddEndpointRetryCount = 3;\n    static constexpr uint32_t m_debounceCreateEndpointFailureTimerMs = 1000;\n    _Requires_lock_held_(m_networkLock)\n    std::vector<EndpointTracking> m_failedEndpointProperties;\n    _Requires_lock_held_(m_networkLock)\n    wil::unique_threadpool_timer m_debounceCreateEndpointFailureTimer;\n    static void __stdcall DebounceCreateEndpointFailureTimerFired(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER);\n\n    // Member variables tracking the WinRT and COM networking APIs required\n    wsl::windows::common::helpers::unique_mta_cookie m_mtaCookie;\n    wil::com_ptr<ABI::Windows::Networking::Connectivity::INetworkInformationStatics> m_networkInformationStatics;\n    wil::com_ptr<INetworkListManager> m_netListManager;\n    wil::com_ptr<INetworkEvents> m_netListManagerEventSink;\n    WslCoreAdviseHandler m_netListManagerAdviseHandler;\n\n    _Guarded_by_(m_networkLock) HnsStatus m_latestHnsStatus { HnsStatus::NoNetworkEverConnected };\n\n    // Members tracking all endpoints created in the container, and the current networks connected\n    _Guarded_by_(m_networkLock) std::vector<NetworkEndpoint> m_networkEndpoints;\n    _Guarded_by_(m_networkLock) std::set<GUID, wsl::windows::common::helpers::GuidLess> m_hostConnectedInterfaces;\n\n    // Members tracking callback functors back through the parent\n    _Guarded_by_(m_networkLock) GnsMessageCallbackWithCallbackResult m_callbackForGnsMessage;\n    // the AddNetworkEndpointCallback is called through the m_gnsMessageQueue\n    // so we don't risk deadlocks if the callback chooses to call back into WslMirroredNetworkManager\n    _Guarded_by_(m_networkLock) AddNetworkEndpointCallback m_addNetworkEndpointCallback;\n\n    // The DNS info synced into the guest\n    _Guarded_by_(m_networkLock) DnsInfo m_trackedDnsInfo;\n    // The current DNS info on the host\n    _Guarded_by_(m_networkLock) DnsInfo m_dnsInfo;\n\n    std::wstring m_dnsTunnelingIpAddress;\n\n    // Tracks whether we are in the mirrored goal state or not.\n    _Guarded_by_(m_networkLock) wil::unique_event m_inMirroredGoalState { wil::EventOptions::ManualReset };\n\n    ConnectivityTelemetry m_connectivityTelemetry;\n\n    // Used for telemetry to see how long it takes to reach the mirrored goal state for the first time.\n    std::chrono::time_point<std::chrono::steady_clock> m_objectCreationTime = std::chrono::steady_clock::now();\n    std::chrono::time_point<std::chrono::steady_clock> m_initialMirroredGoalStateEndTime;\n\n    // Handle for the Hcn* Api. Owned by the caller (WslCoreVm), this is a non-owning copy\n    const HCS_SYSTEM m_hcsSystem{};\n\n    // Config of the WslCoreVm.\n    const Config& m_vmConfig;\n\n    // Ephemeral port range allocated for the VM.\n    std::pair<uint16_t, uint16_t> m_ephemeralPortRange;\n\n    // All guest related messages sent back through callbacks to Linux (GNS)\n    // must be queued in order into a single queue.\n    WslCoreMessageQueue m_gnsCallbackQueue;\n\n    // All host-configuration messages, either back to the parent MirroredNetworking or to HNS/HCS\n    // must have their own queue as to not be blocked by Linux messages.\n    WslCoreMessageQueue m_hnsQueue;\n\n    // Callback timer that is invoked when the HNS service goes down to attempt to re-establish contact\n    static void __stdcall HcnServiceConnectionTimerCallback(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER) noexcept;\n    wil::unique_threadpool_timer m_retryHcnServiceConnectionTimer;\n    _Guarded_by_(m_networkLock) DWORD m_retryHcnServiceConnectionDurationMs = 0;\n\n    // Callback that is invoked by HNS when a service-wide notification\n    // is available (e.g. when a HNS network is created or deleted).\n    static void __stdcall HcnCallback(_In_ DWORD NotificationType, _In_opt_ void* Context, _In_ HRESULT NotificationStatus, _In_opt_ PCWSTR NotificationData) noexcept;\n    windows::common::hcs::unique_hcn_service_callback m_hcnCallback;\n\n    _Guarded_by_(m_networkLock) State m_state { State::Stopped };\n\n    // Callback timer that is invoked when we want to retry syncing the latest Windows IP state with Linux\n    static void __stdcall RetryLinuxIpStateSyncTimerCallback(_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER) noexcept;\n    wil::unique_threadpool_timer m_retryLinuxIpStateSyncTimer;\n    static constexpr uint32_t m_linuxIpStateRetryDebounceTimerMinMilliseconds = 100ul;\n    static constexpr uint32_t m_linuxIpStateRetryDebounceTimerMaxMilliseconds = 2000ul;\n    uint32_t m_linuxIpStateRetryDebounceTimerMilliseconds = m_linuxIpStateRetryDebounceTimerMinMilliseconds;\n\n    class PublicNLMSink final : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, INetworkEvents>\n    {\n        WslMirroredNetworkManager* m_parent{};\n\n    public:\n        explicit PublicNLMSink(WslMirroredNetworkManager* parent) : m_parent(parent)\n        {\n        }\n\n        ~PublicNLMSink() override = default;\n\n        PublicNLMSink(const PublicNLMSink&) = delete;\n        PublicNLMSink& operator=(const PublicNLMSink&) = delete;\n        PublicNLMSink(PublicNLMSink&&) = delete;\n        PublicNLMSink& operator=(PublicNLMSink&&) = delete;\n\n        // INetworkEvents\n        IFACEMETHODIMP NetworkAdded(GUID networkId) override\n        {\n            m_parent->UpdateAllEndpoints(\"INetworkEvents\");\n            return S_OK;\n        }\n\n        IFACEMETHODIMP NetworkDeleted(GUID networkId) override\n        {\n            m_parent->UpdateAllEndpoints(\"INetworkEvents\");\n            return S_OK;\n        }\n\n        IFACEMETHODIMP NetworkConnectivityChanged(GUID networkId, NLM_CONNECTIVITY connectivity) override\n        {\n            m_parent->UpdateAllEndpoints(\"INetworkEvents\");\n            return S_OK;\n        }\n\n        IFACEMETHODIMP NetworkPropertyChanged(GUID networkId, NLM_NETWORK_PROPERTY_CHANGE property) override\n        {\n            m_parent->UpdateAllEndpoints(\"INetworkEvents\");\n            return S_OK;\n        }\n    };\n};\n\n} // namespace wsl::core::networking\n"
  },
  {
    "path": "src/windows/service/exe/application.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\" xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\" >\n<application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings xmlns:ws2=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">\n        <ws2:longPathAware>true</ws2:longPathAware>\n    </windowsSettings>\n</application>\n</assembly>"
  },
  {
    "path": "src/windows/service/exe/main.rc",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.rc\n\nAbstract:\n\n    This file contains resources for wslservice.\n\n--*/\n\n#include <windows.h>\n#include \"resource.h\"\n#include \"wslversioninfo.h\"\n\n#define VER_INTERNALNAME_STR \"wslservice.exe\"\n#define VER_ORIGINALFILENAME_STR \"wslservice.exe\"\n\n#define VER_FILETYPE VFT_APP\n#define VER_FILESUBTYPE VFT2_UNKNOWN\n#define VER_FILEDESCRIPTION_STR \"Windows Subsystem for Linux Service\"\nID_ICON ICON PRELOAD DISCARDABLE \"..\\..\\..\\Images\\wsl.ico\"\n\n\n#include <common.ver>\n"
  },
  {
    "path": "src/windows/service/exe/resource.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    resource.h\n\nAbstract:\n\n    This file contains resource declarations for wslservice.exe\n\n--*/\n\n#define ID_ICON 1\n"
  },
  {
    "path": "src/windows/service/inc/CMakeLists.txt",
    "content": "add_idl(wslserviceidl \"wslservice.idl\" \"windowsdefs.idl\")\nset_target_properties(wslserviceidl PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/service/inc/windowsdefs.idl",
    "content": "/*++\n\nCopyright (c) Microsoft Corporation.  All rights reserved.\n\nModule Name:\n\n    windowsdefs.idl\n\nAbstract:\n\n    This file contains the COM object used by WSL.\n\n--*/\n\nimport \"unknwn.idl\";\nimport \"wtypes.idl\";\n\n[\n    object,\n    uuid(3e57bd3c-5a5d-4bdc-a0a6-5b4193d4b719),\n    pointer_default( unique )\n]\ninterface IVmVirtualDeviceAccess : IUnknown\n{\n    [local]\n    HRESULT _Reserved();\n\n    // N.B. The original declaration has another method before GetDevice(), but that method is declared as call_as, which doesn't generate an entry in the vtable. \n    [local]\n    HRESULT\n    GetDevice(\n        [in]          REFGUID    CategoryID,\n        [in]          REFGUID    DeviceID,\n        [out, retval] IUnknown** Device\n        );\n};\n\ntypedef[v1_enum] enum {\n    FIOV_BAR0 = 0, ///< Bar 0 of the virtual device\n    FIOV_BAR1,     ///< Bar 1 of the virtual device\n    FIOV_BAR2,     ///< Bar 2 of the virtual device\n    FIOV_BAR3,     ///< Bar 3 of the virtual device\n    FIOV_BAR4,     ///< Bar 4 of the virtual device\n    FIOV_BAR5,     ///< Bar 5 of the virtual device\n    FIOV_ROMBAR    ///< ROM BAR of the virtual device\n} FIOV_BAR_SELECTOR;\n\ntypedef[v1_enum] enum {\n    FiovMmioMappingFlagNone = 0x00000000,\n    FiovMmioMappingFlagWriteable = 0x00000001,\n    FiovMmioMappingFlagExecutable = 0x00000002\n\n} FiovMmioMappingFlags;\ncpp_quote(\"DEFINE_ENUM_FLAG_OPERATORS(FiovMmioMappingFlags);\")\n\n[\n    object,\n    uuid(f5dfbec1-b9f3-4b26-bf6f-c251448bcf7a),\n    helpstring(\"IVmFiovGuestMemoryFastNotification Interface\"),\n    pointer_default(unique),\n]\ninterface IVmFiovGuestMemoryFastNotification : IUnknown\n{\n    HRESULT\n    RegisterDoorbell(\n        [in] FIOV_BAR_SELECTOR BarIndex,\n        [in] UINT64 BarOffset,\n        [in] UINT64 TriggerValue,\n        [in] UINT64 Flags,\n        [in, system_handle(sh_event)] HANDLE NotificationEvent\n        );\n\n    HRESULT\n    UnregisterDoorbell(\n        [in]                          FIOV_BAR_SELECTOR    BarIndex,\n        [in]                          UINT64               BarOffset,\n        [in]                          UINT64               TriggerValue,\n        [in]                          UINT64               Flags\n        );\n};\n\n[\n    local,\n    object,\n    uuid(68c6a1b9-de39-42c3-8d28-bf40a5126541)\n]\ninterface ICallingProcessInfo : IUnknown\n{\n    HRESULT OpenCallerProcessHandle(DWORD desiredAccess, [out, annotation(\"_Out_\")] HANDLE *callerProcessHandle);\n};\n\n[\n    object,\n    uuid(9d416457-abbc-46cf-8b93-901c68bec627),\n    helpstring(\"IVmFiovGuestMmioMappings Interface\"),\n    pointer_default(unique),\n]\ninterface IVmFiovGuestMmioMappings : IUnknown\n{\n    HRESULT\n    CreateSectionBackedMmioRange(\n        [in]                             FIOV_BAR_SELECTOR     BarIndex,\n        [in]                             ULONG64               BarOffsetInPages,\n        [in]                             UINT64                PageCount,\n        [in]                             FiovMmioMappingFlags  MappingFlags,\n        [in, system_handle(sh_section)]  HANDLE                SectionHandle,\n        [in]                             UINT64                SectionOffsetInPages\n        );\n\n    HRESULT\n    DestroySectionBackedMmioRange(\n        [in]                             FIOV_BAR_SELECTOR     BarIndex,\n        [in]                             ULONG64               BarOffsetInPages\n        );\n};\n\n[\n    object,\n    uuid(78523d62-d919-47ca-9cd7-08139172d685),\n    helpstring(\"IVmDeviceHost Interface\"),\n    pointer_default(unique)\n]\ninterface IVmDeviceHost : IUnknown\n{\n    HRESULT\n    GetDeviceInstance(\n        [in] REFGUID DeviceClassId,\n        [in] REFGUID DeviceInstanceId,\n        [out, retval] IUnknown** DeviceInstance\n        );\n};\n\n[\n    object,\n    uuid(e31aa49b-0914-465e-b145-1b9ba13efb10),\n    helpstring(\"IVmDeviceHostSupport Interface\"),\n    pointer_default(unique)\n]\ninterface IVmDeviceHostSupport : IUnknown\n{\n    HRESULT\n    RegisterDeviceHost(\n        [in] IVmDeviceHost* DeviceHost,\n        [in] DWORD ProcessId,\n        [out] UINT64* IpcSectionHandle\n        );\n};\n\n[\n    uuid(7649D52D-D275-4289-901E-EB626C967ECE),\n    pointer_default(unique),\n    object\n]\ninterface IPlan9FileSystem : IUnknown\n{\n    HRESULT AddShare([in, unique] LPCWSTR name, [in, system_handle(sh_file)] HANDLE handle, [in] UINT32 flags);\n    HRESULT RemoveShare([in, unique] LPCWSTR name);\n    HRESULT Pause();\n    HRESULT Resume();\n    HRESULT Teardown();\n    HRESULT SetAllowedFiles([in, unique] LPCWSTR shareName, [in, size_is(count)] const LPCWSTR *files, [in] int count);\n    HRESULT Init([in, unique] LPCGUID vmId, [in] ULONG port);\n    HRESULT AddSharePath([in, unique] LPCWSTR name, [in, unique] LPCWSTR path, [in] UINT32 flags);\n    HRESULT CreateVirtioDevice([in, unique] LPCWSTR vmId,\n                               [in, unique] IUnknown *deviceHostSupport,\n                               [in, unique] LPCWSTR tag,\n                               [in, unique, defaultvalue(NULL)] GUID *instanceId);\n    HRESULT IsRunning();\n};\n\n[\n    uuid(8434F839-CA86-495D-8A75-7EB36D073FFE),\n    pointer_default(unique),\n    object\n]\ninterface IPlan9FileSystemHost : IUnknown\n{\n    HRESULT NotifyAllDevicesInUse([in, unique] LPCWSTR tag);\n    HRESULT RegisterDoorbell([in] REFGUID InstanceId,\n                             [in] UINT8 BarIndex,\n                             [in] UINT64 Offset,\n                             [in] UINT64 TriggerValue,\n                             [in] UINT64 Flags,\n                             [in, system_handle(sh_event)] HANDLE Event);\n    HRESULT UnregisterDoorbell([in] REFGUID InstanceId,\n                               [in] UINT8 BarIndex,\n                               [in] UINT64 Offset,\n                               [in] UINT64 TriggerValue,\n                               [in] UINT64 Flags);\n    HRESULT CreateSectionBackedMmioRange([in] REFGUID InstanceId,\n                                         [in] UINT8 BarIndex,\n                                         [in] UINT64 BarOffsetInPages,\n                                         [in] UINT64 PageCount,\n                                         [in] UINT64 MappingFlags,\n                                         [in, system_handle(sh_section)] HANDLE SectionHandle,\n                                         [in] UINT64 SectionOffsetInPages);\n    HRESULT DestroySectionBackedMmioRange([in] REFGUID InstanceId,\n                                          [in] UINT8 BarIndex,\n                                          [in] UINT64 BarOffsetInPages);\n};\n\ncpp_quote(\"#ifdef __cplusplus\")\ncpp_quote(\"namespace p9fs\")\ncpp_quote(\"{\")\ncpp_quote(\"class DECLSPEC_UUID(\\\"AFC7B6DE-D642-41B7-AB0C-A01019510741\\\") Plan9FileSystem;\")\ncpp_quote(\"}\")\ncpp_quote(\"#endif\")"
  },
  {
    "path": "src/windows/service/inc/wslservice.idl",
    "content": "/*++\n\nCopyright (c) Microsoft Corporation.  All rights reserved.\n\nModule Name:\n\n    wslservice.idl\n\nAbstract:\n\n    This file contains the COM object definitions used to talk with the WSL\n    service \"WslService\"\n\n--*/\n\nimport \"unknwn.idl\";\nimport \"wtypes.idl\";\n\n// Name of the inbox WSL service.\ncpp_quote(\"#define LXSS_INBOX_SERVICE_NAME L\\\"lxssmanager\\\"\")\n\n// Legacy install registry key state key name.\n//\n// N.B. This registry key value is used to determine if a user has a pre-RS3\n//      install. This value should only be used to migrate installs to the new\n//      registration format.\ncpp_quote(\"#define LXSS_LEGACY_INSTALL_VALUE L\\\"State\\\"\")\ncpp_quote(\"#define LXSS_LEGACY_INSTALL_NAME L\\\"Legacy\\\"\")\n\n// Special GUIDs used to identify the legacy WSL distro and the system distro.\ncpp_quote(\"const GUID LXSS_LEGACY_DISTRO_GUID = {0x12345678, 0x1234, 0x5678, {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}};\")\ncpp_quote(\"const GUID WSL2_SYSTEM_DISTRO_GUID = {0x20170608, 0x0542, 0x2019, {0x07, 0x29, 0x00, 0x11, 0x20, 0x12, 0x02, 0x14}};\")\n\ntypedef enum _LxssDistributionState\n{\n    LxssDistributionStateInvalid = 0,\n    LxssDistributionStateInstalled,\n    LxssDistributionStateRunning,\n    LxssDistributionStateInstalling,\n    LxssDistributionStateUninstalling,\n    LxssDistributionStateConverting,\n    LxssDistributionStateExporting\n} LxssDistributionState;\n\ntypedef\nenum _LxssHandleType {\n    LxssHandleConsole = 0,\n    LxssHandleInput,\n    LxssHandleOutput,\n} LxssHandleType;\n\ntypedef\nenum _LxssExecutionContextFlags {\n    LxssExecutionContextFlagsNone = 0,\n    LxssExecutionContextFlagsEnableContextualizedErrors = 1,\n    LxssExecutionContextFlagsEnableUserWarnings = 2\n} LxssExecutionContextFlags;\n\ncpp_quote(\"#define LXSS_HANDLE_USE_CONSOLE 0\")\n\ntypedef\nstruct _LXSS_HANDLE {\n    ULONG Handle;\n    LxssHandleType HandleType;\n} LXSS_HANDLE, *PLXSS_HANDLE;\n\ntypedef\nstruct _LXSS_STD_HANDLES {\n    LXSS_HANDLE StdIn;\n    LXSS_HANDLE StdOut;\n    LXSS_HANDLE StdErr;\n} LXSS_STD_HANDLES, *PLXSS_STD_HANDLES;\n\ntypedef\nstruct _LXSS_ENUMERATE_INFO {\n    GUID DistroGuid;\n    LxssDistributionState State;\n    ULONG Version;\n    ULONG Flags;\n    WCHAR DistroName[257];\n} LXSS_ENUMERATE_INFO, *PLXSS_ENUMERATE_INFO;\n\ntypedef\nstruct _LXSS_ERROR_INFO {\n    ULONG Flags;\n    ULONGLONG Context;\n    [string] LPWSTR Message;\n    [string] LPWSTR Warnings;\n    ULONG WarningsPipe;\n} LXSS_ERROR_INFO, *PLXSS_ERROR_INFO;\n\ncpp_quote(\"#define LXSS_DISTRO_VERSION_LEGACY 0\")\ncpp_quote(\"#define LXSS_DISTRO_VERSION_1 1\")\ncpp_quote(\"#define LXSS_DISTRO_VERSION_2 2\")\ncpp_quote(\"#define LXSS_DISTRO_VERSION_CURRENT LXSS_DISTRO_VERSION_2\")\ncpp_quote(\"#define LXSS_DISTRO_USES_WSL_FS(DistroVersion) (DistroVersion >= LXSS_DISTRO_VERSION_2)\")\n\ncpp_quote(\"#define LXSS_DISTRO_DEFAULT_ENVIRONMENT \\\"HOSTTYPE=x86_64\\0LANG=en_US.UTF-8\\0PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games\\0TERM=xterm-256color\\0\\\"\")\n\ncpp_quote(\"#define LXSS_DISTRO_DEFAULT_KERNEL_COMMAND_LINE \\\"BOOT_IMAGE=/kernel init=/init\\\"\")\n\ncpp_quote(\"#define LXSS_DISTRO_FLAGS_ENABLE_INTEROP 0x1\")\ncpp_quote(\"#define LXSS_DISTRO_FLAGS_APPEND_NT_PATH 0x2\")\ncpp_quote(\"#define LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING 0x4\")\ncpp_quote(\"#define LXSS_DISTRO_FLAGS_VM_MODE 0x8\")\ncpp_quote(\"#define LXSS_DISTRO_FLAGS_UNCHANGED 0xFFFFFFFE\") // Not using ~0 since the inbox tests use that value to validate that invalid flags return an error.\ncpp_quote(\"#define LXSS_DISTRO_FLAGS_ALL (LXSS_DISTRO_FLAGS_ENABLE_INTEROP | LXSS_DISTRO_FLAGS_APPEND_NT_PATH | LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING | LXSS_DISTRO_FLAGS_VM_MODE)\")\ncpp_quote(\"#define LXSS_DISTRO_FLAGS_DEFAULT (LXSS_DISTRO_FLAGS_ENABLE_INTEROP | LXSS_DISTRO_FLAGS_APPEND_NT_PATH | LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING)\")\n\ncpp_quote(\"#define LXSS_ENUMERATE_FLAGS_DEFAULT 0x1\")\n\ncpp_quote(\"#define LXSS_GET_DISTRO_ID_LIST_ALL 0x1\")\n\ncpp_quote(\"#define LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE 0x1\")\ncpp_quote(\"#define LXSS_CREATE_INSTANCE_FLAGS_OPEN_EXISTING 0x2\")\ncpp_quote(\"#define LXSS_CREATE_INSTANCE_FLAGS_IGNORE_CLIENT 0x4\")\ncpp_quote(\"#define LXSS_CREATE_INSTANCE_FLAGS_USE_SYSTEM_DISTRO 0x8\")\ncpp_quote(\"#define LXSS_CREATE_INSTANCE_FLAGS_SHELL_LOGIN 0x10\")\ncpp_quote(\"#define LXSS_CREATE_INSTANCE_FLAGS_ALL (LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE | LXSS_CREATE_INSTANCE_FLAGS_OPEN_EXISTING | LXSS_CREATE_INSTANCE_FLAGS_IGNORE_CLIENT | LXSS_CREATE_INSTANCE_FLAGS_USE_SYSTEM_DISTRO | LXSS_CREATE_INSTANCE_FLAGS_SHELL_LOGIN)\")\n\ncpp_quote(\"#define LXSS_EXPORT_DISTRO_FLAGS_VHD 0x1\")\ncpp_quote(\"#define LXSS_EXPORT_DISTRO_FLAGS_GZIP 0x2\")\ncpp_quote(\"#define LXSS_EXPORT_DISTRO_FLAGS_XZIP 0x4\")\ncpp_quote(\"#define LXSS_EXPORT_DISTRO_FLAGS_VERBOSE 0x8\")\ncpp_quote(\"#define LXSS_EXPORT_DISTRO_FLAGS_ALL (LXSS_EXPORT_DISTRO_FLAGS_VHD | LXSS_EXPORT_DISTRO_FLAGS_GZIP | LXSS_EXPORT_DISTRO_FLAGS_XZIP | LXSS_EXPORT_DISTRO_FLAGS_VERBOSE)\")\n\ncpp_quote(\"#define LXSS_IMPORT_DISTRO_FLAGS_VHD 0x1\")\ncpp_quote(\"#define LXSS_IMPORT_DISTRO_FLAGS_CREATE_SHORTCUT 0x2\")\ncpp_quote(\"#define LXSS_IMPORT_DISTRO_FLAGS_NO_OOBE 0x4\")\ncpp_quote(\"#define LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD 0x8\")\ncpp_quote(\"#define LXSS_IMPORT_DISTRO_FLAGS_ALL (LXSS_IMPORT_DISTRO_FLAGS_VHD | LXSS_IMPORT_DISTRO_FLAGS_CREATE_SHORTCUT | LXSS_IMPORT_DISTRO_FLAGS_NO_OOBE | LXSS_IMPORT_DISTRO_FLAGS_FIXED_VHD)\")\n\ncpp_quote(\"#define LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH 0x1\")\ncpp_quote(\"#define LXSS_ATTACH_MOUNT_FLAGS_VHD 0x2\")\n\ncpp_quote(\"#define LXSS_PLAN9_UNIX_SOCKET_A \\\"fsserver\\\"\")\ncpp_quote(\"#define LXSS_PLAN9_UNIX_SOCKET L\\\"fsserver\\\"\")\n\ncpp_quote(\"#define LXSS_VM_MODE_INITRD_NAME L\\\"initrd.img\\\"\")\ncpp_quote(\"#define LXSS_VM_MODE_KERNEL_NAME L\\\"kernel\\\"\")\ncpp_quote(\"#define LXSS_VM_MODE_VHD_NAME L\\\"ext4.vhdx\\\"\")\n\ncpp_quote(\"#define LXSS_TOOLS_DIRECTORY L\\\"tools\\\"\")\n\ncpp_quote(\"#define LXSS_REGISTRY_PATH L\\\"Software\\\\\\\\Microsoft\\\\\\\\Windows\\\\\\\\CurrentVersion\\\\\\\\Lxss\\\"\")\ncpp_quote(\"#define LXSS_DISK_MOUNTS_REGISTRY_PATH LXSS_REGISTRY_PATH L\\\"\\\\\\\\DiskMounts\\\"\")\ncpp_quote(\"#define LXSS_WSL_DEFAULT_VERSION L\\\"DefaultVersion\\\"\")\ncpp_quote(\"#define LXSS_NOTIFICATIONS_KEY L\\\"Notifications\\\"\")\ncpp_quote(\"#define LXSS_NOTIFICATION_DRVFS_PERF_DISABLED L\\\"DrvFsPerfDisabled\\\"\")\ncpp_quote(\"#define LXSS_WSL_VERSION_DEFAULT 0\")\ncpp_quote(\"#define LXSS_WSL_VERSION_1 1\")\ncpp_quote(\"#define LXSS_WSL_VERSION_2 2\")\n\ncpp_quote(\"#define WSL_DISTRO_NAME L\\\"DistributionName\\\"\")\ncpp_quote(\"#define LXSS_OOBE_COMPLETE_NAME L\\\"OOBEComplete\\\"\")\n\ncpp_quote(\"const GUID CLSID_LxssUserSession = {0xa9b7a1b9, 0x0671, 0x405c, {0x95, 0xf1, 0xe0, 0x61, 0x2c, 0xb4, 0xce, 0x7e}};\")\ncpp_quote(\"const GUID CLSID_LxssUserSessionInBox = {0x4f476546, 0xb412, 0x4579, {0xb6, 0x4c, 0x12, 0x3d, 0xf3, 0x31, 0xe3, 0xd6}};\")\ncpp_quote(\"#ifdef __cplusplus\")\ncpp_quote(\"class DECLSPEC_UUID(\\\"a9b7a1b9-0671-405c-95f1-e0612cb4ce7e\\\") LxssUserSession;\")\ncpp_quote(\"class DECLSPEC_UUID(\\\"4f476546-b412-4579-b64c-123df331e3d6\\\") LxssUserSessionInBox;\")\ncpp_quote(\"#endif\")\n\n[\n    uuid(38541BDC-F54F-4CEB-85D0-37F0F3D2617E),\n    pointer_default(unique),\n    object\n]\ninterface ILxssUserSession : IUnknown\n{\n    //\n    // N.B. This function must be the first one in the interface, so that it can\n    //      be exposed privately to external callers without exposing the remaining\n    //      functions below it.\n    //\n\n    HRESULT CreateInstance(\n        [in, unique] LPCGUID DistroGuid,\n        [in] ULONG Flags,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT RegisterDistribution(\n        [in, unique] LPCWSTR DistributionName,\n        [in] ULONG Version,\n        [in, system_handle(sh_file)] HANDLE FileHandle,\n        [in, system_handle(sh_pipe)] HANDLE StderrHandle,\n        [in, unique] LPCWSTR TargetDirectory,\n        [in] ULONG Flags,\n        [in] ULONG64 VhdSize,\n        [in, unique] LPCWSTR PackageFamilyName,\n        [out] LPWSTR* InstalledDistributionName,\n        [in, out] LXSS_ERROR_INFO* Error,\n        [out, retval] GUID* pDistroGuid\n        );\n\n    HRESULT RegisterDistributionPipe(\n        [in, unique] LPCWSTR DistributionName,\n        [in] ULONG Version,\n        [in, system_handle(sh_pipe)] HANDLE PipeHandle,\n        [in, system_handle(sh_pipe)] HANDLE StderrHandle,\n        [in, unique] LPCWSTR TargetDirectory,\n        [in] ULONG Flags,\n        [in] ULONG64 VhdSize,\n        [in, unique] LPCWSTR PackageFamilyName,\n        [out] LPWSTR* InstalledDistributionName,\n        [in, out] LXSS_ERROR_INFO* Error,\n        [out, retval] GUID* pDistroGuid);\n\n    HRESULT GetDistributionId(\n        [in] LPCWSTR DistributionName,\n        [in] ULONG Flags,\n        [in, out] LXSS_ERROR_INFO* Error,\n        [out, retval] GUID* pDistroGuid);\n\n    HRESULT TerminateDistribution(\n        [in, unique] LPCGUID DistroGuid,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT UnregisterDistribution(\n        [in] LPCGUID DistroGuid,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT ConfigureDistribution(\n        [in, unique] LPCGUID DistroGuid,\n        [in] ULONG DefaultUid,\n        [in] ULONG Flags,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT GetDistributionConfiguration(\n        [in, unique] LPCGUID DistroGuid,\n        [out] LPWSTR* DistributionName,\n        [out] ULONG* Version,\n        [out] ULONG* DefaultUid,\n        [out] ULONG* DefaultEnvironmentCount,\n        [out, size_is(, *DefaultEnvironmentCount)] LPSTR** DefaultEnvironment,\n        [out] ULONG* Flags,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT GetDefaultDistribution(\n        [in, out] LXSS_ERROR_INFO* Error,\n        [out, retval] LPGUID pResultState);\n\n    HRESULT ResizeDistribution(\n        [in] LPCGUID DistroGuid,\n        [in, system_handle(sh_pipe)] HANDLE OutputHandle,\n        [in] ULONG64 NewSize,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT SetDefaultDistribution(\n        [in] LPCGUID DistroGuid,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT SetSparse(\n        [in] LPCGUID DistroGuid,\n        [in] BOOLEAN Sparse,\n        [in] BOOLEAN AllowUnsafe,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT EnumerateDistributions(\n        [out] ULONG *DistributionCount,\n        [out, size_is(, *DistributionCount)] LXSS_ENUMERATE_INFO **Distributions,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT CreateLxProcess(\n        [in, unique] LPCGUID DistroGuid,\n        [in, unique] LPCSTR Filename,\n        [in] ULONG CommandLineCount,\n        [in, unique, size_is(CommandLineCount)] LPCSTR* CommandLine,\n        [in, unique] LPCWSTR CurrentWorkingDirectory,\n        [in, unique] LPCWSTR NtPath,\n        [in, unique, size_is(NtEnvironmentLength)] WCHAR *NtEnvironment,\n        [in] ULONG NtEnvironmentLength,\n        [in, unique] LPCWSTR Username,\n        [in] SHORT Columns,\n        [in] SHORT Rows,\n        [in] ULONG ConsoleHandle,\n        [in] PLXSS_STD_HANDLES StdHandles,\n        [in] ULONG Flags,\n        [out] GUID* DistributionId,\n        [out] GUID* InstanceId,\n        [out, system_handle(sh_file)] HANDLE *ProcessHandle,\n        [out, system_handle(sh_file)] HANDLE *ServerHandle,\n        [out, system_handle(sh_socket)] HANDLE* StandardIn,\n        [out, system_handle(sh_socket)] HANDLE* StandardOut,\n        [out, system_handle(sh_socket)] HANDLE* StandardErr,\n        [out, system_handle(sh_socket)] HANDLE* CommunicationChannel,\n        [out, system_handle(sh_socket)] HANDLE* InteropSocket,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT SetVersion(\n        [in] LPCGUID DistroGuid,\n        [in] ULONG Version,\n        [in, system_handle(sh_pipe)] HANDLE StderrHandle,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT ExportDistribution(\n        [in, unique] LPCGUID DistroGuid,\n        [in, system_handle(sh_file)] HANDLE FileHandle,\n        [in, system_handle(sh_pipe)] HANDLE StderrHandle,\n        [in] ULONG Flags,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT ExportDistributionPipe(\n        [in, unique] LPCGUID DistroGuid,\n        [in, system_handle(sh_pipe)] HANDLE PipeHandle,\n        [in, system_handle(sh_pipe)] HANDLE StderrHandle,\n        [in] ULONG Flags,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT AttachDisk(\n        [in] LPCWSTR Disk,\n        [in] ULONG Flags,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT DetachDisk(\n        [in, unique] LPCWSTR Disk,\n        [out] int* Result,\n        [out] int* Step,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT MountDisk(\n        [in] LPCWSTR Disk,\n        [in] ULONG Flags,\n        [in] ULONG PartitionIndex,\n        [in, unique] LPCWSTR Name,\n        [in, unique] LPCWSTR Type,\n        [in, unique] LPCWSTR Options,\n        [out] int* Result,\n        [out] int* Step,\n        [out] LPWSTR* MountName,\n        [in, out] LXSS_ERROR_INFO* Error);\n\n    HRESULT Shutdown(\n        [in] BOOL Force);\n\n    HRESULT ImportDistributionInplace(\n        [in] LPCWSTR DistributionName,\n        [in] LPCWSTR VhdPath,\n        [in, out] LXSS_ERROR_INFO* Error,\n        [out, retval] GUID* pDistroGuid);\n\n    HRESULT MoveDistribution(\n        [in] LPCGUID DistroGuid,\n        [in] LPCWSTR DistributionName, \n        [ in, out ] LXSS_ERROR_INFO * Error);\n};\n\n\ncpp_quote(\"// LXSS specific errors\")\ncpp_quote(\"#define WSL_E_BASE (0x0300)\")\ncpp_quote(\"#define WSL_E_DEFAULT_DISTRO_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 1) /* 0x80040301 */\")\ncpp_quote(\"#define WSL_E_DISTRO_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 2) /* 0x80040302 */\")\ncpp_quote(\"#define WSL_E_WSL1_NOT_SUPPORTED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 3) /* 0x80040303 */\")\ncpp_quote(\"#define WSL_E_VM_MODE_NOT_SUPPORTED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 4) /* 0x80040304 */\")\ncpp_quote(\"#define WSL_E_TOO_MANY_DISKS_ATTACHED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 5) /* 0x80040305 */\")\ncpp_quote(\"#define WSL_E_CONSOLE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 6) /* 0x80040306 */\")\ncpp_quote(\"#define WSL_E_CUSTOM_KERNEL_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 7) /* 0x80040307 */\")\ncpp_quote(\"#define WSL_E_USER_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 8) /* 0x80040308 */\")\ncpp_quote(\"#define WSL_E_INVALID_USAGE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 9) /* 0x80040309 */\")\ncpp_quote(\"#define WSL_E_EXPORT_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0xa) /* 0x8004030a */\")\ncpp_quote(\"#define WSL_E_IMPORT_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0xb) /* 0x8004030b */\")\ncpp_quote(\"#define WSL_E_DISTRO_NOT_STOPPED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0xc) /* 0x8004030c */\")\ncpp_quote(\"#define WSL_E_TTY_LIMIT MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0xd) /* 0x8004030d */\")\ncpp_quote(\"#define WSL_E_CUSTOM_SYSTEM_DISTRO_ERROR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0xe) /* 0x8004030e */\")\ncpp_quote(\"#define WSL_E_LOWER_INTEGRITY MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0xf) /* 0x8004030f */\")\ncpp_quote(\"#define WSL_E_HIGHER_INTEGRITY MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x10) /* 0x80040310 */\")\ncpp_quote(\"#define WSL_E_FS_UPGRADE_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x11) /* 0x80040311 */\")\ncpp_quote(\"#define WSL_E_USER_VHD_ALREADY_ATTACHED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x12) /* 0x80040312 */\")\ncpp_quote(\"#define WSL_E_VM_MODE_INVALID_STATE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x13) /* 0x80040313 */\")\ncpp_quote(\"#define WSL_E_VM_MODE_MOUNT_NAME_ALREADY_EXISTS MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x14) /* 0x80040314 */\")\ncpp_quote(\"#define WSL_E_ELEVATION_NEEDED_TO_MOUNT_DISK MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x15) /* 0x80040315 */\")\ncpp_quote(\"#define WSL_E_DISK_ALREADY_ATTACHED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x18) /* 0x80040318 */\")\ncpp_quote(\"#define WSL_E_DISK_ALREADY_MOUNTED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x19) /* 0x80040319 */\")\ncpp_quote(\"#define WSL_E_DISK_MOUNT_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x1A) /* 0x8004031A */\")\ncpp_quote(\"#define WSL_E_DISK_UNMOUNT_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x1B) /* 0x8004031B */\")\ncpp_quote(\"#define WSL_E_WSL2_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x1C) /* 0x8004031C */\")\ncpp_quote(\"#define WSL_E_VM_MODE_INVALID_MOUNT_NAME MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x1D) /* 0x8004031D */\")\ncpp_quote(\"#define WSL_E_GUI_APPLICATIONS_DISABLED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x1E) /* 0x8004031E */\")\ncpp_quote(\"#define WSL_E_DISTRO_ONLY_AVAILABLE_FROM_STORE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x1F) /* 0x8004031F */\")\ncpp_quote(\"#define WSL_E_WSL_MOUNT_NOT_SUPPORTED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x20) /* 0x80040320 */\")\ncpp_quote(\"#define WSL_E_WSL_OPTIONAL_COMPONENT_REQUIRED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x21) /* 0x80040321 */\")\ncpp_quote(\"#define WSL_E_VMSWITCH_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x24) /* 0x80040324 */\")\ncpp_quote(\"#define WSL_E_VMSWITCH_NOT_SET MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x25) /* 0x80040325 */\")\ncpp_quote(\"#define WSL_E_NOT_A_LINUX_DISTRO MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x26) /* 0x80040326 */\")\ncpp_quote(\"#define WSL_E_OS_NOT_SUPPORTED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x27) /* 0x80040327 */\")\ncpp_quote(\"#define WSL_E_INSTALL_PROCESS_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x28) /* 0x80040328 */\")\ncpp_quote(\"#define WSL_E_INSTALL_COMPONENT_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x29) /* 0x80040329 */\")\ncpp_quote(\"/* 0x8004032A is in use for WSL_E_PLUGIN_REQUIRES_UPDATE */\")\ncpp_quote(\"#define WSL_E_DISK_MOUNT_DISABLED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2B) /* 0x8004032B */\")\ncpp_quote(\"#define WSL_E_WSL1_DISABLED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2C) /* 0x8004032C */\")\ncpp_quote(\"#define WSL_E_VIRTUAL_MACHINE_PLATFORM_REQUIRED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2D) /* 0x8004032D */\")\ncpp_quote(\"#define WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2E) /* 0x8004032E */\")\ncpp_quote(\"#define WSL_E_DISK_CORRUPTED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x2F) /* 0x8004032F */\")\ncpp_quote(\"#define WSL_E_DISTRIBUTION_NAME_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x30) /* 0x80040330 */\")\ncpp_quote(\"#define WSL_E_INVALID_JSON MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x31) /* 0x80040331 */\")\ncpp_quote(\"#define WSL_E_VM_CRASHED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSL_E_BASE + 0x32) /* 0x80040332 */\")\n"
  },
  {
    "path": "src/windows/service/mc/CMakeLists.txt",
    "content": "add_mc(wslservicemc wsleventschema.mc)\nset_target_properties(wslservicemc PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/service/mc/wsleventschema.mc",
    "content": "MessageIdTypeDef=DWORD\n\nSeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS\n               Informational=0x1:STATUS_SEVERITY_INFORMATIONAL\n               Warning=0x2:STATUS_SEVERITY_WARNING\n               Error=0x3:STATUS_SEVERITY_ERROR\n               )\n\nLanguageNames=(Neutral=0x0000:MSG00000)\n\nMessageId=0x0   SymbolicName=MSG_WARNING\nSeverity=Warning\nFacility=Application\nLanguage=Neutral\n%1\n.\n"
  },
  {
    "path": "src/windows/service/stub/CMakeLists.txt",
    "content": "set(SOURCES\n    ${CMAKE_CURRENT_BINARY_DIR}/../inc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/wslservice_i_${TARGET_PLATFORM}.c\n    ${CMAKE_CURRENT_BINARY_DIR}/../inc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/wslservice_p_${TARGET_PLATFORM}.c\n    ${CMAKE_CURRENT_BINARY_DIR}/../inc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/dlldata_${TARGET_PLATFORM}.c\n    ${CMAKE_CURRENT_LIST_DIR}/WslServiceProxyStub.def\n    ${CMAKE_CURRENT_LIST_DIR}/WslServiceProxyStub.rc)\n\nset_source_files_properties(${SOURCES} PROPERTIES GENERATED TRUE)\n\nadd_library(wslserviceproxystub SHARED ${SOURCES})\nadd_dependencies(wslserviceproxystub wslserviceidl)\ntarget_link_libraries(wslserviceproxystub ${COMMON_LINK_LIBRARIES})\nset_target_properties(wslserviceproxystub PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/service/stub/WslServiceProxyStub.def",
    "content": "LIBRARY WslServiceProxyStub.dll\n\nEXPORTS\n    DllGetClassObject      PRIVATE\n    DllCanUnloadNow        PRIVATE\n"
  },
  {
    "path": "src/windows/service/stub/WslServiceProxyStub.rc",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslServiceProxyStub.rc\n\nAbstract:\n\n    This file contains resources for wslserviceproxystub.dll.\n\n--*/\n\n#include <windows.h>\n#include \"wslversioninfo.h\"\n\n#define VER_INTERNALNAME_STR \"wslserviceproxystub.dll\"\n#define VER_ORIGINALFILENAME_STR \"wslserviceproxystub.dll\"\n\n#define VER_FILEDESCRIPTION_STR \"WSL Service ProxyStub DLL\"\n\n#include <common.ver>\n"
  },
  {
    "path": "src/windows/wsl/CMakeLists.txt",
    "content": "set(SOURCES\n    main.cpp\n    main.rc)\n\nset(HEADERS\n    resource.h)\n\nadd_executable(wsl ${SOURCES} ${HEADERS})\n\ntarget_link_libraries(wsl\n                      ${COMMON_LINK_LIBRARIES}\n                      ${MSI_LINK_LIBRARIES}\n                      common\n                      runtimeobject.lib\n                      delayimp.lib)\n\nset_target_properties(wsl PROPERTIES LINK_FLAGS \"/DELAYLOAD:msi.dll /DELAYLOAD:WINTRUST.dll\")\ntarget_precompile_headers(wsl REUSE_FROM common)\nset_target_properties(wsl PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/wsl/main.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.cpp\n\nAbstract:\n\n    This file contains the entry point for wsl.exe.\n\n--*/\n\n#include \"precomp.h\"\n\nint __cdecl wmain()\n{\n    return wsl::windows::common::WslClient::Main(GetCommandLineW());\n}\n"
  },
  {
    "path": "src/windows/wsl/main.rc",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.rc\n\nAbstract:\n\n    This file contains resources for wsl.\n\n--*/\n\n#include <windows.h>\n#include \"resource.h\"\n#include \"wslversioninfo.h\"\n\n#define VER_INTERNALNAME_STR \"wsl.exe\"\n#define VER_ORIGINALFILENAME_STR \"wsl.exe\"\n\n#define VER_FILETYPE VFT_APP\n#define VER_FILESUBTYPE VFT2_UNKNOWN\n#define VER_FILEDESCRIPTION_STR \"Windows Subsystem for Linux\"\nID_ICON ICON PRELOAD DISCARDABLE \"..\\..\\..\\Images\\wsl.ico\"\n\n#include <common.ver>\n"
  },
  {
    "path": "src/windows/wsl/resource.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    resource.h\n\nAbstract:\n\n    This file contains resource declarations for wsl.exe\n\n--*/\n\n#define ID_ICON 1\n"
  },
  {
    "path": "src/windows/wslg/CMakeLists.txt",
    "content": "set(SOURCES\n    main.cpp\n    main.rc)\n\nset(HEADERS\n    resource.h\n    )\n\nadd_executable(wslg WIN32 ${SOURCES} ${HEADERS})\nadd_dependencies(wslg\n                 common)\n\ntarget_link_libraries(wslg\n                      ${COMMON_LINK_LIBRARIES}\n                      ${MSI_LINK_LIBRARIES}\n                      common\n                      delayimp.lib)\n\nset_target_properties(wslg PROPERTIES LINK_FLAGS \"/DELAYLOAD:msi.dll /DELAYLOAD:WINTRUST.dll\")\ntarget_precompile_headers(wslg REUSE_FROM common)\nset_target_properties(wslg PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/wslg/main.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.cpp\n\nAbstract:\n\n    This file contains the entry point for wslg.exe.\n\n--*/\n\n#include \"precomp.h\"\n\nint WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)\n{\n    return wsl::windows::common::WslClient::Main(GetCommandLineW());\n}\n"
  },
  {
    "path": "src/windows/wslg/main.rc",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.rc\n\nAbstract:\n\n    This file contains resources for wslg.\n\n--*/\n\n#include <windows.h>\n#include \"resource.h\"\n#include \"wslversioninfo.h\"\n\n#define VER_INTERNALNAME_STR \"wslg.exe\"\n#define VER_ORIGINALFILENAME_STR \"wslg.exe\"\n\n#define VER_FILETYPE VFT_APP\n#define VER_FILESUBTYPE VFT2_UNKNOWN\n#define VER_FILEDESCRIPTION_STR \"Windows Subsystem for Linux\"\nID_ICON ICON PRELOAD DISCARDABLE \"..\\..\\..\\Images\\wsl.ico\"\n\n#include <common.ver>\n"
  },
  {
    "path": "src/windows/wslg/resource.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    resource.h\n\nAbstract:\n\n    This file contains resource declarations for wslg.exe\n\n--*/\n\n#define ID_ICON 1\n"
  },
  {
    "path": "src/windows/wslhost/CMakeLists.txt",
    "content": "set(SOURCES\n    main.cpp\n    main.rc)\n\nset(HEADERS\n    resource.h)\n\nadd_executable(wslhost WIN32 ${SOURCES} ${HEADERS})\nadd_dependencies(wslhost\n                 common)\n\ntarget_link_libraries(wslhost\n                      ${COMMON_LINK_LIBRARIES}\n                      common\n                      runtimeobject.lib)\n\ntarget_precompile_headers(wslhost REUSE_FROM common)\nset_target_properties(wslhost PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/wslhost/main.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.cpp\n\nAbstract:\n\n    This file contains the entrypoint for wslhost.\n\n--*/\n\n#include \"precomp.h\"\n#include \"CommandLine.h\"\n#include <NotificationActivationCallback.h>\n#include <windows.ui.notifications.h>\n\nusing namespace ABI::Windows::Data::Xml::Dom;\nusing namespace ABI::Windows::UI::Notifications;\nusing namespace Microsoft::WRL;\nusing namespace wsl::windows::common;\nusing namespace wsl::shared;\n\nnamespace {\n\n// Event used to signal that the COM server should exit.\nwil::unique_event g_exitEvent;\n\nvoid AddComRef()\n{\n    CoAddRefServerProcess();\n}\n\nvoid ReleaseComRef()\n{\n    if (CoReleaseServerProcess() == 0)\n    {\n        g_exitEvent.SetEvent();\n    }\n}\n\nvoid ShellExec(_In_ LPCWSTR operation, _In_ LPCWSTR file, _In_ LPCWSTR args)\n{\n    THROW_LAST_ERROR_IF(reinterpret_cast<intptr_t>(::ShellExecuteW(nullptr, operation, file, args, nullptr, SW_SHOW)) < 32);\n}\n\nvoid LaunchWsl(_In_ LPCWSTR args)\n{\n    const auto path = wsl::windows::common::wslutil::GetBasePath() / L\"wsl.exe\";\n    ShellExec(L\"runas\", path.c_str(), args);\n}\n\n} // namespace\n\nclass DECLSPEC_UUID(\"2B9C59C3-98F1-45C8-B87B-12AE3C7927E8\") NotificationActivator\n    : public winrt::implements<NotificationActivator, INotificationActivationCallback>\n{\npublic:\n    NotificationActivator()\n    {\n        AddComRef();\n    }\n\n    ~NotificationActivator() override\n    {\n        ReleaseComRef();\n    }\n\n    STDMETHODIMP Activate(_In_ LPCWSTR appUserModelId, _In_ LPCWSTR invokedArgs, _In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA* data, ULONG dataCount) noexcept override\n    try\n    {\n        // Log telemetry when a WSL notification is activated, used to determine user engagement for notifications\n        WSL_LOG_TELEMETRY(\"NotificationActivate\", PDT_ProductAndServicePerformance, TraceLoggingValue(invokedArgs, \"Arguments\"));\n\n        ArgumentParser parser(invokedArgs, wslhost::binary_name, 0);\n        parser.AddArgument(\n            []() {\n                std::wstring path;\n                THROW_IF_FAILED(wil::GetSystemDirectoryW(path));\n\n                ShellExec(L\"runas\", (std::filesystem::path(std::move(path)) / L\"eventvwr.exe\").c_str(), L\"/c:Application\");\n            },\n            wslhost::event_viewer_arg);\n\n        parser.AddArgument([]() { ShellExec(nullptr, L\"https://github.com/microsoft/WSL/releases\", nullptr); }, wslhost::release_notes_arg);\n\n        parser.AddArgument([](auto) { LaunchWsl(WSL_UPDATE_ARG); }, wslhost::update_arg);\n\n        parser.AddArgument(\n            [](auto) {\n                LaunchWsl(std::format(L\"{} {} {}\", WSL_INSTALL_ARG, WSL_INSTALL_ARG_NO_DISTRIBUTION_OPTION, WSL_INSTALL_ARG_PROMPT_BEFORE_EXIT_OPTION)\n                              .c_str());\n            },\n            wslhost::install_prerequisites_arg);\n\n        parser.AddArgument(\n            [](LPCWSTR input) {\n                if (wsl::shared::string::IsEqual(input, wslhost::docs_arg_filesystem_url, false))\n                {\n                    ShellExec(nullptr, wslhost::docs_arg_filesystem_url, nullptr);\n                }\n                else\n                {\n                    THROW_HR_MSG(E_INVALIDARG, \"Unexpected docs arg: %ls\", input);\n                }\n            },\n            wslhost::docs_arg);\n\n        parser.AddArgument(\n            [](LPCWSTR input) {\n                if (wsl::shared::string::IsEqual(input, LXSS_NOTIFICATION_DRVFS_PERF_DISABLED, false))\n                {\n                    const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\n                    wsl::windows::common::registry::WriteDword(lxssKey.get(), LXSS_NOTIFICATIONS_KEY, LXSS_NOTIFICATION_DRVFS_PERF_DISABLED, 1);\n                }\n                else\n                {\n                    THROW_HR_MSG(E_INVALIDARG, \"Unexpected notification arg: %ls\", input);\n                }\n            },\n            wslhost::disable_notification_arg);\n\n        parser.Parse();\n\n        return S_OK;\n    }\n    CATCH_RETURN()\n};\n\nclass NotificationActivatorFactory : public winrt::implements<NotificationActivatorFactory, IClassFactory>\n{\npublic:\n    STDMETHODIMP CreateInstance(_In_ IUnknown* outer, REFIID iid, _COM_Outptr_ void** result) noexcept override\n    try\n    {\n        *result = nullptr;\n        THROW_HR_IF(CLASS_E_NOAGGREGATION, outer != nullptr);\n\n        return winrt::make<NotificationActivator>()->QueryInterface(iid, result);\n    }\n    CATCH_RETURN()\n\n    STDMETHODIMP LockServer(BOOL lock) noexcept override\n    {\n        if (lock)\n        {\n            AddComRef();\n        }\n        else\n        {\n            ReleaseComRef();\n        }\n\n        return S_OK;\n    }\n};\n\nint WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int)\ntry\n{\n    wsl::windows::common::wslutil::ConfigureCrt();\n    wsl::windows::common::wslutil::InitializeWil();\n\n    // Initialize logging.\n    WslTraceLoggingInitialize(LxssTelemetryProvider, !wsl::shared::OfficialBuild);\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [] { WslTraceLoggingUninitialize(); });\n\n    // Initialize COM.\n    auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);\n    wsl::windows::common::wslutil::CoInitializeSecurity();\n\n    // Initialize winsock.\n    WSADATA data;\n    THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &data));\n\n    // Parse arguments.\n    wil::unique_handle event{};\n    GUID distroId{GUID_NULL};\n    wil::unique_handle handle{};\n    wil::unique_handle parent{};\n    GUID vmId{GUID_NULL};\n    wil::unique_com_class_object_cookie cookie;\n\n    wsl::shared::ArgumentParser parser(GetCommandLineW(), wslhost::binary_name);\n    parser.AddArgument(distroId, wslhost::distro_id_option);\n    parser.AddArgument(Handle(handle), wslhost::handle_option);\n    parser.AddArgument(Handle(event), wslhost::event_option);\n    parser.AddArgument(Handle(parent), wslhost::parent_option);\n    parser.AddArgument(vmId, wslhost::vm_id_option);\n    parser.AddArgument(\n        [&](auto) {\n            // Create an event to be signaled when the last COM object is released.\n            g_exitEvent = wil::unique_event(wil::EventOptions::ManualReset);\n\n            THROW_IF_FAILED(::CoRegisterClassObject(\n                __uuidof(NotificationActivator), winrt::make<NotificationActivatorFactory>().get(), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &cookie));\n\n            return 0;\n        },\n        wslhost::embedding_option);\n\n    parser.Parse();\n\n    if (cookie)\n    {\n        // Wait until all objects have been released.\n        g_exitEvent.wait();\n\n        return 0;\n    }\n\n    WI_ASSERT(GetCurrentPackageId(nullptr, nullptr) != ERROR_SUCCESS);\n\n    // Launch the interop server.\n    //\n    // See GitHub #7568. There needs to be a console for interop.\n    // From GitHub #8161 we learned we can't be attached to the same\n    // console as wsl.exe. If we are we will be terminated and unable\n    // to serve daemonized processes after the console is closed.\n    wsl::windows::common::helpers::CreateConsole(nullptr);\n\n    // Register this process with the instance's lifetime management.\n    auto service = wil::CoCreateInstance<LxssUserSession, ILxssUserSession>(CLSCTX_LOCAL_SERVER);\n    if (!IsEqualGUID(distroId, GUID_NULL))\n    {\n        ClientExecutionContext context(false);\n\n        service->CreateInstance(\n            &distroId, (LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE | LXSS_CREATE_INSTANCE_FLAGS_OPEN_EXISTING), context.OutError());\n    }\n\n    // Signal the registration complete event if one was supplied.\n    if (event)\n    {\n        THROW_IF_WIN32_BOOL_FALSE(SetEvent(event.get()));\n    }\n\n    // If a parent process handle was supplied, wait for the parent\n    // process to exit before starting the worker loop.\n    if (parent)\n    {\n        WaitForSingleObject(parent.get(), INFINITE);\n    }\n\n    // Begin handling interop requests.\n    if (IsEqualGUID(vmId, GUID_NULL))\n    {\n        wsl::windows::common::interop::WorkerThread(std::move(handle));\n    }\n    else\n    {\n        wsl::shared::SocketChannel channel{wil::unique_socket{reinterpret_cast<SOCKET>(handle.release())}, \"Interop-wslhost\"};\n\n        // This is required because there could have been messages between the process and wsl.exe, and wslhost has no way to know what the sequence numbers were.\n        channel.IgnoreSequenceNumbers();\n\n        wsl::windows::common::interop::VmModeWorkerThread(channel, vmId, true);\n    }\n\n    return 0;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return 1;\n}\n"
  },
  {
    "path": "src/windows/wslhost/main.rc",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.rc\n\nAbstract:\n\n    This file contains resources for wslhost.\n\n--*/\n\n#include <windows.h>\n#include \"resource.h\"\n#include \"wslversioninfo.h\"\n\n#define VER_INTERNALNAME_STR \"wslhost.exe\"\n#define VER_ORIGINALFILENAME_STR \"wslhost.exe\"\n\n#define VER_FILEDESCRIPTION_STR \"Windows Subsystem for Linux\"\nID_ICON ICON PRELOAD DISCARDABLE \"..\\..\\..\\Images\\wsl.ico\"\n\n#include <common.ver>\n"
  },
  {
    "path": "src/windows/wslhost/resource.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    resource.h\n\nAbstract:\n\n    This file contains resource declarations for wslhost.exe\n\n--*/\n\n#define ID_ICON 1\n"
  },
  {
    "path": "src/windows/wslinstall/CMakeLists.txt",
    "content": "set(SOURCES\n    DllMain.cpp\n    version.rc)\n\nadd_library(wslinstall SHARED ${SOURCES} wslinstall.def)\n\ntarget_include_directories(wslinstall PUBLIC ${CMAKE_CURRENT_LIST_DIR})\nset_target_properties(wslinstall PROPERTIES FOLDER windows)\ntarget_precompile_headers(wslinstall REUSE_FROM common)\n\ntarget_link_libraries(wslinstall\n                      ${COMMON_LINK_LIBRARIES}\n                      ${MSI_LINK_LIBRARIES}\n                      common\n                      legacy_stdio_definitions\n                      Crypt32.lib\n                      sfc.lib)"
  },
  {
    "path": "src/windows/wslinstall/DllMain.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    DllMain.cpp\n\nAbstract:\n\n    This file contains various methods used during MSI installation (see package.wix.in)\n\n--*/\n\n#include \"precomp.h\"\n#include \"install.h\"\n#include <msiquery.h>\n#include <winrt/Windows.ApplicationModel.Core.h>\n#include <winrt/Windows.Foundation.Collections.h>\n#include <winrt/windows.management.deployment.h>\n#include <Sfc.h>\n#include \"defs.h\"\n\nusing unique_msi_handle = wil::unique_any<MSIHANDLE, decltype(MsiCloseHandle), &MsiCloseHandle>;\n\nusing namespace wsl::windows::common::registry;\nusing namespace wsl::windows::common::wslutil;\nusing namespace wsl::windows::common::install;\n\nstatic constexpr auto c_progIdPrefix{L\"App.\"};\nstatic constexpr auto c_protocolProgIdSuffix{L\".Protocol\"};\nstatic constexpr auto c_wslSettingsInstalledDirectoryPropertyName = L\"WSLSETTINGS\";\nstatic constexpr auto c_wslSettingsAppIDPropertyName = L\"WSLSETTINGSAPPID\";\nstatic constexpr auto c_wslSettingsProgIDPropertyName = L\"WSLSETTINGSPROGID\";\n\n#define IGNORE_MSIX_ERROR_IF_DIRECT_MSI_EXECUTION_SUPPORTED() \\\n    if (DoesBuildSupportDirectMsiExecution()) \\\n    { \\\n        WSL_LOG( \\\n            \"IgnoredMsixError\", \\\n            TraceLoggingValue(wil::ResultFromCaughtException(), \"Error\"), \\\n            TraceLoggingValue(__FUNCTION__, \"Stage\")); \\\n\\\n        return NOERROR; \\\n    }\n\n#define WSL_INSTALL_LOG(Name, ...) \\\n    { \\\n        WSL_LOG(Name, __VA_ARGS__); \\\n        WriteInstallLog(std::format(\"MSI install: {}\", Name)); \\\n    }\n\n#ifndef WSL_OFFICIAL_BUILD\nvoid TrustPackageCertificate(LPCWSTR Path)\n{\n    wil::unique_hcertstore store;\n    wil::unique_hcryptmsg msg;\n\n    WSL_LOG(\"TrustMSIXCertificate\", TraceLoggingValue(Path, \"Path\"));\n\n    // Retrieve the certificate from the MSIX\n    THROW_IF_WIN32_BOOL_FALSE(CryptQueryObject(\n        CERT_QUERY_OBJECT_FILE, Path, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, nullptr, nullptr, nullptr, &store, &msg, nullptr));\n\n    const wil::unique_cert_context cert{\n        CertFindCertificateInStore(store.get(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, nullptr)};\n    THROW_LAST_ERROR_IF(!cert);\n\n    const wil::unique_hcertstore trustedRoot{\n        CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_LOCAL_MACHINE, L\"ROOT\")};\n\n    THROW_LAST_ERROR_IF(!trustedRoot);\n\n    THROW_IF_WIN32_BOOL_FALSE(CertAddCertificateContextToStore(trustedRoot.get(), cert.get(), CERT_STORE_ADD_USE_EXISTING, nullptr));\n}\n#endif\n\nvoid ThrowIfOperationError(\n    const winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Windows::Management::Deployment::DeploymentResult, winrt::Windows::Management::Deployment::DeploymentProgress>& result,\n    const std::source_location& source = std::source_location::current())\n{\n    const auto status = result.get();\n\n    if (result.Status() == winrt::Windows::Foundation::AsyncStatus::Error)\n    {\n        THROW_HR_MSG(result.ErrorCode(), \"Source: %hs() - %hs:%lu\", source.function_name(), source.file_name(), source.line());\n    }\n\n    THROW_IF_FAILED_MSG(status.ExtendedErrorCode(), \"%ls\", status.ErrorText().c_str());\n}\n\nstd::wstring GetMsiProperty(MSIHANDLE install, LPCWSTR name)\n{\n    DWORD size{};\n    std::wstring output(1, '\\0');\n    UINT result = MsiGetProperty(install, name, output.data(), &size);\n    THROW_HR_IF_MSG(E_UNEXPECTED, result != ERROR_SUCCESS && result != ERROR_MORE_DATA, \"MsiGetProperty failed with %u\", result);\n\n    output.resize(size);\n    size = static_cast<DWORD>(output.size() + 1);\n    result = MsiGetProperty(install, name, output.data(), &size);\n    THROW_HR_IF_MSG(E_UNEXPECTED, result != ERROR_SUCCESS, \"MsiGetProperty for '%s' failed with %u\", name, result);\n\n    WI_ASSERT(size == output.size());\n    return output;\n}\n\nstd::wstring GetInstallTarget(MSIHANDLE install)\n{\n    return GetMsiProperty(install, L\"CustomActionData\");\n}\n\nvoid DisplayError(MSIHANDLE install, LPCWSTR message)\n{\n    const unique_msi_handle record{MsiCreateRecord(0)};\n    MsiRecordSetString(record.get(), 0, message);\n\n    MsiProcessMessage(install, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), record.get());\n}\n\nvoid DeleteRegistryKeyIfVolatile(LPCWSTR Parent, LPCWSTR Key)\n{\n\n    const std::wstring path = Parent + std::wstring(L\"\\\\\") + Key;\n    auto [key, error] = wsl::windows::common::registry::OpenKeyNoThrow(HKEY_LOCAL_MACHINE, path.c_str(), KEY_READ);\n\n    if (FAILED(error))\n    {\n        THROW_HR_IF(error, error != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && error != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND));\n\n        // The key doesn't exist, nothing to do.\n        return;\n    }\n\n    if (!wsl::windows::common::registry::IsKeyVolatile(key.get()))\n    {\n        // Registry key is not volatile, nothing to do.\n        return;\n    }\n\n    WSL_LOG(\"CleanMsixRegistryKeys\", TraceLoggingValue(Parent, \"Parent\"), TraceLoggingValue(Key, \"Key\"));\n\n    const auto parent = wsl::windows::common::registry::OpenKey(HKEY_LOCAL_MACHINE, Parent, KEY_ALL_ACCESS);\n    wsl::windows::common::registry::DeleteKey(parent.get(), Key);\n}\n\nbool IsWindowsServerCore()\n{\n    wil::unique_hkey key;\n    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L\"Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Server\\\\ServerLevels\", 0, KEY_READ, &key) == ERROR_SUCCESS)\n    {\n        // NanoServer must be 1, or ServerCore must be 1, Server-Gui-Mgmt must be zero or not present, and Server-Gui-Shell must be zero or not present\n        DWORD value = 0;\n        DWORD valueSize = sizeof(value);\n        if ((RegGetValue(key.get(), nullptr, L\"NanoServer\", RRF_RT_REG_DWORD, nullptr, &value, &valueSize) == ERROR_SUCCESS) && (value == 1))\n        {\n            return true;\n        }\n        else\n        {\n            value = 0;\n            valueSize = sizeof(value);\n            if ((RegGetValue(key.get(), nullptr, L\"ServerCore\", RRF_RT_REG_DWORD, nullptr, &value, &valueSize) == ERROR_SUCCESS) &&\n                (value == 1))\n            {\n                value = 0;\n                valueSize = sizeof(value);\n                RegGetValue(key.get(), nullptr, L\"Server-Gui-Mgmt\", (RRF_RT_REG_DWORD | RRF_ZEROONFAILURE), nullptr, &value, &valueSize);\n                if (value == 0)\n                {\n                    value = 0;\n                    valueSize = sizeof(value);\n                    RegGetValue(key.get(), nullptr, L\"Server-Gui-Shell\", (RRF_RT_REG_DWORD | RRF_ZEROONFAILURE), nullptr, &value, &valueSize);\n                    return value == 0;\n                }\n            }\n        }\n    }\n\n    return false;\n}\n\nbool IsWindowsServerCoreWithMsiSupport()\n{\n    return IsWindowsServerCore() && wsl::windows::common::helpers::GetWindowsVersion().BuildNumber >=\n                                        wsl::windows::common::helpers::WindowsBuildNumbers::Germanium;\n}\n\nbool DoesBuildSupportDirectMsiExecution()\n{\n    const auto buildInfo = wsl::windows::common::helpers::GetWindowsVersion();\n\n    switch (buildInfo.BuildNumber)\n    {\n\n    // For Windows 10, the fix was only serviced to 22h2 and 21h2.\n    case wsl::windows::common::helpers::WindowsBuildNumbers::Vibranium_21H2:\n        return buildInfo.UpdateBuildRevision >= 4529;\n\n    case wsl::windows::common::helpers::WindowsBuildNumbers::Vibranium_22H2:\n        return buildInfo.UpdateBuildRevision >= 4474;\n\n    case wsl::windows::common::helpers::WindowsBuildNumbers::Iron:\n        return buildInfo.UpdateBuildRevision >= 2582;\n\n    case wsl::windows::common::helpers::WindowsBuildNumbers::Cobalt:\n        return false; // cobalt builds aren't serviced anymore, so the fix wasn't backported there.\n\n    case wsl::windows::common::helpers::WindowsBuildNumbers::Nickel:\n    case wsl::windows::common::helpers::WindowsBuildNumbers::Nickel_23H2: // See: https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information\n        return buildInfo.UpdateBuildRevision >= 3672;\n\n    case wsl::windows::common::helpers::WindowsBuildNumbers::Zinc:\n        return buildInfo.UpdateBuildRevision >= 1009;\n\n    default:\n        return buildInfo.BuildNumber >= wsl::windows::common::helpers::WindowsBuildNumbers::Germanium;\n    }\n}\n\nvoid GrantDeletePermissionToSystem(SC_HANDLE Service)\n{\n    // Get the current security descriptor\n    DWORD bytesNeeded{};\n    THROW_LAST_ERROR_IF(!QueryServiceObjectSecurity(Service, DACL_SECURITY_INFORMATION, nullptr, 0, &bytesNeeded) && GetLastError() != ERROR_INSUFFICIENT_BUFFER);\n\n    std::vector<char> buffer(bytesNeeded);\n    THROW_IF_WIN32_BOOL_FALSE(\n        QueryServiceObjectSecurity(Service, DACL_SECURITY_INFORMATION, buffer.data(), static_cast<DWORD>(buffer.size()), &bytesNeeded));\n\n    // Get the DACL.\n    PACL previousAcl{};\n    BOOL present{};\n    BOOL defaulted{};\n    THROW_IF_WIN32_BOOL_FALSE(GetSecurityDescriptorDacl(buffer.data(), &present, &previousAcl, &defaulted));\n\n    // Build a new ACE for SYSTEM.\n    EXPLICIT_ACCESS access{};\n    std::wstring account = L\"SYSTEM\";\n    BuildExplicitAccessWithName(&access, account.data(), DELETE, SET_ACCESS, NO_INHERITANCE);\n\n    // Create a new ACL with the new ACE.\n    wsl::windows::common::security::unique_acl newAcl;\n\n    THROW_IF_WIN32_ERROR(SetEntriesInAcl(1, &access, previousAcl, &newAcl));\n\n    // Build a new security descriptor with that ACL.\n    SECURITY_DESCRIPTOR newDescriptor{};\n    THROW_IF_WIN32_BOOL_FALSE(InitializeSecurityDescriptor(&newDescriptor, SECURITY_DESCRIPTOR_REVISION));\n    THROW_IF_WIN32_BOOL_FALSE(SetSecurityDescriptorDacl(&newDescriptor, true, newAcl.get(), false));\n\n    // Update the service's ACL.\n    THROW_IF_WIN32_BOOL_FALSE(SetServiceObjectSecurity(Service, DACL_SECURITY_INFORMATION, &newDescriptor));\n}\n\nvoid RemoveMsixService()\ntry\n{\n    const wil::unique_schandle manager{OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS)};\n    THROW_LAST_ERROR_IF(!manager);\n\n    wil::unique_schandle wslservice{OpenService(manager.get(), L\"wslservice\", READ_CONTROL | WRITE_DAC)};\n    if (!wslservice)\n    {\n        THROW_LAST_ERROR_IF(GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST);\n\n        // wslservice doesn't exist, this is expected\n        return;\n    }\n\n    // Sanity check: Validate that this is indeed an MSIX service\n    wil::unique_hkey key = wsl::windows::common::registry::OpenKey(HKEY_LOCAL_MACHINE, L\"SYSTEM\\\\CurrentControlSet\\\\Services\", KEY_READ);\n    THROW_LAST_ERROR_IF(!key);\n\n    const auto AppUserModelId = wsl::windows::common::registry::ReadString(key.get(), L\"WSLService\", L\"AppUserModelId\", L\"\");\n    key.reset();\n\n    DWORD DeleteStatus = ERROR_NOT_SUPPORTED;\n\n    if (!AppUserModelId.empty())\n    {\n        GrantDeletePermissionToSystem(wslservice.get());\n        wslservice.reset(OpenService(manager.get(), L\"wslservice\", DELETE));\n\n        if (DeleteService(wslservice.get()))\n        {\n            DeleteStatus = NO_ERROR;\n        }\n        else\n        {\n            DeleteStatus = GetLastError();\n        }\n    }\n\n    WSL_LOG(\n        \"MsixServiceRegistrationFound\",\n        TraceLoggingValue(AppUserModelId.c_str(), \"AppModelUserId\"),\n        TraceLoggingValue(DeleteStatus, \"DeleteStatus\"));\n}\nCATCH_LOG();\n\nbool RemoveRegistryKeyProtectionImpl(LPCWSTR Path)\n{\n    if (!SfcIsKeyProtected(HKEY_LOCAL_MACHINE, Path, KEY_WOW64_64KEY))\n    {\n        return false; // The key doesn't exist or isn't protected, nothing to do.\n    }\n\n    // Open the registry key.\n    auto key = wsl::windows::common::registry::OpenKey(HKEY_LOCAL_MACHINE, Path, KEY_READ | KEY_WRITE, REG_OPTION_BACKUP_RESTORE);\n\n    // Get its security descriptor.\n    DWORD bufferSize = 0;\n    auto result = RegGetKeySecurity(key.get(), OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, nullptr, &bufferSize);\n    THROW_WIN32_IF(result, result != ERROR_INSUFFICIENT_BUFFER);\n\n    std::vector<char> buffer(bufferSize);\n\n    result = RegGetKeySecurity(key.get(), OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, buffer.data(), &bufferSize);\n    THROW_IF_WIN32_ERROR(result);\n\n    // Get the ACL from the security descriptor\n    // N.B. 'acl' is stored inside the security descriptor buffer, and so doesn't need to be individually deleted.\n    PACL acl{};\n    BOOL present{};\n    BOOL defaulted{};\n    THROW_IF_WIN32_BOOL_FALSE(GetSecurityDescriptorDacl(reinterpret_cast<PSECURITY_DESCRIPTOR>(buffer.data()), &present, &acl, &defaulted));\n\n    // Grant write access to local administrator group.\n    // N.B. A registry key is considered protected if:\n    // - TrustedInstaller has GENERIC_ALL or KEY_FULL_ACCESS granted\n    // - No other ACL grants write access to anyone else\n    // - No deny ACL is set for TrustedInstaller\n    auto [localAdministratorsSid, sidBuffer] =\n        wsl::windows::common::security::CreateSid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);\n\n    EXPLICIT_ACCESS newAce{};\n    newAce.grfAccessMode = GRANT_ACCESS;\n    newAce.grfAccessPermissions = KEY_WRITE;\n    newAce.grfInheritance = NO_INHERITANCE;\n    BuildTrusteeWithSid(&newAce.Trustee, localAdministratorsSid);\n\n    // Create an updated ACL.\n    wsl::windows::common::security::unique_acl newAcl{};\n    THROW_IF_WIN32_ERROR(SetEntriesInAcl(1, &newAce, acl, &newAcl));\n\n    // Create a new security descriptor with the updated ACL.\n    SECURITY_DESCRIPTOR newDescriptor{};\n    THROW_IF_WIN32_BOOL_FALSE(InitializeSecurityDescriptor(&newDescriptor, SECURITY_DESCRIPTOR_REVISION));\n    THROW_IF_WIN32_BOOL_FALSE(SetSecurityDescriptorDacl(&newDescriptor, true, newAcl.get(), false));\n\n    // Update the key security descriptor.\n    THROW_IF_WIN32_ERROR_MSG(\n        RegSetKeySecurity(key.get(), DACL_SECURITY_INFORMATION, &newDescriptor), \"Failed to update key security for key: %ls\", Path);\n\n    key.reset();\n\n    if constexpr (wsl::shared::Debug)\n    {\n        THROW_HR_IF_MSG(\n            E_FAIL, SfcIsKeyProtected(HKEY_LOCAL_MACHINE, Path, KEY_WOW64_64KEY), \"Failed to remove protection for key: %ls\", Path);\n    }\n\n    return true;\n}\n\nextern \"C\" UINT __stdcall RemoveRegistryKeyProtections(MSIHANDLE install)\n{\n    try\n    {\n        auto restore = wsl::windows::common::security::AcquirePrivileges({SE_BACKUP_NAME, SE_RESTORE_NAME});\n\n        for (const auto* key : {\n                 LR\"(SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IdListAliasTranslations\\WSL)\",\n                 LR\"(SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IdListAliasTranslations\\WSLLegacy)\",\n                 LR\"(SOFTWARE\\Classes\\Directory\\Background\\shell\\WSL)\",\n                 LR\"(SOFTWARE\\Classes\\Directory\\Background\\shell\\WSL\\command)\",\n                 LR\"(SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6})\",\n                 LR\"(SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel)\",\n             })\n        {\n            bool updated = false;\n            auto result = wil::ResultFromException([&updated, key]() { updated = RemoveRegistryKeyProtectionImpl(key); });\n\n            if (updated || FAILED(result))\n            {\n                WSL_LOG(\n                    \"RemoveKeyProtection\",\n                    TraceLoggingValue(key, \"key\"),\n                    TraceLoggingValue(result, \"error\"),\n                    TraceLoggingValue(updated, \"updated\"));\n            }\n        }\n    }\n    CATCH_LOG();\n\n    return NOERROR;\n}\n\nbool CleanExplorerShortcutFlags(LPCWSTR Sid)\n{\n    constexpr auto valueName = L\"Attributes\";\n\n    const auto keyPath = std::format(\n        LR\"({}\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\{{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}}\\ShellFolder)\", Sid);\n\n    auto [key, result] = wsl::windows::common::registry::OpenKeyNoThrow(HKEY_USERS, keyPath.c_str(), KEY_READ | KEY_WRITE);\n\n    if (!SUCCEEDED(result))\n    {\n        // Either the key doesn't exist, or the user isn't logged in.\n        THROW_HR_IF(result, result != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && result != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND));\n        return false;\n    }\n\n    auto flags = wsl::windows::common::registry::ReadDword(key.get(), nullptr, valueName, 0);\n    if (WI_IsFlagClear(flags, SFGAO_NONENUMERATED))\n    {\n        // The problematic flag is not set, nothing to do.\n        return false;\n    }\n\n    WI_ClearFlag(flags, SFGAO_NONENUMERATED);\n\n    wsl::windows::common::registry::WriteDword(key.get(), nullptr, valueName, flags);\n    return true;\n}\n\nextern \"C\" UINT __stdcall CleanExplorerState(MSIHANDLE install)\n{\n    // N.B. This method is imperfect because it can only access the registry hives of logged in users.\n\n    try\n    {\n        const auto profiles = wsl::windows::common::registry::OpenKey(\n            HKEY_LOCAL_MACHINE, LR\"(SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList)\", KEY_READ);\n\n        // List all available profiles on the machine.\n        for (const auto& [name, key] : wsl::windows::common::registry::EnumKeys(profiles.get(), KEY_READ))\n        {\n            // Look for full profiles.\n            if (wsl::windows::common::registry::ReadDword(key.get(), nullptr, L\"FullProfile\", 0))\n            {\n                bool changed = false;\n                auto result = wil::ResultFromException([&]() { changed = CleanExplorerShortcutFlags(name.c_str()); });\n\n                if (changed || FAILED(result))\n                {\n                    WSL_LOG(\n                        \"ClearExplorerFlag\",\n                        TraceLoggingValue(name.c_str(), \"sid\"),\n                        TraceLoggingValue(result, \"error\"),\n                        TraceLoggingValue(changed, \"changed\"));\n                }\n            }\n        }\n    }\n    CATCH_LOG();\n\n    return NOERROR;\n}\n\nextern \"C\" UINT __stdcall CleanMsixState(MSIHANDLE install)\n{\n    try\n    {\n        WSL_LOG(\"CleanMsixState\");\n\n        const std::map<LPCWSTR, LPCWSTR> keys{\n            {L\"SYSTEM\\\\CurrentControlSet\\\\Services\\\\EventLog\\\\Application\", L\"WSL\"},\n            {L\"SOFTWARE\\\\Classes\\\\CLSID\", L\"{7e6ad219-d1b3-42d5-b8ee-d96324e64ff6}\"},\n            {L\"SOFTWARE\\\\Classes\\\\AppID\", L\"{17696EAC-9568-4CF5-BB8C-82515AAD6C09}\"},\n            {L\"SOFTWARE\\\\Microsoft\\\\Terminal Server Client\", L\"Default\"},\n            {L\"SOFTWARE\\\\Microsoft\\\\Terminal Server Client\\\\Default\", L\"OptionalAddIns\"},\n            {L\"SOFTWARE\\\\Microsoft\\\\Terminal Server Client\\\\Default\\\\OptionalAddIns\", L\"WSLDVC_PACKAGE\"}};\n\n        for (const auto& e : keys)\n        {\n            try\n            {\n                DeleteRegistryKeyIfVolatile(e.first, e.second);\n            }\n            catch (...)\n            {\n                LOG_CAUGHT_EXCEPTION_MSG(\"Failed to clear registry key: %ls/%ls\", e.first, e.second);\n            }\n        }\n\n        /*\n         * Because of a probable bug in MSIX / Packaged COM, it's possible that an old registration is still present on the machine,\n         * which will break instantiations of LxssUserSessions.\n         * Because this method executes after all MSIX packages have been removed, we know that this registration shouldn't be there,\n         * so delete it if it still happens to be there.\n         * See: https://github.com/microsoft/WSL/issues/10782\n         */\n\n        try\n        {\n\n            const auto packagedComClassIndex{\n                wsl::windows::common::registry::OpenKey(HKEY_LOCAL_MACHINE, L\"SOFTWARE\\\\Classes\\\\PackagedCom\\\\ClassIndex\", KEY_WRITE)};\n\n            if (wsl::windows::common::registry::DeleteKey(packagedComClassIndex.get(), L\"{A9B7A1B9-0671-405C-95F1-E0612CB4CE7E}\"))\n            {\n                WSL_LOG(\"OldComRegistrationCleared\");\n            }\n        }\n        CATCH_LOG();\n\n        /*\n         * Because of another probable MSIX bug, wslservice can sometimes be left even after an WSL < 2.0 package is removed, which causes the installation to fail.\n         * If found, we delete the service registration.\n         * See: https://github.com/microsoft/WSL/issues/10831\n         */\n\n        RemoveMsixService();\n    }\n    catch (...)\n    {\n        LOG_CAUGHT_EXCEPTION();\n    }\n\n    // Always succeed here since failure in this method aren't fatal.\n    return NOERROR;\n}\n\nextern \"C\" UINT __stdcall DeprovisionMsix(MSIHANDLE install)\ntry\n{\n    WSL_INSTALL_LOG(\"DeprovisionMsix\");\n\n    const winrt::Windows::Management::Deployment::PackageManager packageManager;\n    const auto result = packageManager.DeprovisionPackageForAllUsersAsync(wsl::windows::common::wslutil::c_msixPackageFamilyName).get();\n    LOG_IF_FAILED_MSG(result.ExtendedErrorCode(), \"%ls\", result.ErrorText().c_str());\n\n    return NOERROR;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n\n    IGNORE_MSIX_ERROR_IF_DIRECT_MSI_EXECUTION_SUPPORTED();\n\n    const auto error = wsl::windows::common::wslutil::GetErrorString(wil::ResultFromCaughtException());\n    DisplayError(install, wsl::shared::Localization::MessagedFailedToRemoveMsix(error).c_str());\n\n    return ERROR_INSTALL_FAILURE;\n}\n\nextern \"C\" UINT __stdcall RemoveMsixAsSystem(MSIHANDLE install)\ntry\n{\n    WSL_INSTALL_LOG(\"RemoveMsixAsSystem\");\n\n    const winrt::Windows::Management::Deployment::PackageManager packageManager;\n\n    for (const auto& e : packageManager.FindPackages(wsl::windows::common::wslutil::c_msixPackageFamilyName))\n    {\n        WSL_LOG(\"RemovePackage\", TraceLoggingValue(e.Id().FullName().c_str(), \"FullName\"));\n\n        ThrowIfOperationError(packageManager.RemovePackageAsync(\n            e.Id().FullName(), winrt::Windows::Management::Deployment::RemovalOptions::RemoveForAllUsers));\n    }\n\n    return NOERROR;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n\n    IGNORE_MSIX_ERROR_IF_DIRECT_MSI_EXECUTION_SUPPORTED();\n\n    const auto error = wsl::windows::common::wslutil::GetErrorString(wil::ResultFromCaughtException());\n    DisplayError(install, wsl::shared::Localization::MessagedFailedToRemoveMsix(error).c_str());\n\n    return ERROR_INSTALL_FAILURE;\n}\n\nextern \"C\" UINT __stdcall RemoveMsixAsUser(MSIHANDLE install)\ntry\n{\n    WSL_INSTALL_LOG(\"RemoveMsixAsUser\");\n\n    const winrt::Windows::Management::Deployment::PackageManager packageManager;\n\n    for (const auto& e : packageManager.FindPackagesForUser(L\"\", wsl::windows::common::wslutil::c_msixPackageFamilyName))\n    {\n        WSL_LOG(\"RemovePackage\", TraceLoggingValue(e.Id().FullName().c_str(), \"FullName\"));\n\n        ThrowIfOperationError(packageManager.RemovePackageAsync(e.Id().FullName()));\n    }\n\n    return NOERROR;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n\n    IGNORE_MSIX_ERROR_IF_DIRECT_MSI_EXECUTION_SUPPORTED();\n\n    const auto error = wsl::windows::common::wslutil::GetErrorString(wil::ResultFromCaughtException());\n    DisplayError(install, wsl::shared::Localization::MessagedFailedToRemoveMsix(error).c_str());\n\n    return ERROR_INSTALL_FAILURE;\n}\n\nwsl::windows::common::filesystem::TempFile ExtractMsix(MSIHANDLE install)\n{\n    // N.B. We need to open the database this way instead of calling MsiGetActiveDatabase() because\n    // this is deferred action so we don't have access to the MSI context here.\n    // The MSIX needs to be extracted like this because in the case of an upgrade this action runs before 'MoveFiles' so the WSL directory isn't available yet.\n\n    const auto installTarget = GetInstallTarget(install);\n\n    unique_msi_handle database;\n    THROW_IF_WIN32_ERROR_MSG(\n        MsiOpenDatabase(installTarget.c_str(), MSIDBOPEN_READONLY, &database), \"Failed to open database: %ls\", installTarget.c_str());\n\n    THROW_LAST_ERROR_IF(!database);\n\n    unique_msi_handle view;\n    THROW_IF_WIN32_ERROR(MsiDatabaseOpenView(database.get(), L\"SELECT Data,Name FROM Binary WHERE Name='msixpackage'\", &view));\n\n    THROW_IF_WIN32_ERROR(MsiViewExecute(view.get(), NULL));\n\n    unique_msi_handle record;\n    THROW_IF_WIN32_ERROR(MsiViewFetch(view.get(), &record));\n\n    auto file = wsl::windows::common::filesystem::TempFile(\n        GENERIC_WRITE, 0, CREATE_ALWAYS, wsl::windows::common::filesystem::TempFileFlags::None, L\"msix\");\n\n    std::vector<char> buffer(1024 * 1024);\n    while (true)\n    {\n        DWORD size = static_cast<DWORD>(buffer.size());\n        THROW_IF_WIN32_ERROR(MsiRecordReadStream(record.get(), 1, buffer.data(), &size));\n        THROW_IF_WIN32_BOOL_FALSE(WriteFile(file.Handle.get(), buffer.data(), size, nullptr, nullptr));\n\n        if (size < buffer.size())\n        {\n            break;\n        }\n    }\n\n    return file;\n}\n\nextern \"C\" UINT __stdcall InstallMsixAsUser(MSIHANDLE install)\ntry\n{\n    WSL_INSTALL_LOG(\"InstallMsixAsUser\");\n\n    // RegisterPackageByFamilyNameAsync() cannot be run as SYSTEM.\n    //  If this thread runs as SYSTEM, simply skip this step.\n    if (wsl::windows::common::security::IsTokenLocalSystem(nullptr))\n    {\n        WSL_LOG(\"InstallMsixAsUserSkipped\");\n        return NOERROR;\n    }\n\n    const winrt::Windows::Management::Deployment::PackageManager packageManager;\n    ThrowIfOperationError(packageManager.RegisterPackageByFamilyNameAsync(\n        wsl::windows::common::wslutil::c_msixPackageFamilyName,\n        nullptr,\n        winrt::Windows::Management::Deployment::DeploymentOptions::ForceTargetApplicationShutdown |\n            winrt::Windows::Management::Deployment::DeploymentOptions::ForceApplicationShutdown,\n        nullptr,\n        nullptr));\n\n    return NOERROR;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n\n    IGNORE_MSIX_ERROR_IF_DIRECT_MSI_EXECUTION_SUPPORTED();\n\n    const auto errorCode = wil::ResultFromCaughtException();\n\n    const auto error = wsl::windows::common::wslutil::GetErrorString(errorCode);\n    DisplayError(install, wsl::shared::Localization::MessagedFailedToInstallMsix(error).c_str());\n\n    return ERROR_INSTALL_FAILURE;\n}\n\nextern \"C\" UINT __stdcall InstallMsix(MSIHANDLE install)\ntry\n{\n    auto msixFile = ExtractMsix(install);\n\n    // Release a file handle to the MSIX file so that it can be installed.\n    msixFile.Handle.reset();\n\n    WSL_INSTALL_LOG(\"InstallMsix\", TraceLoggingValue(msixFile.Path.c_str(), \"Path\"));\n\n    winrt::Windows::Management::Deployment::PackageManager packageManager;\n\n    winrt::Windows::Foundation::Uri uri(msixFile.Path.c_str());\n    winrt::Windows::Management::Deployment::StagePackageOptions options;\n    options.ForceUpdateFromAnyVersion(true);\n\n    try\n    {\n        try\n        {\n            ThrowIfOperationError(packageManager.StagePackageByUriAsync(uri, options));\n        }\n        catch (...)\n        {\n            // For convenience, automatically trust the MSIX's certificate if this is NOT an official build and\n            // the package installation failed because of an untrusted certificate.\n#ifndef WSL_OFFICIAL_BUILD\n            auto error = wil::ResultFromCaughtException();\n            if (error == CERT_E_UNTRUSTEDROOT)\n            {\n                TrustPackageCertificate(msixFile.Path.c_str());\n                ThrowIfOperationError(packageManager.StagePackageByUriAsync(uri, options));\n            }\n#else\n            throw;\n#endif\n        }\n\n        ThrowIfOperationError(packageManager.ProvisionPackageForAllUsersAsync(wsl::windows::common::wslutil::c_msixPackageFamilyName));\n    }\n    catch (...)\n    {\n        // On Windows Server, ProvisionPackageForAllUsersAsync() fails with ERROR_NOT_SUPPORTED or ERROR_INSTALL_FAILED.\n        // Using powershell as a fallback in case we hit this issue.\n        auto error = wil::ResultFromCaughtException();\n        if ((error == REGDB_E_CLASSNOTREG || error == HRESULT_FROM_WIN32(ERROR_INSTALL_REGISTRATION_FAILURE) ||\n             error == HRESULT_FROM_WIN32(ERROR_INSTALL_PACKAGE_NOT_FOUND)) &&\n            IsWindowsServerCoreWithMsiSupport())\n        {\n            // MSIX applications are not supported on ServerCore SKU's so as long as this build has direct MSI support\n            // the installation can continue.\n            return NOERROR;\n        }\n        else if ((error == HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) || error == HRESULT_FROM_WIN32(ERROR_INSTALL_FAILED)) && IsWindowsServer())\n        {\n            std::wstring commandLine;\n            wil::GetSystemDirectoryW(commandLine);\n\n            // N.B. powershell is always installed under 'v1.0' so this path is constant.\n            commandLine +=\n                L\"\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -Command \"\n                L\"Add-AppxProvisionedPackage \"\n                L\"-Online -PackagePath \\\"\" +\n                msixFile.Path.wstring() + L\"\\\" -SkipLicense\";\n\n            WSL_LOG(\"CallPS\", TraceLoggingValue(commandLine.c_str(), \"CommandLine\"));\n\n            wsl::windows::common::SubProcess process(nullptr, commandLine.c_str());\n            process.SetFlags(CREATE_NO_WINDOW);\n            process.SetShowWindow(SW_HIDE);\n\n            auto output = process.RunAndCaptureOutput();\n            if (output.ExitCode != 0)\n            {\n                if (output.Stderr.size() > 250) // Limit how big the error message can get\n                {\n                    output.Stderr.resize(250);\n                }\n\n                DisplayError(install, wsl::shared::Localization::MessagedFailedToInstallMsix(output.Stderr).c_str());\n\n                return ERROR_INSTALL_FAILURE;\n            }\n        }\n        else\n        {\n            THROW_IF_FAILED(error);\n        }\n    }\n\n    WSL_LOG(\"InstallMsixComplete\");\n\n    return NOERROR;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n\n    IGNORE_MSIX_ERROR_IF_DIRECT_MSI_EXECUTION_SUPPORTED();\n\n    auto error = wsl::windows::common::wslutil::GetErrorString(wil::ResultFromCaughtException());\n    DisplayError(install, wsl::shared::Localization::MessagedFailedToInstallMsix(error).c_str());\n\n    return ERROR_INSTALL_FAILURE;\n}\n\nextern \"C\" UINT __stdcall WslFinalizeInstallation(MSIHANDLE install)\n{\n    try\n    {\n        WSL_INSTALL_LOG(\"WslFinalizeInstallation\");\n    }\n    CATCH_LOG();\n\n    return NOERROR;\n}\n\nextern \"C\" UINT __stdcall WslValidateInstallation(MSIHANDLE install)\ntry\n{\n    WSL_INSTALL_LOG(\"WslValidateInstallation\");\n\n    // TODO: Use a more precise version check so we don't install if the Windows build doesn't support lifted.\n\n    if (wsl::windows::common::helpers::GetWindowsVersion().BuildNumber < wsl::windows::common::helpers::Vibranium)\n    {\n        DisplayError(install, wsl::windows::common::wslutil::GetErrorString(WSL_E_OS_NOT_SUPPORTED).c_str());\n        return ERROR_INSTALL_FAILURE;\n    }\n\n    return NOERROR;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n\n    return ERROR_INSTALL_FAILURE;\n}\n\nvoid RegisterLspCategoriesImpl(DWORD flags)\n{\n    const auto installRoot = wsl::windows::common::wslutil::GetMsiPackagePath();\n    THROW_HR_IF(E_INVALIDARG, !installRoot.has_value());\n\n    for (const auto& e : {L\"wsl.exe\", L\"wslhost.exe\", L\"wslrelay.exe\", L\"wslg.exe\", L\"wslservice.exe\"})\n    {\n        auto executable = installRoot.value() + e;\n        INT error{};\n\n        DWORD previous{};\n        LOG_HR_IF_MSG(\n            E_UNEXPECTED,\n            WSCSetApplicationCategory(executable.c_str(), static_cast<DWORD>(executable.size()), nullptr, 0, flags, &previous, &error) == SOCKET_ERROR,\n            \"Failed to register LSP category for : %ls, flags: %lu, error: %i\",\n            executable.c_str(),\n            flags,\n            error);\n    }\n}\n\nextern \"C\" UINT __stdcall RegisterLspCategories(MSIHANDLE install)\n{\n    /*\n     * This logic is required because some VPN providers register LSP components that break WSL.\n     * See: https://github.com/microsoft/WSL/issues/4177/\n     */\n\n    try\n    {\n        WSL_LOG(\"RegisterLspCategories\");\n        RegisterLspCategoriesImpl(LSP_SYSTEM);\n    }\n    CATCH_LOG();\n\n    // Failures in this method aren't fatal.\n    return NOERROR;\n}\n\nextern \"C\" UINT __stdcall UnregisterLspCategories(MSIHANDLE install)\n{\n    try\n    {\n        WSL_LOG(\"UnregisterLspCategories\");\n        RegisterLspCategoriesImpl(0); // '0' means removing the entry.\n    }\n    CATCH_LOG();\n\n    // Failures in this method aren't fatal.\n    return NOERROR;\n}\n\nstd::wstring GetWslSettingsInstalledExePath(MSIHANDLE install)\n{\n    const auto wslSettingsInstallFolder = GetMsiProperty(install, c_wslSettingsInstalledDirectoryPropertyName);\n    THROW_HR_IF_MSG(E_UNEXPECTED, wslSettingsInstallFolder.empty(), \"GetMsiProperty for '%s' resulted in unexpected empty string\", c_wslSettingsInstalledDirectoryPropertyName);\n\n    auto wslSettingsInstalledExePath = std::filesystem::path(wslSettingsInstallFolder) / L\"wslsettings.exe\";\n    return wslSettingsInstalledExePath.make_preferred().wstring();\n}\n\n// The following function is borrowed directly from the Windows App SDK\nstd::wstring ComputeAppId(const std::wstring& seed)\n{\n    // Prefix = App -- Simple human readable piece to help organize these together.\n    // AppId = Prefix + Hash(seed)\n\n    const std::hash<std::wstring> hasher;\n    const auto hash = hasher(seed);\n    uint64_t hash64 = static_cast<uint64_t>(hash);\n\n    // Simulate a larger hash on 32bit platforms to keep the id length consistent.\n    if constexpr (sizeof(size_t) < sizeof(uint64_t))\n    {\n        hash64 = (static_cast<uint64_t>(hash) << 32) | static_cast<uint64_t>(hash);\n    }\n\n    wchar_t hashString[17]{}; // 16 + 1 characters for 64bit value represented as a string with a null terminator.\n    THROW_IF_FAILED(StringCchPrintf(hashString, _countof(hashString), L\"%I64x\", hash64));\n\n    std::wstring result{c_progIdPrefix};\n    result += hashString;\n    return result;\n}\n\nstd::wstring ComputeProgId(const std::wstring& appId)\n{\n    return std::wstring(appId + c_protocolProgIdSuffix);\n}\n\nextern \"C\" UINT __stdcall CalculateWslSettingsProtocolIds(MSIHANDLE install)\n{\n    try\n    {\n        WSL_LOG(\"CalculateWslSettingsProtocolIds\");\n\n        const auto wslSettingsInstalledExePath = GetWslSettingsInstalledExePath(install);\n        THROW_HR_IF_MSG(\n            E_UNEXPECTED,\n            wslSettingsInstalledExePath.empty(),\n            \"Fetching WSL Settings installed exe path resulted in unexpected empty string\");\n\n        const auto appId = ComputeAppId(wslSettingsInstalledExePath);\n        const auto progId = ComputeProgId(appId);\n\n        UINT result = MsiSetProperty(install, c_wslSettingsAppIDPropertyName, appId.c_str());\n        THROW_HR_IF_MSG(E_UNEXPECTED, result != ERROR_SUCCESS, \"MsiSetProperty for '%s' failed with %u\", c_wslSettingsAppIDPropertyName, result);\n\n        result = MsiSetProperty(install, c_wslSettingsProgIDPropertyName, progId.c_str());\n        THROW_HR_IF_MSG(E_UNEXPECTED, result != ERROR_SUCCESS, \"MsiSetProperty for '%s' failed with %u\", c_wslSettingsProgIDPropertyName, result);\n    }\n    CATCH_LOG();\n\n    // Failures in this method aren't fatal.\n\n    return NOERROR;\n}\n\nEXTERN_C BOOL STDAPICALLTYPE DllMain(_In_ HINSTANCE Instance, _In_ DWORD Reason, _In_opt_ LPVOID Reserved)\n{\n    wil::DLLMain(Instance, Reason, Reserved);\n\n    switch (Reason)\n    {\n    case DLL_PROCESS_ATTACH:\n        WslTraceLoggingInitialize(LxssTelemetryProvider, false);\n        break;\n\n    case DLL_PROCESS_DETACH:\n        WslTraceLoggingUninitialize();\n        break;\n    }\n\n    return TRUE;\n}"
  },
  {
    "path": "src/windows/wslinstall/version.rc",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    version.rc\n\nAbstract:\n\n    This file contains resources for wslinstall.dll.\n\n--*/\n\n#include <windows.h>\n#include \"wslversioninfo.h\"\n\n#define VER_INTERNALNAME_STR \"wslinstall.dll\"\n#define VER_ORIGINALFILENAME_STR \"wslinstall.dll\"\n\n#define VER_FILEDESCRIPTION_STR \"WSL Install library\"\n\n#include <common.ver>\n\nSTRINGTABLE DISCARDABLE\nBEGIN\n    100 \"WSLINSTALL\"\n    101 \"Installation logic for WSL\"\nEND\n"
  },
  {
    "path": "src/windows/wslinstall/wslinstall.def",
    "content": "LIBRARY wslinstall\n\nEXPORTS\n    CleanExplorerState\n    CleanMsixState\n    DeprovisionMsix\n    WslValidateInstallation\n    WslFinalizeInstallation\n    InstallMsix\n    InstallMsixAsUser\n    RegisterLspCategories\n    RemoveMsixAsSystem\n    RemoveMsixAsUser\n    RemoveRegistryKeyProtections\n    UnregisterLspCategories\n    CalculateWslSettingsProtocolIds"
  },
  {
    "path": "src/windows/wslinstaller/exe/CMakeLists.txt",
    "content": "set(SOURCES\n    ServiceMain.cpp\n    WslInstallerFactory.cpp\n    WslInstaller.cpp\n    main.rc)\n\nset (HEADERS\n     WslInstallerFactory.h\n     WslInstaller.h)\n\ninclude_directories(${CMAKE_BINARY_DIR}/src/windows/wslinstaller/inc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE})\n\nadd_executable(wslinstaller ${SOURCES} ${HEADERS})\nadd_dependencies(wslinstaller wslinstalleridl)\n\nset_target_properties(wslinstaller PROPERTIES LINK_FLAGS \"/merge:minATL=.rdata /include:__minATLObjMap_WslInstaller_COM\")\ntarget_link_libraries(wslinstaller\n                      ${COMMON_LINK_LIBRARIES}\n                      ${MSI_LINK_LIBRARIES}\n                      common\n                      legacy_stdio_definitions)\n\ntarget_precompile_headers(wslinstaller REUSE_FROM common)\nset_target_properties(wslinstaller PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/wslinstaller/exe/ServiceMain.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    ServiceMain.cpp\n\nAbstract:\n\n    This file contains the entrypoint for WslInstallerService.\n\n--*/\n\n#include \"precomp.h\"\n#include \"comservicehelper.h\"\n#include \"WslTelemetry.h\"\n#include \"WslInstallerFactory.h\"\n\nwil::unique_event g_stopEvent{wil::EventOptions::ManualReset};\n\nstatic constexpr auto c_serviceName = L\"WslInstaller\";\n\nCoCreatableClassWrlCreatorMapInclude(WslInstaller);\n\nstruct WslInstallSecurityPolicy\n{\n    static LPCWSTR GetSDDLText()\n    {\n        // COM Access and Launch permissions allowed for authenticated user, principal self, and system.\n        // 0xB = (COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL | COM_RIGHTS_ACTIVATE_LOCAL)\n        // N.B. This should be kept in sync with the security descriptor in the appxmanifest.\n        return L\"O:BAG:BAD:(A;;0xB;;;AU)(A;;0xB;;;PS)(A;;0xB;;;SY)\";\n    }\n};\n\nclass WslInstallerService\n    : public Windows::Internal::Service<WslInstallerService, Windows::Internal::ShutdownAfterLastObjectReleased, WslInstallSecurityPolicy>\n{\npublic:\n    static wchar_t* GetName()\n    {\n        return const_cast<LPWSTR>(c_serviceName);\n    }\n\n    static HRESULT OnServiceStarting();\n    HRESULT ServiceStarted();\n    static void ServiceStopped();\n    static bool AutoInstallEnabled();\n};\n\nbool WslInstallerService::AutoInstallEnabled()\ntry\n{\n    const auto key = wsl::windows::common::registry::OpenLxssMachineKey(KEY_READ);\n\n    auto value = wsl::windows::common::registry::ReadDword(key.get(), L\"MSI\", L\"AutoUpgradeViaMsix\", 1);\n    WSL_LOG(\"AutoUpgradeViaMsix\", TraceLoggingLevel(WINEVENT_LEVEL_INFO), TraceLoggingValue(value, \"setting\"));\n\n    return value == 1;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return false;\n}\n\nHRESULT WslInstallerService::OnServiceStarting()\n{\n    wil::g_fResultFailFastUnknownExceptions = false;\n    wsl::windows::common::wslutil::ConfigureCrt();\n\n    WslTraceLoggingInitialize(WslServiceTelemetryProvider, !wsl::shared::OfficialBuild);\n    wsl::windows::common::security::ApplyProcessMitigationPolicies();\n\n    return S_OK;\n}\n\nvoid Stop()\n{\n    const wil::unique_schandle scm{OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS)};\n    THROW_LAST_ERROR_IF(!scm);\n\n    const wil::unique_schandle self{OpenServiceW(scm.get(), c_serviceName, SERVICE_STOP | SERVICE_QUERY_STATUS)};\n    THROW_LAST_ERROR_IF(!self);\n\n    SERVICE_STATUS status{};\n    THROW_IF_WIN32_BOOL_FALSE(ControlService(self.get(), SERVICE_CONTROL_STOP, &status));\n}\n\nHRESULT WslInstallerService::ServiceStarted()\n{\n    WSL_LOG(\"WslInstallServiceStarted\", TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n    if (AutoInstallEnabled())\n    {\n        const auto install = LaunchInstall();\n        if (!install)\n        {\n            ReportCurrentStatus();\n            StopAsync();\n            return S_OK;\n        }\n\n        THROW_LAST_ERROR_IF(WaitForSingleObject(install->Thread.get(), INFINITE) != WAIT_OBJECT_0);\n    }\n\n    return S_OK;\n}\n\nvoid WslInstallerService::ServiceStopped()\n{\n    WSL_LOG(\"WslInstallServiceStopping\", TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n    g_stopEvent.SetEvent();\n    ClearSessions();\n}\n\nint __cdecl wmain()\n{\n    WslInstallerService::ProcessMain();\n    return 0;\n}"
  },
  {
    "path": "src/windows/wslinstaller/exe/WslInstaller.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslInstaller.cpp\n\nAbstract:\n\n    This file contains the implementation of the WslInstaller class.\n\n--*/\n\n#include \"precomp.h\"\n#include \"install.h\"\n#include \"WslInstaller.h\"\n\nextern wil::unique_event g_stopEvent;\n\nstd::wstring GetMsiPackagePath()\n{\n#ifdef WSL_DEV_THIN_MSI_PACKAGE\n\n    static_assert(!wsl::shared::OfficialBuild);\n\n    return std::filesystem::weakly_canonical(WSL_DEV_THIN_MSI_PACKAGE).wstring();\n\n#endif\n\n    return (wsl::windows::common::wslutil::GetBasePath() / L\"wsl.msi\").wstring();\n}\n\nstd::optional<std::wstring> GetUpgradeLogFileLocation()\ntry\n{\n    const auto key = wsl::windows::common::registry::OpenLxssMachineKey();\n    const auto path = wsl::windows::common::registry::ReadString(key.get(), L\"MSI\", L\"UpgradeLogFile\", L\"\");\n    if (path.empty())\n    {\n        return {};\n    }\n\n    // A canonical path is required because msiexec doesn't like symlinks.\n    return std::filesystem::weakly_canonical(path);\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return {};\n}\n\nstd::pair<UINT, std::wstring> InstallMsipackageImpl()\n{\n    const auto logFile = GetUpgradeLogFileLocation();\n\n    std::wstring errors;\n    auto messageCallback = [&errors](INSTALLMESSAGE type, LPCWSTR message) {\n        switch (type)\n        {\n        case INSTALLMESSAGE_ERROR:\n        case INSTALLMESSAGE_FATALEXIT:\n        case INSTALLMESSAGE_WARNING:\n        case INSTALLMESSAGE_FILESINUSE:\n        case INSTALLMESSAGE_OUTOFDISKSPACE:\n            if (!errors.empty())\n            {\n                errors += L\"\\n\";\n            }\n\n            errors += message;\n            break;\n\n        default:\n            break;\n        }\n    };\n\n    auto result = wsl::windows::common::install::UpgradeViaMsi(\n        GetMsiPackagePath().c_str(), L\"SKIPMSIX=1\", logFile.has_value() ? logFile->c_str() : nullptr, messageCallback);\n\n    WSL_LOG(\"MSIUpgradeResult\", TraceLoggingValue(result, \"result\"), TraceLoggingValue(errors.c_str(), \"errorMessage\"));\n\n    return {result, errors};\n}\n\nDWORD WINAPI InstallMsiPackage(LPVOID Context)\n{\n    auto* installContext = reinterpret_cast<InstallContext*>(Context);\n\n    try\n    {\n        std::tie(installContext->ExitCode, installContext->Errors) = InstallMsipackageImpl();\n    }\n    catch (...)\n    {\n        LOG_CAUGHT_EXCEPTION();\n        installContext->Result = wil::ResultFromCaughtException();\n        return 0;\n    }\n\n    installContext->Result = S_OK;\n    return 0;\n}\n\nstd::pair<bool, std::wstring> IsUpdateNeeded()\n{\n    try\n    {\n        const auto key = wsl::windows::common::registry::OpenLxssMachineKey();\n\n        const auto installedVersion = wsl::windows::common::registry::ReadString(key.get(), L\"MSI\", L\"Version\", L\"\");\n\n        WSL_LOG(\n            \"DetectedInstalledVersion\",\n            TraceLoggingLevel(WINEVENT_LEVEL_INFO),\n            TraceLoggingValue(installedVersion.c_str(), \"InstalledVersion\"));\n\n        return std::make_pair(\n            installedVersion.empty() || wsl::windows::common::wslutil::ParseWslPackageVersion(installedVersion) < wsl::shared::PackageVersion,\n            installedVersion);\n    }\n    catch (...)\n    {\n        LOG_CAUGHT_EXCEPTION();\n\n        return std::make_pair(false, L\"\");\n    }\n}\n\nstd::shared_ptr<InstallContext> LaunchInstall()\n{\n    static wil::srwlock mutex;\n    static std::weak_ptr<InstallContext> weak_context;\n\n    auto lock = mutex.lock_exclusive();\n\n    auto [updateNeeded, existingVersion] = IsUpdateNeeded();\n    if (!updateNeeded)\n    {\n        return {};\n    }\n\n    wsl::windows::common::install::WriteInstallLog(std::format(\"Starting upgrade via WslInstaller. Previous version: {}\", existingVersion));\n\n    // Return an existing install if any\n    if (auto ptr = weak_context.lock(); ptr != nullptr)\n    {\n        return ptr;\n    }\n\n    // Else launch a new install\n    auto context = std::make_shared<InstallContext>();\n    weak_context = std::weak_ptr<InstallContext>(context);\n\n    context->Thread = wil::unique_handle{CreateThread(nullptr, 0, &InstallMsiPackage, context.get(), 0, nullptr)};\n\n    return context;\n}\n\nHRESULT WslInstaller::Install(UINT* ExitCode, LPWSTR* Errors)\ntry\n{\n    const auto context = LaunchInstall();\n    if (!context)\n    {\n        // This block can be reached if the installation completed after the client looked up the MSI package.\n        // In this case don't attempt to install and return success so the client looks up the MSI package again.\n\n        *ExitCode = 0;\n        *Errors = wil::make_unique_string<wil::unique_cotaskmem_string>(L\"\").release();\n        return S_OK;\n    }\n\n    THROW_LAST_ERROR_IF(WaitForSingleObject(context->Thread.get(), INFINITE) != WAIT_OBJECT_0);\n\n    *ExitCode = context->ExitCode;\n    *Errors = wil::make_unique_string<wil::unique_cotaskmem_string>(context->Errors.c_str()).release();\n\n    return context->Result;\n}\nCATCH_RETURN()"
  },
  {
    "path": "src/windows/wslinstaller/exe/WslInstaller.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslInstaller.h\n\nAbstract:\n\n    This file contains definitions for the WslInstallerService.\n\n--*/\n\n#pragma once\n#include <wil/wrl.h>\n#include <wslinstallerservice.h>\n\nclass DECLSPEC_UUID(\"B5AEB4C3-9541-492F-AD4D-505951F6ADA4\") WslInstaller\n    : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IWslInstaller, IFastRundown>\n{\n    HRESULT Install(UINT* ExitCode, LPWSTR* Error) override;\n};\n\nstruct InstallContext\n{\n    wil::unique_handle Thread;\n    HRESULT Result{};\n    UINT ExitCode{};\n    std::wstring Errors;\n};\n\nstd::shared_ptr<InstallContext> LaunchInstall();\n"
  },
  {
    "path": "src/windows/wslinstaller/exe/WslInstallerFactory.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslInstallerFactory.cpp\n\nAbstract:\n\n    This file contains the WslInstallerFactory class implementation.\n\n--*/\n\n#include \"precomp.h\"\n\n#include \"WslInstallerFactory.h\"\n\nCoCreatableClassWithFactory(WslInstaller, WslInstallerFactory);\n\nstatic std::vector<std::shared_ptr<WslInstaller>> g_sessions;\nstatic wil::srwlock g_mutex;\nextern wil::unique_event g_stopEvent;\n\nvoid ClearSessions()\n{\n    auto lock = g_mutex.lock_exclusive();\n    g_sessions.clear();\n}\n\nHRESULT WslInstallerFactory::CreateInstance(_In_ IUnknown* pUnkOuter, _In_ REFIID riid, _Out_ void** ppCreated)\n{\n    RETURN_HR_IF_NULL(E_POINTER, ppCreated);\n    *ppCreated = nullptr;\n\n    RETURN_HR_IF(CLASS_E_NOAGGREGATION, pUnkOuter != nullptr);\n\n    WSL_LOG(\"WslInstallerCreateInstance\", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));\n\n    auto lock = g_mutex.lock_exclusive();\n\n    if (g_stopEvent.is_signaled())\n    {\n        return S_FALSE;\n    }\n\n    const auto instance = wil::MakeOrThrow<WslInstaller>();\n    instance.CopyTo(riid, ppCreated);\n\n    return S_OK;\n}"
  },
  {
    "path": "src/windows/wslinstaller/exe/WslInstallerFactory.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslInstallerFactory.h\n\nAbstract:\n\n    This file contains the WslInstallerFactory class definition.\n\n--*/\n\n#pragma once\n\n#include <wil/wrl.h>\n#include \"WslInstaller.h\"\n\nclass WslInstallerFactory : public Microsoft::WRL::ClassFactory<>\n{\npublic:\n    WslInstallerFactory() = default;\n    STDMETHODIMP CreateInstance(_In_ IUnknown* pUnkOuter, _In_ REFIID riid, _Out_ void** ppCreated) override;\n};\n\nvoid ClearSessions();"
  },
  {
    "path": "src/windows/wslinstaller/exe/main.rc",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.rc\n\nAbstract:\n\n    This file contains resources for wslinstaller.\n\n--*/\n\n#include <windows.h>\n#include \"wslversioninfo.h\"\n\n#define ID_ICON 1\n#define VER_INTERNALNAME_STR \"wslinstaller.exe\"\n#define VER_ORIGINALFILENAME_STR \"wslinstaller.exe\"\n\n#define VER_FILETYPE VFT_APP\n#define VER_FILESUBTYPE VFT2_UNKNOWN\n#define VER_FILEDESCRIPTION_STR \"Windows Subsystem for Linux Installer Service\"\nID_ICON ICON PRELOAD DISCARDABLE \"..\\..\\..\\images\\wsl.ico\"\n\n\n#include <common.ver>\n"
  },
  {
    "path": "src/windows/wslinstaller/inc/CMakeLists.txt",
    "content": "add_idl(wslinstalleridl wslinstallerservice.idl \"\")\nset_target_properties(wslinstalleridl PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/wslinstaller/inc/wslinstallerservice.idl",
    "content": "/*++\n\nCopyright (c) Microsoft Corporation.  All rights reserved.\n\nModule Name:\n\n    wslinstalleridl.idl\n\nAbstract:\n\n    This file contains the COM object definitions used to talk with the WSL\n    service \"WslInstaller\"\n\n--*/\n\nimport \"unknwn.idl\";\nimport \"wtypes.idl\";\n\ncpp_quote(\"#ifdef __cplusplus\")\ncpp_quote(\"const GUID CLSID_WslInstaller = { 0xb5aeb4c3, 0x9541, 0x492f, { 0xad, 0x4d, 0x50, 0x59, 0x51, 0xf6, 0xad, 0xa4 }};\")\ncpp_quote(\"class DECLSPEC_UUID(\\\"B5AEB4C3-9541-492F-AD4D-505951F6ADA4\\\") WslInstaller;\")\ncpp_quote(\"#endif\")\n\n[\n    uuid(1E912664-C599-474A-A552-DAEAF73B3164),\n    pointer_default(unique),\n    object\n]\ninterface IWslInstaller : IUnknown\n{\n    HRESULT Install([out] UINT* ExitCode, [out] LPWSTR* ErrorMessage);\n};"
  },
  {
    "path": "src/windows/wslinstaller/stub/CMakeLists.txt",
    "content": "add_compile_definitions(\"PROXY_CLSID_IS={ 0xce1044f6, 0x36c5, 0x4599, { 0xa5, 0xa8, 0x3b, 0xbf, 0x27, 0xba, 0x54, 0x95 } }\")\nadd_compile_definitions(\"REGISTER_PROXY_DLL\")\n\nset(SOURCES\n    ${CMAKE_CURRENT_BINARY_DIR}/../inc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/wslinstallerservice_i_${TARGET_PLATFORM}.c\n    ${CMAKE_CURRENT_BINARY_DIR}/../inc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/wslinstallerservice_p_${TARGET_PLATFORM}.c\n    ${CMAKE_CURRENT_BINARY_DIR}/../inc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/dlldata_${TARGET_PLATFORM}.c\n    ${CMAKE_CURRENT_LIST_DIR}/WslInstallerProxyStub.def\n    ${CMAKE_CURRENT_LIST_DIR}/WslInstallerProxyStub.rc)\n\nset_source_files_properties(${SOURCES} PROPERTIES GENERATED TRUE)\n\nadd_library(wslinstallerproxystub SHARED ${SOURCES})\nadd_dependencies(wslinstallerproxystub wslinstalleridl)\ntarget_link_libraries(wslinstallerproxystub ${COMMON_LINK_LIBRARIES})\nset_target_properties(wslinstallerproxystub PROPERTIES FOLDER windows)\n"
  },
  {
    "path": "src/windows/wslinstaller/stub/WslInstallerProxyStub.def",
    "content": "LIBRARY wslinstallerproxystub.dll\n\nEXPORTS\n    DllGetClassObject      PRIVATE\n    DllCanUnloadNow        PRIVATE\n"
  },
  {
    "path": "src/windows/wslinstaller/stub/WslInstallerProxyStub.rc",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    WslInstallerProxyStub.rc\n\nAbstract:\n\n    This file contains resources for WslInstallerProxyStub.dll.\n\n--*/\n\n#include <windows.h>\n#include \"wslversioninfo.h\"\n\n#define VER_INTERNALNAME_STR \"WslInstallerProxyStub.dll\"\n#define VER_ORIGINALFILENAME_STR \"WslInstallerProxyStub.dll\"\n\n#define VER_FILEDESCRIPTION_STR \"WSL Installer ProxyStub DLL\"\n\n#include <common.ver>\n"
  },
  {
    "path": "src/windows/wslinstaller/wslinstaller.idl",
    "content": "/*++\n\nCopyright (c) Microsoft Corporation.  All rights reserved.\n\nModule Name:\n\n    wslinstaller.idl\n\n\n--*/\n\n[\n    uuid(D72BF71C-95DA-400D-9E4A-6ABA6064FE60),\n    pointer_default(unique),\n    object\n]\ninterface IWslInstaller : IUnknown\n{\n    HRESULT Install([out] DWORD* ExitCode);\n};"
  },
  {
    "path": "src/windows/wslrelay/CMakeLists.txt",
    "content": "set(SOURCES\n    localhost.cpp\n    main.cpp\n    main.rc)\n\nset(HEADERS\n    localhost.h\n    resource.h)\n\nadd_executable(wslrelay WIN32 ${SOURCES} ${HEADERS})\nadd_dependencies(wslrelay\n                 common)\n\ntarget_link_libraries(wslrelay\n                      ${COMMON_LINK_LIBRARIES}\n                      common)\n\ntarget_precompile_headers(wslrelay REUSE_FROM common)\nset_target_properties(wslrelay PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/wslrelay/localhost.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    helpers.cpp\n\nAbstract:\n\n    This file contains helper function definitions.\n\n--*/\n\n#include \"precomp.h\"\n#include \"helpers.hpp\"\n#include \"svccomm.hpp\"\n#include \"socket.hpp\"\n#include \"hvsocket.hpp\"\n#include \"relay.hpp\"\n#include \"localhost.h\"\n#include <gsl/algorithm>\n#include <gslhelpers.h>\n\n#define LOCALHOST_RELAY_BUFFER_SIZE (0x20000)\n\nstruct in6_addr_linux\n{\n    union\n    {\n        uint8_t addr[16];\n        uint32_t addr32[4];\n    } u;\n};\n\nconst uint16_t ADDR6_MASK3 = ~in6_addr_linux(IN6ADDR_LOOPBACK_INIT).u.addr32[3];\nconst uint32_t N_ADDR_LOOPBACK = ntohl(INADDR_LOOPBACK);\nconst uint32_t N_ADDR_ANY = ntohl(INADDR_ANY);\n\nstatic VOID PortListenerAsync(_Inout_ std::shared_ptr<LX_PORT_LISTENER_THREAD_CONTEXT> Arguments);\nstatic wil::unique_socket BindRelayListener(ADDRESS_FAMILY const Family, USHORT const Port);\n\nstatic int GetPortListener(_In_ wsl::shared::SocketChannel& Channel)\n{\n    const auto& GuestAgentInfo = Channel.ReceiveMessage<LX_GNS_SET_PORT_LISTENER>();\n    return GuestAgentInfo.HvSocketPort;\n}\n\nstatic int WindowsAddressFamily(int LinuxAddressFamily)\n{\n    switch (LinuxAddressFamily)\n    {\n    case LX_AF_INET:\n        return AF_INET;\n\n    case LX_AF_INET6:\n        return AF_INET6;\n    }\n    WSL_LOG(\"PortRelayBindFamily\", TraceLoggingValue(LinuxAddressFamily, \"LinuxAddressFamily\"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));\n\n    THROW_HR(E_INVALIDARG);\n}\n\nbool BindsLocalhost(int Af, uint32_t const* Address)\n{\n    switch (Af)\n    {\n    case LX_AF_INET:\n        return Address[0] == N_ADDR_ANY || Address[0] == N_ADDR_LOOPBACK;\n    case LX_AF_INET6:\n        return (Address[0] | Address[1] | Address[2] | (Address[3] & ADDR6_MASK3)) == 0;\n    }\n    return false;\n}\n\nvoid wsl::windows::wslrelay::localhost::RelayWorker(_In_ wsl::shared::SocketChannel& Channel, _In_ const GUID& VmId)\n{\n    std::vector<gsl::byte> Buffer;\n    Relay Relay;\n    const int HvSocketPort = GetPortListener(Channel);\n\n    for (;;)\n    {\n        auto [Message, Span] = Channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();\n        if (Message == nullptr)\n        {\n            break;\n        }\n\n        switch (Message->MessageType)\n        {\n        case LxGnsMessagePortListenerRelayStart:\n        case LxGnsMessagePortListenerRelayStop:\n        {\n            const auto* RelayOperation = gslhelpers::try_get_struct<LX_GNS_PORT_LISTENER_RELAY>(Span);\n            THROW_HR_IF(E_INVALIDARG, !RelayOperation);\n            // Ignore non-localhost addresses.\n            if (WindowsAddressFamily(RelayOperation->Family) == AF_INET)\n            {\n                if (RelayOperation->Address[0] != INADDR_ANY && RelayOperation->Address[0] != htonl(INADDR_LOOPBACK))\n                {\n                    continue;\n                }\n            }\n            else\n            {\n                WI_ASSERT(WindowsAddressFamily(RelayOperation->Family) == AF_INET6);\n                bool IgnorePort = false;\n                for (int Part = 0; Part < 3; ++Part)\n                {\n                    if (RelayOperation->Address[Part] != 0)\n                    {\n                        IgnorePort = true;\n                        break;\n                    }\n                }\n                // Create relays for unspecified (any) or loopback Ipv6 addresses.\n                if (IgnorePort || ntohl(RelayOperation->Address[3]) > 1)\n                {\n                    continue;\n                }\n            }\n            if (Message->MessageType == LxGnsMessagePortListenerRelayStart)\n            {\n                Relay.StartPortListener(VmId, RelayOperation->Family, RelayOperation->Port, HvSocketPort);\n            }\n            else\n            {\n                Relay.StopPortListener(RelayOperation->Family, RelayOperation->Port);\n            }\n            break;\n        }\n        case LxGnsMessagePortMappingRequest:\n        {\n            // TODO: handle UDP binds\n            const auto* RelayOperation = gslhelpers::try_get_struct<LX_GNS_PORT_ALLOCATION_REQUEST>(Span);\n            THROW_HR_IF(E_INVALIDARG, !RelayOperation);\n            RESULT_MESSAGE<int32_t> Response = {};\n            Response.Header.MessageType = decltype(Response)::Type;\n            Response.Header.MessageSize = sizeof(Response);\n\n            if (RelayOperation->Protocol == IPPROTO_TCP && BindsLocalhost(RelayOperation->Af, RelayOperation->Address32))\n            {\n                if (RelayOperation->Allocate)\n                {\n                    Response.Result = Relay.StartPortListener(VmId, RelayOperation->Af, RelayOperation->Port, HvSocketPort);\n                }\n                else\n                {\n                    Relay.StopPortListener(RelayOperation->Af, RelayOperation->Port);\n                }\n            }\n\n            Channel.SendMessage(Response);\n            break;\n        }\n\n        default:\n            THROW_HR_MSG(E_UNEXPECTED, \"Unexpected message %d\", Message->MessageType);\n        }\n    }\n}\n\nwsl::windows::wslrelay::localhost::Relay::~Relay()\n{\n    // Iterate through each relay and set the exit event and wait for all worker threads to finish.\n    auto lock = m_lock.lock_exclusive();\n    for (auto const& entry : m_RelayThreads)\n    {\n        entry.second->ThreadContext->ExitEvent.SetEvent();\n    }\n\n    m_RelayThreads.clear();\n}\n\nint wsl::windows::wslrelay::localhost::Relay::StartPortListener(_In_ const GUID& VmId, _In_ unsigned short Family, _In_ unsigned short Port, _In_ int HvSocketPort)\ntry\n{\n    auto lock = m_lock.lock_exclusive();\n    if (const auto iter = m_RelayThreads.find({Family, Port}); iter != m_RelayThreads.end())\n    {\n        iter->second->ThreadContext->Count++;\n        return 0;\n    }\n\n    // Create a worker thread to service the port relay.\n    //\n    // N.B. The worker thread takes ownership of the arguments pointer.\n    const auto Arguments = std::make_shared<LX_PORT_LISTENER_CONTEXT>();\n    Arguments->ThreadContext = std::make_shared<LX_PORT_LISTENER_THREAD_CONTEXT>();\n    Arguments->ThreadContext->VmId = VmId;\n    Arguments->ThreadContext->Family = Family;\n    Arguments->ThreadContext->Port = Port;\n    Arguments->ThreadContext->HvSocketPort = HvSocketPort;\n    Arguments->ThreadContext->ExitEvent.create(wil::EventOptions::ManualReset);\n    Arguments->ThreadContext->ListenSocket = BindRelayListener(Family, Port);\n    Arguments->Worker = std::thread(PortListenerAsync, Arguments->ThreadContext);\n\n    m_RelayThreads[{Family, Port}] = Arguments;\n\n    return 0;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    switch (wil::ResultFromCaughtException())\n    {\n    case HRESULT_FROM_WIN32(WSAEADDRINUSE):\n    case HRESULT_FROM_WIN32(WSAEACCES): // TODO: Remap and handle this next to the bind call.\n        return -LX_EADDRINUSE;\n    default:\n        return -LX_EINVAL;\n    }\n}\n\nvoid wsl::windows::wslrelay::localhost::Relay::StopPortListener(_In_ unsigned short Family, _In_ unsigned short Port)\n{\n    try\n    {\n        // Search through the worker threads and terminate any that match the address family and port.\n        auto lock = m_lock.lock_exclusive();\n        const auto iter = m_RelayThreads.find({Family, Port});\n        if (iter != m_RelayThreads.end())\n        {\n            iter->second->ThreadContext->Count--;\n            if (iter->second->ThreadContext->Count <= 0)\n            {\n                iter->second->ThreadContext->ExitEvent.SetEvent();\n                iter->second->Worker.join();\n                m_RelayThreads.erase(iter);\n                WSL_LOG(\"PortRelayUnBind\", TraceLoggingValue(Family, \"family\"), TraceLoggingValue(Port, \"port\"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));\n            }\n        }\n    }\n    CATCH_LOG()\n}\n\nstatic wil::unique_socket BindRelayListener(ADDRESS_FAMILY const Family, USHORT const Port)\n{\n\n    // Perform a mapping from Linux address family to Windows.\n    const int AddressFamily = WindowsAddressFamily(Family);\n\n    // Create a listening tcp socket on the specified port.\n\n    wil::unique_socket ListenSocket(WSASocket(AddressFamily, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));\n\n    THROW_LAST_ERROR_IF(!ListenSocket);\n\n    // Set the SO_REUSEADDR socket option.\n\n    constexpr BOOLEAN On = true;\n    THROW_LAST_ERROR_IF(setsockopt(ListenSocket.get(), SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&On), sizeof(On)) == SOCKET_ERROR);\n\n    sockaddr* Address;\n    sockaddr_in InetAddress{};\n    sockaddr_in6 Inet6Address{};\n    DWORD AddressSize;\n    if (AddressFamily == AF_INET)\n    {\n        InetAddress.sin_family = AF_INET;\n        InetAddress.sin_port = htons(Port);\n        InetAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\n        Address = reinterpret_cast<sockaddr*>(&InetAddress);\n        AddressSize = sizeof(InetAddress);\n    }\n    else\n    {\n        Inet6Address.sin6_family = AF_INET6;\n        Inet6Address.sin6_port = htons(Port);\n        Inet6Address.sin6_addr = IN6ADDR_LOOPBACK_INIT;\n        Address = reinterpret_cast<sockaddr*>(&Inet6Address);\n        AddressSize = sizeof(Inet6Address);\n    }\n\n    // Start listening on the specified port.\n    // TODO: Catch relevant bind errors WSAEACCES, WSAEADDRINUSE and throw as a new hr that will\n    // end up being emitted by seccomp as EADDRINUSE.\n    THROW_LAST_ERROR_IF(bind(ListenSocket.get(), Address, AddressSize) == SOCKET_ERROR);\n\n    THROW_LAST_ERROR_IF(listen(ListenSocket.get(), -1) == SOCKET_ERROR);\n\n    WSL_LOG(\"PortRelayBind\", TraceLoggingValue(Family, \"family\"), TraceLoggingValue(Port, \"port\"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));\n\n    return ListenSocket;\n}\n\nstatic VOID PortListenerAsync(_Inout_ std::shared_ptr<LX_PORT_LISTENER_THREAD_CONTEXT> Arguments)\ntry\n{\n    // Begin accepting connections until the relay is stopped.\n\n    const int AddressFamily = WindowsAddressFamily(Arguments->Family);\n\n    for (;;)\n    {\n        wil::unique_socket InetSocket(WSASocket(AddressFamily, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));\n        THROW_LAST_ERROR_IF(!InetSocket);\n\n        if (!wsl::windows::common::socket::CancellableAccept(\n                Arguments->ListenSocket.get(), InetSocket.get(), INFINITE, Arguments->ExitEvent.get()))\n        {\n            break; // Exit event was signaled, exit.\n        }\n\n        // Establish a relay thread.\n\n        WSL_LOG(\"PortRelayUsage\", TraceLoggingValue(Arguments->Family, \"family\"), TraceLoggingValue(Arguments->Port, \"port\"), TraceLoggingLevel(WINEVENT_LEVEL_INFO));\n\n        auto RelayThread = std::thread([Arguments, InetSocket = std::move(InetSocket)]() {\n            try\n            {\n                wsl::windows::common::wslutil::SetThreadDescription(L\"Port relay\");\n                const auto HvSocket = wsl::windows::common::hvsocket::Connect(Arguments->VmId, Arguments->HvSocketPort);\n                LX_INIT_START_SOCKET_RELAY Message{};\n                Message.Header.MessageType = LxInitMessageStartSocketRelay;\n                Message.Header.MessageSize = sizeof(Message);\n                Message.Family = Arguments->Family;\n                Message.Port = Arguments->Port;\n                Message.BufferSize = LOCALHOST_RELAY_BUFFER_SIZE;\n                wsl::windows::common::socket::Send(HvSocket.get(), gslhelpers::struct_as_bytes(Message));\n                wsl::windows::common::relay::SocketRelay(InetSocket.get(), HvSocket.get(), Message.BufferSize);\n            }\n            CATCH_LOG()\n        });\n\n        RelayThread.detach();\n    }\n}\nCATCH_LOG()\n"
  },
  {
    "path": "src/windows/wslrelay/localhost.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    localhost.h\n\nAbstract:\n\n    This file contains localhost port relay function declarations.\n\n--*/\n\n#pragma once\n\n#include <winsock2.h>\n#include <map>\n#include <string_view>\n#include <tuple>\n#include \"SocketChannel.h\"\n\ntypedef struct _LX_PORT_LISTENER_THREAD_CONTEXT\n{\n    GUID VmId;\n    unsigned short Family;\n    unsigned short Port;\n    int HvSocketPort;\n    int Count = 1;\n    wil::unique_event ExitEvent;\n    wil::unique_socket ListenSocket;\n} LX_PORT_LISTENER_THREAD_CONTEXT, *PLX_PORT_LISTENER_THREAD_CONTEXT;\n\ntypedef struct _LX_PORT_LISTENER_CONTEXT\n{\n    ~_LX_PORT_LISTENER_CONTEXT()\n    {\n        if (Worker.joinable())\n        {\n            Worker.join();\n        }\n    };\n    std::shared_ptr<LX_PORT_LISTENER_THREAD_CONTEXT> ThreadContext;\n    std::thread Worker;\n} LX_PORT_LISTENER_CONTEXT, *PLX_PORT_LISTENER_CONTEXT;\n\nnamespace wsl::windows::wslrelay::localhost {\nvoid RelayWorker(_In_ wsl::shared::SocketChannel& SocketChannel, _In_ const GUID& VmId);\n\nclass Relay\n{\npublic:\n    ~Relay();\n\n    int StartPortListener(_In_ const GUID& VmId, _In_ unsigned short Family, _In_ unsigned short Port, _In_ int HvSocketPort);\n\n    void StopPortListener(_In_ unsigned short Family, _In_ unsigned short Port);\n\nprivate:\n    wil::srwlock m_lock;\n    _Guarded_by_(m_lock) std::map<std::tuple<unsigned short, unsigned short>, std::shared_ptr<LX_PORT_LISTENER_CONTEXT>> m_RelayThreads;\n};\n} // namespace wsl::windows::wslrelay::localhost\n"
  },
  {
    "path": "src/windows/wslrelay/main.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.cpp\n\nAbstract:\n\n    This file contains the entrypoint for wslrelay.\n\n--*/\n\n#include \"precomp.h\"\n#include \"localhost.h\"\n#include \"CommandLine.h\"\n\nusing namespace wsl::windows::common;\nusing namespace wsl::shared;\n\nint WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)\ntry\n{\n    wsl::windows::common::wslutil::ConfigureCrt();\n    wsl::windows::common::wslutil::InitializeWil();\n\n    // Initialize COM.\n    auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);\n    wsl::windows::common::wslutil::CoInitializeSecurity();\n\n    // Initialize winsock.\n    WSADATA data;\n    THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &data));\n\n    // Parse arguments.\n    wil::unique_handle handle{};\n    wslrelay::RelayMode mode{wslrelay::RelayMode::Invalid};\n    wil::unique_handle pipe{};\n    wil::unique_handle exitEvent{};\n    uint32_t port{};\n    GUID vmId{};\n    bool disableTelemetry = !wsl::shared::OfficialBuild;\n    bool connectPipe = false;\n\n    ArgumentParser parser(GetCommandLineW(), wslrelay::binary_name);\n    parser.AddArgument(Integer(reinterpret_cast<int&>(mode)), wslrelay::mode_option);\n    parser.AddArgument(Handle{handle}, wslrelay::handle_option);\n    parser.AddArgument(vmId, wslrelay::vm_id_option);\n    parser.AddArgument(Handle{pipe}, wslrelay::pipe_option);\n    parser.AddArgument(Handle{exitEvent}, wslrelay::exit_event_option);\n    parser.AddArgument(Integer{port}, wslrelay::port_option);\n    parser.AddArgument(disableTelemetry, wslrelay::disable_telemetry_option);\n    parser.AddArgument(connectPipe, wslrelay::connect_pipe_option);\n    parser.Parse();\n\n    // Initialize logging.\n    WslTraceLoggingInitialize(LxssTelemetryProvider, disableTelemetry);\n    auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [] { WslTraceLoggingUninitialize(); });\n\n    // Ensure that the other end of the pipe has connected if required.\n    if (connectPipe)\n    {\n        std::vector<HANDLE> exitEvents;\n        if (exitEvent)\n        {\n            exitEvents.push_back(exitEvent.get());\n        }\n\n        wsl::windows::common::helpers::ConnectPipe(pipe.get(), (15 * 1000), exitEvents);\n    }\n\n    // Perform the requested operation.\n    switch (mode)\n    {\n    case wslrelay::RelayMode::DebugConsole:\n    {\n        // If not relaying to a file, create a console window.\n        if (!handle)\n        {\n            wsl::windows::common::helpers::CreateConsole(L\"WSL Debug Console\");\n        }\n\n        // Relay the contents of the pipe to the output handle.\n        wsl::windows::common::relay::InterruptableRelay(pipe.get(), handle ? handle.get() : GetStdHandle(STD_OUTPUT_HANDLE));\n\n        // Print a message that the VM has exited and prompt the user for input.\n        wsl::windows::common::wslutil::PrintSystemError(HCS_E_CONNECTION_CLOSED);\n        if (!handle)\n        {\n            getwchar();\n        }\n\n        break;\n    }\n\n    case wslrelay::RelayMode::PortRelay:\n    {\n        wsl::shared::SocketChannel channel{wil::unique_socket{reinterpret_cast<SOCKET>(handle.release())}, \"PortRelay\"};\n        wsl::windows::wslrelay::localhost::RelayWorker(channel, vmId);\n        break;\n    }\n\n    case wslrelay::RelayMode::KdRelay:\n    {\n        THROW_HR_IF(E_INVALIDARG, port == 0);\n\n        // Bind, listen, and accept a connection on the specified port.\n        const wil::unique_socket listenSocket(WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));\n        THROW_LAST_ERROR_IF(!listenSocket);\n\n        sockaddr_in address{};\n        address.sin_family = AF_INET;\n        address.sin_port = htons(port);\n        address.sin_addr.s_addr = htonl(INADDR_ANY);\n        THROW_LAST_ERROR_IF(bind(listenSocket.get(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) == SOCKET_ERROR);\n\n        THROW_LAST_ERROR_IF(listen(listenSocket.get(), 1) == SOCKET_ERROR);\n\n        const wil::unique_socket socket(WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));\n        THROW_LAST_ERROR_IF(!socket);\n\n        if (!wsl::windows::common::socket::CancellableAccept(listenSocket.get(), socket.get(), INFINITE, exitEvent.get()))\n        {\n            return 1;\n        }\n\n        // Begin the relay.\n        wsl::windows::common::relay::BidirectionalRelay(\n            reinterpret_cast<HANDLE>(socket.get()), pipe.get(), 0x1000, wsl::windows::common::relay::RelayFlags::LeftIsSocket);\n\n        break;\n    }\n\n    default:\n        THROW_HR_MSG(E_INVALIDARG, \"Invalid relay mode %d specified.\", static_cast<int>(mode));\n    }\n\n    return 0;\n}\ncatch (...)\n{\n    LOG_CAUGHT_EXCEPTION();\n    return 1;\n}\n"
  },
  {
    "path": "src/windows/wslrelay/main.rc",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    main.rc\n\nAbstract:\n\n    This file contains resources for wslrelay.\n\n--*/\n\n#include <windows.h>\n#include \"resource.h\"\n#include \"wslversioninfo.h\"\n\n#define VER_INTERNALNAME_STR \"wslrelay.exe\"\n#define VER_ORIGINALFILENAME_STR \"wslrelay.exe\"\n\n#define VER_FILEDESCRIPTION_STR \"Windows Subsystem for Linux\"\nID_ICON ICON PRELOAD DISCARDABLE \"..\\..\\..\\Images\\wsl.ico\"\n\n#include <common.ver>\n"
  },
  {
    "path": "src/windows/wslrelay/resource.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    resource.h\n\nAbstract:\n\n    This file contains resource declarations for wslrelay.exe\n\n--*/\n\n#define ID_ICON 1\n"
  },
  {
    "path": "src/windows/wslsettings/Activation/ActivationHandler.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.Activation;\r\n\r\n// Extend this class to implement new ActivationHandlers. See DefaultActivationHandler for an example.\r\n// https://github.com/microsoft/TemplateStudio/blob/main/docs/WinUI/activation.md\r\npublic abstract class ActivationHandler<T> : IActivationHandler\r\n    where T : class\r\n{\r\n    protected readonly INavigationService _navigationService;\r\n\r\n    public ActivationHandler(INavigationService navigationService)\r\n    {\r\n        _navigationService = navigationService;\r\n    }\r\n\r\n    // Override this method to add the logic for whether to handle the activation.\r\n    protected virtual bool CanHandleInternal(T args) => true;\r\n\r\n    // Override this method to add the logic for your activation handler.\r\n    protected abstract Task HandleInternalAsync(T args);\r\n\r\n    protected void Navigate(IWindowService.WindowId windowId, string pageKey, LaunchActivatedEventArgs args)\r\n    {\r\n        WindowEx window = App.GetService<IWindowService>().CreateOrGetWindow(windowId);\r\n\r\n        _navigationService.NavigateTo(pageKey, args.Arguments);\r\n\r\n        window.Activate();\r\n    }\r\n\r\n    public bool CanHandle(object args) => args is T && CanHandleInternal((args as T)!);\r\n\r\n    public async Task HandleAsync(object args) => await HandleInternalAsync((args as T)!);\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Activation/DefaultActivationHandler.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.ViewModels.Settings;\r\n\r\nnamespace WslSettings.Activation;\r\n\r\npublic class DefaultActivationHandler : ActivationHandler<LaunchActivatedEventArgs>\r\n{\r\n    public DefaultActivationHandler(INavigationService navigationService) : base(navigationService)\r\n    {\r\n    }\r\n\r\n    protected override bool CanHandleInternal(LaunchActivatedEventArgs args)\r\n    {\r\n        // None of the ActivationHandlers has handled the activation.\r\n        return _navigationService.Frame?.Content == null;\r\n    }\r\n\r\n    protected async override Task HandleInternalAsync(LaunchActivatedEventArgs args)\r\n    {\r\n        Navigate(IWindowService.WindowId.MainWindow, typeof(MemAndProcViewModel).FullName!, args);\r\n\r\n        await Task.CompletedTask;\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Activation/IActivationHandler.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Activation;\r\n\r\npublic interface IActivationHandler\r\n{\r\n    bool CanHandle(object args);\r\n\r\n    Task HandleAsync(object args);\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Activation/ProtocolActivationHandler.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.Windows.AppLifecycle;\r\nusing System.Runtime.CompilerServices;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.ViewModels.OOBE;\r\nusing WslSettings.ViewModels.Settings;\r\n\r\nnamespace WslSettings.Activation;\r\n\r\npublic class ProtocolActivationHandler : ActivationHandler<LaunchActivatedEventArgs>\r\n{\r\n    public ProtocolActivationHandler(INavigationService navigationService) : base(navigationService)\r\n    {\r\n    }\r\n\r\n    protected override bool CanHandleInternal(LaunchActivatedEventArgs args)\r\n    {\r\n        // None of the ActivationHandlers has handled the activation.\r\n        return AppInstance.GetCurrent().GetActivatedEventArgs().Kind == ExtendedActivationKind.Protocol &&\r\n            _navigationService.Frame?.Content == null;\r\n    }\r\n\r\n    private static IWindowService.WindowId ResolveWindowId(Uri uri)\r\n    {\r\n        IWindowService.WindowId windowId = IWindowService.WindowId.MainWindow;\r\n        switch (uri.Host.ToLower())\r\n        {\r\n            case \"settings\":\r\n                windowId = IWindowService.WindowId.MainWindow;\r\n                break;\r\n            case \"oobe\":\r\n                windowId = IWindowService.WindowId.OOBEWindow;\r\n                break;\r\n            default:\r\n                windowId = IWindowService.WindowId.MainWindow;\r\n                break;\r\n        }\r\n\r\n        return windowId;\r\n    }\r\n\r\n    private static string ResolvePageKey(Uri uri, IWindowService.WindowId windowId)\r\n    {\r\n        string pageName = uri.LocalPath.ToLower().Trim('/');\r\n        string pageKey = typeof(MemAndProcViewModel).FullName!;\r\n        switch (windowId)\r\n        {\r\n            case IWindowService.WindowId.MainWindow:\r\n                switch (pageName)\r\n                {\r\n                    case \"memandproc\":\r\n                        pageKey = typeof(MemAndProcViewModel).FullName!;\r\n                        break;\r\n                    case \"filesystem\":\r\n                        pageKey = typeof(FileSystemViewModel).FullName!;\r\n                        break;\r\n                    case \"networking\":\r\n                        pageKey = typeof(NetworkingViewModel).FullName!;\r\n                        break;\r\n                    case \"optfeatures\":\r\n                        pageKey = typeof(OptionalFeaturesViewModel).FullName!;\r\n                        break;\r\n                    case \"developer\":\r\n                        pageKey = typeof(DeveloperViewModel).FullName!;\r\n                        break;\r\n                    case \"about\":\r\n                        pageKey = typeof(AboutViewModel).FullName!;\r\n                        break;\r\n                    default:\r\n                        pageKey = typeof(MemAndProcViewModel).FullName!;\r\n                        break;\r\n                }\r\n                break;\r\n            case IWindowService.WindowId.OOBEWindow:\r\n                switch (pageName)\r\n                {\r\n                    case \"general\":\r\n                        pageKey = typeof(GeneralViewModel).FullName!;\r\n                        break;\r\n                    case \"crossosfiles\":\r\n                        pageKey = typeof(WorkingAcrossFileSystemsViewModel).FullName!;\r\n                        break;\r\n                    case \"guiapps\":\r\n                        pageKey = typeof(GUIAppsViewModel).FullName!;\r\n                        break;\r\n                    case \"vscodeint\":\r\n                        pageKey = typeof(VSCodeIntegrationViewModel).FullName!;\r\n                        break;\r\n                    case \"vsint\":\r\n                        pageKey = typeof(VSIntegrationViewModel).FullName!;\r\n                        break;\r\n                    case \"gpuaccel\":\r\n                        pageKey = typeof(GPUAccelerationViewModel).FullName!;\r\n                        break;\r\n                    case \"dockerint\":\r\n                        pageKey = typeof(DockerDesktopIntegrationViewModel).FullName!;\r\n                        break;\r\n                    case \"networkingint\":\r\n                        pageKey = typeof(NetworkingIntegrationViewModel).FullName!;\r\n                        break;\r\n                    case \"distromgmt\":\r\n                        pageKey = typeof(DistroManagementViewModel).FullName!;\r\n                        break;\r\n                    default:\r\n                        pageKey = typeof(GeneralViewModel).FullName!;\r\n                        break;\r\n                }\r\n                break;\r\n            default:\r\n                pageKey = typeof(MemAndProcViewModel).FullName!;\r\n                break;\r\n        }\r\n\r\n        return pageKey;\r\n    }\r\n\r\n    protected async override Task HandleInternalAsync(LaunchActivatedEventArgs args)\r\n    {\r\n        Windows.ApplicationModel.Activation.IProtocolActivatedEventArgs eventArgs =\r\n            (Windows.ApplicationModel.Activation.IProtocolActivatedEventArgs)AppInstance.GetCurrent().GetActivatedEventArgs().Data;\r\n        Uri uri = eventArgs.Uri;\r\n        IWindowService.WindowId windowId = ResolveWindowId(uri);\r\n\r\n        Navigate(windowId, ResolvePageKey(uri, windowId), args);\r\n\r\n        await Task.CompletedTask;\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/App.xaml",
    "content": "﻿<Application\r\n    x:Class=\"WslSettings.App\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:converters=\"using:WslSettings.Converters\">\r\n    <Application.Resources>\r\n        <ResourceDictionary>\r\n            <ResourceDictionary.MergedDictionaries>\r\n                <XamlControlsResources xmlns=\"using:Microsoft.UI.Xaml.Controls\" />\r\n                <ResourceDictionary Source=\"/Styles/Button.xaml\" />\r\n                <ResourceDictionary Source=\"/Styles/FontSizes.xaml\" />\r\n                <ResourceDictionary Source=\"/Styles/Thickness.xaml\" />\r\n                <ResourceDictionary Source=\"/Styles/CommonStyles.xaml\" />\r\n            </ResourceDictionary.MergedDictionaries>\r\n            <converters:MegabyteNumberConverter x:Key=\"MegabyteNumberConverter\" />\r\n            <converters:MegabyteStringConverter x:Key=\"MegabyteStringConverter\" />\r\n            <converters:MillisecondsStringConverter x:Key=\"MillisecondsStringConverter\" />\r\n            <converters:BooleanToVisibilityConverter x:Key=\"BooleanToVisibilityConverter\" />\r\n        </ResourceDictionary>\r\n    </Application.Resources>\r\n</Application>\r\n"
  },
  {
    "path": "src/windows/wslsettings/App.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.Extensions.DependencyInjection;\r\nusing Microsoft.Extensions.Hosting;\r\nusing Microsoft.UI.Xaml;\r\nusing WslSettings.Activation;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.Services;\r\nusing WslSettings.ViewModels;\r\nusing WslSettings.ViewModels.OOBE;\r\nusing WslSettings.ViewModels.Settings;\r\nusing WslSettings.Views.OOBE;\r\nusing WslSettings.Views.Settings;\r\n\r\nnamespace WslSettings;\r\n\r\n// To learn more about WinUI 3, see https://docs.microsoft.com/windows/apps/winui/winui3/.\r\npublic partial class App : Application\r\n{\r\n    // The .NET Generic Host provides dependency injection, configuration, logging, and other services.\r\n    // https://docs.microsoft.com/dotnet/core/extensions/generic-host\r\n    // https://docs.microsoft.com/dotnet/core/extensions/dependency-injection\r\n    // https://docs.microsoft.com/dotnet/core/extensions/configuration\r\n    // https://docs.microsoft.com/dotnet/core/extensions/logging\r\n    public IHost Host\r\n    {\r\n        get;\r\n    }\r\n\r\n    public static T GetService<T>()\r\n        where T : class\r\n    {\r\n        if ((App.Current as App)!.Host.Services.GetService(typeof(T)) is not T service)\r\n        {\r\n            throw new ArgumentException($\"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.\");\r\n        }\r\n\r\n        return service;\r\n    }\r\n\r\n    public static MainWindow? MainWindow { get; set; }\r\n\r\n    public static OOBEWindow? OOBEWindow { get; set; }\r\n\r\n    public static UIElement? AppTitlebar { get; set; }\r\n\r\n    public App()\r\n    {\r\n        InitializeComponent();\r\n\r\n        Host = Microsoft.Extensions.Hosting.Host.\r\n        CreateDefaultBuilder().\r\n        UseContentRoot(AppContext.BaseDirectory).\r\n        ConfigureServices((context, services) =>\r\n        {\r\n            // Default Activation Handler\r\n            services.AddTransient<ActivationHandler<LaunchActivatedEventArgs>, DefaultActivationHandler>();\r\n\r\n            // Other Activation Handlers\r\n            services.AddTransient<IActivationHandler, ProtocolActivationHandler>();\r\n\r\n            // Services\r\n            services.AddTransient<INavigationViewService, NavigationViewService>();\r\n\r\n            services.AddSingleton<IActivationService, ActivationService>();\r\n            services.AddSingleton<IPageService, PageService>();\r\n            services.AddSingleton<INavigationService, NavigationService>();\r\n            services.AddSingleton<IWindowService, WindowService>();\r\n            services.AddSingleton<IWslConfigService, WslConfigService>();\r\n\r\n            // Views and ViewModels\r\n            services.AddTransient<AboutViewModel>();\r\n            services.AddTransient<AboutPage>();\r\n            services.AddTransient<DeveloperViewModel>();\r\n            services.AddTransient<DeveloperPage>();\r\n            services.AddTransient<FileSystemViewModel>();\r\n            services.AddTransient<FileSystemPage>();\r\n            services.AddTransient<MemAndProcViewModel>();\r\n            services.AddTransient<MemAndProcPage>();\r\n            services.AddTransient<NetworkingViewModel>();\r\n            services.AddTransient<NetworkingPage>();\r\n            services.AddTransient<OptionalFeaturesViewModel>();\r\n            services.AddTransient<OptionalFeaturesPage>();\r\n            services.AddTransient<ShellViewModel>();\r\n            services.AddTransient<Views.Settings.ShellPage>();\r\n\r\n            services.AddTransient<DistroManagementViewModel>();\r\n            services.AddTransient<DistroManagementPage>();\r\n            services.AddTransient<DockerDesktopIntegrationViewModel>();\r\n            services.AddTransient<DockerDesktopIntegrationPage>();\r\n            services.AddTransient<GeneralViewModel>();\r\n            services.AddTransient<GeneralPage>();\r\n            services.AddTransient<GPUAccelerationViewModel>();\r\n            services.AddTransient<GPUAccelerationPage>();\r\n            services.AddTransient<GUIAppsViewModel>();\r\n            services.AddTransient<GUIAppsPage>();\r\n            services.AddTransient<NetworkingIntegrationViewModel>();\r\n            services.AddTransient<NetworkingIntegrationPage>();\r\n            services.AddTransient<VSCodeIntegrationViewModel>();\r\n            services.AddTransient<VSCodeIntegrationPage>();\r\n            services.AddTransient<VSIntegrationViewModel>();\r\n            services.AddTransient<VSIntegrationPage>();\r\n            services.AddTransient<WorkingAcrossFileSystemsViewModel>();\r\n            services.AddTransient<WorkingAcrossFileSystemsPage>();\r\n            services.AddTransient<Views.OOBE.ShellPage>();\r\n\r\n            // Configuration\r\n        }).\r\n        Build();\r\n\r\n        UnhandledException += App_UnhandledException;\r\n    }\r\n\r\n    private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)\r\n    {\r\n        // TODO: Log and handle exceptions as appropriate.\r\n        // https://docs.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.application.unhandledexception.\r\n    }\r\n\r\n    protected async override void OnLaunched(LaunchActivatedEventArgs args)\r\n    {\r\n        base.OnLaunched(args);\r\n\r\n        await App.GetService<IActivationService>().ActivateAsync(args);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Behaviors/NavigationViewHeaderBehavior.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Navigation;\r\nusing Microsoft.Xaml.Interactivity;\r\n\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.Behaviors;\r\n\r\npublic class NavigationViewHeaderBehavior : Behavior<NavigationView>\r\n{\r\n    private static NavigationViewHeaderBehavior? _current;\r\n\r\n    private Page? _currentPage;\r\n\r\n    public DataTemplate? DefaultHeaderTemplate\r\n    {\r\n        get; set;\r\n    }\r\n\r\n    public object DefaultHeader\r\n    {\r\n        get => GetValue(DefaultHeaderProperty);\r\n        set => SetValue(DefaultHeaderProperty, value);\r\n    }\r\n\r\n    public static readonly DependencyProperty DefaultHeaderProperty =\r\n        DependencyProperty.Register(\"DefaultHeader\", typeof(object), typeof(NavigationViewHeaderBehavior), new PropertyMetadata(null, (d, e) => _current!.UpdateHeader()));\r\n\r\n    public static NavigationViewHeaderMode GetHeaderMode(Page item) => (NavigationViewHeaderMode)item.GetValue(HeaderModeProperty);\r\n\r\n    public static void SetHeaderMode(Page item, NavigationViewHeaderMode value) => item.SetValue(HeaderModeProperty, value);\r\n\r\n    public static readonly DependencyProperty HeaderModeProperty =\r\n        DependencyProperty.RegisterAttached(\"HeaderMode\", typeof(bool), typeof(NavigationViewHeaderBehavior), new PropertyMetadata(NavigationViewHeaderMode.Always, (d, e) => _current!.UpdateHeader()));\r\n\r\n    public static object GetHeaderContext(Page item) => item.GetValue(HeaderContextProperty);\r\n\r\n    public static void SetHeaderContext(Page item, object value) => item.SetValue(HeaderContextProperty, value);\r\n\r\n    public static readonly DependencyProperty HeaderContextProperty =\r\n        DependencyProperty.RegisterAttached(\"HeaderContext\", typeof(object), typeof(NavigationViewHeaderBehavior), new PropertyMetadata(null, (d, e) => _current!.UpdateHeader()));\r\n\r\n    public static DataTemplate GetHeaderTemplate(Page item) => (DataTemplate)item.GetValue(HeaderTemplateProperty);\r\n\r\n    public static void SetHeaderTemplate(Page item, DataTemplate value) => item.SetValue(HeaderTemplateProperty, value);\r\n\r\n    public static readonly DependencyProperty HeaderTemplateProperty =\r\n        DependencyProperty.RegisterAttached(\"HeaderTemplate\", typeof(DataTemplate), typeof(NavigationViewHeaderBehavior), new PropertyMetadata(null, (d, e) => _current!.UpdateHeaderTemplate()));\r\n\r\n    protected override void OnAttached()\r\n    {\r\n        base.OnAttached();\r\n\r\n        var navigationService = App.GetService<INavigationService>();\r\n        navigationService.Navigated += OnNavigated;\r\n\r\n        _current = this;\r\n    }\r\n\r\n    protected override void OnDetaching()\r\n    {\r\n        base.OnDetaching();\r\n\r\n        var navigationService = App.GetService<INavigationService>();\r\n        navigationService.Navigated -= OnNavigated;\r\n    }\r\n\r\n    private void OnNavigated(object sender, NavigationEventArgs e)\r\n    {\r\n        if (sender is Frame frame && frame.Content is Page page)\r\n        {\r\n            _currentPage = page;\r\n\r\n            UpdateHeader();\r\n            UpdateHeaderTemplate();\r\n        }\r\n    }\r\n\r\n    private void UpdateHeader()\r\n    {\r\n        if (_currentPage != null && AssociatedObject != null)\r\n        {\r\n            var headerMode = GetHeaderMode(_currentPage);\r\n            if (headerMode == NavigationViewHeaderMode.Never)\r\n            {\r\n                AssociatedObject.Header = null;\r\n                AssociatedObject.AlwaysShowHeader = false;\r\n            }\r\n            else\r\n            {\r\n                var headerFromPage = GetHeaderContext(_currentPage);\r\n                if (headerFromPage != null)\r\n                {\r\n                    AssociatedObject.Header = headerFromPage;\r\n                }\r\n                else\r\n                {\r\n                    AssociatedObject.Header = DefaultHeader;\r\n                }\r\n\r\n                if (headerMode == NavigationViewHeaderMode.Always)\r\n                {\r\n                    AssociatedObject.AlwaysShowHeader = true;\r\n                }\r\n                else\r\n                {\r\n                    AssociatedObject.AlwaysShowHeader = false;\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    private void UpdateHeaderTemplate()\r\n    {\r\n        if (_currentPage != null)\r\n        {\r\n            var headerTemplate = GetHeaderTemplate(_currentPage);\r\n            AssociatedObject.HeaderTemplate = headerTemplate ?? DefaultHeaderTemplate;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Behaviors/NavigationViewHeaderMode.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Behaviors;\r\n\r\npublic enum NavigationViewHeaderMode\r\n{\r\n    Always,\r\n    Never,\r\n    Minimal\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/CMakeLists.txt",
    "content": "set(TargetApp wslsettings)\n\nproject(${TargetApp} LANGUAGES CSharp)\n\n# needed for csharp_set_xaml_cs_properties\ninclude(CSharpUtilities)\n\n# place wsl settings binaries/assets into\n# separate directory to simplify packaging\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TargetApp}\")\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}/${TargetApp})\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/${TargetApp})\n\nfile(MAKE_DIRECTORY ${BIN}/${TargetApp}/Assets)\n\n# Symlink the icon in the output directory to make local development easier\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/wsl.ico ${BIN}/${TargetApp}/Assets/wsl.ico)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/wslbw.ico ${BIN}/${TargetApp}/Assets/wslbw.ico)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEDockerIcon.png ${BIN}/${TargetApp}/Assets/SettingsOOBEDockerIcon.png)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEFileExplorerIcon.png ${BIN}/${TargetApp}/Assets/SettingsOOBEFileExplorerIcon.png)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEVSCodeIcon.png ${BIN}/${TargetApp}/Assets/SettingsOOBEVSCodeIcon.png)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEVSIcon.png ${BIN}/${TargetApp}/Assets/SettingsOOBEVSIcon.png)\n\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBECrossOSFileAccess.gif ${BIN}/${TargetApp}/Assets/SettingsOOBECrossOSFileAccess.gif)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEDistroManagement.png ${BIN}/${TargetApp}/Assets/SettingsOOBEDistroManagement.png)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEDockerDesktopIntegration.png ${BIN}/${TargetApp}/Assets/SettingsOOBEDockerDesktopIntegration.png)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEGeneral.png ${BIN}/${TargetApp}/Assets/SettingsOOBEGeneral.png)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEGPUAcceleration.gif ${BIN}/${TargetApp}/Assets/SettingsOOBEGPUAcceleration.gif)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEGUIApps.png ${BIN}/${TargetApp}/Assets/SettingsOOBEGUIApps.png)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBENetworkingIntegration.png ${BIN}/${TargetApp}/Assets/SettingsOOBENetworkingIntegration.png)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEVSCodeIntegration.png ${BIN}/${TargetApp}/Assets/SettingsOOBEVSCodeIntegration.png)\nfile(CREATE_LINK ${CMAKE_SOURCE_DIR}/images/SettingsOOBEVSIntegration.png ${BIN}/${TargetApp}/Assets/SettingsOOBEVSIntegration.png)\n\nfile(GLOB STRINGS ${CMAKE_SOURCE_DIR}/localization/strings/**/*.resw)\n\nadd_executable(\n  ${TargetApp} WIN32\n  app.manifest\n  App.xaml\n  App.xaml.cs\n  Constants.cs\n  LibWsl.cs\n  Usings.cs\n  Activation/ActivationHandler.cs\n  Activation/DefaultActivationHandler.cs\n  Activation/IActivationHandler.cs\n  Activation/ProtocolActivationHandler.cs\n  Behaviors/NavigationViewHeaderBehavior.cs\n  Behaviors/NavigationViewHeaderMode.cs\n  Contracts/Services/IActivationService.cs\n  Contracts/Services/INavigationService.cs\n  Contracts/Services/INavigationViewService.cs\n  Contracts/Services/IPageService.cs\n  Contracts/Services/IWindowService.cs\n  Contracts/Services/IWslConfigService.cs\n  Contracts/ViewModels/INavigationAware.cs\n  Controls/HyperlinkTextBlock.xaml\n  Controls/HyperlinkTextBlock.xaml.cs\n  Controls/OOBEContent.xaml\n  Controls/OOBEContent.xaml.cs\n  Converters/BooleanToVisibilityConverter.cs\n  Converters/MegabyteNumberConverter.cs\n  Converters/MegabyteStringConverter.cs\n  Converters/MillisecondsStringConverter.cs\n  Helpers/FrameExtensions.cs\n  Helpers/NavigationHelper.cs\n  Helpers/ResourceExtensions.cs\n  Helpers/RuntimeHelper.cs\n  Helpers/TitleBarHelper.cs\n  Properties/AssemblyInfo.cs\n  Services/ActivationService.cs\n  Services/NavigationService.cs\n  Services/NavigationViewService.cs\n  Services/PageService.cs\n  Services/WindowService.cs\n  Services/WslConfigService.cs\n  Styles/Button.xaml\n  Styles/FontSizes.xaml\n  Styles/CommonStyles.xaml\n  Styles/Thickness.xaml\n  ViewModels/OOBE/DistroManagementViewModel.cs\n  ViewModels/OOBE/DockerDesktopIntegrationViewModel.cs\n  ViewModels/OOBE/GeneralViewModel.cs\n  ViewModels/OOBE/GPUAccelerationViewModel.cs\n  ViewModels/OOBE/GUIAppsViewModel.cs\n  ViewModels/OOBE/NetworkingIntegrationViewModel.cs\n  ViewModels/OOBE/VSCodeIntegrationViewModel.cs\n  ViewModels/OOBE/VSIntegrationViewModel.cs\n  ViewModels/OOBE/WorkingAcrossFileSystemsViewModel.cs\n  ViewModels/Settings/AboutViewModel.cs\n  ViewModels/Settings/DeveloperViewModel.cs\n  ViewModels/Settings/FileSystemViewModel.cs\n  ViewModels/Settings/MemAndProcViewModel.cs\n  ViewModels/Settings/NetworkingViewModel.cs\n  ViewModels/Settings/OptionalFeaturesViewModel.cs\n  ViewModels/Settings/WslConfigSettingViewModel.cs\n  ViewModels/ShellViewModel.cs\n  Views/OOBE/DistroManagementPage.xaml\n  Views/OOBE/DistroManagementPage.xaml.cs\n  Views/OOBE/DockerDesktopIntegrationPage.xaml\n  Views/OOBE/DockerDesktopIntegrationPage.xaml.cs\n  Views/OOBE/GeneralPage.xaml\n  Views/OOBE/GeneralPage.xaml.cs\n  Views/OOBE/GPUAccelerationPage.xaml\n  Views/OOBE/GPUAccelerationPage.xaml.cs\n  Views/OOBE/GUIAppsPage.xaml\n  Views/OOBE/GUIAppsPage.xaml.cs\n  Views/OOBE/NetworkingIntegrationPage.xaml\n  Views/OOBE/NetworkingIntegrationPage.xaml.cs\n  Views/OOBE/ShellPage.xaml\n  Views/OOBE/ShellPage.xaml.cs\n  Views/OOBE/VSCodeIntegrationPage.xaml\n  Views/OOBE/VSCodeIntegrationPage.xaml.cs\n  Views/OOBE/VSIntegrationPage.xaml\n  Views/OOBE/VSIntegrationPage.xaml.cs\n  Views/OOBE/WorkingAcrossFileSystemsPage.xaml\n  Views/OOBE/WorkingAcrossFileSystemsPage.xaml.cs\n  Views/Settings/AboutPage.xaml\n  Views/Settings/AboutPage.xaml.cs\n  Views/Settings/DeveloperPage.xaml\n  Views/Settings/DeveloperPage.xaml.cs\n  Views/Settings/FileSystemPage.xaml\n  Views/Settings/FileSystemPage.xaml.cs\n  Views/Settings/MemAndProcPage.xaml\n  Views/Settings/MemAndProcPage.xaml.cs\n  Views/Settings/NetworkingPage.xaml\n  Views/Settings/NetworkingPage.xaml.cs\n  Views/Settings/OptionalFeaturesPage.xaml\n  Views/Settings/OptionalFeaturesPage.xaml.cs\n  Views/Settings/ShellPage.xaml\n  Views/Settings/ShellPage.xaml.cs\n  Windows/OOBEWindow.xaml\n  Windows/OOBEWindow.xaml.cs\n  Windows/MainWindow.xaml\n  Windows/MainWindow.xaml.cs\n  ${STRINGS}\n)\n\ncsharp_set_xaml_cs_properties(\n  Controls/HyperlinkTextBlock.xaml\n  Controls/HyperlinkTextBlock.xaml.cs\n  Styles/Button.xaml\n  Styles/FontSizes.xaml\n  Styles/CommonStyles.xaml\n  Styles/Thickness.xaml\n  Windows/OOBEWindow.xaml\n  Windows/OOBEWindow.xaml.cs\n  Windows/MainWindow.xaml\n  Windows/MainWindow.xaml.cs\n  Views/OOBE/DistroManagementPage.xaml\n  Views/OOBE/DistroManagementPage.xaml.cs\n  Views/OOBE/DockerDesktopIntegrationPage.xaml\n  Views/OOBE/DockerDesktopIntegrationPage.xaml.cs\n  Views/OOBE/GeneralPage.xaml\n  Views/OOBE/GeneralPage.xaml.cs\n  Views/OOBE/GPUAccelerationPage.xaml\n  Views/OOBE/GPUAccelerationPage.xaml.cs\n  Views/OOBE/GUIAppsPage.xaml\n  Views/OOBE/GUIAppsPage.xaml.cs\n  Views/OOBE/NetworkingIntegrationPage.xaml\n  Views/OOBE/NetworkingIntegrationPage.xaml.cs\n  Views/OOBE/ShellPage.xaml\n  Views/OOBE/ShellPage.xaml.cs\n  Views/OOBE/VSCodeIntegrationPage.xaml\n  Views/OOBE/VSCodeIntegrationPage.xaml.cs\n  Views/OOBE/VSIntegrationPage.xaml\n  Views/OOBE/VSIntegrationPage.xaml.cs\n  Views/OOBE/WorkingAcrossFileSystemsPage.xaml\n  Views/OOBE/WorkingAcrossFileSystemsPage.xaml.cs\n  Views/Settings/AboutPage.xaml\n  Views/Settings/AboutPage.xaml.cs\n  Views/Settings/DeveloperPage.xaml\n  Views/Settings/DeveloperPage.xaml.cs\n  Views/Settings/FileSystemPage.xaml\n  Views/Settings/FileSystemPage.xaml.cs\n  Views/Settings/MemAndProcPage.xaml\n  Views/Settings/MemAndProcPage.xaml.cs\n  Views/Settings/NetworkingPage.xaml\n  Views/Settings/NetworkingPage.xaml.cs\n  Views/Settings/OptionalFeaturesPage.xaml\n  Views/Settings/OptionalFeaturesPage.xaml.cs\n  Views/Settings/ShellPage.xaml\n  Views/Settings/ShellPage.xaml.cs\n)\n\nset_property(\n  SOURCE App.xaml\n  PROPERTY VS_XAML_TYPE\n  \"ApplicationDefinition\"\n)\n\n# Set the C# language version (defaults to 3.0).\nset(\n  CMAKE_CSharp_FLAGS\n  \"/langversion:latest\"\n)\n\ntarget_compile_options(\n  ${TargetApp}\n  PRIVATE \"/debug:full\"\n  PRIVATE \"/unsafe\"\n)\n\nif (${OFFICIAL_BUILD})\n    add_compile_definitions(WSL_OFFICIAL_BUILD TELEMETRYEVENTSOURCE_PUBLIC)\nendif()\n\n# Package References and versions per WinUI Template Studio Sample app\nset_property(\n  TARGET ${TargetApp}\n  PROPERTY VS_PACKAGE_REFERENCES\n  \"CommunityToolkit.Mvvm_${CTK_MVVM_VERSION};\\\nCommunityToolkit.WinUI.Animations_${CTK_ANIMATIONS_VERSION};\\\nCommunityToolkit.WinUI.Controls.SettingsControls_${CTK_STTNGS_CTRLS_VERSION};\\\nMicrosoft.Extensions.Hosting_${EXTS_HOSTING_VERSION};\\\nMicrosoft.WindowsAppSDK_${WIN_APP_SDK_VERSION};\\\nMicrosoft.Xaml.Behaviors.WinUI.Managed_${XAML_BEHAVIORS_VERSION};\\\nWinUIEx_${WINUIEX_VERSION}\"\n)\n\nset(\n  TARGET_PLATFORM_VERSION\n  \"10.0.26100.0\"\n)\nset(\n  WINDOWS_TARGET_PLATFORM_VERSION\n  \"windows${TARGET_PLATFORM_VERSION}\"\n)\nset(\n  TARGET_PLATFORM_MIN_VERSION\n  \"10.0.19041.0\"\n)\nset(\n  WINDOWS_TARGET_PLATFORM_MIN_VERSION\n  \"windows${TARGET_PLATFORM_MIN_VERSION}\"\n)\n\nset_target_properties(\n  ${TargetApp} PROPERTIES\n  # ----- Dotnet, Windows App SDK and WinUI stuff starts here -----\n  VS_GLOBAL_RootNamespace wslsettings\n  VS_GLOBAL_AppContainerApplication false\n  VS_GLOBAL_AppxPackage false\n  VS_GLOBAL_UseWinUI true\n  VS_GLOBAL_WindowsPackageType None\n  VS_GLOBAL_SelfContained true\n  VS_GLOBAL_WindowsAppSDKSelfContained true\n  VS_GLOBAL_ApplicationIcon \"${CMAKE_SOURCE_DIR}/images/wsl.ico\"\n  VS_GLOBAL_ApplicationManifest \"${CMAKE_CURRENT_SOURCE_DIR}/app.manifest\"\n  VS_GLOBAL_RuntimeIdentifier \"win-${TARGET_PLATFORM}\"\n  VS_GLOBAL_RuntimeIdentifiers \"win-${TARGET_PLATFORM}\"\n  VS_GLOBAL_Platform \"${TARGET_PLATFORM}\"\n  VS_GLOBAL_Platforms \"${TARGET_PLATFORM}\"\n  VS_GLOBAL_PlatformTarget \"${TARGET_PLATFORM}\"\n  VS_GLOBAL_TargetPlatformVersion \"${TARGET_PLATFORM_VERSION}\"\n  VS_GLOBAL_TargetPlatformMinVersion \"${TARGET_PLATFORM_MIN_VERSION}\"\n  VS_GLOBAL_WindowsSdkPackageVersion \"${WINDOWS_SDK_DOTNET_VERSION}\"\n  VS_GLOBAL_ImplicitUsings enable\n  VS_GLOBAL_Nullable enable\n  VS_GLOBAL_AppendRuntimeIdentifierToOutputPath false\n  VS_GLOBAL_GenerateAssemblyInfo false\n  VS_GLOBAL_TargetLatestRuntimePatch false\n  DOTNET_SDK \"Microsoft.NET.Sdk\"\n  DOTNET_TARGET_FRAMEWORK \"net8.0-${WINDOWS_TARGET_PLATFORM_VERSION}\"\n)\n\nconfigure_file(\n  \"${CMAKE_CURRENT_LIST_DIR}/directory.build.targets.in\"\n  \"${CMAKE_CURRENT_LIST_DIR}/directory.build.targets\"\n)\n\nconfigure_file(\n  \"${CMAKE_CURRENT_LIST_DIR}/Properties/AssemblyInfo.cs.in\"\n  \"${CMAKE_CURRENT_LIST_DIR}/Properties/AssemblyInfo.cs\"\n)\n\nadd_dependencies(wslsettings libwsl)\nset_target_properties(wslsettings PROPERTIES FOLDER windows)"
  },
  {
    "path": "src/windows/wslsettings/Constants.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing System.Text.RegularExpressions;\r\n\r\nnamespace WslSettings.Helpers;\r\n\r\npublic class Constants\r\n{\r\n    public static uint MB = 1024 * 1024;\r\n    public static Regex WholeNumberRegex = new Regex(\"^[0-9]+$\");\r\n    public static Regex IntegerRegex = new Regex(\"^((-[1-9][0-9]*)|([0-9]+))$\");\r\n    public static Regex CommaSeparatedWholeNumbersOrEmptyRegex = new Regex(@\"^(([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])[,])*([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?$\");\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Contracts/Services/IActivationService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Contracts.Services;\r\n\r\npublic interface IActivationService\r\n{\r\n    Task ActivateAsync(object activationArgs);\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Contracts/Services/INavigationService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Navigation;\r\n\r\nnamespace WslSettings.Contracts.Services;\r\n\r\npublic interface INavigationService\r\n{\r\n    event NavigatedEventHandler Navigated;\r\n\r\n    bool CanGoBack\r\n    {\r\n        get;\r\n    }\r\n\r\n    Frame? Frame\r\n    {\r\n        get; set;\r\n    }\r\n\r\n    bool NavigateTo(string pageKey, object? parameter = null, bool clearNavigation = false);\r\n\r\n    bool GoBack();\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Contracts/Services/INavigationViewService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\n\r\nnamespace WslSettings.Contracts.Services;\r\n\r\npublic interface INavigationViewService\r\n{\r\n    IList<object>? MenuItems\r\n    {\r\n        get;\r\n    }\r\n\r\n    object? SettingsItem\r\n    {\r\n        get;\r\n    }\r\n\r\n    void Initialize(NavigationView navigationView);\r\n\r\n    void UnregisterEvents();\r\n\r\n    NavigationViewItem? GetSelectedItem(Type pageType);\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Contracts/Services/IPageService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Contracts.Services;\r\n\r\npublic interface IPageService\r\n{\r\n    Type GetPageType(string key);\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Contracts/Services/IWindowService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Contracts.Services;\r\n\r\npublic interface IWindowService\r\n{\r\n    public enum WindowId\r\n    {\r\n        MainWindow,\r\n        OOBEWindow\r\n    }\r\n\r\n    WindowEx CreateOrGetWindow(WindowId windowId);\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Contracts/Services/IWslConfigService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Contracts.Services;\r\n\r\npublic interface IWslConfigService\r\n{\r\n    IWslConfigSetting GetWslConfigSetting(WslConfigEntry wslConfigEntry, bool defaultSetting = false);\r\n    uint SetWslConfigSetting(IWslConfigSetting setting);\r\n    public delegate void WslConfigChangedEventHandler();\r\n    event WslConfigChangedEventHandler WslConfigChanged;\r\n}\r\n\r\npublic interface IWslConfigSetting\r\n{\r\n    WslConfigEntry ConfigEntry { get; }\r\n    string StringValue { get; }\r\n    ulong UInt64Value { get; }\r\n    int Int32Value { get; }\r\n    bool BoolValue { get; }\r\n    NetworkingConfiguration NetworkingConfigurationValue { get; }\r\n    MemoryReclaimMode MemoryReclaimModeValue { get; }\r\n    uint SetValue(object? value);\r\n    bool Equals(object? obj);\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Contracts/ViewModels/INavigationAware.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Contracts.ViewModels;\r\n\r\npublic interface INavigationAware\r\n{\r\n    void OnNavigatedTo(object parameter);\r\n\r\n    void OnNavigatedFrom();\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Controls/HyperlinkTextBlock.xaml",
    "content": "<UserControl\r\n    x:Class=\"WslSettings.Controls.HyperlinkTextBlock\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\r\n    mc:Ignorable=\"d\">\r\n\r\n    <TextBlock TextWrapping=\"WrapWholeWords\">\r\n        <!-- Note: The comments in between the elements prevents extra space from being added to the string -->\r\n        <Run Text=\"{x:Bind TextBeforeHyperlink}\" /><!--\r\n        --><Hyperlink NavigateUri=\"{x:Bind NavigateUri}\">\r\n            <Run Text=\"{x:Bind HyperLinkText}\" />\r\n        </Hyperlink><!--\r\n        --><Run Text=\"{x:Bind TextAfterHyperlink}\" />\r\n    </TextBlock>\r\n</UserControl>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Controls/HyperlinkTextBlock.xaml.cs",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Controls;\r\n\r\nnamespace WslSettings.Controls;\r\n\r\n/// <summary>\r\n/// Custom control for a text block that contains a hyperlink in the text.\r\n/// </summary>\r\n/// <remarks>\r\n/// The <see cref=\"Text\"/> property must contain a substring enclosed in square\r\n/// brackets ('[' and ']'). When displaying the text block, the brackets are\r\n/// removed and the text inside is made into a link that points to <see cref=\"NavigateUri\"/>\r\n/// </remarks>\r\npublic sealed partial class HyperlinkTextBlock : UserControl\r\n{\r\n    /// <summary>\r\n    /// Gets or sets the text for the text block. This must contain\r\n    /// a substring contained within square brackets ('[', ']')\r\n    /// </summary>\r\n    public string Text\r\n    {\r\n        get => (string)GetValue(TextProperty);\r\n        set\r\n        {\r\n            SetValue(TextProperty, value);\r\n\r\n            var openingBracketIndex = value.IndexOf('[');\r\n            var closingBracketIndex = value.IndexOf(']');\r\n\r\n            if (openingBracketIndex == -1 || closingBracketIndex == -1\r\n                || openingBracketIndex > closingBracketIndex)\r\n            {\r\n                // If there is not string contained between brackets, show the text as is\r\n                TextBeforeHyperlink = value;\r\n                HyperLinkText = string.Empty;\r\n                TextAfterHyperlink = string.Empty;\r\n            }\r\n            else\r\n            {\r\n                TextBeforeHyperlink = value.Substring(0, openingBracketIndex);\r\n                HyperLinkText = value.Substring(openingBracketIndex + 1, closingBracketIndex - openingBracketIndex - 1);\r\n                TextAfterHyperlink = value.Substring(closingBracketIndex + 1);\r\n            }\r\n        }\r\n    }\r\n\r\n    public string NavigateUri\r\n    {\r\n        get => (string)GetValue(NavigateUriProperty);\r\n        set => SetValue(NavigateUriProperty, value);\r\n    }\r\n\r\n    internal string TextBeforeHyperlink { get; private set; } = string.Empty;\r\n\r\n    internal string HyperLinkText { get; private set; } = string.Empty;\r\n\r\n    internal string TextAfterHyperlink { get; private set; } = string.Empty;\r\n\r\n    public HyperlinkTextBlock()\r\n    {\r\n        InitializeComponent();\r\n    }\r\n\r\n    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(HyperlinkTextBlock), new PropertyMetadata(string.Empty));\r\n    public static readonly DependencyProperty NavigateUriProperty = DependencyProperty.Register(nameof(NavigateUri), typeof(string), typeof(HyperlinkTextBlock), new PropertyMetadata(string.Empty));\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Controls/OOBEContent.xaml",
    "content": "﻿<UserControl\r\n    x:Class=\"WslSettings.Controls.OOBEContent\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\r\n\r\n    <Grid>\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"*\" />\r\n        </Grid.RowDefinitions>\r\n\r\n        <Image\r\n            x:Name=\"HeaderImage\"\r\n            Height=\"{x:Bind HeroImageHeight}\"\r\n            HorizontalAlignment=\"Center\"\r\n            VerticalAlignment=\"Center\"\r\n            Source=\"{x:Bind HeroImage}\"\r\n            Stretch=\"UniformToFill\" />\r\n\r\n        <ScrollViewer Grid.Row=\"1\" VerticalScrollBarVisibility=\"Auto\">\r\n            <StackPanel\r\n                Margin=\"{StaticResource ContentPageMargin}\"\r\n                VerticalAlignment=\"Top\"\r\n                Orientation=\"Vertical\"\r\n                Spacing=\"12\">\r\n\r\n                <TextBlock\r\n                    x:Name=\"TitleTxt\"\r\n                    AutomationProperties.HeadingLevel=\"Level1\"\r\n                    Style=\"{ThemeResource TitleTextBlockStyle}\"\r\n                    Text=\"{x:Bind Title}\" />\r\n\r\n                <TextBlock\r\n                    x:Name=\"DescriptionTxt\"\r\n                    Foreground=\"{ThemeResource TextFillColorSecondaryBrush}\"\r\n                    Text=\"{x:Bind Description}\"\r\n                    TextWrapping=\"Wrap\" />\r\n\r\n                <ContentPresenter\r\n                    x:Name=\"ModuleContentPresenter\"\r\n                    Margin=\"0,8\"\r\n                    HorizontalAlignment=\"Stretch\"\r\n                    HorizontalContentAlignment=\"Stretch\"\r\n                    Content=\"{x:Bind PageContent}\" />\r\n            </StackPanel>\r\n        </ScrollViewer>\r\n    </Grid>\r\n</UserControl>"
  },
  {
    "path": "src/windows/wslsettings/Controls/OOBEContent.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Windows.UI.ViewManagement;\r\n\r\nnamespace WslSettings.Controls\r\n{\r\n    public sealed partial class OOBEContent : UserControl\r\n    {\r\n        // Constants for hero image height calculations\r\n        private const double BaseImageHeight = 280.0;\r\n        private const double MinimumImageHeight = 200.0;\r\n\r\n        private static readonly UISettings Settings = new UISettings();\r\n\r\n        public OOBEContent()\r\n        {\r\n            this.InitializeComponent();\r\n\r\n            // Set initial hero image height based on current text scaling\r\n            UpdateHeroImageHeight();\r\n\r\n            // Subscribe to text scale factor changes for dynamic updates\r\n            Settings.TextScaleFactorChanged += OnTextScaleFactorChanged;\r\n\r\n            // Ensure event cleanup when control is unloaded\r\n            this.Unloaded += (s, e) => Settings.TextScaleFactorChanged -= OnTextScaleFactorChanged;\r\n        }\r\n\r\n        private void UpdateHeroImageHeight()\r\n        {\r\n            double textScaleFactor = Settings.TextScaleFactor;\r\n\r\n            // Reduce image height when text scaling increases to preserve content space\r\n            // Use inverse relationship: as text gets larger, image gets proportionally smaller\r\n            HeroImageHeight = Math.Max(BaseImageHeight / textScaleFactor, MinimumImageHeight);\r\n        }\r\n\r\n        private void OnTextScaleFactorChanged(UISettings sender, object args)\r\n        {\r\n            // Update hero image height when text scaling changes at runtime\r\n            this.DispatcherQueue.TryEnqueue(() => UpdateHeroImageHeight());\r\n        }\r\n\r\n        public string Title\r\n        {\r\n            get { return (string)GetValue(TitleProperty); }\r\n            set { SetValue(TitleProperty, value); }\r\n        }\r\n\r\n        public string Description\r\n        {\r\n            get => (string)GetValue(DescriptionProperty);\r\n            set => SetValue(DescriptionProperty, value);\r\n        }\r\n\r\n        public string HeroImage\r\n        {\r\n            get => (string)GetValue(HeroImageProperty);\r\n            set => SetValue(HeroImageProperty, value);\r\n        }\r\n\r\n        public double HeroImageHeight\r\n        {\r\n            get { return (double)GetValue(HeroImageHeightProperty); }\r\n            set { SetValue(HeroImageHeightProperty, value); }\r\n        }\r\n\r\n        public object PageContent\r\n        {\r\n            get { return (object)GetValue(PageContentProperty); }\r\n            set { SetValue(PageContentProperty, value); }\r\n        }\r\n\r\n        public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(\"Title\", typeof(string), typeof(OOBEContent), new PropertyMetadata(default(string)));\r\n        public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(\"Description\", typeof(string), typeof(OOBEContent), new PropertyMetadata(default(string)));\r\n        public static readonly DependencyProperty HeroImageProperty = DependencyProperty.Register(\"HeroImage\", typeof(string), typeof(OOBEContent), new PropertyMetadata(default(string)));\r\n        public static readonly DependencyProperty PageContentProperty = DependencyProperty.Register(\"PageContent\", typeof(object), typeof(OOBEContent), new PropertyMetadata(new Grid()));\r\n        public static readonly DependencyProperty HeroImageHeightProperty = DependencyProperty.Register(\"HeroImageHeight\", typeof(double), typeof(OOBEContent), new PropertyMetadata(BaseImageHeight));\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Converters/BooleanToVisibilityConverter.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Data;\r\nusing Microsoft.UI.Xaml;\r\n\r\nnamespace WslSettings.Converters\r\n{\r\n    public sealed class BooleanToVisibilityConverter : IValueConverter\r\n    {\r\n        public object Convert(object value, Type targetType, object parameter, string language)\r\n        {\r\n            return (value is bool && (bool)value) ? Visibility.Visible : Visibility.Collapsed;\r\n        }\r\n\r\n        public object ConvertBack(object value, Type targetType, object parameter, string language)\r\n        {\r\n            return value is Visibility && (Visibility)value == Visibility.Visible;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Converters/MegabyteNumberConverter.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Converters\r\n{\r\n    public sealed class MegabyteNumberConverter : Microsoft.UI.Xaml.Data.IValueConverter\r\n    {\r\n        public object? Convert(object value, Type targetType, object parameter, string language)\r\n        {\r\n            if (value != null && UInt64.TryParse(value as string, out UInt64 parseResult))\r\n            {\r\n                return (parseResult / Constants.MB).ToString();\r\n            }\r\n\r\n            return null;\r\n        }\r\n\r\n        public object? ConvertBack(object value, Type targetType, object parameter, string language)\r\n        {\r\n\r\n            if (value != null && UInt64.TryParse(value as string, out UInt64 parseResult))\r\n            {\r\n                return (parseResult * Constants.MB).ToString();\r\n            }\r\n\r\n            return null;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Converters/MegabyteStringConverter.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Converters\r\n{\r\n    public sealed class MegabyteStringConverter : Microsoft.UI.Xaml.Data.IValueConverter\r\n    {\r\n        public object? Convert(object value, Type targetType, object parameter, string language)\r\n        {\r\n            if (value == null)\r\n            {\r\n                return null;\r\n            }\r\n\r\n            return string.Format(\"Settings_MegabyteStringFormat\".GetLocalized(), (System.Convert.ToUInt64(value) / Constants.MB));\r\n        }\r\n\r\n        public object ConvertBack(object value, Type targetType, object parameter, string language)\r\n        {\r\n            throw new NotImplementedException();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Converters/MillisecondsStringConverter.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nnamespace WslSettings.Converters\r\n{\r\n    public sealed class MillisecondsStringConverter : Microsoft.UI.Xaml.Data.IValueConverter\r\n    {\r\n        public object? Convert(object value, Type targetType, object parameter, string language)\r\n        {\r\n            if (value == null || (value is ulong && (ulong)value == 0))\r\n            {\r\n                return null;\r\n            }\r\n\r\n            return string.Format(\"Settings_MillisecondsStringFormat\".GetLocalized(), value);\r\n        }\r\n\r\n        public object ConvertBack(object value, Type targetType, object parameter, string language)\r\n        {\r\n            throw new NotImplementedException();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Helpers/FrameExtensions.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\n\r\nnamespace WslSettings.Helpers;\r\n\r\npublic static class FrameExtensions\r\n{\r\n    public static object? GetPageViewModel(this Frame frame) => frame?.Content?.GetType().GetProperty(\"ViewModel\")?.GetValue(frame.Content, null);\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Helpers/NavigationHelper.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Controls;\r\n\r\nnamespace WslSettings.Helpers;\r\n\r\n// Helper class to set the navigation target for a NavigationViewItem.\r\n//\r\n// Usage in XAML:\r\n// <NavigationViewItem x:Uid=\"Shell_Main\" Icon=\"Document\" helpers:NavigationHelper.NavigateTo=\"AppName.ViewModels.MainViewModel\" />\r\n//\r\n// Usage in code:\r\n// NavigationHelper.SetNavigateTo(navigationViewItem, typeof(MainViewModel).FullName);\r\npublic class NavigationHelper\r\n{\r\n    public static string GetNavigateTo(NavigationViewItem item) => (string)item.GetValue(NavigateToProperty);\r\n\r\n    public static void SetNavigateTo(NavigationViewItem item, string value) => item.SetValue(NavigateToProperty, value);\r\n\r\n    public static readonly DependencyProperty NavigateToProperty =\r\n        DependencyProperty.RegisterAttached(\"NavigateTo\", typeof(string), typeof(NavigationHelper), new PropertyMetadata(null));\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Helpers/ResourceExtensions.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.Windows.ApplicationModel.Resources;\r\n\r\nnamespace WslSettings.Helpers;\r\n\r\npublic static class ResourceExtensions\r\n{\r\n    private static readonly ResourceLoader _resourceLoader = new();\r\n\r\n    public static string GetLocalized(this string resourceKey) => _resourceLoader.GetString(resourceKey);\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Helpers/RuntimeHelper.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.WinUI.Controls;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Input;\r\nusing System.Diagnostics;\r\nusing System.Reflection;\r\nusing System.Runtime.InteropServices;\r\nusing System.Text;\r\nusing Windows.ApplicationModel;\r\nusing Windows.Storage.Pickers;\r\n\r\nnamespace WslSettings.Helpers;\r\n\r\npublic class RuntimeHelper\r\n{\r\n    [DllImport(\"kernel32.dll\", CharSet = CharSet.Unicode, SetLastError = true)]\r\n    private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder? packageFullName);\r\n\r\n    public static Windows.Foundation.IAsyncOperation<Windows.Storage.StorageFile> PickSingleFileAsync(List<string>? fileTypeFilters = null)\r\n    {\r\n        // Create a file picker\r\n        var openPicker = new FileOpenPicker();\r\n        var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);\r\n\r\n        // Initialize the file picker with the window handle (HWND).\r\n        WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd);\r\n\r\n        fileTypeFilters ??= [\"*\"];\r\n\r\n        // Set options for your file picker\r\n        openPicker.ViewMode = PickerViewMode.List;\r\n        foreach (string fileTypeFilter in fileTypeFilters)\r\n        {\r\n            openPicker.FileTypeFilter.Add(fileTypeFilter);\r\n        }\r\n\r\n        // Open the picker for the user to pick a file\r\n        return openPicker.PickSingleFileAsync();\r\n    }\r\n\r\n    public static void TryMoveFocusPreviousControl(Button? button)\r\n    {\r\n        if (button == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        FindNextElementOptions fneo = new() { SearchRoot = button.XamlRoot.Content };\r\n        FocusManager.TryMoveFocus(FocusNavigationDirection.Previous, fneo);\r\n    }\r\n\r\n    public static void SetupSettingsExpanderFocusManagement(Microsoft.UI.Xaml.FrameworkElement expander, Microsoft.UI.Xaml.Controls.Control firstFocusableElement)\r\n    {\r\n        if (expander is CommunityToolkit.WinUI.Controls.SettingsExpander settingsExpander)\r\n        {\r\n            settingsExpander.RegisterPropertyChangedCallback(CommunityToolkit.WinUI.Controls.SettingsExpander.IsExpandedProperty, (sender, dp) =>\r\n            {\r\n                if (sender is CommunityToolkit.WinUI.Controls.SettingsExpander se && se.IsExpanded)\r\n                {\r\n                    System.EventHandler<object>? layoutHandler = null;\r\n                    layoutHandler = (s, e) =>\r\n                    {\r\n                        se.LayoutUpdated -= layoutHandler;\r\n                        firstFocusableElement.Focus(Microsoft.UI.Xaml.FocusState.Keyboard);\r\n                    };\r\n\r\n                    se.LayoutUpdated += layoutHandler;\r\n                }\r\n            });\r\n        }\r\n    }\r\n\r\n    public static void SetupExpanderFocusManagementByName(Microsoft.UI.Xaml.FrameworkElement parent, string expanderName, string textBoxName)\r\n    {\r\n        var expander = parent.FindName(expanderName) as Microsoft.UI.Xaml.FrameworkElement;\r\n        var textBox = parent.FindName(textBoxName) as Microsoft.UI.Xaml.Controls.Control;\r\n\r\n        Debug.Assert(expander != null, $\"Expander '{expanderName}' not found\");\r\n        Debug.Assert(textBox != null, $\"TextBox '{textBoxName}' not found\");\r\n\r\n        if (expander != null && textBox != null)\r\n        {\r\n            SetupSettingsExpanderFocusManagement(expander, textBox);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Helpers/TitleBarHelper.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing System.Runtime.InteropServices;\r\nusing Microsoft.UI;\r\nusing Microsoft.UI.Xaml;\r\nusing Windows.UI;\r\nusing Windows.UI.ViewManagement;\r\n\r\nnamespace WslSettings.Helpers;\r\n\r\n// Helper class to workaround custom title bar bugs.\r\n// DISCLAIMER: The resource key names and color values used below are subject to change. Do not depend on them.\r\n// https://github.com/microsoft/TemplateStudio/issues/4516\r\ninternal class TitleBarHelper\r\n{\r\n    private const int WAINACTIVE = 0x00;\r\n    private const int WAACTIVE = 0x01;\r\n    private const int WMACTIVATE = 0x0006;\r\n\r\n    [DllImport(\"user32.dll\")]\r\n    private static extern IntPtr GetActiveWindow();\r\n\r\n    [DllImport(\"user32.dll\", CharSet = CharSet.Auto)]\r\n    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);\r\n\r\n    public static void UpdateTitleBar(Window window, ElementTheme theme)\r\n    {\r\n        if (window.ExtendsContentIntoTitleBar)\r\n        {\r\n            if (theme == ElementTheme.Default)\r\n            {\r\n                var uiSettings = new UISettings();\r\n                var background = uiSettings.GetColorValue(UIColorType.Background);\r\n\r\n                theme = background == Microsoft.UI.Colors.White ? ElementTheme.Light : ElementTheme.Dark;\r\n            }\r\n\r\n            if (theme == ElementTheme.Default)\r\n            {\r\n                theme = Application.Current.RequestedTheme == ApplicationTheme.Light ? ElementTheme.Light : ElementTheme.Dark;\r\n            }\r\n\r\n            window.AppWindow.TitleBar.ButtonForegroundColor = theme switch\r\n            {\r\n                ElementTheme.Dark => Microsoft.UI.Colors.White,\r\n                ElementTheme.Light => Microsoft.UI.Colors.Black,\r\n                _ => Microsoft.UI.Colors.Transparent\r\n            };\r\n\r\n            window.AppWindow.TitleBar.ButtonHoverForegroundColor = theme switch\r\n            {\r\n                ElementTheme.Dark => Microsoft.UI.Colors.White,\r\n                ElementTheme.Light => Microsoft.UI.Colors.Black,\r\n                _ => Microsoft.UI.Colors.Transparent\r\n            };\r\n\r\n            window.AppWindow.TitleBar.ButtonHoverBackgroundColor = theme switch\r\n            {\r\n                ElementTheme.Dark => Color.FromArgb(0x33, 0xFF, 0xFF, 0xFF),\r\n                ElementTheme.Light => Color.FromArgb(0x33, 0x00, 0x00, 0x00),\r\n                _ => Microsoft.UI.Colors.Transparent\r\n            };\r\n\r\n            window.AppWindow.TitleBar.ButtonPressedBackgroundColor = theme switch\r\n            {\r\n                ElementTheme.Dark => Color.FromArgb(0x66, 0xFF, 0xFF, 0xFF),\r\n                ElementTheme.Light => Color.FromArgb(0x66, 0x00, 0x00, 0x00),\r\n                _ => Microsoft.UI.Colors.Transparent\r\n            };\r\n\r\n            window.AppWindow.TitleBar.BackgroundColor = Microsoft.UI.Colors.Transparent;\r\n\r\n            var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(window);\r\n            if (hwnd == GetActiveWindow())\r\n            {\r\n                SendMessage(hwnd, WMACTIVATE, WAINACTIVE, IntPtr.Zero);\r\n                SendMessage(hwnd, WMACTIVATE, WAACTIVE, IntPtr.Zero);\r\n            }\r\n            else\r\n            {\r\n                SendMessage(hwnd, WMACTIVATE, WAACTIVE, IntPtr.Zero);\r\n                SendMessage(hwnd, WMACTIVATE, WAINACTIVE, IntPtr.Zero);\r\n            }\r\n        }\r\n    }\r\n\r\n    public static void ApplySystemThemeToCaptionButtons(Window window)\r\n    {\r\n        var frame = App.AppTitlebar as FrameworkElement;\r\n        if (frame != null)\r\n        {\r\n            UpdateTitleBar(window, frame.ActualTheme);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/LibWsl.cs",
    "content": "// ----------------------------------------------------------------------------\r\n// <auto-generated>\r\n// This is autogenerated code by CppSharp with tweaks.\r\n// Do not edit this file or all your changes will be lost after re-generation.\r\n// </auto-generated>\r\n// ----------------------------------------------------------------------------\r\nusing System;\r\nusing System.Runtime.InteropServices;\r\nusing System.Security;\r\nusing WslSettings;\r\nusing __CallingConvention = global::System.Runtime.InteropServices.CallingConvention;\r\nusing __IntPtr = global::System.IntPtr;\r\n\r\n#pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required\r\n\r\nnamespace LibWsl\r\n{\r\n    public enum WslConfigEntry\r\n    {\r\n        NoEntry = 0,\r\n        ProcessorCount = 1,\r\n        MemorySizeBytes = 2,\r\n        SwapSizeBytes = 3,\r\n        SwapFilePath = 4,\r\n        VhdSizeBytes = 5,\r\n        NetworkingMode = 6,\r\n        FirewallEnabled = 7,\r\n        IgnoredPorts = 8,\r\n        LocalhostForwardingEnabled = 9,\r\n        HostAddressLoopbackEnabled = 10,\r\n        AutoProxyEnabled = 11,\r\n        InitialAutoProxyTimeout = 12,\r\n        DNSProxyEnabled = 13,\r\n        DNSTunnelingEnabled = 14,\r\n        BestEffortDNSParsingEnabled = 15,\r\n        AutoMemoryReclaim = 16,\r\n        GUIApplicationsEnabled = 17,\r\n        NestedVirtualizationEnabled = 18,\r\n        SafeModeEnabled = 19,\r\n        SparseVHDEnabled = 20,\r\n        VMIdleTimeout = 21,\r\n        DebugConsoleEnabled = 22,\r\n        HardwarePerformanceCountersEnabled = 23,\r\n        KernelPath = 24,\r\n        SystemDistroPath = 25,\r\n        KernelModulesPath = 26\r\n    }\r\n\r\n    public enum NetworkingConfiguration\r\n    {\r\n        None = 0,\r\n        Nat = 1,\r\n        Bridged = 2,\r\n        Mirrored = 3,\r\n        VirtioProxy = 4\r\n    }\r\n\r\n    public enum MemoryReclaimMode\r\n    {\r\n        Disabled = 0,\r\n        Gradual = 1,\r\n        DropCache = 2\r\n    }\r\n\r\n    public unsafe partial class WslConfig\r\n    {\r\n        public partial struct __Internal\r\n        {\r\n        }\r\n\r\n        public __IntPtr __Instance { get; protected set; }\r\n\r\n        internal static readonly new global::System.Collections.Concurrent.ConcurrentDictionary<IntPtr, global::LibWsl.WslConfig> NativeToManagedMap =\r\n            new global::System.Collections.Concurrent.ConcurrentDictionary<IntPtr, global::LibWsl.WslConfig>();\r\n\r\n        internal static void __RecordNativeToManagedMapping(IntPtr native, global::LibWsl.WslConfig managed)\r\n        {\r\n            NativeToManagedMap[native] = managed;\r\n        }\r\n\r\n        internal static bool __TryGetNativeToManagedMapping(IntPtr native, out global::LibWsl.WslConfig managed)\r\n        {\r\n\r\n            return NativeToManagedMap.TryGetValue(native, out managed);\r\n        }\r\n\r\n        protected bool __ownsNativeInstance;\r\n\r\n        internal static WslConfig __CreateInstance(__IntPtr native, bool skipVTables = false)\r\n        {\r\n            if (native == __IntPtr.Zero)\r\n                return null;\r\n            return new WslConfig(native.ToPointer(), skipVTables);\r\n        }\r\n\r\n        internal static WslConfig __GetOrCreateInstance(__IntPtr native, bool saveInstance = false, bool skipVTables = false)\r\n        {\r\n            if (native == __IntPtr.Zero)\r\n                return null;\r\n            if (__TryGetNativeToManagedMapping(native, out var managed))\r\n                return (WslConfig)managed;\r\n            var result = __CreateInstance(native, skipVTables);\r\n            if (saveInstance)\r\n                __RecordNativeToManagedMapping(native, result);\r\n            return result;\r\n        }\r\n\r\n        internal static WslConfig __CreateInstance(__Internal native, bool skipVTables = false)\r\n        {\r\n            return new WslConfig(native, skipVTables);\r\n        }\r\n\r\n        private static void* __CopyValue(__Internal native)\r\n        {\r\n            var ret = Marshal.AllocHGlobal(sizeof(__Internal));\r\n            *(__Internal*) ret = native;\r\n            return ret.ToPointer();\r\n        }\r\n\r\n        private WslConfig(__Internal native, bool skipVTables = false)\r\n            : this(__CopyValue(native), skipVTables)\r\n        {\r\n            __ownsNativeInstance = true;\r\n            __RecordNativeToManagedMapping(__Instance, this);\r\n        }\r\n\r\n        protected WslConfig(void* native, bool skipVTables = false)\r\n        {\r\n            if (native == null)\r\n                return;\r\n            __Instance = new __IntPtr(native);\r\n        }\r\n    }\r\n\r\n    public unsafe partial class WslConfigSetting : IDisposable\r\n    {\r\n        [StructLayout(LayoutKind.Explicit, Size = 16)]\r\n        public partial struct __Internal\r\n        {\r\n            [FieldOffset(0)]\r\n            internal global::LibWsl.WslConfigEntry ConfigEntry;\r\n\r\n            [FieldOffset(8)]\r\n            internal __IntPtr StringValue;\r\n\r\n            [FieldOffset(8)]\r\n            internal ulong UInt64Value;\r\n\r\n            [FieldOffset(8)]\r\n            internal int Int32Value;\r\n\r\n            [FieldOffset(8)]\r\n            internal byte BoolValue;\r\n\r\n            [FieldOffset(8)]\r\n            internal global::LibWsl.NetworkingConfiguration NetworkingConfigurationValue;\r\n\r\n            [FieldOffset(8)]\r\n            internal global::LibWsl.MemoryReclaimMode MemoryReclaimModeValue;\r\n        }\r\n\r\n        public __IntPtr __Instance { get; protected set; }\r\n\r\n        internal static readonly new global::System.Collections.Concurrent.ConcurrentDictionary<IntPtr, global::LibWsl.WslConfigSetting> NativeToManagedMap =\r\n            new global::System.Collections.Concurrent.ConcurrentDictionary<IntPtr, global::LibWsl.WslConfigSetting>();\r\n\r\n        internal static void __RecordNativeToManagedMapping(IntPtr native, global::LibWsl.WslConfigSetting managed)\r\n        {\r\n            NativeToManagedMap[native] = managed;\r\n        }\r\n\r\n        internal static bool __TryGetNativeToManagedMapping(IntPtr native, out global::LibWsl.WslConfigSetting managed)\r\n        {\r\n\r\n            return NativeToManagedMap.TryGetValue(native, out managed);\r\n        }\r\n\r\n        protected bool __ownsNativeInstance;\r\n        protected bool __ownsNativeStringValueInstance;\r\n\r\n        internal static WslConfigSetting __CreateInstance(__IntPtr native, bool skipVTables = false)\r\n        {\r\n            if (native == __IntPtr.Zero)\r\n                return null;\r\n            return new WslConfigSetting(native.ToPointer(), skipVTables);\r\n        }\r\n\r\n        internal static WslConfigSetting __GetOrCreateInstance(__IntPtr native, bool saveInstance = false, bool skipVTables = false)\r\n        {\r\n            if (native == __IntPtr.Zero)\r\n                return null;\r\n            if (__TryGetNativeToManagedMapping(native, out var managed))\r\n                return (WslConfigSetting)managed;\r\n            var result = __CreateInstance(native, skipVTables);\r\n            if (saveInstance)\r\n                __RecordNativeToManagedMapping(native, result);\r\n            return result;\r\n        }\r\n\r\n        internal static WslConfigSetting __CreateInstance(__Internal native, bool skipVTables = false)\r\n        {\r\n            return new WslConfigSetting(native, skipVTables);\r\n        }\r\n\r\n        private static void* __CopyValue(__Internal native)\r\n        {\r\n            var ret = Marshal.AllocHGlobal(sizeof(__Internal));\r\n            *(__Internal*) ret = native;\r\n            return ret.ToPointer();\r\n        }\r\n\r\n        private WslConfigSetting(__Internal native, bool skipVTables = false)\r\n            : this(__CopyValue(native), skipVTables)\r\n        {\r\n            __ownsNativeInstance = true;\r\n            __RecordNativeToManagedMapping(__Instance, this);\r\n            __ownsNativeStringValueInstance = false;\r\n        }\r\n\r\n        protected WslConfigSetting(void* native, bool skipVTables = false)\r\n        {\r\n            if (native == null)\r\n                return;\r\n            __Instance = new __IntPtr(native);\r\n        }\r\n\r\n        public void Dispose()\r\n        {\r\n            Dispose(disposing: true, callNativeDtor : __ownsNativeInstance );\r\n        }\r\n\r\n        partial void DisposePartial(bool disposing);\r\n\r\n        internal protected virtual void Dispose(bool disposing, bool callNativeDtor )\r\n        {\r\n            if (__Instance == IntPtr.Zero)\r\n                return;\r\n            NativeToManagedMap.TryRemove(__Instance, out _);\r\n            DisposePartial(disposing);\r\n            if (__ownsNativeInstance)\r\n                Marshal.FreeHGlobal(__Instance);\r\n            if (__ownsNativeStringValueInstance && ((__Internal*)__Instance)->StringValue != __IntPtr.Zero)\r\n                Marshal.FreeHGlobal(((__Internal*)__Instance)->StringValue);\r\n            __Instance = IntPtr.Zero;\r\n        }\r\n\r\n        public global::LibWsl.WslConfigEntry ConfigEntry\r\n        {\r\n            get\r\n            {\r\n                return ((__Internal*)__Instance)->ConfigEntry;\r\n            }\r\n\r\n            set\r\n            {\r\n                ((__Internal*)__Instance)->ConfigEntry = value;\r\n            }\r\n        }\r\n\r\n        public string StringValue\r\n        {\r\n            get\r\n            {\r\n                return Marshal.PtrToStringAuto((IntPtr)((__Internal*)__Instance)->StringValue);\r\n            }\r\n            set\r\n            {\r\n                if (__ownsNativeStringValueInstance && ((__Internal*)__Instance)->StringValue != __IntPtr.Zero)\r\n                    Marshal.FreeHGlobal(((__Internal*)__Instance)->StringValue);\r\n                ((__Internal*)__Instance)->StringValue = Marshal.StringToHGlobalAuto(value);\r\n                __ownsNativeStringValueInstance = true;\r\n            }\r\n        }\r\n\r\n        public ulong UInt64Value\r\n        {\r\n            get\r\n            {\r\n                return ((__Internal*)__Instance)->UInt64Value;\r\n            }\r\n\r\n            set\r\n            {\r\n                ((__Internal*)__Instance)->UInt64Value = value;\r\n            }\r\n        }\r\n\r\n        public int Int32Value\r\n        {\r\n            get\r\n            {\r\n                return ((__Internal*)__Instance)->Int32Value;\r\n            }\r\n\r\n            set\r\n            {\r\n                ((__Internal*)__Instance)->Int32Value = value;\r\n            }\r\n        }\r\n\r\n        public bool BoolValue\r\n        {\r\n            get\r\n            {\r\n                return ((__Internal*)__Instance)->BoolValue != 0;\r\n            }\r\n\r\n            set\r\n            {\r\n                ((__Internal*)__Instance)->BoolValue = (byte) (value ? 1 : 0);\r\n            }\r\n        }\r\n\r\n        public global::LibWsl.NetworkingConfiguration NetworkingConfigurationValue\r\n        {\r\n            get\r\n            {\r\n                return ((__Internal*)__Instance)->NetworkingConfigurationValue;\r\n            }\r\n\r\n            set\r\n            {\r\n                ((__Internal*)__Instance)->NetworkingConfigurationValue = value;\r\n            }\r\n        }\r\n\r\n        public global::LibWsl.MemoryReclaimMode MemoryReclaimModeValue\r\n        {\r\n            get\r\n            {\r\n                return ((__Internal*)__Instance)->MemoryReclaimModeValue;\r\n            }\r\n\r\n            set\r\n            {\r\n                ((__Internal*)__Instance)->MemoryReclaimModeValue = value;\r\n            }\r\n        }\r\n    }\r\n\r\n    public unsafe partial class WslCoreConfigInterface\r\n    {\r\n        public partial struct __Internal\r\n        {\r\n            [SuppressUnmanagedCodeSecurity, DllImport(@\"..\\libwsl.dll\", EntryPoint = \"GetWslConfigFilePath\", CallingConvention = __CallingConvention.StdCall)]\r\n            internal static extern char* GetWslConfigFilePath();\r\n\r\n            [SuppressUnmanagedCodeSecurity, DllImport(@\"..\\libwsl.dll\", EntryPoint = \"CreateWslConfig\", CallingConvention = __CallingConvention.StdCall)]\r\n            internal static extern __IntPtr CreateWslConfig(char* wslConfigFilePath);\r\n\r\n            [SuppressUnmanagedCodeSecurity, DllImport(@\"..\\libwsl.dll\", EntryPoint = \"FreeWslConfig\", CallingConvention = __CallingConvention.StdCall)]\r\n            internal static extern void FreeWslConfig(__IntPtr wslConfig);\r\n\r\n            [SuppressUnmanagedCodeSecurity, DllImport(@\"..\\libwsl.dll\", EntryPoint = \"GetWslConfigSetting\", CallingConvention = __CallingConvention.StdCall)]\r\n            internal static extern global::LibWsl.WslConfigSetting.__Internal GetWslConfigSetting(__IntPtr wslConfig, global::LibWsl.WslConfigEntry ConfigEntry);\r\n\r\n            [SuppressUnmanagedCodeSecurity, DllImport(@\"..\\libwsl.dll\", EntryPoint = \"SetWslConfigSetting\", CallingConvention = __CallingConvention.StdCall)]\r\n            internal static extern uint SetWslConfigSetting(__IntPtr wslConfig, global::LibWsl.WslConfigSetting.__Internal setting);\r\n        }\r\n\r\n        public static string GetWslConfigFilePath()\r\n        {\r\n            var ___ret = __Internal.GetWslConfigFilePath();\r\n            return Marshal.PtrToStringAuto((IntPtr)___ret);\r\n        }\r\n\r\n        public static global::LibWsl.WslConfig CreateWslConfig(string wslConfigFilePath)\r\n        {\r\n            var __arg0 = (char*)Marshal.StringToHGlobalAuto(wslConfigFilePath);\r\n            var ___ret = __Internal.CreateWslConfig(__arg0);\r\n            var __result0 = global::LibWsl.WslConfig.__GetOrCreateInstance(___ret, false);\r\n            Marshal.FreeHGlobal((IntPtr)__arg0);\r\n            return __result0;\r\n        }\r\n\r\n        public static void FreeWslConfig(global::LibWsl.WslConfig wslConfig)\r\n        {\r\n            var __arg0 = wslConfig is null ? __IntPtr.Zero : wslConfig.__Instance;\r\n            __Internal.FreeWslConfig(__arg0);\r\n        }\r\n\r\n        public static global::LibWsl.WslConfigSetting GetWslConfigSetting(global::LibWsl.WslConfig wslConfig, global::LibWsl.WslConfigEntry ConfigEntry)\r\n        {\r\n            var __arg0 = wslConfig is null ? __IntPtr.Zero : wslConfig.__Instance;\r\n            var ___ret = __Internal.GetWslConfigSetting(__arg0, ConfigEntry);\r\n            return global::LibWsl.WslConfigSetting.__CreateInstance(___ret);\r\n        }\r\n\r\n        public static uint SetWslConfigSetting(global::LibWsl.WslConfig wslConfig, global::LibWsl.WslConfigSetting setting)\r\n        {\r\n            var __arg0 = wslConfig is null ? __IntPtr.Zero : wslConfig.__Instance;\r\n            if (ReferenceEquals(setting, null))\r\n                throw new global::System.ArgumentNullException(\"setting\", \"Cannot be null because it is passed by value.\");\r\n            var __arg1 = setting.__Instance;\r\n            var ___ret = __Internal.SetWslConfigSetting(__arg0, *(global::LibWsl.WslConfigSetting.__Internal*) __arg1);\r\n            return ___ret;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Services/ActivationService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing WslSettings.Activation;\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.Services;\r\n\r\npublic class ActivationService : IActivationService\r\n{\r\n    private readonly ActivationHandler<LaunchActivatedEventArgs> _defaultHandler;\r\n    private readonly IEnumerable<IActivationHandler> _activationHandlers;\r\n\r\n    public ActivationService(ActivationHandler<LaunchActivatedEventArgs> defaultHandler, IEnumerable<IActivationHandler> activationHandlers)\r\n    {\r\n        _defaultHandler = defaultHandler;\r\n        _activationHandlers = activationHandlers;\r\n    }\r\n\r\n    public async Task ActivateAsync(object activationArgs)\r\n    {\r\n        // Execute tasks before activation.\r\n        await InitializeAsync();\r\n\r\n        // Handle activation via ActivationHandlers.\r\n        await HandleActivationAsync(activationArgs);\r\n\r\n        // Execute tasks after activation.\r\n        await StartupAsync();\r\n    }\r\n\r\n    private async Task HandleActivationAsync(object activationArgs)\r\n    {\r\n        var activationHandler = _activationHandlers.FirstOrDefault(h => h.CanHandle(activationArgs));\r\n\r\n        if (activationHandler != null)\r\n        {\r\n            await activationHandler.HandleAsync(activationArgs);\r\n        }\r\n\r\n        if (_defaultHandler.CanHandle(activationArgs))\r\n        {\r\n            await _defaultHandler.HandleAsync(activationArgs);\r\n        }\r\n    }\r\n\r\n    private async Task InitializeAsync()\r\n    {\r\n        await Task.CompletedTask;\r\n    }\r\n\r\n    private async Task StartupAsync()\r\n    {\r\n        await Task.CompletedTask;\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Services/NavigationService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Navigation;\r\nusing System.Diagnostics.CodeAnalysis;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.Contracts.ViewModels;\r\n\r\nnamespace WslSettings.Services;\r\n\r\n// For more information on navigation between pages see\r\n// https://github.com/microsoft/TemplateStudio/blob/main/docs/WinUI/navigation.md\r\npublic class NavigationService : INavigationService\r\n{\r\n    private readonly IPageService _pageService;\r\n    private object? _lastParameterUsed;\r\n    private Frame? _frame;\r\n\r\n    public event NavigatedEventHandler? Navigated;\r\n\r\n    public Frame? Frame\r\n    {\r\n        get\r\n        {\r\n            return _frame;\r\n        }\r\n\r\n        set\r\n        {\r\n            UnregisterFrameEvents();\r\n            _frame = value;\r\n            RegisterFrameEvents();\r\n        }\r\n    }\r\n\r\n    [MemberNotNullWhen(true, nameof(Frame), nameof(_frame))]\r\n    public bool CanGoBack => Frame != null && Frame.CanGoBack;\r\n\r\n    public NavigationService(IPageService pageService)\r\n    {\r\n        _pageService = pageService;\r\n    }\r\n\r\n    private void RegisterFrameEvents()\r\n    {\r\n        if (_frame != null)\r\n        {\r\n            _frame.Navigated += OnNavigated;\r\n        }\r\n    }\r\n\r\n    private void UnregisterFrameEvents()\r\n    {\r\n        if (_frame != null)\r\n        {\r\n            _frame.Navigated -= OnNavigated;\r\n        }\r\n    }\r\n\r\n    public bool GoBack()\r\n    {\r\n        if (CanGoBack)\r\n        {\r\n            var vmBeforeNavigation = _frame.GetPageViewModel();\r\n            _frame.GoBack();\r\n            if (vmBeforeNavigation is INavigationAware navigationAware)\r\n            {\r\n                navigationAware.OnNavigatedFrom();\r\n            }\r\n\r\n            return true;\r\n        }\r\n\r\n        return false;\r\n    }\r\n\r\n    public bool NavigateTo(string pageKey, object? parameter = null, bool clearNavigation = false)\r\n    {\r\n        var pageType = _pageService.GetPageType(pageKey);\r\n\r\n        if (_frame != null && (_frame.Content?.GetType() != pageType || (parameter != null && !parameter.Equals(_lastParameterUsed))))\r\n        {\r\n            _frame.Tag = clearNavigation;\r\n            var vmBeforeNavigation = _frame.GetPageViewModel();\r\n            var navigated = _frame.Navigate(pageType, parameter);\r\n            if (navigated)\r\n            {\r\n                _lastParameterUsed = parameter;\r\n                if (vmBeforeNavigation is INavigationAware navigationAware)\r\n                {\r\n                    navigationAware.OnNavigatedFrom();\r\n                }\r\n            }\r\n\r\n            return navigated;\r\n        }\r\n\r\n        return false;\r\n    }\r\n\r\n    private void OnNavigated(object sender, NavigationEventArgs e)\r\n    {\r\n        if (sender is Frame frame)\r\n        {\r\n            var clearNavigation = (bool)frame.Tag;\r\n            if (clearNavigation)\r\n            {\r\n                frame.BackStack.Clear();\r\n            }\r\n\r\n            if (frame.GetPageViewModel() is INavigationAware navigationAware)\r\n            {\r\n                navigationAware.OnNavigatedTo(e.Parameter);\r\n            }\r\n\r\n            Navigated?.Invoke(sender, e);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Services/NavigationViewService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing System.Diagnostics.CodeAnalysis;\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.Services;\r\n\r\npublic class NavigationViewService : INavigationViewService\r\n{\r\n    private readonly INavigationService _navigationService;\r\n\r\n    private readonly IPageService _pageService;\r\n\r\n    private NavigationView? _navigationView;\r\n\r\n    public IList<object>? MenuItems => _navigationView?.MenuItems;\r\n\r\n    public object? SettingsItem => _navigationView?.SettingsItem;\r\n\r\n    public NavigationViewService(INavigationService navigationService, IPageService pageService)\r\n    {\r\n        _navigationService = navigationService;\r\n        _pageService = pageService;\r\n    }\r\n\r\n    [MemberNotNull(nameof(_navigationView))]\r\n    public void Initialize(NavigationView navigationView)\r\n    {\r\n        _navigationView = navigationView;\r\n        _navigationView.BackRequested += OnBackRequested;\r\n        _navigationView.ItemInvoked += OnItemInvoked;\r\n    }\r\n\r\n    public void UnregisterEvents()\r\n    {\r\n        if (_navigationView != null)\r\n        {\r\n            _navigationView.BackRequested -= OnBackRequested;\r\n            _navigationView.ItemInvoked -= OnItemInvoked;\r\n        }\r\n    }\r\n\r\n    public NavigationViewItem? GetSelectedItem(Type pageType)\r\n    {\r\n        if (_navigationView != null)\r\n        {\r\n            return GetSelectedItem(_navigationView.MenuItems, pageType) ?? GetSelectedItem(_navigationView.FooterMenuItems, pageType);\r\n        }\r\n\r\n        return null;\r\n    }\r\n\r\n    private void OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args) => _navigationService.GoBack();\r\n\r\n    private void OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)\r\n    {\r\n        if (args.IsSettingsInvoked)\r\n        {\r\n            // Navigate to the settings page.\r\n        }\r\n        else\r\n        {\r\n            var selectedItem = args.InvokedItemContainer as NavigationViewItem;\r\n\r\n            if (selectedItem?.GetValue(NavigationHelper.NavigateToProperty) is string pageKey)\r\n            {\r\n                _navigationService.NavigateTo(pageKey);\r\n            }\r\n        }\r\n    }\r\n\r\n    private NavigationViewItem? GetSelectedItem(IEnumerable<object> menuItems, Type pageType)\r\n    {\r\n        foreach (var item in menuItems.OfType<NavigationViewItem>())\r\n        {\r\n            if (IsMenuItemForPageType(item, pageType))\r\n            {\r\n                return item;\r\n            }\r\n\r\n            var selectedChild = GetSelectedItem(item.MenuItems, pageType);\r\n            if (selectedChild != null)\r\n            {\r\n                return selectedChild;\r\n            }\r\n        }\r\n\r\n        return null;\r\n    }\r\n\r\n    private bool IsMenuItemForPageType(NavigationViewItem menuItem, Type sourcePageType)\r\n    {\r\n        if (menuItem.GetValue(NavigationHelper.NavigateToProperty) is string pageKey)\r\n        {\r\n            return _pageService.GetPageType(pageKey) == sourcePageType;\r\n        }\r\n\r\n        return false;\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Services/PageService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.ViewModels.OOBE;\r\nusing WslSettings.ViewModels.Settings;\r\nusing WslSettings.Views.OOBE;\r\nusing WslSettings.Views.Settings;\r\n\r\nnamespace WslSettings.Services;\r\n\r\npublic class PageService : IPageService\r\n{\r\n    private readonly Dictionary<string, Type> _pages = new();\r\n\r\n    public PageService()\r\n    {\r\n        Configure<MemAndProcViewModel, MemAndProcPage>();\r\n        Configure<FileSystemViewModel, FileSystemPage>();\r\n        Configure<NetworkingViewModel, NetworkingPage>();\r\n        Configure<OptionalFeaturesViewModel, OptionalFeaturesPage>();\r\n        Configure<DeveloperViewModel, DeveloperPage>();\r\n        Configure<AboutViewModel, AboutPage>();\r\n\r\n        Configure<GeneralViewModel, GeneralPage>();\r\n        Configure<WorkingAcrossFileSystemsViewModel, WorkingAcrossFileSystemsPage>();\r\n        Configure<VSCodeIntegrationViewModel, VSCodeIntegrationPage>();\r\n        Configure<VSIntegrationViewModel, VSIntegrationPage>();\r\n        Configure<GUIAppsViewModel, GUIAppsPage>();\r\n        Configure<GPUAccelerationViewModel, GPUAccelerationPage>();\r\n        Configure<DockerDesktopIntegrationViewModel, DockerDesktopIntegrationPage>();\r\n        Configure<NetworkingIntegrationViewModel, NetworkingIntegrationPage>();\r\n        Configure<DistroManagementViewModel, DistroManagementPage>();\r\n    }\r\n\r\n    public Type GetPageType(string key)\r\n    {\r\n        Type? pageType;\r\n        lock (_pages)\r\n        {\r\n            if (!_pages.TryGetValue(key, out pageType))\r\n            {\r\n                throw new ArgumentException($\"Page not found: {key}. Did you forget to call PageService.Configure?\");\r\n            }\r\n        }\r\n\r\n        return pageType;\r\n    }\r\n\r\n    private void Configure<VM, V>()\r\n        where VM : ObservableObject\r\n        where V : Page\r\n    {\r\n        lock (_pages)\r\n        {\r\n            var key = typeof(VM).FullName!;\r\n            if (_pages.ContainsKey(key))\r\n            {\r\n                throw new ArgumentException($\"The key {key} is already configured in PageService\");\r\n            }\r\n\r\n            var type = typeof(V);\r\n            if (_pages.ContainsValue(type))\r\n            {\r\n                throw new ArgumentException($\"This type is already configured with key {_pages.First(p => p.Value == type).Key}\");\r\n            }\r\n\r\n            _pages.Add(key, type);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Services/WindowService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.Services;\r\n\r\npublic class WindowService : IWindowService\r\n{\r\n    public WindowService()\r\n    {\r\n    }\r\n\r\n    public WindowEx CreateOrGetWindow(IWindowService.WindowId windowId)\r\n    {\r\n        WindowEx window;\r\n        switch (windowId)\r\n        {\r\n            case IWindowService.WindowId.MainWindow:\r\n                if (App.MainWindow == null)\r\n                {\r\n                    App.MainWindow = new MainWindow();\r\n                }\r\n\r\n                if (App.MainWindow!.Content == null)\r\n                {\r\n                    App.MainWindow.Content = App.GetService<Views.Settings.ShellPage>();\r\n                }\r\n\r\n                window = App.MainWindow;\r\n                break;\r\n            case IWindowService.WindowId.OOBEWindow:\r\n                if (App.OOBEWindow == null)\r\n                {\r\n                    App.OOBEWindow = new OOBEWindow();\r\n                }\r\n\r\n                if (App.OOBEWindow.Content == null)\r\n                {\r\n                    App.OOBEWindow.Content = App.GetService<Views.OOBE.ShellPage>();\r\n                }\r\n\r\n                window = App.OOBEWindow;\r\n                break;\r\n            default:\r\n                throw new ArgumentException(\"Invalid ActivationWindowId\");\r\n        }\r\n\r\n        return window;\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Services/WslConfigService.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing WslSettings.Contracts.Services;\r\nusing static WslSettings.Contracts.Services.IWslConfigService;\r\n\r\nnamespace WslSettings.Services;\r\n\r\npublic class WslConfigService : IWslConfigService\r\n{\r\n    private WslConfig? _wslConfig { get; set; }\r\n    private WslConfig? _wslConfigDefaults { get; init; }\r\n    private readonly object? _wslCoreConfigInterfaceLockObj = null;\r\n    private FileSystemWatcher? _wslConfigFileSystemWatcher = null;\r\n\r\n    public WslConfigService()\r\n    {\r\n        string filePath = WslCoreConfigInterface.GetWslConfigFilePath();\r\n        _wslConfig = WslCoreConfigInterface.CreateWslConfig(filePath);\r\n        _wslConfigDefaults = WslCoreConfigInterface.CreateWslConfig(null);\r\n        _wslCoreConfigInterfaceLockObj = new object();\r\n        _wslConfigFileSystemWatcher = new FileSystemWatcher(Path.GetDirectoryName(filePath) ?? string.Empty, Path.GetFileName(filePath));\r\n\r\n        _wslConfigFileSystemWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;\r\n\r\n        _wslConfigFileSystemWatcher.Changed += OnWslConfigFileChanged;\r\n        _wslConfigFileSystemWatcher.Deleted += OnWslConfigFileChanged;\r\n        _wslConfigFileSystemWatcher.Renamed += OnWslConfigFileChanged;\r\n\r\n        _wslConfigFileSystemWatcher!.EnableRaisingEvents = true;\r\n    }\r\n\r\n    ~WslConfigService()\r\n    {\r\n        WslCoreConfigInterface.FreeWslConfig(_wslConfig);\r\n        WslCoreConfigInterface.FreeWslConfig(_wslConfigDefaults);\r\n    }\r\n\r\n    public IWslConfigSetting GetWslConfigSetting(WslConfigEntry wslConfigEntry, bool defaultSetting)\r\n    {\r\n        WslConfigSettingManaged? wslConfigSetting = null;\r\n        lock (_wslCoreConfigInterfaceLockObj!)\r\n        {\r\n            wslConfigSetting = new WslConfigSettingManaged(WslCoreConfigInterface.GetWslConfigSetting(defaultSetting ? _wslConfigDefaults : _wslConfig, wslConfigEntry));\r\n        }\r\n\r\n        return wslConfigSetting;\r\n    }\r\n\r\n    public uint SetWslConfigSetting(IWslConfigSetting wslConfigSetting)\r\n    {\r\n        var wslConfigSettingsManaged = wslConfigSetting as WslConfigSettingManaged;\r\n        if (wslConfigSettingsManaged == null)\r\n        {\r\n            throw new ArgumentNullException(\"wslConfigSetting\");\r\n        }\r\n\r\n        uint result = 0;\r\n        lock (_wslCoreConfigInterfaceLockObj!)\r\n        {\r\n            _wslConfigFileSystemWatcher!.EnableRaisingEvents = false;\r\n            result = WslCoreConfigInterface.SetWslConfigSetting(_wslConfig, wslConfigSettingsManaged.ConfigSetting);\r\n            _wslConfigFileSystemWatcher!.EnableRaisingEvents = true;\r\n        }\r\n\r\n        return result;\r\n    }\r\n\r\n    private WslConfigChangedEventHandler? _onWslConfigChangedHandler = null;\r\n    public event WslConfigChangedEventHandler WslConfigChanged\r\n    {\r\n        add\r\n        {\r\n            _onWslConfigChangedHandler += value;\r\n        }\r\n        remove\r\n        {\r\n            _onWslConfigChangedHandler -= value;\r\n        }\r\n    }\r\n\r\n    private void OnWslConfigFileChanged(object sender, FileSystemEventArgs e)\r\n    {\r\n        lock (_wslCoreConfigInterfaceLockObj!)\r\n        {\r\n            _wslConfigFileSystemWatcher!.EnableRaisingEvents = false;\r\n            WslCoreConfigInterface.FreeWslConfig(_wslConfig);\r\n            _wslConfig = WslCoreConfigInterface.CreateWslConfig(WslCoreConfigInterface.GetWslConfigFilePath());\r\n            _wslConfigFileSystemWatcher!.EnableRaisingEvents = true;\r\n        }\r\n\r\n        _onWslConfigChangedHandler?.Invoke();\r\n    }\r\n}\r\n\r\npublic partial class WslConfigSettingManaged : IWslConfigSetting\r\n{\r\n    public WslConfigSettingManaged(WslConfigSetting wslConfigSetting)\r\n    {\r\n        ConfigSetting = wslConfigSetting;\r\n    }\r\n\r\n    ~WslConfigSettingManaged()\r\n    {\r\n        ConfigSetting.Dispose();\r\n    }\r\n\r\n    public WslConfigSetting ConfigSetting { get; init; }\r\n    public WslConfigEntry ConfigEntry { get { return ConfigSetting.ConfigEntry; } }\r\n    public string StringValue { get { return ConfigSetting.StringValue; } }\r\n    public ulong UInt64Value { get { return ConfigSetting.UInt64Value; } }\r\n    public int Int32Value { get { return ConfigSetting.Int32Value; } }\r\n    public bool BoolValue { get { return ConfigSetting.BoolValue; } }\r\n    public NetworkingConfiguration NetworkingConfigurationValue { get { return ConfigSetting.NetworkingConfigurationValue; } }\r\n    public MemoryReclaimMode MemoryReclaimModeValue { get { return ConfigSetting.MemoryReclaimModeValue; } }\r\n\r\n#nullable enable\r\n    public uint SetValue(object? value)\r\n    {\r\n        if (value == null)\r\n        {\r\n            throw new ArgumentNullException(\"value\");\r\n        }\r\n\r\n        if (\"\".GetType() == value.GetType())\r\n        {\r\n            ConfigSetting.StringValue = (string)value;\r\n        }\r\n        else if (ConfigSetting.UInt64Value.GetType() == value.GetType())\r\n        {\r\n            ConfigSetting.UInt64Value = (ulong)value;\r\n        }\r\n        else if (ConfigSetting.Int32Value.GetType() == value.GetType())\r\n        {\r\n            ConfigSetting.Int32Value = (int)value;\r\n        }\r\n        else if (ConfigSetting.BoolValue.GetType() == value.GetType())\r\n        {\r\n            ConfigSetting.BoolValue = (bool)value;\r\n        }\r\n        else if (ConfigSetting.NetworkingConfigurationValue.GetType() == value.GetType())\r\n        {\r\n            ConfigSetting.NetworkingConfigurationValue = (NetworkingConfiguration)value;\r\n        }\r\n        else if (ConfigSetting.MemoryReclaimModeValue.GetType() == value.GetType())\r\n        {\r\n            ConfigSetting.MemoryReclaimModeValue = (MemoryReclaimMode)value;\r\n        }\r\n        else\r\n        {\r\n            throw new InvalidDataException();\r\n        }\r\n\r\n        return App.GetService<IWslConfigService>().SetWslConfigSetting(this);\r\n    }\r\n\r\n    public override bool Equals(object? value)\r\n    {\r\n        if (value == null)\r\n        {\r\n            throw new ArgumentNullException(\"value\");\r\n        }\r\n\r\n        // Special handling for byte values. Compare using MB since in the UI the user works with MB.\r\n        if (ConfigSetting.ConfigEntry == WslConfigEntry.MemorySizeBytes ||\r\n            ConfigSetting.ConfigEntry == WslConfigEntry.SwapSizeBytes ||\r\n            ConfigSetting.ConfigEntry == WslConfigEntry.VhdSizeBytes)\r\n        {\r\n            return ((ulong)value / Constants.MB) == (UInt64Value / Constants.MB);\r\n        }\r\n\r\n        if (\"\".GetType() == value.GetType())\r\n        {\r\n            return ConfigSetting.StringValue == (string)value;\r\n        }\r\n        else if (ConfigSetting.UInt64Value.GetType() == value.GetType())\r\n        {\r\n            return ConfigSetting.UInt64Value == (ulong)value;\r\n        }\r\n        else if (ConfigSetting.Int32Value.GetType() == value.GetType())\r\n        {\r\n            return ConfigSetting.Int32Value == (int)value;\r\n        }\r\n        else if (ConfigSetting.BoolValue.GetType() == value.GetType())\r\n        {\r\n            return ConfigSetting.BoolValue == (bool)value;\r\n        }\r\n        else if (ConfigSetting.NetworkingConfigurationValue.GetType() == value.GetType())\r\n        {\r\n            return ConfigSetting.NetworkingConfigurationValue == (NetworkingConfiguration)value;\r\n        }\r\n        else if (ConfigSetting.MemoryReclaimModeValue.GetType() == value.GetType())\r\n        {\r\n            return ConfigSetting.MemoryReclaimModeValue == (MemoryReclaimMode)value;\r\n        }\r\n        else\r\n        {\r\n            throw new InvalidDataException();\r\n        }\r\n    }\r\n\r\n    public override int GetHashCode()\r\n    {\r\n        return base.GetHashCode();\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Styles/Button.xaml",
    "content": "<ResourceDictionary\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\r\n\r\n    <Style x:Key=\"TextButtonStyle\" TargetType=\"ButtonBase\">\r\n        <Setter Property=\"Background\" Value=\"{ThemeResource HyperlinkButtonBackground}\" />\r\n        <Setter Property=\"Foreground\" Value=\"{ThemeResource HyperlinkButtonForeground}\" />\r\n        <Setter Property=\"MinWidth\" Value=\"0\" />\r\n        <Setter Property=\"MinHeight\" Value=\"0\" />\r\n        <Setter Property=\"Margin\" Value=\"0\" />\r\n        <Setter Property=\"UseSystemFocusVisuals\" Value=\"{StaticResource UseSystemFocusVisuals}\" />\r\n        <Setter Property=\"Template\">\r\n            <Setter.Value>\r\n                <ControlTemplate TargetType=\"ButtonBase\">\r\n                    <Grid\r\n                            Margin=\"{TemplateBinding Padding}\"\r\n                            Background=\"{TemplateBinding Background}\"\r\n                            CornerRadius=\"4\">\r\n                        <ContentPresenter\r\n                                x:Name=\"Text\"\r\n                                HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\r\n                                VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"\r\n                                Content=\"{TemplateBinding Content}\"\r\n                                FontWeight=\"SemiBold\" />\r\n\r\n                        <VisualStateManager.VisualStateGroups>\r\n                            <VisualStateGroup x:Name=\"CommonStates\">\r\n                                <VisualState x:Name=\"Normal\" />\r\n                                <VisualState x:Name=\"PointerOver\">\r\n                                    <Storyboard>\r\n                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Text\" Storyboard.TargetProperty=\"Foreground\">\r\n                                            <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{ThemeResource HyperlinkButtonForegroundPointerOver}\" />\r\n                                        </ObjectAnimationUsingKeyFrames>\r\n                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Text\" Storyboard.TargetProperty=\"Background\">\r\n                                            <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{ThemeResource HyperlinkButtonBackgroundPointerOver}\" />\r\n                                        </ObjectAnimationUsingKeyFrames>\r\n                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Text\" Storyboard.TargetProperty=\"BorderBrush\">\r\n                                            <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{ThemeResource HyperlinkButtonBorderBrushPointerOver}\" />\r\n                                        </ObjectAnimationUsingKeyFrames>\r\n                                    </Storyboard>\r\n                                </VisualState>\r\n\r\n                                <VisualState x:Name=\"Pressed\">\r\n                                    <Storyboard>\r\n                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Text\" Storyboard.TargetProperty=\"Foreground\">\r\n                                            <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{ThemeResource HyperlinkButtonForegroundPressed}\" />\r\n                                        </ObjectAnimationUsingKeyFrames>\r\n                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Text\" Storyboard.TargetProperty=\"Background\">\r\n                                            <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{ThemeResource HyperlinkButtonBackgroundPressed}\" />\r\n                                        </ObjectAnimationUsingKeyFrames>\r\n                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Text\" Storyboard.TargetProperty=\"BorderBrush\">\r\n                                            <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{ThemeResource HyperlinkButtonBorderBrushPressed}\" />\r\n                                        </ObjectAnimationUsingKeyFrames>\r\n                                    </Storyboard>\r\n                                </VisualState>\r\n\r\n                                <VisualState x:Name=\"Disabled\">\r\n                                    <Storyboard>\r\n                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Text\" Storyboard.TargetProperty=\"Foreground\">\r\n                                            <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{ThemeResource HyperlinkButtonForegroundDisabled}\" />\r\n                                        </ObjectAnimationUsingKeyFrames>\r\n                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Text\" Storyboard.TargetProperty=\"Background\">\r\n                                            <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{ThemeResource HyperlinkButtonBackgroundDisabled}\" />\r\n                                        </ObjectAnimationUsingKeyFrames>\r\n                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName=\"Text\" Storyboard.TargetProperty=\"BorderBrush\">\r\n                                            <DiscreteObjectKeyFrame KeyTime=\"0\" Value=\"{ThemeResource HyperlinkButtonBorderBrushDisabled}\" />\r\n                                        </ObjectAnimationUsingKeyFrames>\r\n                                    </Storyboard>\r\n                                </VisualState>\r\n                            </VisualStateGroup>\r\n                        </VisualStateManager.VisualStateGroups>\r\n                    </Grid>\r\n\r\n                </ControlTemplate>\r\n            </Setter.Value>\r\n        </Setter>\r\n    </Style>\r\n\r\n</ResourceDictionary>"
  },
  {
    "path": "src/windows/wslsettings/Styles/CommonStyles.xaml",
    "content": "﻿<ResourceDictionary\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\r\n\r\n    <x:Double x:Key=\"BoxMinWidth\">120</x:Double>\r\n    <x:Double x:Key=\"BoxMaxWidth\">240</x:Double>\r\n    <x:Double x:Key=\"ButtonMinWidth\">120</x:Double>\r\n    <x:Double x:Key=\"BoxPathMaxWidth\">480</x:Double>\r\n    <x:Double x:Key=\"TextMaxWidth\">240</x:Double>\r\n    <x:Double x:Key=\"TextPathMaxWidth\">480</x:Double>\r\n\r\n    <Style x:Key=\"PageTitleStyle\" TargetType=\"TextBlock\">\r\n        <Setter Property=\"VerticalAlignment\" Value=\"Center\" />\r\n        <Setter Property=\"FontWeight\" Value=\"SemiLight\" />\r\n        <Setter Property=\"FontSize\" Value=\"{StaticResource LargeFontSize}\" />\r\n        <Setter Property=\"TextTrimming\" Value=\"CharacterEllipsis\" />\r\n        <Setter Property=\"TextWrapping\" Value=\"NoWrap\" />\r\n    </Style>\r\n\r\n    <Style x:Key=\"BodyTextStyle\" TargetType=\"TextBlock\">\r\n        <Setter Property=\"FontWeight\" Value=\"Normal\" />\r\n        <Setter Property=\"FontSize\" Value=\"{StaticResource MediumFontSize}\" />\r\n        <Setter Property=\"TextTrimming\" Value=\"CharacterEllipsis\" />\r\n        <Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n    </Style>\r\n\r\n    <Style x:Key=\"BaseTextBlockStyle\" TargetType=\"TextBlock\">\r\n        <Setter Property=\"FontSize\" Value=\"14\" />\r\n        <Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n        <Setter Property=\"TextTrimming\" Value=\"CharacterEllipsis\" />\r\n    </Style>\r\n\r\n    <Style x:Key=\"SmallTextBlockStyle\" TargetType=\"TextBlock\">\r\n        <Setter Property=\"FontSize\" Value=\"12\" />\r\n        <Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n        <Setter Property=\"TextTrimming\" Value=\"CharacterEllipsis\" />\r\n    </Style>\r\n\r\n    <!--  Style (inc. the correct spacing) of a section header  -->\r\n    <Style x:Key=\"PageHeaderTextBlockStyle\"\r\n               BasedOn=\"{StaticResource BodyStrongTextBlockStyle}\"\r\n               TargetType=\"TextBlock\">\r\n        <Style.Setters>\r\n            <Setter Property=\"FontSize\" Value=\"20\" />\r\n            <Setter Property=\"FontWeight\" Value=\"SemiBold\" />\r\n            <Setter Property=\"Margin\" Value=\"1,28,0,4\" />\r\n            <Setter Property=\"AutomationProperties.HeadingLevel\" Value=\"Level1\" />\r\n        </Style.Setters>\r\n    </Style>\r\n\r\n    <Style x:Key=\"ButtonSettingStyle\"\r\n           BasedOn=\"{StaticResource DefaultButtonStyle}\"\r\n           TargetType=\"Button\">\r\n        <Setter Property=\"MinWidth\" Value=\"{StaticResource ButtonMinWidth}\" />\r\n    </Style>\r\n\r\n    <Style x:Key=\"TextBlockFilePathStyle\"\r\n           TargetType=\"TextBlock\">\r\n        <Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n        <Setter Property=\"MaxWidth\" Value=\"{StaticResource TextPathMaxWidth}\" />\r\n    </Style>\r\n\r\n    <Style x:Key=\"TextBlockSettingStyle\"\r\n           TargetType=\"TextBlock\">\r\n        <Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n        <Setter Property=\"MaxWidth\" Value=\"{StaticResource TextMaxWidth}\" />\r\n    </Style>\r\n\r\n    <Style x:Key=\"TextBoxFilePathStyle\"\r\n           BasedOn=\"{StaticResource DefaultTextBoxStyle}\"\r\n           TargetType=\"TextBox\">\r\n        <Setter Property=\"MinWidth\" Value=\"{StaticResource BoxMinWidth}\" />\r\n        <Setter Property=\"MaxWidth\" Value=\"{StaticResource BoxPathMaxWidth}\" />\r\n        <Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\r\n        <Setter Property=\"TextAlignment\" Value=\"Left\" />\r\n        <Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n        <Setter Property=\"IsSpellCheckEnabled\" Value=\"False\" />\r\n    </Style>\r\n\r\n    <Style x:Key=\"TextBoxSettingStyle\"\r\n           BasedOn=\"{StaticResource DefaultTextBoxStyle}\"\r\n           TargetType=\"TextBox\">\r\n        <Setter Property=\"MinWidth\" Value=\"{StaticResource BoxMinWidth}\" />\r\n        <Setter Property=\"MaxWidth\" Value=\"{StaticResource BoxMaxWidth}\" />\r\n        <Setter Property=\"HorizontalAlignment\" Value=\"Left\" />\r\n        <Setter Property=\"TextAlignment\" Value=\"Center\" />\r\n        <Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n        <Setter Property=\"IsSpellCheckEnabled\" Value=\"False\" />\r\n    </Style>\r\n\r\n    <Style x:Key=\"ComboBoxSettingStyle\"\r\n           BasedOn=\"{StaticResource DefaultComboBoxStyle}\"\r\n           TargetType=\"ComboBox\">\r\n        <Setter Property=\"MinWidth\" Value=\"{StaticResource BoxMinWidth}\" />\r\n    </Style>\r\n\r\n    <Style x:Key=\"OobeSubtitleStyle\" TargetType=\"TextBlock\">\r\n        <Setter Property=\"Margin\" Value=\"0,0,0,-12\" />\r\n        <Setter Property=\"Foreground\" Value=\"{ThemeResource DefaultTextForegroundThemeBrush}\" />\r\n        <Setter Property=\"AutomationProperties.HeadingLevel\" Value=\"Level3\" />\r\n        <Setter Property=\"FontFamily\" Value=\"XamlAutoFontFamily\" />\r\n        <Setter Property=\"FontSize\" Value=\"{StaticResource BodyTextBlockFontSize}\" />\r\n        <Setter Property=\"FontWeight\" Value=\"SemiBold\" />\r\n        <Setter Property=\"TextTrimming\" Value=\"CharacterEllipsis\" />\r\n        <Setter Property=\"TextWrapping\" Value=\"Wrap\" />\r\n        <Setter Property=\"LineStackingStrategy\" Value=\"MaxHeight\" />\r\n        <Setter Property=\"TextLineBounds\" Value=\"Full\" />\r\n    </Style>\r\n\r\n</ResourceDictionary>"
  },
  {
    "path": "src/windows/wslsettings/Styles/FontSizes.xaml",
    "content": "﻿<ResourceDictionary\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\r\n\r\n    <x:Double x:Key=\"LargeFontSize\">24</x:Double>\r\n\r\n    <x:Double x:Key=\"MediumFontSize\">16</x:Double>\r\n\r\n</ResourceDictionary>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Styles/Thickness.xaml",
    "content": "﻿<ResourceDictionary\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\r\n\r\n    <Thickness x:Key=\"InputControlSpacingMargin\">4,0</Thickness>\r\n    \r\n    <Thickness x:Key=\"LargeTopMargin\">0,36,0,0</Thickness>\r\n    <Thickness x:Key=\"LargeTopBottomMargin\">0,36,0,36</Thickness>\r\n\r\n    <Thickness x:Key=\"MediumTopMargin\">0,24,0,0</Thickness>\r\n    <Thickness x:Key=\"MediumTopBottomMargin\">0,24,0,24</Thickness>\r\n    <Thickness x:Key=\"MediumLeftRightMargin\">24,0,24,0</Thickness>\r\n    <Thickness x:Key=\"MediumBottomMargin\">0,0,0,24</Thickness>\r\n\r\n    <Thickness x:Key=\"MediumSmallBottomMargin\">0,0,0,16</Thickness>\r\n\r\n    <Thickness x:Key=\"SmallLeftMargin\">12,0,0,0</Thickness>\r\n    <Thickness x:Key=\"SmallLeftRightMargin\">12,0,12,0</Thickness>\r\n    <Thickness x:Key=\"SmallTopMargin\">0,12,0,0</Thickness>\r\n    <Thickness x:Key=\"SmallRightMargin\">0,0,12,0</Thickness>\r\n    <Thickness x:Key=\"SmallTopBottomMargin\">0,12,0,12</Thickness>\r\n\r\n    <Thickness x:Key=\"XSmallLeftMargin\">8,0,0,0</Thickness>\r\n    <Thickness x:Key=\"XSmallTopMargin\">0,8,0,0</Thickness>\r\n    <Thickness x:Key=\"XSmallTopBottomMargin\">0,8,0,8</Thickness>\r\n    <Thickness x:Key=\"XSmallLeftTopRightBottomMargin\">8,8,8,8</Thickness>\r\n\r\n    <Thickness x:Key=\"XXSmallTopMargin\">0,4,0,0</Thickness>\r\n    <Thickness x:Key=\"XXSmallLeftTopRightBottomMargin\">4,4,4,4</Thickness>\r\n\r\n    <Thickness x:Key=\"HyperlinkButtonNegativeMargin\">-12,0,0,0</Thickness>\r\n\r\n    <Thickness x:Key=\"ContentPageMargin\">16,0,24,8</Thickness>\r\n\r\n    <Thickness x:Key=\"MenuBarContentMargin\">36,24,36,0</Thickness>\r\n\r\n    <Thickness x:Key=\"SettingsPageHyperlinkButtonMargin\">-12,4,0,0</Thickness>\r\n\r\n    <Thickness x:Key=\"SettingsCardMargin\">0,0,0,4</Thickness>\r\n    <x:Double x:Key=\"SettingsCardSpacing\">4</x:Double>\r\n    <Thickness x:Key=\"SettingsExpanderItemMargin\">-46,0,0,0</Thickness>\r\n</ResourceDictionary>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Usings.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nglobal using WinUIEx;\r\nglobal using LibWsl;\r\nglobal using WslSettings.Helpers;"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/DistroManagementViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\n\r\nnamespace WslSettings.ViewModels.OOBE;\r\n\r\npublic partial class DistroManagementViewModel : ObservableRecipient\r\n{\r\n    public DistroManagementViewModel()\r\n    {\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/DockerDesktopIntegrationViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\n\r\nnamespace WslSettings.ViewModels.OOBE;\r\n\r\npublic partial class DockerDesktopIntegrationViewModel : ObservableRecipient\r\n{\r\n    public DockerDesktopIntegrationViewModel()\r\n    {\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/GPUAccelerationViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\n\r\nnamespace WslSettings.ViewModels.OOBE;\r\n\r\npublic partial class GPUAccelerationViewModel : ObservableRecipient\r\n{\r\n    public GPUAccelerationViewModel()\r\n    {\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/GUIAppsViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\n\r\nnamespace WslSettings.ViewModels.OOBE;\r\n\r\npublic partial class GUIAppsViewModel : ObservableRecipient\r\n{\r\n    public GUIAppsViewModel()\r\n    {\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/GeneralViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\n\r\nnamespace WslSettings.ViewModels.OOBE;\r\n\r\npublic partial class GeneralViewModel : ObservableRecipient\r\n{\r\n    public GeneralViewModel()\r\n    {\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/NetworkingIntegrationViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\n\r\nnamespace WslSettings.ViewModels.OOBE;\r\n\r\npublic partial class NetworkingIntegrationViewModel : ObservableRecipient\r\n{\r\n    public NetworkingIntegrationViewModel()\r\n    {\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/VSCodeIntegrationViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\n\r\nnamespace WslSettings.ViewModels.OOBE;\r\n\r\npublic partial class VSCodeIntegrationViewModel : ObservableRecipient\r\n{\r\n    public VSCodeIntegrationViewModel()\r\n    {\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/VSIntegrationViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\n\nusing CommunityToolkit.Mvvm.ComponentModel;\n\nnamespace WslSettings.ViewModels.OOBE;\n\npublic partial class VSIntegrationViewModel : ObservableRecipient\n{\n    public VSIntegrationViewModel()\n    {\n    }\n}\n"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/WelcomeToUbuntuViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\n\r\nnamespace WslSettings.ViewModels.OOBE;\r\n\r\npublic partial class WelcomeToUbuntuViewModel : ObservableRecipient\r\n{\r\n    public WelcomeToUbuntuViewModel()\r\n    {\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/OOBE/WorkingAcrossFileSystemsViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\n\r\nnamespace WslSettings.ViewModels.OOBE;\r\n\r\npublic partial class WorkingAcrossFileSystemsViewModel : ObservableRecipient\r\n{\r\n    public WorkingAcrossFileSystemsViewModel()\r\n    {\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/Settings/AboutViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\nusing System.Reflection;\r\n\r\nnamespace WslSettings.ViewModels.Settings;\r\n\r\npublic partial class AboutViewModel : ObservableRecipient\r\n{\r\n    public AboutViewModel()\r\n    {\r\n    }\r\n\r\n    public string VersionDescription\r\n    {\r\n        get\r\n        {\r\n            Version version = Assembly.GetExecutingAssembly().GetName().Version!;\r\n            return $\"{version.Major}.{version.Minor}.{version.Build}\";\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/Settings/DeveloperViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.ViewModels.Settings;\r\n\r\npublic partial class DeveloperViewModel : WslConfigSettingViewModel\r\n{\r\n    private IWslConfigSetting? _debugConsole;\r\n    private IWslConfigSetting? _hWPerfCounters;\r\n    private IWslConfigSetting? _kernelPath;\r\n    private IWslConfigSetting? _kernelModulesPath;\r\n    private IWslConfigSetting? _systemDistroPath;\r\n\r\n    public DeveloperViewModel()\r\n    {\r\n        InitializeConfigSettings();\r\n    }\r\n\r\n    override protected void InitializeConfigSettings()\r\n    {\r\n        var wslConfigService = App.GetService<IWslConfigService>();\r\n        _debugConsole = wslConfigService.GetWslConfigSetting(WslConfigEntry.DebugConsoleEnabled);\r\n        _hWPerfCounters = wslConfigService.GetWslConfigSetting(WslConfigEntry.HardwarePerformanceCountersEnabled);\r\n        _kernelPath = wslConfigService.GetWslConfigSetting(WslConfigEntry.KernelPath);\r\n        _kernelModulesPath = wslConfigService.GetWslConfigSetting(WslConfigEntry.KernelModulesPath);\r\n        _systemDistroPath = wslConfigService.GetWslConfigSetting(WslConfigEntry.SystemDistroPath);\r\n    }\r\n\r\n    public bool IsOnDebugConsole\r\n    {\r\n        get { return _debugConsole!.BoolValue; }\r\n        set { Set(ref _debugConsole!, value); }\r\n    }\r\n\r\n    public bool IsOnHWPerfCounters\r\n    {\r\n        get { return _hWPerfCounters!.BoolValue; }\r\n        set { Set(ref _hWPerfCounters!, value); }\r\n    }\r\n\r\n    public string CustomKernelPath\r\n    {\r\n        get { return _kernelPath!.StringValue; }\r\n        set { Set(ref _kernelPath!, value); }\r\n    }\r\n\r\n    public string CustomKernelModulesPath\r\n    {\r\n        get { return _kernelModulesPath!.StringValue; }\r\n        set { Set(ref _kernelModulesPath!, value); }\r\n    }\r\n\r\n    public string CustomSystemDistroPath\r\n    {\r\n        get { return _systemDistroPath!.StringValue; }\r\n        set { Set(ref _systemDistroPath!, value); }\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/Settings/FileSystemViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\nusing CommunityToolkit.Mvvm.Input;\r\nusing System.Windows.Input;\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.ViewModels.Settings;\r\n\r\npublic partial class FileSystemViewModel : WslConfigSettingViewModel\r\n{\r\n    private IWslConfigSetting? _defaultVHDSize;\r\n    private ulong _defaultDefaultVHDSizeBytes;\r\n    private bool _defaultVHDSize_ResetEnabled;\r\n\r\n    public FileSystemViewModel()\r\n    {\r\n        InitializeConfigSettings();\r\n\r\n        DefaultVHDSize_ResetEnabled = !Equals(_defaultDefaultVHDSizeBytes, _defaultVHDSize!.UInt64Value);\r\n    }\r\n\r\n    override protected void InitializeConfigSettings()\r\n    {\r\n        var wslConfigService = App.GetService<IWslConfigService>();\r\n        _defaultVHDSize = wslConfigService.GetWslConfigSetting(WslConfigEntry.VhdSizeBytes);\r\n\r\n        _defaultDefaultVHDSizeBytes = wslConfigService.GetWslConfigSetting(WslConfigEntry.VhdSizeBytes, true).UInt64Value;\r\n    }\r\n\r\n    public string DefaultVHDSize\r\n    {\r\n        get\r\n        {\r\n            return _defaultVHDSize!.UInt64Value.ToString();\r\n        }\r\n        set\r\n        {\r\n            if (ValidateInput(value, Constants.WholeNumberRegex))\r\n            {\r\n                if (UInt64.TryParse(value, out ulong parsedValue))\r\n                {\r\n                    Set(ref _defaultVHDSize!, parsedValue);\r\n                }\r\n                else\r\n                {\r\n                    OnPropertyChanged();\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    public void SetDefaultVHDSize_ResetEnabled(string? value)\r\n    {\r\n        if (UInt64.TryParse(value, out UInt64 parseResult))\r\n        {\r\n            DefaultVHDSize_ResetEnabled = !Equals(_defaultDefaultVHDSizeBytes / Constants.MB, parseResult);\r\n        }\r\n        else\r\n        {\r\n            DefaultVHDSize_ResetEnabled = true;\r\n        }\r\n    }\r\n\r\n    public bool DefaultVHDSize_ResetEnabled\r\n    {\r\n        get => _defaultVHDSize_ResetEnabled;\r\n        set => SetProperty(ref _defaultVHDSize_ResetEnabled, value);\r\n    }\r\n\r\n    private void DefaultVHDSize_ResetExecuted(string? param)\r\n    {\r\n        DefaultVHDSize = _defaultDefaultVHDSizeBytes.ToString();\r\n    }\r\n\r\n    public ICommand DefaultVHDSize_ResetCommand => new RelayCommand<string>(DefaultVHDSize_ResetExecuted);\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/Settings/MemAndProcViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\nusing CommunityToolkit.Mvvm.Input;\r\nusing System.Windows.Input;\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.ViewModels.Settings;\r\n\r\npublic partial class MemAndProcViewModel : WslConfigSettingViewModel\r\n{\r\n    private IWslConfigSetting? _procCount;\r\n    private IWslConfigSetting? _memorySize;\r\n    private IWslConfigSetting? _swapSize;\r\n    private IWslConfigSetting? _swapFilePath;\r\n    private int _defaultProcCount;\r\n    private ulong _defaultMemorySize;\r\n    private ulong _defaultSwapSize;\r\n    private bool _procCount_ResetEnabled;\r\n    private bool _memorySize_ResetEnabled;\r\n    private bool _swapSize_ResetEnabled;\r\n\r\n    public MemAndProcViewModel()\r\n    {\r\n        InitializeConfigSettings();\r\n\r\n        ProcCount_ResetEnabled = !Equals(_defaultProcCount, _procCount!.Int32Value);\r\n        MemorySize_ResetEnabled = !Equals(_defaultMemorySize, _memorySize!.UInt64Value);\r\n        SwapSize_ResetEnabled = !Equals(_defaultSwapSize, _swapSize!.UInt64Value);\r\n    }\r\n\r\n    protected override void InitializeConfigSettings()\r\n    {\r\n        var wslConfigService = App.GetService<IWslConfigService>();\r\n        _procCount = wslConfigService.GetWslConfigSetting(WslConfigEntry.ProcessorCount);\r\n        _memorySize = wslConfigService.GetWslConfigSetting(WslConfigEntry.MemorySizeBytes);\r\n        _swapSize = wslConfigService.GetWslConfigSetting(WslConfigEntry.SwapSizeBytes);\r\n        _swapFilePath = wslConfigService.GetWslConfigSetting(WslConfigEntry.SwapFilePath);\r\n\r\n        _defaultProcCount = wslConfigService.GetWslConfigSetting(WslConfigEntry.ProcessorCount, true).Int32Value;\r\n        _defaultMemorySize = wslConfigService.GetWslConfigSetting(WslConfigEntry.MemorySizeBytes, true).UInt64Value;\r\n        _defaultSwapSize = wslConfigService.GetWslConfigSetting(WslConfigEntry.SwapSizeBytes, true).UInt64Value;\r\n    }\r\n\r\n    public string ProcCount\r\n    {\r\n        get\r\n        {\r\n            return _procCount!.Int32Value.ToString();\r\n        }\r\n        set\r\n        {\r\n            if (ValidateInput(value, Constants.IntegerRegex))\r\n            {\r\n                if (Int32.TryParse(value, out int parsedValue))\r\n                {\r\n                    Set(ref _procCount!, parsedValue);\r\n                }\r\n                else\r\n                {\r\n                    OnPropertyChanged();\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    public void SetProcCount_ResetEnabled(string? value)\r\n    {\r\n        if (Int32.TryParse(value, out Int32 parseResult))\r\n        {\r\n            ProcCount_ResetEnabled = !Equals(_defaultProcCount, parseResult);\r\n        }\r\n        else\r\n        {\r\n            ProcCount_ResetEnabled = true;\r\n        }\r\n    }\r\n\r\n    public bool ProcCount_ResetEnabled\r\n    {\r\n        get => _procCount_ResetEnabled;\r\n        set => SetProperty(ref _procCount_ResetEnabled, value);\r\n    }\r\n\r\n    private void ProcCount_ResetExecuted(string? param)\r\n    {\r\n        ProcCount = _defaultProcCount.ToString();\r\n    }\r\n\r\n    public ICommand ProcCount_ResetCommand => new RelayCommand<string>(ProcCount_ResetExecuted);\r\n\r\n    public string MemorySize\r\n    {\r\n        get\r\n        {\r\n            return _memorySize!.UInt64Value.ToString();\r\n        }\r\n        set\r\n        {\r\n            if (ValidateInput(value, Constants.WholeNumberRegex))\r\n            {\r\n                if (UInt64.TryParse(value, out ulong parsedValue))\r\n                {\r\n                    Set(ref _memorySize!, parsedValue);\r\n                }\r\n                else\r\n                {\r\n                    OnPropertyChanged();\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    public void SetMemorySize_ResetEnabled(string? value)\r\n    {\r\n        if (UInt64.TryParse(value, out UInt64 parseResult))\r\n        {\r\n            MemorySize_ResetEnabled = !Equals(_defaultMemorySize / Constants.MB, parseResult);\r\n        }\r\n        else\r\n        {\r\n            MemorySize_ResetEnabled = true;\r\n        }\r\n    }\r\n\r\n    public bool MemorySize_ResetEnabled\r\n    {\r\n        get => _memorySize_ResetEnabled;\r\n        set => SetProperty(ref _memorySize_ResetEnabled, value);\r\n    }\r\n\r\n    private void MemorySize_ResetExecuted(string? param)\r\n    {\r\n        MemorySize = _defaultMemorySize.ToString();\r\n    }\r\n\r\n    public ICommand MemorySize_ResetCommand => new RelayCommand<string>(MemorySize_ResetExecuted);\r\n\r\n    public string SwapSize\r\n    {\r\n        get\r\n        {\r\n            return _swapSize!.UInt64Value.ToString();\r\n        }\r\n        set\r\n        {\r\n            if (ValidateInput(value, Constants.WholeNumberRegex))\r\n            {\r\n                if (UInt64.TryParse(value, out ulong parsedValue))\r\n                {\r\n                    Set(ref _swapSize!, parsedValue);\r\n                }\r\n                else\r\n                {\r\n                    OnPropertyChanged();\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    public void SetSwapSize_ResetEnabled(string? value)\r\n    {\r\n        if (UInt64.TryParse(value, out UInt64 parseResult))\r\n        {\r\n            SwapSize_ResetEnabled = !Equals(_defaultSwapSize / Constants.MB, parseResult);\r\n        }\r\n        else\r\n        {\r\n            SwapSize_ResetEnabled = true;\r\n        }\r\n    }\r\n\r\n    public bool SwapSize_ResetEnabled\r\n    {\r\n        get => _swapSize_ResetEnabled;\r\n        set => SetProperty(ref _swapSize_ResetEnabled, value);\r\n    }\r\n\r\n    private void SwapSize_ResetExecuted(string? param)\r\n    {\r\n        SwapSize = _defaultSwapSize.ToString();\r\n    }\r\n\r\n    public ICommand SwapSize_ResetCommand => new RelayCommand<string>(SwapSize_ResetExecuted);\r\n\r\n    public string SwapFilePath\r\n    {\r\n        get { return _swapFilePath!.StringValue; }\r\n        set { Set(ref _swapFilePath!, value); }\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/Settings/NetworkingViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\nusing CommunityToolkit.Mvvm.Input;\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing System.Windows.Input;\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.ViewModels.Settings;\r\n\r\npublic partial class NetworkingViewModel : WslConfigSettingViewModel\r\n{\r\n    private IWslConfigSetting? _networkingMode;\r\n    private IWslConfigSetting? _hyperVFirewall;\r\n    private IWslConfigSetting? _ignoredPorts;\r\n    private IWslConfigSetting? _localhostForwarding;\r\n    private IWslConfigSetting? _hostAddressLoopback;\r\n    private IWslConfigSetting? _autoProxy;\r\n    private IWslConfigSetting? _initialAutoProxyTimeout;\r\n    private IWslConfigSetting? _dNSProxy;\r\n    private IWslConfigSetting? _dNSTunneling;\r\n    private IWslConfigSetting? _bestEffortDNS;\r\n    private string? _defaultIgnoredPorts;\r\n    private int _defaultInitialAutoProxyTimeout;\r\n    private List<ComboBoxItem> _networkingModeItems;\r\n    private bool _ignoredPorts_ResetEnabled;\r\n    private bool _initialAutoProxyTimeout_ResetEnabled;\r\n\r\n    public NetworkingViewModel()\r\n    {\r\n        InitializeConfigSettings();\r\n\r\n        InitialAutoProxyTimeout_ResetEnabled = !Equals(_defaultInitialAutoProxyTimeout, _initialAutoProxyTimeout!.Int32Value);\r\n\r\n        _networkingModeItems = new List<ComboBoxItem>();\r\n        foreach (var networkModeItem in Enum.GetNames(typeof(NetworkingConfiguration)).ToList())\r\n        {\r\n            _networkingModeItems.Add(new ComboBoxItem() { Name = networkModeItem, Content = networkModeItem });\r\n        }\r\n\r\n        _networkingModeItems[(int)NetworkingConfiguration.Bridged].Visibility = NetworkingModeSelected == (int)NetworkingConfiguration.Bridged ?\r\n            Visibility.Visible : Visibility.Collapsed;\r\n    }\r\n\r\n    protected override void InitializeConfigSettings()\r\n    {\r\n        var wslConfigService = App.GetService<IWslConfigService>();\r\n        _networkingMode = wslConfigService.GetWslConfigSetting(WslConfigEntry.NetworkingMode);\r\n        _hyperVFirewall = wslConfigService.GetWslConfigSetting(WslConfigEntry.FirewallEnabled);\r\n        _ignoredPorts = wslConfigService.GetWslConfigSetting(WslConfigEntry.IgnoredPorts);\r\n        _localhostForwarding = wslConfigService.GetWslConfigSetting(WslConfigEntry.LocalhostForwardingEnabled);\r\n        _hostAddressLoopback = wslConfigService.GetWslConfigSetting(WslConfigEntry.HostAddressLoopbackEnabled);\r\n        _autoProxy = wslConfigService.GetWslConfigSetting(WslConfigEntry.AutoProxyEnabled);\r\n        _initialAutoProxyTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.InitialAutoProxyTimeout);\r\n        _dNSProxy = wslConfigService.GetWslConfigSetting(WslConfigEntry.DNSProxyEnabled);\r\n        _dNSTunneling = wslConfigService.GetWslConfigSetting(WslConfigEntry.DNSTunnelingEnabled);\r\n        _bestEffortDNS = wslConfigService.GetWslConfigSetting(WslConfigEntry.BestEffortDNSParsingEnabled);\r\n\r\n        string defaultIgnoredPorts = wslConfigService.GetWslConfigSetting(WslConfigEntry.IgnoredPorts, true).StringValue;\r\n        _defaultIgnoredPorts = defaultIgnoredPorts == null ? String.Empty : defaultIgnoredPorts;\r\n        _defaultInitialAutoProxyTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.InitialAutoProxyTimeout, true).Int32Value;\r\n    }\r\n\r\n    public List<ComboBoxItem> NetworkingModes\r\n    {\r\n        get { return _networkingModeItems; }\r\n    }\r\n\r\n    public int NetworkingModeSelected\r\n    {\r\n        get { return (int)_networkingMode!.NetworkingConfigurationValue; }\r\n        set { Set(ref _networkingMode!, value); }\r\n    }\r\n\r\n    public bool IsOnHyperVFirewall\r\n    {\r\n        get { return _hyperVFirewall!.BoolValue; }\r\n        set { Set(ref _hyperVFirewall!, value); }\r\n    }\r\n\r\n    public string IgnoredPorts\r\n    {\r\n        get\r\n        {\r\n            IgnoredPorts_ResetEnabled = !Equals(_defaultIgnoredPorts, _ignoredPorts!.StringValue);\r\n            return _ignoredPorts!.StringValue;\r\n        }\r\n        set\r\n        {\r\n            if (ValidateInput(value, Constants.CommaSeparatedWholeNumbersOrEmptyRegex))\r\n            {\r\n                Set(ref _ignoredPorts!, value);\r\n            }\r\n        }\r\n    }\r\n\r\n    public bool IgnoredPorts_ResetEnabled\r\n    {\r\n        get => _ignoredPorts_ResetEnabled;\r\n        set => SetProperty(ref _ignoredPorts_ResetEnabled, value);\r\n    }\r\n\r\n    private void IgnoredPorts_ResetExecuted(string? param)\r\n    {\r\n        IgnoredPorts = _defaultIgnoredPorts!;\r\n    }\r\n\r\n    public ICommand IgnoredPorts_ResetCommand => new RelayCommand<string>(IgnoredPorts_ResetExecuted);\r\n\r\n    public bool IsOnLocalhostForwarding\r\n    {\r\n        get { return _localhostForwarding!.BoolValue; }\r\n        set { Set(ref _localhostForwarding!, value); }\r\n    }\r\n\r\n    public bool IsOnHostAddressLoopback\r\n    {\r\n        get { return _hostAddressLoopback!.BoolValue; }\r\n        set { Set(ref _hostAddressLoopback!, value); }\r\n    }\r\n\r\n    public bool IsOnAutoProxy\r\n    {\r\n        get { return _autoProxy!.BoolValue; }\r\n        set { Set(ref _autoProxy!, value); }\r\n    }\r\n\r\n    public string InitialAutoProxyTimeout\r\n    {\r\n        get\r\n        {\r\n            return _initialAutoProxyTimeout!.Int32Value.ToString();\r\n        }\r\n        set\r\n        {\r\n            if (ValidateInput(value, Constants.IntegerRegex))\r\n            {\r\n                if (Int32.TryParse(value, out int parsedValue))\r\n                {\r\n                    Set(ref _initialAutoProxyTimeout!, parsedValue);\r\n                }\r\n                else\r\n                {\r\n                    OnPropertyChanged();\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    public void SetInitialAutoProxyTimeout_ResetEnabled(string? value)\r\n    {\r\n        if (Int32.TryParse(value, out Int32 parseResult))\r\n        {\r\n            InitialAutoProxyTimeout_ResetEnabled = !Equals(_defaultInitialAutoProxyTimeout, parseResult);\r\n        }\r\n        else\r\n        {\r\n            InitialAutoProxyTimeout_ResetEnabled = true;\r\n        }\r\n    }\r\n\r\n    public bool InitialAutoProxyTimeout_ResetEnabled\r\n    {\r\n        get => _initialAutoProxyTimeout_ResetEnabled;\r\n        set => SetProperty(ref _initialAutoProxyTimeout_ResetEnabled, value);\r\n    }\r\n\r\n    private void InitialAutoProxyTimeout_ResetExecuted(string? param)\r\n    {\r\n        InitialAutoProxyTimeout = _defaultInitialAutoProxyTimeout.ToString();\r\n    }\r\n\r\n    public ICommand InitialAutoProxyTimeout_ResetCommand => new RelayCommand<string>(InitialAutoProxyTimeout_ResetExecuted);\r\n\r\n    public bool IsOnDNSProxy\r\n    {\r\n        get { return _dNSProxy!.BoolValue; }\r\n        set { Set(ref _dNSProxy!, value); }\r\n    }\r\n\r\n    public bool IsOnDNSTunneling\r\n    {\r\n        get { return _dNSTunneling!.BoolValue; }\r\n        set { Set(ref _dNSTunneling!, value); }\r\n    }\r\n\r\n    public bool IsOnBestEffortDNS\r\n    {\r\n        get { return _bestEffortDNS!.BoolValue; }\r\n        set { Set(ref _bestEffortDNS!, value); }\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/Settings/OptionalFeaturesViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\nusing CommunityToolkit.Mvvm.Input;\r\nusing System.Windows.Input;\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.ViewModels.Settings;\r\n\r\npublic partial class OptionalFeaturesViewModel : WslConfigSettingViewModel\r\n{\r\n    private IWslConfigSetting? _memoryReclaimMode;\r\n    private IWslConfigSetting? _gUIApplications;\r\n    private IWslConfigSetting? _nestedVirtualization;\r\n    private IWslConfigSetting? _safeMode;\r\n    private IWslConfigSetting? _sparseVHD;\r\n    private IWslConfigSetting? _vMIdleTimeout;\r\n    private int _defaultVMIdleTimeout;\r\n\r\n    public OptionalFeaturesViewModel()\r\n    {\r\n        InitializeConfigSettings();\r\n\r\n        VMIdleTimeout_ResetEnabled = !Equals(_defaultVMIdleTimeout, _vMIdleTimeout!.Int32Value);\r\n    }\r\n\r\n    protected override void InitializeConfigSettings()\r\n    {\r\n        var wslConfigService = App.GetService<IWslConfigService>();\r\n        _memoryReclaimMode = wslConfigService.GetWslConfigSetting(WslConfigEntry.AutoMemoryReclaim);\r\n        _gUIApplications = wslConfigService.GetWslConfigSetting(WslConfigEntry.GUIApplicationsEnabled);\r\n        _nestedVirtualization = wslConfigService.GetWslConfigSetting(WslConfigEntry.NestedVirtualizationEnabled);\r\n        _safeMode = wslConfigService.GetWslConfigSetting(WslConfigEntry.SafeModeEnabled);\r\n        _sparseVHD = wslConfigService.GetWslConfigSetting(WslConfigEntry.SparseVHDEnabled);\r\n        _vMIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.VMIdleTimeout);\r\n\r\n        _defaultVMIdleTimeout = wslConfigService.GetWslConfigSetting(WslConfigEntry.VMIdleTimeout, true).Int32Value;\r\n    }\r\n\r\n    public List<string> MemoryReclaimModes\r\n    {\r\n        get { return Enum.GetNames(typeof(MemoryReclaimMode)).ToList(); }\r\n    }\r\n\r\n    public int MemoryReclaimModeSelected\r\n    {\r\n        get { return (int)_memoryReclaimMode!.MemoryReclaimModeValue; }\r\n        set { Set(ref _memoryReclaimMode!, value); }\r\n    }\r\n\r\n    public bool IsOnGUIApplications\r\n    {\r\n        get { return _gUIApplications!.BoolValue; }\r\n        set { Set(ref _gUIApplications!, value); }\r\n    }\r\n\r\n    public bool IsOnNestedVirtualization\r\n    {\r\n        get { return _nestedVirtualization!.BoolValue; }\r\n        set { Set(ref _nestedVirtualization!, value); }\r\n    }\r\n\r\n    public bool IsOnSafeMode\r\n    {\r\n        get { return _safeMode!.BoolValue; }\r\n        set { Set(ref _safeMode!, value); }\r\n    }\r\n\r\n    public bool IsOnSparseVHD\r\n    {\r\n        get { return _sparseVHD!.BoolValue; }\r\n        set { Set(ref _sparseVHD!, value); }\r\n    }\r\n\r\n    public string VMIdleTimeout\r\n    {\r\n        get\r\n        {\r\n            return _vMIdleTimeout!.Int32Value.ToString();\r\n        }\r\n        set\r\n        {\r\n            if (ValidateInput(value, Constants.IntegerRegex))\r\n            {\r\n                if (Int32.TryParse(value, out int parsedValue))\r\n                {\r\n                    Set(ref _vMIdleTimeout!, parsedValue);\r\n                }\r\n                else\r\n                {\r\n                    OnPropertyChanged();\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    public void SetVMIdleTimeout_ResetEnabled(string? value)\r\n    {\r\n        if (Int32.TryParse(value, out Int32 parseResult))\r\n        {\r\n            VMIdleTimeout_ResetEnabled = !Equals(_defaultVMIdleTimeout, parseResult);\r\n        }\r\n        else\r\n        {\r\n            VMIdleTimeout_ResetEnabled = true;\r\n        }\r\n    }\r\n\r\n    private bool _vMIdleTimeout_ResetEnabled;\r\n\r\n    public bool VMIdleTimeout_ResetEnabled\r\n    {\r\n        get => _vMIdleTimeout_ResetEnabled;\r\n        set => SetProperty(ref _vMIdleTimeout_ResetEnabled, value);\r\n    }\r\n\r\n    private void VMIdleTimeout_ResetExecuted(string? param)\r\n    {\r\n        VMIdleTimeout = _defaultVMIdleTimeout.ToString();\r\n    }\r\n\r\n    public ICommand VMIdleTimeout_ResetCommand => new RelayCommand<string>(VMIdleTimeout_ResetExecuted);\r\n}"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/Settings/WslConfigSettingViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\nusing Microsoft.UI.Dispatching;\r\nusing System.Runtime.CompilerServices;\r\nusing System.Text.RegularExpressions;\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.ViewModels.Settings\r\n{\r\n    abstract public partial class WslConfigSettingViewModel : ObservableRecipient\r\n    {\r\n        private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();\r\n\r\n        protected WslConfigSettingViewModel()\r\n        {\r\n            App.GetService<IWslConfigService>().WslConfigChanged += OnConfigChanged;\r\n        }\r\n\r\n        public void OnConfigChanged()\r\n        {\r\n            InitializeConfigSettings();\r\n            _dispatcherQueue.TryEnqueue(() =>\r\n            {\r\n                OnPropertyChanged(String.Empty);\r\n            });\r\n        }\r\n\r\n        abstract protected void InitializeConfigSettings();\r\n\r\n        protected bool ValidateInput(string? newValue, Regex regex, [CallerMemberName] string? propertyName = null)\r\n        {\r\n            if (newValue == null || !regex.IsMatch(newValue))\r\n            {\r\n                // Notify the property so it can revert back to its previous value.\r\n                OnPropertyChanged(propertyName);\r\n                return false;\r\n            }\r\n\r\n            return true;\r\n        }\r\n\r\n        protected void Set<T>(ref IWslConfigSetting wslConfigSetting, T newValue, [CallerMemberName] string? propertyName = null)\r\n        {\r\n            if (wslConfigSetting.Equals(newValue))\r\n            {\r\n                return;\r\n            }\r\n\r\n            if (wslConfigSetting.SetValue(newValue) != 0)\r\n            {\r\n                SettingsContentVisibility = false;\r\n                ErrorVisibility = !SettingsContentVisibility;\r\n                return;\r\n            }\r\n\r\n            OnPropertyChanged(propertyName);\r\n        }\r\n\r\n        private bool _errorVisibility = false;\r\n        public bool ErrorVisibility\r\n        {\r\n            get => _errorVisibility;\r\n            set => SetProperty(ref _errorVisibility, value);\r\n        }\r\n\r\n        private bool _settingsContentVisibility = true;\r\n        public bool SettingsContentVisibility\r\n        {\r\n            get => _settingsContentVisibility;\r\n            set => SetProperty(ref _settingsContentVisibility, value);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/ViewModels/ShellViewModel.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.Mvvm.ComponentModel;\r\nusing Microsoft.UI.Xaml.Navigation;\r\nusing WslSettings.Contracts.Services;\r\n\r\nnamespace WslSettings.ViewModels;\r\n\r\npublic partial class ShellViewModel : ObservableRecipient\r\n{\r\n    private bool isBackEnabled;\r\n    private object? selected;\r\n\r\n    public bool IsBackEnabled\r\n    {\r\n        get => isBackEnabled;\r\n        set => SetProperty(ref isBackEnabled, value);\r\n    }\r\n\r\n    public object? Selected\r\n    {\r\n        get => selected;\r\n        set => SetProperty(ref selected, value);\r\n    }\r\n\r\n    public INavigationService NavigationService\r\n    {\r\n        get;\r\n    }\r\n\r\n    public INavigationViewService NavigationViewService\r\n    {\r\n        get;\r\n    }\r\n\r\n    public ShellViewModel(INavigationService navigationService, INavigationViewService navigationViewService)\r\n    {\r\n        NavigationService = navigationService;\r\n        NavigationService.Navigated += OnNavigated;\r\n        NavigationViewService = navigationViewService;\r\n    }\r\n\r\n    private void OnNavigated(object sender, NavigationEventArgs e)\r\n    {\r\n        IsBackEnabled = NavigationService.CanGoBack;\r\n        var selectedItem = NavigationViewService.GetSelectedItem(e.SourcePageType);\r\n        if (selectedItem != null)\r\n        {\r\n            Selected = selectedItem;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/DistroManagementPage.xaml",
    "content": "<Page\r\n    x:Class=\"WslSettings.Views.OOBE.DistroManagementPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <controls:OOBEContent x:Uid=\"Settings_OOBEDistroManagement\" HeroImage=\"ms-appx:///Assets/SettingsOOBEDistroManagement.png\">\r\n        <controls:OOBEContent.PageContent>\r\n            <StackPanel Orientation=\"Vertical\" Spacing=\"12\">\r\n                <TextBlock x:Uid=\"Settings_OOBEDistroManagement_InstallableDistrosTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,12,0,6\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBEDistroManagement_InstallableDistrosSample\" TextWrapping=\"Wrap\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBEDistroManagement_InstallNamedDistrosTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,24,0,12\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBEDistroManagement_InstallNamedDistrosSample\" TextWrapping=\"Wrap\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBEDistroManagement_AvailableDistrosTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,24,0,6\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBEDistroManagement_AvailableDistrosSample\" TextWrapping=\"Wrap\"/>\r\n                <HyperlinkButton x:Uid=\"Settings_OOBEDistroManagementBasicWSLCommandsLink\" Style=\"{StaticResource TextButtonStyle}\" Margin=\"0,12,0,-12\"/>\r\n                <HyperlinkButton x:Uid=\"Settings_OOBEDistroManagementImportCustomDistroLink\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n            </StackPanel>\r\n        </controls:OOBEContent.PageContent>\r\n    </controls:OOBEContent>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/DistroManagementPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.ViewModels.OOBE;\r\nnamespace WslSettings.Views.OOBE;\r\n\r\npublic sealed partial class DistroManagementPage : Page\r\n{\r\n    public DistroManagementViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public DistroManagementPage()\r\n    {\r\n        ViewModel = App.GetService<DistroManagementViewModel>();\r\n        InitializeComponent();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/DockerDesktopIntegrationPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.OOBE.DockerDesktopIntegrationPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <controls:OOBEContent x:Uid=\"Settings_OOBEDockerDesktopIntegration\" HeroImage=\"ms-appx:///Assets/SettingsOOBEDockerDesktopIntegration.png\">\r\n        <controls:OOBEContent.PageContent>\r\n            <StackPanel Orientation=\"Vertical\" Spacing=\"12\">\r\n                <HyperlinkButton x:Uid=\"Settings_OOBEDockerDesktopIntegrationLink\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n            </StackPanel>\r\n        </controls:OOBEContent.PageContent>\r\n    </controls:OOBEContent>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/DockerDesktopIntegrationPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.ViewModels.OOBE;\r\nnamespace WslSettings.Views.OOBE;\r\n\r\npublic sealed partial class DockerDesktopIntegrationPage : Page\r\n{\r\n    public DockerDesktopIntegrationViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public DockerDesktopIntegrationPage()\r\n    {\r\n        ViewModel = App.GetService<DockerDesktopIntegrationViewModel>();\r\n        InitializeComponent();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/GPUAccelerationPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.OOBE.GPUAccelerationPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <controls:OOBEContent x:Uid=\"Settings_OOBEGPUAcceleration\" HeroImage=\"ms-appx:///Assets/SettingsOOBEGPUAcceleration.gif\">\r\n        <controls:OOBEContent.PageContent>\r\n            <StackPanel Orientation=\"Vertical\" Spacing=\"12\">\r\n                <HyperlinkButton x:Uid=\"Settings_OOBEGPUAccelerationLink\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n            </StackPanel>\r\n        </controls:OOBEContent.PageContent>\r\n    </controls:OOBEContent>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/GPUAccelerationPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.ViewModels.OOBE;\r\nnamespace WslSettings.Views.OOBE;\r\n\r\npublic sealed partial class GPUAccelerationPage : Page\r\n{\r\n    public GPUAccelerationViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public GPUAccelerationPage()\r\n    {\r\n        ViewModel = App.GetService<GPUAccelerationViewModel>();\r\n        InitializeComponent();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/GUIAppsPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.OOBE.GUIAppsPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <controls:OOBEContent x:Uid=\"Settings_OOBEGUIApps\" HeroImage=\"ms-appx:///Assets/SettingsOOBEGUIApps.png\">\r\n        <controls:OOBEContent.PageContent>\r\n            <StackPanel Orientation=\"Vertical\" Spacing=\"12\">\r\n                <TextBlock x:Uid=\"Settings_OOBEGUIApps_AppsListDescription\" TextWrapping=\"Wrap\"/>\r\n                <HyperlinkButton x:Uid=\"Settings_OOBEGUIAppsLink\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n            </StackPanel>\r\n        </controls:OOBEContent.PageContent>\r\n    </controls:OOBEContent>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/GUIAppsPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.ViewModels.OOBE;\r\nnamespace WslSettings.Views.OOBE;\r\n\r\npublic sealed partial class GUIAppsPage : Page\r\n{\r\n    public GUIAppsViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public GUIAppsPage()\r\n    {\r\n        ViewModel = App.GetService<GUIAppsViewModel>();\r\n        InitializeComponent();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/GeneralPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.OOBE.GeneralPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <controls:OOBEContent x:Uid=\"Settings_OOBEGeneral\" HeroImage=\"ms-appx:///Assets/SettingsOOBEGeneral.png\">\r\n        <controls:OOBEContent.PageContent>\r\n            <StackPanel Orientation=\"Vertical\">\r\n                <HyperlinkButton x:Uid=\"Settings_OOBEGeneral_WSLDocumentationLink\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n                <HyperlinkButton x:Uid=\"Settings_OOBEGeneral_BestPracticesSetupLink\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n                <HyperlinkButton x:Uid=\"Settings_OOBEGeneral_GettingStartedLinuxLink\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n            </StackPanel>\r\n        </controls:OOBEContent.PageContent>\r\n    </controls:OOBEContent>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/GeneralPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.ViewModels.OOBE;\r\nnamespace WslSettings.Views.OOBE;\r\n\r\npublic sealed partial class GeneralPage : Page\r\n{\r\n    public GeneralViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public GeneralPage()\r\n    {\r\n        ViewModel = App.GetService<GeneralViewModel>();\r\n        InitializeComponent();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/NetworkingIntegrationPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.OOBE.NetworkingIntegrationPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <controls:OOBEContent x:Uid=\"Settings_OOBENetworkingIntegration\" HeroImage=\"ms-appx:///Assets/SettingsOOBENetworkingIntegration.png\">\r\n        <controls:OOBEContent.PageContent>\r\n            <StackPanel Orientation=\"Vertical\" Spacing=\"12\">\r\n                <TextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,0,0,-6\"/>\r\n                <controls:HyperlinkTextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_NetworkingAppsFromWindowsSample\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,12,0,-6\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_MirroredModeNetworkingSample\" TextWrapping=\"Wrap\"/>\r\n                <HyperlinkButton x:Uid=\"Settings_OOBENetworkingIntegrationLinuxNetworkingAppsLink\" Style=\"{StaticResource TextButtonStyle}\" Margin=\"0,6,0,-12\"/>\r\n                <HyperlinkButton x:Uid=\"Settings_OOBENetworkingIntegrationMirroredModeLink\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n            </StackPanel>\r\n        </controls:OOBEContent.PageContent>\r\n    </controls:OOBEContent>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/NetworkingIntegrationPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.ViewModels.OOBE;\r\nnamespace WslSettings.Views.OOBE;\r\n\r\npublic sealed partial class NetworkingIntegrationPage : Page\r\n{\r\n    public NetworkingIntegrationViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public NetworkingIntegrationPage()\r\n    {\r\n        ViewModel = App.GetService<NetworkingIntegrationViewModel>();\r\n        InitializeComponent();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/ShellPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.OOBE.ShellPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:animations=\"using:CommunityToolkit.WinUI.Animations\"\r\n    xmlns:helpers=\"using:WslSettings.Helpers\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:i=\"using:Microsoft.Xaml.Interactivity\"\r\n    Loaded=\"OnLoaded\">\r\n\r\n    <Grid>\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"*\" />\r\n        </Grid.RowDefinitions>\r\n        <Button\r\n            x:Name=\"PaneToggleBtn\"\r\n            Width=\"48\"\r\n            HorizontalAlignment=\"Left\"\r\n            VerticalAlignment=\"Center\"\r\n            Click=\"PaneToggleBtn_Click\"\r\n            Style=\"{StaticResource PaneToggleButtonStyle}\" />\r\n        <Grid Grid.Row=\"0\"\r\n              x:Name=\"AppTitleBar\"\r\n              Margin=\"16,0\"\r\n              Canvas.ZIndex=\"1\"\r\n              Height=\"{Binding ElementName=NavigationViewControl, Path=CompactPaneLength}\"\r\n              IsHitTestVisible=\"True\"\r\n              VerticalAlignment=\"Top\"\r\n              HorizontalAlignment=\"Stretch\">\r\n              <animations:Implicit.Animations>\r\n                  <animations:OffsetAnimation Duration=\"0:0:0.3\" />\r\n              </animations:Implicit.Animations>\r\n            <Grid.ColumnDefinitions>\r\n                <ColumnDefinition Width=\"Auto\" />\r\n                <ColumnDefinition Width=\"*\" />\r\n            </Grid.ColumnDefinitions>\r\n            <Image Grid.Column=\"0\"\r\n                   Source=\"/Assets/wsl.ico\"\r\n                   VerticalAlignment=\"Center\"\r\n                   HorizontalAlignment=\"Left\"\r\n                   Width=\"16\"\r\n                   Height=\"16\" />\r\n            <TextBlock Grid.Column=\"1\"\r\n                       x:Name=\"AppTitleBarText\"\r\n                       VerticalAlignment=\"Center\"\r\n                       TextWrapping=\"NoWrap\"\r\n                       Style=\"{StaticResource CaptionTextBlockStyle}\"\r\n                       Margin=\"16,0,0,0\"/>\r\n        </Grid>\r\n        <NavigationView\r\n            Grid.Row=\"1\"\r\n            x:Name=\"NavigationViewControl\"\r\n            Canvas.ZIndex=\"0\"\r\n            DisplayModeChanged=\"NavigationViewControl_DisplayModeChanged\"\r\n            Header=\"{x:Bind ((ContentControl)ViewModel.Selected).Content, Mode=OneWay}\"\r\n            IsBackButtonVisible=\"Collapsed\"\r\n            IsBackEnabled=\"{x:Bind ViewModel.IsBackEnabled, Mode=OneWay}\"\r\n            IsPaneOpen=\"True\"\r\n            IsPaneToggleButtonVisible=\"False\"\r\n            IsSettingsVisible=\"False\"\r\n            ItemInvoked=\"NavigationViewControl_ItemInvoked\"\r\n            OpenPaneLength=\"296\"\r\n            PaneDisplayMode=\"Left\"\r\n            SelectedItem=\"{x:Bind ViewModel.Selected, Mode=OneWay}\">\r\n            <NavigationView.MenuItems>\r\n                <!--\r\n                TODO: Update item titles by updating <x:Uid>.Content entries in Strings/en-us/Resources.resw.\r\n                https://docs.microsoft.com/windows/uwp/app-resources/localize-strings-ui-manifest#refer-to-a-string-resource-identifier-from-xaml\r\n            \r\n                TODO: Update item icons by updating FontIcon.Glyph properties.\r\n                https://docs.microsoft.com/windows/apps/design/style/segoe-fluent-icons-font#icon-list\r\n                -->\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_General\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.OOBE.GeneralViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <BitmapIcon ShowAsMonochrome=\"False\" UriSource=\"/Assets/wslbw.ico\" />\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_WorkingAcrossFileSystems\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.OOBE.WorkingAcrossFileSystemsViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <BitmapIcon ShowAsMonochrome=\"False\" UriSource=\"/Assets/SettingsOOBEFileExplorerIcon.png\" />\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_GUIApps\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.OOBE.GUIAppsViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xED35;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_GPUAcceleration\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.OOBE.GPUAccelerationViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xE964;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_NetworkingIntegration\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.OOBE.NetworkingIntegrationViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xE88A;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_DistroManagement\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.OOBE.DistroManagementViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xE912;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItemSeparator/>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_DockerDesktopIntegration\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.OOBE.DockerDesktopIntegrationViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <BitmapIcon ShowAsMonochrome=\"False\" UriSource=\"/Assets/SettingsOOBEDockerIcon.png\" />\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_VSCodeIntegration\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.OOBE.VSCodeIntegrationViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <BitmapIcon ShowAsMonochrome=\"False\" UriSource=\"/Assets/SettingsOOBEVSCodeIcon.png\" />\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_VSIntegration\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.OOBE.VSIntegrationViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <BitmapIcon ShowAsMonochrome=\"False\" UriSource=\"/Assets/SettingsOOBEVSIcon.png\" />\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n            </NavigationView.MenuItems>\r\n            <NavigationView.FooterMenuItems>\r\n                <!--\r\n                TODO: Update item titles by updating <x:Uid>.Content entries in Strings/en-us/Resources.resw.\r\n                https://docs.microsoft.com/windows/uwp/app-resources/localize-strings-ui-manifest#refer-to-a-string-resource-identifier-from-xaml\r\n            \r\n                TODO: Update item icons by updating FontIcon.Glyph properties.\r\n                https://docs.microsoft.com/windows/apps/design/style/segoe-fluent-icons-font#icon-list\r\n                -->\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_WhatsNew\" Visibility=\"Collapsed\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xE789;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_Settings\" Tag=\"Settings\" SelectsOnInvoked=\"False\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xe713;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n            </NavigationView.FooterMenuItems>\r\n            <NavigationView.HeaderTemplate>\r\n                <DataTemplate>\r\n                    <Grid>\r\n                        <TextBlock\r\n                            Text=\"{Binding}\"\r\n                            Style=\"{ThemeResource TitleTextBlockStyle}\" />\r\n                    </Grid>\r\n                </DataTemplate>\r\n            </NavigationView.HeaderTemplate>\r\n            <i:Interaction.Behaviors>\r\n                <behaviors:NavigationViewHeaderBehavior\r\n                    DefaultHeader=\"{x:Bind ((ContentControl)ViewModel.Selected).Content, Mode=OneWay}\">\r\n                    <behaviors:NavigationViewHeaderBehavior.DefaultHeaderTemplate>\r\n                        <DataTemplate>\r\n                            <Grid>\r\n                                <TextBlock\r\n                                    Text=\"{Binding}\"\r\n                                    Style=\"{ThemeResource TitleTextBlockStyle}\" />\r\n                            </Grid>\r\n                        </DataTemplate>\r\n                    </behaviors:NavigationViewHeaderBehavior.DefaultHeaderTemplate>\r\n                </behaviors:NavigationViewHeaderBehavior>\r\n            </i:Interaction.Behaviors>\r\n            <Grid>\r\n                <Frame x:Name=\"NavigationFrame\" />\r\n            </Grid>\r\n        </NavigationView>\r\n\r\n        <VisualStateManager.VisualStateGroups>\r\n            <VisualStateGroup x:Name=\"LayoutVisualStates\">\r\n                <VisualState x:Name=\"WideLayout\">\r\n                    <VisualState.StateTriggers>\r\n                        <AdaptiveTrigger MinWindowWidth=\"720\" />\r\n                    </VisualState.StateTriggers>\r\n                </VisualState>\r\n                <VisualState x:Name=\"SmallLayout\">\r\n                    <VisualState.StateTriggers>\r\n                        <AdaptiveTrigger MinWindowWidth=\"600\" />\r\n                        <AdaptiveTrigger MinWindowWidth=\"0\" />\r\n                    </VisualState.StateTriggers>\r\n                    <VisualState.Setters>\r\n                        <Setter Target=\"NavigationViewControl.PaneDisplayMode\" Value=\"LeftMinimal\" />\r\n                    </VisualState.Setters>\r\n                </VisualState>\r\n            </VisualStateGroup>\r\n        </VisualStateManager.VisualStateGroups>\r\n    </Grid>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/ShellPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Input;\r\n\r\nusing Windows.System;\r\n\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.ViewModels;\r\n\r\nnamespace WslSettings.Views.OOBE;\r\n\r\npublic sealed partial class ShellPage : Page\r\n{\r\n    private readonly Microsoft.UI.Dispatching.DispatcherQueue _dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();\r\n\r\n    private void RegisterNavigationService()\r\n    {\r\n        ViewModel.NavigationViewService.UnregisterEvents();\r\n        ViewModel.NavigationService.Frame = NavigationFrame;\r\n        ViewModel.NavigationViewService.Initialize(NavigationViewControl);\r\n    }\r\n\r\n    public ShellViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public ShellPage(ShellViewModel viewModel)\r\n    {\r\n        ViewModel = viewModel;\r\n        InitializeComponent();\r\n\r\n        RegisterNavigationService();\r\n\r\n        // TODO: Set the title bar icon by updating /Assets/wsl.ico.\r\n        // A custom title bar is required for full window theme and Mica support.\r\n        // https://docs.microsoft.com/windows/apps/develop/title-bar?tabs=winui3#full-customization\r\n        App.OOBEWindow!.ExtendsContentIntoTitleBar = true;\r\n        App.OOBEWindow.SetTitleBar(AppTitleBar);\r\n        App.OOBEWindow.Activated += OOBEWindow_Activated;\r\n        AppTitleBarText.Text = \"Settings_OOBEDisplayName\".GetLocalized();\r\n    }\r\n\r\n    private void OnLoaded(object sender, RoutedEventArgs e)\r\n    {\r\n        TitleBarHelper.UpdateTitleBar(App.OOBEWindow!, RequestedTheme);\r\n\r\n        KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.Left, VirtualKeyModifiers.Menu));\r\n        KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.GoBack));\r\n    }\r\n\r\n    private void OOBEWindow_Activated(object sender, WindowActivatedEventArgs args)\r\n    {\r\n        App.AppTitlebar = AppTitleBarText as UIElement;\r\n        RegisterNavigationService();\r\n        if (ViewModel.NavigationService.Frame?.Content == null)\r\n        {\r\n            ViewModel.NavigationService.NavigateTo(typeof(ViewModels.OOBE.GeneralViewModel).FullName!);\r\n        }\r\n    }\r\n\r\n    private void NavigationViewControl_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)\r\n    {\r\n        if (args.DisplayMode == NavigationViewDisplayMode.Compact || args.DisplayMode == NavigationViewDisplayMode.Minimal)\r\n        {\r\n            PaneToggleBtn.Visibility = Visibility.Visible;\r\n            AppTitleBar.Margin = new Thickness(48, 0, 0, 0);\r\n            AppTitleBarText.Margin = new Thickness(12, 0, 0, 0);\r\n        }\r\n        else\r\n        {\r\n            PaneToggleBtn.Visibility = Visibility.Collapsed;\r\n            AppTitleBar.Margin = new Thickness(16, 0, 0, 0);\r\n            AppTitleBarText.Margin = new Thickness(16, 0, 0, 0);\r\n        }\r\n    }\r\n\r\n    private void PaneToggleBtn_Click(object sender, RoutedEventArgs e)\r\n    {\r\n        NavigationViewControl.IsPaneOpen = !NavigationViewControl.IsPaneOpen;\r\n    }\r\n\r\n    private async void NavigationViewControl_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)\r\n    {\r\n        switch (args.InvokedItemContainer.Tag)\r\n        {\r\n            case \"Settings\":\r\n                await Task.Run(() =>\r\n                {\r\n                    _dispatcherQueue.TryEnqueue(() =>\r\n                    {\r\n                        App.GetService<IWindowService>().CreateOrGetWindow(IWindowService.WindowId.MainWindow).Activate();\r\n                    });\r\n                });\r\n                break;\r\n            default:\r\n                break;\r\n        }\r\n    }\r\n\r\n    private static KeyboardAccelerator BuildKeyboardAccelerator(VirtualKey key, VirtualKeyModifiers? modifiers = null)\r\n    {\r\n        var keyboardAccelerator = new KeyboardAccelerator() { Key = key };\r\n\r\n        if (modifiers.HasValue)\r\n        {\r\n            keyboardAccelerator.Modifiers = modifiers.Value;\r\n        }\r\n\r\n        keyboardAccelerator.Invoked += OnKeyboardAcceleratorInvoked;\r\n\r\n        return keyboardAccelerator;\r\n    }\r\n\r\n    private static void OnKeyboardAcceleratorInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)\r\n    {\r\n        var navigationService = App.GetService<INavigationService>();\r\n\r\n        var result = navigationService.GoBack();\r\n\r\n        args.Handled = result;\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/VSCodeIntegrationPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.OOBE.VSCodeIntegrationPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <controls:OOBEContent x:Uid=\"Settings_OOBEVSCodeIntegration\" HeroImage=\"ms-appx:///Assets/SettingsOOBEVSCodeIntegration.png\">\r\n        <controls:OOBEContent.PageContent>\r\n            <StackPanel Orientation=\"Vertical\" Spacing=\"12\">\r\n                <TextBlock x:Uid=\"Settings_OOBEVSCodeIntegration_VSCodeInstallTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,0,0,-6\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBEVSCodeIntegration_VSCodeInstallSample\" TextWrapping=\"Wrap\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,12,0,-6\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBEVSCodeIntegration_VSCodeOpenProjectSample\" TextWrapping=\"Wrap\"/>\r\n                <HyperlinkButton x:Uid=\"Settings_OOBEVSCodeIntegrationLink\" Margin=\"0,6,0,0\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n            </StackPanel>\r\n        </controls:OOBEContent.PageContent>\r\n    </controls:OOBEContent>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/VSCodeIntegrationPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.ViewModels.OOBE;\r\nnamespace WslSettings.Views.OOBE;\r\n\r\npublic sealed partial class VSCodeIntegrationPage : Page\r\n{\r\n    public VSCodeIntegrationViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public VSCodeIntegrationPage()\r\n    {\r\n        ViewModel = App.GetService<VSCodeIntegrationViewModel>();\r\n        InitializeComponent();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/VSIntegrationPage.xaml",
    "content": "<Page\n    x:Class=\"WslSettings.Views.OOBE.VSIntegrationPage\"\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\n    xmlns:controls=\"using:WslSettings.Controls\"\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\n\n    <controls:OOBEContent x:Uid=\"Settings_OOBEVSIntegration\" HeroImage=\"ms-appx:///Assets/SettingsOOBEVSIntegration.png\">\n        <controls:OOBEContent.PageContent>\n            <StackPanel Orientation=\"Vertical\" Spacing=\"12\">\n                <TextBlock x:Uid=\"Settings_OOBEVSIntegration_Title\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,0,0,-6\"/>\n                <TextBlock x:Uid=\"Settings_OOBEVSIntegration_Description\" TextWrapping=\"Wrap\"/>\n                <HyperlinkButton x:Uid=\"Settings_OOBEVSIntegration_VSForDotNetLink\" Margin=\"0,6,0,0\" Style=\"{StaticResource TextButtonStyle}\"/>\n                <HyperlinkButton x:Uid=\"Settings_OOBEVSIntegration_VSForCppLink\" Margin=\"0,6,0,0\" Style=\"{StaticResource TextButtonStyle}\"/>\n            </StackPanel>\n        </controls:OOBEContent.PageContent>\n    </controls:OOBEContent>\n</Page>\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/VSIntegrationPage.xaml.cs",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\n\nusing Microsoft.UI.Xaml.Controls;\nusing WslSettings.ViewModels.OOBE;\nnamespace WslSettings.Views.OOBE;\n\npublic sealed partial class VSIntegrationPage : Page\n{\n    public VSIntegrationViewModel ViewModel\n    {\n        get;\n    }\n\n    public VSIntegrationPage()\n    {\n        ViewModel = App.GetService<VSIntegrationViewModel>();\n        InitializeComponent();\n    }\n}\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/WorkingAcrossFileSystemsPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.OOBE.WorkingAcrossFileSystemsPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <controls:OOBEContent x:Uid=\"Settings_OOBECrossOSFileAccess\" HeroImage=\"ms-appx:///Assets/SettingsOOBECrossOSFileAccess.gif\">\r\n        <controls:OOBEContent.PageContent>\r\n            <StackPanel Orientation=\"Vertical\" Spacing=\"12\">\r\n                <TextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,0,0,-6\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_WindowsFromLinuxSample\" TextWrapping=\"Wrap\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,12,0,-6\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_LinuxFromFileExplorerSample\" TextWrapping=\"Wrap\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLTitle\" Style=\"{StaticResource OobeSubtitleStyle}\" Margin=\"0,12,0,-6\"/>\r\n                <TextBlock x:Uid=\"Settings_OOBECrossOSFileAccess_LaunchWindowsFromWSLSample\" TextWrapping=\"Wrap\"/>\r\n                <HyperlinkButton x:Uid=\"Settings_OOBECrossOSFileAccessLink\" Margin=\"0,6,0,0\" Style=\"{StaticResource TextButtonStyle}\"/>\r\n            </StackPanel>\r\n        </controls:OOBEContent.PageContent>\r\n    </controls:OOBEContent>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/OOBE/WorkingAcrossFileSystemsPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.ViewModels.OOBE;\r\nnamespace WslSettings.Views.OOBE;\r\n\r\npublic sealed partial class WorkingAcrossFileSystemsPage : Page\r\n{\r\n    public WorkingAcrossFileSystemsViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public WorkingAcrossFileSystemsPage()\r\n    {\r\n        ViewModel = App.GetService<WorkingAcrossFileSystemsViewModel>();\r\n        InitializeComponent();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/AboutPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.Settings.AboutPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:ctControls=\"using:CommunityToolkit.WinUI.Controls\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <Page.Resources>\r\n        <DataTemplate x:Key=\"UnderlinedHyperlinkTemplate\">\r\n            <TextBlock Text=\"{Binding}\" TextDecorations=\"Underline\" />\r\n        </DataTemplate>\r\n    </Page.Resources>\r\n\r\n    <Grid Margin=\"{ThemeResource ContentPageMargin}\">\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"*\" />\r\n        </Grid.RowDefinitions>\r\n        <TextBlock x:Uid=\"Settings_AboutPageTitle\" Style=\"{ThemeResource PageHeaderTextBlockStyle}\" Margin=\"{StaticResource MediumSmallBottomMargin}\" HorizontalAlignment=\"Left\"/>\r\n        <ScrollViewer Grid.Row=\"1\" VerticalScrollBarVisibility=\"Auto\">\r\n            <StackPanel Spacing=\"{StaticResource SettingsCardSpacing}\">\r\n                <ctControls:SettingsExpander x:Uid=\"Settings_About\" IsExpanded=\"True\">\r\n                    <TextBlock IsTextSelectionEnabled=\"True\" Text=\"{x:Bind ViewModel.VersionDescription, Mode=OneWay}\" />\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"-42,0,0,0\">\r\n                            <StackPanel Orientation=\"Vertical\">\r\n                                <HyperlinkButton x:Uid=\"Settings_IssuesLink\" Margin=\"{StaticResource HyperlinkButtonNegativeMargin}\" ContentTemplate=\"{StaticResource UnderlinedHyperlinkTemplate}\" />\r\n                                <HyperlinkButton x:Uid=\"Settings_DocumentationLink\" Margin=\"{StaticResource HyperlinkButtonNegativeMargin}\" ContentTemplate=\"{StaticResource UnderlinedHyperlinkTemplate}\" />\r\n                                <HyperlinkButton x:Uid=\"Settings_ReleaseNotesLink\" Margin=\"{StaticResource HyperlinkButtonNegativeMargin}\" ContentTemplate=\"{StaticResource UnderlinedHyperlinkTemplate}\" />\r\n                                <HyperlinkButton x:Uid=\"Settings_PrivacyPolicyLink\" Margin=\"{StaticResource HyperlinkButtonNegativeMargin}\" ContentTemplate=\"{StaticResource UnderlinedHyperlinkTemplate}\" />\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n            </StackPanel>\r\n        </ScrollViewer>\r\n    </Grid>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/AboutPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing WslSettings.ViewModels.Settings;\r\n\r\nnamespace WslSettings.Views.Settings;\r\n\r\npublic sealed partial class AboutPage : Page\r\n{\r\n    public AboutViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public AboutPage()\r\n    {\r\n        ViewModel = App.GetService<AboutViewModel>();\r\n        InitializeComponent();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/DeveloperPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.Settings.DeveloperPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:ctControls=\"using:CommunityToolkit.WinUI.Controls\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <Grid Margin=\"{ThemeResource ContentPageMargin}\"\r\n          IsTabStop=\"True\"\r\n\t\t  AutomationProperties.LandmarkType=\"Main\"\r\n\t\t  x:Name=\"DeveloperPageRoot\">\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"*\" />\r\n        </Grid.RowDefinitions>\r\n        <TextBlock x:Uid=\"Settings_DeveloperPageTitle\" Style=\"{ThemeResource PageHeaderTextBlockStyle}\" Margin=\"{StaticResource MediumSmallBottomMargin}\" HorizontalAlignment=\"Left\" AutomationProperties.HeadingLevel=\"Level1\"/>\r\n        <TextBlock x:Uid=\"Settings_ErrorTryAgainLater\" x:Name=\"Settings_ErrorTryAgainLater\" Grid.Row=\"1\" HorizontalAlignment=\"Left\" AutomationProperties.LiveSetting=\"Assertive\"\r\n            Visibility=\"{x:Bind ViewModel.ErrorVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\"/>\r\n        <ScrollViewer Grid.Row=\"2\" VerticalScrollBarVisibility=\"Auto\"\r\n            Visibility=\"{x:Bind ViewModel.SettingsContentVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\">\r\n            <StackPanel Spacing=\"{StaticResource SettingsCardSpacing}\">\r\n                <ctControls:SettingsCard x:Uid=\"Settings_DebugConsole\">\r\n                    <ToggleSwitch x:Uid=\"Settings_DebugConsoleToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnDebugConsole, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_HWPerfCounters\">\r\n                    <ToggleSwitch x:Uid=\"Settings_HWPerfCountersToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnHWPerfCounters, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsExpander x:Name=\"CustomKernelPathExpander\" x:Uid=\"Settings_CustomKernelPath\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockFilePathStyle}\" Text=\"{x:Bind ViewModel.CustomKernelPath, Mode=OneWay}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"CustomKernelPathTextBox\" x:Uid=\"Settings_CustomKernelPathTextBox\" Style=\"{StaticResource TextBoxFilePathStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.CustomKernelPath, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\"/>\r\n                                <Button x:Uid=\"Settings_CustomKernelPathBrowseButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Click=\"CustomKernelPath_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n                <ctControls:SettingsExpander x:Name=\"CustomKernelModulesPathExpander\" x:Uid=\"Settings_CustomKernelModulesPath\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockFilePathStyle}\" Text=\"{x:Bind ViewModel.CustomKernelModulesPath, Mode=OneWay}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"CustomKernelModulesPathTextBox\" x:Uid=\"Settings_CustomKernelModulesPathTextBox\" Style=\"{StaticResource TextBoxFilePathStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.CustomKernelModulesPath, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\"/>\r\n                                <Button x:Uid=\"Settings_CustomKernelModulesPathBrowseButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Click=\"CustomKernelModulesPath_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n                <ctControls:SettingsExpander x:Name=\"CustomSystemDistroPathExpander\" x:Uid=\"Settings_CustomSystemDistroPath\">\r\n                    <ctControls:SettingsExpander.Description>\r\n                        <controls:HyperlinkTextBlock x:Uid=\"Settings_CustomSystemDistroPathDescription\"/>\r\n                    </ctControls:SettingsExpander.Description>\r\n                    <TextBlock Style=\"{StaticResource TextBlockFilePathStyle}\" Text=\"{x:Bind ViewModel.CustomSystemDistroPath, Mode=OneWay}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"CustomSystemDistroPathTextBox\" x:Uid=\"Settings_CustomSystemDistroPathTextBox\" Style=\"{StaticResource TextBoxFilePathStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.CustomSystemDistroPath, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\"/>\r\n                                <Button x:Uid=\"Settings_CustomSystemDistroPathBrowseButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Click=\"CustomSystemDistroPath_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n            </StackPanel>\r\n        </ScrollViewer>\r\n    </Grid>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/DeveloperPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Automation.Peers;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Navigation;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.ViewModels.Settings;\r\n\r\nnamespace WslSettings.Views.Settings;\r\n\r\npublic sealed partial class DeveloperPage : Page\r\n{\r\n    public DeveloperViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public DeveloperPage()\r\n    {\r\n        ViewModel = App.GetService<DeveloperViewModel>();\r\n        InitializeComponent();\r\n        Settings_ErrorTryAgainLater.RegisterPropertyChangedCallback(TextBlock.VisibilityProperty, (s, e) =>\r\n        {\r\n            if (ViewModel.ErrorVisibility)\r\n            {\r\n                FrameworkElementAutomationPeer.FromElement(Settings_ErrorTryAgainLater).RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);\r\n            }\r\n        });\r\n\r\n        this.Loaded += OnPageLoaded;\r\n    }\r\n\r\n    private void OnPageLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        DeveloperPageRoot.Focus(FocusState.Programmatic);\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"CustomKernelPathExpander\", \"CustomKernelPathTextBox\");\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"CustomKernelModulesPathExpander\", \"CustomKernelModulesPathTextBox\");\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"CustomSystemDistroPathExpander\", \"CustomSystemDistroPathTextBox\");\r\n    }\r\n\r\n    override protected void OnNavigatedFrom(NavigationEventArgs e)\r\n    {\r\n        App.GetService<IWslConfigService>().WslConfigChanged -= ViewModel.OnConfigChanged;\r\n    }\r\n\r\n    public async void CustomKernelPath_Click(object sender, RoutedEventArgs e)\r\n    {\r\n        // Open the picker for the user to pick a file\r\n        Windows.Storage.StorageFile file = await RuntimeHelper.PickSingleFileAsync();\r\n        if (file != null)\r\n        {\r\n            ViewModel.CustomKernelPath = file.Path;\r\n        }\r\n    }\r\n\r\n    public async void CustomKernelModulesPath_Click(object sender, RoutedEventArgs e)\r\n    {\r\n        // Open the picker for the user to pick a file\r\n        Windows.Storage.StorageFile file = await RuntimeHelper.PickSingleFileAsync([\".vhd\", \".vhdx\"]);\r\n        if (file != null)\r\n        {\r\n            ViewModel.CustomKernelModulesPath = file.Path;\r\n        }\r\n    }\r\n\r\n    public async void CustomSystemDistroPath_Click(object sender, RoutedEventArgs e)\r\n    {\r\n        // Open the picker for the user to pick a file\r\n        Windows.Storage.StorageFile file = await RuntimeHelper.PickSingleFileAsync([\".img\", \".vhd\"]);\r\n        if (file != null)\r\n        {\r\n            ViewModel.CustomSystemDistroPath = file.Path;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/FileSystemPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.Settings.FileSystemPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:ctControls=\"using:CommunityToolkit.WinUI.Controls\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <Grid Margin=\"{ThemeResource ContentPageMargin}\"\r\n          IsTabStop=\"True\"\r\n\t\t  AutomationProperties.LandmarkType=\"Main\"\r\n\t\t  x:Name=\"FileSystemPageRoot\">\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"*\" />\r\n        </Grid.RowDefinitions>\r\n        <TextBlock x:Uid=\"Settings_FileSystemPageTitle\" Style=\"{ThemeResource PageHeaderTextBlockStyle}\" Margin=\"{StaticResource MediumSmallBottomMargin}\" HorizontalAlignment=\"Left\" AutomationProperties.HeadingLevel=\"Level1\"/>\r\n        <TextBlock x:Uid=\"Settings_ErrorTryAgainLater\" x:Name=\"Settings_ErrorTryAgainLater\" Grid.Row=\"1\" HorizontalAlignment=\"Left\" AutomationProperties.LiveSetting=\"Assertive\"\r\n            Visibility=\"{x:Bind ViewModel.ErrorVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\"/>\r\n        <ScrollViewer Grid.Row=\"2\" VerticalScrollBarVisibility=\"Auto\"\r\n            Visibility=\"{x:Bind ViewModel.SettingsContentVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\">\r\n            <StackPanel Spacing=\"{StaticResource SettingsCardSpacing}\">\r\n                <ctControls:SettingsExpander x:Name=\"DefaultVHDSizeExpander\" x:Uid=\"Settings_DefaultVHDSize\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockSettingStyle}\" Text=\"{x:Bind ViewModel.DefaultVHDSize, Mode=OneWay, Converter={StaticResource MegabyteStringConverter}}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"DefaultVHDSizeTextBox\" x:Uid=\"Settings_DefaultVHDSizeTextBox\" Style=\"{StaticResource TextBoxSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.DefaultVHDSize, Mode=TwoWay, UpdateSourceTrigger=LostFocus, Converter={StaticResource MegabyteNumberConverter}}\" TextChanged=\"DefaultVHDSizeTextBox_TextChanged\"/>\r\n                                <Button x:Uid=\"Settings_DefaultVHDSizeResetButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Command=\"{x:Bind ViewModel.DefaultVHDSize_ResetCommand}\" IsEnabled=\"{x:Bind ViewModel.DefaultVHDSize_ResetEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\r\n                                    Click=\"Settings_ResetButton_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n            </StackPanel>\r\n        </ScrollViewer>\r\n    </Grid>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/FileSystemPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing CommunityToolkit.WinUI.Controls;\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Automation.Peers;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Navigation;\r\nusing System.Diagnostics;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.ViewModels.Settings;\r\n\r\nnamespace WslSettings.Views.Settings;\r\n\r\npublic sealed partial class FileSystemPage : Page\r\n{\r\n    public FileSystemViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public FileSystemPage()\r\n    {\r\n        ViewModel = App.GetService<FileSystemViewModel>();\r\n        InitializeComponent();\r\n        Settings_ErrorTryAgainLater.RegisterPropertyChangedCallback(TextBlock.VisibilityProperty, (s, e) =>\r\n        {\r\n            if (ViewModel.ErrorVisibility)\r\n            {\r\n                FrameworkElementAutomationPeer.FromElement(Settings_ErrorTryAgainLater).RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);\r\n            }\r\n        });\r\n\r\n        this.Loaded += OnPageLoaded;\r\n    }\r\n\r\n    override protected void OnNavigatedFrom(NavigationEventArgs e)\r\n    {\r\n        App.GetService<IWslConfigService>().WslConfigChanged -= ViewModel.OnConfigChanged;\r\n    }\r\n\r\n    private void Settings_ResetButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        RuntimeHelper.TryMoveFocusPreviousControl(sender as Button);\r\n    }\r\n\r\n    private void DefaultVHDSizeTextBox_TextChanged(object sender, TextChangedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        TextBox? textBox = sender as TextBox;\r\n        ViewModel.SetDefaultVHDSize_ResetEnabled(textBox!.Text);\r\n    }\r\n\r\n    private void OnPageLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        FileSystemPageRoot.Focus(FocusState.Programmatic);\r\n        var expander = this.FindName(\"DefaultVHDSizeExpander\") as SettingsExpander;\r\n        var textBox = this.FindName(\"DefaultVHDSizeTextBox\") as TextBox;\r\n\r\n        Debug.Assert(expander != null, \"DefaultVHDSizeExpander not found\");\r\n        Debug.Assert(textBox != null, \"DefaultVHDSizeTextBox not found\");\r\n\r\n        if (expander != null && textBox != null)\r\n        {\r\n            RuntimeHelper.SetupSettingsExpanderFocusManagement(expander, textBox);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/MemAndProcPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.Settings.MemAndProcPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:ctControls=\"using:CommunityToolkit.WinUI.Controls\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <Grid Margin=\"{ThemeResource ContentPageMargin}\"\r\n          IsTabStop=\"True\"\r\n\t\t  AutomationProperties.LandmarkType=\"Main\"\r\n\t\t  x:Name=\"MemAndProcPageRoot\">\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"*\" />\r\n        </Grid.RowDefinitions>\r\n        <TextBlock x:Uid=\"Settings_MemAndProcPageTitle\" Style=\"{ThemeResource PageHeaderTextBlockStyle}\" Margin=\"{StaticResource MediumSmallBottomMargin}\" HorizontalAlignment=\"Left\" AutomationProperties.HeadingLevel=\"Level1\"/>\r\n        <TextBlock x:Uid=\"Settings_ErrorTryAgainLater\" x:Name=\"Settings_ErrorTryAgainLater\" Grid.Row=\"1\" HorizontalAlignment=\"Left\" AutomationProperties.LiveSetting=\"Assertive\"\r\n            Visibility=\"{x:Bind ViewModel.ErrorVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\"/>\r\n        <ScrollViewer Grid.Row=\"2\" VerticalScrollBarVisibility=\"Auto\"\r\n            Visibility=\"{x:Bind ViewModel.SettingsContentVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\">\r\n            <StackPanel Spacing=\"{StaticResource SettingsCardSpacing}\">\r\n                <ctControls:SettingsExpander x:Name=\"ProcCountExpander\" x:Uid=\"Settings_ProcCount\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockSettingStyle}\" Text=\"{x:Bind ViewModel.ProcCount, Mode=OneWay}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"ProcCountTextBox\" x:Uid=\"Settings_ProcCountTextBox\" Style=\"{StaticResource TextBoxSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.ProcCount, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\" TextChanged=\"ProcCountTextBox_TextChanged\"/>\r\n                                <Button x:Uid=\"Settings_ProcCountResetButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Command=\"{x:Bind ViewModel.ProcCount_ResetCommand}\" IsEnabled=\"{x:Bind ViewModel.ProcCount_ResetEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\r\n                                    Click=\"Settings_ResetButton_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n                <ctControls:SettingsExpander x:Name=\"MemorySizeExpander\" x:Uid=\"Settings_MemorySize\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockSettingStyle}\" Text=\"{x:Bind ViewModel.MemorySize, Mode=OneWay, Converter={StaticResource MegabyteStringConverter}}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"MemorySizeTextBox\" x:Uid=\"Settings_MemorySizeTextBox\" Style=\"{StaticResource TextBoxSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.MemorySize, Mode=TwoWay, UpdateSourceTrigger=LostFocus, Converter={StaticResource MegabyteNumberConverter}}\" TextChanged=\"MemorySizeTextBox_TextChanged\"/>\r\n                                <Button x:Uid=\"Settings_MemorySizeResetButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Command=\"{x:Bind ViewModel.MemorySize_ResetCommand}\" IsEnabled=\"{x:Bind ViewModel.MemorySize_ResetEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\r\n                                    Click=\"Settings_ResetButton_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n                <ctControls:SettingsExpander x:Name=\"SwapSizeExpander\" x:Uid=\"Settings_SwapSize\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockSettingStyle}\" Text=\"{x:Bind ViewModel.SwapSize, Mode=OneWay, Converter={StaticResource MegabyteStringConverter}}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"SwapSizeTextBox\" x:Uid=\"Settings_SwapSizeTextBox\" Style=\"{StaticResource TextBoxSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.SwapSize, Mode=TwoWay, UpdateSourceTrigger=LostFocus, Converter={StaticResource MegabyteNumberConverter}}\" TextChanged=\"SwapSizeTextBox_TextChanged\"/>\r\n                                <Button x:Uid=\"Settings_SwapSizeResetButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Command=\"{x:Bind ViewModel.SwapSize_ResetCommand}\" IsEnabled=\"{x:Bind ViewModel.SwapSize_ResetEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\r\n                                    Click=\"Settings_ResetButton_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n                <ctControls:SettingsExpander x:Name=\"SwapFilePathExpander\" x:Uid=\"Settings_SwapFilePath\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockFilePathStyle}\" Text=\"{x:Bind ViewModel.SwapFilePath, Mode=OneWay}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"SwapFilePathTextBox\" x:Uid=\"Settings_SwapFilePathTextBox\" Style=\"{StaticResource TextBoxFilePathStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.SwapFilePath, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\"/>\r\n                                <Button x:Uid=\"Settings_SwapFilePathBrowseButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Click=\"SwapFilePath_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n            </StackPanel>\r\n        </ScrollViewer>\r\n    </Grid>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/MemAndProcPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Automation.Peers;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Navigation;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.ViewModels.Settings;\r\n\r\nnamespace WslSettings.Views.Settings;\r\n\r\npublic sealed partial class MemAndProcPage : Page\r\n{\r\n    public MemAndProcViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public MemAndProcPage()\r\n    {\r\n        ViewModel = App.GetService<MemAndProcViewModel>();\r\n        InitializeComponent();\r\n        Settings_ErrorTryAgainLater.RegisterPropertyChangedCallback(TextBlock.VisibilityProperty, (s, e) =>\r\n        {\r\n            if (ViewModel.ErrorVisibility)\r\n            {\r\n                FrameworkElementAutomationPeer.FromElement(Settings_ErrorTryAgainLater).RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);\r\n            }\r\n        });\r\n\r\n        this.Loaded += OnPageLoaded;\r\n    }\r\n\r\n    private void OnPageLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        MemAndProcPageRoot.Focus(FocusState.Programmatic);\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"ProcCountExpander\", \"ProcCountTextBox\");\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"MemorySizeExpander\", \"MemorySizeTextBox\");\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"SwapSizeExpander\", \"SwapSizeTextBox\");\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"SwapFilePathExpander\", \"SwapFilePathTextBox\");\r\n    }\r\n\r\n    override protected void OnNavigatedFrom(NavigationEventArgs e)\r\n    {\r\n        App.GetService<IWslConfigService>().WslConfigChanged -= ViewModel.OnConfigChanged;\r\n    }\r\n\r\n    private void Settings_ResetButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        RuntimeHelper.TryMoveFocusPreviousControl(sender as Button);\r\n    }\r\n\r\n    public async void SwapFilePath_Click(object sender, RoutedEventArgs e)\r\n    {\r\n        // Open the picker for the user to pick a file\r\n        Windows.Storage.StorageFile file = await RuntimeHelper.PickSingleFileAsync([\".vhdx\"]);\r\n        if (file != null)\r\n        {\r\n            ViewModel.SwapFilePath = file.Path;\r\n        }\r\n    }\r\n\r\n    private void ProcCountTextBox_TextChanged(object sender, TextChangedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        TextBox? textBox = sender as TextBox;\r\n        ViewModel.SetProcCount_ResetEnabled(textBox!.Text);\r\n    }\r\n\r\n    private void MemorySizeTextBox_TextChanged(object sender, TextChangedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        TextBox? textBox = sender as TextBox;\r\n        ViewModel.SetMemorySize_ResetEnabled(textBox!.Text);\r\n    }\r\n\r\n    private void SwapSizeTextBox_TextChanged(object sender, TextChangedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        TextBox? textBox = sender as TextBox;\r\n        ViewModel.SetSwapSize_ResetEnabled(textBox!.Text);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/NetworkingPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.Settings.NetworkingPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:ctControls=\"using:CommunityToolkit.WinUI.Controls\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <Grid Margin=\"{ThemeResource ContentPageMargin}\"\r\n          IsTabStop=\"True\"\r\n\t\t  AutomationProperties.LandmarkType=\"Main\"\r\n\t\t  x:Name=\"NetworkingPageRoot\">\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"*\" />\r\n        </Grid.RowDefinitions>\r\n        <TextBlock x:Uid=\"Settings_NetworkingPageTitle\" Style=\"{ThemeResource PageHeaderTextBlockStyle}\" Margin=\"{StaticResource MediumSmallBottomMargin}\" HorizontalAlignment=\"Left\" AutomationProperties.HeadingLevel=\"Level1\"/>\r\n        <TextBlock x:Uid=\"Settings_ErrorTryAgainLater\" x:Name=\"Settings_ErrorTryAgainLater\" Grid.Row=\"1\" HorizontalAlignment=\"Left\" AutomationProperties.LiveSetting=\"Assertive\"\r\n            Visibility=\"{x:Bind ViewModel.ErrorVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\"/>\r\n        <ScrollViewer Grid.Row=\"2\" VerticalScrollBarVisibility=\"Auto\"\r\n            Visibility=\"{x:Bind ViewModel.SettingsContentVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\">\r\n            <StackPanel Spacing=\"{StaticResource SettingsCardSpacing}\">\r\n                <ctControls:SettingsCard x:Uid=\"Settings_NetworkingMode\">\r\n                    <ComboBox x:Uid=\"Settings_NetworkingModeComboBox\" Style=\"{StaticResource ComboBoxSettingStyle}\" HorizontalAlignment=\"Right\"\r\n                        ItemsSource=\"{x:Bind ViewModel.NetworkingModes}\" SelectedIndex=\"{x:Bind ViewModel.NetworkingModeSelected, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_HyperVFirewall\">\r\n                    <ToggleSwitch x:Uid=\"Settings_HyperVFirewallToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnHyperVFirewall, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsExpander x:Name=\"IgnoredPortsExpander\" x:Uid=\"Settings_IgnoredPorts\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockSettingStyle}\" Text=\"{x:Bind ViewModel.IgnoredPorts, Mode=OneWay}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"IgnoredPortsTextBox\" x:Uid=\"Settings_IgnoredPortsTextBox\" Style=\"{StaticResource TextBoxSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.IgnoredPorts, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\"/>\r\n                                <Button x:Uid=\"Settings_IgnoredPortsResetButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Command=\"{x:Bind ViewModel.IgnoredPorts_ResetCommand}\" IsEnabled=\"{x:Bind ViewModel.IgnoredPorts_ResetEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\r\n                                    Click=\"Settings_ResetButton_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_LocalhostForwarding\">\r\n                    <ToggleSwitch x:Uid=\"Settings_LocalhostForwardingToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnLocalhostForwarding, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_HostAddressLoopback\">\r\n                    <ToggleSwitch x:Uid=\"Settings_HostAddressLoopbackToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnHostAddressLoopback, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_AutoProxy\">\r\n                    <ToggleSwitch x:Uid=\"Settings_AutoProxyToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnAutoProxy, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsExpander x:Name=\"InitialAutoProxyTimeoutExpander\" x:Uid=\"Settings_InitialAutoProxyTimeout\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockSettingStyle}\" Text=\"{x:Bind ViewModel.InitialAutoProxyTimeout, Mode=OneWay, Converter={StaticResource MillisecondsStringConverter}}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"InitialAutoProxyTimeoutTextBox\" x:Uid=\"Settings_InitialAutoProxyTimeoutTextBox\" Style=\"{StaticResource TextBoxSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.InitialAutoProxyTimeout, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\" TextChanged=\"InitialAutoProxyTimeoutTextBox_TextChanged\"/>\r\n                                <Button x:Uid=\"Settings_InitialAutoProxyTimeoutResetButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Command=\"{x:Bind ViewModel.InitialAutoProxyTimeout_ResetCommand}\" IsEnabled=\"{x:Bind ViewModel.InitialAutoProxyTimeout_ResetEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\r\n                                    Click=\"Settings_ResetButton_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_DNSProxy\">\r\n                    <ToggleSwitch x:Uid=\"Settings_DNSProxyToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnDNSProxy, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_DNSTunneling\">\r\n                    <ToggleSwitch x:Uid=\"Settings_DNSTunnelingToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnDNSTunneling, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_BestEffortDNS\">\r\n                    <ToggleSwitch x:Uid=\"Settings_BestEffortDNSToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnBestEffortDNS, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n            </StackPanel>\r\n        </ScrollViewer>\r\n    </Grid>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/NetworkingPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Automation.Peers;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Navigation;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.ViewModels.Settings;\r\n\r\nnamespace WslSettings.Views.Settings;\r\n\r\npublic sealed partial class NetworkingPage : Page\r\n{\r\n    public NetworkingViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public NetworkingPage()\r\n    {\r\n        ViewModel = App.GetService<NetworkingViewModel>();\r\n        InitializeComponent();\r\n        Settings_ErrorTryAgainLater.RegisterPropertyChangedCallback(TextBlock.VisibilityProperty, (s, e) =>\r\n        {\r\n            if (ViewModel.ErrorVisibility)\r\n            {\r\n                FrameworkElementAutomationPeer.FromElement(Settings_ErrorTryAgainLater).RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);\r\n            }\r\n        });\r\n\r\n        this.Loaded += OnPageLoaded;\r\n    }\r\n\r\n    private void OnPageLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        NetworkingPageRoot.Focus(FocusState.Programmatic);\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"IgnoredPortsExpander\", \"IgnoredPortsTextBox\");\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"InitialAutoProxyTimeoutExpander\", \"InitialAutoProxyTimeoutTextBox\");\r\n    }\r\n\r\n    override protected void OnNavigatedFrom(NavigationEventArgs e)\r\n    {\r\n        App.GetService<IWslConfigService>().WslConfigChanged -= ViewModel.OnConfigChanged;\r\n    }\r\n\r\n    private void Settings_ResetButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        RuntimeHelper.TryMoveFocusPreviousControl(sender as Button);\r\n    }\r\n\r\n    private void InitialAutoProxyTimeoutTextBox_TextChanged(object sender, TextChangedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        TextBox? textBox = sender as TextBox;\r\n        ViewModel.SetInitialAutoProxyTimeout_ResetEnabled(textBox!.Text);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/OptionalFeaturesPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.Settings.OptionalFeaturesPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:ctControls=\"using:CommunityToolkit.WinUI.Controls\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:controls=\"using:WslSettings.Controls\"\r\n    behaviors:NavigationViewHeaderBehavior.HeaderMode=\"Never\">\r\n\r\n    <Grid Margin=\"{ThemeResource ContentPageMargin}\"\r\n          IsTabStop=\"True\"\r\n\t\t  AutomationProperties.LandmarkType=\"Main\"\r\n\t\t  x:Name=\"OptionalFeaturesPageRoot\">\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"*\" />\r\n        </Grid.RowDefinitions>\r\n        <TextBlock x:Uid=\"Settings_OptionalFeaturesPageTitle\" Style=\"{ThemeResource PageHeaderTextBlockStyle}\" Margin=\"{StaticResource MediumSmallBottomMargin}\" HorizontalAlignment=\"Left\" AutomationProperties.HeadingLevel=\"Level1\"/>\r\n        <TextBlock x:Uid=\"Settings_ErrorTryAgainLater\" x:Name=\"Settings_ErrorTryAgainLater\" Grid.Row=\"1\" HorizontalAlignment=\"Left\" AutomationProperties.LiveSetting=\"Assertive\"\r\n            Visibility=\"{x:Bind ViewModel.ErrorVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\"/>\r\n        <ScrollViewer Grid.Row=\"2\" VerticalScrollBarVisibility=\"Auto\"\r\n            Visibility=\"{x:Bind ViewModel.SettingsContentVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToVisibilityConverter}}\">\r\n            <StackPanel Spacing=\"{StaticResource SettingsCardSpacing}\">\r\n                <ctControls:SettingsCard x:Uid=\"Settings_AutoMemoryReclaim\">\r\n                    <ComboBox x:Uid=\"Settings_AutoMemoryReclaimComboBox\" Style=\"{StaticResource ComboBoxSettingStyle}\" HorizontalAlignment=\"Right\"\r\n                        ItemsSource=\"{x:Bind ViewModel.MemoryReclaimModes}\" SelectedIndex=\"{x:Bind ViewModel.MemoryReclaimModeSelected, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_GUIApplications\">\r\n                    <ctControls:SettingsCard.Description>\r\n                        <controls:HyperlinkTextBlock x:Uid=\"Settings_GUIApplicationsDescription\"/>\r\n                    </ctControls:SettingsCard.Description>\r\n                    <ToggleSwitch x:Uid=\"Settings_GUIApplicationsToggleSwitch\" HorizontalAlignment=\"Right\" IsOn=\"{x:Bind ViewModel.IsOnGUIApplications, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_NestedVirtualization\">\r\n                    <ToggleSwitch x:Uid=\"Settings_NestedVirtualizationToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnNestedVirtualization, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_SafeMode\">\r\n                    <ToggleSwitch x:Uid=\"Settings_SafeModeToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnSafeMode, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsCard x:Uid=\"Settings_SparseVHD\">\r\n                    <ToggleSwitch x:Uid=\"Settings_SparseVHDToggleSwitch\" HorizontalAlignment=\"Right\" MinWidth=\"0\" IsOn=\"{x:Bind ViewModel.IsOnSparseVHD, Mode=TwoWay}\"/>\r\n                </ctControls:SettingsCard>\r\n                <ctControls:SettingsExpander x:Name=\"VMIdleTimeoutExpander\" x:Uid=\"Settings_VMIdleTimeout\">\r\n                    <TextBlock Style=\"{StaticResource TextBlockSettingStyle}\" Text=\"{x:Bind ViewModel.VMIdleTimeout, Mode=OneWay, Converter={StaticResource MillisecondsStringConverter}}\"/>\r\n                    <ctControls:SettingsExpander.Items>\r\n                        <ctControls:SettingsCard HorizontalContentAlignment=\"Left\" ContentAlignment=\"Left\" Margin=\"{StaticResource SettingsExpanderItemMargin}\">\r\n                            <StackPanel Orientation=\"Horizontal\">\r\n                                <TextBox x:Name=\"VMIdleTimeoutTextBox\" x:Uid=\"Settings_VMIdleTimeoutTextBox\" Style=\"{StaticResource TextBoxFilePathStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Text=\"{x:Bind ViewModel.VMIdleTimeout, Mode=TwoWay, UpdateSourceTrigger=LostFocus}\" TextChanged=\"VMIdleTimeoutTextBox_TextChanged\"/>\r\n                                <Button x:Uid=\"Settings_VMIdleTimeoutResetButton\" Style=\"{StaticResource ButtonSettingStyle}\" Margin=\"{StaticResource InputControlSpacingMargin}\"\r\n                                    Command=\"{x:Bind ViewModel.VMIdleTimeout_ResetCommand}\" IsEnabled=\"{x:Bind ViewModel.VMIdleTimeout_ResetEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"\r\n                                    Click=\"Settings_ResetButton_Click\"/>\r\n                            </StackPanel>\r\n                        </ctControls:SettingsCard>\r\n                    </ctControls:SettingsExpander.Items>\r\n                </ctControls:SettingsExpander>\r\n            </StackPanel>\r\n        </ScrollViewer>\r\n    </Grid>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/OptionalFeaturesPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Automation.Peers;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Navigation;\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.ViewModels.Settings;\r\n\r\nnamespace WslSettings.Views.Settings;\r\n\r\npublic sealed partial class OptionalFeaturesPage : Page\r\n{\r\n    public OptionalFeaturesViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public OptionalFeaturesPage()\r\n    {\r\n        ViewModel = App.GetService<OptionalFeaturesViewModel>();\r\n        InitializeComponent();\r\n        Settings_ErrorTryAgainLater.RegisterPropertyChangedCallback(TextBlock.VisibilityProperty, (s, e) =>\r\n        {\r\n            if (ViewModel.ErrorVisibility)\r\n            {\r\n                FrameworkElementAutomationPeer.FromElement(Settings_ErrorTryAgainLater).RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);\r\n            }\r\n        });\r\n\r\n        this.Loaded += OnPageLoaded;\r\n    }\r\n\r\n    private void OnPageLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        OptionalFeaturesPageRoot.Focus(FocusState.Programmatic);\r\n        RuntimeHelper.SetupExpanderFocusManagementByName(this, \"VMIdleTimeoutExpander\", \"VMIdleTimeoutTextBox\");\r\n    }\r\n\r\n    override protected void OnNavigatedFrom(NavigationEventArgs e)\r\n    {\r\n        App.GetService<IWslConfigService>().WslConfigChanged -= ViewModel.OnConfigChanged;\r\n    }\r\n\r\n    private void Settings_ResetButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        RuntimeHelper.TryMoveFocusPreviousControl(sender as Button);\r\n    }\r\n\r\n    private void VMIdleTimeoutTextBox_TextChanged(object sender, TextChangedEventArgs e)\r\n    {\r\n        if (sender == null)\r\n        {\r\n            return;\r\n        }\r\n\r\n        TextBox? textBox = sender as TextBox;\r\n        ViewModel.SetVMIdleTimeout_ResetEnabled(textBox!.Text);\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/ShellPage.xaml",
    "content": "﻿<Page\r\n    x:Class=\"WslSettings.Views.Settings.ShellPage\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:animations=\"using:CommunityToolkit.WinUI.Animations\"\r\n    xmlns:helpers=\"using:WslSettings.Helpers\"\r\n    xmlns:behaviors=\"using:WslSettings.Behaviors\"\r\n    xmlns:i=\"using:Microsoft.Xaml.Interactivity\"\r\n    Loaded=\"OnLoaded\">\r\n\r\n    <Grid>\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition Height=\"Auto\" />\r\n            <RowDefinition Height=\"*\" />\r\n        </Grid.RowDefinitions>\r\n        <Button\r\n            x:Name=\"PaneToggleBtn\"\r\n            Width=\"48\"\r\n            HorizontalAlignment=\"Left\"\r\n            VerticalAlignment=\"Center\"\r\n            Click=\"PaneToggleBtn_Click\"\r\n            Style=\"{StaticResource PaneToggleButtonStyle}\" />\r\n        <Grid Grid.Row=\"0\"\r\n              x:Name=\"AppTitleBar\"\r\n              Margin=\"16,0\"\r\n              Canvas.ZIndex=\"1\"\r\n              Height=\"{Binding ElementName=NavigationViewControl, Path=CompactPaneLength}\"\r\n              IsHitTestVisible=\"True\"\r\n              VerticalAlignment=\"Top\"\r\n              HorizontalAlignment=\"Stretch\">\r\n            <animations:Implicit.Animations>\r\n                <animations:OffsetAnimation Duration=\"0:0:0.3\" />\r\n            </animations:Implicit.Animations>\r\n            <Grid.ColumnDefinitions>\r\n                <ColumnDefinition Width=\"Auto\" />\r\n                <ColumnDefinition Width=\"*\" />\r\n            </Grid.ColumnDefinitions>\r\n            <Image Grid.Column=\"0\"\r\n                   Source=\"/Assets/wsl.ico\"\r\n                   VerticalAlignment=\"Center\"\r\n                   HorizontalAlignment=\"Left\"\r\n                   Width=\"16\"\r\n                   Height=\"16\" />\r\n            <TextBlock Grid.Column=\"1\"\r\n                       x:Name=\"AppTitleBarText\"\r\n                       VerticalAlignment=\"Center\"\r\n                       TextWrapping=\"NoWrap\"\r\n                       Style=\"{StaticResource CaptionTextBlockStyle}\"\r\n                       Margin=\"16,0,0,0\"/>\r\n        </Grid>\r\n        <NavigationView\r\n            x:Name=\"NavigationViewControl\"\r\n            Grid.Row=\"1\"\r\n            Canvas.ZIndex=\"0\"\r\n            CompactModeThresholdWidth=\"1007\"\r\n            DisplayModeChanged=\"NavigationViewControl_DisplayModeChanged\"\r\n            ExpandedModeThresholdWidth=\"1007\"\r\n            Header=\"{x:Bind ((ContentControl)ViewModel.Selected).Content, Mode=OneWay}\"\r\n            IsBackButtonVisible=\"Collapsed\"\r\n            IsBackEnabled=\"{x:Bind ViewModel.IsBackEnabled, Mode=OneWay}\"\r\n            IsPaneToggleButtonVisible=\"False\"\r\n            IsSettingsVisible=\"False\"\r\n            ItemInvoked=\"NavigationViewControl_ItemInvoked\"\r\n            SelectedItem=\"{x:Bind ViewModel.Selected, Mode=OneWay}\">\r\n            <NavigationView.Resources>\r\n                <ResourceDictionary>\r\n                    <ResourceDictionary.ThemeDictionaries>\r\n                        <ResourceDictionary x:Key=\"Dark\">\r\n                            <SolidColorBrush x:Key=\"NavigationViewContentGridBorderBrush\" Color=\"{StaticResource NavigationViewExpandedPaneBackground}\" />\r\n                            <SolidColorBrush x:Key=\"NavigationViewContentBackground\" Color=\"{StaticResource NavigationViewExpandedPaneBackground}\" />\r\n                        </ResourceDictionary>\r\n                        <ResourceDictionary x:Key=\"Light\">\r\n                            <SolidColorBrush x:Key=\"NavigationViewContentGridBorderBrush\" Color=\"{StaticResource NavigationViewExpandedPaneBackground}\" />\r\n                            <SolidColorBrush x:Key=\"NavigationViewContentBackground\" Color=\"{StaticResource NavigationViewExpandedPaneBackground}\" />\r\n                        </ResourceDictionary>\r\n                        <ResourceDictionary x:Key=\"HighContrast\">\r\n                            <SolidColorBrush x:Key=\"NavigationViewContentGridBorderBrush\" Color=\"{ThemeResource SystemColorWindowColor}\" />\r\n                            <SolidColorBrush x:Key=\"NavigationViewContentBackground\" Color=\"{ThemeResource SystemColorWindowColor}\" />\r\n                        </ResourceDictionary>\r\n                    </ResourceDictionary.ThemeDictionaries>\r\n                </ResourceDictionary>\r\n            </NavigationView.Resources>\r\n            <NavigationView.MenuItems>\r\n                <!--\r\n                TODO: Update item titles by updating <x:Uid>.Content entries in Strings/en-us/Resources.resw.\r\n                https://docs.microsoft.com/windows/uwp/app-resources/localize-strings-ui-manifest#refer-to-a-string-resource-identifier-from-xaml\r\n            \r\n                TODO: Update item icons by updating FontIcon.Glyph properties.\r\n                https://docs.microsoft.com/windows/apps/design/style/segoe-fluent-icons-font#icon-list\r\n                -->\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_MemAndProc\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.Settings.MemAndProcViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xe950;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_FileSystem\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.Settings.FileSystemViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xf12b;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_Networking\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.Settings.NetworkingViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xe88a;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_OptionalFeatures\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.Settings.OptionalFeaturesViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xe713;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_Developer\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.Settings.DeveloperViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xe943;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n            </NavigationView.MenuItems>\r\n            <NavigationView.FooterMenuItems>\r\n                <!--\r\n                TODO: Update item titles by updating <x:Uid>.Content entries in Strings/en-us/Resources.resw.\r\n                https://docs.microsoft.com/windows/uwp/app-resources/localize-strings-ui-manifest#refer-to-a-string-resource-identifier-from-xaml\r\n            \r\n                TODO: Update item icons by updating FontIcon.Glyph properties.\r\n                https://docs.microsoft.com/windows/apps/design/style/segoe-fluent-icons-font#icon-list\r\n                -->\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_LaunchWSL\" Tag=\"LaunchWSL\" SelectsOnInvoked=\"False\">\r\n                    <NavigationViewItem.Icon>\r\n                        <BitmapIcon ShowAsMonochrome=\"False\" UriSource=\"/Assets/wsl.ico\" />\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_OOBE\" Tag=\"OOBE\" SelectsOnInvoked=\"False\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xf133;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n                <NavigationViewItem x:Uid=\"Settings_Shell_About\" helpers:NavigationHelper.NavigateTo=\"WslSettings.ViewModels.Settings.AboutViewModel\">\r\n                    <NavigationViewItem.Icon>\r\n                        <FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&#xe946;\"/>\r\n                    </NavigationViewItem.Icon>\r\n                </NavigationViewItem>\r\n            </NavigationView.FooterMenuItems>\r\n            <NavigationView.HeaderTemplate>\r\n                <DataTemplate>\r\n                    <Grid>\r\n                        <TextBlock\r\n                            Text=\"{Binding}\"\r\n                            Style=\"{ThemeResource TitleTextBlockStyle}\" />\r\n                    </Grid>\r\n                </DataTemplate>\r\n            </NavigationView.HeaderTemplate>\r\n            <i:Interaction.Behaviors>\r\n                <behaviors:NavigationViewHeaderBehavior\r\n                    DefaultHeader=\"{x:Bind ((ContentControl)ViewModel.Selected).Content, Mode=OneWay}\">\r\n                    <behaviors:NavigationViewHeaderBehavior.DefaultHeaderTemplate>\r\n                        <DataTemplate>\r\n                            <Grid>\r\n                                <TextBlock\r\n                                    Text=\"{Binding}\"\r\n                                    Style=\"{ThemeResource TitleTextBlockStyle}\" />\r\n                            </Grid>\r\n                        </DataTemplate>\r\n                    </behaviors:NavigationViewHeaderBehavior.DefaultHeaderTemplate>\r\n                </behaviors:NavigationViewHeaderBehavior>\r\n            </i:Interaction.Behaviors>\r\n            <Grid>\r\n                <Frame x:Name=\"NavigationFrame\" />\r\n            </Grid>\r\n        </NavigationView>\r\n    </Grid>\r\n</Page>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Views/Settings/ShellPage.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Controls;\r\nusing Microsoft.UI.Xaml.Input;\r\n\r\nusing System.Diagnostics;\r\n\r\nusing Windows.System;\r\n\r\nusing WslSettings.Contracts.Services;\r\nusing WslSettings.Services;\r\nusing WslSettings.ViewModels;\r\n\r\nnamespace WslSettings.Views.Settings;\r\n\r\npublic sealed partial class ShellPage : Page\r\n{\r\n    private readonly Microsoft.UI.Dispatching.DispatcherQueue _dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();\r\n\r\n    private void RegisterNavigationService()\r\n    {\r\n        ViewModel.NavigationViewService.UnregisterEvents();\r\n        ViewModel.NavigationService.Frame = NavigationFrame;\r\n        ViewModel.NavigationViewService.Initialize(NavigationViewControl);\r\n    }\r\n\r\n    public ShellViewModel ViewModel\r\n    {\r\n        get;\r\n    }\r\n\r\n    public ShellPage(ShellViewModel viewModel)\r\n    {\r\n        ViewModel = viewModel;\r\n        InitializeComponent();\r\n\r\n        RegisterNavigationService();\r\n\r\n        // TODO: Set the title bar icon by updating /Assets/wsl.ico.\r\n        // A custom title bar is required for full window theme and Mica support.\r\n        // https://docs.microsoft.com/windows/apps/develop/title-bar?tabs=winui3#full-customization\r\n        App.MainWindow!.ExtendsContentIntoTitleBar = true;\r\n        App.MainWindow.SetTitleBar(AppTitleBar);\r\n        App.MainWindow.Activated += MainWindow_Activated;\r\n        AppTitleBarText.Text = \"Settings_AppDisplayName\".GetLocalized();\r\n        NavigationFrame.LostFocus += NavigationFrame_LostFocus;\r\n    }\r\n\r\n    private void OnLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\r\n    {\r\n        TitleBarHelper.UpdateTitleBar(App.MainWindow!, RequestedTheme);\r\n\r\n        KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.Left, VirtualKeyModifiers.Menu));\r\n        KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.GoBack));\r\n    }\r\n\r\n    private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)\r\n    {\r\n        App.AppTitlebar = AppTitleBarText as UIElement;\r\n        RegisterNavigationService();\r\n        if (ViewModel.NavigationService.Frame?.Content == null)\r\n        {\r\n            ViewModel.NavigationService.NavigateTo(typeof(ViewModels.Settings.MemAndProcViewModel).FullName!);\r\n        }\r\n    }\r\n\r\n    private void NavigationViewControl_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)\r\n    {\r\n        if (args.DisplayMode == NavigationViewDisplayMode.Compact || args.DisplayMode == NavigationViewDisplayMode.Minimal)\r\n        {\r\n            PaneToggleBtn.Visibility = Visibility.Visible;\r\n            AppTitleBar.Margin = new Thickness(48, 0, 0, 0);\r\n            AppTitleBarText.Margin = new Thickness(12, 0, 0, 0);\r\n        }\r\n        else\r\n        {\r\n            PaneToggleBtn.Visibility = Visibility.Collapsed;\r\n            AppTitleBar.Margin = new Thickness(16, 0, 0, 0);\r\n            AppTitleBarText.Margin = new Thickness(16, 0, 0, 0);\r\n        }\r\n    }\r\n\r\n    private void PaneToggleBtn_Click(object sender, RoutedEventArgs e)\r\n    {\r\n        NavigationViewControl.IsPaneOpen = !NavigationViewControl.IsPaneOpen;\r\n    }\r\n\r\n    private async void NavigationViewControl_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)\r\n    {\r\n        switch (args.InvokedItemContainer.Tag)\r\n        {\r\n            case \"LaunchWSL\":\r\n                var wslPath = Path.Combine(AppContext.BaseDirectory, \"..\", \"wsl.exe\");\r\n                await Task.Run(() => Process.Start(wslPath, \"--cd ~\"));\r\n                break;\r\n            case \"OOBE\":\r\n                await Task.Run(() =>\r\n                {\r\n                    _dispatcherQueue.TryEnqueue(() =>\r\n                    {\r\n                        App.GetService<IWindowService>().CreateOrGetWindow(IWindowService.WindowId.OOBEWindow).Activate();\r\n                    });\r\n                });\r\n                break;\r\n            default:\r\n                break;\r\n        }\r\n    }\r\n\r\n    private static KeyboardAccelerator BuildKeyboardAccelerator(VirtualKey key, VirtualKeyModifiers? modifiers = null)\r\n    {\r\n        var keyboardAccelerator = new KeyboardAccelerator() { Key = key };\r\n\r\n        if (modifiers.HasValue)\r\n        {\r\n            keyboardAccelerator.Modifiers = modifiers.Value;\r\n        }\r\n\r\n        keyboardAccelerator.Invoked += OnKeyboardAcceleratorInvoked;\r\n\r\n        return keyboardAccelerator;\r\n    }\r\n\r\n    private static void OnKeyboardAcceleratorInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)\r\n    {\r\n        var navigationService = App.GetService<INavigationService>();\r\n\r\n        var result = navigationService.GoBack();\r\n\r\n        args.Handled = result;\r\n    }\r\n\r\n    private void NavigationFrame_LostFocus(object sender, RoutedEventArgs e)\r\n    {\r\n        DispatcherQueue.TryEnqueue(() =>\r\n        {\r\n            var focused = FocusManager.GetFocusedElement(NavigationViewControl.XamlRoot);\r\n\r\n            // If focus is transferred to the selected page itself, do nothing\r\n            if (focused is not NavigationViewItem && focused is not NavigationView)\r\n            {\r\n                return;\r\n            }\r\n\r\n            // Restore focus to the selected navigation item\r\n            var selected = NavigationViewControl.SelectedItem;\r\n            var container = NavigationViewControl.ContainerFromMenuItem(selected) as Control;\r\n            container?.Focus(FocusState.Keyboard);\r\n        });\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Windows/MainWindow.xaml",
    "content": "﻿<windowex:WindowEx\r\n    x:Class=\"WslSettings.MainWindow\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:local=\"using:WslSettings\"\r\n    xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\r\n    xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\r\n    xmlns:windowex=\"using:WinUIEx\"\r\n    MinWidth=\"800\"\r\n    MinHeight=\"480\"\r\n    Width=\"1300\"\r\n    Height=\"800\"\r\n    Closed=\"Window_Closed\"\r\n    mc:Ignorable=\"d\">\r\n    <Window.SystemBackdrop>\r\n        <MicaBackdrop/>\r\n    </Window.SystemBackdrop>\r\n</windowex:WindowEx>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Windows/MainWindow.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Xaml;\r\nusing Windows.UI.ViewManagement;\r\n\r\nnamespace WslSettings;\r\n\r\npublic sealed partial class MainWindow : WindowEx\r\n{\r\n    private Microsoft.UI.Dispatching.DispatcherQueue dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();\r\n\r\n    private UISettings settings = new UISettings();\r\n\r\n    public MainWindow()\r\n    {\r\n        InitializeComponent();\r\n\r\n        AppWindow.SetIcon(Path.Combine(AppContext.BaseDirectory, \"Assets/wsl.ico\"));\r\n        Content = null;\r\n        Title = \"Settings_AppDisplayName\".GetLocalized();\r\n\r\n        // Theme change code picked from https://github.com/microsoft/WinUI-Gallery/pull/1239\r\n        settings.ColorValuesChanged += Settings_ColorValuesChanged; // cannot use FrameworkElement.ActualThemeChanged event\r\n    }\r\n\r\n    // this handles updating the caption button colors correctly when windows system theme is changed\r\n    // while the app is open\r\n    private void Settings_ColorValuesChanged(UISettings sender, object args)\r\n    {\r\n        // This calls comes off-thread, hence we will need to dispatch it to current app's thread\r\n        dispatcherQueue.TryEnqueue(() =>\r\n        {\r\n            TitleBarHelper.ApplySystemThemeToCaptionButtons(this);\r\n        });\r\n    }\r\n\r\n    public void CloseHiddenWindow()\r\n    {\r\n        if (!Visible)\r\n        {\r\n            Close();\r\n        }\r\n    }\r\n\r\n    private void Window_Closed(object sender, WindowEventArgs args)\r\n    {\r\n        if (App.OOBEWindow == null)\r\n        {\r\n            App.MainWindow = null;\r\n            settings.ColorValuesChanged -= Settings_ColorValuesChanged;\r\n        }\r\n        else\r\n        {\r\n            args.Handled = true;\r\n            this.Hide();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/windows/wslsettings/Windows/OOBEWindow.xaml",
    "content": "﻿<windowex:WindowEx\r\n    x:Class=\"WslSettings.OOBEWindow\"\r\n    xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\r\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\r\n    xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\r\n    xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\r\n    xmlns:windowex=\"using:WinUIEx\"\r\n    MinWidth=\"600\"\r\n    MinHeight=\"600\"\r\n    Closed=\"Window_Closed\"\r\n    mc:Ignorable=\"d\">\r\n    <Window.SystemBackdrop>\r\n        <MicaBackdrop/>\r\n    </Window.SystemBackdrop>\r\n</windowex:WindowEx>\r\n"
  },
  {
    "path": "src/windows/wslsettings/Windows/OOBEWindow.xaml.cs",
    "content": "﻿// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing Microsoft.UI.Windowing;\r\nusing Microsoft.UI.Xaml;\r\nusing Microsoft.UI.Xaml.Input;\r\nusing System.Runtime.InteropServices;\r\nusing Windows.Graphics;\r\nusing Windows.System;\r\nusing WinUIEx.Messaging;\r\nusing Windows.UI.ViewManagement;\r\nusing Windows.UI.WindowManagement;\r\nusing System.Runtime.Intrinsics.Arm;\r\n\r\nnamespace WslSettings;\r\n\r\n/// <summary>\r\n/// An empty window that can be used on its own or navigated to within a Frame.\r\n/// </summary>\r\npublic sealed partial class OOBEWindow : WindowEx, IDisposable\r\n{\r\n    [DllImport(\"User32.dll\")]\r\n    internal static extern int GetDpiForWindow(IntPtr hwnd);\r\n\r\n    private const int ExpectedWidth = 1100;\r\n    private const int ExpectedHeight = 700;\r\n    private const int DefaultDPI = 96;\r\n    private int currentDPI;\r\n    private IntPtr hWnd;\r\n    private Microsoft.UI.Dispatching.DispatcherQueue dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();\r\n    private UISettings settings = new UISettings();\r\n    private WindowMessageMonitor msgMonitor;\r\n    private bool disposedValue;\r\n\r\n    public OOBEWindow()\r\n    {\r\n        InitializeComponent();\r\n\r\n        AppWindow.SetIcon(Path.Combine(AppContext.BaseDirectory, \"Assets/wsl.ico\"));\r\n        Content = null;\r\n        Title = \"Settings_OOBEDisplayName\".GetLocalized();\r\n\r\n        // Theme change code picked from https://github.com/microsoft/WinUI-Gallery/pull/1239\r\n        settings.ColorValuesChanged += Settings_ColorValuesChanged; // cannot use FrameworkElement.ActualThemeChanged event\r\n        settings.TextScaleFactorChanged += Settings_TextScaleFactorChanged;\r\n\r\n        WindowManager.Get(this).IsMinimizable = false;\r\n        WindowManager.Get(this).IsMaximizable = false;\r\n\r\n        hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);\r\n        currentDPI = GetDpiForWindow(hWnd);\r\n        ResizeWindow();\r\n\r\n        SizeChanged += Window_SizeChanged;\r\n\r\n        msgMonitor = new WindowMessageMonitor(this);\r\n        msgMonitor.WindowMessageReceived += (_, e) =>\r\n        {\r\n            const int WM_NCLBUTTONDBLCLK = 0x00A3;\r\n            if (e.Message.MessageId == WM_NCLBUTTONDBLCLK)\r\n            {\r\n                // Disable double click on title bar to maximize window\r\n                e.Result = 0;\r\n                e.Handled = true;\r\n            }\r\n        };\r\n\r\n        this.Activated += OnWindowActivated;\r\n    }\r\n\r\n    // this handles updating the caption button colors correctly when windows system theme is changed\r\n    // while the app is open\r\n    private void Settings_ColorValuesChanged(UISettings sender, object args)\r\n    {\r\n        // This calls comes off-thread, hence we will need to dispatch it to current app's thread\r\n        dispatcherQueue.TryEnqueue(() =>\r\n        {\r\n            TitleBarHelper.ApplySystemThemeToCaptionButtons(this);\r\n        });\r\n    }\r\n\r\n    // This handles text scaling changes for accessibility\r\n    private void Settings_TextScaleFactorChanged(UISettings sender, object args)\r\n    {\r\n        // This calls comes off-thread, hence we will need to dispatch it to current app's thread\r\n        dispatcherQueue.TryEnqueue(() =>\r\n        {\r\n            ResizeWindow();\r\n        });\r\n    }\r\n\r\n    private void Window_SizeChanged(object sender, WindowSizeChangedEventArgs args)\r\n    {\r\n        var dpi = GetDpiForWindow(hWnd);\r\n        if (currentDPI != dpi)\r\n        {\r\n            // Reacting to a DPI change. Should not cause a resize -> sizeChanged loop.\r\n            currentDPI = dpi;\r\n            ResizeWindow();\r\n        }\r\n    }\r\n\r\n    private void Window_Closed(object sender, WindowEventArgs args)\r\n    {\r\n        App.OOBEWindow = null;\r\n        App.MainWindow?.CloseHiddenWindow();\r\n        Dispose();\r\n    }\r\n\r\n    private void ResizeWindow()\r\n    {\r\n        float dpiScalingFactor = (float)currentDPI / DefaultDPI;\r\n        float textScalingFactor = (float)settings.TextScaleFactor;\r\n\r\n        // Combine DPI scaling and text scaling for accessibility\r\n        float combinedScalingFactor = dpiScalingFactor * textScalingFactor;\r\n\r\n        int width = (int)(ExpectedWidth * combinedScalingFactor);\r\n        int height = (int)(ExpectedHeight * combinedScalingFactor);\r\n\r\n        SizeInt32 size;\r\n        size.Width = width;\r\n        size.Height = height;\r\n        AppWindow.Resize(size);\r\n    }\r\n\r\n    private void Dispose(bool disposing)\r\n    {\r\n        if (!disposedValue)\r\n        {\r\n            msgMonitor?.Dispose();\r\n            settings.ColorValuesChanged -= Settings_ColorValuesChanged;\r\n            settings.TextScaleFactorChanged -= Settings_TextScaleFactorChanged;\r\n            this.Activated -= OnWindowActivated;\r\n            if (this.Content is Microsoft.UI.Xaml.Controls.Page page)\r\n            {\r\n                page.KeyboardAccelerators.Clear();\r\n            }\r\n\r\n            disposedValue = true;\r\n        }\r\n    }\r\n\r\n    public void Dispose()\r\n    {\r\n        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method\r\n        Dispose(disposing: true);\r\n        GC.SuppressFinalize(this);\r\n    }\r\n\r\n    private void OnWindowActivated(object sender, WindowActivatedEventArgs args)\r\n    {\r\n        if (args.WindowActivationState != WindowActivationState.Deactivated && this.Content != null)\r\n        {\r\n            this.Activated -= OnWindowActivated;\r\n\r\n            if (this.Content is Microsoft.UI.Xaml.Controls.Page page)\r\n            {\r\n                var escapeAccelerator = new KeyboardAccelerator() { Key = VirtualKey.Escape };\r\n                escapeAccelerator.Invoked += OnCloseKeyboardAcceleratorInvoked;\r\n                page.KeyboardAccelerators.Add(escapeAccelerator);\r\n            }\r\n        }\r\n    }\r\n\r\n    private void OnCloseKeyboardAcceleratorInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)\r\n    {\r\n        Close();\r\n        args.Handled = true;\r\n    }\r\n}"
  },
  {
    "path": "src/windows/wslsettings/app.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\r\n  <assemblyIdentity version=\"1.0.0.0\" name=\"WslSettings.app\"/>\r\n    <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\r\n      <application>\r\n        <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\r\n      </application>\r\n    </compatibility>\r\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\r\n    <windowsSettings>\r\n      <dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/PM</dpiAware>\r\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness>\r\n    </windowsSettings>\r\n  </application>\r\n</assembly>\r\n"
  },
  {
    "path": "src/windows/wslsettings/directory.build.targets.in",
    "content": "<Project>\r\n  <ItemGroup>\r\n    <FrameworkReference Update=\"Microsoft.Windows.SDK.NET.Ref\" RuntimeFrameworkVersion=\"${WINDOWS_SDK_DOTNET_VERSION}\" TargetingPackVersion=\"${WINDOWS_SDK_DOTNET_VERSION}\" />\r\n    <!-- The below can be removed when the OneBranch pipeline container image is updated to use a newer version of the dotnet SDK -->\r\n    <WindowsSdkSupportedTargetPlatformVersion Include=\"10.0.26100.0\" Exclude=\"@(WindowsSdkSupportedTargetPlatformVersion)\" WindowsSdkPackageVersion=\"10.0.26100.0\" MinimumNETVersion=\"6.0\" />\r\n    <SdkSupportedTargetPlatformVersion Include=\"10.0.26100.0\" Exclude=\"@(SdkSupportedTargetPlatformVersion)\"/>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "src/windows/wslsettings/properties/AssemblyInfo.cs.in",
    "content": "// Copyright (C) Microsoft Corporation. All rights reserved.\r\n\r\nusing System;\r\nusing System.Reflection;\r\n\r\n[assembly: System.Reflection.AssemblyTitleAttribute(\"Windows Subsystem for Linux Settings\")]\r\n[assembly: System.Reflection.AssemblyDescriptionAttribute(\"$Windows Subsystem for Linux Settings\")]\r\n[assembly: System.Reflection.AssemblyProductAttribute(\"Windows Subsystem for Linux\")]\r\n[assembly: System.Reflection.AssemblyCompanyAttribute(\"Microsoft Corporation\")]\r\n[assembly: System.Reflection.AssemblyConfigurationAttribute(\"\")]\r\n[assembly: System.Reflection.AssemblyCopyrightAttribute(\"Copyright (c) Microsoft Corporation\")]\r\n[assembly: System.Reflection.AssemblyTrademarkAttribute(\"\")]\r\n[assembly: System.Reflection.AssemblyCultureAttribute(\"\")]\r\n[assembly: System.Reflection.AssemblyFileVersionAttribute(\"${PACKAGE_VERSION}\")]\r\n[assembly: System.Reflection.AssemblyInformationalVersionAttribute(\"${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_REVISION}\")]\r\n[assembly: System.Reflection.AssemblyVersionAttribute(\"${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_REVISION}\")]\r\n[assembly: System.Runtime.Versioning.TargetPlatformAttribute(\"${WINDOWS_TARGET_PLATFORM_VERSION}\")]\r\n[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute(\"${WINDOWS_TARGET_PLATFORM_MIN_VERSION}\")]"
  },
  {
    "path": "storebroker/PDPs/cs-CZ/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->subsystém</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->Linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Subsystém Windows pro Linux umožňuje vývojářům spouštět prostředí GNU/Linux - včetně většiny nástrojů příkazového řádku, nástrojů a aplikací - přímo ve Windows, bez úprav, bez režie tradičního virtuálního počítače nebo nastavení duálního spouštění.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Grafické linuxové aplikace s WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL se používá s Terminál Windows\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL podporuje širokou škálu linuxových distribucí\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/cs-cz/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/da-DK/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->undersystem</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Windows-undersystem til Linux giver udviklere mulighed for at køre et GNU/Linux-miljø – herunder de fleste kommandolinjeværktøjer, hjælpeprogrammer og programmer – direkte på Windows, uændrede, uden omkostninger ved en traditionel virtuel maskine eller dual boot-konfiguration.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Grafiske Linux-programmer med WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL bruges med Windows Terminal\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL understøtter en lang række Linux-distributioner\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/da-dk/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/de-DE/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->Windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->Teilsystem</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->Linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->WSL</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Windows Subsystem für Linux ermöglicht Entwicklern das Ausführen einer GNU/Linux-Umgebung – einschließlich der meisten Befehlszeilentools, Hilfsprogramme und Anwendungen – direkt unter Windows, unverändert, ohne den Mehraufwand einer herkömmlichen virtuellen Maschine oder einem dualen Boot-Setup.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Grafische Linux-Anwendungen mit WSL-\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->Mit Windows-Terminal\n verwendetes WSL    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL unterstützt eine Vielzahl von Linux-Distributionen\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/de-de/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/en-GB/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->subsystem</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Windows Subsystem for Linux lets developers run a GNU/Linux environment -- including most command-line tools, utilities, and applications -- directly on Windows, unmodified, without the overhead of a traditional virtual machine or dual boot setup.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Graphical Linux applications with WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL used with Windows Terminal\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL supports a wide variety of Linux distributions\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/en-us/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/en-us/PDP.xml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\r\n  <AppStoreName _locID=\"App_AppStoreName\">\r\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\r\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\r\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\r\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\r\n    <!-- Windows Subsystem for Linux Preview -->\r\n  </AppStoreName>\r\n  <Keywords>\r\n    <!-- Valid length: 30 character limit, up to 7 elements -->\r\n    <Keyword _locID=\"App_keyword1\">\r\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\r\n    <Keyword _locID=\"App_keyword2\">\r\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->subsystem</Keyword>\r\n    <Keyword _locID=\"App_keyword3\">\r\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\r\n    <Keyword _locID=\"App_keyword4\">\r\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\r\n  </Keywords>\r\n  <Description _locID=\"App_Description\">\r\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Windows Subsystem for Linux lets developers run a GNU/Linux environment -- including most command-line tools, utilities, and applications -- directly on Windows, unmodified, without the overhead of a traditional virtual machine or dual boot setup.</Description>\r\n  <ShortDescription _locID=\"App_ShortDescription\">\r\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\r\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\r\n  </ShortDescription>\r\n  <ShortTitle _locID=\"App_ShortTitle\">\r\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\r\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\r\n  <SortTitle _locID=\"App_SortTitle\">\r\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\r\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\r\n  </SortTitle>\r\n  <VoiceTitle _locID=\"App_VoiceTitle\">\r\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\r\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\r\n  </VoiceTitle>\r\n  <DevStudio _locID=\"App_DevStudio\">\r\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\r\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\r\n  </DevStudio>\r\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\r\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\r\n  </ReleaseNotes>\r\n  <ScreenshotCaptions>\r\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\r\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\r\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\r\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Graphical Linux applications with WSL\r\n    </Caption>\r\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\r\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL used with Windows Terminal\r\n    </Caption>\r\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\r\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL supports a wide variety of Linux distributions\r\n    </Caption>\r\n  </ScreenshotCaptions>\r\n  <AdditionalAssets>\r\n    <!-- Valid elements:-->\r\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\r\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\r\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\r\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\r\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\r\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\r\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\r\n    <Icon FileName=\"WSLAppList 1.png\" />\r\n  </AdditionalAssets>\r\n  <Trailers>\r\n    <!-- Maximum number of trailers permitted: 15 -->\r\n  </Trailers>\r\n  <AppFeatures>\r\n    <!-- Valid length: 200 character limit, up to 20 elements -->\r\n    <AppFeature _locID=\"App_feature1\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature2\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature3\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature4\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature5\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature6\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature7\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature8\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature9\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature10\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature11\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature12\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature13\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature14\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature15\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature16\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature17\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature18\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature19\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\r\n    </AppFeature>\r\n    <AppFeature _locID=\"App_feature20\">\r\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\r\n    </AppFeature>\r\n  </AppFeatures>\r\n  <RecommendedHardware>\r\n    <!-- Valid length: 200 character limit, up to 11 elements -->\r\n  </RecommendedHardware>\r\n  <MinimumHardware>\r\n    <!-- Valid length: 200 character limit, up to 11 elements -->\r\n  </MinimumHardware>\r\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\r\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\r\n  </CopyrightAndTrademark>\r\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\r\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\r\n  </AdditionalLicenseTerms>\r\n  <WebsiteURL _locID=\"App_WebsiteURL\">\r\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\r\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\r\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\r\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\r\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/en-us/privacystatement</PrivacyPolicyURL>\r\n</ProductDescription>\r\n"
  },
  {
    "path": "storebroker/PDPs/es-ES/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->Windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->subsistema</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Subsistema de Windows para Linux permite a los desarrolladores ejecutar un entorno GNU/Linux , que incluye la mayoría de las herramientas, utilidades y aplicaciones de la línea de comandos, directamente en Windows, sin modificar, sin la sobrecarga de una máquina virtual tradicional o una instalación de arranque dual.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Aplicaciones gráficas de Linux con WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL usado con Terminal Windows\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL admite una amplia variedad de distribuciones de Linux\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/es-es/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/fi-FI/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->alijärjestelmä</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Windows-alijärjestelmä Linuxille -teknologian avulla kehittäjät voivat suorittaa GNU/Linux-ympäristön, mukaan lukien useimmat komentorivityökalut, apuohjelmat ja sovellukset, suoraan Windowsissa muokkaamattomana ja ilman perinteisen virtuaalikoneen tai kaksoiskäynnistysasennuksen aiheuttamaa kuormitusta.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Graafiset Linux -sovellukset WSL:llä\n</Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL:ää käytetään Windows-päätteen\n kanssa</Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL tukee erilaisia Linux-jakeluita\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/en-us/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/fr-FR/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->sous-système</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Le Sous-système Windows pour Linux permet aux développeurs d’exécuter un environnement GNU/Linux (y compris la plupart des outils en ligne de commande, utilitaires et applications) directement sur Windows, sans modification, sans surcharge d’une machine virtuelle traditionnelle ou de la configuration d’un double démarrage.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Applications graphiques Linux avec WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL utilisé avec Terminal Windows\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL prend en charge une grande variété de distributions Linux\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/fr-fr/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/hu-HU/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->alrendszer</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->A Linuxos Windows-alrendszer lehetővé teszi, hogy a fejlesztők GNU/Linux környezetet futtassanak – beleértve a legáltalánosabb parancssori eszközöket, segédprogramokat és alkalmazásokat – közvetlenül a Windowsban, módosítások nélkül, a hagyományos virtuális gépek vagy kettős rendszerindítási beállítások okozta többletterhelés nélkül.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Grafikus Linux-alkalmazások WSL-lel\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->Windows terminállal használt WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->A WSL számos Linux-disztribúciót támogat\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/hu-hu/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/it-IT/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->sottosistema</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->sottosistema Windows per Linux consente agli sviluppatori di eseguire un ambiente GNU/Linux, inclusi la maggior parte degli strumenti da riga di comando, le utilità e le applicazioni, direttamente in Windows, senza modifiche, senza il sovraccarico di una macchina virtuale tradizionale o di una configurazione a doppio avvio.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Applicazioni Linux grafiche con WSL\n</Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL usato con Terminale Windows\n</Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL supporta un'ampia gamma di distribuzioni Linux\n</Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/it-it/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/ja-JP/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->Windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->サブシステム</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->Linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->WSL</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Linux 用 Windows サブシステムを使用すると、開発者は、ほとんどのコマンドライン ツール、ユーティリティ、アプリケーションを含む GNU/Linux 環境を、従来の仮想マシンやデュアル ブート セットアップのオーバーヘッドなしに、変更せずに Windows 上で直接実行できます。</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->WSL を使用したグラフィカル Linux アプリケーション\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->Windows ターミナルで使用される WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL はさまざまな Linux ディストリビューションをサポート\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/ja-jp/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/ko-KR/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->Windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->하위 시스템</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Linux용 Windows 하위 시스템을 사용하면 개발자가 대부분의 명령줄 도구, 유틸리티 및 응용 프로그램을 포함한 GNU/Linux 환경을 수정하지 않고 기존 가상 머신이나 이중 부팅 설정의 오버헤드 없이 Windows에서 직접 실행할 수 있습니다.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->WSL이 있는 그래픽 Linux 애플리케이션\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->Windows 터미널과 함께 사용되는 WSL\n   </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL은 다양한 Linux 배포\n     지원합니다.</Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/ko-kr/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/nb-NO/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->delsystem</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Med Windows-undersystem for Linux kan utviklere kjøre et GNU/Linux-miljø – inkludert de fleste kommandolinjeverktøy, verktøy og programmer – direkte på Windows, uendret, uten kostnadene ved en tradisjonell virtuell maskin eller konfigurasjon av dobbeltoppstart.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Grafiske Linux-programmer med WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL brukes med Windows Terminal\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL støtter en rekke Linux-distribusjoner\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/nb-no/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/nl-NL/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->subsysteem</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Met Windows-subsysteem voor Linux kunnen ontwikkelaars een GNU/Linux-omgeving uitvoeren, inclusief de meeste opdrachtregelprogramma's, hulpprogramma's en toepassingen, rechtstreeks in Windows, ongewijzigd, zonder de overhead van een traditionele virtuele machine of dualboot-installatie.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Grafische Linux-toepassingen met WSL\n   </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL gebruikt met Windows Terminal\n   </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL ondersteunt een groot aantal Linux-distributies\n   </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/nl-nl/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/pl-PL/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->podsystem</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Podsystem Windows dla systemu Linux (WSL) umożliwia deweloperom uruchamianie środowiska GNU/Linux — a w tym większości narzędzi wiersza polecenia, narzędzi i aplikacji — bezpośrednio w systemie Windows, bez modyfikacji, bez obciążenia tradycyjnej maszyny wirtualnej lub konfiguracji uruchomienia jednego z kilku systemów operacyjnych.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Graficzne aplikacje systemu Linux z podsystemem WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->Podsystem WSL używany z aplikacją Terminal Windows\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->Podsystem WSL obsługuje szeroką gamę dystrybucji systemu Linux\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/pl-pl/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/pt-BR/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->subsistema</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->O Subsistema do Windows para Linux permite que os desenvolvedores executem um ambiente GNU/Linux, incluindo a maioria das ferramentas, utilitários e aplicativos de linha de comando, diretamente no Windows, sem modificações, sem a sobrecarga de uma máquina virtual tradicional ou configuração de inicialização dupla.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Aplicativos Gráficos do Linux com WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL usado com Terminal do Windows\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->O WSL dá suporte a uma ampla variedade de distribuições do Linux\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/pt-br/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/pt-PT/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->subsistema</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->O Subsistema Windows para Linux permite aos programadores executar um ambiente GNU/Linux, incluindo a maioria das ferramentas de linha de comandos, utilitários e aplicações, diretamente no Windows, sem modificação, sem a sobrecarga de uma máquina virtual tradicional ou configuração de arranque duplo.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Aplicações gráficas do Linux com o WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL utilizado com o Terminal do Windows\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->O WSL suporta uma grande variedade de distribuições do Linux\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/pt-pt/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/qps-ploc/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->ωϊŉďõŵš !!</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->şúвšуśтєм !!!</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->ℓįйű× !</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->щšļ </Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Ŵїŋδõшš Şūвśÿşτєм ƒǿѓ £įňϋ× ļзťś ðενεℓοφĕŗś ŗυⁿ ã ĠЙŰ/Ŀîⁿϋх êпνіґōймεпţ -- іпсļűðїпğ möѕт сömmåńδ-ĺĩņę τőôℓš, ûŧĭℓįŧΐєş, âήď аφφℓϊçªťîóпś -- đìяεćŧℓý бή Шΐŉđöώŝ, ŭńmôďîƒίέδ, ώîŧћóúŧ ťнэ øν℮řħēåð ôƒ à тґªδįţїòŋåĺ νιґтŭάĺ мäćђīñз όř δûǻℓ ьŏǿť şěтųр. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->ЩŜĿ </ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Ġřǻφнĭсǻℓ Ĺϊňù× àррļîсáŧίöŉŝ шїтн ẄŠ₤ !!! !!! !!! !!\n     !</Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->ШŠĿ ûşėď ώìţħ Ẃíпďōẅѕ Τĕѓmìňåľ !!! !!! !!!\n     !</Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->ШŚ₤ šūφрǿгťş ǻ ώїδě νåяįётý őƒ Łîηůж δїѕţŕĩьûτїθⁿş !!! !!! !!! !!! !!!\n     !</Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->ђтťρš://āķâ.мŝ/ẃѕļ2 !!! !!!</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->ђŧтρś://ğīţнųъ.çоm/Μΐčѓσşőƒŧ/ẀŜĻ !!! !!! !!! </SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->ĥŧŧφş://ряіνдćý.мίςяőŝöƒţ.¢θm/℮ŉ-ûś/рґіνãсÿšŧáţéмэиţ !!! !!! !!! !!! !!! </PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/qps-ploca/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->ẅίлδôщş !!</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->ѕųъşўŝťèм !!!</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->ŀϊηüж !</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->ωŝŀ </Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Шìпďóẅš Ѕϋъśγşτэм ƒǿŗ Ļíήµх ļëтš ďéνёľσρêŗś ѓΰñ ą ĢŇÚ/Ľïñύж ęŉνїяǿñмèŋт -- ìпčℓũðїйģ mσŝŧ ćŏммâŉδ-ĺíήε ţόőļѕ, úţíŀιтīëŝ, αпď αрρłįċāтĩõňŝ -- δΐř℮¢тŀў όή Ẃîηđǿшś, ūпmŏđîƒієđ, ωìťħθŭт τћз őνέŗћеåď ŏƒ α τŕаδїťϊøŉαĺ νïяτŭàł мǻ¢ħįńê оŗ đμάℓ ьőоŧ šėŧµφ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->ŴŜŁ </ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Ĝядφћíçäŀ Ŀįňůх ãрφĺìĉàтισήš ẁìτћ ẂЅŁ !!! !!! !!! !!\n     !</Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->ЩŞ£ ûşěđ ẁīťћ Ŵìиďóщš Тèгмíŉãℓ !!! !!! !!!\n     !</Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->ẂŞĹ ѕύррθŕţŝ ą ẁιďё νąřιėтÿ бƒ Łїńμж ďïšŧгîьυŧіóлš !!! !!! !!! !!! !!!\n     !</Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->ħттφŝ://ąķд.mš/ẁşł2 !!! !!!</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->ħťτрš://ğΐťћџъ.¢θm/Μì¢ŕοŝθƒţ/ŴŞĿ !!! !!! !!! </SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->ђττрŝ://φŗΐνã¢ў.мϊçѓόšõƒť.ćбм/ĕň-ùŝ/ряíνåçŷśτǻŧεмэπт !!! !!! !!! !!! !!! </PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/qps-plocm/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->ẁίňðοωѕ !!</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->ŝūъŝуşťęм !!!</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->łïйϋ× !</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->ŵšℓ </Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Щΐñďøωѕ Śџъşÿśţεм ƒσř Ļįпū× łėŧş δĕνеłǿрέŗŝ гúл â ĢЛŲ/Ĺίηΰх еήνīяòńмёñт -- ĩлčłμďîήĝ mǿѕт çømмäήđ-ℓϊπе ŧőøľѕ, џťįℓĩтìĕś, аňď àρφℓιćáţϊóήŝ -- ðїѓє¢ţľÿ όň Ŵΐⁿδŏώŝ, µήmоďіƒîєδ, ωíŧђθμť τħė õνзѓнёäď õƒ α тřάđίţĭθйäŀ νĩŗţũάļ mдçĥΐйё őř ðΰåł ъõòť ŝеťũр. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->ẂЅĻ </ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Ĝѓªρħįĉăļ Ĺίńϋж ǻрφĺïćąτιòⁿѕ ẁίŧђ ŴŚĽ !!! !!! !!! !!\n     !</Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->ẂŠĽ υšέđ ωїŧћ Ẁĭņďòώŝ Τέямīŋªľ !!! !!! !!!\n     !</Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->ẀŠŁ śũφρóřţś â ŵįđê νářīєţŷ οƒ Ŀīńù× ďįѕŧřіьцţιôпѕ !!! !!! !!! !!! !!!\n     !</Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->ħтţφŝ://аĸа.mš/ẁşľ2 !!! !!!</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->ħťтрѕ://ģìτнΰь.ċőм/Мї¢ŗõşōƒτ/ŴŞ£ !!! !!! !!! </SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->ћτťφş://φřįνàςγ.mīćřόśőƒτ.¢őm/ëň-ůś/φŗĭνâċýŝŧäţέмэпτ !!! !!! !!! !!! !!! </PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/ru-RU/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->подсистема</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Подсистема Windows для Linux позволяет разработчикам запускать среду GNU/Linux (включая большинство программ командной строки, служебных программ и приложений) непосредственно в Windows без изменений, без накладных расходов традиционной виртуальной машины или настройки двойной загрузки.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Графические приложения Linux с WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->Подсистема WSL, используемая с Терминалом Windows\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL поддерживает широкий спектр дистрибутивов Linux\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/ru-ru/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/sv-SE/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->delsystem</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Windows-undersystem för Linux gör att utvecklare kan köra en GNU/Linux-miljö, inklusive de flesta kommandoradsverktyg, verktyg och program, direkt i Windows, oförändrade, utan att behöva använda en traditionell virtuell dator eller installation med dubbel start.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->Grafiska Linux-program med WSL-\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->WSL som används med Windows-terminal\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL stöder en mängd olika Linux-distributioner\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/sv-se/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/tr-TR/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->alt sistem</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Linux için Windows Alt Sistemi, geliştiricilerin bir GNU/Linux ortamını -- çoğu komut satırı aracı, yardımcı program ve uygulama dahil -- doğrudan Windows üzerinde, değiştirilmeden, geleneksel bir sanal makine veya çift önyükleme kurulumunun ek yükü olmadan çalıştırmasını sağlar.</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->WSL ile grafiksel Linux uygulamaları\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->Windows Terminali ile kullanılan WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL, çok çeşitli Linux dağıtımlarını destekler\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/en-us/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/zh-CN/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->子系统</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->Linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->WSL</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->适用于 Linux 的 Windows 子系统使开发人员可以直接在 Windows 上运行未经修改的 GNU/Linux 环境，包括大多数命令行工具、实用工具和应用程序，而无需传统虚拟机或双引导设置的开销。</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->通过 WSL 使用图形 Linux 应用程序\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->通过 Windows 终端\n 使用 WSL    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL 支持多种 Linux 分发\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/zh-cn/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/PDPs/zh-TW/PDP.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ProductDescription language=\"en-us\" xmlns=\"http://schemas.microsoft.com/appx/2012/ProductDescription\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xml:lang=\"en-us\" Release=\"\" FallbackLanguage=\"en-us\">\n  <AppStoreName _locID=\"App_AppStoreName\">\n    <!-- This is optional.  AppStoreName is typically extracted from your package's AppxManifest DisplayName property. -->\n    <!-- Uncomment (and localize) this Store name if your application package does not contain a localization for the DisplayName in this language. -->\n    <!-- Leaving this uncommented for a language that your application package DOES contain a DisplayName for will result in a submission failure with the API. -->\n    <!-- _locComment_text=\"{MaxLength=200} App AppStoreName\" -->\n    <!-- Windows Subsystem for Linux Preview -->\n  </AppStoreName>\n  <Keywords>\n    <!-- Valid length: 30 character limit, up to 7 elements -->\n    <Keyword _locID=\"App_keyword1\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 1\" -->Windows</Keyword>\n    <Keyword _locID=\"App_keyword2\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 2\" -->子系統</Keyword>\n    <Keyword _locID=\"App_keyword3\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 3\" -->Linux</Keyword>\n    <Keyword _locID=\"App_keyword4\">\n      <!-- _locComment_text=\"{MaxLength=30} App keyword 4\" -->wsl</Keyword>\n  </Keywords>\n  <Description _locID=\"App_Description\">\n    <!-- _locComment_text=\"{MaxLength=10000} App Description\" -->Windows 子系統 Linux 版可讓開發人員直接在 Windows 上執行 GNU/Linux 環境 (包括大部分的命令列工具、公用程式和應用程式)，而不需要傳統虛擬機器或雙重開機設定的額外負荷。</Description>\n  <ShortDescription _locID=\"App_ShortDescription\">\n    <!-- Only used for games. This description appears in the Information section of the Game Hub on Xbox One, and helps customers understand more about your game. -->\n    <!-- _locComment_text=\"{MaxLength=500} App ShortDescription\" -->\n  </ShortDescription>\n  <ShortTitle _locID=\"App_ShortTitle\">\n    <!-- A shorter version of your product's name. If provided, this shorter name may appear in various places on Xbox One (during installation, in Achievements, etc.) in place of the full title of your product. -->\n    <!-- _locComment_text=\"{MaxLength=50} App ShortTitle\" -->WSL</ShortTitle>\n  <SortTitle _locID=\"App_SortTitle\">\n    <!-- If your product could be alphabetized in different ways, you can enter another version here. This may help customers find the product more quickly when searching. -->\n    <!-- _locComment_text=\"{MaxLength=255} App SortTitle\" -->\n  </SortTitle>\n  <VoiceTitle _locID=\"App_VoiceTitle\">\n    <!-- An alternate name for your product that, if provided, may be used in the audio experience on Xbox One when using Kinect or a headset. -->\n    <!-- _locComment_text=\"{MaxLength=255} App VoiceTitle\" -->\n  </VoiceTitle>\n  <DevStudio _locID=\"App_DevStudio\">\n    <!-- Specify this value if you want to include a \"Developed by\" field in the listing. (The \"Published by\" field will list the publisher display name associated with your account, whether or not you provide a devStudio value.) -->\n    <!-- _locComment_text=\"{MaxLength=255} App DevStudio\" -->\n  </DevStudio>\n  <ReleaseNotes _locID=\"App_ReleaseNotes\">\n    <!-- _locComment_text=\"{MaxLength=1500} App Release Note\" -->\n  </ReleaseNotes>\n  <ScreenshotCaptions>\n    <!-- Valid length: 200 character limit, up to 9 elements per platform -->\n    <!-- Valid attributes: any of DesktopImage, MobileImage, XboxImage, SurfaceHubImage, and HoloLensImage -->\n    <Caption DesktopImage=\"WSLTerminalWSLgAsset-Export.png\" _locID=\"App_caption1\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 1\" -->具有 WSL 的圖形 Linux 應用程式\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalPanes-Export.png\" _locID=\"App_caption2\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 2\" -->與 Windows 終端機搭配使用的 WSL\n    </Caption>\n    <Caption DesktopImage=\"WSLTerminalAssetSettingsColour-Export.png\" _locID=\"App_caption3\">\n      <!-- _locComment_text=\"{MaxLength=200} Screenshot caption 3\" -->WSL 支援各種 Linux 發佈\n    </Caption>\n  </ScreenshotCaptions>\n  <AdditionalAssets>\n    <!-- Valid elements:-->\n    <!--   HeroImage414x180, HeroImage846x468, HeroImage558x756, HeroImage414x468, HeroImage558x558, HeroImage2400x1200,-->\n    <!--   ScreenshotWXGA, ScreenshotHD720, ScreenshotWVGA, Doublewide, Panoramic, Square,-->\n    <!--   SmallMobileTile, SmallXboxLiveTile, LargeMobileTile, LargeXboxLiveTile, Tile,-->\n    <!--   DesktopIcon, Icon (use this value for the 1:1 300x300 pixels logo), AchievementIcon,-->\n    <!--   ChallengePromoIcon, RewardDisplayIcon, Icon150X150, Icon71X71,-->\n    <!--   BoxArt, BrandedKeyArt, PosterArt, FeaturedPromotionalArt, PromotionalArt16x9, TitledHeroArt-->\n    <!-- There is no content for any of these elements, just a single attribute called FileName. -->\n    <Icon FileName=\"WSLAppList 1.png\"/>\n  </AdditionalAssets>\n  <Trailers>\n    <!-- Maximum number of trailers permitted: 15 -->\n  </Trailers>\n  <AppFeatures>\n    <!-- Valid length: 200 character limit, up to 20 elements -->\n    <AppFeature _locID=\"App_feature1\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 1\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature2\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 2\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature3\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 3\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature4\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 4\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature5\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 5\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature6\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 6\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature7\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 7\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature8\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 8\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature9\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 9\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature10\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 10\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature11\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 11\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature12\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 12\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature13\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 13\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature14\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 14\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature15\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 15\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature16\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 16\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature17\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 17\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature18\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 18\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature19\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 19\" -->\n    </AppFeature>\n    <AppFeature _locID=\"App_feature20\">\n      <!-- _locComment_text=\"{MaxLength=200} App Feature 20\" -->\n    </AppFeature>\n  </AppFeatures>\n  <RecommendedHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </RecommendedHardware>\n  <MinimumHardware>\n    <!-- Valid length: 200 character limit, up to 11 elements -->\n  </MinimumHardware>\n  <CopyrightAndTrademark _locID=\"App_CopyrightandTrademark\">\n    <!-- _locComment_text=\"{MaxLength=200} Copyright and Trademark\" -->\n  </CopyrightAndTrademark>\n  <AdditionalLicenseTerms _locID=\"App_AdditionalLicenseTerms\">\n    <!-- _locComment_text=\"{MaxLength=10000} Additional License Terms\" -->\n  </AdditionalLicenseTerms>\n  <WebsiteURL _locID=\"App_WebsiteURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} WebsiteURL\" -->https://aka.ms/wsl2</WebsiteURL>\n  <SupportContactInfo _locID=\"App_SupportContactInfo\">\n    <!-- _locComment_text=\"{MaxLength=2048} Support Contact Info\" -->https://github.com/Microsoft/WSL</SupportContactInfo>\n  <PrivacyPolicyURL _locID=\"App_PrivacyURL\">\n    <!-- _locComment_text=\"{MaxLength=2048} Privacy Policy URL\" -->https://privacy.microsoft.com/zh-tw/privacystatement</PrivacyPolicyURL>\n</ProductDescription>\n"
  },
  {
    "path": "storebroker/sbconfig.json",
    "content": "﻿{\r\n    \"helpUri\": \"https:\\\\\\\\aka.ms\\\\StoreBroker_Config\",\r\n    \"schemaVersion\": 2,\r\n    \"packageParameters\": {\r\n        \"PDPRootPath\": \"\",\r\n        \"Release\": \"\",\r\n        \"PDPInclude\": [\r\n            \"PDP.xml\"\r\n        ],\r\n        \"PDPExclude\": [],\r\n        \"LanguageExclude\": [\r\n            \"default\",\r\n            \"qps-ploc\",\r\n            \"qps-ploca\",\r\n            \"qps-plocm\"\r\n        ],\r\n        \"MediaRootPath\": \"\",\r\n        \"MediaFallbackLanguage\": \"\",\r\n        \"PackagePath\": [],\r\n        \"OutPath\": \"\",\r\n        \"OutName\": \"WindowsSubsystemForLinux\",\r\n        \"DisableAutoPackageNameFormatting\": false\r\n    },\r\n    \"appSubmission\": {\r\n        \"productId\": \"00014131597032361940\",\r\n        \"targetPublishMode\": \"Manual\",\r\n        \"targetPublishDate\": null,\r\n        \"visibility\": \"Public\",\r\n        \"pricing\": {\r\n            \"priceId\": \"NotAvailable\",\r\n            \"trialPeriod\": \"NoFreeTrial\",\r\n            \"marketSpecificPricings\": {},\r\n            \"sales\": []\r\n        },\r\n        \"allowTargetFutureDeviceFamilies\": {\r\n            \"Xbox\": false,\r\n            \"Team\": false,\r\n            \"Holographic\": false,\r\n            \"Desktop\": true,\r\n            \"Mobile\": false\r\n        },\r\n        \"allowMicrosoftDecideAppAvailabilityToFutureDeviceFamilies\": false,\r\n        \"enterpriseLicensing\": \"None\",\r\n        \"applicationCategory\": \"NotSet\",\r\n        \"hardwarePreferences\": [],\r\n        \"hasExternalInAppProducts\": false,\r\n        \"meetAccessibilityGuidelines\": false,\r\n        \"canInstallOnRemovableMedia\": false,\r\n        \"automaticBackupEnabled\": false,\r\n        \"isGameDvrEnabled\": false,\r\n        \"gamingOptions\": [\r\n            {\r\n                \"genres\": [],\r\n                \"isLocalMultiplayer\": false,\r\n                \"isLocalCooperative\": false,\r\n                \"isOnlineMultiplayer\": false,\r\n                \"isOnlineCooperative\": false,\r\n                \"localMultiplayerMinPlayers\": 0,\r\n                \"localMultiplayerMaxPlayers\": 0,\r\n                \"localCooperativeMinPlayers\": 0,\r\n                \"localCooperativeMaxPlayers\": 0,\r\n                \"isBroadcastingPrivilegeGranted\": false,\r\n                \"isCrossPlayEnabled\": false,\r\n                \"kinectDataForExternal\": \"Disabled\"\r\n            }\r\n        ],\r\n        \"notesForCertification\": \"\"\r\n    }\r\n}"
  },
  {
    "path": "test/README.md",
    "content": "# Testing\r\n\r\n## Setup\r\n\r\nTests are created and executed using the [Test Authoring and Execution Framework (TAEF)](https://docs.microsoft.com/windows-hardware/drivers/taef/). Once you have successfully built and deployed the WSL application, all you need are the TAEF binaries to begin authoring and running tests. It is best practice to use taef binaries included in the Microsoft.Taef nuget package used to compile the tests. For example: `packages\\Microsoft.Taef.10.77.230207002\\build\\Binaries`\r\n\r\n## Executing Tests\r\n\r\nExecuting tests with TAEF is done by invoking the `TE.exe` binary:\r\n\r\n1. Open a command prompt with administrative privileges.\r\n2. Navigate to the subdirectory containing the built test binaries (`bin/<X64|Arm64>/<Debug|Release>/`)\r\n3. Execute the binaries via invoking TE and passing the test dll/s as arguments: `TE.exe test1.dll test2.dll test3.dll`\r\n\r\n## Useful **TE.exe** Command Line Parameters for Debugging/Executing Tests\r\n\r\nCommand Line parameters are passed to `TE.exe` after supplying the target `.dll`:\r\n\r\n### **/list**\r\n\r\nLists the individual tests loaded from the test `.dll` passed in:\r\n\r\n`TE.exe test1.dll test2.dll /list`\r\n\r\n### **/name:\\<testname\\>**\r\n\r\nSpecifies a specific test or group of tests, supporting wildcards `*` and `?` to execute (without this, every test will be run on invoke):\r\n\r\n`TE.exe test1.dll /name:*HelloWorldTest*`\r\n\r\n### **/inproc**\r\n\r\nVery useful for debugging via WinDbg, executes tests within the TE.exe process and not the TE.ProcessHost.exe child process:\r\n\r\n`TE.exe test1.dll /inproc`\r\n\r\n### **/breakOnCreate /breakOnError /breakOnInvoke**\r\n\r\nEspecially useful for WinDbg debugging when coupled with `/inproc`. They break into the debugger if/on: before instantiating a test class, if a error or test failure is logged, and prior to test method invoking, respectively.\r\n\r\n`TE.exe test1.dll /inproc /breakOnCreate /breakOnError /breakOnInvoke`\r\n\r\n### **/p:\\<paramName\\>=\\<paramName\\>**\r\n\r\nUsed for passing runtime parameters to test methods, as well as to setup and cleanup methods. Be mindful of the use of quotation marks.\r\n\r\n`TE.exe test1.dll /p:\"foo=hello\" /p:\"bar=2\"`\r\n\r\nThese variables can be retrieved in test source code using the following example:\r\n\r\n```cpp\r\n    using namespace WEX::Common;\r\n    using namespace WEX::TestExecution;\r\n\r\n    String runtimeParamString;\r\n    DWORD fooBar;\r\n\r\n    VERIFY_SUCCEEDED(RuntimeParameters::TryGetValue(L\"foo\", runtimeParamString));\r\n    VERIFY_SUCCEEDED(RuntimeParameters::TryGetValue(L\"bar\", fooBar));\r\n```\r\n\r\n### **/runas:<\\RunAsType\\>**\r\n\r\nSpecifies the environment to run the tests in:\r\n\r\n`TE.exe *.dll /runas:<System|Elevated|Restricted|LowIL|AppContainer|etc>`\r\n\r\n### **/sessionTime:<\\value\\>**\r\n\r\nSpecify a timeout for the **TE.exe** execution, which aborts on timeout.\r\n\r\n`TE.exe test1.dll /sessionTimeout:0:0:0.5 // [Day.]Hour[:Minute[:Second[.FractionalSeconds]]`\r\n\r\n## Creating Tests\r\n\r\nA good example for [how to create tests with TAEF](https://docs.microsoft.com/windows-hardware/drivers/taef/authoring-tests-in-c--) can be found in the `/test/SimpleTests.cpp`, `/test/MountTests.cpp`, and `/test/CMakeLists.txt`.\r\n\r\nMake sure to locate the TAEF header file the files at `%\\Program Files (x86)\\Windows Kits\\10\\Testing\\Development\\inc\\WexTestClass.h`.\r\n\r\nBelow is a brief overview:\r\n\r\n### Writing the Test\r\n\r\nFor example, consider the file below, named `ExampleTest.cpp`:\r\n\r\n```cpp\r\n    #include \"WexTestClass.h\" // this included be used for creating TAEF tests classes\r\n\r\n    #include \"Common.h\" // referring to /test/Common.h, where general utility functions for interacting with WSL in regards to testing reside\r\n\r\n    #define INLINE_TEST_METHOD_MARKUP // optional, but defined within the directory cmake build instructions. this is the practice that the preexisting tests use\r\n\r\n    namespace ExampleTest\r\n    {\r\n        class ExampleTest\r\n        {\r\n            TEST_CLASS(ExampleTest) // define this as a test class\r\n\r\n            // add tests via test methods of the test class\r\n            TEST_METHOD(HelloWorldTest) // ExampleTest::ExampleTest::HelloWorldTest\r\n            {\r\n                std::wstring outputExpected = L\"Linux on Windows Rocks!\\n\";\r\n                auto [output, __] = LxsstuLaunchWslAndCaptureOutput(L\"echo Linux on Windows Rocks!\"); // from /test/Common.h\r\n                VERIFY_ARE_EQUAL(output, outputExpected); // TAEF test method that passes if both are equal, and fails otherwise.\r\n            }\r\n        };\r\n    } //namespace ExampleTest\r\n```\r\n\r\nFor more in-depth examples of writing TAEF tests, check out `/tests/MountTests.cpp` and [Advanced Authoring Tests in C++](https://docs.microsoft.com/windows-hardware/drivers/taef/authoring-tests-in-c--#advanced-authoring-tests-in-c).\r\n\r\n## Building Tests\r\n\r\n### CMake\r\n\r\nFor examples on how to get your test/s building within the repo, please view `/test/CMakeLists.txt` for the structure of creating add to the `wsltest.dll`. For additional information on how to use CMake, try [CMake Documentation and Community](https://cmake.org/documentation/).\r\n\r\n### Building\r\n\r\nFollow the same instructions listed at the root of this repository and build the application as you would regularly.\r\n\r\n### Executing\r\n\r\nSee the parts above for how to run your new test, but if nothing went awry, your shiny new test dll should be placed in the binary directory. Try running it with:\r\n\r\n`TE.exe exampletest.dll`\r\n\r\n## Existing Tests\r\n\r\nTo run all existing tests: `TE.exe wsltests.dll`\r\n\r\n### SimpleTests\r\n\r\nVery basic tests focusing on the connection to WSL. Tests examine commands like `wsl echo`, `wsl --user`, and `wsl --cd`.\r\n\r\nRun these with: `TE.exe wsltests.dll /name:*SimpleTests*`\r\n\r\n### MountTests\r\n\r\nTests focusing on the `wsl --mount` functionality. These tests include things like: `--bare` mounting, mounting disk partitions, mounting FAT partitions, etc.\r\n\r\nRun these with: `TE.exe wsltests.dll /name:*MountTests*`\r\n### NetworkTests\r\n\r\nTests focusing on the networking aspects of WSL. These are also used to test certain functionality like WSL configurations related to networking, mirrored networking, flow steering, etc.\r\n\r\nRun these with `TE.exe wsltests.dll /name:*NetworkTests*`\r\n### Plan9Tests\r\n\r\nTests that focus on validating the functionality of the Plan 9 filesystem component of WSL, testing filesystem-related operations like the creation, deletion, and I/O of files and directories.\r\n\r\nRun these with: `TE.exe wsltests.dll /name:*Plan9Tests*`\r\n\r\n### UnitTests\r\n\r\nTests that assess general Linux behavior from within the distribution and the features/changes WSL has made on the Linux side. This includes process creation, signals, sockets, etc.\r\nThe individual tests are located under `linux/unit_test/*.c` with the exception of `systemd` tests, which are defined in the `windows/UnitTests.cpp`.\r\n\r\nRun all unit tests with: `TE.exe wsltests.dll /name:*UnitTests*`\r\n\r\nTo run only `systemd` tests, use: `TE.exe wsltests.dll /name:UnitTests::UnitTests::Systemd*`\r\n"
  },
  {
    "path": "test/linux/unit_tests/Makefile",
    "content": "ARCH=$(shell uname -m)\r\nCC=gcc\r\nCFLAGS=-ggdb -Werror -Wno-format-truncation -Wno-format-overflow -D_GNU_SOURCE=1\r\nLDFLAGS=-pthread -lutil -lmount\r\nLDLIBFLAGS=-L.\r\n\r\nTEST_BINARY=wsl_unit_tests\r\n\r\nUNIT_TEST_HEADER=unittests.h\r\n\r\nUNIT_TEST_OBJECTS=\\\r\n\tdev_pt_common.o \\\r\n\tauxv.o \\\r\n\tbinfmt.o \\\r\n\tbrk.o \\\r\n\tcgroup.o \\\r\n\tcommon.o \\\r\n\tdev_pt.o \\\r\n\tdev_pt_2.o \\\r\n\tdrvfs.o \\\r\n\tdup.o \\\r\n\tepoll.o \\\r\n\teventfd.o \\\r\n\texecve.o \\\r\n\tflock.o \\\r\n\tfork.o \\\r\n\tfscommon.o \\\r\n\tfstab.o \\\r\n\tget_set_id.o \\\r\n\tgetaddrinfo.o \\\r\n\tgettime.o \\\r\n\tinotify.o \\\r\n\tinterop.o \\\r\n\tioprio.o \\\r\n\tkeymgmt.o \\\r\n\tlxtevent.o \\\r\n\tlxtfs.o \\\r\n\tlxtlog.o \\\r\n\tlxtmount.o \\\r\n\tlxtutil.o \\\r\n\tmadvise.o \\\r\n\tmprotect.o \\\r\n\tmremap.o \\\r\n\tnamespace.o \\\r\n\tnetlink.o \\\r\n\toverlayfs.o \\\r\n\tpipe.o \\\r\n\tpoll.o \\\r\n\trandom.o \\\r\n\tresourcelimits.o \\\r\n\tsched.o \\\r\n\tsem.o \\\r\n\tshm.o \\\r\n\tsocket_nonblock.o \\\r\n\tsplice.o \\\r\n\tsysfs.o \\\r\n\tsysinfo.o \\\r\n\ttimer.o \\\r\n\ttimerfd.o \\\r\n\ttty.o \\\r\n\tttys.o \\\r\n\tunittests.o \\\r\n\tuser.o \\\r\n\tutimensat.o \\\r\n\tvfsaccess.o \\\r\n\tvnet.o \\\r\n\twaitpid.o \\\r\n\twslpath.o \\\r\n\txattr.o\r\n\r\n#__NR_select has been removed from arm64 unitstd.h\r\nTEST_BINARY_x86_64_OBJECTS=\\\r\n\tselect.o\r\n\r\nTEST_BINARY_x86_OBJECTS=$(TEST_BINARY_x86_64_OBJECTS)\r\n\r\nTEST_BINARY_aarch64_OBJECTS=\r\n\r\nTEST_BINARY_OBJECTS=$(UNIT_TEST_OBJECTS) $(TEST_BINARY_$(ARCH)_OBJECTS)\r\n\r\n.PHONY: clean all\r\n\r\nall: $(TEST_BINARY)\r\n\r\nclean:\r\n\trm -f *.o\r\n\trm -f *.a\r\n\trm -f $(TEST_BINARY)\r\n\r\n$(TEST_BINARY): $(TEST_BINARY_OBJECTS)\r\n\t$(CC) $^ $(LDFLAGS) $(LDLIBFLAGS) -o $@\r\n"
  },
  {
    "path": "test/linux/unit_tests/auxv.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    auxv.c\r\n\r\nAbstract:\r\n\r\n    This file is a test for the auxiliary vector functionality.\r\n\r\n--*/\r\n\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <sys/auxv.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <fcntl.h>\r\n#include <unistd.h>\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n\r\n#define AUXV_UID 1004\r\n#define AUXV_GID 1004\r\n\r\n#define LXT_NAME \"auxv\"\r\n\r\n#define AUXV_TEST_SCRIPT \"auxv_test_script.sh\"\r\n#define AUXV_TEST_SCRIPT_SOURCE \"#!\" AUXV_TEST_PROGRAM_PATH\r\n#define AUXV_TEST_PROGRAM \"auxv_test_program\"\r\n#define AUXV_TEST_PROGRAM_PATH \"/data/test/\" AUXV_TEST_PROGRAM\r\n#define AUXV_TEST_PROGRAM_SOURCE_FILE \"auxv_test_program.c\"\r\n#define AUXV_TEST_PROGRAM_SOURCE \\\r\n    \"#include <stdio.h>\\n\" \\\r\n    \"#include <string.h>\\n\" \\\r\n    \"#include <stdlib.h>\\n\" \\\r\n    \"#include <sys/auxv.h>\\n\" \\\r\n    \"\\n\" \\\r\n    \"int main(int Argc, char** Argv)\\n\" \\\r\n    \"{\\n\" \\\r\n    \"    int Index;\\n\" \\\r\n    \"    char* Filename = (char*)getauxval(AT_EXECFN);\\n\" \\\r\n    \"    char* Platform = (char*)getauxval(AT_PLATFORM);\\n\" \\\r\n    \"    printf(\\\"AT_EXECFN:   %%s {%%p}\\\\n\\\", Filename, Filename);\\n\" \\\r\n    \"    printf(\\\"AT_PLATFORM: %%s {%%p}\\\\n\\\", Platform, Platform);\\n\" \\\r\n    \"    for (Index = 0; Index < Argc; Index += 1) {\\n\" \\\r\n    \"        printf(\\\"Argv[%%d] = %%s\\\\n\\\", Index, Argv[Index]);\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (Platform > Filename) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if ((strcmp(Platform, \\\"%s\\\") != 0) ||\\n\" \\\r\n    \"        (strcmp(Filename, \\\"%s\\\") != 0)) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    return 0;\\n\" \\\r\n    \"}\"\r\n\r\nint AuxvAtExecfn(PLXT_ARGS Args);\r\n\r\nint AuxvGetAuxv(PLXT_ARGS Args);\r\n\r\nint AuxvGetAuxvChild(void);\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {{\"getauxv\", AuxvGetAuxv}, {\"AT_EXECFN\", AuxvAtExecfn}};\r\n\r\nint AuxvTestEntry(int Argc, char* Argv[])\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int ArgvIndex;\r\n    int Result;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n\r\n    //\r\n    // Parse the arguments.\r\n    //\r\n\r\n    for (ArgvIndex = 1; ArgvIndex < Argc; ++ArgvIndex)\r\n    {\r\n        if (Argv[ArgvIndex][0] != '-')\r\n        {\r\n            printf(\"Unexpected character %s\", Argv[ArgvIndex]);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        switch (Argv[ArgvIndex][1])\r\n        {\r\n        case 'c':\r\n\r\n            //\r\n            // Run the getauxv child variation.\r\n            //\r\n\r\n            return AuxvGetAuxvChild();\r\n\r\n            //\r\n            // The below arguments are taken care of by LxtInitialize.\r\n            //\r\n\r\n        case 'a':\r\n            break;\r\n\r\n        case 'v':\r\n        case 'l':\r\n            ++ArgvIndex;\r\n\r\n            break;\r\n\r\n        default:\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // If -c was not specified, just run the tests\r\n    //\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return 0;\r\n}\r\n\r\nint AuxvGetAuxv(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    char* Argv[4];\r\n    struct stat Buffer;\r\n    int ChildPid;\r\n    int Mode;\r\n    int OriginalMode;\r\n    gid_t OriginalGid;\r\n    uid_t OriginalUid;\r\n    int Result;\r\n    unsigned long Value;\r\n\r\n    ChildPid = -1;\r\n    OriginalMode = 0;\r\n    OriginalUid = 0;\r\n    OriginalGid = 0;\r\n    Value = getauxval(AT_SECURE);\r\n    LxtLogInfo(\"Parent AT_SECURE = %u\", Value);\r\n    LxtCheckEqual(Value, 0, \"%u\");\r\n\r\n    // change Args->Argv[0] so that it points to the new single test binary design\r\n    Args->Argv[0] = Argv[0] = WSL_UNIT_TEST_BINARY;\r\n    LxtLogInfo(\"calling stat(%s)\", Args->Argv[0]);\r\n    LxtCheckErrno(stat(Args->Argv[0], &Buffer));\r\n    OriginalMode = Buffer.st_mode;\r\n    OriginalGid = Buffer.st_gid;\r\n    OriginalUid = Buffer.st_uid;\r\n\r\n    LxtLogInfo(\"Setting the set-user-ID bit\");\r\n    Mode = OriginalMode | S_ISUID;\r\n\r\n    LxtCheckErrno(chown(Args->Argv[0], AUXV_UID, AUXV_UID));\r\n    LxtCheckErrno(chmod(Args->Argv[0], Mode));\r\n\r\n    //\r\n    // Start a child process to verify the value of AT_SECURE.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[1] = \"auxv\";\r\n        Argv[2] = \"-c\";\r\n        Argv[3] = NULL;\r\n        execve(Args->Argv[0], Argv, NULL);\r\n        LxtLogError(\"Execve failed, errno: %d (%s)\", errno, strerror(errno));\r\n        _exit(LXT_RESULT_FAILURE);\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    LxtLogInfo(\"Setting the set-group-ID bit\");\r\n    Mode = OriginalMode | S_ISGID;\r\n    LxtCheckErrno(chmod(Args->Argv[0], Mode));\r\n\r\n    //\r\n    // Start a child process to verify the value of AT_SECURE.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[1] = \"auxv\";\r\n        Argv[2] = \"-c\";\r\n        Argv[3] = NULL;\r\n        execve(Argv[0], Argv, NULL);\r\n        LxtLogError(\"Execve failed, errno: %d (%s)\", errno, strerror(errno));\r\n        _exit(LXT_RESULT_FAILURE);\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (OriginalMode != 0)\r\n    {\r\n        chmod(Args->Argv[0], OriginalMode);\r\n        chown(Args->Argv[0], OriginalUid, OriginalGid);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint AuxvGetAuxvChild(void)\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Result;\r\n    unsigned long Value;\r\n\r\n    ChildPid = -1;\r\n    Value = getauxval(AT_SECURE);\r\n    LxtLogInfo(\"child AT_SECURE = %u\", Value);\r\n    LxtCheckEqual(Value, 1, \"%u\");\r\n\r\n    //\r\n    // Start a child process to verify the value of AT_SECURE.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Value = getauxval(AT_SECURE);\r\n        LxtLogInfo(\"child fork AT_SECURE = %u\", Value);\r\n        LxtCheckEqual(Value, 1, \"%u\");\r\n        Result = LXT_RESULT_SUCCESS;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint AuxvAtExecfnCompile(char* Filename)\r\n\r\n{\r\n\r\n    char* Argv[5];\r\n    char Buffer[1024];\r\n    int ByteCount;\r\n    int ChildPid;\r\n    int Fd;\r\n    char* Platform;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n    Fd = -1;\r\n    Platform = (char*)getauxval(AT_PLATFORM);\r\n\r\n    ByteCount = snprintf(Buffer, sizeof(Buffer), AUXV_TEST_PROGRAM_SOURCE, Platform, Filename);\r\n\r\n    if (ByteCount < 0)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Formatting test source failed %d\", errno);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Create the source file to be compiled.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(AUXV_TEST_PROGRAM_SOURCE_FILE, 0755));\r\n    LxtCheckErrno(ByteCount = write(Fd, Buffer, ByteCount));\r\n\r\n    //\r\n    // Compile the binary\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Filename = \"/usr/bin/gcc\";\r\n        Argv[0] = \"gcc\";\r\n        Argv[1] = AUXV_TEST_PROGRAM_SOURCE_FILE;\r\n        Argv[2] = \"-o\";\r\n        Argv[3] = AUXV_TEST_PROGRAM_PATH;\r\n        Argv[4] = NULL;\r\n        LxtCheckErrno(execv(Filename, Argv));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, 30));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    unlink(AUXV_TEST_PROGRAM_SOURCE_FILE);\r\n    return Result;\r\n}\r\n\r\nint AuxvAtExecfn(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    char* Argv[2];\r\n    int ByteCount;\r\n    int ChildPid;\r\n    char* Environment[2];\r\n    int Fd;\r\n    char* Filename;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n    Fd = -1;\r\n    Filename = AUXV_TEST_PROGRAM_PATH;\r\n    LxtCheckResult(AuxvAtExecfnCompile(Filename));\r\n\r\n    //\r\n    // Run the binary with a non-null argument array.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[0] = AUXV_TEST_PROGRAM_PATH;\r\n        Argv[1] = NULL;\r\n        LxtCheckErrno(execv(Filename, Argv));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Run the binary with a null argument array.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Environment[0] = \"FOO=bar\";\r\n        Environment[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(Filename, NULL, Environment));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Run the binary with an Argv[0] that does not match the filename.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[0] = \"FOO\";\r\n        Argv[1] = NULL;\r\n        LxtCheckErrno(execv(Filename, Argv));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Run the binary with an empty command line.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Environment[0] = \"FOO=bar\";\r\n        Environment[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(Filename, NULL, Environment));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Run the binary with null argument and environment arrays.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(LxtExecve(Filename, NULL, NULL));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Create a script that uses #! to launch the test binary.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(AUXV_TEST_SCRIPT, 0755));\r\n    LxtCheckErrno(ByteCount = write(Fd, AUXV_TEST_SCRIPT_SOURCE, sizeof(AUXV_TEST_SCRIPT_SOURCE)));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Recompile the binary with the script as the expected AT_EXECFN.\r\n    //\r\n\r\n    Filename = AUXV_TEST_SCRIPT;\r\n    LxtCheckResult(AuxvAtExecfnCompile(Filename));\r\n\r\n    //\r\n    // Run the script with a non-null argument array.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[0] = Filename;\r\n        Argv[1] = NULL;\r\n        LxtCheckErrno(execv(Filename, Argv));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Run the script with a null argument array.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Environment[0] = \"FOO=bar\";\r\n        Environment[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(Filename, NULL, Environment));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Run the script with an Argv[0] that does not match the filename.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[0] = \"FOO\";\r\n        Argv[1] = NULL;\r\n        LxtCheckErrno(execv(Filename, Argv));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Run the script with an empty command line.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Environment[0] = \"FOO=bar\";\r\n        Environment[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(Filename, NULL, Environment));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Run the script with null argument and environment arrays.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(LxtExecve(Filename, NULL, NULL));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    unlink(AUXV_TEST_PROGRAM_PATH);\r\n    unlink(AUXV_TEST_SCRIPT);\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/binfmt.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    binfmt.c\r\n\r\nAbstract:\r\n\r\n    This file contains tests for the binfmt file system.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/syscall.h>\r\n#include <sys/utsname.h>\r\n#include <unistd.h>\r\n#include <dirent.h>\r\n#include <fcntl.h>\r\n#include <pthread.h>\r\n\r\n#define LXT_NAME \"BinFmt\"\r\n\r\n#define BINFMT_MNT \"/proc/sys/fs/binfmt_misc\"\r\n#define BINFMT_TEST_FILE \"/data/test/lxt_binfmt_test\"\r\n#define BINFMT_TIMEOUT 60\r\n\r\n#define BINFMT_DISABLE_STRING \"0\"\r\n#define BINFMT_ENABLE_STRING \"1\"\r\n#define BINFMT_REMOVE_STRING \"-1\"\r\n\r\n#define BINFMT_REGISTER_NAME \"Test\"\r\n#define BINFMT_INTERPRETER_SCRIPT \"/data/test/lxt_binfmt_interpreter.sh\"\r\n#define BINFMT_REGISTER_SCRIPT_STRING \":\" BINFMT_REGISTER_NAME \":M::\\\\xff\\\\xff\\\\xff\\\\xff::\" BINFMT_INTERPRETER_SCRIPT \":\"\r\n\r\n#define BINFMT_INTERPRETER_SCRIPT_CONTENTS \\\r\n    \"#!/bin/bash\\n\" \\\r\n    \"# \" BINFMT_INTERPRETER_SCRIPT \" - the wrapper for WSL binfmt_misc testing\\n\" WSL_UNIT_TEST_BINARY \" binfmt -a -i \\\"$@\\\"\"\r\n\r\n#define BINFMT_INTERPRETER_BINARY \"/data/test/lxt_binfmt_interpreter_binary\"\r\n#define BINFMT_INTERPRETER_BINARY_SOURCEFILE \"/data/test/lxt_binfmt_interpreter_binary.c\"\r\n\r\n//\r\n// N.B. These UID and GID values must be kept in-sync with the values in the\r\n// source below.\r\n//\r\n\r\n#define BINFMT_CALLER_UID 0\r\n#define BINFMT_CALLER_GID 0\r\n#define BINFMT_BINARY_UID 1044\r\n#define BINFMT_BINARY_GID 1044\r\n#define BINFMT_P_FLAG_ARG \"foo\"\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \\\r\n    \"#define _GNU_SOURCE\\n\" \\\r\n    \"#include <stdio.h>\\n\" \\\r\n    \"#include <string.h>\\n\" \\\r\n    \"#include <stdlib.h>\\n\" \\\r\n    \"#include <fcntl.h>\\n\" \\\r\n    \"#include <unistd.h>\\n\" \\\r\n    \"#include <errno.h>\\n\" \\\r\n    \"#include <sys/auxv.h>\\n\" \\\r\n    \"#define BINFMT_CALLER_UID 0\\n\" \\\r\n    \"#define BINFMT_CALLER_GID 0\\n\" \\\r\n    \"#define BINFMT_BINARY_UID 1044\\n\" \\\r\n    \"#define BINFMT_BINARY_GID 1044\\n\" \\\r\n    \"#define BINFMT_P_FLAG_ARG \\\"foo\\\"\\n\" \\\r\n    \"#define BINFMT_INTERPRETER_BINARY \\\"/data/test/lxt_binfmt_interpreter_binary\\\"\\n\" \\\r\n    \"#define BINFMT_TEST_FILE \\\"/data/test/lxt_binfmt_test\\\"\\n\" \\\r\n    \"\\n\" \\\r\n    \"int main(int Argc, char** Argv)\\n\" \\\r\n    \"{\\n\" \\\r\n    \"    struct stat Buffer;\\n\" \\\r\n    \"    int Fd;\\n\" \\\r\n    \"    int Index;\\n\" \\\r\n    \"    uid_t Real, Effective, Saved;\\n\" \\\r\n    \"    printf(\\\"Pid = %d\\\\n\\\", getpid());\\n\" \\\r\n    \"    Fd = getauxval(AT_EXECFD);\\n\" \\\r\n    \"    printf(\\\"AT_EXECFD = %d errno = %d\\\\n\\\", Fd, errno);\\n\" \\\r\n    \"    getresuid(&Real, &Effective, &Saved);\\n\" \\\r\n    \"    printf(\\\"Real %d Effective %d Saved %d\\\\n\\\", Real, Effective, Saved);\\n\" \\\r\n    \"    printf(\\\"Argc = %d\\\\n\\\", Argc);\\n\" \\\r\n    \"    for (Index = 0; Index < Argc; Index += 1) {\\n\" \\\r\n    \"        printf(\\\"Argv[%d] = %s\\\\n\\\", Index, Argv[Index]);\\n\" \\\r\n    \"    }\\n\"\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_VERIFY_TWO_ARGS \\\r\n    \"    if (Argc != 2) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (strcmp(Argv[0], BINFMT_INTERPRETER_BINARY) != 0) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (strcmp(Argv[1], BINFMT_TEST_FILE) != 0) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\"\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_C_FLAG \\\r\n    \"    if ((Fd == 0) && (errno == ENOENT)) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (fcntl(Fd, F_GETFD) != 0) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if ((Real != BINFMT_CALLER_UID) ||\\n\" \\\r\n    \"        (Effective != BINFMT_BINARY_UID) ||\\n\" \\\r\n    \"        (Saved != BINFMT_BINARY_UID)) {\\n\" \\\r\n    \"            return -1;\\n\" \\\r\n    \"    }\\n\" BINFMT_INTERPRETER_BINARY_SOURCE_VERIFY_TWO_ARGS\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_O_FLAG \\\r\n    \"    if ((Fd == 0) && (errno == ENOENT)) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (fcntl(Fd, F_GETFD) != 0) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if ((Real != BINFMT_CALLER_UID) ||\\n\" \\\r\n    \"        (Effective != BINFMT_CALLER_UID) ||\\n\" \\\r\n    \"        (Saved != BINFMT_CALLER_UID)) {\\n\" \\\r\n    \"            return -1;\\n\" \\\r\n    \"    }\\n\" BINFMT_INTERPRETER_BINARY_SOURCE_VERIFY_TWO_ARGS\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_P_FLAG \\\r\n    \"    if ((Fd != 0) || (errno != ENOENT)) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (Argc != 4) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (strcmp(Argv[0], BINFMT_INTERPRETER_BINARY) != 0) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (strcmp(Argv[1], BINFMT_TEST_FILE) != 0) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (strcmp(Argv[2], BINFMT_TEST_FILE) != 0) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\" \\\r\n    \"    if (strcmp(Argv[3], BINFMT_P_FLAG_ARG) != 0) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\"\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_NO_FLAGS \\\r\n    \"    if ((Fd != 0) || (errno != ENOENT)) {\\n\" \\\r\n    \"        return -1;\\n\" \\\r\n    \"    }\\n\"\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_END \\\r\n    \"    return 0;\\n\" \\\r\n    \"}\"\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_C_FLAG \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_C_FLAG \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_END\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_O_FLAG \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_O_FLAG \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_END\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_P_FLAG \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_P_FLAG \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_END\r\n\r\n#define BINFMT_INTERPRETER_BINARY_SOURCE_NO_FLAGS \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_NO_FLAGS \\\r\n    BINFMT_INTERPRETER_BINARY_SOURCE_END\r\n\r\n#define BINFMT_OFFSET_TEST \"/data/test/binfmt_offset\"\r\n#define BINFMT_OFFSET_TEST_PATTERN \"GSH\"\r\n\r\n#define BINFMT_REGISTER_BINARY_STRING \":\" BINFMT_REGISTER_NAME \":M::\\\\xff\\\\xff\\\\xff\\\\xff::\" BINFMT_INTERPRETER_BINARY \":\"\r\n#define BINFMT_REGISTER_BINARY_STRING_C BINFMT_REGISTER_BINARY_STRING \"C\"\r\n#define BINFMT_REGISTER_BINARY_STRING_O BINFMT_REGISTER_BINARY_STRING \"O\"\r\n#define BINFMT_REGISTER_BINARY_STRING_P BINFMT_REGISTER_BINARY_STRING \"P\"\r\n\r\n#define BINFMT_STATUS_ENABLED \"enabled\\n\"\r\n#define BINFMT_STATUS_DISABLED \"disabled\\n\"\r\n\r\n#define BINFMT_REGISTRATION_ENABLED_STRING \\\r\n    BINFMT_STATUS_ENABLED \\\r\n    \"interpreter \" BINFMT_INTERPRETER_SCRIPT \\\r\n    \"\\n\" \\\r\n    \"flags: \\n\" \\\r\n    \"offset 0\\n\" \\\r\n    \"magic ffffffff\\n\"\r\n\r\n#define BINFMT_REGISTRATION_DISABLED_STRING \\\r\n    BINFMT_STATUS_DISABLED \\\r\n    \"interpreter \" BINFMT_INTERPRETER_SCRIPT \\\r\n    \"\\n\" \\\r\n    \"flags: \\n\" \\\r\n    \"offset 0\\n\" \\\r\n    \"magic ffffffff\\n\"\r\n\r\ntypedef struct _LXT_BINFMT_REGISTRATION\r\n{\r\n    char* RegistrationString;\r\n    char Magic[4];\r\n    char* TestFile;\r\n} LXT_BINFMT_REGISTRATION, PLXT_BINFMT_REGISTRATION;\r\n\r\nLXT_BINFMT_REGISTRATION g_BinfmtRegistrations[] = {\r\n    {\":binfmt_1:M::\\\\x01\\x01\\x01\\x01::/data/test/lxt_binfmt_2:\", {0x1, 0x1, 0x1, 0x1}, \"/data/test/lxt_binfmt_1\"},\r\n    {\":binfmt_2:M::\\\\x02\\x02\\x02\\x02::/data/test/lxt_binfmt_3:\", {0x2, 0x2, 0x2, 0x2}, \"/data/test/lxt_binfmt_2\"},\r\n    {\":binfmt_3:M::\\\\x03\\x03\\x03\\x03::/data/test/lxt_binfmt_4:\", {0x3, 0x3, 0x3, 0x3}, \"/data/test/lxt_binfmt_3\"},\r\n    {\":binfmt_4:M::\\\\x04\\x04\\x04\\x04::/data/test/lxt_binfmt_5:\", {0x4, 0x4, 0x4, 0x4}, \"/data/test/lxt_binfmt_4\"},\r\n    {\":binfmt_5:M::\\\\x05\\x05\\x05\\x05::/data/test/lxt_binfmt_6:\", {0x5, 0x5, 0x5, 0x5}, \"/data/test/lxt_binfmt_5\"},\r\n    {\":binfmt_6:M::\\\\x06\\x06\\x06\\x06::/data/test/lxt_binfmt_7:\", {0x6, 0x6, 0x6, 0x6}, \"/data/test/lxt_binfmt_6\"},\r\n    {\":binfmt_7:M::\\\\x07\\x07\\x07\\x07::/bin/echo:\", {0x7, 0x7, 0x7, 0x7}, \"/data/test/lxt_binfmt_7\"}};\r\n\r\nvoid BimFmtCleanup(void);\r\n\r\nint BinFmtExtension(PLXT_ARGS Args);\r\n\r\nint BinFmtInvalidParam(PLXT_ARGS Args);\r\n\r\nint BinFmtOffset(PLXT_ARGS Args);\r\n\r\nint BinFmtOptions(PLXT_ARGS Args);\r\n\r\nint BinFmtRegister(PLXT_ARGS Args);\r\n\r\nint BinFmtRoot(PLXT_ARGS Args);\r\n\r\nint BinFmtStatus(PLXT_ARGS Args);\r\n\r\nint BinFmtInterpreterEntry(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"BinFmt - \" BINFMT_MNT \" root\", BinFmtRoot},\r\n    {\"BinFmt - \" BINFMT_MNT \"/register\", BinFmtRegister},\r\n    {\"BinFmt - \" BINFMT_MNT \"/status\", BinFmtStatus},\r\n    {\"BinFmt - Extensions\", BinFmtExtension},\r\n    {\"BinFmt - Options\", BinFmtOptions},\r\n    {\"BinFmt - Offset\", BinFmtOffset},\r\n    {\"BinFmt - Invalid Parameter\", BinFmtInvalidParam}};\r\n\r\nstatic const LXT_CHILD_INFO g_BinFmtRootChildren[] = {{\"register\", DT_REG}, {\"status\", DT_REG}};\r\n\r\nstatic const char* g_BinFmtRegisterInvalid[] = {\r\n    \"::M::BACON::/usr/bin/test:\",\r\n    \":Test:B::BACON::/usr/bin/test:\",\r\n    \":Test:M::BACON:BACONISAWESOME:/usr/bin/test:\",\r\n    \":Test:M::BACON:\\\\xff:/usr/bin/test:\",\r\n    \":Test:M::BACON:\\\\xff\\\\xff\\\\xff\\\\xff\\\\xf:/usr/bin/test:\",\r\n    \":Test:M::BACON:::\",\r\n    \":Test:M::BACON::/usr/bin/test:B\",\r\n    \":Test:M::BACON::/usr/bin/test: \",\r\n    \":Test:M::BACON::/usr/bin/test:\\nO\",\r\n    \":Test:E::B/ACON::/usr/bin/test:\",\r\n\r\n    /*\r\n     \":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaa:\",\r\n     \":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaaaaaaaa:\",\r\n     \":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaaaaaaaaaaaaaaaaa:\",\r\n     \":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\",\r\n     \":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:\",\r\n    */\r\n\r\n    \":::::::::::::::::\",\r\n    \"\",\r\n    \"\\0\"};\r\n\r\nint BinFmtTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine is the main entry point for the binfmt tests.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Opt;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    optind = 0;\r\n    opterr = 0;\r\n    while ((Opt = getopt(Argc, Argv, \"i\")) != -1)\r\n    {\r\n        switch (Opt)\r\n        {\r\n        case 'i':\r\n            Result = BinFmtInterpreterEntry(&Args);\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nvoid BimFmtCleanup(void)\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    int Size;\r\n\r\n    //\r\n    // Remove the test entry via the registration file.\r\n    //\r\n\r\n    Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR);\r\n    if (Fd >= 0)\r\n    {\r\n        Size = strlen(BINFMT_REMOVE_STRING);\r\n        LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size));\r\n    }\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint BinFmtExtension(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests binformat extensions.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[16];\r\n    size_t BytesWritten;\r\n    int ChildPid;\r\n    char* ExecArgs[2];\r\n    int Fd = -1;\r\n    int Index;\r\n    int RegisterFd;\r\n    LXT_CHILD_INFO Registration;\r\n    int Result;\r\n    ssize_t Size;\r\n    int Status;\r\n\r\n    //\r\n    // Clean any binfmt interpreters from a previous iteration of the test.\r\n    //\r\n\r\n    BimFmtCleanup();\r\n\r\n    //\r\n    // Create the binfmt interpreter.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_SCRIPT, 0777));\r\n    LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_SCRIPT_CONTENTS, (sizeof(BINFMT_INTERPRETER_SCRIPT_CONTENTS) - 1)));\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Register a binfmt extension.\r\n    //\r\n\r\n    LxtCheckErrno(RegisterFd = open(BINFMT_MNT \"/register\", O_WRONLY));\r\n    Size = strlen(BINFMT_REGISTER_SCRIPT_STRING);\r\n    LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_SCRIPT_STRING, Size));\r\n\r\n    //\r\n    // Create a file that will be handled by the binfmt extension.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(BINFMT_TEST_FILE, 0777));\r\n    memset(Buffer, 0xff, sizeof(Buffer));\r\n    LxtCheckErrno(BytesWritten = write(Fd, Buffer, sizeof(Buffer)));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Fork and exec the file.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = BINFMT_TEST_FILE;\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    }\r\n\r\n    //\r\n    // Remove the new entry via the registration file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR));\r\n    Size = strlen(BINFMT_REMOVE_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Create many registrations and test files.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_BinfmtRegistrations); Index += 1)\r\n    {\r\n        Size = strlen(g_BinfmtRegistrations[Index].RegistrationString);\r\n        LxtCheckErrno(Size = write(RegisterFd, g_BinfmtRegistrations[Index].RegistrationString, Size));\r\n        LxtCheckErrno(Fd = creat(g_BinfmtRegistrations[Index].TestFile, 0777));\r\n        LxtCheckErrno(BytesWritten = write(Fd, g_BinfmtRegistrations[Index].Magic, sizeof(g_BinfmtRegistrations[Index].Magic)));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Fork and exec the file to test the interpreter depth.\r\n    //\r\n\r\n    Index = 2;\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = g_BinfmtRegistrations[Index].TestFile;\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    }\r\n\r\n    //\r\n    // Test max interpreter link depth (should fail).\r\n    //\r\n\r\n    Index = 1;\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = g_BinfmtRegistrations[Index].TestFile;\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), ELOOP);\r\n        exit(0);\r\n\r\n        //\r\n        // The parent waits for the child to exit.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    }\r\n\r\n    //\r\n    // Remove the entries via the status file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/status\", O_RDWR));\r\n    Size = strlen(BINFMT_REMOVE_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\nErrorExit:\r\n    if (RegisterFd > 0)\r\n    {\r\n        LxtClose(RegisterFd);\r\n    }\r\n\r\n    if (Fd > 0)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint BinFmtInvalidParam(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests invalid argument handling for the binfmt register file.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Index;\r\n    int Result;\r\n    ssize_t Size;\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/register\", O_WRONLY));\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_BinFmtRegisterInvalid); Index += 1)\r\n    {\r\n        LxtLogInfo(\"Index[%d] %s\", Index, g_BinFmtRegisterInvalid[Index]);\r\n        Size = strlen(g_BinFmtRegisterInvalid[Index]);\r\n        LxtCheckErrnoFailure(Size = write(Fd, g_BinFmtRegisterInvalid[Index], Size), EINVAL);\r\n    }\r\n\r\nErrorExit:\r\n    if (Fd > 0)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint BinFmtOffset(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests binformat interpreter options.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    char* ExecArgs[2];\r\n    int Fd;\r\n    char RegisterString[] = \":\" BINFMT_REGISTER_NAME \":M:2:\" BINFMT_OFFSET_TEST_PATTERN \"::/bin/true:\";\r\n    int Result;\r\n    ssize_t Size;\r\n\r\n    //\r\n    // Register an interpreter with a known string at a two byte offset.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/register\", O_WRONLY));\r\n    Size = strlen(RegisterString);\r\n    LxtCheckErrno(Size = write(Fd, RegisterString, Size));\r\n\r\n    //\r\n    // Create a test file that matches this pattern.\r\n    //\r\n\r\n    LxtClose(Fd);\r\n    LxtCheckErrno(Fd = creat(BINFMT_OFFSET_TEST, 0777));\r\n    LxtCheckErrno(Size = write(Fd, \"00\" BINFMT_OFFSET_TEST_PATTERN, sizeof(BINFMT_OFFSET_TEST_PATTERN) + 2));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Fork and exec the file and ensure that the binfmt interpreter is invoked.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = BINFMT_OFFSET_TEST;\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // Create a test file that does not match the pattern.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(BINFMT_OFFSET_TEST, 0777));\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_OFFSET_TEST_PATTERN, sizeof(BINFMT_OFFSET_TEST_PATTERN)));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Fork and exec the file and ensure that the exec fails.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = BINFMT_OFFSET_TEST;\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), ENOEXEC);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd > 0)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    //\r\n    // Unregister the interpreter and delete the test file.\r\n    //\r\n\r\n    BimFmtCleanup();\r\n    unlink(BINFMT_OFFSET_TEST);\r\n\r\n    return Result;\r\n}\r\n\r\nint BinFmtOptions(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests binformat interpreter options.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char* Argv[5];\r\n    char Buffer[16];\r\n    size_t BytesWritten;\r\n    int ChildPid;\r\n    char* ExecArgs[3];\r\n    int Fd = -1;\r\n    int Index;\r\n    int RegisterFd;\r\n    LXT_CHILD_INFO Registration;\r\n    int Result;\r\n    ssize_t Size;\r\n    int Status;\r\n\r\n    //\r\n    // Clean any binfmt interpreters from a previous iteration of the test.\r\n    //\r\n\r\n    BimFmtCleanup();\r\n\r\n    //\r\n    // Create a file that will be handled by the binfmt extension.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(BINFMT_TEST_FILE, 0777));\r\n    memset(Buffer, 0xff, sizeof(Buffer));\r\n    LxtCheckErrno(BytesWritten = write(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckErrno(fchown(Fd, BINFMT_BINARY_UID, BINFMT_BINARY_GID));\r\n    LxtCheckErrno(fchmod(Fd, 0777 | S_ISUID));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Create a binfmt interpreter without any flags.\r\n    //\r\n\r\n    LxtLogInfo(\"Testing no flags\");\r\n    LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_BINARY_SOURCEFILE, 0777));\r\n    LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_BINARY_SOURCE_NO_FLAGS, (sizeof(BINFMT_INTERPRETER_BINARY_SOURCE_NO_FLAGS) - 1)));\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Compile the binary\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[0] = \"gcc\";\r\n        Argv[1] = BINFMT_INTERPRETER_BINARY_SOURCEFILE;\r\n        Argv[2] = \"-o\";\r\n        Argv[3] = BINFMT_INTERPRETER_BINARY;\r\n        Argv[4] = NULL;\r\n        LxtCheckErrno(execv(\"/usr/bin/gcc\", Argv));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, BINFMT_TIMEOUT));\r\n\r\n    //\r\n    // Register a binfmt extension without flags.\r\n    //\r\n\r\n    LxtCheckErrno(RegisterFd = open(BINFMT_MNT \"/register\", O_WRONLY));\r\n    Size = strlen(BINFMT_REGISTER_BINARY_STRING);\r\n    LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_BINARY_STRING, Size));\r\n\r\n    //\r\n    // Fork and exec the file.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(LxtSetresuid(BINFMT_CALLER_UID, BINFMT_CALLER_UID, BINFMT_CALLER_UID));\r\n        ExecArgs[0] = BINFMT_TEST_FILE;\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, 0, 0, BINFMT_TIMEOUT));\r\n\r\n    //\r\n    // Unregister the interpreter.\r\n    //\r\n\r\n    BimFmtCleanup();\r\n\r\n    //\r\n    // Create the binfmt interpreter that handles the 'O' flag.\r\n    //\r\n\r\n    LxtLogInfo(\"Testing 'O' flag\");\r\n    LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_BINARY_SOURCEFILE, 0777));\r\n    LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_BINARY_SOURCE_O_FLAG, (sizeof(BINFMT_INTERPRETER_BINARY_SOURCE_O_FLAG) - 1)));\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Compile the binary\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[0] = \"gcc\";\r\n        Argv[1] = BINFMT_INTERPRETER_BINARY_SOURCEFILE;\r\n        Argv[2] = \"-o\";\r\n        Argv[3] = BINFMT_INTERPRETER_BINARY;\r\n        Argv[4] = NULL;\r\n        LxtCheckErrno(execv(\"/usr/bin/gcc\", Argv));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, BINFMT_TIMEOUT));\r\n\r\n    //\r\n    // Register a binfmt extension.\r\n    //\r\n\r\n    LxtCheckErrno(RegisterFd = open(BINFMT_MNT \"/register\", O_WRONLY));\r\n    Size = strlen(BINFMT_REGISTER_BINARY_STRING_O);\r\n    LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_BINARY_STRING_O, Size));\r\n\r\n    //\r\n    // Fork and exec the file.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(LxtSetresuid(BINFMT_CALLER_UID, BINFMT_CALLER_UID, BINFMT_CALLER_UID));\r\n        ExecArgs[0] = BINFMT_TEST_FILE;\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, 0, 0, BINFMT_TIMEOUT));\r\n\r\n    //\r\n    // Unregister the interpreter.\r\n    //\r\n\r\n    BimFmtCleanup();\r\n\r\n    //\r\n    // Create the binfmt interpreter that handles the 'C' flag.\r\n    //\r\n\r\n    LxtLogInfo(\"Testing 'C' flag\");\r\n    LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_BINARY_SOURCEFILE, 0777));\r\n    LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_BINARY_SOURCE_C_FLAG, (sizeof(BINFMT_INTERPRETER_BINARY_SOURCE_C_FLAG) - 1)));\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Compile the binary\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[0] = \"gcc\";\r\n        Argv[1] = BINFMT_INTERPRETER_BINARY_SOURCEFILE;\r\n        Argv[2] = \"-o\";\r\n        Argv[3] = BINFMT_INTERPRETER_BINARY;\r\n        Argv[4] = NULL;\r\n        LxtCheckErrno(execv(\"/usr/bin/gcc\", Argv));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, BINFMT_TIMEOUT));\r\n\r\n    //\r\n    // Register a binfmt extension.\r\n    //\r\n\r\n    LxtCheckErrno(RegisterFd = open(BINFMT_MNT \"/register\", O_WRONLY));\r\n    Size = strlen(BINFMT_REGISTER_BINARY_STRING_C);\r\n    LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_BINARY_STRING_C, Size));\r\n\r\n    //\r\n    // Fork and exec the file.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(LxtSetresuid(BINFMT_CALLER_UID, BINFMT_CALLER_UID, BINFMT_CALLER_UID));\r\n        ExecArgs[0] = BINFMT_TEST_FILE;\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, 0, 0, BINFMT_TIMEOUT));\r\n\r\n    //\r\n    // Unregister the interpreter.\r\n    //\r\n\r\n    BimFmtCleanup();\r\n\r\n    //\r\n    // Create the binfmt interpreter that handles the 'P' flag.\r\n    //\r\n\r\n    LxtLogInfo(\"Testing 'P' flag\");\r\n    LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_BINARY_SOURCEFILE, 0777));\r\n    LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_BINARY_SOURCE_P_FLAG, (sizeof(BINFMT_INTERPRETER_BINARY_SOURCE_P_FLAG) - 1)));\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Compile the binary\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[0] = \"gcc\";\r\n        Argv[1] = BINFMT_INTERPRETER_BINARY_SOURCEFILE;\r\n        Argv[2] = \"-o\";\r\n        Argv[3] = BINFMT_INTERPRETER_BINARY;\r\n        Argv[4] = NULL;\r\n        LxtCheckErrno(execv(\"/usr/bin/gcc\", Argv));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, BINFMT_TIMEOUT));\r\n\r\n    //\r\n    // Register a binfmt extension.\r\n    //\r\n\r\n    LxtCheckErrno(RegisterFd = open(BINFMT_MNT \"/register\", O_WRONLY));\r\n    Size = strlen(BINFMT_REGISTER_BINARY_STRING_P);\r\n    LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_BINARY_STRING_P, Size));\r\n\r\n    //\r\n    // Fork and exec the file.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = BINFMT_TEST_FILE;\r\n        ExecArgs[1] = BINFMT_P_FLAG_ARG;\r\n        ExecArgs[2] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, 0, 0, BINFMT_TIMEOUT));\r\n\r\n    //\r\n    // Unregister the interpreter.\r\n    //\r\n\r\n    BimFmtCleanup();\r\n\r\nErrorExit:\r\n    if (RegisterFd > 0)\r\n    {\r\n        LxtClose(RegisterFd);\r\n    }\r\n\r\n    if (Fd > 0)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint BinFmtRoot(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the contents of the binfmt directory.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    // LxtCheckResult(LxtCheckStat(BINFMT_MNT, 1, DT_DIR));\r\n    LxtCheckResult(LxtCheckDirectoryContents(BINFMT_MNT, g_BinFmtRootChildren, LXT_COUNT_OF(g_BinFmtRootChildren)));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint BinFmtRegister(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the binfmt register file.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[128];\r\n    int Fd;\r\n    LXT_CHILD_INFO Registration;\r\n    int Result;\r\n    ssize_t Size;\r\n\r\n    //\r\n    // Clean up any previously registered interpreters.\r\n    //\r\n\r\n    BimFmtCleanup();\r\n\r\n    //\r\n    // Open the register file and verify that binfmt registrations are able to be registered.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/register\", O_WRONLY));\r\n\r\n    Size = strlen(BINFMT_REGISTER_SCRIPT_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_REGISTER_SCRIPT_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    Registration.Name = BINFMT_REGISTER_NAME;\r\n    Registration.FileType = DT_REG;\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(BINFMT_MNT, &Registration, 1));\r\n\r\n    //\r\n    // Open the registration file and verify that it behaves as expected.\r\n    // Status should initially be enabled.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR));\r\n    LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n    Buffer[Size] = '\\0';\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckStringEqual(Buffer, BINFMT_REGISTRATION_ENABLED_STRING);\r\n\r\n    //\r\n    // Disable the registration and verify the string changes.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR));\r\n    Size = strlen(BINFMT_DISABLE_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_DISABLE_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR));\r\n    LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n    Buffer[Size] = '\\0';\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckStringEqual(Buffer, BINFMT_REGISTRATION_DISABLED_STRING);\r\n\r\n    //\r\n    // Enable and verify the string changes.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR));\r\n    Size = strlen(BINFMT_ENABLE_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_ENABLE_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR));\r\n    LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n    Buffer[Size] = '\\0';\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckStringEqual(Buffer, BINFMT_REGISTRATION_ENABLED_STRING);\r\n\r\n    //\r\n    // Remove the new entry via the registration file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR));\r\n    Size = strlen(BINFMT_REMOVE_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Attempt to open the file (should fail);\r\n    //\r\n\r\n    LxtCheckErrnoFailure(Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR), ENOENT);\r\n\r\nErrorExit:\r\n    if (Fd > 0)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint BinFmtStatus(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the binfmt status file.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[64];\r\n    int Fd;\r\n    LXT_CHILD_INFO Registration;\r\n    int Result;\r\n    ssize_t Size;\r\n\r\n    //\r\n    // Open the status file and verify that status behaves as expected.\r\n    // Status should initially be enabled.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/status\", O_RDWR));\r\n    LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n    Buffer[Size] = '\\0';\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckStringEqual(Buffer, BINFMT_STATUS_ENABLED);\r\n\r\n    //\r\n    // Disable status and verify the string changes.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/status\", O_RDWR));\r\n    Size = strlen(BINFMT_DISABLE_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_DISABLE_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/status\", O_RDWR));\r\n    LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n    Buffer[Size] = '\\0';\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckStringEqual(Buffer, BINFMT_STATUS_DISABLED);\r\n\r\n    //\r\n    // Enable and verify the string changes.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/status\", O_RDWR));\r\n    Size = strlen(BINFMT_ENABLE_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_ENABLE_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/status\", O_RDWR));\r\n    LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n    Buffer[Size] = '\\0';\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckStringEqual(Buffer, BINFMT_STATUS_ENABLED);\r\n\r\n    //\r\n    // Register a binfmt extension and verify that it is removed when\r\n    // -1 is written to the status file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/register\", O_RDWR));\r\n    Size = strlen(BINFMT_REGISTER_SCRIPT_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_REGISTER_SCRIPT_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    Registration.Name = BINFMT_REGISTER_NAME;\r\n    Registration.FileType = DT_REG;\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(BINFMT_MNT, &Registration, 1));\r\n\r\n    //\r\n    // Remove the new entry via the status file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(BINFMT_MNT \"/status\", O_RDWR));\r\n    Size = strlen(BINFMT_REMOVE_STRING);\r\n    LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Attempt to open the file (should fail);\r\n    //\r\n\r\n    LxtCheckErrnoFailure(Fd = open(BINFMT_MNT \"/\" BINFMT_REGISTER_NAME, O_RDWR), ENOENT);\r\n\r\nErrorExit:\r\n    if (Fd > 0)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint BinFmtInterpreterEntry(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine implements the entry point for the test binfmt interpreter.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n    int Result;\r\n    printf(\"Pid = %d\\n\", getpid());\r\n    for (Index = 0; Index < Args->Argc; Index += 1)\r\n    {\r\n        printf(\"Argv[%d]: %s\\n\", Index, Args->Argv[Index]);\r\n    }\r\n\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/brk.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    brk.c\r\n\r\nAbstract:\r\n\r\n    This file is the brk test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <unistd.h>\r\n#include <stdio.h>\r\n\r\n#define LXT_NAME \"brk\"\r\n\r\nint BrkTest(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Brk Test\", BrkTest},\r\n};\r\n\r\nint BrkTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint BrkTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int Status;\r\n    char* BreakAddress;\r\n    char* NewBreakAddress;\r\n    int PageSize;\r\n\r\n    //\r\n    // Get current brk address.\r\n    //\r\n\r\n    LxtLogInfo(\"Getting current break address\");\r\n    BreakAddress = (char*)sbrk(0);\r\n    LxtLogInfo(\"Current break address is 0x%p\", BreakAddress);\r\n\r\n    //\r\n    // Increase the brk address.\r\n    //\r\n\r\n    PageSize = 4096;\r\n    BreakAddress += PageSize;\r\n    Status = brk(BreakAddress);\r\n    if (Status != 0)\r\n    {\r\n        LxtLogError(\"Brk call to increase the address failed\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    NewBreakAddress = (char*)sbrk(0);\r\n    LxtLogInfo(\"New break address 0x%p\", NewBreakAddress);\r\n    if ((NewBreakAddress < BreakAddress) || (NewBreakAddress > (BreakAddress + PageSize)))\r\n    {\r\n        LxtLogError(\r\n            \"The returned brk address does not match the expected \\\r\n            break address\");\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n    LxtLogInfo(\"New Break address set!\");\r\n\r\n    //\r\n    // Decrease the break address.\r\n    //\r\n\r\n    BreakAddress = (NewBreakAddress - PageSize);\r\n    LxtLogInfo(\"Decreasing the break address by a page\");\r\n    Status = brk(BreakAddress);\r\n    if (Status != 0)\r\n    {\r\n        LxtLogError(\"Brk call to decrease the address failed\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    NewBreakAddress = (char*)sbrk(0);\r\n    if (NewBreakAddress != BreakAddress)\r\n    {\r\n        LxtLogError(\r\n            \"The returned brk address after decreasing did not match\\\r\n            the expected break address\");\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}"
  },
  {
    "path": "test/linux/unit_tests/build_tests.sh",
    "content": "make -j8 -k -C /data/test"
  },
  {
    "path": "test/linux/unit_tests/cgroup.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    cgroup.c\r\n\r\nAbstract:\r\n\r\n    This file contains unit tests for cgroup support.\r\n\r\n    N.B. This test depends on libmount, which is part of the libmount-dev\r\n         apt package.\r\n\r\n    N.B. To pass on native Linux this test requires cgroups to not be managed by\r\n         an OS daemon. cgclear can be used to remove some subsystems.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/mount.h>\r\n#include <linux/capability.h>\r\n#include <libgen.h>\r\n#include <fcntl.h>\r\n#include <stdlib.h>\r\n#include <libmount/libmount.h>\r\n#include <sys/mman.h>\r\n#include \"lxtmount.h\"\r\n#include <unistd.h>\r\n#include <dirent.h>\r\n#include <fcntl.h>\r\n\r\n#define LXT_NAME \"cgroup\"\r\n#define CGROUP_TEST_PATH \"/data\"\r\n#define CGROUP_TEST_MOUNT_NAME \"cgroup\"\r\n#define CGROUP_TEST_MOUNT_POINT_NAME \"cgroup_mount_test\"\r\n#define CGROUP_TEST_MOUNT_POINT CGROUP_TEST_PATH \"/\" CGROUP_TEST_MOUNT_POINT_NAME\r\n#define CGROUP_TEST_MOUNT_POINT2 \"/sys/fs/cgroup\"\r\n\r\n#define CGROUP_TEST_MOUNT_POINT_DIR1_NAME \"dir1\"\r\n#define CGROUP_TEST_MOUNT_POINT_DIR1 CGROUP_TEST_MOUNT_POINT \"/\" CGROUP_TEST_MOUNT_POINT_DIR1_NAME\r\n#define CGROUP_TEST_MOUNT_POINT2_DIR1 CGROUP_TEST_MOUNT_POINT2 \"/\" CGROUP_TEST_MOUNT_POINT_DIR1_NAME\r\n#define CGROUP_TEST_MOUNT_POINT_DIR1_CHILD_NAME \"child\"\r\n#define CGROUP_TEST_MOUNT_POINT_DIR1_CHILD CGROUP_TEST_MOUNT_POINT_DIR1 \"/\" CGROUP_TEST_MOUNT_POINT_DIR1_CHILD_NAME\r\n\r\n#define CGROUP_TEST_DEFAULT_BUFFER_SIZE 128\r\n#define CGROUP_TEST_MAX_CGROUPS 12\r\n#define CGROUP_TEST_MAX_NAME_LENGTH 32\r\n\r\n#define CGROUP_TEST_MAX_PIDS 2048\r\n\r\n#define CGROUP_TEST_DEVICES_DEFAULT_LIST \"a *:* rwm\\n\"\r\n\r\nLXT_VARIATION_HANDLER CgroupTestBasicMount;\r\nLXT_VARIATION_HANDLER CgroupTestMkdir;\r\nLXT_VARIATION_HANDLER CgroupTestThreads;\r\nLXT_VARIATION_HANDLER CgroupTestProcfs;\r\nLXT_VARIATION_HANDLER CgroupTestProcsFile;\r\nLXT_VARIATION_HANDLER CgroupTestMountReuse;\r\nLXT_VARIATION_HANDLER CgroupTestDevices;\r\n\r\n//\r\n// TODO_LX: Enable all files when supported.\r\n//\r\n\r\nstatic const LXT_CHILD_INFO g_CgroupRootChildren[] = {\r\n    {\"cgroup.sane_behavior\", DT_REG},\r\n    /* {\"cgroup.clone_children\", DT_REG},\r\n    {\"cgroup.event_control\", DT_REG}, */\r\n    {\"cgroup.procs\", DT_REG}};\r\n/* {\"notify_on_release\", DT_REG},\r\n {\"release_agent\", DT_REG},\r\n {\"tasks\", DT_REG}};*/\r\n\r\nstatic const LXT_CHILD_INFO g_CgroupDefaultChildren[] = {/* {\"cgroup.clone_children\", DT_REG},\r\n                                                         {\"cgroup.event_control\", DT_REG}, */\r\n                                                         {\"cgroup.procs\", DT_REG}};\r\n/* {\"notify_on_release\", DT_REG},\r\n {\"release_agent\", DT_REG},\r\n {\"tasks\", DT_REG}};*/\r\n\r\nstatic const LXT_CHILD_INFO g_CgroupDevicesChildren[] = {{\"devices.allow\", DT_REG}, {\"devices.deny\", DT_REG}, {\"devices.list\", DT_REG}};\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"cgroup - basic mount\", CgroupTestBasicMount},\r\n    {\"cgroup - mkdir\", CgroupTestMkdir},\r\n    {\"cgroup - threads\", CgroupTestThreads},\r\n    {\"cgroup - procfs\", CgroupTestProcfs},\r\n    {\"cgroup - cgroup.procs file\", CgroupTestProcsFile},\r\n    {\"cgroup - mount reuse\", CgroupTestMountReuse},\r\n    {\"cgroup - devices subsystem\", CgroupTestDevices}};\r\n\r\nstatic int g_TestPathMountId;\r\n\r\nint CgroupTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    //\r\n    // Clean-up from previous iterations.\r\n    //\r\n\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    umount(CGROUP_TEST_MOUNT_POINT2);\r\n    umount(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT2);\r\n\r\n    //\r\n    // Run the test variations.\r\n    //\r\n\r\n    LxtCheckResult(g_TestPathMountId = MountGetMountId(CGROUP_TEST_PATH));\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\n    //\r\n    // Mount cgroup with a folder to test the instance uninitialize path.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, \"devices\"));\r\n\r\n    LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint CgroupTestBasicMount(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the mount and umount system calls for cgroups.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int MountId;\r\n    int Result;\r\n\r\n    //\r\n    // Create the directory and ensure it's not a mount point yet.\r\n    //\r\n    LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777));\r\n    LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT));\r\n\r\n    //\r\n    // Mount a cgroup instance and check it was mounted.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, NULL));\r\n\r\n    LxtCheckResult(\r\n        MountId = MountCheckIsMount(\r\n            CGROUP_TEST_MOUNT_POINT,\r\n            g_TestPathMountId,\r\n            \"mycgroupnew\",\r\n            CGROUP_TEST_MOUNT_NAME,\r\n            \"/\",\r\n            \"rw,relatime\",\r\n            \"rw,devices\",\r\n            \"rw,relatime,devices\",\r\n            0));\r\n\r\n    //\r\n    // Mounting again should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, NULL), EBUSY);\r\n\r\n    LxtCheckErrnoFailure(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, \"devices\"), EBUSY);\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT));\r\n    LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT));\r\n\r\nErrorExit:\r\n    umount(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT);\r\n    return Result;\r\n}\r\n\r\nint CgroupTestMkdir(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the mkdir and rmdir system calls for cgroups.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_CHILD_INFO ChildInfo;\r\n    char ChildPidBuffer[CGROUP_TEST_DEFAULT_BUFFER_SIZE];\r\n    int ChildPidBufferLength;\r\n    char Path[512];\r\n    int ProcsFd;\r\n    int MountId;\r\n    int Result;\r\n\r\n    ProcsFd = -1;\r\n\r\n    //\r\n    // Mount cgroup.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, \"devices\"));\r\n\r\n    //\r\n    // Removing the mount point root directory should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rmdir(CGROUP_TEST_MOUNT_POINT), EBUSY);\r\n\r\n    //\r\n    // Create two subdirectories.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT, g_CgroupRootChildren, LXT_COUNT_OF(g_CgroupRootChildren)));\r\n\r\n    LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777));\r\n    ChildInfo.FileType = DT_DIR;\r\n    ChildInfo.Name = CGROUP_TEST_MOUNT_POINT_DIR1_NAME;\r\n    LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT, &ChildInfo, 1));\r\n\r\n    LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD, 0777));\r\n    ChildInfo.Name = CGROUP_TEST_MOUNT_POINT_DIR1_CHILD_NAME;\r\n    LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT_DIR1, &ChildInfo, 1));\r\n\r\n    //\r\n    // Removing the first directory should fail if the second one still exists,\r\n    // otherwise it succeeds.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1), EBUSY);\r\n    LxtCheckErrno(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD));\r\n    LxtCheckErrno(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1));\r\n\r\n    //\r\n    // Check that removing the first directory fails if a thread is still\r\n    // associated; otherwise, it succeeds.\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777));\r\n    sprintf(Path, \"%s/%s\", CGROUP_TEST_MOUNT_POINT_DIR1, \"cgroup.procs\");\r\n    LxtCheckErrno(ProcsFd = open(Path, O_WRONLY));\r\n    ChildPidBufferLength = sprintf(ChildPidBuffer, \"%d\\n\", getpid());\r\n    LxtCheckErrno(write(ProcsFd, ChildPidBuffer, ChildPidBufferLength));\r\n    LxtClose(ProcsFd);\r\n    ProcsFd = -1;\r\n    LxtCheckErrnoFailure(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1), EBUSY);\r\n    sprintf(Path, \"%s/%s\", CGROUP_TEST_MOUNT_POINT, \"cgroup.procs\");\r\n    LxtCheckErrno(ProcsFd = open(Path, O_WRONLY));\r\n    LxtCheckErrno(write(ProcsFd, ChildPidBuffer, ChildPidBufferLength));\r\n    LxtClose(ProcsFd);\r\n    ProcsFd = -1;\r\n    LxtCheckErrno(rmdir(CGROUP_TEST_MOUNT_POINT_DIR1));\r\n\r\n    //\r\n    // Unmount cgroup.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT));\r\n    LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT));\r\n\r\nErrorExit:\r\n    if (ProcsFd != -1)\r\n    {\r\n        LxtClose(ProcsFd);\r\n    }\r\n\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    umount(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT);\r\n    return Result;\r\n}\r\n\r\nint CgroupTestThreads(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests thread behavior with cgroups mounts.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int MountId;\r\n    LXT_PIPE Pipe = {-1, -1};\r\n    int Result;\r\n\r\n    //\r\n    // Create a thread, mount cgroup, and signal the thread to exit to test\r\n    // cgroup assignment during mount.\r\n    //\r\n\r\n    LxtCheckResult(LxtCreatePipe(&Pipe));\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        read(Pipe.Read, &Result, sizeof(Result));\r\n        _exit(Result);\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, NULL));\r\n\r\n    write(Pipe.Write, &Result, sizeof(Result));\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    //\r\n    // Create a thread to test cgroup inheritance.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    //\r\n    // Unmount and exit.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT));\r\n    LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT));\r\n\r\nErrorExit:\r\n    write(Pipe.Write, &Result, sizeof(Result));\r\n    LxtClosePipe(&Pipe);\r\n    umount(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT);\r\n    return Result;\r\n}\r\n\r\ntypedef struct _CGROUP_TEST_PROCFS_ENTRY\r\n{\r\n    char Name[CGROUP_TEST_MAX_NAME_LENGTH];\r\n    int Hierarchy;\r\n    int NumCgroups;\r\n    int Enabled;\r\n} CGROUP_TEST_PROCFS_ENTRY, *PCGROUP_TEST_PROCFS_ENTRY;\r\n\r\ntypedef struct _CGROUP_TEST_PROCFS\r\n{\r\n    int EntryCount;\r\n    CGROUP_TEST_PROCFS_ENTRY Entries[CGROUP_TEST_MAX_CGROUPS];\r\n} CGROUP_TEST_PROCFS, *PCGROUP_TEST_PROCFS;\r\n\r\nint CgroupTestReadProcfs(PCGROUP_TEST_PROCFS Data)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine parses /proc/cgroups.\r\n\r\nArguments:\r\n\r\n    Data - Supplies a buffer to store the data\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Line[CGROUP_TEST_DEFAULT_BUFFER_SIZE];\r\n    FILE* CgroupFile;\r\n    int NumCgroups;\r\n    int Result;\r\n\r\n    memset(Data, 0, sizeof(*Data));\r\n    CgroupFile = fopen(\"/proc/cgroups\", \"r\");\r\n    LxtCheckNotEqual(CgroupFile, NULL, \"%p\");\r\n    if (fgets(Line, LXT_COUNT_OF(Line), CgroupFile) == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Failed to read header\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckStringEqual(Line, \"#subsys_name\\thierarchy\\tnum_cgroups\\tenabled\\n\");\r\n    NumCgroups = 0;\r\n    while (fgets(Line, LXT_COUNT_OF(Line), CgroupFile) != NULL)\r\n    {\r\n        sscanf(\r\n            Line,\r\n            \"%s\\t%d\\t%d\\t%d\",\r\n            Data->Entries[NumCgroups].Name,\r\n            &Data->Entries[NumCgroups].Hierarchy,\r\n            &Data->Entries[NumCgroups].NumCgroups,\r\n            &Data->Entries[NumCgroups].Enabled);\r\n\r\n        NumCgroups += 1;\r\n    }\r\n\r\n    Data->EntryCount = NumCgroups;\r\n\r\nErrorExit:\r\n    if (CgroupFile != NULL)\r\n    {\r\n        fclose(CgroupFile);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\ntypedef struct _CGROUP_TEST_PROCFS_PID_ENTRY\r\n{\r\n    int Hierarchy;\r\n    char Subsystems[CGROUP_TEST_DEFAULT_BUFFER_SIZE];\r\n    char CgroupPath[CGROUP_TEST_DEFAULT_BUFFER_SIZE];\r\n} CGROUP_TEST_PROCFS_PID_ENTRY, *PCGROUP_TEST_PROCFS_PID_ENTRY;\r\n\r\ntypedef struct _CGROUP_TEST_PROCFS_PID\r\n{\r\n    int EntryCount;\r\n    CGROUP_TEST_PROCFS_PID_ENTRY Entries[CGROUP_TEST_MAX_CGROUPS];\r\n} CGROUP_TEST_PROCFS_PID, *PCGROUP_TEST_PROCFS_PID;\r\n\r\nint CgroupTestReadProcfsPid(PCGROUP_TEST_PROCFS_PID Data)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine parses /proc/self/cgroup.\r\n\r\nArguments:\r\n\r\n    Data - Supplies a buffer to store the data\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Line[CGROUP_TEST_DEFAULT_BUFFER_SIZE];\r\n    FILE* CgroupFile;\r\n    int NumCgroups;\r\n    int Result;\r\n\r\n    memset(Data, 0, sizeof(*Data));\r\n    CgroupFile = fopen(\"/proc/self/cgroup\", \"r\");\r\n    LxtCheckNotEqual(CgroupFile, NULL, \"%p\");\r\n\r\n    NumCgroups = 0;\r\n    while (fgets(Line, LXT_COUNT_OF(Line), CgroupFile) != NULL)\r\n    {\r\n        sscanf(\r\n            Line,\r\n            \"%d:%[^:]:%[^:\\n]\",\r\n            &Data->Entries[NumCgroups].Hierarchy,\r\n            Data->Entries[NumCgroups].Subsystems,\r\n            Data->Entries[NumCgroups].CgroupPath);\r\n\r\n        NumCgroups += 1;\r\n    }\r\n\r\n    Data->EntryCount = NumCgroups;\r\n\r\nErrorExit:\r\n    if (CgroupFile != NULL)\r\n    {\r\n        fclose(CgroupFile);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint CgroupTestProcfs(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the cgroup procfs files.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesRead;\r\n    int Found;\r\n    int Index;\r\n    int MountId;\r\n    CGROUP_TEST_PROCFS ProcfsNew;\r\n    CGROUP_TEST_PROCFS ProcfsOrig;\r\n    CGROUP_TEST_PROCFS_PID ProcfsPidNew;\r\n    CGROUP_TEST_PROCFS_PID ProcfsPidOrig;\r\n    int Result;\r\n\r\n    //\r\n    // Create the cgroup mount.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, \"devices\"));\r\n\r\n    //\r\n    // Read the procfs files before cgroups are mounted, staring\r\n    // with /proc/cgroup.\r\n    //\r\n\r\n    LxtCheckResult(CgroupTestReadProcfs(&ProcfsNew));\r\n    LxtCheckNotEqual(ProcfsNew.EntryCount, 0, \"%d\");\r\n    Found = 0;\r\n    for (Index = 0; Index < ProcfsNew.EntryCount; ++Index)\r\n    {\r\n        LxtCheckNotEqual(ProcfsNew.Entries[Index].NumCgroups, 0, \"%d\");\r\n        LxtCheckEqual(ProcfsNew.Entries[Index].Enabled, 1, \"%d\");\r\n        if (strcmp(ProcfsNew.Entries[Index].Name, \"devices\") == 0)\r\n        {\r\n            LxtCheckNotEqual(Found, 1, \"%d\");\r\n            LxtCheckNotEqual(ProcfsNew.Entries[Index].Hierarchy, 0, \"%d\");\r\n            Found = 1;\r\n        }\r\n    }\r\n\r\n    LxtCheckEqual(Found, 1, \"%d\");\r\n\r\n    //\r\n    // Now /proc/self/cgroup.\r\n    //\r\n\r\n    LxtCheckResult(CgroupTestReadProcfsPid(&ProcfsPidNew));\r\n    Found = 0;\r\n    for (Index = 0; Index < ProcfsPidNew.EntryCount; ++Index)\r\n    {\r\n        if (strstr(ProcfsPidNew.Entries[Index].Subsystems, \"devices\") != NULL)\r\n        {\r\n            LxtCheckNotEqual(Found, 1, \"%d\");\r\n            LxtCheckNotEqual(ProcfsPidNew.Entries[Index].Hierarchy, 0, \"%d\");\r\n            LxtCheckStringEqual(ProcfsPidNew.Entries[Index].CgroupPath, \"/\");\r\n            Found = 1;\r\n        }\r\n    }\r\n\r\n    LxtCheckEqual(Found, 1, \"%d\");\r\n\r\n    //\r\n    // Unmount and recheck the original.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT));\r\n    LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT));\r\n\r\nErrorExit:\r\n    umount(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT);\r\n    return Result;\r\n}\r\n\r\nint CgroupTestGetProcsFileIds(char* CgroupPath, pid_t* IdArray[], int* IdArrayCount)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the behavior of the cgroup.procs file.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path to query.\r\n\r\n    IdArray - Supplies a buffer to store the array of ids.\r\n\r\n    IdArrayCount - Supplies a buffer to store the number of ids.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t* Array;\r\n    int Count;\r\n    int Index;\r\n    FILE* File;\r\n    char Line[CGROUP_TEST_DEFAULT_BUFFER_SIZE];\r\n    char Path[512];\r\n    int PathLength;\r\n    int Result;\r\n\r\n    Array = NULL;\r\n    File = NULL;\r\n    sprintf(Path, \"%s/%s\", CgroupPath, \"cgroup.procs\");\r\n    File = fopen(Path, \"r\");\r\n    LxtCheckNotEqual(File, NULL, \"%p\");\r\n    Array = LxtAlloc(sizeof(*Array) * CGROUP_TEST_MAX_PIDS);\r\n    if (Array == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Count = 0;\r\n    while (fgets(Line, LXT_COUNT_OF(Line), File) != NULL)\r\n    {\r\n        if (Count > CGROUP_TEST_MAX_PIDS)\r\n        {\r\n            LxtLogError(\"Unexpected count\");\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        sscanf(Line, \"%d\\n\", &Array[Count]);\r\n        Count += 1;\r\n    }\r\n\r\n    for (Index = 1; Index < Count; ++Index)\r\n    {\r\n        if (Array[Index - 1] >= Array[Index])\r\n        {\r\n            LxtLogError(\"Unexpected value %d, %d\", Array[Index - 1], Array[Index]);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    *IdArray = Array;\r\n    Array = NULL;\r\n    *IdArrayCount = Count;\r\n\r\nErrorExit:\r\n    if (File != NULL)\r\n    {\r\n        fclose(File);\r\n    }\r\n\r\n    if (Array != NULL)\r\n    {\r\n        LxtFree(Array);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint CgroupTestProcsFile(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the behavior of the cgroup.procs file.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t* Array;\r\n    pid_t ChildPid;\r\n    char ChildPidBuffer[CGROUP_TEST_DEFAULT_BUFFER_SIZE];\r\n    int ChildPidBufferLength;\r\n    int Count;\r\n    int Index;\r\n    int MountId;\r\n    char Path[512];\r\n    int ProcsFd;\r\n    LXT_PIPE Pipe = {-1, -1};\r\n    int Result;\r\n\r\n    Array = NULL;\r\n    ProcsFd = -1;\r\n    LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, \"devices\"));\r\n\r\n    //\r\n    // Create a threadgroup and check that it is in the root folder.\r\n    //\r\n\r\n    LxtCheckResult(LxtCreatePipe(&Pipe));\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        read(Pipe.Read, &Result, sizeof(Result));\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT, &Array, &Count));\r\n    for (Index = 0; Index < Count; ++Index)\r\n    {\r\n        if (Array[Index] == ChildPid)\r\n        {\r\n            break;\r\n        }\r\n    }\r\n\r\n    LxtCheckNotEqual(Index, Count, \"%d\");\r\n    LxtFree(Array);\r\n    Array = NULL;\r\n\r\n    //\r\n    // Create a folder and check that it is empty.\r\n    //\r\n\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777));\r\n    LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT_DIR1, &Array, &Count));\r\n    LxtCheckEqual(0, Count, \"%d\");\r\n    LxtFree(Array);\r\n    Array = NULL;\r\n\r\n    //\r\n    // Move the thread to the folder and check that the thread was moved.\r\n    //\r\n\r\n    sprintf(Path, \"%s/%s\", CGROUP_TEST_MOUNT_POINT_DIR1, \"cgroup.procs\");\r\n    LxtCheckErrno(ProcsFd = open(Path, O_WRONLY));\r\n    ChildPidBufferLength = sprintf(ChildPidBuffer, \"%d\\n\", ChildPid);\r\n    LxtCheckErrno(write(ProcsFd, ChildPidBuffer, ChildPidBufferLength));\r\n    LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT_DIR1, &Array, &Count));\r\n    for (Index = 0; Index < Count; ++Index)\r\n    {\r\n        if (Array[Index] == ChildPid)\r\n        {\r\n            break;\r\n        }\r\n    }\r\n\r\n    LxtCheckNotEqual(Index, Count, \"%d\");\r\n    LxtFree(Array);\r\n    Array = NULL;\r\n\r\n    LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT, &Array, &Count));\r\n    for (Index = 0; Index < Count; ++Index)\r\n    {\r\n        if (Array[Index] == ChildPid)\r\n        {\r\n            break;\r\n        }\r\n    }\r\n\r\n    LxtCheckEqual(Index, Count, \"%d\");\r\n    LxtFree(Array);\r\n    Array = NULL;\r\n    LxtClose(ProcsFd);\r\n    ProcsFd = -1;\r\n\r\n    //\r\n    // Move the thread to the root and check that the thread was moved.\r\n    //\r\n\r\n    sprintf(Path, \"%s/%s\", CGROUP_TEST_MOUNT_POINT, \"cgroup.procs\");\r\n    LxtCheckErrno(ProcsFd = open(Path, O_WRONLY));\r\n    ChildPidBufferLength = sprintf(ChildPidBuffer, \"%d\\n\", ChildPid);\r\n    LxtCheckErrno(write(ProcsFd, ChildPidBuffer, ChildPidBufferLength));\r\n    LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT_DIR1, &Array, &Count));\r\n    LxtCheckEqual(0, Count, \"%d\");\r\n    LxtFree(Array);\r\n    Array = NULL;\r\n\r\n    LxtCheckResult(CgroupTestGetProcsFileIds(CGROUP_TEST_MOUNT_POINT, &Array, &Count));\r\n    for (Index = 0; Index < Count; ++Index)\r\n    {\r\n        if (Array[Index] == ChildPid)\r\n        {\r\n            break;\r\n        }\r\n    }\r\n\r\n    LxtCheckNotEqual(Index, Count, \"%d\");\r\n    LxtFree(Array);\r\n    Array = NULL;\r\n    LxtClose(ProcsFd);\r\n    ProcsFd = -1;\r\n\r\n    //\r\n    // Unmount and exit.\r\n    //\r\n\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT));\r\n    LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT));\r\n\r\nErrorExit:\r\n    write(Pipe.Write, &Result, sizeof(Result));\r\n    LxtClosePipe(&Pipe);\r\n    if (Array != NULL)\r\n    {\r\n        LxtFree(Array);\r\n    }\r\n\r\n    if (ProcsFd != -1)\r\n    {\r\n        LxtClose(ProcsFd);\r\n    }\r\n\r\n    umount(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    return Result;\r\n}\r\n\r\nint CgroupTestMountReuse(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the behavior of reusing cgroup hierarchy mounts.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_CHILD_INFO ChildInfo;\r\n    char ChildPidBuffer[CGROUP_TEST_DEFAULT_BUFFER_SIZE];\r\n    int ChildPidBufferLength;\r\n    int Found;\r\n    int Index;\r\n    char Path[512];\r\n    int ProcsFd;\r\n    CGROUP_TEST_PROCFS ProcfsNew;\r\n    int MountId;\r\n    int Result;\r\n\r\n    ProcsFd = -1;\r\n\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    umount(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT);\r\n\r\n    //\r\n    // Mount cgroup.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, \"devices\"));\r\n\r\n    //\r\n    // A cgroup with a directory should be reported as active when unmounted\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777));\r\n    LxtCheckErrno(access(CGROUP_TEST_MOUNT_POINT_DIR1, F_OK));\r\n    LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT));\r\n    LxtCheckErrnoFailure(access(CGROUP_TEST_MOUNT_POINT_DIR1, F_OK), ENOENT);\r\n    LxtCheckResult(CgroupTestReadProcfs(&ProcfsNew));\r\n    LxtCheckNotEqual(ProcfsNew.EntryCount, 0, \"%d\");\r\n    Found = 0;\r\n    for (Index = 0; Index < ProcfsNew.EntryCount; ++Index)\r\n    {\r\n        LxtCheckNotEqual(ProcfsNew.Entries[Index].NumCgroups, 0, \"%d\");\r\n        LxtCheckEqual(ProcfsNew.Entries[Index].Enabled, 1, \"%d\");\r\n        if (strcmp(ProcfsNew.Entries[Index].Name, \"devices\") == 0)\r\n        {\r\n            LxtCheckNotEqual(Found, 1, \"%d\");\r\n            LxtCheckNotEqual(ProcfsNew.Entries[Index].Hierarchy, 0, \"%d\");\r\n            Found = 1;\r\n        }\r\n    }\r\n    LxtCheckEqual(Found, 1, \"%d\");\r\n\r\n    //\r\n    // When remounted the directory is present\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, \"devices\"));\r\n\r\n    LxtCheckErrno(access(CGROUP_TEST_MOUNT_POINT_DIR1, F_OK));\r\n\r\n    //\r\n    // When that cgroup is mounted again, the directory should be present\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT2, CGROUP_TEST_MOUNT_NAME, 0, \"devices\"));\r\n\r\n    LxtCheckErrno(access(CGROUP_TEST_MOUNT_POINT2_DIR1, F_OK));\r\n    umount(CGROUP_TEST_MOUNT_POINT2);\r\n\r\n    //\r\n    // Failing variation to check the mount all case.\r\n    //\r\n    // TODO_LX: This variation needs to be updated once multiple subsystems are\r\n    //          supported.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT2, CGROUP_TEST_MOUNT_NAME, 0, NULL));\r\n\r\n    LxtCheckErrno(access(CGROUP_TEST_MOUNT_POINT2_DIR1, F_OK));\r\n\r\n    //\r\n    // Unmount and exit.\r\n    //\r\n\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT2));\r\n    LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT));\r\n    LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT));\r\n\r\nErrorExit:\r\n    if (ProcsFd != -1)\r\n    {\r\n        LxtClose(ProcsFd);\r\n    }\r\n\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1_CHILD);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    umount(CGROUP_TEST_MOUNT_POINT2);\r\n    umount(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT2);\r\n    return Result;\r\n}\r\n\r\nint CgroupTestDevices(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the files for the devices subsystem.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesRead;\r\n    int CgroupFd;\r\n    char DevicesListBuffer[32];\r\n    char Path[512];\r\n    char PidBuffer[CGROUP_TEST_DEFAULT_BUFFER_SIZE];\r\n    int PidBufferLength;\r\n    int Result;\r\n\r\n    CgroupFd = -1;\r\n    PidBufferLength = sprintf(PidBuffer, \"%d\\n\", getpid());\r\n\r\n    //\r\n    // Mount cgroup.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(CGROUP_TEST_MOUNT_POINT, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(\"mycgroupnew\", CGROUP_TEST_MOUNT_POINT, CGROUP_TEST_MOUNT_NAME, 0, \"devices\"));\r\n\r\n    //\r\n    // Check for the expected default files and devices files in the root.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT, g_CgroupRootChildren, LXT_COUNT_OF(g_CgroupRootChildren)));\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT, g_CgroupDevicesChildren, LXT_COUNT_OF(g_CgroupDevicesChildren)));\r\n\r\n    //\r\n    // Check for the expected default files and devices files in a subdirectory.\r\n    //\r\n    // N.B. A thread has to exist in the cgroup for some files to successfully\r\n    //      be read.\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(CGROUP_TEST_MOUNT_POINT_DIR1, 0777));\r\n    sprintf(Path, \"%s/%s\", CGROUP_TEST_MOUNT_POINT_DIR1, \"cgroup.procs\");\r\n    LxtCheckErrno(CgroupFd = open(Path, O_WRONLY));\r\n    LxtCheckErrno(write(CgroupFd, PidBuffer, PidBufferLength));\r\n    LxtClose(CgroupFd);\r\n    CgroupFd = -1;\r\n    LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT_DIR1, g_CgroupDefaultChildren, LXT_COUNT_OF(g_CgroupDefaultChildren)));\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(CGROUP_TEST_MOUNT_POINT_DIR1, g_CgroupDevicesChildren, LXT_COUNT_OF(g_CgroupDevicesChildren)));\r\n\r\n    //\r\n    // Check for the expected value of the devices.list file in both folders.\r\n    //\r\n\r\n    sprintf(Path, \"%s/%s\", CGROUP_TEST_MOUNT_POINT, \"devices.list\");\r\n    LxtCheckErrno(CgroupFd = open(Path, O_RDONLY));\r\n    LxtCheckErrno(BytesRead = read(CgroupFd, DevicesListBuffer, sizeof(DevicesListBuffer) - 1));\r\n    DevicesListBuffer[BytesRead] = 0;\r\n    LxtClose(CgroupFd);\r\n    CgroupFd = -1;\r\n    LxtCheckStringEqual(DevicesListBuffer, CGROUP_TEST_DEVICES_DEFAULT_LIST);\r\n\r\n    sprintf(Path, \"%s/%s\", CGROUP_TEST_MOUNT_POINT_DIR1, \"devices.list\");\r\n    LxtCheckErrno(CgroupFd = open(Path, O_RDONLY));\r\n    LxtCheckErrno(BytesRead = read(CgroupFd, DevicesListBuffer, sizeof(DevicesListBuffer) - 1));\r\n    DevicesListBuffer[BytesRead] = 0;\r\n    LxtClose(CgroupFd);\r\n    CgroupFd = -1;\r\n    LxtCheckStringEqual(DevicesListBuffer, CGROUP_TEST_DEVICES_DEFAULT_LIST);\r\n\r\n    //\r\n    // Unmount cgroup.\r\n    //\r\n\r\n    sprintf(Path, \"%s/%s\", CGROUP_TEST_MOUNT_POINT, \"cgroup.procs\");\r\n    LxtCheckErrno(CgroupFd = open(Path, O_WRONLY));\r\n    LxtCheckErrno(write(CgroupFd, PidBuffer, PidBufferLength));\r\n    LxtClose(CgroupFd);\r\n    CgroupFd = -1;\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    LxtCheckErrnoZeroSuccess(umount(CGROUP_TEST_MOUNT_POINT));\r\n    LxtCheckResult(MountCheckIsNotMount(CGROUP_TEST_MOUNT_POINT));\r\n\r\nErrorExit:\r\n    if (CgroupFd != -1)\r\n    {\r\n        LxtClose(CgroupFd);\r\n    }\r\n\r\n    rmdir(CGROUP_TEST_MOUNT_POINT_DIR1);\r\n    umount(CGROUP_TEST_MOUNT_POINT);\r\n    rmdir(CGROUP_TEST_MOUNT_POINT);\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/common.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    common.c\r\n\r\nAbstract:\r\n\r\n    Common socket definitions and helper routines.\r\n\r\n--*/\r\n\r\n#include <stdlib.h>\r\n#include <sys/socket.h>\r\n#include <sys/epoll.h>\r\n#include <stdbool.h>\r\n#include \"common.h\"\r\n#include \"lxtcommon.h\"\r\n\r\nint LxtSocketEpoll(int Descriptor, int Event, int Timeout)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks whether the given epoll is set in the file descriptor.\r\n\r\nArguments:\r\n\r\n    Descriptor - Supplies the descriptor.\r\n\r\n    Event - Supplies an event to check for.\r\n\r\n    Timeout - Supplies the timeout value in milliseconds.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct epoll_event EpollEvent;\r\n    int EpollFd;\r\n    int Iterator;\r\n    int NumberDescriptors;\r\n    int Result;\r\n\r\n    EpollFd = -1;\r\n    LxtCheckErrno(EpollFd = epoll_create(1));\r\n    EpollEvent.events = Event;\r\n    EpollEvent.data.fd = Descriptor;\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, Descriptor, &EpollEvent));\r\n    LxtCheckErrno(NumberDescriptors = epoll_wait(EpollFd, &EpollEvent, 1, Timeout));\r\n\r\n    //\r\n    // If no descriptors were ready within the timeout, that is an error condition\r\n    //\r\n\r\n    if (NumberDescriptors != 1)\r\n    {\r\n        LxtLogInfo(\"expecting epoll_wait to return 1, but it returned %d\", NumberDescriptors);\r\n\r\n        Result = -1;\r\n        errno = EAGAIN;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if ((EpollEvent.events & Event) == 0)\r\n    {\r\n        LxtLogError(\"epoll event(%d) is not set. Epoll event(s) set: %d\", Event, EpollEvent.events);\r\n\r\n        Result = -1;\r\n        errno = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (EpollFd != -1)\r\n    {\r\n        close(EpollFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid* SocketBlockedReaderThread(void* Arg)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will call read on the given fd and block.\r\n\r\nArguments:\r\n\r\n    Arg - Supplies the argument for the datagram server to operate.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[10] = \"123456789\";\r\n    ssize_t BytesRead;\r\n    int Fd;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    Fd = *(int*)Arg;\r\n    LxtCheckErrno(BytesRead = recv(Fd, Buffer, sizeof(Buffer), 0));\r\n    if (BytesRead != 0)\r\n    {\r\n        LxtLogError(\"recv should return 0 bytes read, but it returned %d bytes\", BytesRead);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"recv unblocked\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    pthread_exit((void*)(ssize_t)Result);\r\n}\r\n\r\nvoid* SocketBlockedReaderZeroBufferThread(void* Arg)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will call read on the given fd with a zero-byte receive buffer\r\n    and block.\r\n\r\nArguments:\r\n\r\n    Arg - Supplies the argument for the datagram server to operate.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[10] = \"123456789\";\r\n    ssize_t BytesRead;\r\n    int Fd;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    Fd = *(int*)Arg;\r\n    LxtCheckErrno(BytesRead = recv(Fd, Buffer, 0, 0));\r\n    if (BytesRead != 0)\r\n    {\r\n        LxtLogError(\"recv should return 0 bytes read, but it returned %d bytes\", BytesRead);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"recv unblocked\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    pthread_exit((void*)(ssize_t)Result);\r\n}\r\n\r\nstruct cmsghdr* SocketGetControlMessage(struct msghdr* MessageHeader, struct cmsghdr* StartControlMessage, int Level, int Type)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will return the control information at the given level and\r\n    type.\r\n\r\nArguments:\r\n\r\n    MessageHeader - Supplies the message header from which the control\r\n        information has to be extracted.\r\n\r\n    StartControlMessage - Supplies the control message from where to start.\r\n        NULL if the search has to start from the beginning.\r\n\r\n    Level - Supplies the level of the control information.\r\n\r\n    Type - Supplies the type of the control information.\r\n\r\nReturn Value:\r\n\r\n    Control message or NULL if it does not exist.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct cmsghdr* ControlMessage;\r\n\r\n    //\r\n    // Use the system macros to extract receive the ip packet control info.\r\n    //\r\n\r\n    ControlMessage = NULL;\r\n\r\n    //\r\n    // If the start control message is provided use that, else get the first\r\n    // control message. This is automatically handled by MY_CMSG_NXTHDR.\r\n    //\r\n\r\n    for (ControlMessage = MY_CMSG_NXTHDR(MessageHeader, StartControlMessage); ControlMessage != NULL;\r\n         ControlMessage = MY_CMSG_NXTHDR(MessageHeader, ControlMessage))\r\n    {\r\n\r\n        if (ControlMessage->cmsg_len < sizeof(struct cmsghdr))\r\n        {\r\n            break;\r\n        }\r\n\r\n        //\r\n        // Look for a match.\r\n        //\r\n\r\n        if ((ControlMessage->cmsg_level == Level) && (ControlMessage->cmsg_type == Type))\r\n        {\r\n\r\n            return ControlMessage;\r\n        }\r\n    }\r\n\r\n    return NULL;\r\n}\r\n\r\nvoid* SocketBlockedWriterThread(void* Arg)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will call write on the given fd and block.\r\n\r\nArguments:\r\n\r\n    Arg - Supplies the argument for the datagram server to operate.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[10] = \"123456789\";\r\n    ssize_t BytesWritten;\r\n    int Fd;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    Fd = *(int*)Arg;\r\n    LxtCheckErrnoFailure(send(Fd, Buffer, sizeof(Buffer), 0), EPIPE);\r\n    LxtLogInfo(\"send unblocked\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    pthread_exit((void*)(ssize_t)Result);\r\n}\r\n\r\nint SocketGetSetBooleanSocketOption(int Socket, int OptionLevel, int OptionName, bool SmallerSizeAllowed)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the getsockopt() and setsockopt() API for the\r\n    any of the boolean socket option.\r\n\r\nArguments:\r\n\r\n    Socket - Supplies the socket.\r\n\r\n    OptionLevel - Supplies the level at which the option has to be applied.\r\n\r\n    Option - Supplies the option to test.\r\n\r\n    SmallerSizeAllowed - Supplies a boolean indicating whether sizes smaller\r\n        than the size of the option are allowed.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n{\r\n\r\n    int Result;\r\n    int Option;\r\n    socklen_t OptionLength;\r\n    long long int OptionLong;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n\r\n    //\r\n    // Validate proper handling of boolean socket options.\r\n    //\r\n\r\n    Option = 1;\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n    Option = 0;\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n    LxtCheckEqual(Option, 1, \"%d\");\r\n\r\n    //\r\n    // Reset the option value to 0.\r\n    //\r\n\r\n    Option = 0;\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n    Option = 0;\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n    LxtCheckEqual(Option, 0, \"%d\");\r\n\r\n    //\r\n    // Since it is a boolean option, any value other than 0 is accepted for\r\n    // enabling the option. Try -ve.\r\n    //\r\n\r\n    Option = -1;\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n    Option = 0;\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n    LxtCheckEqual(Option, 1, \"%d\");\r\n\r\n    //\r\n    // Reset the option value to 0.\r\n    //\r\n\r\n    Option = 0;\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n    Option = 0;\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n    LxtCheckEqual(Option, 0, \"%d\");\r\n\r\n    //\r\n    // Since it is a boolean option, any value other than 0 is accepted for\r\n    // enabling the option. Try > 0.\r\n    //\r\n\r\n    Option = 15;\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n    LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n    LxtCheckEqual(Option, 1, \"%d\");\r\n\r\n    if (SmallerSizeAllowed == false)\r\n    {\r\n\r\n        //\r\n        // Validate that also 1,2 and 3 byte size is not a valid option size for\r\n        // boolean socket option.\r\n        //\r\n\r\n        OptionLength = 1;\r\n        LxtCheckErrnoFailure(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength), EINVAL);\r\n\r\n        OptionLength = 2;\r\n        LxtCheckErrnoFailure(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength), EINVAL);\r\n\r\n        OptionLength = 3;\r\n        LxtCheckErrnoFailure(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength), EINVAL);\r\n\r\n        OptionLength = sizeof(Option);\r\n        LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n        LxtCheckEqual(Option, 1, \"%d\");\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Supplying an option size of 1, 2 and 3 are also accepted.\r\n        //\r\n\r\n        Option = 1;\r\n        OptionLength = 1;\r\n        LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n        LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n        LxtCheckEqual(Option, 1, \"%d\");\r\n\r\n        //\r\n        // Reset the option value to 0.\r\n        //\r\n\r\n        Option = 0;\r\n        OptionLength = sizeof(Option);\r\n        LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n        //\r\n        // Option size of 2.\r\n        //\r\n\r\n        Option = 1;\r\n        OptionLength = 2;\r\n        LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n        LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n        LxtCheckEqual(Option, 1, \"%d\");\r\n\r\n        //\r\n        // Reset the option value to 0.\r\n        //\r\n\r\n        Option = 0;\r\n        OptionLength = sizeof(Option);\r\n        LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n        //\r\n        // Use option size of 3.\r\n        //\r\n\r\n        Option = 1;\r\n        OptionLength = 3;\r\n        LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));\r\n\r\n        LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n        LxtCheckEqual(Option, 1, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Verify that anything above 4 bytes is ignored truncated.\r\n    //\r\n\r\n    OptionLong = 0x200000000;\r\n    OptionLength = sizeof(OptionLong);\r\n    LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &OptionLong, OptionLength));\r\n\r\n    OptionLength = sizeof(Option);\r\n    LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));\r\n\r\n    LxtCheckEqual(Option, 0, \"%d\");\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nchar* SocketGetTypeAsString(int Type)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine returns the string equivalent for the give socket type.\r\n\r\nArguments:\r\n\r\n    Type - Supplies the socket type.\r\n\r\nReturn Value:\r\n\r\n    Returns the string equivalent for the type; NULL otherwise.\r\n\r\n--*/\r\n{\r\n\r\n    switch (Type)\r\n    {\r\n    case SOCK_STREAM:\r\n        return LXT_SOCKET_STREAM_STRING;\r\n\r\n    case SOCK_DGRAM:\r\n        return LXT_SOCKET_DGRAM_STRING;\r\n\r\n    case SOCK_RAW:\r\n        return LXT_SOCKET_RAW_STRING;\r\n\r\n    case SOCK_SEQPACKET:\r\n        return LXT_SOCKET_SEQPACKET_STRING;\r\n\r\n    case SOCK_PACKET:\r\n        return LXT_SOCKET_PACKET_STRING;\r\n\r\n    default:\r\n        return NULL;\r\n    }\r\n}\r\n\r\nint SocketStreamClientMsgWaitAll(int ConnectedSocket)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This is a client helper routine testing MSG_WAITALL flag recv syscall.\r\n\r\nArguments:\r\n\r\n    ConnectedSocket - Supplies a socket fd.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    int FullMessageSize;\r\n    char* ReceiveBuffer;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    char* SendBuffer;\r\n    int Size;\r\n\r\n    SendBuffer = LXT_SOCKET_DEFAULT_SEND_STRING;\r\n    FullMessageSize = 2 * strlen(SendBuffer);\r\n    ReceiveBuffer = malloc(FullMessageSize);\r\n    if (ReceiveBuffer == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Client: 1. send\");\r\n    LxtCheckErrno(Size = send(ConnectedSocket, SendBuffer, strlen(SendBuffer), 0));\r\n\r\n    //\r\n    // Sleep long enough that the second send won't be concatenated by WSK to\r\n    // test MSW_WAITALL code path, if the socket is inet socket.\r\n    //\r\n\r\n    sleep(1);\r\n    LxtLogInfo(\"Client: 2. delayed send\");\r\n    LxtCheckErrno(Size = send(ConnectedSocket, SendBuffer, strlen(SendBuffer), 0));\r\n\r\n    memset(ReceiveBuffer, 0, FullMessageSize);\r\n    LxtLogInfo(\"Client: recv(MSG_WAITALL)\");\r\n    LxtCheckErrno(Size = recv(ConnectedSocket, ReceiveBuffer, FullMessageSize, MSG_WAITALL));\r\n\r\n    LxtCheckMemoryEqual(SendBuffer, ReceiveBuffer, FullMessageSize / 2);\r\n    LxtCheckMemoryEqual(SendBuffer, ReceiveBuffer + FullMessageSize / 2, sizeof(SendBuffer));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ReceiveBuffer != NULL)\r\n    {\r\n        free(ReceiveBuffer);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketStreamServerMsgWaitAll(int AcceptedSocket)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This is a server helper routine testing MSG_WAITALL flag recv syscall.\r\n\r\nArguments:\r\n\r\n    AcceptedSocket - Supplies a socket fd.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    int Index;\r\n    char* ReceiveBuffer;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    int Size;\r\n    int FullMessageSize;\r\n    int Socket = 0;\r\n\r\n    ReceiveBuffer = LXT_SOCKET_DEFAULT_SEND_STRING;\r\n    FullMessageSize = 2 * strlen(ReceiveBuffer);\r\n    ReceiveBuffer = malloc(FullMessageSize);\r\n    if (ReceiveBuffer == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Server: recv(MSG_WAITALL)\");\r\n    memset(ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    LxtCheckErrno(Size = recv(AcceptedSocket, ReceiveBuffer, FullMessageSize, MSG_WAITALL));\r\n\r\n    LxtLogInfo(\"Server: write all back\");\r\n    LxtCheckErrno(write(AcceptedSocket, ReceiveBuffer, Size));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ReceiveBuffer != NULL)\r\n    {\r\n        free(ReceiveBuffer);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/common.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    common.h\r\n\r\nAbstract:\r\n\r\n    Common socket definitions and helper routines.\r\n\r\n--*/\r\n\r\n#ifndef _LXT_SOCKET_COMMON_\r\n#define _LXT_SOCKET_COMMON_\r\n\r\n#define LXT_SOCKET_CLIENT_VARIATION_SLEEP_SECONDS 5\r\n#define LXT_SOCKET_DEFAULT_BUFFER_LENGTH 512\r\n#define LXT_SOCKET_DEFAULT_PORT 50001\r\n#define LXT_SOCKET_DEFAULT_PORT_IPV6 50002\r\n#define LXT_SOCKET_DEFAULT_PORT_STRING \"50001\"\r\n#define LXT_SOCKET_DEFAULT_PORT_IPV6_STRING \"50002\"\r\n#define LXT_SOCKET_VARIATION_TIMEOUT (5 * 1000)\r\n#define LXT_SOCKET_DEFAULT_BACKLOG 32\r\n#define LXT_SOCKET_STREAM_STRING \"SOCK_STREAM\"\r\n#define LXT_SOCKET_DGRAM_STRING \"SOCK_DGRAM\"\r\n#define LXT_SOCKET_RAW_STRING \"SOCK_RAW\"\r\n#define LXT_SOCKET_SEQPACKET_STRING \"SOCK_SEQPACKET\"\r\n#define LXT_SOCKET_PACKET_STRING \"SOCK_PACKET\"\r\n#define LXT_SOCKET_AF_INET_STRING \"AF_INET\"\r\n#define LXT_SOCKET_AF_INET6_STRING \"AF_INET6\"\r\n\r\n#define LxtCheckBytesSendRecv(_requested, _actual) \\\r\n    { \\\r\n        if ((_requested) != (_actual)) \\\r\n        { \\\r\n            LxtLogError( \\\r\n                \"Bytes requested in send/recv do not match actual. \" \\\r\n                \"Requested: %d, Actual:%d.\", \\\r\n                (_requested), \\\r\n                (_actual)); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n#define LxtCheckPoll(_numfdsExpected, _numfdsActual, _reventExpected, _reventActual) \\\r\n    { \\\r\n        if ((_numfdsExpected) != (_numfdsActual)) \\\r\n        { \\\r\n            LxtLogError(\"poll returned unexpected value, expecting %d, actual: %d. revents: 0x%x\", (_numfdsExpected), (_numfdsActual), (_reventActual)); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        if ((_reventExpected) != (_reventActual)) \\\r\n        { \\\r\n            LxtLogError( \\\r\n                \"expected epoll events do not match actual. \" \\\r\n                \"Expected: 0x%x, Actual: 0x%x\", \\\r\n                (_reventExpected), \\\r\n                (_reventActual)); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n//\r\n// This macro will send data from a socket to its connected peer.\r\n//\r\n\r\n#define LxtCheckSend(_clientsocket, _sendbuffer, _numbytes, _clientname) \\\r\n    { \\\r\n        LxtLogInfo(\"[%s]Sending data to server\", (_clientname)); \\\r\n        LxtCheckErrno((BytesSent = send((_clientsocket), (_sendbuffer), _numbytes, 0))); \\\r\n        LxtCheckBytesSendRecv((ssize_t)_numbytes, BytesSent); \\\r\n    }\r\n\r\n#define LxtCheckRecv(_servername, _clientname, _recvbuffer, _sendbuffer, _serversocket) \\\r\n    { \\\r\n        ExpectedBytes = strlen(_sendbuffer); \\\r\n        LxtCheckErrno((BytesReceived = recvfrom((_serversocket), (_recvbuffer), sizeof(_recvbuffer), 0, NULL, NULL))); \\\r\n        LxtCheckBytesSendRecv(ExpectedBytes, BytesReceived); \\\r\n        LxtLogInfo(\"[%s]Data received from %s\", (_servername), (_clientname)); \\\r\n    }\r\n\r\n//\r\n// TODO: switch over to an array of strings to send / receive.\r\n//\r\n\r\n#define LXT_SOCKET_DEFAULT_SEND_STRING \"test socket test string\\n\"\r\n\r\n#define LXT_SOCKET_SERVER_MAX_BACKLOG_NUM 5\r\n\r\n#define LxtCheckEpoll(_fd, _event, _timeout) \\\r\n    { \\\r\n        LxtCheckErrno(LxtSocketEpoll((_fd), (_event), (_timeout))); \\\r\n    }\r\n\r\n#define LxtCheckAncillaryCredentials(_cmsg, _pid, _uid, _gid) \\\r\n    { \\\r\n        struct ucred* Credentials; \\\r\n        LxtCheckEqual((_cmsg)->cmsg_level, SOL_SOCKET, \"%d\"); \\\r\n        LxtCheckEqual((_cmsg)->cmsg_type, SCM_CREDENTIALS, \"%d\"); \\\r\n        LxtCheckEqual((_cmsg)->cmsg_len, CMSG_LEN(sizeof(struct ucred)), \"%d\"); \\\r\n        Credentials = (struct ucred*)CMSG_DATA(_cmsg); \\\r\n        LxtCheckEqual(Credentials->pid, (_pid), \"%d\"); \\\r\n        LxtCheckEqual(Credentials->uid, (_uid), \"%d\"); \\\r\n        LxtCheckEqual(Credentials->gid, (_gid), \"%d\"); \\\r\n    }\r\n\r\n#define LxtSocketGetDomainAsString(_domain) (((_domain) == AF_INET) ? LXT_SOCKET_AF_INET_STRING : LXT_SOCKET_AF_INET6_STRING)\r\n\r\n//\r\n// Private macro to get the next message header which returns the first\r\n// control message when the control message pointer is NULL. glibc's\r\n// 'CMSG_NXTHDR' does not handle that case.\r\n//\r\n\r\n#define MY_CMSG_NXTHDR(_msghdr, _pcmsg) (((_pcmsg) == NULL) ? CMSG_FIRSTHDR(_msghdr) : CMSG_NXTHDR(_msghdr, _pcmsg))\r\n\r\nint LxtSocketEpoll(int Descriptor, int Event, int Timeout);\r\n\r\nvoid* SocketBlockedReaderThread(void* Arg);\r\n\r\nvoid* SocketBlockedReaderZeroBufferThread(void* Arg);\r\n\r\nvoid* SocketBlockedWriterThread(void* Arg);\r\n\r\nstruct cmsghdr* SocketGetControlMessage(struct msghdr* MessageHeader, struct cmsghdr* StartControlMessage, int Level, int Type);\r\n\r\nint SocketGetSetBooleanSocketOption(int Socket, int OptionLevel, int OptionName, bool SmallerSizeAllowed);\r\n\r\nchar* SocketGetTypeAsString(int Type);\r\n\r\nint SocketStreamClientMsgWaitAll(int ConnectedSocket);\r\n\r\nint SocketStreamServerMsgWaitAll(int AcceptedSocket);\r\n\r\n#endif // _LXT_SOCKET_COMMON_"
  },
  {
    "path": "test/linux/unit_tests/dev_pt.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft Corporation. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    dev_pt.c\r\n\r\nAbstract:\r\n\r\n    This file is a test for the Pseudo Terminals: /dev/ptmx, /dev/pts/<n>\r\n    devices.\r\n\r\n--*/\r\n\r\n#include \"dev_pt_common.h\"\r\n\r\n#define LXT_NAME \"dev_pt\"\r\n\r\n//\r\n// Currently the max pseudo terminals that is supported by LXSS\r\n// is set to 10.\r\n// TODO_LX_PTYT: Query this from '/proc/sys/kernel/pty/nr' after\r\n//     the integration with procfs.\r\n//\r\n\r\n#define PTY_MAX_OPEN_LIMIT 10\r\n\r\n//\r\n// Configuration to be used for the stress test.\r\n// Total number of cycles =\r\n//     (STRESS_NUM_PT * STRESS_NUM_THREAD * STRESS_NUM_ITERATION)\r\n//\r\n\r\n#define STRESS_NUM_PT 5\r\n#define STRESS_NUM_THREAD 100\r\n#define STRESS_NUM_ITERATION 6400\r\n\r\n#define IS_CONTROL_CHAR_ECHO_STRING(s, c) (((s)[0] == '^') && (((s)[1] > 0x40)) && (((s)[1] - 0x40) == (c)))\r\n\r\n//\r\n// Globals.\r\n//\r\n\r\npthread_mutex_t DevPtStressMutex = PTHREAD_MUTEX_INITIALIZER;\r\n\r\n//\r\n// struct that defines the argument passed to a stress thread.\r\n//\r\n\r\ntypedef struct _StressThreadArg\r\n{\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int LoopCount;\r\n} StressThreadArg;\r\n\r\n//\r\n// Functions.\r\n//\r\n\r\nvoid* PerformIoStressThread(void* Config);\r\n\r\n//\r\n// Test cases.\r\n//\r\n\r\nint PtBasic(PLXT_ARGS Args);\r\n\r\nint PtBasic2(PLXT_ARGS Args);\r\n\r\nint PtBasic3(PLXT_ARGS Args);\r\n\r\nint PtBasic4(PLXT_ARGS Args);\r\n\r\nint PtBasic5(PLXT_ARGS Args);\r\n\r\nint PtCheck1(PLXT_ARGS Args);\r\n\r\nint PtCheck2(PLXT_ARGS Args);\r\n\r\nint PtCheck3(PLXT_ARGS Args);\r\n\r\nint PtCheck4(PLXT_ARGS Args);\r\n\r\nint PtControlCharCheck(PLXT_ARGS Args);\r\n\r\nint PtControlCharCheck2(PLXT_ARGS Args);\r\n\r\nint PtControlCharCheck3(PLXT_ARGS Args);\r\n\r\nint PtControlCharCheck4(PLXT_ARGS Args);\r\n\r\nint PtControlCharCheck5(PLXT_ARGS Args);\r\n\r\nint PtControlCharCheck6(PLXT_ARGS Args);\r\n\r\nint PtDisassociateTty(PLXT_ARGS Args);\r\n\r\nint PtEmbeddedNullReadWrite(PLXT_ARGS Args);\r\n\r\nint PtEraseCheck(PLXT_ARGS Args);\r\n\r\nint PtEraseCheck2(PLXT_ARGS Args);\r\n\r\nint PtEraseCheck3(PLXT_ARGS Args);\r\n\r\nint PtEraseCheck4(PLXT_ARGS Args);\r\n\r\nint PtGlibcForkPtyBasic(PLXT_ARGS Args);\r\n\r\nint PtLateOpen1(PLXT_ARGS Args);\r\n\r\nint PtLateOpen2(PLXT_ARGS Args);\r\n\r\nint PtLineDiscipline(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck2(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck3(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck4(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck5(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck6(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck7(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck8(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck9(PLXT_ARGS Args);\r\n\r\nint PtLineBreakCheck10(PLXT_ARGS Args);\r\n\r\nint PtMasterFillBuffer(PLXT_ARGS Args);\r\n\r\nint PtMasterHangup1(PLXT_ARGS Args);\r\n\r\nint PtMasterHangup2(PLXT_ARGS Args);\r\n\r\nint PtMasterHangup3(PLXT_ARGS Args);\r\n\r\nint PtMasterHangup4(PLXT_ARGS Args);\r\n\r\nint PtMoreThanOne(PLXT_ARGS Args);\r\n\r\nint PtMultiMessageReadWrite(PLXT_ARGS Args);\r\n\r\nint PtReadNoSub1(PLXT_ARGS Args);\r\n\r\nint PtReadNoSub2(PLXT_ARGS Args);\r\n\r\nint PtReadNoSub3(PLXT_ARGS Args);\r\n\r\nint PtReadNoSub4(PLXT_ARGS Args);\r\n\r\nint PtSessionBasic(PLXT_ARGS Args);\r\n\r\nint PtSessionNoTerminal(PLXT_ARGS Args);\r\n\r\nint PtStressIo(PLXT_ARGS Args);\r\n\r\nint PtUTF8Basic(PLXT_ARGS Args);\r\n\r\nint PtUTF8Basic2(PLXT_ARGS Args);\r\n\r\nint PtUTF8Basic3(PLXT_ARGS Args);\r\n\r\nint PtUTF8Basic4(PLXT_ARGS Args);\r\n\r\nint PtUTF8Basic5(PLXT_ARGS Args);\r\n\r\nint PtUTF8Basic6(PLXT_ARGS Args);\r\n\r\nint PtUTF8Basic7(PLXT_ARGS Args);\r\n\r\nint PtUTF8Basic8(PLXT_ARGS Args);\r\n\r\nint PtUTF8Malformed(PLXT_ARGS Args);\r\n\r\nint PtUTF8Malformed2(PLXT_ARGS Args);\r\n\r\nint PtUTF8Malformed3(PLXT_ARGS Args);\r\n\r\nint PtUTF8Malformed4(PLXT_ARGS Args);\r\n\r\nint PtWindowSizeCheck(PLXT_ARGS Args);\r\n\r\nint PtWriteNoSub1(PLXT_ARGS Args);\r\n\r\nint PtWriteNoSub2(PLXT_ARGS Args);\r\n\r\nint PtWriteToSubReadFromMaster1(PLXT_ARGS Args);\r\n\r\nvoid TestFun(void);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\n//\r\n// N.B. LXT_VARIATION is capped at 64 in order to support the variation mask.\r\n//      Additional tests can be found in dev_pt2.c. This also keeps the files\r\n//      from becoming overly large.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"PT Basic\", PtBasic},\r\n    {\"PT Basic2\", PtBasic2},\r\n    {\"PT Basic3\", PtBasic3},\r\n    {\"PT Basic4\", PtBasic4},\r\n    {\"PT Basic5\", PtBasic5},\r\n    {\"Miscellaneous checks (part 1)\", PtCheck1},\r\n    {\"Miscellaneous checks (part 2)\", PtCheck2},\r\n    {\"Multiple open on the same subordinate \", PtCheck3},\r\n    {\"re-open subordinate and read pending data\", PtCheck4},\r\n    {\"check control character behavior (part 1)\", PtControlCharCheck},\r\n    {\"check control character behavior (part 2)\", PtControlCharCheck2},\r\n    {\"check control character behavior (part 3)\", PtControlCharCheck3},\r\n    {\"check control character behavior (part 4)\", PtControlCharCheck4},\r\n    {\"check control character behavior (part 5)\", PtControlCharCheck5},\r\n    {\"check control character behavior (part 6)\", PtControlCharCheck6},\r\n    {\"Disassociate from a controlling terminal\", PtDisassociateTty},\r\n    {\"send a message with an embedded NULL\", PtEmbeddedNullReadWrite},\r\n    {\"PT Erase character handling (part 1)\", PtEraseCheck},\r\n    {\"PT Erase character handling (part 2)\", PtEraseCheck2},\r\n    {\"PT Erase character handling (part 3)\", PtEraseCheck3},\r\n    {\"PT Erase character handling (part 4)\", PtEraseCheck4},\r\n    {\"Sanity check of forkpty\", PtGlibcForkPtyBasic},\r\n    {\"Open subordinate after closing master (part 1)\", PtLateOpen1},\r\n    {\"Open subordinate after closing master (part 2)\", PtLateOpen2},\r\n    {\"PT line-break handling (part 1)\", PtLineBreakCheck},\r\n    {\"PT line-break handling (part 2)\", PtLineBreakCheck2},\r\n    {\"PT line-break handling (part 3)\", PtLineBreakCheck3},\r\n    {\"PT line-break handling (part 4)\", PtLineBreakCheck4},\r\n    {\"PT line-break handling (part 5)\", PtLineBreakCheck5},\r\n    {\"PT line-break handling (part 6)\", PtLineBreakCheck6},\r\n    {\"PT line-break handling (part 7)\", PtLineBreakCheck7},\r\n    {\"PT line-break handling (part 8)\", PtLineBreakCheck8},\r\n    {\"PT line-break handling (part 9)\", PtLineBreakCheck9},\r\n    {\"PT line-break handling (part 10)\", PtLineBreakCheck10},\r\n    {\"Tests with the master buffer full\", PtMasterFillBuffer},\r\n    {\"Master hangup on subordinate (part 1)\", PtMasterHangup1},\r\n    {\"Master hangup on subordinate (part 2)\", PtMasterHangup2},\r\n    {\"Master hangup on subordinate (part 3)\", PtMasterHangup3},\r\n    {\"Master hangup on subordinate (part 4)\", PtMasterHangup4},\r\n    {\">1 pseudo terminal support\", PtMoreThanOne},\r\n    {\"Multimessage read/write\", PtMultiMessageReadWrite},\r\n    {\"Read from master with no sub (part 1)\", PtReadNoSub1},\r\n    {\"Read from master with no sub (part 2)\", PtReadNoSub2},\r\n    {\"Read from master with no sub (part 3)\", PtReadNoSub3},\r\n    {\"Session with basic controlling terminal IO\", PtSessionBasic},\r\n    {\"Session with no controlling terminal IO\", PtSessionNoTerminal},\r\n    {\"PT UTF-8 Basic\", PtUTF8Basic},\r\n    {\"PT UTF-8 Basic2\", PtUTF8Basic2},\r\n    {\"PT UTF-8 Basic3\", PtUTF8Basic3},\r\n    {\"PT UTF-8 Basic4\", PtUTF8Basic4},\r\n    {\"PT UTF-8 Basic5\", PtUTF8Basic5},\r\n    {\"PT UTF-8 Basic6\", PtUTF8Basic6},\r\n    {\"PT UTF-8 Basic7\", PtUTF8Basic7},\r\n    {\"PT UTF-8 Basic8\", PtUTF8Basic8},\r\n    {\"PT UTF-8 Malformed character handling (part 1)\", PtUTF8Malformed},\r\n    {\"PT UTF-8 Malformed character handling (part 2)\", PtUTF8Malformed2},\r\n    {\"PT UTF-8 Malformed character handling (part 3)\", PtUTF8Malformed3},\r\n    {\"PT UTF-8 Malformed character handling (part 4)\", PtUTF8Malformed4},\r\n    {\"Window size handling check\", PtWindowSizeCheck},\r\n    {\"Write on master with no sub (part 1)\", PtWriteNoSub1},\r\n    {\"Write on master with no sub (part 2)\", PtWriteNoSub2},\r\n    {\"Write to sub, read from master (part 1)\", PtWriteToSubReadFromMaster1},\r\n    //{ \"I/O stress test\", PtStressIo }\r\n    {\"Line discipline\", PtLineDiscipline}};\r\n\r\nint DevPtTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine main entry point for the test for dup, dup2 system call.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckErrno(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n\r\n    //\r\n    // Query the pseudo terminal buffer size before running any test cases.\r\n    //\r\n\r\n    LxtCheckErrno(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\n    // TestFun();\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY();\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nvoid* PerformIoStressThread(void* Config)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs IO Stress test on the given PT as\r\n    per the configuration specified by the argument.\r\n\r\n\r\nArguments:\r\n\r\n    Config - Supplies the stress related configuration argument.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    StressThreadArg* IoDetails;\r\n    int LoopItr;\r\n    int Result;\r\n\r\n    IoDetails = (StressThreadArg*)Config;\r\n\r\n    //\r\n    // Lock/unlock the mutex before proceeding. This mutex signifies\r\n    // the start of the race.\r\n    //\r\n\r\n    pthread_mutex_lock(&DevPtStressMutex);\r\n    pthread_mutex_unlock(&DevPtStressMutex);\r\n\r\n    for (LoopItr = 0; LoopItr < IoDetails->LoopCount; LoopItr++)\r\n    {\r\n        LxtCheckErrno(SimpleReadWriteCheck(IoDetails->PtmFd, IoDetails->PtsFd));\r\n    }\r\n\r\nErrorExit:\r\n    pthread_exit(0);\r\n    return NULL;\r\n}\r\n\r\nint PtBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a very basic check for pseudo terminal. The steps are:\r\n    - Open the master.\r\n    - Open the subordinate.\r\n    - Turns off canonical mode to avoid line discipline.\r\n    - Perform simple read/write check on the master-subordinate.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int PtmFd;\r\n    int PtsFd;\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Verify the starting notification state of both endpoints.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    Timeout.tv_sec = 0;\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    FD_SET(PtsFd, &ReadFds);\r\n    LxtCheckErrno(select((max(PtmFd, PtsFd) + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((max(PtmFd, PtsFd) + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 2, \"%d\");\r\n\r\n    //\r\n    // Perform IO.\r\n    //\r\n\r\n    LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtBasic2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a very basic check for pseudo terminal. The steps are:\r\n    - Open the master.\r\n    - Open the subordinate.\r\n    - Perform simple read/write check on the master-subordinate.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int PtmFd;\r\n    int PtsFd;\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Verify the starting notification state of both endpoints.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    Timeout.tv_sec = 0;\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    FD_SET(PtsFd, &ReadFds);\r\n    LxtCheckErrno(select((max(PtmFd, PtsFd) + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((max(PtmFd, PtsFd) + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 2, \"%d\");\r\n\r\n    //\r\n    // Perform IO.\r\n    //\r\n\r\n    LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtBasic3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a very basic check for pseudo terminal. The steps are:\r\n    - Open the master.\r\n    - Open the subordinate.\r\n    - Turns off ICRNL to verify termios applies only to subordinate.\r\n    - Perform simple read/write check on the master-subordinate.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* GreetingsCR = \"Hi there!!\\r\";\r\n    const char* GreetingsNL = \"Hi there!!\\n\";\r\n    int PtmFd;\r\n    int PtsFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[1024];\r\n    const char* ReplyCR = \"Hi, how are you?\\r\";\r\n    const char* ReplyNL = \"Hi, how are you?\\n\";\r\n    int Result;\r\n    int SerialNumber;\r\n    tcflag_t TermiosFlags;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    LxtCheckErrno(PtsFlags = fcntl(PtsFd, F_GETFL, 0));\r\n\r\n    //\r\n    // Turn on OCRNL and turn off ICRNL to verify termios is effecting output\r\n    // on only the subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(TerminalSettingsGetInputFlags(PtsFd, &TermiosFlags));\r\n    LxtCheckErrno(TerminalSettingsSetInputFlags(PtsFd, (TermiosFlags & ~ICRNL)));\r\n    LxtCheckErrno(TerminalSettingsGetOutputFlags(PtsFd, &TermiosFlags));\r\n    LxtCheckErrno(TerminalSettingsSetOutputFlags(PtsFd, (TermiosFlags | OCRNL)));\r\n\r\n    //\r\n    // Write the greetings message to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(GreetingsCR);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, GreetingsCR, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, ExpectedResult, GreetingsCR);\r\n\r\n    //\r\n    // Read from subordinate. This should block because the master does not\r\n    // respect the termios settings and a carriage-return does not signal the\r\n    // end of a line.\r\n    //\r\n\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (PtsFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtLogInfo(\"Message not ready for subordinate(FD:%d)\", PtsFd);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, PtsFlags));\r\n\r\n    //\r\n    // In canonical mode, even though a full line was not presented, the\r\n    // characters should have been echoed back with the carriage-return\r\n    // control character \"^M\"\r\n    //\r\n\r\n    LxtLogInfo(\"Reading echo to master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, (ExpectedResult + 1));\r\n    if (ReadBuffer[BytesReadWrite - 1] != 'M' || ReadBuffer[BytesReadWrite - 2] != '^')\r\n    {\r\n        LxtLogError(\"Expected ^M carriage-return to be echoed.\");\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckMemoryEqual(ReadBuffer, (char*)GreetingsCR, (ExpectedResult - 1));\r\n\r\n    //\r\n    // Now write from the subordinate and read from the master, which should\r\n    // use termios settings.\r\n    //\r\n\r\n    LxtLogInfo(\"Subordinate(FD:%d) --> master(FD:%d):%*s\", PtsFd, PtmFd, ExpectedResult, ReplyCR);\r\n\r\n    ExpectedResult = strlen(ReplyCR);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, ReplyCR, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Read from master. This should succeed and the carriage-return should be\r\n    // transformed to a newline by the termios settings.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Reply received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckMemoryEqual(ReadBuffer, (char*)ReplyNL, BytesReadWrite);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtBasic4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a very basic check for pseudo terminal. The steps are:\r\n    - Open the master.\r\n    - Open the subordinate.\r\n    - Modify termios on subordinate, check on master.\r\n    - Close subordinate, check termios on master.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n    tcflag_t TermiosFlags;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set an input flag on the subordinate and read it from the master.\r\n    //\r\n\r\n    LxtCheckErrno(TerminalSettingsSetInputFlags(PtsFd, INLCR));\r\n    LxtCheckErrno(TerminalSettingsGetInputFlags(PtmFd, &TermiosFlags));\r\n    LxtCheckEqual(TermiosFlags, INLCR, \"%lu\");\r\n\r\n    //\r\n    // Close the subordinate and check again.\r\n    //\r\n\r\n    close(PtsFd);\r\n    PtsFd = -1;\r\n    LxtCheckErrno(TerminalSettingsGetInputFlags(PtmFd, &TermiosFlags));\r\n    LxtCheckEqual(TermiosFlags, INLCR, \"%lu\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtBasic5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a very basic check for pseudo terminal. The steps are:\r\n    - Open the master.\r\n    - Open the subordinate.\r\n    - Call ttyname on both file descriptors.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char NameBuffer[50];\r\n    int NameSerialNumber;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n    tcflag_t TermiosFlags;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Fetch the names and compare with expected values.\r\n    //\r\n\r\n    LxtCheckErrno(ttyname_r(PtmFd, NameBuffer, sizeof(NameBuffer)));\r\n    LxtCheckStringEqual(NameBuffer, \"/dev/ptmx\");\r\n    LxtCheckErrno(ttyname_r(PtsFd, NameBuffer, sizeof(NameBuffer)));\r\n    LxtCheckNotEqual(sscanf(NameBuffer, \"/dev/pts/%d\", &NameSerialNumber), EOF, \"%d\");\r\n    LxtCheckEqual(SerialNumber, NameSerialNumber, \"%d\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSessionNoTerminal(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks PTY access from a session with no controlling terminal\r\n    to a terminal that is also not associated with a session.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int ChildStatus;\r\n    pid_t ForegroundId;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n    pid_t SessionId;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(SessionId = setsid());\r\n        ForegroundId = getpid();\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtLogInfo(\"Verifying access to a non-controlling terminal from a new session\");\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n\r\n        //\r\n        // Querying the foreground process on the master endpoint doesn't fail,\r\n        // instead returning 0 if there is no foreground process (either\r\n        // because the terminal is not a controlling terminal of a session or\r\n        // the session has no foreground process).\r\n        //\r\n\r\n        LxtCheckErrno(tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(Result, 0, \"%d\");\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &ChildStatus, 0)));\r\n        LxtCheckResult(WIFEXITED(ChildStatus) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(ChildStatus));\r\n    }\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSessionBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs basic checks on endpoints made the controlling\r\n    terminal of a new session.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int ChildStatus;\r\n    pid_t ForegroundId;\r\n    pid_t SelfPid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SessionId;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(SelfPid = getpid());\r\n        LxtCheckResult(SessionId = getsid(0));\r\n        LxtCheckEqual(SelfPid, SessionId, \"%d\");\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtsFd));\r\n        LxtCheckEqual(SessionId, TerminalSessionId, \"%d\");\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n        LxtCheckEqual(SessionId, TerminalSessionId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(SelfPid, TerminalForegroundId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(SelfPid, TerminalForegroundId, \"%d\");\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &ChildStatus, 0)));\r\n        LxtCheckResult(WIFEXITED(ChildStatus) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(ChildStatus));\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtDisassociateTty(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine removes the controlling terminal from its process and checks\r\n    IO.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    BOOLEAN EndChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = TRUE;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n        LxtCheckResult(ForegroundId = getpid());\r\n        LxtCheckResult(SessionId = getsid(0));\r\n\r\n        //\r\n        // Allow the other thread to try to disassociate the terminal, and wait\r\n        // for that to complete.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        //\r\n        // Check session and foreground process group for both endpoints of\r\n        // the psuedo-terminal.\r\n        //\r\n\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtsFd));\r\n        LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n        LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n\r\n        //\r\n        // Disconnect the controlling terminal.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n\r\n        //\r\n        // TODO_LX: Support SIGCONT.\r\n        //\r\n        // LxtCheckResult(LxtSignalCheckReceived(SIGCONT));\r\n        //\r\n\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n        LxtSignalResetReceived();\r\n\r\n        //\r\n        // Trying to disconnect again should fail.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n\r\n        //\r\n        // The terminal is no longer associated, so it is expected to fail the\r\n        // commands to retrieve session and foreground process group.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n\r\n        //\r\n        // On Linux, The master endpoint returns foreground/session state, but\r\n        // instead of failing the foreground group query will just return 0.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(TerminalForegroundId, 0, \"%d\");\r\n\r\n        //\r\n        // Do a simple IO check.\r\n        //\r\n\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        //\r\n        // Test TIOCSTI.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCSTI, \"x\"));\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCSTI, (char*)NULL), EFAULT);\r\n        LxtCheckErrno(setuid(1001));\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCSTI, \"x\"), EPERM);\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCSTI, (char*)NULL), EPERM);\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Try to disassociate terminal from another session.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait for the child here in order to run more tests after the session\r\n        // has been destroyed.\r\n        //\r\n\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &Status, 0)));\r\n        EndChildPidSynchronization = FALSE;\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        //\r\n        // Check status of master endpoint after session is gone.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrno(tcgetpgrp(PtmFd));\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (EndChildPidSynchronization != FALSE)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtGlibcForkPtyBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine does a basic sanity test of glibc's forkpty.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ChildStatus;\r\n    pid_t ForegroundId;\r\n    const char* Message1 = \"Message1\\n\";\r\n    const char* Message2 = \"2egasseM\\n\";\r\n    char MessageBuffer[10];\r\n    size_t MessageLength;\r\n    int PtmFd;\r\n    char PtsBuffer[PTS_DEV_NAME_BUFFER_SIZE];\r\n    int Result;\r\n    pid_t SelfPid;\r\n    pid_t SessionId;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n\r\n    LxtCheckErrno(ChildPid = forkpty(&PtmFd, PtsBuffer, NULL, NULL));\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // N.B. forkpty resets STDOUT/IN/ERR to the pty fd so no messages will\r\n        //      appear to the console, but they will still be logged.\r\n        //\r\n        //      No information logging is allowed since it will go to STDOUT\r\n        //      which is being tested.\r\n        //\r\n\r\n        LxtCheckResult(SelfPid = getpid());\r\n        LxtCheckResult(SessionId = getsid(0));\r\n        LxtCheckEqual(SelfPid, SessionId, \"%d\");\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(STDOUT));\r\n        LxtCheckEqual(SessionId, TerminalSessionId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(STDOUT));\r\n        LxtCheckEqual(SelfPid, TerminalForegroundId, \"%d\");\r\n\r\n        MessageLength = strlen(Message1);\r\n        memset(MessageBuffer, 0, sizeof(MessageBuffer));\r\n        LxtCheckErrno(BytesReadWrite = read(STDIN, MessageBuffer, MessageLength));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, MessageLength);\r\n        LxtCheckStringEqual(Message1, MessageBuffer);\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        MessageLength = strlen(Message2);\r\n        LxtCheckErrno(BytesReadWrite = write(STDOUT, Message2, MessageLength));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, MessageLength);\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckErrno(TerminalSettingsSetInputFlags(PtmFd, 0));\r\n        LxtCheckErrno(TerminalSettingsSetOutputFlags(PtmFd, 0));\r\n        LxtCheckErrno(TerminalSettingsSetLocalFlags(PtmFd, (ICANON | TOSTOP)));\r\n        MessageLength = strlen(Message1);\r\n        LxtLogInfo(\"Writing '%s' to master (fd:%d)\", Message1, PtmFd);\r\n        LxtCheckErrno(BytesReadWrite = write(PtmFd, Message1, MessageLength));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, MessageLength);\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        MessageLength = strlen(Message2);\r\n        memset(MessageBuffer, 0, sizeof(MessageBuffer));\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, MessageBuffer, MessageLength));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, MessageLength);\r\n        LxtLogInfo(\"Read '%s' from master\", MessageBuffer);\r\n        LxtCheckStringEqual(Message2, MessageBuffer);\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &ChildStatus, 0)));\r\n        ChildPid = -1;\r\n        LxtCheckResult(WIFEXITED(ChildStatus) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(ChildStatus));\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        exit(Result);\r\n    }\r\n    else if (ChildPid > 0)\r\n    {\r\n        kill(ChildPid, SIGKILL);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtCheck1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates following checks:\r\n    1. Open a subordinate device that does not exist.\r\n    Expected Result: The operation should fail with error: ENOENT.\r\n    2. Open a subordinate that has not been unlocked.\r\n    Expected Result: The operation should fail with result EIO.\r\n    3. Open a master, get the subordinate device name, close the master\r\n       and then open the subordinate.\r\n    Expected Result: The operation should fail with error ENOENT.\r\n    4. Open the master, open the subordinate, close the master and try\r\n       opening the subordinate again.\r\n    Expected Result: The last open operation should fail with error ENOENT.\r\n    5. Open the master, open a subordinate, close it and then open the\r\n       subordinate again.\r\n       Expected Result: As long as the master is alive, one should be\r\n       able to get a handle to the subordinate.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int PtmFd;\r\n    char PtsDevName[PTS_DEV_NAME_BUFFER_SIZE];\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Check 1:\r\n    // Choose a subordinate device that is highly unlikely to exist,\r\n    // and open it.\r\n    //\r\n\r\n    strcpy(PtsDevName, \"/dev/pts/100\");\r\n    LxtCheckErrnoFailure(open(PtsDevName, O_RDWR), ENOENT);\r\n\r\n    //\r\n    // Open the master.\r\n    //\r\n\r\n    LxtCheckErrno((PtmFd = open(\"/dev/ptmx\", O_RDWR)));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtCheckErrno(grantpt(PtmFd));\r\n\r\n    //\r\n    // Check 2:\r\n    // Do not unlock the subordinate. Try opening the subordinate.\r\n    // It should fail.\r\n    //\r\n\r\n    LxtCheckErrno(ptsname_r(PtmFd, PtsDevName, PTS_DEV_NAME_BUFFER_SIZE));\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n    PtsFd = open(PtsDevName, O_RDWR);\r\n    LxtCheckErrnoFailure(open(PtsDevName, O_RDWR), EIO);\r\n\r\n    //\r\n    // Unlock the subordinate and try opening the subordinate again.\r\n    // It should succeed this time.\r\n    //\r\n\r\n    LxtCheckErrno(unlockpt(PtmFd));\r\n    LxtCheckErrno((PtsFd = open(PtsDevName, O_RDWR)));\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Check 3.\r\n    // Close the subordinate and the master and then try opening the\r\n    // subordinate again. It should fail.\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n    LxtClose(PtsFd);\r\n    LxtCheckErrnoFailure(open(PtsDevName, O_RDWR), ENOENT);\r\n\r\n    //\r\n    // Check 4.\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Subordinate is opened. Close the master.\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n\r\n    //\r\n    // Try opening the same subordinate again. It should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(open(PtsDevName, O_RDWR), ENOENT);\r\n    LxtClose(PtmFd);\r\n    LxtClose(PtsFd);\r\n\r\n    //\r\n    // Check 5.\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, PtsDevName, &SerialNumber));\r\n\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Close the subordinate and open it again.\r\n    //\r\n\r\n    LxtClose(PtsFd);\r\n    LxtCheckErrno(PtsFd = open(PtsDevName, O_RDWR));\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtCheck2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates that the serial number for the pseudo terminal\r\n    does not get reused if there are still open handle(s) to the master\r\n    or the subordinate.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int PtmFd;\r\n    char PtsDevName[PTS_DEV_NAME_BUFFER_SIZE];\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber1;\r\n    int SerialNumber2;\r\n    int SerialNumber3;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    SerialNumber1 = -1;\r\n    SerialNumber2 = -1;\r\n    SerialNumber3 = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, PtsDevName, &SerialNumber1));\r\n\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber1);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Close the master, but keep the subordinate open.\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n\r\n    //\r\n    // Open a new pseudo terminal.\r\n    //\r\n\r\n    LxtCheckErrno((PtmFd = open(\"/dev/ptmx\", O_RDWR)));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtCheckErrno(ptsname_r(PtmFd, PtsDevName, PTS_DEV_NAME_BUFFER_SIZE));\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n    LxtCheckErrno((SerialNumber2 = GetPtSerialNumFromDeviceString(PtsDevName)));\r\n\r\n    //\r\n    // SerialNumber2 should not be the same as SerialNumber1 because\r\n    // the subordinate pseudo terminal handle is still open.\r\n    //\r\n\r\n    if (SerialNumber1 == SerialNumber2)\r\n    {\r\n        LxtLogError(\r\n            \"Serial number was re-used while handle(s) to \"\r\n            \"subordinate were still open. SerialNumber1 = %d, \"\r\n            \"SerialNumber2 = %d\",\r\n            SerialNumber1,\r\n            SerialNumber2);\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Close all handles to master and subordinate.\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n    LxtClose(PtsFd);\r\n\r\n    //\r\n    // Open Master-Subordinate again.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, PtsDevName, &SerialNumber3));\r\n\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber3);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // SerialNumber1 should get repurposed for this pseudo terminal.\r\n    //\r\n\r\n    if (SerialNumber3 != SerialNumber1)\r\n    {\r\n        LxtLogError(\r\n            \"Serial number was not re-purposed. \"\r\n            \"(SerialNumber1 = %d) != (SerialNumber3 = %d)\",\r\n            SerialNumber1,\r\n            SerialNumber3);\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtCheck3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates that the pseudo terminal driver is able to\r\n    handle multiple opens on the same subordinate device.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int PtmFd;\r\n    char PtsDevName[PTS_DEV_NAME_BUFFER_SIZE];\r\n    int PtsFd;\r\n    int PtsFd1;\r\n    int PtsFd2;\r\n    int PtsFd3;\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    PtsFd1 = -1;\r\n    PtsFd2 = -1;\r\n    PtsFd3 = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, PtsDevName, &SerialNumber));\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n    LxtCheckErrno(PtsFd1 = open(PtsDevName, O_RDWR));\r\n    LxtCheckErrno(RawInit(PtsFd1));\r\n    LxtLogInfo(\"Subordinate opened again at FD:%d\", PtsFd1);\r\n    LxtCheckErrno(PtsFd2 = open(PtsDevName, O_RDWR));\r\n    LxtCheckErrno(RawInit(PtsFd2));\r\n    LxtLogInfo(\"Subordinate opened again at FD:%d\", PtsFd2);\r\n    LxtCheckErrno(PtsFd3 = open(PtsDevName, O_RDWR));\r\n    LxtCheckErrno(RawInit(PtsFd3));\r\n    LxtLogInfo(\"Subordinate opened again at FD:%d\", PtsFd3);\r\n\r\n    //\r\n    // Do simple read\\write check on each of the subordinates.\r\n    // Master should be connected to all of them.\r\n    //\r\n\r\n    LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n    LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd1));\r\n    LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd2));\r\n    LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd3));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (PtsFd1 != -1)\r\n    {\r\n        close(PtsFd1);\r\n    }\r\n\r\n    if (PtsFd2 != -1)\r\n    {\r\n        close(PtsFd2);\r\n    }\r\n\r\n    if (PtsFd3 != -1)\r\n    {\r\n        close(PtsFd3);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtCheck4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates that the subordinate should be able to\r\n    read any pending data that is written by the master even\r\n    after closing and opening the handle to the subordinate.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    char Message1[] = \"ls -al\\n\";\r\n    char Message2[] = \"date\\n\";\r\n    int PtmFd;\r\n    char PtsDevName[PTS_DEV_NAME_BUFFER_SIZE];\r\n    int PtsFd;\r\n    char ReadBuffer[50];\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, PtsDevName, &SerialNumber));\r\n\r\n    //\r\n    // This is a message boundary test, do not set the subordinate for raw init.\r\n    //\r\n\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Send message 1 and 2 to the subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Message1);\r\n    BytesReadWrite = write(PtmFd, Message1, ExpectedResult);\r\n    LxtLogInfo(\"Message sent(%d bytes) to subordinate: \\n%s\", BytesReadWrite, Message1);\r\n\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    ExpectedResult = strlen(Message2);\r\n    BytesReadWrite = write(PtmFd, Message2, ExpectedResult);\r\n    LxtLogInfo(\"Message sent(%d bytes) to subordinate: \\n%s\", BytesReadWrite, Message2);\r\n\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Read Message 1 from the subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Message1);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Message read(%d bytes) from subordinate: \\n%s\", BytesReadWrite, ReadBuffer);\r\n\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    ExpectedResult = strlen(Message1);\r\n    if (memcmp(ReadBuffer, Message1, min(BytesReadWrite, ExpectedResult)) != 0)\r\n    {\r\n\r\n        LxtLogError(\r\n            \"Data read from subordinate does not match what was \"\r\n            \"written by master.\");\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Close and re-open the subordinate.\r\n    //\r\n\r\n    LxtClose(PtsFd);\r\n    LxtLogInfo(\"Closing and opening subordinate.\");\r\n    LxtCheckErrno(PtsFd = open(PtsDevName, O_RDWR));\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Read Message 2 from the subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Message2);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Message read(%d bytes) from subordinate: \\n%s\", BytesReadWrite, ReadBuffer);\r\n\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    ExpectedResult = strlen(Message2);\r\n    if (memcmp(ReadBuffer, Message2, min(BytesReadWrite, ExpectedResult)) != 0)\r\n    {\r\n\r\n        LxtLogError(\r\n            \"Data read from subordinate does not match what was \"\r\n            \"written by master.\");\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControlCharCheck(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that SIGINT is delivered with a ^C.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char ReadBuffer[10];\r\n    ssize_t BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ChildStatus;\r\n    cc_t ControlArray[NCCS];\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int PtsFlags;\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGINT, SA_SIGINFO));\r\n        LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VINTR], 1));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n        //\r\n        // A SIGINT signal should be generated shortly after the control\r\n        // character is received.\r\n        //\r\n\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGINT));\r\n        LxtSignalResetReceived();\r\n\r\n        //\r\n        // The control character sequence should have been echoed back.\r\n        //\r\n\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, 2);\r\n        LxtCheckTrue(IS_CONTROL_CHAR_ECHO_STRING(ReadBuffer, ControlArray[VINTR]));\r\n\r\n        //\r\n        // There should be no character waiting at the subordinate.\r\n        //\r\n\r\n        LxtCheckErrno(PtsFlags = fcntl(PtsFd, F_GETFL, 0));\r\n        LxtCheckErrno(fcntl(PtsFd, F_SETFL, (PtsFlags | O_NONBLOCK)));\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n        LxtCheckErrno(fcntl(PtsFd, F_SETFL, PtsFlags));\r\n        Result = 0;\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &ChildStatus, 0)));\r\n        LxtCheckResult(WIFEXITED(ChildStatus) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(ChildStatus));\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControlCharCheck2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that changing VINTR to TAB still delivers SIGINT.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char ReadBuffer[10];\r\n    ssize_t BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ChildStatus;\r\n    cc_t ControlArray[NCCS];\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int PtsFlags;\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n        ControlArray[VINTR] = '\\t';\r\n        LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGINT, SA_SIGINFO));\r\n        LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VINTR], 1));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n        //\r\n        // A SIGINT signal should be generated shortly after the control\r\n        // character is received.\r\n        //\r\n\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGINT));\r\n        LxtSignalResetReceived();\r\n\r\n        //\r\n        // TAB does not get echoed as a control character.\r\n        //\r\n\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n        LxtCheckEqual(ReadBuffer[0], '\\t', \"%hhd\");\r\n\r\n        //\r\n        // There should be no character waiting at the subordinate.\r\n        //\r\n\r\n        LxtCheckErrno(PtsFlags = fcntl(PtsFd, F_GETFL, 0));\r\n        LxtCheckErrno(fcntl(PtsFd, F_SETFL, (PtsFlags | O_NONBLOCK)));\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n        LxtCheckErrno(fcntl(PtsFd, F_SETFL, PtsFlags));\r\n        Result = 0;\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &ChildStatus, 0)));\r\n        LxtCheckResult(WIFEXITED(ChildStatus) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(ChildStatus));\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControlCharCheck3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that changing VINTR to the letter 'A' still delivers\r\n    SIGINT.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char ReadBuffer[10];\r\n    ssize_t BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ChildStatus;\r\n    cc_t ControlArray[NCCS];\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int PtsFlags;\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n        ControlArray[VINTR] = 'A';\r\n        LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGINT, SA_SIGINFO));\r\n        LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VINTR], 1));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n        //\r\n        // A SIGINT signal should be generated shortly after the control\r\n        // character is received.\r\n        //\r\n\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGINT));\r\n        LxtSignalResetReceived();\r\n\r\n        //\r\n        // 'A' does not get echoed as a control character.\r\n        //\r\n\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n        LxtCheckEqual(ReadBuffer[0], 'A', \"%hhd\");\r\n\r\n        //\r\n        // There should be no character waiting at the subordinate.\r\n        //\r\n\r\n        LxtCheckErrno(PtsFlags = fcntl(PtsFd, F_GETFL, 0));\r\n        LxtCheckErrno(fcntl(PtsFd, F_SETFL, (PtsFlags | O_NONBLOCK)));\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n        LxtCheckErrno(fcntl(PtsFd, F_SETFL, PtsFlags));\r\n        Result = 0;\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &ChildStatus, 0)));\r\n        LxtCheckResult(WIFEXITED(ChildStatus) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(ChildStatus));\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControlCharCheck4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that control character are echoed back properly. This\r\n    test skips control characters with special behaviors (suspend, et al.).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    ssize_t CumulativeBytesRead;\r\n    int Index;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[50];\r\n    const char ReadResult[] = {0, 1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 10, 14, 15, 16, 20, 24, 25, 27, 29, 30, 31, 32, '\\n'};\r\n    int Result;\r\n    const char WriteBuffer[] = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 25, 27, 29, 30, 31, 32, '\\n'};\r\n    const char* WriteBufferEcho = \"^@^A^B^E^F^G^H\\t\\r\\n^K^L\\r\\n^N^O^P^T^X^Y^[^]^^^_ \\r\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteBuffer, sizeof(WriteBuffer)));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, sizeof(WriteBuffer));\r\n\r\n    //\r\n    // Check the echo result\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, strlen(WriteBufferEcho));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteBufferEcho);\r\n\r\n    //\r\n    // Check the subordinate data.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 3);\r\n    CumulativeBytesRead = BytesReadWrite;\r\n\r\n    //\r\n    // Read past EOF (0x4)\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, &ReadBuffer[CumulativeBytesRead], (sizeof(ReadBuffer) - CumulativeBytesRead)));\r\n\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 6);\r\n    CumulativeBytesRead += BytesReadWrite;\r\n\r\n    //\r\n    // Read past newline (0xa)\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, &ReadBuffer[CumulativeBytesRead], (sizeof(ReadBuffer) - CumulativeBytesRead)));\r\n\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 3);\r\n    CumulativeBytesRead += BytesReadWrite;\r\n\r\n    //\r\n    // Read past carriage-return (0xd)\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, &ReadBuffer[CumulativeBytesRead], (sizeof(ReadBuffer) - CumulativeBytesRead)));\r\n\r\n    CumulativeBytesRead += BytesReadWrite;\r\n    LxtCheckFnResults(\"read\", CumulativeBytesRead, sizeof(ReadResult));\r\n    LxtCheckMemoryEqual(ReadBuffer, (void*)ReadResult, sizeof(ReadResult));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControlCharCheck5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that VINTR flushes the buffer.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char ReadBuffer[10];\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int PtsFlags;\r\n    int Result;\r\n    const char* WriteString = \"hello\\n\\x3\";\r\n    const char* WriteStringEcho = \"^C\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    ExpectedResult = strlen(WriteString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check the echo result\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteStringEcho);\r\n\r\n    //\r\n    // There should be no characters waiting at the subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(PtsFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (PtsFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, PtsFlags));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControlCharCheck6(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that VINTR does not flush the buffer with NOFLSH set.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char ReadBuffer[10];\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    tcflag_t LocalFlags;\r\n    const char* WriteString = \"hello\\n\\x3\";\r\n    const char* WriteStringEcho = \"hello\\r\\n^C\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtCheckResult(TerminalSettingsGetLocalFlags(PtsFd, &LocalFlags));\r\n    LxtCheckResult(TerminalSettingsSetLocalFlags(PtsFd, LocalFlags | NOFLSH));\r\n    ExpectedResult = strlen(WriteString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check the echo result\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteStringEcho);\r\n\r\n    //\r\n    // Check data at subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteString) - 1;\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckMemoryEqual(ReadBuffer, (void*)WriteString, ExpectedResult);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtEmbeddedNullReadWrite(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates embedded NULL behavior.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    char* EmbeddedNullMessage = \"ABC\\0DEF\\n\";\r\n    int OldFlags;\r\n    void* PointerResult;\r\n    int PtmFd;\r\n    FILE* PtmFile;\r\n    int PtsFd;\r\n    char ReadMessage[50] = {0};\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    PtmFile = NULL;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtCheckNullErrno(PtmFile = fdopen(PtmFd, \"w\"));\r\n\r\n    //\r\n    // This is a message boundary test, do not set the subordinate for raw init.\r\n    //\r\n\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Write string with an embedded NULL.\r\n    //\r\n\r\n    ExpectedResult = sizeof(EmbeddedNullMessage);\r\n    BytesReadWrite = write(PtmFd, EmbeddedNullMessage, ExpectedResult);\r\n    LxtLogInfo(\"Message sent(%d bytes) to subordinate: \\n%s...\", BytesReadWrite, EmbeddedNullMessage);\r\n\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Read next message.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadMessage, sizeof(ReadMessage)));\r\n\r\n    ReadMessage[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Message read(%d bytes) from subordinate: \\n%s\", BytesReadWrite, ReadMessage);\r\n\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    if (memcmp(ReadMessage, EmbeddedNullMessage, min(BytesReadWrite, ExpectedResult)) != 0)\r\n    {\r\n\r\n        LxtLogError(\r\n            \"Data read from subordinate does not match what was \"\r\n            \"written by master.\");\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (PtmFile != NULL)\r\n    {\r\n        fclose(PtmFile);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtEraseCheck(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine writes a string, sends the delete character and checks that\r\n    both the echo bytes and the final string match expected values.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    char EndString[3] = {0, '\\n', '\\0'};\r\n    const char* EndStringEcho = \"\\x8 \\x8\\r\\n\";\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    const char* SendString = \"hello\\nhi\";\r\n    const char* SendStringEcho = \"hello\\r\\nhi\";\r\n    const char* SendStringFinal = \"hello\\nh\\n\";\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    EndString[0] = ControlArray[VERASE];\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(SendString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, SendString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, SendString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(SendStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, SendStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Now send delete character followed by the newline.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(EndString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, EndString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, EndString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(EndStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, EndStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not match expected value.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read the message from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    ExpectedResult = strlen(SendStringFinal);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ExpectedResult -= BytesReadWrite;\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, &ReadBuffer[BytesReadWrite], (sizeof(ReadBuffer) - BytesReadWrite)));\r\n\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, SendStringFinal, strlen(SendStringFinal)) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtEraseCheck2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine switches to raw input mode and then sends an erase character\r\n    on an empty buffer. In raw mode the erase character should not be treated\r\n    special.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n\r\n    //\r\n    // Write the erase character to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VERASE], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n    //\r\n    // Read the message from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ControlArray[VERASE], ReadBuffer[0], \"%%d\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtEraseCheck3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends the erase character on an empty buffer. In canonical\r\n    mode this should do nothing.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    LxtCheckErrno(PtmFlags = fcntl(PtmFd, F_GETFL, 0));\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n\r\n    //\r\n    // Send erase character on an empty buffer.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VERASE], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n    //\r\n    // Canonical mode should not echo anything back.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (PtmFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtLogInfo(\"No bytes echoed(FD:%d)\", PtmFd);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, PtmFlags));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtEraseCheck4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine writes a string with control characters, sends delete\r\n    characters and checks that both the echo bytes and the final string match\r\n    expected values.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    char EndString[] = {0, 0, '\\n', '\\0'};\r\n    const char* EndStringEcho = \"\\x8 \\x8\\x8 \\x8\\x8 \\x8\\r\\n\";\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[15];\r\n    int Result;\r\n    const char* SendString = \"hi\\x2 \";\r\n    const char* SendStringEcho = \"hi^B \";\r\n    const char* SendStringFinal = \"hi\\n\";\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    EndString[0] = ControlArray[VERASE];\r\n    EndString[1] = ControlArray[VERASE];\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(SendString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, SendString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, SendString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(SendStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, SendStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Now send two delete characters followed by the newline.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(EndString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, EndString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, EndString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(EndStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, EndStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not match expected value.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read the message from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    ExpectedResult = strlen(SendStringFinal);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, SendStringFinal, strlen(SendStringFinal)) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLateOpen1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates part (1) below;\r\n    when:\r\n    1. A subordinate is opened after the master has been closed and\r\n    2. An open handle exists for the subordinate, master is closed\r\n       and the subordinate is opened again.\r\n\r\n    Expected Result: in both (1) and (2), once the master is closed,\r\n    the open on subordinate should return with error:2 (ENOENT)\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int PtmFd;\r\n    char PtsDevName[PTS_DEV_NAME_BUFFER_SIZE];\r\n    int PtsFd;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master.\r\n    //\r\n\r\n    LxtCheckErrno((PtmFd = open(\"/dev/ptmx\", O_RDWR)));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtCheckErrno(grantpt(PtmFd));\r\n    LxtCheckErrno(unlockpt(PtmFd));\r\n    LxtCheckErrno(ptsname_r(PtmFd, PtsDevName, PTS_DEV_NAME_BUFFER_SIZE));\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n\r\n    //\r\n    // Close the master.\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n\r\n    //\r\n    // Open the subordinate after closing the master.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(open(PtsDevName, O_RDWR), ENOENT);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLateOpen2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates part (2) of the behavior as described in PtLateOpen1.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int PtmFd;\r\n    char PtsDevName[PTS_DEV_NAME_BUFFER_SIZE];\r\n    int PtsFd;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, PtsDevName, &SerialNumber));\r\n\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Close the master.\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n\r\n    //\r\n    // Master is closed, try to open subordinate again.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(open(PtsDevName, O_RDWR), ENOENT);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineDiscipline(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests replacing LF with CRLF sequences.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[64];\r\n    ssize_t BytesRead;\r\n    ssize_t BytesWritten;\r\n    const char* Message = \"This\\nis\\na\\ntest\";\r\n    const char* ExpectedMessage = \"This\\r\\nis\\r\\na\\r\\ntest\";\r\n    size_t ExpectedSize;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Write a message with new lines to the subordinate.\r\n    //\r\n\r\n    ExpectedSize = strlen(Message);\r\n    LxtCheckErrno(BytesWritten = write(PtsFd, Message, ExpectedSize));\r\n    LxtCheckEqual((size_t)BytesWritten, ExpectedSize, \"%ld\");\r\n\r\n    //\r\n    // Read the message from the master.\r\n    //\r\n\r\n    ExpectedSize = strlen(ExpectedMessage);\r\n    LxtCheckErrno(BytesRead = read(PtmFd, Buffer, sizeof(Buffer) - 1));\r\n    LxtCheckEqual((size_t)BytesRead, ExpectedSize, \"%ld\");\r\n    Buffer[BytesRead] = '\\0';\r\n    LxtCheckStringEqual(ExpectedMessage, Buffer);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine writes VEOF to an empty buffer and checks the results.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n\r\n    //\r\n    // Write VEOF to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VEOF], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n    //\r\n    // Nothing is expected to be echoed.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrnoFailure(read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n\r\n    //\r\n    // Check subordinate data.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 0);\r\n\r\n    //\r\n    // No subordinate data should be left.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrnoFailure(read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets an EOL character and echoes it to an empty buffer,\r\n    verifying the expected results..\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    const char* EchoResult = \"^E\";\r\n    int ExpectedResult;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set VEOL\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    ControlArray[VEOL] = 5;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtsFd, ControlArray));\r\n\r\n    //\r\n    // Write VEOL to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VEOL], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n    //\r\n    // Check echo result.\r\n    //\r\n\r\n    ExpectedResult = strlen(EchoResult);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, EchoResult);\r\n\r\n    //\r\n    // Check subordinate data.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], ControlArray[VEOL], \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets an EOL2 character and echoes it to an empty buffer,\r\n    verifying the expected results..\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    const char* EchoResult = \"^E\";\r\n    int ExpectedResult;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set VEOL2\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    ControlArray[VEOL2] = 5;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtsFd, ControlArray));\r\n\r\n    //\r\n    // Write VEOL to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VEOL2], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n    //\r\n    // Check echo result.\r\n    //\r\n\r\n    ExpectedResult = strlen(EchoResult);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, EchoResult);\r\n\r\n    //\r\n    // Check subordinate data.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], ControlArray[VEOL2], \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine set the VEOL character and sends a string with an embedded\r\n    VEOL, checking the results.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int ExpectedResult;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    const char* WriteValue =\r\n        \"hi\\x5\"\r\n        \"bye\\n\";\r\n    const char* WriteValueEcho = \"hi^Ebye\\r\\n\";\r\n    const char* WriteValueRead1 = \"hi\\x5\";\r\n    const char* WriteValueRead2 = \"bye\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set VEOL\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    ControlArray[VEOL] = 5;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtsFd, ControlArray));\r\n\r\n    //\r\n    // Write string with embedded VEOL to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteValue);\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteValue, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check echo result.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteValueEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteValueEcho);\r\n\r\n    //\r\n    // Check subordinate data. It should be returned as two strings.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteValueRead1);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[ExpectedResult] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteValueRead1);\r\n\r\n    ExpectedResult = strlen(WriteValueRead2);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[ExpectedResult] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteValueRead2);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a string with an embedded VEOF, checking the results.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int ExpectedResult;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    struct timeval Timeout;\r\n    char WriteBuffer[] = {'h', 'i', 0, 'b', 'y', 'e', '\\n'};\r\n    const char* WriteValueEcho = \"hibye\\r\\n\";\r\n    const char* WriteValueRead1 = \"hi\";\r\n    const char* WriteValueRead2 = \"bye\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Add VEOF character\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    WriteBuffer[2] = ControlArray[VEOF];\r\n\r\n    //\r\n    // Write string with embedded VEOL to the master.\r\n    //\r\n\r\n    ExpectedResult = sizeof(WriteBuffer);\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteBuffer, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check echo result.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteValueEcho);\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    Timeout.tv_sec = 1;\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteValueEcho);\r\n\r\n    //\r\n    // Check subordinate data. It should be returned as two strings.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteValueRead1);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[ExpectedResult] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteValueRead1);\r\n\r\n    ExpectedResult = strlen(WriteValueRead2);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[ExpectedResult] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteValueRead2);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck6(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine writes VEOF to an empty buffer, switches to non-canonical mode\r\n    and checks the results.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n\r\n    //\r\n    // Write VEOF to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master: %hhd\", ControlArray[VEOF]);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VEOF], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n    //\r\n    // On Ubuntu16 pty processing is asynchronous so sleep for a second to make\r\n    // sure the VEOF character is processed before switching to raw mode.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Turn off canonical mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Nothing is expected to be echoed.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrnoFailure(read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n\r\n    //\r\n    // Check subordinate data.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck7(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine writes string with VEOF characters in non-canonical mode and\r\n    then reads it back in canonical mode.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    tcflag_t ControlFlags;\r\n    int ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    tcflag_t LocalFlags;\r\n    tcflag_t OutputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    char WriteBuffer[] = {'h', 'i', 0, 'b', 'y', 'e'};\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Capture termios settings.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGet(PtsFd, ControlArray, &ControlFlags, &InputFlags, &LocalFlags, &OutputFlags));\r\n\r\n    WriteBuffer[2] = ControlArray[VEOF];\r\n\r\n    //\r\n    // Switch to non-canonical mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Write string with embedded VEOF to the master.\r\n    //\r\n\r\n    ExpectedResult = sizeof(WriteBuffer);\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteBuffer, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // On Ubuntu16 pty processing is done asynchronously so wait a second to\r\n    // give the character time to be processed before turning off canonical\r\n    // mode.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // No echo expected in non-canonical mode.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrnoFailure(read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n\r\n    //\r\n    // Restore termios settings.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsSet(PtsFd, ControlArray, ControlFlags, InputFlags, LocalFlags, OutputFlags));\r\n\r\n    //\r\n    // Check subordinate data.\r\n    //\r\n\r\n    ExpectedResult = sizeof(WriteBuffer);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckMemoryEqual(ReadBuffer, WriteBuffer, ExpectedResult);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck8(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine writes a string ending with a VEOF character.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int ExpectedResult;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    char WriteBuffer[] = {'a', 0};\r\n    const char* WriteEcho = \"a\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Get control characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    WriteBuffer[1] = ControlArray[VEOF];\r\n\r\n    //\r\n    // Write string to the master.\r\n    //\r\n\r\n    ExpectedResult = sizeof(WriteBuffer);\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteBuffer, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check echo.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteEcho);\r\n\r\n    //\r\n    // Check subordinate data.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, 1));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], WriteBuffer[0], \"%hhd\");\r\n\r\n    //\r\n    // Wrote EOF byte, but it should have been consumed with the last character\r\n    // of the line.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrnoFailure(read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck9(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine writes a non-terminated string in canonical mode, then\r\n    switches to raw and back without any writes to check the availability of\r\n    the data.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    tcflag_t ControlFlags;\r\n    int ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    tcflag_t LocalFlags;\r\n    tcflag_t OutputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    const char* WriteValue = \"hello\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteValue);\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteValue, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Capture termios settings.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGet(PtsFd, ControlArray, &ControlFlags, &InputFlags, &LocalFlags, &OutputFlags));\r\n\r\n    //\r\n    // On Ubuntu16 pty processing is done asynchronously so pause for a second\r\n    // to give the write time to be processed before turning off canonical mode.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Switch to non-canonical mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Restore termios settings.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsSet(PtsFd, ControlArray, ControlFlags, InputFlags, LocalFlags, OutputFlags));\r\n\r\n    //\r\n    // Check subordinate data.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate...\");\r\n    ExpectedResult = strlen(WriteValue);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[ExpectedResult] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteValue);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtLineBreakCheck10(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine writes a non-terminated string in canonical mode, then\r\n    switches to and from raw mode, eventually reading the results in\r\n    raw mode.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    tcflag_t ControlFlags;\r\n    int ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    tcflag_t LocalFlags;\r\n    tcflag_t OutputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    const char* WriteValue = \"hello\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, NULL));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteValue);\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteValue, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Capture termios settings.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGet(PtsFd, ControlArray, &ControlFlags, &InputFlags, &LocalFlags, &OutputFlags));\r\n\r\n    //\r\n    // Switch to non-canonical mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Restore termios settings.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsSet(PtsFd, ControlArray, ControlFlags, InputFlags, LocalFlags, OutputFlags));\r\n\r\n    //\r\n    // Switch back to non-canonical mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Check subordinate data.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteValue);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[ExpectedResult] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, WriteValue);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtMasterFillBuffer(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will fill the master buffer by writing to the subordinate, and\r\n    then test various scenarios:\r\n        1. Write to the master with echo on.\r\n        2. Turn suspend on/off which normally would echo.\r\n        3. Perform a blocking write and unblock via different mechanisms\r\n            a. Read bytes to free up space\r\n            b. flush\r\n            c. close the master causing a hangup\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success; -1 on error.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int Iterations;\r\n    int OldFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[1024];\r\n    int Result;\r\n    int SerialNumber;\r\n    int Status;\r\n    char TestBuffer[] = \"ZYXWVUTSRQPO\\n\";\r\n    char TestBufferEcho[] = \"ZYXWVUTSRQPO\\r\\n\";\r\n    size_t TestBufferLen;\r\n    size_t TotalBytes;\r\n    struct timeval Timeout;\r\n    char WriteBuffer[] = \"0123456789ABC\";\r\n    size_t WriteBufferLen;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    Result = 0;\r\n    TestBufferLen = sizeof(TestBuffer) - 1;\r\n    WriteBufferLen = sizeof(WriteBuffer) - 1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Fork\r\n    //\r\n\r\n    LxtCheckErrno((ChildPid = fork()));\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Child.\r\n        //\r\n        // Mark the subordinate Non-blocking and write to it in a loop.\r\n        // When it is out of room, it will return with EAGAIN.\r\n        //\r\n\r\n        fcntl(PtsFd, F_SETFL, O_NONBLOCK);\r\n        LxtLogInfo(\r\n            \"Filling up the subordinate's buffer. \"\r\n            \"This might take some time...\");\r\n\r\n        TotalBytes = 0;\r\n        for (;;)\r\n        {\r\n            BytesReadWrite = write(PtsFd, WriteBuffer, WriteBufferLen);\r\n            if (BytesReadWrite < 0)\r\n            {\r\n                if (errno != EAGAIN)\r\n                {\r\n                    LxtLogError(\r\n                        \"Expecting the write to return with \"\r\n                        \"result:%d(%s), but it returned with result:%d(%s)\",\r\n                        EAGAIN,\r\n                        strerror(EAGAIN),\r\n                        errno,\r\n                        strerror(errno));\r\n\r\n                    Result = -1;\r\n                    goto ErrorExit;\r\n                }\r\n                else\r\n                {\r\n\r\n                    //\r\n                    // On Ubuntu, the buffer auto-expands at least once under\r\n                    // memory pressure so wait a bit to see if the buffer is\r\n                    // really full.\r\n                    //\r\n\r\n                    memset(&Timeout, 0, sizeof(Timeout));\r\n                    Timeout.tv_sec = 1;\r\n                    FD_ZERO(&WriteFds);\r\n                    FD_SET(PtsFd, &WriteFds);\r\n                    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n                    if (Result == 0)\r\n                    {\r\n                        break;\r\n                    }\r\n                }\r\n            }\r\n            else if (BytesReadWrite < WriteBufferLen)\r\n            {\r\n                LxtLogInfo(\"Last write added %d bytes of %d bytes\", BytesReadWrite, WriteBufferLen);\r\n            }\r\n\r\n            TotalBytes += BytesReadWrite;\r\n        }\r\n\r\n        LxtLogInfo(\"Buffer filled up with %lld bytes\", TotalBytes);\r\n\r\n        //\r\n        // Try to write to the master with echo on and a full master endpoint\r\n        // buffer. The write should succeed and the echo characters should be\r\n        // discarded.\r\n        //\r\n\r\n        fcntl(PtmFd, F_SETFL, O_NONBLOCK);\r\n        LxtCheckErrno(BytesReadWrite = write(PtmFd, TestBuffer, TestBufferLen));\r\n\r\n        //\r\n        // Check that the test message with failed echo was received.\r\n        //\r\n\r\n        LxtLogInfo(\"Reading back message written to master...\");\r\n        LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckEqual(BytesReadWrite, TestBufferLen, \"%llu\");\r\n        LxtCheckMemoryEqual(ReadBuffer, TestBuffer, TestBufferLen);\r\n\r\n        //\r\n        // Try to turn suspend on/off with the buffer full. This normally would\r\n        // echo the start/stop characters back to the master endpoint.\r\n        //\r\n\r\n        LxtLogInfo(\"Toggling suspend...\");\r\n        LxtCheckErrno(tcflow(PtmFd, TCIOFF));\r\n        LxtCheckErrno(tcflow(PtmFd, TCION));\r\n        LxtClose(PtmFd);\r\n\r\n        //\r\n        // Drain on Linux is effected by the buffer being full, but because\r\n        // PtsFd is marked with O_NONBLOCK this should complete.\r\n        //\r\n\r\n        LxtLogInfo(\"Draining queue...\");\r\n        LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n        //\r\n        // Sanity check to verify that the buffer is still full.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, WriteBuffer, WriteBufferLen), EAGAIN);\r\n\r\n        //\r\n        // Try to write a byte, which will block. Wait for the other thread\r\n        // to unblock this request. Do this multiple times to test different\r\n        // methods of unblocking.\r\n        //\r\n\r\n        for (Iterations = 0; Iterations < 2; Iterations += 1)\r\n        {\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LxtLogInfo(\"Writing to the subordinate.\");\r\n            LxtCheckErrno((OldFlags = fcntl(PtsFd, F_GETFL)));\r\n            fcntl(PtsFd, F_SETFL, OldFlags & ~O_NONBLOCK);\r\n            LxtCheckErrno(BytesReadWrite = write(PtsFd, WriteBuffer, 1));\r\n            LxtCheckEqual(BytesReadWrite, 1, \"%llu\");\r\n\r\n            //\r\n            // Fill the buffer back up.\r\n            //\r\n\r\n            LxtLogInfo(\"Refilling the buffer...\");\r\n            LxtCheckErrno((OldFlags = fcntl(PtsFd, F_GETFL)));\r\n            fcntl(PtsFd, F_SETFL, OldFlags | O_NONBLOCK);\r\n            for (;;)\r\n            {\r\n                BytesReadWrite = write(PtsFd, WriteBuffer, WriteBufferLen);\r\n                if (BytesReadWrite < 0)\r\n                {\r\n                    LxtCheckErrnoFailure(-1, EAGAIN);\r\n                    break;\r\n                }\r\n            }\r\n        }\r\n\r\n        //\r\n        // When the master hangs up eventually, the blocked write should\r\n        // return.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtLogInfo(\"Writing to the subordinate.\");\r\n        LxtCheckErrno((OldFlags = fcntl(PtsFd, F_GETFL)));\r\n        fcntl(PtsFd, F_SETFL, OldFlags & ~O_NONBLOCK);\r\n        LxtCheckErrnoFailure((BytesReadWrite = write(PtsFd, WriteBuffer, 1)), EIO);\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Parent.\r\n        //\r\n\r\n        //\r\n        // Close the subordinate device handle and wait for the master buffer\r\n        // to fill.\r\n        //\r\n\r\n        LxtLogInfo(\"Waiting for the subordinate to fill its buffer...\");\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait a bit to make sure the write from the child is blocked.\r\n        //\r\n\r\n        LxtLogInfo(\"Waiting a bit for the subordinate write to block...\");\r\n        sleep(1);\r\n\r\n        //\r\n        // On Ubuntu, there seems to be some odd behavior when you fill the\r\n        // buffer up. After filling the buffer, you need to read some multiple\r\n        // of the byte chunks written before a new write will succeed. For\r\n        // example, if you write 13 bytes 1522 times to fill up the buffer, you\r\n        // may need to read back 36 of those writes (13*36 = 468 bytes) before\r\n        // the next write will succeed. Worse, if you queue a write while the\r\n        // buffer is full, you need to completely empty the buffer before that\r\n        // write will complete.\r\n        //\r\n\r\n        LxtLogInfo(\"Unblocking write by reading from master...\");\r\n        TotalBytes = 0;\r\n        for (Iterations = 0;; Iterations += 1)\r\n        {\r\n            LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n            TotalBytes += BytesReadWrite;\r\n            LxtLogInfo(\"Checking write ready...\");\r\n            memset(&Timeout, 0, sizeof(Timeout));\r\n            Timeout.tv_sec = 1;\r\n            FD_ZERO(&WriteFds);\r\n            FD_SET(PtsFd, &WriteFds);\r\n            LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n            if (Result == 1)\r\n            {\r\n                break;\r\n            }\r\n        }\r\n\r\n        LxtLogInfo(\"Removed %lld bytes from buffer\", TotalBytes);\r\n\r\n        //\r\n        // Unblock write by flushing master input buffer.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtLogInfo(\"Waiting a bit for the subordinate write to block...\");\r\n        sleep(1);\r\n        LxtLogInfo(\"Unblocking write by flushing master input...\");\r\n        LxtCheckErrno(tcflush(PtmFd, TCIFLUSH));\r\n\r\n        //\r\n        // On Ubuntu, flushing the subordinate output buffer appears to free up\r\n        // space as expected. It does not however seem to complete the queued\r\n        // read. Skip this test for now.\r\n        //\r\n\r\n        /*\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtLogInfo(\"Waiting a bit for the subordinate write to block...\");\r\n        sleep(1);\r\n        LxtLogInfo(\"Unblocking write by flushing subordinate output...\");\r\n        LxtCheckErrno(tcflush(PtsFd, TCOFLUSH));\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, 1));\r\n        */\r\n\r\n        LxtClose(PtsFd);\r\n\r\n        //\r\n        // Hangup the master endpoint.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtLogInfo(\"Waiting a bit for the subordinate write to block...\");\r\n        sleep(1);\r\n        LxtLogInfo(\"Hanging up master endpoint to unblock writer thread.\") LxtClose(PtmFd);\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtMasterHangup1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will try to determine the behavior when the subordinate\r\n    tries to write after the master has hangup.\r\n    Expected Result: The write on subordinate should return with error 5:EIO.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success; -1 on error.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int LoopCount;\r\n    int NonBlockingValue;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[1024];\r\n    int Result;\r\n    int SerialNumber;\r\n    tcflag_t TermiosFlags;\r\n    char WriteBuffer[10] = {\"123456789\"};\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    Result = 0;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Hangup Master\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n\r\n    //\r\n    // Set a file-descriptor flag.\r\n    //\r\n\r\n    NonBlockingValue = 0;\r\n    LxtCheckErrno(ioctl(PtsFd, FIONBIO, &NonBlockingValue));\r\n\r\n    //\r\n    // Attempt to get the current termios settings from the subordinate.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(TerminalSettingsGetOutputFlags(PtsFd, &TermiosFlags), EIO);\r\n\r\n    //\r\n    // Write on subordinate.\r\n    //\r\n\r\n    LxtCheckErrnoFailure((BytesReadWrite = write(PtsFd, WriteBuffer, strlen(WriteBuffer) + 1)), EIO);\r\n\r\n    //\r\n    // Mark the subordinate as non-blocking and attempt write again.\r\n    // Expected behavior is the same as of previous write.\r\n    //\r\n\r\n    fcntl(PtsFd, F_SETFL, O_NONBLOCK);\r\n    LxtCheckErrnoFailure((BytesReadWrite = write(PtsFd, WriteBuffer, strlen(WriteBuffer) + 1)), EIO);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtMasterHangup2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will try to determine the behavior when the master opens\r\n    and closes immediately. Subordinate then tries to read. Also checks the\r\n    behavior of the master disconnecting while the subordinate is blocked in a\r\n    read.\r\n    Expected Result: The read on subordinate should return 0 bytes read.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success; -1 on error.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int LoopCount;\r\n    pid_t Pid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[1024];\r\n    int Result;\r\n    int SerialNumber;\r\n    char WriteBuffer[10] = {\"123456789\"};\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    Pid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    Result = LXT_RESULT_FAILURE;\r\n\r\n    //\r\n    // First check the behavior of read after hang-up.\r\n    //\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Hangup Master\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n\r\n    //\r\n    // read on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno((BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer))));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 0);\r\n    LxtClose(PtsFd);\r\n\r\n    //\r\n    // Now, check the behavior of hang-up during a blocking read.\r\n    //\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    LxtCheckErrno((Pid = fork()));\r\n    if (Pid == 0)\r\n    {\r\n\r\n        //\r\n        // Child - hangup during a blocked read returns EIO but a read after\r\n        // hangup returns EOF.\r\n        //\r\n\r\n        LxtClose(PtmFd);\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EIO);\r\n        LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, 0);\r\n        Result = LXT_RESULT_SUCCESS;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Close the subordinate device handle.\r\n    //\r\n\r\n    LxtClose(PtsFd);\r\n    LxtLogInfo(\"Waiting for the subordinate to block in read...\");\r\n    usleep(2 * 500 * 1000);\r\n\r\n    //\r\n    // Hangup master. This should unblock the subordinate's blocked read.\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n    LxtCheckResult(LxtWaitPidPoll(Pid, 0));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    //\r\n    // Exit if child process.\r\n    //\r\n\r\n    if (Pid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtMasterHangup3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will try to determine the behavior when the master opens,\r\n    writes some complete messages and closes. Subordinate then tries to read.\r\n    Expected Result: The read on subordinate should return 0 bytes read.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success; -1 on error.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int LoopCount;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[50];\r\n    int Result;\r\n    int SerialNumber;\r\n    char WriteBuffer[] = {\"123456789\"};\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    Result = 0;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Write two complete messages to the Master.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteBuffer) + 1;\r\n    LxtCheckErrno((BytesReadWrite = write(PtmFd, WriteBuffer, ExpectedResult)));\r\n\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno((BytesReadWrite = write(PtmFd, WriteBuffer, ExpectedResult)));\r\n\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Hangup Master\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n\r\n    //\r\n    // read on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno((BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer))));\r\n\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 0);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtMasterHangup4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will try to determine the behavior when the master opens,\r\n    writes some incomplete messages and closes. Subordinate then tries to read.\r\n    Expected Result: The read on subordinate should return 0 bytes read.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success; -1 on error.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int LoopCount;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[1024];\r\n    int Result;\r\n    int SerialNumber;\r\n    char* WriteBuffer = \"123456789\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    Result = 0;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n    LxtLogInfo(\"Setting non blocking\");\r\n    int one = fcntl(PtsFd, F_SETFD, O_NONBLOCK);\r\n    fcntl(PtmFd, F_SETFD, O_NONBLOCK);\r\n\r\n    //\r\n    // Write an incomplete(without the last CR) message to the Master.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteBuffer);\r\n    LxtCheckErrno((BytesReadWrite = write(PtmFd, WriteBuffer, ExpectedResult)));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Hangup Master\r\n    //\r\n\r\n    LxtClose(PtmFd);\r\n\r\n    //\r\n    // read on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno((BytesReadWrite = read(PtsFd, ReadBuffer, 1)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 0);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtMoreThanOne(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates that more than one pseudo terminal can be opened\r\n    at any given time. Ideally the test should validate for MAX pt, but there\r\n    can be open pt's while the test is executing (for example, if the test is\r\n    run over adb shell). So, the test will validate that at least half of max\r\n    pt's can be opened. For every master-subordinate pair that it opens, it\r\n    will also perform a simple read/write check.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Itr;\r\n\r\n    //\r\n    // Number of times to test.\r\n    //\r\n\r\n    int Loop;\r\n    int LoopCount;\r\n    int NumPtToTest;\r\n    char PtsDevName[PTS_DEV_NAME_BUFFER_SIZE];\r\n    int Result;\r\n\r\n    //\r\n    // PtsFd[<n>][0] will hold the fd for master and\r\n    // PtsFd[<n>][1]] will hold the fd for the subordinate.\r\n    //\r\n\r\n    int PtFds[PTY_MAX_OPEN_LIMIT / 2][2];\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    Loop = 2;\r\n    NumPtToTest = PTY_MAX_OPEN_LIMIT / 2;\r\n    for (Itr = 0; Itr < NumPtToTest; Itr++)\r\n    {\r\n        PtFds[Itr][0] = -1;\r\n        PtFds[Itr][1] = -1;\r\n    }\r\n\r\n    for (LoopCount = 0; LoopCount < Loop; LoopCount++)\r\n    {\r\n        LxtLogInfo(\"Opening %d pt's, loop count:%d\", NumPtToTest, LoopCount + 1);\r\n\r\n        for (Itr = 0; Itr < NumPtToTest; Itr++)\r\n        {\r\n\r\n            //\r\n            // Open Master-Subordinate for Itr\r\n            //\r\n\r\n            LxtCheckErrno(OpenMasterSubordinate(&PtFds[Itr][0], &PtFds[Itr][1], PtsDevName, &SerialNumber));\r\n\r\n            //\r\n            // Enable raw input on the subordinates.\r\n            //\r\n\r\n            LxtCheckErrno(RawInit(PtFds[Itr][1]));\r\n            LxtLogInfo(\"Master opened at FD:%d\", PtFds[Itr][0]);\r\n            LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n            LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n            LxtLogInfo(\"Subordinate opened at FD:%d\", PtFds[Itr][1]);\r\n\r\n            //\r\n            // Perform a simple read/write check on the master-subordinate.\r\n            //\r\n\r\n            LxtLogInfo(\r\n                \"Performing a simple read/write check on\"\r\n                \"master-subordinate pair...\");\r\n\r\n            LxtCheckErrno(SimpleReadWriteCheck(PtFds[Itr][0], PtFds[Itr][1]));\r\n        }\r\n\r\n        //\r\n        // Once all of the pt's are open, close them for the next\r\n        // Loop.\r\n        //\r\n\r\n        LxtLogInfo(\"Closing the pt's\");\r\n        for (Itr = 0; Itr < NumPtToTest; Itr++)\r\n        {\r\n            LxtClose(PtFds[Itr][0]);\r\n            LxtClose(PtFds[Itr][1]);\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    for (Itr = 0; Itr < NumPtToTest; Itr++)\r\n    {\r\n        if (PtFds[Itr][0] != -1)\r\n        {\r\n            close(PtFds[Itr][0]);\r\n        }\r\n\r\n        if (PtFds[Itr][1] != -1)\r\n        {\r\n            close(PtFds[Itr][1]);\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtMultiMessageReadWrite(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates multi-message behavior.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    char Lf = 10;\r\n    struct iovec Iov[5];\r\n    int MessageNum;\r\n    char* Messages[] = {\"ABC\\n\", \"\\n\", \"DE\\r\", \"FG\", \"HI\\n\"};\r\n    char* ExpectedReadMessages[] = {\"ABC\\n\", \"\\n\", \"DE\\n\", \"FGHI\\n\"};\r\n    int OldFlags;\r\n    void* PointerResult;\r\n    int PtmFd;\r\n    FILE* PtmFile;\r\n    int PtsFd;\r\n    char ReadMessage[50] = {0};\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    PtmFile = NULL;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtCheckNullErrno(PtmFile = fdopen(PtmFd, \"w\"));\r\n\r\n    //\r\n    // This is a message boundary test, do not set the subordinate for raw init.\r\n    //\r\n\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Send the message(1 to 4) to the subordinate.\r\n    //\r\n\r\n    BytesReadWrite = fprintf(PtmFile, \"%s%s%s%s\", Messages[0], Messages[1], Messages[2], Messages[3]);\r\n    LxtCheckErrno(fflush(PtmFile));\r\n    ExpectedResult = strlen(Messages[0]) + strlen(Messages[1]) + strlen(Messages[2]) + strlen(Messages[3]);\r\n    LxtLogInfo(\"Message sent(%d bytes) to subordinate: \\n%s%s%s%s\", BytesReadWrite, Messages[0], Messages[1], Messages[2], Messages[3]);\r\n\r\n    LxtCheckFnResults(\"fprintf\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Every read from the subordinate should return one message at a time.\r\n    // If the message is not complete, the read will block.\r\n    //\r\n\r\n    for (MessageNum = 0; MessageNum < 3; ++MessageNum)\r\n    {\r\n        LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadMessage, sizeof(ReadMessage)));\r\n\r\n        ReadMessage[BytesReadWrite] = '\\0';\r\n        LxtLogInfo(\"Message read(%d bytes) from subordinate: \\n%s\", BytesReadWrite, ReadMessage);\r\n\r\n        ExpectedResult = strlen(ExpectedReadMessages[MessageNum]);\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n        //\r\n        // Compare the messages.\r\n        //\r\n\r\n        if (memcmp(ReadMessage, ExpectedReadMessages[MessageNum], min(BytesReadWrite, ExpectedResult)) != 0)\r\n        {\r\n\r\n            LxtLogError(\r\n                \"Data read from subordinate does not match what was \"\r\n                \"written by master.\");\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Next read on the subordinate should block. Set it to non-blocking.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrnoFailure(read(PtsFd, ReadMessage, sizeof(ReadMessage)), EAGAIN);\r\n\r\n    //\r\n    // Complete the message from the master side and try reading again\r\n    // from the subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Messages[MessageNum + 1]);\r\n    BytesReadWrite = write(PtmFd, Messages[MessageNum + 1], ExpectedResult);\r\n    LxtLogInfo(\"Message sent(%d bytes) to subordinate: \\n%s\", BytesReadWrite, Messages[MessageNum + 1]);\r\n\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Mark the subordinate as blocking again.\r\n    //\r\n\r\n    LxtCheckErrno((OldFlags = fcntl(PtsFd, F_GETFL)));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, OldFlags & ~O_NONBLOCK));\r\n\r\n    //\r\n    // Read next message.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadMessage, sizeof(ReadMessage)));\r\n\r\n    ReadMessage[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Message read(%d bytes) from subordinate: \\n%s\", BytesReadWrite, ReadMessage);\r\n\r\n    ExpectedResult = strlen(ExpectedReadMessages[MessageNum]);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadMessage, ExpectedReadMessages[MessageNum], min(BytesReadWrite, ExpectedResult)) != 0)\r\n    {\r\n\r\n        LxtLogError(\r\n            \"Data read from subordinate does not match what was \"\r\n            \"written by master.\");\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Use the writev system call to write the messages again.\r\n    //\r\n\r\n    Iov[0].iov_base = Messages[0];\r\n    Iov[0].iov_len = strlen(Messages[0]);\r\n    Iov[1].iov_base = Messages[1];\r\n    Iov[1].iov_len = strlen(Messages[1]);\r\n    Iov[2].iov_base = Messages[2];\r\n    Iov[2].iov_len = strlen(Messages[2]);\r\n    Iov[3].iov_base = Messages[3];\r\n    Iov[3].iov_len = strlen(Messages[3]);\r\n    Iov[4].iov_base = Messages[4];\r\n    Iov[4].iov_len = strlen(Messages[4]);\r\n    LxtCheckErrno(BytesReadWrite = writev(PtmFd, Iov, 5));\r\n    LxtLogInfo(\"writev wrote %d bytes\", BytesReadWrite);\r\n\r\n    //\r\n    // Every read from the subordinate should return one message at a time.\r\n    // If the message is not complete, the read will block.\r\n    //\r\n\r\n    for (MessageNum = 0; MessageNum < 4; ++MessageNum)\r\n    {\r\n        LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadMessage, sizeof(ReadMessage)));\r\n\r\n        ReadMessage[BytesReadWrite] = '\\0';\r\n        LxtLogInfo(\"Message %d read(%d bytes) from subordinate: \\n%s\", MessageNum, BytesReadWrite, ReadMessage);\r\n\r\n        ExpectedResult = strlen(ExpectedReadMessages[MessageNum]);\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n        //\r\n        // Compare the messages.\r\n        //\r\n\r\n        if (memcmp(ReadMessage, ExpectedReadMessages[MessageNum], min(BytesReadWrite, ExpectedResult)) != 0)\r\n        {\r\n\r\n            LxtLogError(\r\n                \"Data read from subordinate does not match what was \"\r\n                \"written by master.\");\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Ensure there are no other messages.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrnoFailure(read(PtsFd, ReadMessage, sizeof(ReadMessage)), EAGAIN);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (PtmFile != NULL)\r\n    {\r\n        fclose(PtmFile);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtReadNoSub1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    The PtReadNoSubxxx validate the behavior of read on the master, when\r\n    there are no open handles to the subordinate; where 'xxx' is the\r\n    sub-test case as described below:\r\n    1. A handle to the subordinate was never opened\r\n       Expected Result:\r\n           For blocking call, the read will block.\r\n           For non-blocking call, read should return error EAGAIN.\r\n    2. A handle to subordinate was opened and closed and then the read on\r\n       master was attempted.\r\n       Expected Result: Read should return error:5(EIO).\r\n    3. A handle to subordinate was opened, sub wrote few bytes and then\r\n       closed. Then read is attempted on master for fewer bytes than that\r\n       were written.\r\n       Expected Result: Read should return successfully for the number of\r\n           bytes written. After that any read should return error:5(EIO).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer;\r\n    int PtmFd;\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n\r\n    //\r\n    // Open Master.\r\n    //\r\n\r\n    LxtCheckErrno((PtmFd = open(\"/dev/ptmx\", O_RDWR)));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n\r\n    //\r\n    // Set master to non-blocking and then attempt a read on master.\r\n    //\r\n\r\n    fcntl(PtmFd, F_SETFL, O_NONBLOCK);\r\n    LxtCheckErrnoFailure(read(PtmFd, &Buffer, 1), EAGAIN);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtReadNoSub2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    See PtReadNoSub1 for details.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Close the subordinate.\r\n    //\r\n\r\n    LxtClose(PtsFd);\r\n    LxtLogInfo(\"Subordinate closed\");\r\n\r\n    //\r\n    // Set master to non-blocking and then attempt a read on master.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(read(PtmFd, &Buffer, 1), EIO);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtReadNoSub3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    See PtReadNoSub1 for details.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int ExpectedResult;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    char WriteBuffer[] = \"abcd\";\r\n    int SerialNumber;\r\n    int BytesReadWrite;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Write few bytes to the subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(WriteBuffer);\r\n    LxtCheckErrno((BytesReadWrite = write(PtsFd, &WriteBuffer, ExpectedResult)));\r\n\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Close the subordinate.\r\n    //\r\n\r\n    LxtClose(PtsFd);\r\n    LxtLogInfo(\"Subordinate closed\");\r\n\r\n    //\r\n    // Set master to non-blocking and then attempt a read on master.\r\n    //\r\n\r\n    LxtCheckErrno((BytesReadWrite = read(PtmFd, ReadBuffer, 1)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    if (ReadBuffer[0] != WriteBuffer[0])\r\n    {\r\n        LxtLogError(\r\n            \"data read does not match expected. Expected data:%d, \"\r\n            \"read:%d\",\r\n            WriteBuffer[0],\r\n            ReadBuffer[0]);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Drain all the data from master. We have already read 1 byte before.\r\n    //\r\n\r\n    LxtCheckErrno((BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer))));\r\n\r\n    ExpectedResult = strlen(WriteBuffer) - 1;\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Once the data has been drained from the master buffer, read\r\n    // should return error.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(read(PtmFd, &ReadBuffer, 1), EIO);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtStressIo(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    The routine performs IO Stress test. It will open STRESS_NUM_PT\r\n    number of pseudo terminals (pt). For each pt, it will create\r\n    STRESS_NUM_THREAD threads, where each thread will do a\r\n    SimpleReadWrite check for STRESS_NUM_ITERATION cycles.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Itr;\r\n    char PtsDevName[50];\r\n\r\n    //\r\n    // PtsFd[<n>][0] will hold the fd for master and\r\n    // PtsFd[<n>][1]] will hold the fd for the subordinate.\r\n    //\r\n\r\n    int PtFds[STRESS_NUM_PT][2];\r\n    int Result;\r\n    int SerialNumber;\r\n    pthread_t Thread[STRESS_NUM_PT][STRESS_NUM_THREAD];\r\n    StressThreadArg ThreadArg[STRESS_NUM_PT];\r\n    int ThreadItr;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    for (Itr = 0; Itr < STRESS_NUM_PT; Itr++)\r\n    {\r\n        PtFds[Itr][0] = -1;\r\n        PtFds[Itr][1] = -1;\r\n    }\r\n\r\n    //\r\n    // Open all the pseudo terminals required for the stress.\r\n    //\r\n\r\n    for (Itr = 0; Itr < STRESS_NUM_PT; Itr++)\r\n    {\r\n\r\n        //\r\n        // Open Master-Subordinate for Itr\r\n        //\r\n\r\n        LxtCheckErrno(OpenMasterSubordinate(&PtFds[Itr][0], &PtFds[Itr][1], PtsDevName, &SerialNumber));\r\n\r\n        //\r\n        // This is a message boundary test, do not set the subordinate for raw\r\n        // init.\r\n        //\r\n\r\n        LxtLogInfo(\"PT#%d: Master FD:%d\", Itr, PtFds[Itr][0]);\r\n        LxtLogInfo(\"PT#%dSubordinate FD:%d\", Itr, PtFds[Itr][1]);\r\n        LxtLogInfo(\"PT#%dSubordinate Device is:%s\", Itr, PtsDevName);\r\n        LxtLogInfo(\"PT#%dSubordinate Serial Number: %d\", Itr, SerialNumber);\r\n    }\r\n\r\n    //\r\n    // For each PT, create threads.\r\n    // Lock the stress mutex. This will allow to gate every stress thread\r\n    // at the start.\r\n    //\r\n\r\n    pthread_mutex_lock(&DevPtStressMutex);\r\n    for (Itr = 0; Itr < STRESS_NUM_PT; Itr++)\r\n    {\r\n\r\n        //\r\n        // Set up the argument for the stress I/O thread.\r\n        //\r\n\r\n        ThreadArg[Itr].PtmFd = PtFds[Itr][0];\r\n        ThreadArg[Itr].PtsFd = PtFds[Itr][1];\r\n        ThreadArg[Itr].LoopCount = STRESS_NUM_ITERATION;\r\n\r\n        for (ThreadItr = 0; ThreadItr < STRESS_NUM_THREAD; ThreadItr++)\r\n        {\r\n\r\n            //\r\n            // Create I/O Stress thread#ThreadItr for PT#Itr\r\n            //\r\n\r\n            LxtCheckErrno(pthread_create(&Thread[Itr][ThreadItr], NULL, PerformIoStressThread, (void*)&ThreadArg[Itr]));\r\n        }\r\n    }\r\n\r\n    LxtLogInfo(\"\\nStress Start Time:\");\r\n    system(\"date\");\r\n\r\n    //\r\n    // Open the flood gates.\r\n    //\r\n\r\n    pthread_mutex_unlock(&DevPtStressMutex);\r\n\r\n    //\r\n    // Wait for all the threads to terminate.\r\n    //\r\n\r\n    for (Itr = 0; Itr < STRESS_NUM_PT; Itr++)\r\n    {\r\n        for (ThreadItr = 0; ThreadItr < STRESS_NUM_THREAD; ThreadItr++)\r\n        {\r\n            pthread_join(Thread[Itr][ThreadItr], NULL);\r\n        }\r\n    }\r\n\r\n    LxtLogInfo(\"\\nStress End Time:\");\r\n    system(\"date\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    for (Itr = 0; Itr < STRESS_NUM_PT; Itr++)\r\n    {\r\n        if (PtFds[Itr][0] != -1)\r\n        {\r\n            close(PtFds[Itr][0]);\r\n            PtFds[Itr][0] = -1;\r\n        }\r\n\r\n        if (PtFds[Itr][1] != -1)\r\n        {\r\n            close(PtFds[Itr][1]);\r\n            PtFds[Itr][1] = -1;\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Basic(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns off canonical mode, turns on UTF8 mode and send a UTF8\r\n    character. UTF8 mode should have no effect in either raw or canonical mode\r\n    for this operation.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"\\xE2\\x82\\xAC\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Write UTF-8 character to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Read from subordinate.\r\n    //\r\n\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8String, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Basic2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns off canonical mode, turns on UTF8 mode and sends two\r\n    UTF8 characters. UTF8 mode should have no effect in either raw or canonical\r\n    mode for this operation.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"\\xE2\\x82\\xAC\\xE2\\x82\\xAC\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Verify that the minimum character value is '1' by default.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    LxtCheckEqual(ControlArray[VMIN], 1, \"%hhd\");\r\n\r\n    //\r\n    // Write UTF-8 characters to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Read a single byte from the subordinate.\r\n    //\r\n\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, 1));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(Utf8String[0], ReadBuffer[0], \"%hhd\");\r\n\r\n    //\r\n    // Read the remainder from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, &ReadBuffer[1], (sizeof(ReadBuffer) - 1)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, (ExpectedResult - 1));\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Basic3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns on UTF8 mode and sends two UTF-8 characters. UTF8 mode\r\n    should have no effect in either raw or canonical mode for this operation.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char Utf8FirstByte = '\\xE2';\r\n    const char* Utf8String = \"\\xE2\\x82\\xAC\\xE2\\x82\\xAC\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Write UTF-8 characters to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master with a\r\n    // carriage-return and newline.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, (ExpectedResult + 1));\r\n    if ((ReadBuffer[BytesReadWrite - 1] != '\\n') || (ReadBuffer[BytesReadWrite - 2] != '\\r'))\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not end with \\r\\n.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    ReadBuffer[BytesReadWrite - 2] = '\\n';\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Try to read a single-byte from subordinate.\r\n    //\r\n\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtLogInfo(\"Reading one byte from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, 1));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(Utf8FirstByte, ReadBuffer[0], \"%c\");\r\n\r\n    //\r\n    // Try to read the rest of the message.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading more from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, &ReadBuffer[1], sizeof(ReadBuffer) - 1));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, (ExpectedResult - 1));\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8String, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Basic4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns on UTF8 mode and send a UTF8 character. UTF8 mode should\r\n    have no effect for this operation.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"\\xE2\\x82\\xAC\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Write UTF-8 characters to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master with a\r\n    // carriage-return and newline.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, (ExpectedResult + 1));\r\n    if ((ReadBuffer[BytesReadWrite - 1] != '\\n') || (ReadBuffer[BytesReadWrite - 2] != '\\r'))\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not end with \\r\\n.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    ReadBuffer[BytesReadWrite - 2] = '\\n';\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read from subordinate.\r\n    //\r\n\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8String, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Basic5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends part of a UTF8 character.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"\\xE2\\x82\\xAC\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Write the first byte of the UTF-8 characters to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n    //\r\n    // Check that the byte has been echoed back.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(Utf8String[0], ReadBuffer[0], \"%hhd\");\r\n\r\n    //\r\n    // Write the remaining bytes of the UTF-8 characters to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &Utf8String[1], (ExpectedResult - 1)));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, (ExpectedResult - 1));\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master with a\r\n    // carriage-return and newline.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, &ReadBuffer[1], (sizeof(ReadBuffer) - 1)));\r\n    BytesReadWrite += 1;\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, (ExpectedResult + 1));\r\n    if ((ReadBuffer[BytesReadWrite - 1] != '\\n') || (ReadBuffer[BytesReadWrite - 2] != '\\r'))\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not end with \\r\\n.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    ReadBuffer[BytesReadWrite - 2] = '\\n';\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read from subordinate.\r\n    //\r\n\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8String, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Basic6(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns on UTF8 mode and sends part of a UTF8 character.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"\\xE2\\x82\\xAC\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Write the first byte of the UTF-8 characters to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\n    //\r\n    // Check that the byte has been echoed back.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(Utf8String[0], ReadBuffer[0], \"%hhd\");\r\n\r\n    //\r\n    // Write the remaining bytes of the UTF-8 characters to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &Utf8String[1], (ExpectedResult - 1)));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, (ExpectedResult - 1));\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master with a\r\n    // carriage-return and newline.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, &ReadBuffer[1], (sizeof(ReadBuffer) - 1)));\r\n    BytesReadWrite += 1;\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, (ExpectedResult + 1));\r\n    if ((ReadBuffer[BytesReadWrite - 1] != '\\n') || (ReadBuffer[BytesReadWrite - 2] != '\\r'))\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not end with \\r\\n.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    ReadBuffer[BytesReadWrite - 2] = '\\n';\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read from subordinate.\r\n    //\r\n\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8String, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Basic7(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a string ending with a UTF-8 character, followed by the\r\n    delete char. This is expected to remove only a single byte from the string.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"hello\\xE2\\x82\\xAC\";\r\n    const char* Utf8StringFinal = \"hello\\xE2\\x82\\n\";\r\n    char EndString[3] = {0, '\\n', '\\0'};\r\n    const char* EndStringEcho = \"\\x8 \\x8\\r\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Do NOT set UTF-8 mode for this test.\r\n    //\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    EndString[0] = ControlArray[VERASE];\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Now send delete character followed by the newline.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(EndString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, EndString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, EndString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(EndStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, EndStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not match expected value.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read the message from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    ExpectedResult = strlen(Utf8StringFinal);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8StringFinal, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Basic8(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns on UTF8 mode and then sends a string ending with a UTF-8\r\n    character. Then it sends the delete char. This is expected to remove all of\r\n    the bytes from the UTF-8 character.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[10];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"hello\\xE2\\x82\\xAC\";\r\n    const char* Utf8StringFinal = \"hello\\n\";\r\n    char EndString[3] = {0, '\\n', '\\0'};\r\n    const char* EndStringEcho = \"\\x8 \\x8\\r\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    EndString[0] = ControlArray[VERASE];\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Now send delete character followed by the newline.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(EndString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, EndString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, EndString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(EndStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, EndStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not match expected value.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read the message from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    ExpectedResult = strlen(Utf8StringFinal);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8StringFinal, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Malformed(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns on UTF8 mode and then sends a string ending with a\r\n    malformed UTF-8 character. Then it sends the delete char.\r\n\r\n    The observed behavior is to do a simple removal of all bytes beginning with\r\n    0b10 plus one more which for a real UTF-8 character would be the beginning\r\n    byte.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[15];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"howdy\\x80\\x80\\x80\\x80\\x80\\x80\";\r\n    const char* Utf8StringFinal = \"howd\\n\";\r\n    char EndString[3] = {0, '\\n', '\\0'};\r\n    const char* EndStringEcho = \"\\x8 \\x8\\r\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    EndString[0] = ControlArray[VERASE];\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Now send delete character followed by the newline.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(EndString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, EndString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, EndString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(EndStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, EndStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not match expected value.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read the message from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    ExpectedResult = strlen(Utf8StringFinal);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8StringFinal, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Malformed2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns on UTF8 mode and then sends a string ending with a\r\n    malformed UTF-8 character. Then it sends the delete char.\r\n\r\n    The observed behavior is to do a simple removal of all bytes beginning with\r\n    0b10 plus one more which for a real UTF-8 character would be the beginning\r\n    byte.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[15];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"howdy\\x80\\x80\\x80\\x80\\x80\\xf0\";\r\n    const char* Utf8StringFinal = \"howdy\\x80\\x80\\x80\\x80\\x80\\n\";\r\n    char EndString[3] = {0, '\\n', '\\0'};\r\n    const char* EndStringEcho = \"\\x8 \\x8\\r\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    EndString[0] = ControlArray[VERASE];\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Now send delete character followed by the newline.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(EndString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, EndString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, EndString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(EndStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, EndStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not match expected value.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read the message from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    ExpectedResult = strlen(Utf8StringFinal);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8StringFinal, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Malformed3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns on UTF8 mode and then sends two strings, the last\r\n    consisting of only a malformed UTF-8 character. Then it sends the delete\r\n    char.\r\n\r\n    The observed behavior is to do a simple removal of all bytes\r\n    beginning with 0b10 until it hits the beginning of the line. Apparently\r\n    treating this as an error, no echo is done.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[15];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"howdy\\n\\x80\\x80\";\r\n    const char* Utf8StringEcho = \"howdy\\r\\n\\x80\\x80\";\r\n    const char* Utf8StringFinal = \"howdy\\n\";\r\n    char EndString[3] = {0, '\\n', '\\0'};\r\n    const char* EndStringEcho = \"\\r\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    EndString[0] = ControlArray[VERASE];\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(Utf8StringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, Utf8StringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Now send delete character followed by the newline.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(EndString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, EndString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, EndString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(EndStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, EndStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not match expected value.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read the message from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    ExpectedResult = strlen(Utf8StringFinal);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8StringFinal, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtUTF8Malformed4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine turns on UTF8 mode and then sends a malformed UTF-8 character,\r\n    followed by an erase character.\r\n\r\n    The observed behavior is to do a simple removal of all bytes beginning with\r\n    0b10 until it hits the beginning of the buffer. Apparently treating this as\r\n    an error, it leaves the data unchanged and does no echo.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    ssize_t ExpectedResult;\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[15];\r\n    int Result;\r\n    int SerialNumber;\r\n    const char* Utf8String = \"\\x80\\x80\\x80\\x80\";\r\n    const char* Utf8StringFinal = \"\\x80\\x80\\x80\\x80\\n\";\r\n    char EndString[3] = {0, '\\n', '\\0'};\r\n    const char* EndStringEcho = \"\\r\\n\";\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Set UTF-8 mode.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IUTF8));\r\n\r\n    //\r\n    // Fetch special characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    EndString[0] = ControlArray[VERASE];\r\n\r\n    //\r\n    // Write non-terminated string to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(Utf8String);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Utf8String, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, Utf8String);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, Utf8String, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Echo to master(FD:%d) does not match what was \"\r\n            \"written.\",\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Now send delete character followed by the newline.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = strlen(EndString);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, EndString, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, BytesReadWrite, EndString);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master.\r\n    //\r\n\r\n    ExpectedResult = strlen(EndStringEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    if (memcmp(ReadBuffer, EndStringEcho, ExpectedResult) != 0)\r\n    {\r\n        LxtLogError(\"Echo to master(FD:%d) does not match expected value.\", PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read the message from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    ExpectedResult = strlen(Utf8StringFinal);\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBuffer, Utf8StringFinal, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from subordinate(FD:%d) does not match what was \"\r\n            \"written by master(FD:%d).\",\r\n            PtsFd,\r\n            PtmFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtWriteNoSub1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    The PtWriteNoSubxxx validate the behavior of write on the master, when\r\n    there are no open handles to the subordinate; where 'xxx' is the\r\n    sub-test case as described below:\r\n    1. A handle to the subordinate was never opened\r\n       Expected Result: The write should succeed.\r\n    2. A handle to subordinate was opened and closed and then the write\r\n       on master was attempted.\r\n       Expected Result: Same as (1) above.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer;\r\n    int BytesReadWrite;\r\n    int PtmFd;\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n\r\n    //\r\n    // Open Master.\r\n    //\r\n\r\n    LxtCheckErrno((PtmFd = open(\"/dev/ptmx\", O_RDWR)));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n\r\n    //\r\n    // Now attempt a write on the master.\r\n    //\r\n\r\n    Buffer = 'a';\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &Buffer, 1));\r\n\r\n    //\r\n    // write should have written 1-byte.\r\n    //\r\n\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtWriteNoSub2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    See PtWriteNoSub1 for details.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer;\r\n    int BytesReadWrite;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Close the subordinate.\r\n    //\r\n\r\n    LxtClose(PtsFd);\r\n    LxtLogInfo(\"Subordinate closed\");\r\n\r\n    //\r\n    // Now attempt a write on the master.\r\n    //\r\n\r\n    Buffer = 'a';\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &Buffer, 1));\r\n\r\n    //\r\n    // write should have written 1-byte.\r\n    //\r\n\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtWriteToSubReadFromMaster1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the scenario where the subordinate does one write of\r\n    'n' bytes and the master does 'm' reads each of (n/m) bytes where n = m * x.\r\n    Each read should return (n/m) bytes and the data read should line up with\r\n    the data written.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Itr;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    size_t ReadSizes[50];\r\n    int Result;\r\n    int SerialNumber;\r\n    int TotalWriteSize;\r\n    size_t WriteSizes[2];\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Write 'n' bytes and read 'n' bytes. This is equivalent to a simple write\r\n    // read check.\r\n    //\r\n\r\n    WriteSizes[0] = 50;\r\n    ReadSizes[0] = 50;\r\n    LxtCheckErrno(WriteReadFdCommon(PtsFd, WriteSizes, 1, PtmFd, ReadSizes, 1));\r\n\r\n    LxtLogInfo(\"Case 1 passed\");\r\n\r\n    //\r\n    // Write 'n' bytes and do 2 reads each of 'n/2' bytes.\r\n    //\r\n\r\n    WriteSizes[0] = 50;\r\n    ReadSizes[0] = 25;\r\n    ReadSizes[1] = 25;\r\n    LxtCheckErrno(WriteReadFdCommon(PtsFd, WriteSizes, 1, PtmFd, ReadSizes, 2));\r\n\r\n    LxtLogInfo(\"Case 2 passed\");\r\n\r\n    //\r\n    // Write 'n' bytes and do 'n/2' reads each of 2 bytes.\r\n    //\r\n\r\n    WriteSizes[0] = 50;\r\n    for (Itr = 0; Itr < 25; Itr++)\r\n    {\r\n        ReadSizes[Itr] = 2;\r\n    }\r\n\r\n    LxtCheckErrno(WriteReadFdCommon(PtsFd, WriteSizes, 1, PtmFd, ReadSizes, 25));\r\n\r\n    LxtLogInfo(\"Case 3 passed\");\r\n\r\n    //\r\n    // Write 'n' bytes and do 'n' reads each of 1 byte.\r\n    //\r\n\r\n    WriteSizes[0] = 50;\r\n    for (Itr = 0; Itr < 50; Itr++)\r\n    {\r\n        ReadSizes[Itr] = 1;\r\n    }\r\n\r\n    LxtCheckErrno(WriteReadFdCommon(PtsFd, WriteSizes, 1, PtmFd, ReadSizes, 50));\r\n\r\n    LxtLogInfo(\"Case 4 passed\");\r\n\r\n    //\r\n    // Do 2 writes 'n' and 'm' bytes and do several reads totalling to a size\r\n    // of = (m+n) bytes.\r\n    //\r\n\r\n    WriteSizes[0] = 50;\r\n    WriteSizes[1] = 10;\r\n    ReadSizes[0] = 55;\r\n    ReadSizes[1] = 5;\r\n    LxtCheckErrno(WriteReadFdCommon(PtsFd, WriteSizes, 2, PtmFd, ReadSizes, 2));\r\n\r\n    LxtLogInfo(\"Case 5 passed\");\r\n    WriteSizes[0] = 50;\r\n    WriteSizes[1] = 10;\r\n    ReadSizes[0] = 40;\r\n    ReadSizes[1] = 5;\r\n    ReadSizes[2] = 10;\r\n    ReadSizes[3] = 3;\r\n    ReadSizes[4] = 2;\r\n    LxtCheckErrno(WriteReadFdCommon(PtsFd, WriteSizes, 2, PtmFd, ReadSizes, 5));\r\n\r\n    LxtLogInfo(\"Case 6 passed\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtWindowSizeCheck(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that window size can be read and set from both the\r\n    master and terminal, and that a change in size delivers a SIGWINCH signal.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int ChildStatus;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    struct winsize WindowSizeM;\r\n    struct winsize WindowSizeS;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGWINCH, SA_SIGINFO));\r\n\r\n        //\r\n        // Test the master endpoint.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCGWINSZ, &WindowSizeM));\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCSWINSZ, &WindowSizeM));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        WindowSizeM.ws_row -= 10;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCSWINSZ, &WindowSizeM));\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGWINCH));\r\n        LxtSignalResetReceived();\r\n\r\n        //\r\n        // Test the subordinate endpoint.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCGWINSZ, &WindowSizeS));\r\n        LxtCheckMemoryEqual(&WindowSizeM, &WindowSizeS, sizeof(WindowSizeM));\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCSWINSZ, &WindowSizeS));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        WindowSizeS.ws_row -= 10;\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCSWINSZ, &WindowSizeS));\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGWINCH));\r\n        LxtSignalResetReceived();\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCGWINSZ, &WindowSizeM));\r\n        LxtCheckMemoryEqual(&WindowSizeM, &WindowSizeS, sizeof(WindowSizeM));\r\n\r\n        Result = 0;\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &ChildStatus, 0)));\r\n        LxtCheckResult(WIFEXITED(ChildStatus) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(ChildStatus));\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid TestFun(void)\r\n{\r\n    char Buffer[50];\r\n    int Result;\r\n\r\n    LxtCheckErrno(GetRandomMessage(Buffer, sizeof(Buffer), FALSE));\r\n    DumpBuffer(Buffer, sizeof(Buffer));\r\n\r\nErrorExit:\r\n    return;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/dev_pt_2.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    dev_pt_2.c\r\n\r\nAbstract:\r\n\r\n    This file is a test for the Pseudo Terminals: /dev/ptmx, /dev/pts/<n>\r\n    devices.\r\n\r\n--*/\r\n\r\n#include \"dev_pt_common.h\"\r\n#include <sys/mount.h>\r\n#include <libmount/libmount.h>\r\n\r\n//\r\n// Globals.\r\n//\r\n\r\n#define LXT_NAME \"dev_pt_2\"\r\n#define PTS_START_CONTROL_CHAR \"^S\"\r\n#define PTS_STOP_CONTROL_CHAR \"^Q\"\r\n#define PTS_TEST_MNT \"/data/pts\"\r\n\r\ntypedef struct _PT_THREAD_PARAMETERS\r\n{\r\n    pid_t ForegroundId;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    pid_t SessionId;\r\n    PLXT_SYNCHRONIZATION_EVENT SynchronizationEventChild;\r\n    PLXT_SYNCHRONIZATION_EVENT SynchronizationEventParent;\r\n} PT_THREAD_PARAMETERS, *PPT_THREAD_PARAMETERS;\r\n\r\n//\r\n// Functions.\r\n//\r\n\r\nvoid* PtBackgroundDisassociateTty6Thread(PPT_THREAD_PARAMETERS ThreadParameters);\r\n\r\nint PtBackgroundSwitchToForegroundWorker(bool UseMasterEndpoint);\r\n\r\n//\r\n// Test cases.\r\n//\r\n\r\nLXT_VARIATION_HANDLER PtBackgroundBasic;\r\nLXT_VARIATION_HANDLER PtBackgroundBlockedSignals;\r\nLXT_VARIATION_HANDLER PtBackgroundDisassociateTty1;\r\nLXT_VARIATION_HANDLER PtBackgroundDisassociateTty2;\r\nLXT_VARIATION_HANDLER PtBackgroundDisassociateTty3;\r\nLXT_VARIATION_HANDLER PtBackgroundDisassociateTty4;\r\nLXT_VARIATION_HANDLER PtBackgroundDisassociateTty5;\r\nLXT_VARIATION_HANDLER PtBackgroundDisassociateTty6;\r\nLXT_VARIATION_HANDLER PtBackgroundSwitchToForeground;\r\nLXT_VARIATION_HANDLER PtBufferTerminalFill;\r\nLXT_VARIATION_HANDLER PtControllingTerminalForeground;\r\nLXT_VARIATION_HANDLER PtControllingTerminalForeground2;\r\nLXT_VARIATION_HANDLER PtControllingTerminalForeground3;\r\nLXT_VARIATION_HANDLER PtControllingTerminalForeground4;\r\nLXT_VARIATION_HANDLER PtControllingTerminalForeground5;\r\nLXT_VARIATION_HANDLER PtControllingTerminalForeground6;\r\nLXT_VARIATION_HANDLER PtControllingTerminalForeground7;\r\nLXT_VARIATION_HANDLER PtMountBasic;\r\nLXT_VARIATION_HANDLER PtPacketBasic1;\r\nLXT_VARIATION_HANDLER PtPacketBasic2;\r\nLXT_VARIATION_HANDLER PtPacketBasic3;\r\nLXT_VARIATION_HANDLER PtPacketBasic4;\r\nLXT_VARIATION_HANDLER PtPacketToggleMode1;\r\nLXT_VARIATION_HANDLER PtPacketToggleMode2;\r\nLXT_VARIATION_HANDLER PtPacketToggleMode3;\r\nLXT_VARIATION_HANDLER PtPacketToggleMode4;\r\nLXT_VARIATION_HANDLER PtPacketToggleMode5;\r\nLXT_VARIATION_HANDLER PtPacketToggleMode6;\r\nLXT_VARIATION_HANDLER PtPacketToggleMode7;\r\nLXT_VARIATION_HANDLER PtPacketFlushRead1;\r\nLXT_VARIATION_HANDLER PtPacketFlushRead2;\r\nLXT_VARIATION_HANDLER PtPacketFlushRead3;\r\nLXT_VARIATION_HANDLER PtPacketFlushWrite1;\r\nLXT_VARIATION_HANDLER PtPacketFlushWrite2;\r\nLXT_VARIATION_HANDLER PtPacketFlushReadWrite1;\r\nLXT_VARIATION_HANDLER PtPacketFlushReadWrite2;\r\nLXT_VARIATION_HANDLER PtPacketFlushReadWrite3;\r\nLXT_VARIATION_HANDLER PtPacketFlushReadWrite4;\r\nLXT_VARIATION_HANDLER PtPacketFlushReadWrite5;\r\nLXT_VARIATION_HANDLER PtPacketHangup;\r\nLXT_VARIATION_HANDLER PtPacketControlCharCheck1;\r\nLXT_VARIATION_HANDLER PtPacketControlCharCheck2;\r\nLXT_VARIATION_HANDLER PtPacketControlCharCheck3;\r\nLXT_VARIATION_HANDLER PtPacketToggleWithControlByte;\r\nLXT_VARIATION_HANDLER PtSessionBasicMaster;\r\nLXT_VARIATION_HANDLER PtSuspendOutput1;\r\nLXT_VARIATION_HANDLER PtSuspendOutput2;\r\nLXT_VARIATION_HANDLER PtSuspendOutput3;\r\nLXT_VARIATION_HANDLER PtSuspendOutput4;\r\nLXT_VARIATION_HANDLER PtSuspendOutput5;\r\nLXT_VARIATION_HANDLER PtSuspendOutput6;\r\nLXT_VARIATION_HANDLER PtSuspendOutput7;\r\nLXT_VARIATION_HANDLER PtSuspendOutput8;\r\nLXT_VARIATION_HANDLER PtSuspendOutput9;\r\nLXT_VARIATION_HANDLER PtSuspendOutput10;\r\nLXT_VARIATION_HANDLER PtSuspendOutput11;\r\nLXT_VARIATION_HANDLER PtSuspendOutput12;\r\nLXT_VARIATION_HANDLER PtSuspendOutput13;\r\nLXT_VARIATION_HANDLER PtSuspendOutput14;\r\nLXT_VARIATION_HANDLER PtSuspendOutput15;\r\nLXT_VARIATION_HANDLER PtSuspendOutput16;\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Controlling terminal foreground tests\", PtControllingTerminalForeground},\r\n    {\"Controlling terminal foreground tests (part 2)\", PtControllingTerminalForeground2},\r\n    {\"Controlling terminal foreground tests (part 3)\", PtControllingTerminalForeground3},\r\n    {\"Controlling terminal foreground tests (part 4)\", PtControllingTerminalForeground4},\r\n    {\"Controlling terminal foreground tests (part 5)\", PtControllingTerminalForeground5},\r\n    {\"Controlling terminal foreground tests (part 6)\", PtControllingTerminalForeground6},\r\n    {\"Controlling terminal foreground tests (part 7)\", PtControllingTerminalForeground7},\r\n    {\"Basic background IO\", PtBackgroundBasic},\r\n    {\"Background IO with signals blocked\", PtBackgroundBlockedSignals},\r\n    {\"Disassociate from a controlling terminal\", PtBackgroundDisassociateTty1},\r\n    {\"Disassociate from a controlling terminal (part 2)\", PtBackgroundDisassociateTty2},\r\n    {\"Disassociate from a controlling terminal (part 3)\", PtBackgroundDisassociateTty3},\r\n    {\"Disassociate from a controlling terminal (part 4)\", PtBackgroundDisassociateTty4},\r\n    {\"Disassociate from a controlling terminal (part 5)\", PtBackgroundDisassociateTty5},\r\n    {\"Disassociate from a controlling terminal (part 6)\", PtBackgroundDisassociateTty6},\r\n    {\"Background switching to foreground\", PtBackgroundSwitchToForeground},\r\n\r\n    //\r\n    // TODO_LX: Implement master endpoint that can be a controlling terminal.\r\n    //\r\n    //{ \"Session with basic controlling terminal IO (master endpoint)\", PtSessionBasicMaster },\r\n    //\r\n\r\n    {\"PT terminal buffer fill\", PtBufferTerminalFill},\r\n    {\"PT basic mount verification\", PtMountBasic},\r\n    {\"PT Basic packet-mode\", PtPacketBasic1},\r\n    {\"PT Basic packet-mode (part 2)\", PtPacketBasic2},\r\n    {\"PT Basic packet-mode (part 3)\", PtPacketBasic3},\r\n    {\"PT Basic packet-mode (part 4)\", PtPacketBasic4},\r\n    {\"PT toggle packet-mode\", PtPacketToggleMode1},\r\n    {\"PT toggle packet-mode (part 2)\", PtPacketToggleMode2},\r\n    {\"PT toggle packet-mode (part 3)\", PtPacketToggleMode3},\r\n    {\"PT toggle packet-mode (part 4)\", PtPacketToggleMode4},\r\n    {\"PT toggle packet-mode (part 5)\", PtPacketToggleMode5},\r\n    {\"PT toggle packet-mode (part 6)\", PtPacketToggleMode6},\r\n    {\"PT toggle packet-mode (part 7)\", PtPacketToggleMode7},\r\n    {\"PT packet-mode flush read queue\", PtPacketFlushRead1},\r\n    {\"PT packet-mode flush read queue (part 2)\", PtPacketFlushRead2},\r\n    {\"PT packet-mode flush read queue (part 3)\", PtPacketFlushRead3},\r\n    {\"PT packet-mode flush write queue\", PtPacketFlushWrite1},\r\n    {\"PT packet-mode flush write queue (part 2)\", PtPacketFlushWrite2},\r\n    {\"PT packet-mode flush read/write queue\", PtPacketFlushReadWrite1},\r\n    {\"PT packet-mode flush read/write queue (part 2)\", PtPacketFlushReadWrite2},\r\n    {\"PT packet-mode flush read/write queue (part 3)\", PtPacketFlushReadWrite3},\r\n    {\"PT packet-mode flush read/write queue (part 4)\", PtPacketFlushReadWrite4},\r\n    {\"PT packet-mode flush read/write queue (part 5)\", PtPacketFlushReadWrite5},\r\n    {\"PT packet-mode hangup\", PtPacketHangup},\r\n    {\"PT packet-mode Ctrl-C\", PtPacketControlCharCheck1},\r\n    {\"PT packet-mode START/STOP assignment\", PtPacketControlCharCheck2},\r\n    {\"PT packet-mode START/STOP\", PtPacketControlCharCheck3},\r\n    {\"PT packet-mode toggle with control byte\", PtPacketToggleWithControlByte},\r\n    {\"PT suspend output\", PtSuspendOutput1},\r\n    {\"PT suspend output (part 2)\", PtSuspendOutput2},\r\n    {\"PT suspend output (part 3)\", PtSuspendOutput3},\r\n    {\"PT suspend output (part 4)\", PtSuspendOutput4},\r\n    {\"PT suspend output (part 5)\", PtSuspendOutput5},\r\n    {\"PT suspend output (part 6)\", PtSuspendOutput6},\r\n    {\"PT suspend output (part 7)\", PtSuspendOutput7},\r\n    {\"PT suspend output (part 8)\", PtSuspendOutput8},\r\n    {\"PT suspend output (part 9)\", PtSuspendOutput9},\r\n    {\"PT suspend output (part 10)\", PtSuspendOutput10},\r\n    {\"PT suspend output (part 11)\", PtSuspendOutput11},\r\n    {\"PT suspend output (part 12)\", PtSuspendOutput12},\r\n    {\"PT suspend output (part 13)\", PtSuspendOutput13},\r\n    {\"PT suspend output (part 14)\", PtSuspendOutput14},\r\n    {\"PT suspend output (part 15)\", PtSuspendOutput15},\r\n    {\"PT suspend output (part 16)\", PtSuspendOutput16},\r\n};\r\n\r\nint DevPtTwoTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine main entry point for the pty(2) test.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckErrno(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n    LxtCheckErrno(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY();\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint PtBackgroundBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs basic IO checks from a background process.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t ForegroundId;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPtyBackground(&PtmFd, &PtsFd, &ForegroundId));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckResult(SessionId = getsid(0));\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtsFd));\r\n        LxtCheckEqual(SessionId, TerminalSessionId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(ForegroundId, TerminalForegroundId, \"%d\");\r\n        LxtCheckErrnoFailure(RawInit(PtsFd), EINTR);\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGTTOU));\r\n        LxtSignalResetReceived();\r\n        LxtCheckErrno(SimpleReadWriteCheckEx(PtmFd, PtsFd, SimpleReadWriteBackgroundSignalNoStop));\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGTTIN));\r\n        LxtSignalResetReceived();\r\n        LxtCheckErrno(tcflush(PtmFd, TCIFLUSH));\r\n\r\n        //\r\n        // Temporarily block SIGTTOU in order to enable TOSTOP\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(LxtSignalBlock(SIGTTOU));\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LxtCheckErrnoZeroSuccess(LxtSignalUnblock(SIGTTOU));\r\n\r\n        //\r\n        // Try again with TOSTOP enabled\r\n        //\r\n\r\n        LxtLogInfo(\"Check with TOSTOP flag enabled\");\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckErrno(SimpleReadWriteCheckEx(PtmFd, PtsFd, SimpleReadWriteBackgroundSignal));\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGTTIN));\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGTTOU));\r\n        LxtSignalResetReceived();\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtBackgroundBlockedSignals(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs IO checks from a background thread that has blocked\r\n    SIGTTIN and SIGTTOU.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t ForegroundId;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int Status;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPtyBackground(&PtmFd, &PtsFd, &ForegroundId));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckErrnoZeroSuccess(LxtSignalBlock(SIGTTIN));\r\n        LxtCheckErrnoZeroSuccess(LxtSignalBlock(SIGTTOU));\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckErrno(SimpleReadWriteCheckEx(PtmFd, PtsFd, SimpleReadWriteBackgroundNoSignal));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtBackgroundSwitchToForeground(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine moves from a background process to a foreground process, with\r\n    sanity IO checks.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(PtBackgroundSwitchToForegroundWorker(false));\r\n\r\n    //\r\n    // TODO_LX: Implement master endpoint that can be a controlling terminal.\r\n    //\r\n\r\n    // LxtCheckErrno(PtBackgroundSwitchToForegroundWorker(true));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint PtBackgroundSwitchToForegroundWorker(bool UseMasterEndpoint)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine moves from a background process to a foreground process, with\r\n    sanity IO checks.\r\n\r\nArguments:\r\n\r\n    UseMasterEndpoint - Supplies the flag indicating whether the call should be\r\n        made on the master or the subordinate endpoints.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t ForegroundId;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPtyBackground(&PtmFd, &PtsFd, &ForegroundId));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckErrnoFailure(tcsetpgrp((UseMasterEndpoint) ? PtmFd : PtsFd, getpgid(0)), EINTR);\r\n\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGTTOU));\r\n        LxtSignalResetReceived();\r\n\r\n        //\r\n        // Temporarily block SIGTTOU to force process to foreground.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(LxtSignalBlock(SIGTTOU));\r\n        LxtCheckErrno(tcsetpgrp((UseMasterEndpoint) ? PtmFd : PtsFd, getpgid(0)));\r\n\r\n        LxtCheckErrnoZeroSuccess(LxtSignalUnblock(SIGTTOU));\r\n\r\n        //\r\n        // Verify foreground IO behavior.\r\n        //\r\n\r\n        LxtCheckResult(SessionId = getsid(0));\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtsFd));\r\n        LxtCheckEqual(SessionId, TerminalSessionId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckNotEqual(ForegroundId, TerminalForegroundId, \"%d\");\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LxtSignalResetReceived();\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtBackgroundDisassociateTty1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine removes the controlling terminal from a background thread and\r\n    then tests ioctl behavior.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    tcflag_t ControlFlags;\r\n    pid_t ForegroundId;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n    struct winsize WindowSizeM;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPtyBackground(&PtmFd, &PtsFd, &ForegroundId));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n        LxtCheckResult(SessionId = getsid(0));\r\n\r\n        //\r\n        // Disconnect the controlling terminal.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        //\r\n        // Test various ioctl behavior on the subordinate endpoint.\r\n        //\r\n\r\n        LxtCheckErrno(TerminalSettingsGetControlFlags(PtsFd, &ControlFlags));\r\n        LxtCheckErrno(TerminalSettingsSetControlFlags(PtsFd, ControlFlags));\r\n        LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcsetpgrp(PtsFd, getpgid(0)), ENOTTY);\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCGWINSZ, &WindowSizeM));\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCSWINSZ, &WindowSizeM));\r\n        LxtCheckErrno(tcflow(PtsFd, TCOOFF));\r\n        LxtCheckErrno(tcflow(PtsFd, TCOON));\r\n        LxtCheckErrno(tcflow(PtsFd, TCIOFF));\r\n        LxtCheckErrno(tcflow(PtsFd, TCION));\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCSCTTY, (char*)NULL), EPERM);\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n        LxtCheckErrno(tcdrain(PtsFd));\r\n        LxtCheckErrno(tcflush(PtsFd, TCIOFLUSH));\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCSTI, \"x\"));\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCSTI, (char*)NULL), EFAULT);\r\n\r\n        //\r\n        // Test various ioctl behavior on the master endpoint.\r\n        //\r\n\r\n        LxtCheckErrno(TerminalSettingsGetControlFlags(PtmFd, &ControlFlags));\r\n        LxtCheckErrno(TerminalSettingsSetControlFlags(PtmFd, ControlFlags));\r\n\r\n        //\r\n        // On Linux, The master endpoint returns the foreground/session state.\r\n        //\r\n\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n        LxtCheckErrnoFailure(tcsetpgrp(PtmFd, getpgid(0)), ENOTTY);\r\n\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n        LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCGWINSZ, &WindowSizeM));\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCSWINSZ, &WindowSizeM));\r\n        LxtCheckErrno(tcflow(PtmFd, TCOOFF));\r\n        LxtCheckErrno(tcflow(PtmFd, TCOON));\r\n        LxtCheckErrno(tcflow(PtmFd, TCIOFF));\r\n        LxtCheckErrno(tcflow(PtmFd, TCION));\r\n        LxtCheckErrnoFailure(ioctl(PtmFd, TIOCSCTTY, (char*)NULL), EPERM);\r\n        LxtCheckErrnoFailure(ioctl(PtmFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n        LxtCheckErrno(tcdrain(PtmFd));\r\n        LxtCheckErrno(tcflush(PtmFd, TCIOFLUSH));\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCSTI, \"x\"));\r\n        LxtCheckErrnoFailure(ioctl(PtmFd, TIOCSTI, (char*)NULL), EFAULT);\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n\r\n    return Result;\r\n}\r\n\r\nint PtBackgroundDisassociateTty2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine removes the controlling terminal from a background thread.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    BOOLEAN EndChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = TRUE;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPtyBackground(&PtmFd, &PtsFd, &ForegroundId));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n        LxtCheckResult(SessionId = getsid(0));\r\n\r\n        //\r\n        // Allow the other thread to try to disassociate the terminal, and wait\r\n        // for that to complete.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        //\r\n        // Check session and foreground process group for both endpoints of\r\n        // the psuedo-terminal.\r\n        //\r\n\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtsFd));\r\n        LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n        LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n\r\n        //\r\n        // Disconnect the controlling terminal.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        //\r\n        // Trying to disconnect again should fail.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n\r\n        //\r\n        // The terminal is no longer associated, so it is expected to fail the\r\n        // commands to retrieve session and foreground process group.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(TerminalSessionId = tcgetsid(PtsFd), ENOTTY);\r\n        LxtCheckErrnoFailure(TerminalForegroundId = tcgetpgrp(PtsFd), ENOTTY);\r\n\r\n        //\r\n        // On Linux, The master endpoint returns the foreground/session state.\r\n        //\r\n\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n        LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n\r\n        //\r\n        // Do a simple IO check.\r\n        //\r\n\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Try to disassociate terminal from another session.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &Status, 0)));\r\n        EndChildPidSynchronization = FALSE;\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        //\r\n        // Check status of master endpoint after session is gone.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrno(tcgetpgrp(PtmFd));\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (EndChildPidSynchronization != FALSE)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtBackgroundDisassociateTty3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine removes the controlling terminal from a foreground thread and\r\n    checks the behavior on both foreground and background threads.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    BOOLEAN EndChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = TRUE;\r\n    GrandChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n        LxtCheckResult(ForegroundId = getpid());\r\n        LxtCheckResult(SessionId = getsid(0));\r\n\r\n        //\r\n        // Allow the other thread to try to disassociate the terminal, and wait\r\n        // for that to complete.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        //\r\n        // Fork again to create a foreground and background thread.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid);\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n\r\n            //\r\n            // Move to the background.\r\n            //\r\n\r\n            LxtLogInfo(\"Moving thread %d to the background.\", getpid());\r\n            LxtCheckErrno(setpgid(0, 0));\r\n\r\n            //\r\n            // Check session and foreground process group for both endpoints of\r\n            // the psuedo-terminal.\r\n            //\r\n\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(PtsFd));\r\n            LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n            LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtsFd));\r\n            LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n            LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n\r\n            //\r\n            // Signal the foreground thread to disconnect the controlling\r\n            // terminal, and wait for the signal that it has completed.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n\r\n            //\r\n            // On Linux, The master endpoint returns foreground/session state,\r\n            // but instead of failing the foreground group query will just\r\n            // return 0.\r\n            //\r\n\r\n            LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n            LxtCheckEqual(TerminalForegroundId, 0, \"%d\");\r\n\r\n            //\r\n            // Do a simple IO test.\r\n            //\r\n\r\n            LxtCheckErrno(RawInit(PtsFd));\r\n            LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n        }\r\n        else\r\n        {\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // TODO_LX: Support SIGCONT.\r\n            //\r\n            // LxtCheckResult(LxtSignalCheckReceived(SIGCONT));\r\n            //\r\n\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            LxtSignalResetReceived();\r\n            LxtCheckErrnoFailure(ioctl(PtsFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n\r\n            //\r\n            // The terminal is no longer associated, so it is expected to fail the\r\n            // commands to retrieve session and foreground process group.\r\n            //\r\n\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n\r\n            //\r\n            // On Linux, The master endpoint returns foreground/session state,\r\n            // but instead of failing the foreground group query will just\r\n            // return 0.\r\n            //\r\n\r\n            LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n            LxtCheckEqual(TerminalForegroundId, 0, \"%d\");\r\n\r\n            //\r\n            // Wait for other thread to finish its IO test, then do an IO test\r\n            // here.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckErrno(RawInit(PtsFd));\r\n            LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n        }\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Try to disassociate terminal from another session.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait for the child here in order to run more tests after the session\r\n        // has been destroyed.\r\n        //\r\n\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &Status, 0)));\r\n        EndChildPidSynchronization = FALSE;\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        //\r\n        // Check status of master endpoint after session is gone.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrno(tcgetpgrp(PtmFd));\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    if (EndChildPidSynchronization != FALSE)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtBackgroundDisassociateTty4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine removes the controlling terminal from a background thread and\r\n    checks the behavior on both foreground and background threads.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    BOOLEAN EndChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = TRUE;\r\n    GrandChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n        LxtCheckResult(ForegroundId = getpid());\r\n        LxtCheckResult(SessionId = getsid(0));\r\n\r\n        //\r\n        // Allow the other thread to try to disassociate the terminal, and wait\r\n        // for that to complete.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        //\r\n        // Fork again to create a foreground and background thread.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid);\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            EndChildPidSynchronization = FALSE;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n\r\n            //\r\n            // Move to the background.\r\n            //\r\n\r\n            LxtLogInfo(\"Moving thread %d to the background.\", getpid());\r\n            LxtCheckErrno(setpgid(0, 0));\r\n\r\n            //\r\n            // Check session and foreground process group for both endpoints of\r\n            // the psuedo-terminal.\r\n            //\r\n\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(PtsFd));\r\n            LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n            LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtsFd));\r\n            LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n            LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n\r\n            //\r\n            // Disconnect the controlling terminal.\r\n            //\r\n\r\n            LxtLogInfo(\r\n                \"Disconnecting controlling terminal from background \"\r\n                \"thread %d.\",\r\n                getpid());\r\n\r\n            LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n            //\r\n            // Check session and foreground process group again.\r\n            //\r\n\r\n            LxtLogInfo(\"Checking ioctls from thread %d after disconnect.\", getpid());\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n\r\n            //\r\n            // On Linux, The master endpoint returns the foreground/session\r\n            // state.\r\n            //\r\n\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n            LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n            LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n\r\n            //\r\n            // Do a simple IO test.\r\n            //\r\n\r\n            LxtCheckErrno(RawInit(PtsFd));\r\n            LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n        }\r\n        else\r\n        {\r\n\r\n            //\r\n            // Wait for the background thread to disconnect from the\r\n            // controlling terminal.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n            //\r\n            // Check session and foreground process group for both endpoints of\r\n            // the psuedo-terminal.\r\n            //\r\n\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(PtsFd));\r\n            LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n            LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtsFd));\r\n            LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n            LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n\r\n            //\r\n            // Wait for other thread to finish its IO test, then do an IO test\r\n            // here.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckErrno(RawInit(PtsFd));\r\n            LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n        }\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Try to disassociate terminal from another session.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait for the child here in order to run more tests after the session\r\n        // has been destroyed.\r\n        //\r\n\r\n        LxtLogInfo(\"Waiting for child thread %d to exit.\", ChildPid);\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &Status, 0)));\r\n        EndChildPidSynchronization = FALSE;\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        //\r\n        // Check status of master endpoint after session is gone.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrno(tcgetpgrp(PtmFd));\r\n    }\r\n\r\nErrorExit:\r\n    LxtLogInfo(\"Exiting thread %d with Result = %d.\", getpid(), Result);\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    if (EndChildPidSynchronization != FALSE)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtBackgroundDisassociateTty5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine removes the controlling terminal from a background thread,\r\n    switches to a new session and establishes a new controlling terminal.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    pid_t GrandChildSessionId;\r\n    int PtmFd;\r\n    int Ptm2Fd;\r\n    int PtsFd;\r\n    int Pts2Fd;\r\n    int Result;\r\n    int SerialNumber;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n    struct winsize WindowSizeM;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    GrandChildPid = -1;\r\n    PtmFd = -1;\r\n    Ptm2Fd = -1;\r\n    PtsFd = -1;\r\n    Pts2Fd = -1;\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPtyBackground(&PtmFd, &PtsFd, &ForegroundId));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n        LxtCheckResult(ForegroundId = getpid());\r\n        LxtCheckResult(SessionId = getsid(0));\r\n\r\n        //\r\n        // Disconnect the controlling terminal.\r\n        //\r\n\r\n        LxtLogInfo(\r\n            \"Disconnecting controlling terminal from background \"\r\n            \"thread %d.\",\r\n            getpid());\r\n\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        //\r\n        // Create a second set of endpoints.\r\n        //\r\n\r\n        LxtCheckErrno(OpenMasterSubordinate(&Ptm2Fd, &Pts2Fd, NULL, &SerialNumber));\r\n        LxtLogInfo(\"Second master opened at FD:%d\", Ptm2Fd);\r\n        LxtLogInfo(\"Second subordinate Serial Number: %d\", SerialNumber);\r\n        LxtLogInfo(\"Second subordinate opened at FD:%d\", Pts2Fd);\r\n\r\n        //\r\n        // Fork again to test terminal behavior on new thread after disconnect.\r\n        //\r\n\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n\r\n            //\r\n            // Check that the new thread is still disconnected from the\r\n            // original endpoints, and not associated with the new endpoints.\r\n            //\r\n\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetsid(Pts2Fd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetpgrp(Pts2Fd), ENOTTY);\r\n\r\n            //\r\n            // Try to add a controlling terminal before creating a new session.\r\n            //\r\n\r\n            LxtCheckErrnoFailure(ioctl(Pts2Fd, TIOCSCTTY, (char*)NULL), EPERM);\r\n            LxtCheckErrnoFailure(ioctl(PtsFd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n\r\n            //\r\n            // Create a new session.\r\n            //\r\n\r\n            LxtCheckErrno(GrandChildSessionId = setsid());\r\n\r\n            //\r\n            // Check that the thread, now inside a new session is still\r\n            // disconnected from any endpoints.\r\n            //\r\n\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetsid(Pts2Fd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetpgrp(Pts2Fd), ENOTTY);\r\n\r\n            //\r\n            // Try to add a controlling terminal inside the new session.\r\n            //\r\n\r\n            LxtCheckErrno(ioctl(Pts2Fd, TIOCSCTTY, (char*)NULL));\r\n            LxtCheckResult(ForegroundId = getpid());\r\n\r\n            //\r\n            // Check session and foreground process group again.\r\n            //\r\n\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(Pts2Fd));\r\n            LxtCheckEqual(GrandChildSessionId, TerminalSessionId, \"%d\");\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(Ptm2Fd));\r\n            LxtCheckEqual(GrandChildSessionId, TerminalSessionId, \"%d\");\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(Pts2Fd));\r\n            LxtCheckEqual(ForegroundId, TerminalForegroundId, \"%d\");\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(Ptm2Fd));\r\n            LxtCheckEqual(ForegroundId, TerminalForegroundId, \"%d\");\r\n            LxtCheckErrno(RawInit(Pts2Fd));\r\n            LxtCheckErrno(SimpleReadWriteCheck(Ptm2Fd, Pts2Fd));\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n        }\r\n        else\r\n        {\r\n\r\n            //\r\n            // Try cross-session access to the endpoints.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckErrnoFailure(tcgetsid(Pts2Fd), ENOTTY);\r\n            LxtCheckErrno(TerminalSessionId = tcgetsid(Ptm2Fd));\r\n            LxtCheckNotEqual(SessionId, TerminalSessionId, \"%d\");\r\n            LxtCheckErrnoFailure(tcgetpgrp(Pts2Fd), ENOTTY);\r\n            LxtCheckErrno(TerminalForegroundId = tcgetpgrp(Ptm2Fd));\r\n            LxtCheckNotEqual(ForegroundId, TerminalForegroundId, \"%d\");\r\n            LxtCheckErrnoFailure(tcsetpgrp(Pts2Fd, getpgid(0)), ENOTTY);\r\n            LxtCheckErrno(ioctl(Pts2Fd, TIOCGWINSZ, &WindowSizeM));\r\n            LxtCheckErrno(ioctl(Ptm2Fd, TIOCGWINSZ, &WindowSizeM));\r\n            LxtCheckErrno(ioctl(Pts2Fd, TIOCSWINSZ, &WindowSizeM));\r\n            LxtCheckErrno(ioctl(Ptm2Fd, TIOCSWINSZ, &WindowSizeM));\r\n            LxtCheckErrno(tcflow(Pts2Fd, TCOOFF));\r\n            LxtCheckErrno(tcflow(Ptm2Fd, TCOOFF));\r\n            LxtCheckErrno(tcflow(Pts2Fd, TCOON));\r\n            LxtCheckErrno(tcflow(Ptm2Fd, TCOON));\r\n            LxtCheckErrno(tcflow(Pts2Fd, TCIOFF));\r\n            LxtCheckErrno(tcflow(Ptm2Fd, TCIOFF));\r\n            LxtCheckErrno(tcflow(Pts2Fd, TCION));\r\n            LxtCheckErrno(tcflow(Ptm2Fd, TCION));\r\n            LxtCheckErrnoFailure(ioctl(Pts2Fd, TIOCSCTTY, (char*)NULL), EPERM);\r\n            LxtCheckErrnoFailure(ioctl(Ptm2Fd, TIOCSCTTY, (char*)NULL), EPERM);\r\n            LxtCheckErrnoFailure(ioctl(Pts2Fd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n            LxtCheckErrnoFailure(ioctl(Ptm2Fd, TIOCNOTTY, (char*)NULL), ENOTTY);\r\n            LxtCheckErrno(tcdrain(Pts2Fd));\r\n            LxtCheckErrno(tcdrain(Ptm2Fd));\r\n            LxtCheckErrno(tcflush(Pts2Fd, TCIOFLUSH));\r\n            LxtCheckErrno(tcflush(Ptm2Fd, TCIOFLUSH));\r\n\r\n            //\r\n            // Test IO.\r\n            //\r\n\r\n            LxtCheckErrno(RawInit(Pts2Fd));\r\n            LxtCheckErrno(SimpleReadWriteCheck(Ptm2Fd, Pts2Fd));\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n            //\r\n            // Test TIOCSTI.\r\n            //\r\n\r\n            LxtCheckErrno(ioctl(Pts2Fd, TIOCSTI, \"x\"));\r\n            LxtCheckErrno(ioctl(Ptm2Fd, TIOCSTI, \"x\"));\r\n            LxtCheckErrnoFailure(ioctl(Pts2Fd, TIOCSTI, (char*)NULL), EFAULT);\r\n            LxtCheckErrnoFailure(ioctl(Ptm2Fd, TIOCSTI, (char*)NULL), EFAULT);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    LxtLogInfo(\"Exiting thread %d with Result = %d.\", getpid(), Result);\r\n    if (Ptm2Fd != -1)\r\n    {\r\n        close(Ptm2Fd);\r\n    }\r\n\r\n    if (Pts2Fd != -1)\r\n    {\r\n        close(Pts2Fd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    if (GrandChildPid != 0)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtBackgroundDisassociateTty6(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine removes the controlling terminal from another thread created\r\n    with CLONE_THREAD and checks the behavior on all threads.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    LXT_CLONE_ARGS CloneArgs;\r\n    pid_t ForegroundId;\r\n    pthread_t GrandChildTid;\r\n    void* GrandChildResult;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildTid);\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n    PT_THREAD_PARAMETERS ThreadParameters;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    GrandChildTid = 0;\r\n    memset(&CloneArgs, 0, sizeof(CloneArgs));\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildTid);\r\n\r\n    LxtCheckErrno(ChildPid = ForkPtyBackground(&PtmFd, &PtsFd, &ForegroundId));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n        LxtCheckResult(SessionId = getsid(0));\r\n\r\n        //\r\n        // Clone a new thread.\r\n        //\r\n\r\n        memset(&ThreadParameters, 0, sizeof(ThreadParameters));\r\n        ThreadParameters.ForegroundId = ForegroundId;\r\n        ThreadParameters.PtmFd = PtmFd;\r\n        ThreadParameters.PtsFd = PtsFd;\r\n        ThreadParameters.SessionId = SessionId;\r\n        ThreadParameters.SynchronizationEventChild = LxtSyncGrandChildTidChild;\r\n        ThreadParameters.SynchronizationEventParent = LxtSyncGrandChildTidParent;\r\n        LxtCheckErrno(pthread_create(&GrandChildTid, NULL, (void* (*)(void*))PtBackgroundDisassociateTty6Thread, &ThreadParameters));\r\n\r\n        //\r\n        // Wait for the other thread to disconnect from the\r\n        // controlling terminal.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildTid);\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n\r\n        //\r\n        // On Linux, The master endpoint returns the foreground/session\r\n        // state.\r\n        //\r\n\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n        LxtCheckEqual(TerminalSessionId, SessionId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(TerminalForegroundId, ForegroundId, \"%d\");\r\n\r\n        //\r\n        // Wait for other thread to finish its IO test, then do an IO test\r\n        // here.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildTid);\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Wait for the child here in order to run more tests after the session\r\n        // has been destroyed.\r\n        //\r\n\r\n        LxtLogInfo(\"Waiting for child thread %d to exit.\", ChildPid);\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &Status, 0)));\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        //\r\n        // Check status of master endpoint after session is gone.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrno(tcgetpgrp(PtmFd));\r\n    }\r\n\r\nErrorExit:\r\n    LxtLogInfo(\"Exiting thread %d with Result = %d.\", getpid(), Result);\r\n    if (GrandChildTid > 0)\r\n    {\r\n        if (Result < 0)\r\n        {\r\n            LxtSynchronizationEventFail(LxtSyncGrandChildTidChild);\r\n        }\r\n\r\n        Result = pthread_join(GrandChildTid, &GrandChildResult);\r\n        if (Result != 0)\r\n        {\r\n            LxtLogError(\"Failed pthread_join with error %d\", Result);\r\n        }\r\n        else\r\n        {\r\n\r\n            Result = (int)(long)GrandChildResult;\r\n        }\r\n\r\n        exit(Result);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid* PtBackgroundDisassociateTty6Thread(PPT_THREAD_PARAMETERS ThreadParameters)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine is called on a new thread from PtBackgroundDisassociateTty6.\r\n\r\nArguments:\r\n\r\n    ThreadParameters - Supplies thread parameters.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n\r\n    LxtCheckResult(LxtSignalInitializeThread());\r\n    LxtSignalSetAllowMultiple(TRUE);\r\n    LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n    LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n    LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n    LxtCheckResult(LxtSignalSetupHandler(SIGCONT, SA_SIGINFO));\r\n\r\n    //\r\n    // Check session and foreground process group for both endpoints of\r\n    // the psuedo-terminal.\r\n    //\r\n\r\n    LxtCheckErrno(TerminalSessionId = tcgetsid(ThreadParameters->PtsFd));\r\n    LxtCheckEqual(TerminalSessionId, ThreadParameters->SessionId, \"%d\");\r\n    LxtCheckErrno(TerminalSessionId = tcgetsid(ThreadParameters->PtmFd));\r\n    LxtCheckEqual(TerminalSessionId, ThreadParameters->SessionId, \"%d\");\r\n    LxtCheckErrno(TerminalForegroundId = tcgetpgrp(ThreadParameters->PtsFd));\r\n    LxtCheckEqual(TerminalForegroundId, ThreadParameters->ForegroundId, \"%d\");\r\n    LxtCheckErrno(TerminalForegroundId = tcgetpgrp(ThreadParameters->PtmFd));\r\n    LxtCheckEqual(TerminalForegroundId, ThreadParameters->ForegroundId, \"%d\");\r\n\r\n    //\r\n    // Disconnect the controlling terminal.\r\n    //\r\n\r\n    LxtLogInfo(\"Disconnecting controlling terminal from thread %d.\", gettid());\r\n\r\n    LxtCheckErrno(ioctl(ThreadParameters->PtsFd, TIOCNOTTY, (char*)NULL));\r\n    LxtCheckResult(LxtSignalCheckNoSignal());\r\n    LXT_SYNCHRONIZATION_POINT_SYNCVARS(TRUE, ThreadParameters->SynchronizationEventParent, ThreadParameters->SynchronizationEventChild);\r\n\r\n    //\r\n    // Check session and foreground process group again.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking ioctls from thread %d after disconnect.\", gettid());\r\n    LxtCheckErrnoFailure(tcgetsid(ThreadParameters->PtsFd), ENOTTY);\r\n    LxtCheckErrnoFailure(tcgetpgrp(ThreadParameters->PtsFd), ENOTTY);\r\n\r\n    //\r\n    // On Linux, The master endpoint returns the foreground/session\r\n    // state.\r\n    //\r\n\r\n    LxtCheckErrno(TerminalSessionId = tcgetsid(ThreadParameters->PtmFd));\r\n    LxtCheckEqual(TerminalSessionId, ThreadParameters->SessionId, \"%d\");\r\n    LxtCheckErrno(TerminalForegroundId = tcgetpgrp(ThreadParameters->PtmFd));\r\n    LxtCheckEqual(TerminalForegroundId, ThreadParameters->ForegroundId, \"%d\");\r\n\r\n    //\r\n    // Do a simple IO test.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(ThreadParameters->PtsFd));\r\n    LxtCheckErrno(SimpleReadWriteCheck(ThreadParameters->PtmFd, ThreadParameters->PtsFd));\r\n\r\n    LxtCheckResult(LxtSignalCheckNoSignal());\r\n    LXT_SYNCHRONIZATION_POINT_SYNCVARS(TRUE, ThreadParameters->SynchronizationEventParent, ThreadParameters->SynchronizationEventChild);\r\n\r\nErrorExit:\r\n    LxtLogInfo(\"Exiting thread %d with Result = %d\", gettid(), Result);\r\n    if (Result < 0)\r\n    {\r\n        LxtSynchronizationEventFail(ThreadParameters->SynchronizationEventParent);\r\n    }\r\n\r\n    return (void*)(long)Result;\r\n}\r\n\r\nint PtBufferTerminalFill(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks the internal implementation of the buffer by attempting\r\n    to fill it.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    char WriteBuffer[] = \"abcdefghijklmn\";\r\n    char WriteBuffer2[] = \"0123456\\n789ABC\";\r\n    size_t WriteBufferLen;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    WriteBufferLen = sizeof(WriteBuffer) - 1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Mark the master non-blocking and write to it in a loop.\r\n    // When it is out of room, it will return with EAGAIN.\r\n    //\r\n\r\n    fcntl(PtmFd, F_SETFL, O_NONBLOCK);\r\n    LxtLogInfo(\"Filling up the buffer - this might take some time...\");\r\n    for (;;)\r\n    {\r\n        BytesReadWrite = write(PtmFd, WriteBuffer, WriteBufferLen);\r\n        if (BytesReadWrite != WriteBufferLen)\r\n        {\r\n            break;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Given the odd number of bytes written, it is expected for the write\r\n    // to fail partway through the last transfer, returning a non-zero\r\n    // number of bytes written.\r\n    //\r\n    // N.B. On Ubuntu16, because the buffer size grows asynchronously under\r\n    //      pressure the buffer may be writeable again by this point.\r\n    //\r\n\r\n    if (BytesReadWrite < 0)\r\n    {\r\n        LxtLogError(\"Write failed with errno %d: %s\", errno, strerror(errno));\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Last write was %d bytes of the %d byte buffer\", BytesReadWrite, WriteBufferLen);\r\n\r\n    LxtCheckNotEqual(BytesReadWrite, WriteBufferLen, \"%llu\");\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    Timeout.tv_sec = 2;\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, WriteBuffer2, WriteBufferLen));\r\n    LxtLogInfo(\"Last write was %d bytes of the %d byte buffer\", BytesReadWrite, WriteBufferLen);\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    Timeout.tv_sec = 2;\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n\r\n    //\r\n    // On Ubuntu16 the characters after the '\\n' are added to the next\r\n    // allocated page of terminal buffer. On WSL there is no dynamic allocation\r\n    // so this will return after writing the '\\n'. In both cases, the\r\n    // characters before the '\\n' are discarded by virtue of being replaced\r\n    // by the subsequent character until finally hitting the '\\n'.\r\n    //\r\n\r\n    if (BytesReadWrite == WriteBufferLen)\r\n    {\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n    }\r\n    else\r\n    {\r\n        LxtCheckEqual(Result, 0, \"%d\");\r\n    }\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControllingTerminalForeground(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine terminates the session leader of a terminal and checks various\r\n    foreground behaviors.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    bool EndChildPidSynchronization;\r\n    bool EndGrandChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SelfPid;\r\n    int Status;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = true;\r\n    EndGrandChildPidSynchronization = true;\r\n    GrandChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n        //\r\n        // Verify current foreground process group.\r\n        //\r\n\r\n        SelfPid = getpid();\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(SelfPid, ForegroundId, \"%d\");\r\n\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid);\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Move to standalone process group.\r\n            //\r\n\r\n            LxtCheckErrno(setpgid(0, 0));\r\n\r\n            //\r\n            // Have parent set this process as foreground group.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Wait for session leader to terminate.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            EndChildPidSynchronization = true;\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            LxtSignalResetReceived();\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n\r\n            //\r\n            // Wait for master endpoint to close.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n            LXT_SYNCHRONIZATION_POINT();\r\n        }\r\n        else\r\n        {\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Set (grand)child as the foreground process group.\r\n            //\r\n\r\n            LxtCheckErrno(tcsetpgrp(PtsFd, GrandChildPid));\r\n            LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n            LxtCheckEqual(GrandChildPid, ForegroundId, \"%d\");\r\n\r\n            //\r\n            // Terminating before child.\r\n            //\r\n\r\n            EndGrandChildPidSynchronization = false;\r\n\r\n            //\r\n            // Communication with parent is now via grandchild.\r\n            //\r\n\r\n            EndChildPidSynchronization = false;\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            _exit(0);\r\n        }\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Wait for the child to terminate. The grandchild should still be\r\n        // running.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        EndChildPidSynchronization = false;\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &Status, 0)));\r\n\r\n        EndChildPidSynchronization = FALSE;\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        //\r\n        // Signal grandchild that its parent has terminated.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Close the master endpoint and signal the grandchild.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n        LXT_SYNCHRONIZATION_POINT();\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    LxtLogInfo(\"Thread exit: %d, Result=%d\", getpid(), Result);\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (EndGrandChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    }\r\n\r\n    if (EndChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControllingTerminalForeground2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine closes the master endpoint of a terminal and checks various\r\n    foreground behaviors.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    bool EndChildPidSynchronization;\r\n    bool EndGrandChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SelfPid;\r\n    int Status;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = true;\r\n    EndGrandChildPidSynchronization = true;\r\n    GrandChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n        //\r\n        // Verify current foreground process group.\r\n        //\r\n\r\n        SelfPid = getpid();\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(SelfPid, ForegroundId, \"%d\");\r\n\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid);\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Move to standalone process group.\r\n            //\r\n\r\n            LxtCheckErrno(setpgid(0, 0));\r\n\r\n            //\r\n            // Have parent set this process as foreground group and close the\r\n            // master endpoint.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n            //\r\n            // Signal parent to terminate and take over communication with\r\n            // grand-parent.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            EndChildPidSynchronization = true;\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            LxtSignalResetReceived();\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n            //\r\n            // Wait for session leader to terminate.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n        }\r\n        else\r\n        {\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Set (grand)child as the foreground process group.\r\n            //\r\n\r\n            LxtCheckErrno(tcsetpgrp(PtsFd, GrandChildPid));\r\n\r\n            //\r\n            // Signal parent to close last master endpoint descriptor.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            LxtSignalResetReceived();\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Terminating before child.\r\n            //\r\n\r\n            EndGrandChildPidSynchronization = false;\r\n\r\n            //\r\n            // Communication with parent is now via grandchild.\r\n            //\r\n\r\n            EndChildPidSynchronization = false;\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            _exit(0);\r\n        }\r\n    }\r\n    else\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait for the child to terminate. The grandchild should still be\r\n        // running.\r\n        //\r\n\r\n        EndChildPidSynchronization = false;\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &Status, 0)));\r\n\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        //\r\n        // Signal grandchild that its parent has terminated.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (EndGrandChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    }\r\n\r\n    if (EndChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControllingTerminalForeground3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine disconnects the terminal via TIOCNOTTY and checks various\r\n    foreground behaviors.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    bool EndChildPidSynchronization;\r\n    bool EndGrandChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SelfPid;\r\n    int Status;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = true;\r\n    EndGrandChildPidSynchronization = true;\r\n    GrandChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n        //\r\n        // Verify current foreground process group.\r\n        //\r\n\r\n        SelfPid = getpid();\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(SelfPid, ForegroundId, \"%d\");\r\n\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid);\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Move to standalone process group.\r\n            //\r\n\r\n            LxtCheckErrno(setpgid(0, 0));\r\n\r\n            //\r\n            // Have parent set this process as foreground group and disconnect\r\n            // the session terminal.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            LxtSignalResetReceived();\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n\r\n            //\r\n            // Signal parent to close last descriptor.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            EndChildPidSynchronization = true;\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n            //\r\n            // Wait for session leader to terminate.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n        }\r\n        else\r\n        {\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Set (grand)child as the foreground process group.\r\n            //\r\n\r\n            LxtCheckErrno(tcsetpgrp(PtsFd, GrandChildPid));\r\n\r\n            //\r\n            // Disassociate terminal.\r\n            //\r\n\r\n            LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Signal parent to close last master endpoint descriptor. No\r\n            // signal is expected because the terminal has been disconnected.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Terminating before child.\r\n            //\r\n\r\n            EndGrandChildPidSynchronization = false;\r\n\r\n            //\r\n            // Communication with parent is now via grandchild.\r\n            //\r\n\r\n            EndChildPidSynchronization = false;\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            _exit(0);\r\n        }\r\n    }\r\n    else\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait for the child to terminate. The grandchild should still be\r\n        // running.\r\n        //\r\n\r\n        EndChildPidSynchronization = false;\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &Status, 0)));\r\n\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        //\r\n        // Signal grandchild that its parent has terminated.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (EndGrandChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    }\r\n\r\n    if (EndChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControllingTerminalForeground4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine closes the master endpoint of a terminal where the session\r\n    leader is ignoring SIGHUP, and checks various foreground behaviors.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    bool EndChildPidSynchronization;\r\n    bool EndGrandChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SelfPid;\r\n    int Status;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = true;\r\n    EndGrandChildPidSynchronization = true;\r\n    GrandChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n        //\r\n        // Verify current foreground process group.\r\n        //\r\n\r\n        SelfPid = getpid();\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(SelfPid, ForegroundId, \"%d\");\r\n\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid);\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Move to standalone process group.\r\n            //\r\n\r\n            LxtCheckErrno(setpgid(0, 0));\r\n\r\n            //\r\n            // Have parent set this process as foreground group and close the\r\n            // master endpoint.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n\r\n            //\r\n            // Signal parent to terminate and take over communication with\r\n            // grand-parent.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            EndChildPidSynchronization = true;\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            LxtSignalResetReceived();\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n            //\r\n            // Wait for session leader to terminate.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n        }\r\n        else\r\n        {\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Set (grand)child as the foreground process group.\r\n            //\r\n\r\n            LxtCheckErrno(tcsetpgrp(PtsFd, GrandChildPid));\r\n\r\n            //\r\n            // Ignore SIGHUP on the session leader.\r\n            //\r\n\r\n            LxtCheckErrno(LxtSignalIgnore(SIGHUP));\r\n\r\n            //\r\n            // Signal parent to close last master endpoint descriptor.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Terminating before child.\r\n            //\r\n\r\n            EndGrandChildPidSynchronization = false;\r\n\r\n            //\r\n            // Communication with parent is now via grandchild.\r\n            //\r\n\r\n            EndChildPidSynchronization = false;\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            _exit(0);\r\n        }\r\n    }\r\n    else\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait for the child to terminate. The grandchild should still be\r\n        // running.\r\n        //\r\n\r\n        EndChildPidSynchronization = false;\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(ChildPid, &Status, 0)));\r\n\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        //\r\n        // Signal grandchild that its parent has terminated.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if (EndGrandChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    }\r\n\r\n    if (EndChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControllingTerminalForeground5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine connects a second process to the current foreground process\r\n    group and checks various foreground properties.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    bool EndChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    pid_t GrandChildPid2;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SelfPid;\r\n    int Status;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = true;\r\n    GrandChildPid = -1;\r\n    GrandChildPid2 = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid2);\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n        //\r\n        // Verify current foreground process group.\r\n        //\r\n\r\n        SelfPid = getpid();\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(SelfPid, ForegroundId, \"%d\");\r\n\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid);\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Move to standalone process group.\r\n            //\r\n\r\n            LxtCheckErrno(setpgid(0, 0));\r\n\r\n            //\r\n            // Have parent set this process as foreground group.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Wait for test to finish before terminating.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n        //\r\n        // Set (grand)child as the foreground process group.\r\n        //\r\n\r\n        LxtCheckErrno(tcsetpgrp(PtsFd, GrandChildPid));\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(ForegroundId, GrandChildPid, \"%d\");\r\n        LxtCheckErrno(tcgetsid(PtsFd));\r\n\r\n        //\r\n        // Start another child and try to connect to previous process group.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid2);\r\n        LxtCheckErrno(GrandChildPid2 = fork());\r\n        if (GrandChildPid2 == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Attempt to move to previously created process group.\r\n            //\r\n\r\n            LxtCheckErrno(setpgid(0, GrandChildPid));\r\n\r\n            //\r\n            // Signal parent to disconnect from the terminal.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            LxtSignalResetReceived();\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n\r\n            //\r\n            // Signal parent to close the terminal descriptor.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n            //\r\n            // Terminate.\r\n            //\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n        //\r\n        // Disconnect terminal.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n        //\r\n        // Signal parent to close last master endpoint descriptor.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n        LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n        LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n        //\r\n        // Signal original child to exit.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n    }\r\n    else\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid2, TRUE);\r\n    LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    if (EndChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControllingTerminalForeground6(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine terminates the current foreground process group and checks\r\n    various foreground properties.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    bool EndChildPidSynchronization;\r\n    bool EndGrandChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    pid_t GrandChildPid2;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SelfPid;\r\n    int Status;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = true;\r\n    EndGrandChildPidSynchronization = true;\r\n    GrandChildPid = -1;\r\n    GrandChildPid2 = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid2);\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n        //\r\n        // Verify current foreground process group.\r\n        //\r\n\r\n        SelfPid = getpid();\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(SelfPid, ForegroundId, \"%d\");\r\n\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid);\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Move to standalone process group.\r\n            //\r\n\r\n            LxtCheckErrno(setpgid(0, 0));\r\n\r\n            //\r\n            // Have parent set this process as foreground group.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Terminate.\r\n            //\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n        //\r\n        // Set (grand)child as the foreground process group.\r\n        //\r\n\r\n        LxtCheckErrno(tcsetpgrp(PtsFd, GrandChildPid));\r\n\r\n        //\r\n        // Wait for child to terminate.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n        EndGrandChildPidSynchronization = false;\r\n        LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(GrandChildPid, &Status, 0)));\r\n\r\n        LxtCheckResult(WIFEXITED(Status) ? 0 : -1);\r\n        LxtCheckResult((int)(char)WEXITSTATUS(Status));\r\n\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(ForegroundId, GrandChildPid, \"%d\");\r\n        LxtCheckErrno(tcgetsid(PtsFd));\r\n\r\n        //\r\n        // Start another child and try to connect to previous process group.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid2);\r\n        LxtCheckErrno(GrandChildPid2 = fork());\r\n        if (GrandChildPid2 == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Attempt to move to previously created process group of now\r\n            // terminated process.\r\n            //\r\n\r\n            LxtCheckErrnoFailure(setpgid(0, GrandChildPid), EPERM);\r\n\r\n            //\r\n            // Signal parent to disconnect from the terminal.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n\r\n            //\r\n            // Signal parent to close the terminal descriptor.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n            //\r\n            // Terminate.\r\n            //\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n        //\r\n        // Disconnect terminal.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n        //\r\n        // Signal parent to close last master endpoint descriptor.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtSignalWait();\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n        LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n        LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n    }\r\n    else\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid2, TRUE);\r\n    if (EndGrandChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    }\r\n\r\n    if (EndChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtControllingTerminalForeground7(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine connects a second process to an existing foreground group,\r\n    disconnects it from the controlling terminal and then tests various\r\n    properties.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    bool EndChildPidSynchronization;\r\n    pid_t ForegroundId;\r\n    pid_t GrandChildPid;\r\n    pid_t GrandChildPid2;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    pid_t SelfPid;\r\n    int Status;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);\r\n\r\n    ChildPid = -1;\r\n    EndChildPidSynchronization = true;\r\n    GrandChildPid = -1;\r\n    GrandChildPid2 = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid2);\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtSignalSetAllowMultiple(TRUE);\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n        //\r\n        // Verify current foreground process group.\r\n        //\r\n\r\n        SelfPid = getpid();\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(SelfPid, ForegroundId, \"%d\");\r\n\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid);\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Move to standalone process group.\r\n            //\r\n\r\n            LxtCheckErrno(setpgid(0, 0));\r\n\r\n            //\r\n            // Have parent set this process as foreground group.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n            //\r\n            // Wait for test to finish before terminating.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n\r\n        //\r\n        // Set (grand)child as the foreground process group.\r\n        //\r\n\r\n        LxtCheckErrno(tcsetpgrp(PtsFd, GrandChildPid));\r\n        LxtCheckErrno(ForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(ForegroundId, GrandChildPid, \"%d\");\r\n        LxtCheckErrno(tcgetsid(PtsFd));\r\n\r\n        //\r\n        // Start another child and try to connect to previous process group.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid2);\r\n        LxtCheckErrno(GrandChildPid2 = fork());\r\n        if (GrandChildPid2 == 0)\r\n        {\r\n            EndChildPidSynchronization = false;\r\n            LxtCheckResult(LxtSignalInitialize());\r\n            LxtSignalSetAllowMultiple(TRUE);\r\n            LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n\r\n            //\r\n            // Attempt to move to previously created process group.\r\n            //\r\n\r\n            LxtCheckErrno(setpgid(0, GrandChildPid));\r\n\r\n            //\r\n            // Disconnect from the controlling terminal.\r\n            //\r\n\r\n            LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n\r\n            //\r\n            // Signal parent to disconnect from the terminal.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n            LxtSignalResetReceived();\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n\r\n            //\r\n            // Signal parent to close the terminal descriptor.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n            LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n            LxtSignalWait();\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n            LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n            LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n            //\r\n            // Terminate.\r\n            //\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n        //\r\n        // Disconnect terminal.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCNOTTY, (char*)NULL));\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n\r\n        //\r\n        // Signal parent to close last master endpoint descriptor.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid2);\r\n        LxtCheckErrnoFailure(tcgetpgrp(PtsFd), EIO);\r\n        LxtCheckErrnoFailure(tcgetsid(PtsFd), EIO);\r\n\r\n        //\r\n        // Signal original child to exit.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_FOR(GrandChildPid);\r\n    }\r\n    else\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtClose(PtmFd);\r\n        PtmFd = -1;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid2, TRUE);\r\n    LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid, TRUE);\r\n    if (EndChildPidSynchronization)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtMountBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine verifies basic mount operations on the devpts file system.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    gid_t CurrentGid;\r\n    uid_t CurrentUid;\r\n    char EndpointName[sizeof(PTS_TEST_MNT) + 4];\r\n    struct stat EndpointStat;\r\n    int PtmFd;\r\n    int PtmFd2;\r\n    char* PtmxName = PTS_TEST_MNT \"/ptmx\";\r\n    struct stat PtmxStat;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    PtmFd = -1;\r\n    PtmFd2 = -1;\r\n    PtsFd = -1;\r\n\r\n    CurrentUid = geteuid();\r\n    CurrentGid = getegid();\r\n\r\n    //\r\n    // Create an endpoint to test default vs new mounts.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    if (SerialNumber > 999)\r\n    {\r\n        LxtLogError(\"Unexpectedly large number of opened ptys!\");\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    sprintf(EndpointName, \"%s/%d\", PTS_TEST_MNT, SerialNumber);\r\n\r\n    //\r\n    // Create a temporary directory to create mounts.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(PTS_TEST_MNT, 0777));\r\n\r\n    //\r\n    // Mount the default devpts instance.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(NULL, PTS_TEST_MNT, \"devpts\", MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL));\r\n\r\n    //\r\n    // Verify previously created endpoint exists in the new mount.\r\n    //\r\n\r\n    LxtCheckErrno(stat(EndpointName, &EndpointStat));\r\n    LxtCheckErrno(stat(PtmxName, &PtmxStat));\r\n    LxtCheckErrno(umount(PTS_TEST_MNT));\r\n\r\n    //\r\n    // Mount with \"newinstance\" option.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(NULL, PTS_TEST_MNT, \"devpts\", MS_NOEXEC | MS_NOSUID | MS_RELATIME, \"newinstance\"));\r\n\r\n    //\r\n    // Verify previously created endpoint does not exist in the new mount.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(stat(EndpointName, &EndpointStat), ENOENT);\r\n    LxtCheckErrno(stat(PtmxName, &PtmxStat));\r\n\r\n    //\r\n    // Check default ptmxmode settings.\r\n    //\r\n\r\n    LxtCheckEqual(PtmxStat.st_mode, S_IFCHR, \"%d\");\r\n\r\n    //\r\n    // Create a new endpoint and check default UID/GID/mode settings.\r\n    //\r\n\r\n    sprintf(EndpointName, \"%s/0\", PTS_TEST_MNT);\r\n    LxtCheckErrno((PtmFd2 = open(PtmxName, O_RDWR)));\r\n    LxtCheckErrno(stat(EndpointName, &EndpointStat));\r\n    LxtCheckEqual(EndpointStat.st_uid, CurrentUid, \"%d\");\r\n    LxtCheckEqual(EndpointStat.st_gid, CurrentGid, \"%d\");\r\n    LxtCheckEqual(EndpointStat.st_mode, (S_IFCHR | 0600), \"%d\");\r\n    LxtClose(PtmFd2);\r\n    LxtCheckErrno(umount(PTS_TEST_MNT));\r\n\r\n    //\r\n    // Mount with \"newinstance\" and specify UID/GID/mode/ptmxmode options.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(\r\n        NULL, PTS_TEST_MNT, \"devpts\", MS_NOEXEC | MS_NOSUID | MS_RELATIME, \"newinstance,uid=0,gid=5,mode=0620,ptmxmode=666\"));\r\n\r\n    LxtCheckErrno(stat(PtmxName, &PtmxStat));\r\n\r\n    //\r\n    // Check default ptmxmode settings.\r\n    //\r\n\r\n    LxtCheckEqual(PtmxStat.st_mode, (S_IFCHR | 0666), \"%d\");\r\n\r\n    //\r\n    // Create a new endpoint and check default UID/GID/mode settings.\r\n    //\r\n\r\n    sprintf(EndpointName, \"%s/0\", PTS_TEST_MNT);\r\n    LxtCheckErrno((PtmFd2 = open(PtmxName, O_RDWR)));\r\n    LxtCheckErrno(stat(EndpointName, &EndpointStat));\r\n    LxtCheckEqual(EndpointStat.st_uid, 0, \"%d\");\r\n    LxtCheckEqual(EndpointStat.st_gid, 5, \"%d\");\r\n    LxtCheckEqual(EndpointStat.st_mode, (S_IFCHR | 0620), \"%d\");\r\n    LxtClose(PtmFd2);\r\n    LxtCheckErrno(umount(PTS_TEST_MNT));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtmFd2 != -1)\r\n    {\r\n        close(PtmFd2);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    umount(PTS_TEST_MNT);\r\n    rmdir(PTS_TEST_MNT);\r\n    return Result;\r\n}\r\n\r\nint PtPacketBasic1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets packet-mode and checks the set value.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    char ControlByte;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Verify packet-mode is off (0) to start.\r\n    //\r\n\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCGPKT, &PacketMode));\r\n    LxtCheckEqual(PacketMode, 0, \"%d\");\r\n\r\n    //\r\n    // Turn on packet-mode using a non-zero value.\r\n    //\r\n\r\n    PacketMode = 0x123;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Verify packet-mode value.\r\n    //\r\n\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCGPKT, &PacketMode));\r\n    LxtCheckEqual(PacketMode, 1, \"%d\");\r\n\r\n    //\r\n    // Verify no data waiting.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Verify no data available to read.\r\n    //\r\n\r\n    LxtCheckErrno(PtmFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (PtmFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, &ControlByte, sizeof(ControlByte)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, PtmFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketBasic2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a basic packet-mode pseudo terminal test. The steps are:\r\n    - Turn on packet mode.\r\n    - Perform simple read/write check on the master-subordinate.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Turn on packet-mode.\r\n    //\r\n\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Test simple send/receive.\r\n    //\r\n\r\n    LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n\r\n    //\r\n    // Enable raw mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Test simple send/receive.\r\n    //\r\n\r\n    LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketBasic3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a basic packet-mode pseudo terminal test. The steps are:\r\n    - Turns off canonical mode to avoid line discipline.\r\n    - Turn on packet mode.\r\n    - Read back partial message.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Read just header byte from master.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading header byte from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, 1));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n\r\n    //\r\n    // Original message should still be there.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for remaining message from master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult + 1);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n    LxtCheckStringEqual(ReadBuffer + 1, Greetings);\r\n\r\n    //\r\n    // Write to master.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Read from subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, 1));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], Greetings[0], \"%hhd\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult - 1);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings + 1);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketBasic4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a basic packet-mode pseudo terminal test. The steps are:\r\n    - Turns off canonical mode to avoid line discipline.\r\n    - Turn on packet mode.\r\n    - Read back partial messages.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Read first two bytes from master.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading two bytes from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, 2));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 2);\r\n    LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n    LxtCheckEqual(ReadBuffer[1], Greetings[0], \"%hhd\");\r\n\r\n    //\r\n    // Check for remaining message bytes.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for remaining message from master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n    LxtCheckStringEqual(ReadBuffer + 1, Greetings + 1);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketToggleMode1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a message to the subordinate with packet-mode off, turns\r\n    it on and reads from master.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Configure as packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Read message from master.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult + 1);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n    LxtCheckStringEqual(ReadBuffer + 1, Greetings);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketToggleMode2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a message to the subordinate with packet-mode on, turns\r\n    it off and reads from master.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Turn off packet-mode.\r\n    //\r\n\r\n    PacketMode = 0;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Read message from master.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketToggleMode3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs operations that would normally result in a packet\r\n    mode status, then turns on packet mode and checks the control byte.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    char DefaultStartChar;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Fetch the default control character array values.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n    DefaultStartChar = ControlArray[VSTART];\r\n\r\n    //\r\n    // Modify the start control character.\r\n    //\r\n\r\n    ControlArray[VSTART] = _POSIX_VDISABLE;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOOFF));\r\n\r\n    //\r\n    // Flush subordinate read and write queues.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIOFLUSH));\r\n\r\n    //\r\n    // Configure packet-mode.\r\n    //\r\n\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Master endpoint should not be ready for read.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // No data expected.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Now with packet-mode enabled, undo/redo the previous steps.\r\n    //\r\n\r\n    //\r\n    // Modify the start control character.\r\n    //\r\n\r\n    ControlArray[VSTART] = DefaultStartChar;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Resume output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOON));\r\n\r\n    //\r\n    // Flush subordinate read and write queues.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIOFLUSH));\r\n\r\n    //\r\n    // Master endpoint should now be ready for read.\r\n    //\r\n\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], (TIOCPKT_FLUSHREAD | TIOCPKT_FLUSHWRITE | TIOCPKT_START | TIOCPKT_DOSTOP), \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketToggleMode4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine waits for data on the master subordinate on one thread, while\r\n    a second thread switches the master endpoint to packet-mode and flushes\r\n    the read queue.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ExpectedResult;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int Status;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        Timeout.tv_sec = 3;\r\n        FD_ZERO(&ReadFds);\r\n        FD_SET(PtmFd, &ReadFds);\r\n        LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Give child a chance to wait in select system call.\r\n        //\r\n\r\n        sleep(1);\r\n\r\n        //\r\n        // Enable packet mode.\r\n        //\r\n\r\n        PacketMode = 1;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n        sleep(1);\r\n\r\n        //\r\n        // Flush subordinate read queue.\r\n        //\r\n\r\n        LxtCheckErrno(tcflush(PtsFd, TCIFLUSH));\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtPacketToggleMode5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine enables packet mode and then waits for data on the master\r\n    subordinate on one thread. A second thread disables packet-mode on the\r\n    master endpoint and flushes the read queue.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ExpectedResult;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int Status;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Enable packet mode.\r\n        //\r\n\r\n        PacketMode = 1;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n        //\r\n        // Wait for data on master.\r\n        //\r\n\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        Timeout.tv_sec = 2;\r\n        FD_ZERO(&ReadFds);\r\n        FD_SET(PtmFd, &ReadFds);\r\n        LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n        LxtCheckEqual(Result, 0, \"%d\");\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Give child a chance to wait in select system call.\r\n        //\r\n\r\n        sleep(1);\r\n\r\n        //\r\n        // Disable packet mode.\r\n        //\r\n\r\n        PacketMode = 0;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n        //\r\n        // Flush subordinate read queue.\r\n        //\r\n\r\n        LxtCheckErrno(tcflush(PtsFd, TCIFLUSH));\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtPacketToggleMode6(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine enables packet mode and starts a read. A second thread\r\n    disables packet-mode and writes data.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int Status;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START()\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Configure as raw / packet-mode.\r\n        //\r\n\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        PacketMode = 1;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n        //\r\n        // Read message from master.\r\n        //\r\n\r\n        LxtLogInfo(\"Reading from master\");\r\n        memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult + 1);\r\n        ReadBuffer[BytesReadWrite] = '\\0';\r\n        LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n        LxtCheckStringEqual(ReadBuffer + 1, Greetings);\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Give child a chance to wait in read system call.\r\n        //\r\n\r\n        sleep(1);\r\n\r\n        //\r\n        // Disable packet mode.\r\n        //\r\n\r\n        PacketMode = 0;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n        //\r\n        // Write to subordinate.\r\n        //\r\n\r\n        LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n        LxtCheckErrno(tcdrain(PtsFd));\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtPacketToggleMode7(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine starts a read on the master endpoint. A second thread\r\n    enables packet-mode and writes data.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int Status;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Configure as raw.\r\n        //\r\n\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n\r\n        //\r\n        // Read message from master.\r\n        //\r\n\r\n        LxtLogInfo(\"Reading from master\");\r\n        memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n        ReadBuffer[BytesReadWrite] = '\\0';\r\n        LxtCheckStringEqual(ReadBuffer, Greetings);\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Give child a chance to wait in read system call.\r\n        //\r\n\r\n        sleep(1);\r\n\r\n        //\r\n        // Enable packet mode.\r\n        //\r\n\r\n        PacketMode = 1;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n        //\r\n        // Write to subordinate.\r\n        //\r\n\r\n        LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n        LxtCheckErrno(tcdrain(PtsFd));\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushRead1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine flushes the subordinate read queue.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[1];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Flush subordinate read queue.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIFLUSH));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_FLUSHREAD, \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushRead2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a message to the subordinate with packet-mode on, then\r\n    flushes the subordinate read queue.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Flush subordinate read queue.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIFLUSH));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_FLUSHREAD, \"%hhd\");\r\n\r\n    //\r\n    // Read message from master.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult + 1);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n    LxtCheckStringEqual(ReadBuffer + 1, Greetings);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushRead3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine waits for data on the master subordinate on one thread, while\r\n    a second thread flushes the read queue.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ExpectedResult;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int Status;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Enable packet mode.\r\n        //\r\n\r\n        PacketMode = 1;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n        //\r\n        // Read from master.\r\n        //\r\n\r\n        LxtLogInfo(\"Reading from master\");\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n        LxtCheckEqual(ReadBuffer[0], TIOCPKT_FLUSHREAD, \"%hhd\");\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Give child a chance to wait in the read system call.\r\n        //\r\n\r\n        sleep(1);\r\n\r\n        //\r\n        // Flush subordinate read queue.\r\n        //\r\n\r\n        LxtCheckErrno(tcflush(PtsFd, TCIFLUSH));\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushWrite1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine flushes the subordinate write queue.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[1];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Flush subordinate write queue.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCOFLUSH));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_FLUSHWRITE, \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushWrite2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a message to the subordinate with packet-mode on, then\r\n    flushes the subordinate write queue.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Flush subordinate write queue.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCOFLUSH));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_FLUSHWRITE, \"%hhd\");\r\n\r\n    //\r\n    // No message should be waiting because the write queue was flushed.\r\n    //\r\n\r\n    LxtCheckErrno(PtmFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (PtmFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, PtmFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushReadWrite1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine flushes the subordinate read and write queues.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[1];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Flush subordinate read and write queues.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIOFLUSH));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], (TIOCPKT_FLUSHREAD | TIOCPKT_FLUSHWRITE), \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushReadWrite2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a message to the subordinate with packet-mode on, then\r\n    flushes the subordinate read and write queues.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Flush subordinate read and write queues.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIFLUSH));\r\n    LxtCheckErrno(tcflush(PtsFd, TCOFLUSH));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], (TIOCPKT_FLUSHREAD | TIOCPKT_FLUSHWRITE), \"%hhd\");\r\n\r\n    //\r\n    // No message should be waiting because the write queue was flushed.\r\n    //\r\n\r\n    LxtCheckErrno(PtmFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (PtmFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, PtmFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushReadWrite3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a message to the subordinate with packet-mode on, then\r\n    flushes the subordinate read and write queues.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Flush subordinate read and write queues.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIOFLUSH));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], (TIOCPKT_FLUSHREAD | TIOCPKT_FLUSHWRITE), \"%hhd\");\r\n\r\n    //\r\n    // No message should be waiting because the write queue was flushed.\r\n    //\r\n\r\n    LxtCheckErrno(PtmFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (PtmFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, PtmFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushReadWrite4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a message to the subordinate with packet-mode on, then\r\n    flushes the subordinate read and write queues.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Flush subordinate write queue.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCOFLUSH));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Flush subordinate read queue.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIFLUSH));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], (TIOCPKT_FLUSHREAD | TIOCPKT_FLUSHWRITE), \"%hhd\");\r\n\r\n    //\r\n    // Read message from master.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult + 1);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n    LxtCheckStringEqual(ReadBuffer + 1, Greetings);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketFlushReadWrite5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a message to the subordinate with packet-mode on, then\r\n    flushes the subordinate read and write queues.\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure as raw / packet-mode.\r\n    //\r\n\r\n    LxtCheckErrno(RawInit(PtsFd));\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Flush subordinate read queue.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIFLUSH));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Flush subordinate write queue.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCOFLUSH));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], (TIOCPKT_FLUSHREAD | TIOCPKT_FLUSHWRITE), \"%hhd\");\r\n\r\n    //\r\n    // No message should be waiting because the write queue was flushed.\r\n    //\r\n\r\n    LxtCheckErrno(PtmFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (PtmFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, PtmFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketHangup(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine enables packet mode, then does a read on the master endpoint.\r\n    A second thread hangs up the subordinate.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    pid_t ChildPid;\r\n    int ExpectedResult;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int Status;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Close subordinate for this thread.\r\n        //\r\n\r\n        LxtClose(PtsFd);\r\n\r\n        //\r\n        // Enable packet mode.\r\n        //\r\n\r\n        PacketMode = 1;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n        //\r\n        // Read control byte.\r\n        //\r\n\r\n        LxtLogInfo(\"Reading from master\");\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EIO);\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Give child a chance to wait in read system call.\r\n        //\r\n\r\n        sleep(1);\r\n\r\n        //\r\n        // Hang up subordinate.\r\n        //\r\n\r\n        LxtClose(PtsFd);\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtPacketControlCharCheck1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that SIGINT is delivered with a ^C and that a flush\r\n    packet is returned as a side-effect.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char ReadBuffer[10];\r\n    ssize_t BytesReadWrite;\r\n    pid_t ChildPid;\r\n    cc_t ControlArray[NCCS];\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int PtsFlags;\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    int Status;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGINT, SA_SIGINFO));\r\n\r\n        //\r\n        // Configure as packet-mode.\r\n        //\r\n\r\n        PacketMode = 1;\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n        //\r\n        // Write the interrupt control character.\r\n        //\r\n\r\n        LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VINTR], 1));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n        LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n        //\r\n        // A SIGINT signal should be generated.\r\n        //\r\n\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGINT));\r\n        LxtSignalResetReceived();\r\n\r\n        //\r\n        // Check for available data.\r\n        //\r\n\r\n        LxtLogInfo(\"Verifying master has data to read...\");\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        FD_ZERO(&ReadFds);\r\n        FD_SET(PtmFd, &ReadFds);\r\n        LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n        //\r\n        // Read control byte.\r\n        //\r\n\r\n        LxtLogInfo(\"Reading from master\");\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n        LxtCheckEqual(ReadBuffer[0], (TIOCPKT_FLUSHREAD | TIOCPKT_FLUSHWRITE), \"%hhd\");\r\n\r\n        //\r\n        // The control character sequence should have been echoed back.\r\n        //\r\n\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, 3);\r\n        LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n        LxtCheckTrue(IS_CONTROL_CHAR_ECHO_STRING(ReadBuffer + 1, ControlArray[VINTR]));\r\n\r\n        //\r\n        // There should be no character waiting at the subordinate.\r\n        //\r\n\r\n        LxtCheckErrno(PtsFlags = fcntl(PtsFd, F_GETFL, 0));\r\n        LxtCheckErrno(fcntl(PtsFd, F_SETFL, (PtsFlags | O_NONBLOCK)));\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n        LxtCheckErrno(fcntl(PtsFd, F_SETFL, PtsFlags));\r\n        Result = 0;\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtPacketControlCharCheck2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that changing the STOP/START control character delivers\r\n    a control byte.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    char DefaultStartChar;\r\n    char DefaultStopChar;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[1];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure for packet-mode.\r\n    //\r\n\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Fetch the default control character array values.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n    DefaultStartChar = ControlArray[VSTART];\r\n    DefaultStopChar = ControlArray[VSTOP];\r\n\r\n    //\r\n    // Modify the start control character.\r\n    //\r\n\r\n    ControlArray[VSTART] = _POSIX_VDISABLE;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_NOSTOP, \"%hhd\");\r\n\r\n    //\r\n    // Modify the stop control character.\r\n    //\r\n\r\n    ControlArray[VSTOP] = _POSIX_VDISABLE;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Should be no change in state.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Restore the start control character.\r\n    //\r\n\r\n    ControlArray[VSTART] = DefaultStartChar;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Should be no change in state.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Restore the stop control character.\r\n    //\r\n\r\n    ControlArray[VSTOP] = DefaultStopChar;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Now should see a control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_DOSTOP, \"%hhd\");\r\n\r\n    //\r\n    // Finally, modify just the stop control character.\r\n    //\r\n\r\n    ControlArray[VSTOP] = _POSIX_VDISABLE;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_NOSTOP, \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketControlCharCheck3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that the STOP/START control characters deliver\r\n    control bytes.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[1];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure for packet-mode.\r\n    //\r\n\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Fetch the default control character array values.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Send the STOP control character.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTOP], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_STOP, \"%hhd\");\r\n\r\n    //\r\n    // Send a few extraneous STOP control character.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTOP], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTOP], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTOP], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // This should not result in a control byte as state did not change.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has no data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Send the START control character.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTART], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process start.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_START, \"%hhd\");\r\n\r\n    //\r\n    // Send both the STOP and START control characters.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTOP], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTART], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process start.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Check for control byte\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has no data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], TIOCPKT_START, \"%hhd\");\r\n\r\n    //\r\n    // Send a few extraneous START control characters.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTART], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTART], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTART], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // This should not result in a control byte as state did not change.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has no data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtPacketToggleWithControlByte(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine causes a control byte to be generated and then toggles packet\r\n    mode off and then on to check what state persists.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    char DefaultStartChar;\r\n    char DefaultStopChar;\r\n    int PacketMode;\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[1];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Configure for packet-mode.\r\n    //\r\n\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Fetch the default control character array values.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n    DefaultStartChar = ControlArray[VSTART];\r\n    DefaultStopChar = ControlArray[VSTOP];\r\n\r\n    //\r\n    // Modify the start control character.\r\n    //\r\n\r\n    ControlArray[VSTART] = _POSIX_VDISABLE;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Flush subordinate read and write queues.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIOFLUSH));\r\n\r\n    //\r\n    // Send the STOP control character.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTOP], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Turn off packet-mode.\r\n    //\r\n\r\n    PacketMode = 0;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Turn on packet-mode.\r\n    //\r\n\r\n    PacketMode = 1;\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCPKT, &PacketMode));\r\n\r\n    //\r\n    // Check for available data.\r\n    //\r\n\r\n    LxtLogInfo(\"Verifying master has no data to read...\");\r\n    FD_SET(PtmFd, &ReadFds);\r\n    LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Read control byte.\r\n    //\r\n\r\n    // LxtLogInfo(\"Reading from master\");\r\n    // LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    // LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    // LxtCheckEqual(ReadBuffer[0], TIOCPKT_NOSTOP, \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSessionBasicMaster(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs simple session controlling terminal tests.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[64];\r\n    pid_t ChildPid;\r\n    pid_t ForegroundId;\r\n    pid_t SelfPid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n    pid_t SessionId;\r\n    int Status;\r\n    pid_t TerminalForegroundId;\r\n    pid_t TerminalSessionId;\r\n    int TtyFd;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Current session should already have a controlling terminal, so\r\n        // expect a failure trying to set a new one.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(ioctl(PtmFd, TIOCSCTTY, (char*)NULL), EPERM);\r\n\r\n        //\r\n        // Move to a new session\r\n        //\r\n\r\n        LxtLogInfo(\"Creating new session and verifying state.\");\r\n        LxtCheckErrno(SessionId = setsid());\r\n        LxtCheckResult(SelfPid = getpid());\r\n        LxtCheckResult(SessionId = getsid(0));\r\n        LxtCheckEqual(SelfPid, SessionId, \"%d\");\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTOU, SA_SIGINFO));\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGTTIN, SA_SIGINFO));\r\n        LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n        LxtCheckErrno(tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(Result, 0, \"%d\");\r\n        LxtCheckErrnoFailure((TtyFd = open(\"/dev/tty\", O_RDONLY)), ENXIO);\r\n\r\n        //\r\n        // Set the master endpoint as the controlling terminal for the session.\r\n        //\r\n\r\n        LxtLogInfo(\"Setting master endpoint as the controlling terminal.\");\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCSCTTY, (char*)NULL));\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCSCTTY, (char*)NULL));\r\n\r\n        //\r\n        // Check current state.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(tcgetsid(PtsFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcgetsid(PtmFd), ENOTTY);\r\n        LxtCheckErrnoFailure(tcgetpgrp(PtsFd), ENOTTY);\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(TerminalForegroundId, 0, \"%d\");\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LxtCheckErrnoFailure((TtyFd = open(\"/dev/tty\", O_RDONLY)), EIO);\r\n        // LxtCheckErrno((TtyFd = open(\"/dev/tty\", O_RDWR)));\r\n        // LxtCheckErrno(ttyname_r(TtyFd, Buffer, sizeof(Buffer)));\r\n        // LxtClose(TtyFd);\r\n        // LxtLogInfo(\"Controlling terminal: %s\", Buffer);\r\n\r\n        //\r\n        // Remove the master endpoint as the controlling terminal.\r\n        //\r\n\r\n        LxtLogInfo(\"Verifying locked controlling terminal.\");\r\n        LxtCheckErrnoFailure(ioctl(PtsFd, TIOCSCTTY, (char*)NULL), EPERM);\r\n        LxtLogInfo(\"Removing master endpoint as controlling terminal.\");\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n        LxtCheckErrno(ioctl(PtmFd, TIOCNOTTY, (char*)NULL));\r\n        LxtCheckResult(LxtSignalCheckReceived(SIGHUP));\r\n        LxtSignalResetReceived();\r\n\r\n        //\r\n        // Now try to set the subordinate endpoint as the controlling terminal.\r\n        //\r\n\r\n        LxtLogInfo(\"Adding subordinate endpoint as controlling terminal and verifying state.\");\r\n        LxtCheckErrno(ioctl(PtsFd, TIOCSCTTY, (char*)NULL));\r\n        LxtCheckErrno((TtyFd = open(\"/dev/tty\", O_RDWR)));\r\n        LxtCheckErrno(ttyname_r(TtyFd, Buffer, sizeof(Buffer)));\r\n        LxtClose(TtyFd);\r\n        LxtLogInfo(\"Controlling terminal: %s\", Buffer);\r\n        LxtLogInfo(\"Controlling terminal: %s\", Buffer);\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtsFd));\r\n        LxtCheckEqual(SessionId, TerminalSessionId, \"%d\");\r\n        LxtCheckErrno(TerminalSessionId = tcgetsid(PtmFd));\r\n        LxtCheckEqual(SessionId, TerminalSessionId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtsFd));\r\n        LxtCheckEqual(SelfPid, TerminalForegroundId, \"%d\");\r\n        LxtCheckErrno(TerminalForegroundId = tcgetpgrp(PtmFd));\r\n        LxtCheckEqual(SelfPid, TerminalForegroundId, \"%d\");\r\n        LxtCheckErrno(RawInit(PtsFd));\r\n        LxtCheckErrno(SimpleReadWriteCheck(PtmFd, PtsFd));\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks suspend output support on the subordinate endpoint.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOOFF));\r\n\r\n    //\r\n    // subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Verify flush and drain are not effected.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempting drain...\");\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n    LxtLogInfo(\"Attempting flush...\");\r\n    LxtCheckErrno(tcflush(PtsFd, TCIOFLUSH));\r\n\r\n    //\r\n    // Restart output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOON));\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // No output expected for subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks suspend output support on the subordinate endpoint\r\n    using the control characters.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Get control characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTOP], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Restart output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTART], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process start.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // No output expected for subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks suspend output support on the master endpoint.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    const char* GreetingsEcho = \"Hi there!!\\r\\n\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Master endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Suspend output on master.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCOOFF));\r\n\r\n    //\r\n    // Master endpoint should not be ready for write.\r\n    //\r\n\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Verify flush and drain are not effected.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempting drain...\");\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n    LxtLogInfo(\"Attempting flush...\");\r\n    LxtCheckErrno(tcflush(PtmFd, TCIOFLUSH));\r\n\r\n    //\r\n    // Restart output on master.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCOON));\r\n\r\n    //\r\n    // Master endpoint should be ready for write.\r\n    //\r\n\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Try to write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on subordinate\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // Check echo output.\r\n    //\r\n\r\n    ExpectedResult = strlen(GreetingsEcho);\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsEcho);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks suspend output support on the subordinate endpoint via\r\n    TCI(OFF/ON) from the master.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    const char* GreetingsOut = \"Hi there!!\\r\\n\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCIOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Restart output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCION));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process start.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    ExpectedResult = strlen(GreetingsOut);\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsOut);\r\n\r\n    //\r\n    // No output expected for subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks the control characters transmitted via TCI(ON/OFF).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\";\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Get default control characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // Turn off IXON to disable START/STOP characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags & ~IXON));\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCIOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Check for echo to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for echo to master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 2);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, PTS_START_CONTROL_CHAR);\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on subordinate\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // Restart output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCION));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process start.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Check for echo to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on subordinate\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 2);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, PTS_STOP_CONTROL_CHAR);\r\n\r\n    //\r\n    // No output expected for subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput6(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that alternate control characters do not effect\r\n    TCI(ON/OFF), rather they keep it from working properly.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Change START/STOP control characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n    ControlArray[VSTART] = 1;\r\n    ControlArray[VSTOP] = 2;\r\n    LxtCheckResult(TerminalSettingsSetControlArray(PtsFd, ControlArray));\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCIOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 2);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, PTS_START_CONTROL_CHAR);\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // Restart output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCION));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process start.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 2);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, PTS_STOP_CONTROL_CHAR);\r\n\r\n    //\r\n    // No output expected for subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput7(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks that TCI(OFF/ON) from the subordinate have no effect on\r\n    the master endpoint.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    const char* GreetingsOut = \"Hi there!!\\r\\n\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Get default control characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtsFd, ControlArray));\r\n\r\n    //\r\n    // Suspend output on master.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCIOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Master endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Check for \"suspend\" character on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], ControlArray[VSTOP], \"%hhd\");\r\n\r\n    //\r\n    // Try to write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on subordinate\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // Check for echo to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on subordinate\");\r\n    ExpectedResult = strlen(GreetingsOut);\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsOut);\r\n\r\n    //\r\n    // Restart output on master.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCION));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process start.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Master endpoint should be ready for write.\r\n    //\r\n\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Check for \"resume\" character on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, 1);\r\n    LxtCheckEqual(ReadBuffer[0], ControlArray[VSTART], \"%hhd\");\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput8(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks TCOFF/ON when IXON is disabled.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\";\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Turn off IXON to disable START/STOP characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags & ~IXON));\r\n\r\n    //\r\n    // subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOOFF));\r\n\r\n    //\r\n    // Subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Restart output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOON));\r\n\r\n    //\r\n    // Subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // No output expected for subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput9(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks echo support while output is suspended on the\r\n    subordinate endpoint.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    const char* GreetingsEcho = \"Hi there!!\\r\\n\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOOFF));\r\n\r\n    //\r\n    // Write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process message.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // No echo expected to master.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Resume output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOON));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process state\r\n    // change.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Echo characters expected to have been discarded.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput10(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks echo support while output is suspended on the\r\n    master endpoint.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    const char* GreetingsEcho = \"Hi there!!\\r\\n\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Suspend output on master.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCOOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process message.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Check for echo on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for echo on master\");\r\n    ExpectedResult = strlen(GreetingsEcho);\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsEcho);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput11(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks suspend output support on the subordinate endpoint via\r\n    TCI(OFF/ON) from the master when the master has its own output suspended.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    const char* GreetingsOut = \"Hi there!!\\r\\n\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Suspend output on master.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCOOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Try to suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCIOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // No echo expected to master as control character shouldn't have been\r\n    // transmitted.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    ExpectedResult = strlen(GreetingsOut);\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsOut);\r\n\r\n    //\r\n    // Restart output on master.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCOON));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process start.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // No echo expected to master as control character should have been\r\n    // discarded.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    ExpectedResult = strlen(GreetingsOut);\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsOut);\r\n\r\n    //\r\n    // No output expected for subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput12(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks suspend output support on the subordinate with IXANY\r\n    using the control character (^S).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    const char* GreetingsOut = \"Hi there!!\\r\\n\";\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Enable IXANY flag.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtsFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtsFd, InputFlags | IXANY));\r\n\r\n    //\r\n    // Suspend output on subordinate with control character.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCIOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Check for message on subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on subordinate.\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // Check for echo on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master.\");\r\n    ExpectedResult = strlen(GreetingsOut);\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsOut);\r\n\r\n    //\r\n    // Subordinate endpoint should have resumed output.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtCheckErrno(tcdrain(PtsFd));\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master.\");\r\n    ExpectedResult = strlen(GreetingsOut);\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsOut);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput13(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks suspend output support on the subordinate with IXANY\r\n    using TCOOFF which should not be effected.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    const char* GreetingsOut = \"Hi there!!\\r\\n\";\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Enable IXANY flag.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetInputFlags(PtmFd, &InputFlags));\r\n    LxtCheckResult(TerminalSettingsSetInputFlags(PtmFd, InputFlags | IXANY));\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on subordinate\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // Subordinate endpoint should still not be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput14(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks suspend output on the master when the subordinate\r\n    disconnects.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    const char* GreetingsOut = \"Hi there!!\\r\\n\";\r\n    tcflag_t InputFlags;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char PtsDevName[PTS_DEV_NAME_BUFFER_SIZE];\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, PtsDevName, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Suspend output on master.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCOOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Master endpoint should not be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Close the subordinate.\r\n    //\r\n\r\n    LxtClose(PtsFd);\r\n\r\n    //\r\n    // Master endpoint should not be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Reopen the subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(PtsFd = open(PtsDevName, O_RDWR));\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Master endpoint should not be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    ExpectedResult = strlen(GreetingsOut);\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsOut);\r\n\r\n    //\r\n    // Master endpoint should not be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtmFd, &WriteFds);\r\n    LxtCheckErrno(select((PtmFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtmFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtmFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Resume output on master.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtmFd, TCOON));\r\n\r\n    //\r\n    // Write to master.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to master...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // Check for echo on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for echo on master\");\r\n    ExpectedResult = strlen(GreetingsOut);\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, GreetingsOut);\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput15(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks suspend output support using combinations of\r\n    control-characters and TCIOFF/ON.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    cc_t ControlArray[NCCS];\r\n    int ExpectedResult;\r\n    int FileFlags;\r\n    const char* Greetings = \"Hi there!!\";\r\n    int PtmFd;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    int Result;\r\n    int SerialNumber;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Get control characters.\r\n    //\r\n\r\n    LxtCheckResult(TerminalSettingsGetControlArray(PtmFd, ControlArray));\r\n\r\n    //\r\n    // subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTOP], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Use TCION to try to resume output.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOON));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Use TCIOFF to suspend output on already CTRL-S suspended terminal.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOOFF));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Restart output on subordinate with control character.\r\n    //\r\n\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, &ControlArray[VSTART], 1));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, 1);\r\n    LxtCheckErrno(tcdrain(PtmFd));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process start.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // subordinate endpoint should not be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\n    //\r\n    // Use TCOON to resume output.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOON));\r\n\r\n    //\r\n    // Ubuntu16 asynchronous pty processing needs some time to process stop.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // subordinate endpoint should be ready for write.\r\n    //\r\n\r\n    FD_SET(PtsFd, &WriteFds);\r\n    LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Try to write to subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Write to subordinate...\");\r\n    ExpectedResult = strlen(Greetings);\r\n    LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Check for message on master.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking for message on master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n    ReadBuffer[BytesReadWrite] = '\\0';\r\n    LxtCheckStringEqual(ReadBuffer, Greetings);\r\n\r\n    //\r\n    // No output expected for subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(FileFlags = fcntl(PtsFd, F_GETFL, 0));\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, (FileFlags | O_NONBLOCK)));\r\n    LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n    LxtCheckErrno(fcntl(PtsFd, F_SETFL, FileFlags));\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PtSuspendOutput16(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tries to write output to a suspended endpoint.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ChildPid;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hi there!!\\n\";\r\n    int PtmFd;\r\n    int PtmFlags;\r\n    int PtsFd;\r\n    char ReadBuffer[20];\r\n    fd_set ReadFds;\r\n    int Result;\r\n    int SerialNumber;\r\n    int Status;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    ExpectedResult = strlen(Greetings);\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n\r\n    //\r\n    // Open Master-Subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    //\r\n    // Suspend output on subordinate.\r\n    //\r\n\r\n    LxtCheckErrno(tcflow(PtsFd, TCOOFF));\r\n\r\n    //\r\n    // Flush subordinate read and write queues.\r\n    //\r\n\r\n    LxtCheckErrno(tcflush(PtsFd, TCIOFLUSH));\r\n\r\n    //\r\n    // Fork a thread that should block on suspended output.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // subordinate endpoint should not be ready for write.\r\n        //\r\n\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        FD_ZERO(&WriteFds);\r\n        FD_SET(PtsFd, &WriteFds);\r\n        LxtCheckErrno(select((PtsFd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n        LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n        //\r\n        // Try to write to subordinate.\r\n        //\r\n\r\n        LxtLogInfo(\"Attempt to write to suspended subordinate...\");\r\n        LxtCheckErrno(BytesReadWrite = write(PtsFd, Greetings, ExpectedResult));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n\r\n        //\r\n        // Make sure subordinate output is resumed.\r\n        //\r\n\r\n        LxtCheckErrno(tcflow(PtsFd, TCOON));\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Wait a bit to give the child thread time to attempt the write.\r\n        //\r\n\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        Timeout.tv_sec = 1;\r\n        FD_ZERO(&ReadFds);\r\n        FD_SET(PtmFd, &ReadFds);\r\n        LxtLogInfo(\"Waiting one second for data...\");\r\n        LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n\r\n        //\r\n        // There should be no data available.\r\n        //\r\n\r\n        LxtLogInfo(\"Checking for available data...\");\r\n        LxtCheckEqual(Result, 0, \"%d\");\r\n        LxtCheckErrno(PtmFlags = fcntl(PtmFd, F_GETFL, 0));\r\n        LxtCheckErrno(fcntl(PtmFd, F_SETFL, (PtmFlags | O_NONBLOCK)));\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)), EAGAIN);\r\n\r\n        //\r\n        // Resume subordinate output.\r\n        //\r\n\r\n        LxtLogInfo(\"Resuming suspended subordinate...\");\r\n        LxtCheckErrno(tcflow(PtsFd, TCOON));\r\n\r\n        //\r\n        // Data should become available.\r\n        //\r\n\r\n        LxtLogInfo(\"Checking again for available data...\");\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        Timeout.tv_sec = 1;\r\n        FD_SET(PtmFd, &ReadFds);\r\n        LxtCheckErrno(select((PtmFd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n        LxtLogInfo(\"Reading available data...\");\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult + 1);\r\n    }\r\n\r\nErrorExit:\r\n    if ((ChildPid != 0) && (PtmFd != -1))\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if ((ChildPid != 0) && (PtsFd != -1))\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/dev_pt_common.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    dev_pt_common.c\r\n\r\nAbstract:\r\n\r\n    This file is a test for the Pseudo Terminals: /dev/ptmx, /dev/pts/<n>\r\n    devices.\r\n\r\n--*/\r\n\r\n#include \"dev_pt_common.h\"\r\n\r\npid_t ForkPtyCommon(int* PtmFdOut, int* PtsFdOut, bool UseMasterEndpoint);\r\n\r\nvoid DumpBuffer(const char Data[], size_t DataSize)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will log the Data.\r\n\r\nArguments:\r\n\r\n    Data - Supplies the buffer to be filled.\r\n\r\n    DataSize - Supplies the size of the data buffer.\r\n\r\nReturn Value:\r\n\r\n    None\r\n\r\n--*/\r\n\r\n{\r\n\r\n    size_t Index;\r\n    for (Index = 0; Index < DataSize; Index++)\r\n    {\r\n        printf(\"%d:(\", Data[Index]);\r\n        if (Data[Index] == '\\n')\r\n        {\r\n            printf(\"\\\\n\");\r\n        }\r\n        else if (Data[Index] == '\\r')\r\n        {\r\n            printf(\"\\\\r\");\r\n        }\r\n        else if (Data[Index] == '\\t')\r\n        {\r\n            printf(\"\\\\t\");\r\n        }\r\n        else\r\n        {\r\n            printf(\"%c\", Data[Index]);\r\n        }\r\n\r\n        printf(\") \");\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint GetPtSerialNumFromDeviceString(const char PtsNameString[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will parse the PTS (Pseudo Terminal Slave) device name and\r\n    retrieve the Serial Number from the string.\r\n\r\nArguments:\r\n\r\n    PtsNameString - Supplies the device name of the PTS. The name should be\r\n        of the format \"/dev/pts/<n>\" where 'n' is the Serial Number. The string\r\n        should also be NULL terminated.\r\n\r\nReturn Value:\r\n\r\n    Returns the Serial Number, which is >=0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int NumberOfItemsScanned;\r\n    int Result;\r\n    int SerialNumber;\r\n\r\n    SerialNumber = 0;\r\n\r\n    NumberOfItemsScanned = sscanf(PtsNameString, \"/dev/pts/%d\", &SerialNumber);\r\n    if (NumberOfItemsScanned != 1)\r\n    {\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = SerialNumber;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint GetRandomMessage(char Message[], size_t MessageSize, bool CompleteMessage)\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will fill the message buffer with random bytes for the\r\n    specified size. If a complete message is requested, then it will set the\r\n    last byte in the message with the completion character.\r\n\r\nArguments:\r\n\r\n    Message - Supplies the buffer to be filled with data.\r\n\r\n    MessageSize - Supplies the size to be filled. Size should be >=1.\r\n\r\n    CompleteMessage - Supplies the flag which indicates whether the message\r\n        should be completed with a terminating character or not.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    size_t Itr;\r\n    size_t NumBytesToFill;\r\n\r\n    //\r\n    // If the message has to be completed, last byte is reserved for the\r\n    // terminating character.\r\n    //\r\n\r\n    if (CompleteMessage != FALSE)\r\n    {\r\n        NumBytesToFill = MessageSize - 1;\r\n        Message[NumBytesToFill] = '\\n';\r\n    }\r\n    else\r\n    {\r\n        NumBytesToFill = MessageSize;\r\n    }\r\n\r\n    for (Itr = 0; Itr < NumBytesToFill; Itr += 1)\r\n    {\r\n\r\n        //\r\n        // TODO_LX_PTYT: Randomize the data.\r\n        //\r\n\r\n        Message[Itr] = 'A' + (Itr % 26);\r\n    }\r\n\r\n    return 0;\r\n}\r\n\r\nint OpenMasterSubordinate(int* PtmFd, int* PtsFd, char* PtsDevName, int* SerialNumber)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will open a master and subordinate pseudo terminal.\r\n    It will also extract the serial number of the subordinate and\r\n    return it in 'SerialNumber'.\r\n\r\nArguments:\r\n\r\n    PtmFd - Supplies the pointer which will be set to the FD of the\r\n        master, on success. This parameter is not optional.\r\n\r\n    PtsFd - Supplies the pointer which will be set to the FD of the\r\n        subordinate, on success. This parameter is not optional.\r\n\r\n    PtsDevName - Supplies the pointer to the buffer that will hold\r\n        the subordinate device name, on success. This parameter is\r\n        optional.\r\n\r\n    SerialNumber - Supplies the pointer which will be set to the\r\n        serial number of the subordinate pseudo terminal. This\r\n        parameter is optional.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n--*/\r\n\r\n{\r\n\r\n    int Fdm;\r\n    int Fds;\r\n    char LocalBuffer[PTS_DEV_NAME_BUFFER_SIZE];\r\n    int Result;\r\n    int SubordinateSerialNumber;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    Result = -1;\r\n    Fdm = -1;\r\n    Fds = -1;\r\n\r\n    LxtCheckErrno((Fdm = open(\"/dev/ptmx\", O_RDWR)));\r\n    LxtCheckErrno(grantpt(Fdm));\r\n    LxtCheckErrno(unlockpt(Fdm));\r\n    LxtCheckErrno(ptsname_r(Fdm, LocalBuffer, PTS_DEV_NAME_BUFFER_SIZE));\r\n    LxtCheckErrno(Fds = open(LocalBuffer, O_RDWR));\r\n    if (PtsDevName != NULL)\r\n    {\r\n        strcpy(PtsDevName, LocalBuffer);\r\n    }\r\n\r\n    if (SerialNumber != NULL)\r\n    {\r\n        LxtCheckErrno(SubordinateSerialNumber = GetPtSerialNumFromDeviceString(LocalBuffer));\r\n        *SerialNumber = SubordinateSerialNumber;\r\n    }\r\n\r\n    *PtmFd = Fdm;\r\n    *PtsFd = Fds;\r\n    Fdm = -1;\r\n    Fds = -1;\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fdm != -1)\r\n    {\r\n        close(Fdm);\r\n    }\r\n\r\n    if (Fds != -1)\r\n    {\r\n        close(Fds);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\npid_t ForkPty(int* PtmFdOut, int* PtsFdOut)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets up a new process as a foreground process using PtsFd as\r\n    its controlling terminal.\r\n\r\nArguments:\r\n\r\n    PtmFdOut - Supplies a pointer to receive the master file descriptor.\r\n\r\n    PtsFdOut - Supplies a pointer to receive the subordinate file descriptor.\r\n\r\nReturn Value:\r\n\r\n    Returns pid of newly forked process to the parent, zero to the child and\r\n    <0 on error.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return ForkPtyCommon(PtmFdOut, PtsFdOut, false);\r\n}\r\n\r\nint ForkPtyBackground(int* PtmFdOut, int* PtsFdOut, pid_t* ForegroundIdOut)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets up a new process as a background process using PtsFd as\r\n    its controlling terminal.\r\n\r\nArguments:\r\n\r\n    PtmFdOut - Supplies a pointer to receive the master file descriptor.\r\n\r\n    PtsFdOut - Supplies a pointer to receive the subordinate file descriptor.\r\n\r\n    ForegroundId - Supplies a pointer to receive the ID of the foreground\r\n        process.\r\n\r\nReturn Value:\r\n\r\n    Returns pid of newly forked process to the parent, zero to the child and\r\n    <0 on error.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int GrandChildPid;\r\n    int GrandChildStatus;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n    GrandChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    LxtCheckErrno(ChildPid = ForkPty(&PtmFd, &PtsFd));\r\n    if (ChildPid == 0)\r\n    {\r\n        *ForegroundIdOut = getpid();\r\n        LxtCheckErrno(GrandChildPid = fork());\r\n        if (GrandChildPid == 0)\r\n        {\r\n            LxtCheckErrno(setpgid(0, 0));\r\n        }\r\n        else\r\n        {\r\n            LxtCheckErrno(TEMP_FAILURE_RETRY(Result = waitpid(GrandChildPid, &GrandChildStatus, 0)));\r\n            LxtCheckResult(WIFEXITED(GrandChildStatus) ? 0 : -1);\r\n            LxtCheckResult((int)(char)WEXITSTATUS(GrandChildStatus));\r\n        }\r\n    }\r\n    else\r\n    {\r\n        *ForegroundIdOut = ChildPid;\r\n    }\r\n\r\n    *PtmFdOut = PtmFd;\r\n    PtmFd = -1;\r\n    *PtsFdOut = PtsFd;\r\n    PtsFd = -1;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    if ((ChildPid == 0) && (GrandChildPid > 0))\r\n    {\r\n        exit(Result);\r\n    }\r\n\r\n    return ChildPid;\r\n}\r\n\r\npid_t ForkPtyCommon(int* PtmFdOut, int* PtsFdOut, bool UseMasterEndpoint)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets up a new process as a foreground process using PtsFd as\r\n    its controlling terminal.\r\n\r\nArguments:\r\n\r\n    PtmFdOut - Supplies a pointer to receive the master file descriptor.\r\n\r\n    PtsFdOut - Supplies a pointer to receive the subordinate file descriptor.\r\n\r\n    UseMasterEndpoint - Supplies a flag indicating whether the master or\r\n        subordinate endpoint should be set as the controlling terminal.\r\n\r\nReturn Value:\r\n\r\n    Returns pid of newly forked process to the parent, zero to the child and\r\n    <0 on error.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int PtmFd;\r\n    int PtsFd;\r\n    int Result;\r\n    int SerialNumber;\r\n    pid_t SessionId;\r\n\r\n    //\r\n    // Initialize locals\r\n    //\r\n\r\n    ChildPid = -1;\r\n    PtmFd = -1;\r\n    PtsFd = -1;\r\n\r\n    //\r\n    // Open Master-Subordinate\r\n    //\r\n\r\n    LxtCheckErrno(OpenMasterSubordinate(&PtmFd, &PtsFd, NULL, &SerialNumber));\r\n    LxtLogInfo(\"Master opened at FD:%d\", PtmFd);\r\n    LxtLogInfo(\"Subordinate Serial Number: %d\", SerialNumber);\r\n    LxtLogInfo(\"Subordinate opened at FD:%d\", PtsFd);\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Move to a new session\r\n        //\r\n\r\n        LxtCheckErrno(SessionId = setsid());\r\n\r\n        //\r\n        // Set the fd as the controlling terminal for the session, calling\r\n        // again should not fail.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl((UseMasterEndpoint) ? PtmFd : PtsFd, TIOCSCTTY, (char*)NULL));\r\n        LxtCheckErrno(ioctl((UseMasterEndpoint) ? PtmFd : PtsFd, TIOCSCTTY, (char*)NULL));\r\n    }\r\n\r\n    *PtmFdOut = PtmFd;\r\n    PtmFd = -1;\r\n    *PtsFdOut = PtsFd;\r\n    PtsFd = -1;\r\n\r\nErrorExit:\r\n    if (PtmFd != -1)\r\n    {\r\n        close(PtmFd);\r\n    }\r\n\r\n    if (PtsFd != -1)\r\n    {\r\n        close(PtsFd);\r\n    }\r\n\r\n    return ChildPid;\r\n}\r\n\r\npid_t ForkPtyMaster(int* PtmFdOut, int* PtsFdOut)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets up a new process as a foreground process using PtmFd as\r\n    its controlling terminal.\r\n\r\nArguments:\r\n\r\n    PtmFdOut - Supplies a pointer to receive the master file descriptor.\r\n\r\n    PtsFdOut - Supplies a pointer to receive the subordinate file descriptor.\r\n\r\nReturn Value:\r\n\r\n    Returns pid of newly forked process to the parent, zero to the child and\r\n    <0 on error.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return ForkPtyCommon(PtmFdOut, PtsFdOut, true);\r\n}\r\n\r\nint RawInit(int Fd)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to set the FD for raw input/output.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    cc_t ControlArray[NCCS];\r\n    int Result;\r\n\r\n    //\r\n    // After the switch to RAW mode want no timeout and a minimum of 1 char.\r\n    //\r\n\r\n    Result = TerminalSettingsGetControlArray(Fd, ControlArray);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    ControlArray[VTIME] = 0;\r\n    ControlArray[VMIN] = 1;\r\n    Result = TerminalSettingsSetControlArray(Fd, ControlArray);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Disable echo, cannon and other flags. Set TOSTOP so signals are\r\n    // generated by default.\r\n    //\r\n\r\n    Result = TerminalSettingsSetLocalFlags(Fd, TOSTOP);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SimpleReadWriteCheck(int PtmFd, int PtsFd)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a simple read/write check on the master-subordinate\r\n    pseudo terminal pair. The check is as follows:\r\n    - Write to the master.\r\n    - Read from the subordinate. Read data should match the data written\r\n          by master.\r\n    - Write to the subordinate.\r\n    - Read data from the master. Data read from the mater should match\r\n          what was written by the subordinate.\r\n\r\nArguments:\r\n\r\n    PtmFd - Supplies the FD for the master.\r\n\r\n    PtsFd - Supplies the FD for the subordinate.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return SimpleReadWriteCheckEx(PtmFd, PtsFd, SimpleReadWriteForeground);\r\n}\r\n\r\nint SimpleReadWriteCheckEx(int PtmFd, int PtsFd, SIMPLE_READ_WRITE_MODE Mode)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs a simple read/write check on the master-subordinate\r\n    pseudo terminal pair. The check is as follows:\r\n    - Write to the master.\r\n    - Read from the subordinate. Read data should match the data written\r\n          by master.\r\n    - Write to the subordinate.\r\n    - Read data from the master. Data read from the mater should match\r\n          what was written by the subordinate.\r\n\r\nArguments:\r\n\r\n    PtmFd - Supplies the FD for the master.\r\n\r\n    PtsFd - Supplies the FD for the subordinate.\r\n\r\n    Mode - Supplies a value indicating whether this access is from foreground\r\n        or background, and whether signals are enabled.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int BytesReadWrite;\r\n    int ExpectedResult;\r\n    const char* Greetings = \"Hello there!!\\n\";\r\n    size_t GreetingsLength;\r\n    int PacketMode;\r\n    int PtmFlags;\r\n    char ReadBuffer[1024];\r\n    char* ReadBufferMaster;\r\n    const char* Reply = \"Hi, how are you?\\r\";\r\n    size_t ReplyLength;\r\n    int Result;\r\n    struct termios TiosMaster;\r\n    struct termios TiosSubordinate;\r\n\r\n    LxtCheckErrno(PtmFlags = fcntl(PtmFd, F_GETFL, 0));\r\n\r\n    memset(&TiosMaster, 0, sizeof(TiosMaster));\r\n    LxtCheckErrno(tcgetattr(PtmFd, &TiosMaster));\r\n    memset(&TiosSubordinate, 0, sizeof(TiosSubordinate));\r\n    LxtCheckErrno(tcgetattr(PtsFd, &TiosSubordinate));\r\n    LxtCheckMemoryEqual(&TiosMaster, &TiosSubordinate, sizeof(TiosMaster));\r\n    GreetingsLength = strlen(Greetings);\r\n    ReplyLength = strlen(Reply);\r\n    if (TiosSubordinate.c_lflag & ICANON)\r\n    {\r\n        LxtLogInfo(\"Canonical mode.\");\r\n    }\r\n    else\r\n    {\r\n        LxtLogInfo(\"Raw mode.\");\r\n        --GreetingsLength;\r\n        --ReplyLength;\r\n    }\r\n\r\n    LxtCheckErrno(ioctl(PtmFd, TIOCGPKT, &PacketMode));\r\n    if (PacketMode != 0)\r\n    {\r\n        LxtLogInfo(\"Packet mode enabled.\");\r\n        ReadBufferMaster = &ReadBuffer[1];\r\n    }\r\n    else\r\n    {\r\n        ReadBufferMaster = &ReadBuffer[0];\r\n    }\r\n\r\n    //\r\n    // Write the greetings message to the master.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing to master\");\r\n    ExpectedResult = GreetingsLength;\r\n    LxtCheckErrno(BytesReadWrite = write(PtmFd, Greetings, ExpectedResult));\r\n    LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    LxtLogInfo(\"Master(FD:%d) --> subordinate(FD:%d):%*s\", PtmFd, PtsFd, GreetingsLength, Greetings);\r\n\r\n    //\r\n    // Canonical mode should echo the input back to the master with a\r\n    // carriage-return and newline.\r\n    //\r\n\r\n    if (TiosSubordinate.c_lflag & ICANON)\r\n    {\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        if (PacketMode != 0)\r\n        {\r\n            LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n            if (BytesReadWrite > 0)\r\n            {\r\n                BytesReadWrite -= 1;\r\n            }\r\n        }\r\n\r\n        ReadBufferMaster[BytesReadWrite] = '\\0';\r\n        LxtLogInfo(\"Echo received by master(FD:%d):%s\", PtmFd, ReadBufferMaster);\r\n        LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBufferMaster[BytesReadWrite - 1], '\\n', '\\r');\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, (ExpectedResult + 1));\r\n        if ((ReadBufferMaster[BytesReadWrite - 1] != '\\n') || (ReadBufferMaster[BytesReadWrite - 2] != '\\r'))\r\n        {\r\n            LxtLogError(\"Echo to master(FD:%d) does not end with \\r\\n.\", PtmFd);\r\n\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        ReadBufferMaster[BytesReadWrite - 2] = '\\n';\r\n        if (memcmp(ReadBufferMaster, Greetings, ExpectedResult) != 0)\r\n        {\r\n            LxtLogError(\r\n                \"Echo to master(FD:%d) does not match what was \"\r\n                \"written.\",\r\n                PtmFd);\r\n\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Read from subordinate.\r\n    //\r\n\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    LxtLogInfo(\"Reading from subordinate\");\r\n    if (Mode == SimpleReadWriteForeground)\r\n    {\r\n        LxtCheckErrno(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        LxtLogInfo(\"Message received by subordinate(FD:%d):%s\", PtsFd, ReadBuffer);\r\n        LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBuffer[BytesReadWrite - 1], '\\n', '\\r');\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n        //\r\n        // Compare the messages.\r\n        //\r\n\r\n        if (memcmp(ReadBuffer, Greetings, BytesReadWrite) != 0)\r\n        {\r\n            LxtLogError(\r\n                \"Data read from subordinate(FD:%d) does not match what was \"\r\n                \"written by master(FD:%d).\",\r\n                PtsFd,\r\n                PtmFd);\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n    else if ((Mode == SimpleReadWriteBackgroundSignal) || (Mode == SimpleReadWriteBackgroundSignalNoStop))\r\n    {\r\n\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EINTR);\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtsFd, ReadBuffer, sizeof(ReadBuffer)), EIO);\r\n    }\r\n\r\n    //\r\n    // So far so good, now write a response from the subordinate.\r\n    //\r\n\r\n    LxtLogInfo(\"Subordinate(FD:%d) --> master(FD:%d):%*s\", PtsFd, PtmFd, ReplyLength, Reply);\r\n\r\n    ExpectedResult = ReplyLength;\r\n    if (Mode != SimpleReadWriteBackgroundSignal)\r\n    {\r\n        LxtCheckErrno(BytesReadWrite = write(PtsFd, Reply, ExpectedResult));\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, ExpectedResult);\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrnoFailure(BytesReadWrite = write(PtsFd, Reply, ExpectedResult), EINTR);\r\n        BytesReadWrite = ExpectedResult;\r\n        ExpectedResult = 0;\r\n    }\r\n\r\n    //\r\n    // Read from master.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading from master\");\r\n    memset(ReadBuffer, 0, sizeof(ReadBuffer));\r\n    if (Mode != SimpleReadWriteBackgroundSignal)\r\n    {\r\n        LxtCheckErrno(BytesReadWrite = read(PtmFd, ReadBuffer, sizeof(ReadBuffer)));\r\n        if (PacketMode != 0)\r\n        {\r\n            LxtCheckEqual(ReadBuffer[0], 0, \"%hhd\");\r\n            if (BytesReadWrite > 0)\r\n            {\r\n                BytesReadWrite -= 1;\r\n            }\r\n        }\r\n\r\n        ReadBufferMaster[BytesReadWrite] = '\\0';\r\n        LxtLogInfo(\"Reply received by master(FD:%d):%s\", PtmFd, ReadBufferMaster);\r\n        LxtLogInfo(\"Last character = %d [\\\\n = %d, \\\\r = %d]\", ReadBufferMaster[BytesReadWrite - 1], '\\n', '\\r');\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(fcntl(PtmFd, F_SETFL, (PtmFlags | O_NONBLOCK)));\r\n        LxtCheckErrnoFailure(BytesReadWrite = read(PtmFd, ReadBuffer, BytesReadWrite), EAGAIN);\r\n        BytesReadWrite = 0;\r\n    }\r\n\r\n    LxtCheckFnResults(\"read\", BytesReadWrite, ExpectedResult);\r\n\r\n    //\r\n    // Compare the messages.\r\n    //\r\n\r\n    if (memcmp(ReadBufferMaster, Reply, BytesReadWrite) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Data read from master(FD:%d) does not match what was \"\r\n            \"written by subordinate(FD:%d).\",\r\n            PtmFd,\r\n            PtsFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\nErrorExit:\r\n    fcntl(PtmFd, F_SETFL, PtmFlags);\r\n    return Result;\r\n}\r\n\r\nint TerminalSettingsGet(int Fd, cc_t* ControlArrayOut, tcflag_t* ControlFlagsOut, tcflag_t* InputFlagsOut, tcflag_t* LocalFlagsOut, tcflag_t* OutputFlagsOut)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to get the settings for the FD.\r\n    All of the *Out variables are optional.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    ControlArrayOut - Supplies an optional pointer to receive the NCCS element\r\n        control array.\r\n\r\n    ControlFlagsOut - Supplies an optional pointer to receive the terminal\r\n        control flags.\r\n\r\n    InputFlagsOut - Supplies an optional pointer to receive the terminal input\r\n        flags.\r\n\r\n    LocalFlagsOut - Supplies an optional pointer to receive the terminal local\r\n        flags.\r\n\r\n    OutputFlagsOut - Supplies an optional pointer to receive the terminal\r\n        output flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct termios Tios;\r\n\r\n    Result = tcgetattr(Fd, &Tios);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (ControlArrayOut != NULL)\r\n    {\r\n        memcpy(ControlArrayOut, Tios.c_cc, NCCS * sizeof(cc_t));\r\n    }\r\n\r\n    if (ControlFlagsOut != NULL)\r\n    {\r\n        *ControlFlagsOut = Tios.c_cflag;\r\n    }\r\n\r\n    if (InputFlagsOut != NULL)\r\n    {\r\n        *InputFlagsOut = Tios.c_iflag;\r\n    }\r\n\r\n    if (LocalFlagsOut != NULL)\r\n    {\r\n        *LocalFlagsOut = Tios.c_lflag;\r\n    }\r\n\r\n    if (OutputFlagsOut != NULL)\r\n    {\r\n        *OutputFlagsOut = Tios.c_oflag;\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TerminalSettingsGetControlArray(int Fd, cc_t* ControlArrayOut)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to get the NCCS element control array.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    ControlArrayOut - Supplies a pointer to receive the NCCS element control\r\n        array.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return TerminalSettingsGet(Fd, ControlArrayOut, NULL, NULL, NULL, NULL);\r\n}\r\n\r\nint TerminalSettingsGetControlFlags(int Fd, tcflag_t* ControlFlagsOut)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to get the local flags for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    ControlFlagsOut - Supplies a pointer to receive the terminal control flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return TerminalSettingsGet(Fd, NULL, ControlFlagsOut, NULL, NULL, NULL);\r\n}\r\n\r\nint TerminalSettingsGetInputFlags(int Fd, tcflag_t* InputFlagsOut)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to get the input flags for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    InputFlagsOut - Supplies a pointer to receive the terminal input flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return TerminalSettingsGet(Fd, NULL, NULL, InputFlagsOut, NULL, NULL);\r\n}\r\n\r\nint TerminalSettingsGetLocalFlags(int Fd, tcflag_t* LocalFlagsOut)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to get the local flags for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    LocalFlagsOut - Supplies a pointer to receive the terminal local flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return TerminalSettingsGet(Fd, NULL, NULL, NULL, LocalFlagsOut, NULL);\r\n}\r\n\r\nint TerminalSettingsGetOutputFlags(int Fd, tcflag_t* OutputFlagsOut)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to get the output flags for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    OutputFlagsOut - Supplies a pointer to receive the terminal output flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return TerminalSettingsGet(Fd, NULL, NULL, NULL, NULL, OutputFlagsOut);\r\n}\r\n\r\nint TerminalSettingsSet(int Fd, cc_t* ControlArray, tcflag_t ControlFlags, tcflag_t InputFlags, tcflag_t LocalFlags, tcflag_t OutputFlags)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to update the settings for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    ControlArray - Supplies a pointer to the new NCCS element control array.\r\n\r\n    ControlFlags - Supplies the new terminal control flags.\r\n\r\n    InputFlagsOut - Supplies the new terminal input flags.\r\n\r\n    LocalFlagsOut - Supplies the new terminal local flags.\r\n\r\n    OutputFlagsOut - Supplies the new terminal output flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct termios Tios = {0};\r\n\r\n    memcpy(Tios.c_cc, ControlArray, NCCS * sizeof(cc_t));\r\n    Tios.c_cflag = ControlFlags;\r\n    Tios.c_iflag = InputFlags;\r\n    Tios.c_lflag = LocalFlags;\r\n    Tios.c_oflag = OutputFlags;\r\n    Result = tcsetattr(Fd, TCSANOW, &Tios);\r\n    return Result;\r\n}\r\n\r\nint TerminalSettingsSetControlArray(int Fd, cc_t* ControlArray)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to set the control array for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    ControlArray - Supplies the new control array.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct termios Tios = {0};\r\n\r\n    Result = TerminalSettingsGet(Fd, NULL, &Tios.c_cflag, &Tios.c_iflag, &Tios.c_lflag, &Tios.c_oflag);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = TerminalSettingsSet(Fd, ControlArray, Tios.c_cflag, Tios.c_iflag, Tios.c_lflag, Tios.c_oflag);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TerminalSettingsSetControlFlags(int Fd, tcflag_t ControlFlags)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to set the control flags for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    ControlFlags - Supplies the new control flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct termios Tios = {0};\r\n\r\n    Result = TerminalSettingsGet(Fd, Tios.c_cc, NULL, &Tios.c_iflag, &Tios.c_lflag, &Tios.c_oflag);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = TerminalSettingsSet(Fd, Tios.c_cc, ControlFlags, Tios.c_iflag, Tios.c_lflag, Tios.c_oflag);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TerminalSettingsSetInputFlags(int Fd, tcflag_t InputFlags)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to set the input flags for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    InputFlags - Supplies the new input flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct termios Tios = {0};\r\n\r\n    Result = TerminalSettingsGet(Fd, Tios.c_cc, &Tios.c_cflag, NULL, &Tios.c_lflag, &Tios.c_oflag);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = TerminalSettingsSet(Fd, Tios.c_cc, Tios.c_cflag, InputFlags, Tios.c_lflag, Tios.c_oflag);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TerminalSettingsSetLocalFlags(int Fd, tcflag_t LocalFlags)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to set the local flags for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    LocalFlags - Supplies the new local flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct termios Tios = {0};\r\n\r\n    Result = TerminalSettingsGet(Fd, Tios.c_cc, &Tios.c_cflag, &Tios.c_iflag, NULL, &Tios.c_oflag);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = TerminalSettingsSet(Fd, Tios.c_cc, Tios.c_cflag, Tios.c_iflag, LocalFlags, Tios.c_oflag);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TerminalSettingsSetOutputFlags(int Fd, tcflag_t OutputFlags)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will use termios to set the output flags for the FD.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the FD.\r\n\r\n    OutputFlags - Supplies the new output flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct termios Tios = {0};\r\n\r\n    Result = TerminalSettingsGet(Fd, Tios.c_cc, &Tios.c_cflag, &Tios.c_iflag, &Tios.c_lflag, NULL);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = TerminalSettingsSet(Fd, Tios.c_cc, Tios.c_cflag, Tios.c_iflag, Tios.c_lflag, OutputFlags);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint WriteReadFdCommon(int WriteFd, size_t WriteSizes[], size_t NumWriteSizes, int ReadFd, size_t ReadSizes[], size_t NumReadSizes)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine performs write operation of given sizes to the WriteFd and\r\n    reads from the ReadFd for the specified sizes. Each write is expected to\r\n    succeed for the given size and each read is expected to succeed for the\r\n    given size. This routine will also validate that the data read aligns up\r\n    with the data written. All the writes will be performed before any of the\r\n    reads.\r\n\r\nArguments:\r\n\r\n    WriteFd - Supplies the FD on which the write operation should be called.\r\n\r\n    WriteSizes - Supplies the array of sizes for the write operation..\r\n\r\n    NumWriteSizes - Supplies the number of elements in the write size array.\r\n\r\n    ReadFd - Supplies the FD on which the read operation should be called.\r\n\r\n    ReadSizes - Supplies the array of sizes for the read operation..\r\n\r\n    NumReadSizes - Supplies the number of elements in the read size array.\r\n\r\nReturn Value:\r\n\r\n    0 on success, error code on failure.\r\n--*/\r\n\r\n{\r\n\r\n    char* AccumulatedReadBuffer;\r\n    char* AccumulatedWriteBuffer;\r\n    ssize_t BytesReadWrite;\r\n    size_t Itr;\r\n    size_t Offset;\r\n    char* ReadBuffer;\r\n    int Result;\r\n    size_t TotalReadSize;\r\n    size_t TotalWriteSize;\r\n    char* WriteBuffer;\r\n\r\n    AccumulatedReadBuffer = NULL;\r\n    AccumulatedWriteBuffer = NULL;\r\n    ReadBuffer = NULL;\r\n    WriteBuffer = NULL;\r\n    TotalWriteSize = 0;\r\n    TotalReadSize = 0;\r\n\r\n    //\r\n    // Calculate the size of total number of bytes to be written and read.\r\n    //\r\n\r\n    for (Itr = 0; Itr < NumWriteSizes; Itr += 1)\r\n    {\r\n        TotalWriteSize += WriteSizes[Itr];\r\n    }\r\n\r\n    for (Itr = 0; Itr < NumReadSizes; Itr += 1)\r\n    {\r\n        TotalReadSize += ReadSizes[Itr];\r\n    }\r\n\r\n    //\r\n    // Allocate memory to hold the data for the accumulated writes and reads.\r\n    //\r\n\r\n    AccumulatedWriteBuffer = calloc(TotalWriteSize, 1);\r\n    if (AccumulatedWriteBuffer == NULL)\r\n    {\r\n        LxtLogError(\"Failed to allocate memory for Write Buffer\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    AccumulatedReadBuffer = calloc(TotalReadSize, 1);\r\n    if (AccumulatedReadBuffer == NULL)\r\n    {\r\n        LxtLogError(\"Failed to allocate memory for Read Buffer\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // First perform all the writes.\r\n    //\r\n\r\n    Offset = 0;\r\n    for (Itr = 0; Itr < NumWriteSizes; Itr += 1)\r\n    {\r\n        WriteBuffer = calloc(WriteSizes[Itr], 1);\r\n        if (WriteBuffer == NULL)\r\n        {\r\n            Result = ENOMEM;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtCheckErrno(GetRandomMessage(WriteBuffer, WriteSizes[Itr], FALSE));\r\n        LxtCheckErrno(BytesReadWrite = write(WriteFd, WriteBuffer, WriteSizes[Itr]));\r\n\r\n        LxtCheckFnResults(\"write\", BytesReadWrite, (ssize_t)WriteSizes[Itr]);\r\n        memcpy(&AccumulatedWriteBuffer[Offset], WriteBuffer, WriteSizes[Itr]);\r\n\r\n        Offset += WriteSizes[Itr];\r\n        free(WriteBuffer);\r\n        WriteBuffer = NULL;\r\n    }\r\n\r\n    //\r\n    // On Ubuntu16 pty processing is asynchronous so pause a second to give the\r\n    // writes time to be processed.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Now read the data previously written.\r\n    //\r\n\r\n    Offset = 0;\r\n    for (Itr = 0; Itr < NumReadSizes; Itr += 1)\r\n    {\r\n        ReadBuffer = calloc(ReadSizes[Itr], 1);\r\n        if (ReadBuffer == NULL)\r\n        {\r\n            Result = ENOMEM;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtCheckErrno(BytesReadWrite = read(ReadFd, ReadBuffer, ReadSizes[Itr]));\r\n\r\n        LxtCheckFnResults(\"read\", BytesReadWrite, (ssize_t)ReadSizes[Itr]);\r\n        memcpy(&AccumulatedReadBuffer[Offset], ReadBuffer, ReadSizes[Itr]);\r\n        Offset += ReadSizes[Itr];\r\n        free(ReadBuffer);\r\n        ReadBuffer = NULL;\r\n    }\r\n\r\n    //\r\n    // Data read should align up with the previously written data.\r\n    //\r\n\r\n    if (memcmp(AccumulatedWriteBuffer, AccumulatedReadBuffer, min(TotalWriteSize, TotalReadSize)) != 0)\r\n    {\r\n\r\n        LxtLogError(\r\n            \"Data read from FD:%d does not match what was \"\r\n            \"written by FD:%d.\",\r\n            ReadFd,\r\n            WriteFd);\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    /*\r\n    LxtLogInfo(\"Data Written: \");\r\n    DumpBuffer(AccumulatedWriteBuffer, TotalWriteSize);\r\n    LxtLogInfo(\"Data Read: \");\r\n    DumpBuffer(AccumulatedReadBuffer, TotalReadSize);\r\n    */\r\n\r\nErrorExit:\r\n\r\n    if (AccumulatedReadBuffer != NULL)\r\n    {\r\n        free(AccumulatedReadBuffer);\r\n    }\r\n\r\n    if (AccumulatedWriteBuffer != NULL)\r\n    {\r\n        free(AccumulatedWriteBuffer);\r\n    }\r\n\r\n    if (ReadBuffer != NULL)\r\n    {\r\n        free(ReadBuffer);\r\n    }\r\n\r\n    if (WriteBuffer != NULL)\r\n    {\r\n        free(WriteBuffer);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/dev_pt_common.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    dev_pt_common.h\r\n\r\nAbstract:\r\n\r\n    This file is a test for the Pseudo Terminals: /dev/ptmx, /dev/pts/<n>\r\n    devices.\r\n\r\n--*/\r\n\r\n#ifndef _DEV_PT_COMMON\r\n#define _DEV_PT_COMMON\r\n\r\n#include <errno.h>\r\n#include <sys/types.h>\r\n#include <sys/wait.h>\r\n#include <sys/cdefs.h>\r\n#include <linux/limits.h>\r\n#include <fcntl.h>\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <termios.h>\r\n#include <pthread.h>\r\n#include <stdbool.h>\r\n#include <unistd.h>\r\n#include <pty.h>\r\n#include <sys/ioctl.h>\r\n#include <sys/uio.h>\r\n#include <termios.h>\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n\r\n//\r\n// min, max macros\r\n//\r\n\r\n#define min(a, b) (((a) < (b)) ? (a) : (b))\r\n#define max(a, b) (((a) > (b)) ? (a) : (b))\r\n\r\n//\r\n// TRUE, FALSE macros\r\n//\r\n\r\n#define FALSE 0\r\n#define TRUE 1\r\n\r\n#define PTS_DEV_NAME_BUFFER_SIZE 50\r\n\r\n#define IS_CONTROL_CHAR_ECHO_STRING(s, c) (((s)[0] == '^') && (((s)[1] > 0x40)) && (((s)[1] - 0x40) == (c)))\r\n\r\n#define LxtCheckFnResults(fn, result, expectedresult) \\\r\n    { \\\r\n        if ((result) != (expectedresult)) \\\r\n        { \\\r\n            LxtLogError(\"Unexpected results. %s returned with result:%d, expected:%d.\", #fn, result, expectedresult); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#define LxtClose(_fd) \\\r\n    { \\\r\n        if ((_fd) >= 0) \\\r\n        { \\\r\n            LxtCheckErrno(close(_fd)); \\\r\n            (_fd) = -1; \\\r\n        } \\\r\n    }\r\n\r\n#ifndef STDIN\r\n#define STDIN 0\r\n#endif\r\n\r\n#ifndef STDOUT\r\n#define STDOUT 1\r\n#endif\r\n\r\ntypedef enum _SIMPLE_READ_WRITE_MODE\r\n{\r\n    SimpleReadWriteForeground,\r\n    SimpleReadWriteBackgroundNoSignal,\r\n    SimpleReadWriteBackgroundSignal,\r\n    SimpleReadWriteBackgroundSignalNoStop\r\n} SIMPLE_READ_WRITE_MODE;\r\n\r\n//\r\n// Functions.\r\n//\r\n\r\nvoid DumpBuffer(const char Data[], size_t DataSize);\r\n\r\nint GetPtSerialNumFromDeviceString(const char PtsNameString[]);\r\n\r\nint GetRandomMessage(char Message[], size_t MessageSize, bool CompleteMessage);\r\n\r\nint OpenMasterSubordinate(int* PtmFd, int* PtsFd, char* PtsDevName, int* SerialNumber);\r\n\r\npid_t ForkPty(int* PtmFdOut, int* PtsFdOut);\r\n\r\npid_t ForkPtyBackground(int* PtmFdOut, int* PtsFdOut, pid_t* ForegroundIdOut);\r\n\r\npid_t ForkPtyMaster(int* PtmFdOut, int* PtsFdOut);\r\n\r\nvoid PtSignalHandler(int signal, siginfo_t* signalInfo, void* context);\r\n\r\nint RawInit(int Fd);\r\n\r\nint SimpleReadWriteCheck(int PtmFd, int PtsFd);\r\n\r\nint SimpleReadWriteCheckEx(int PtmFd, int PtsFd, SIMPLE_READ_WRITE_MODE Mode);\r\n\r\nint TerminalSettingsGet(int Fd, cc_t* ControlArrayOut, tcflag_t* ControlFlagsOut, tcflag_t* InputFlagsOut, tcflag_t* LocalFlagsOut, tcflag_t* OutputFlagsOut);\r\n\r\nint TerminalSettingsGetControlArray(int Fd, cc_t* ControlArrayOut);\r\n\r\nint TerminalSettingsGetControlFlags(int Fd, tcflag_t* ControlFlagsOut);\r\n\r\nint TerminalSettingsGetInputFlags(int Fd, tcflag_t* InputFlagsOut);\r\n\r\nint TerminalSettingsGetLocalFlags(int Fd, tcflag_t* LocalFlagsOut);\r\n\r\nint TerminalSettingsGetOutputFlags(int Fd, tcflag_t* OutputFlagsOut);\r\n\r\nint TerminalSettingsSet(int Fd, cc_t* ControlArray, tcflag_t ControlFlags, tcflag_t InputFlags, tcflag_t LocalFlags, tcflag_t OutputFlags);\r\n\r\nint TerminalSettingsSetControlArray(int Fd, cc_t* ControlArray);\r\n\r\nint TerminalSettingsSetControlFlags(int Fd, tcflag_t ControlFlags);\r\n\r\nint TerminalSettingsSetInputFlags(int Fd, tcflag_t InputFlags);\r\n\r\nint TerminalSettingsSetLocalFlags(int Fd, tcflag_t LocalFlags);\r\n\r\nint TerminalSettingsSetOutputFlags(int Fd, tcflag_t OutputFlags);\r\n\r\nint WriteReadFdCommon(int WriteFd, size_t WriteSizes[], size_t NumWriteSizes, int ReadFd, size_t ReadSizes[], size_t NumReadSizes);\r\n\r\n#endif // _DEV_PT_COMMON\r\n"
  },
  {
    "path": "test/linux/unit_tests/drvfs.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    drvfs.c\r\n\r\nAbstract:\r\n\r\n    This file contains tests for the drvfs file system plugin.\r\n\r\n    The drvfs tests are kept separate from other file system tests to make it\r\n    possible to run the tests in \"fallback mode,\" the mode drvfs operates in\r\n    when NtQueryInformationByName or FILE_STAT_INFORMATION are not supported\r\n    by the underlying file system.\r\n\r\n    Additionally, most of these tests do not pass on native Linux, so keeping\r\n    the drvfs tests apart makes it easier to validate the other file system\r\n    tests on native Linux.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/uio.h>\r\n#include <sys/syscall.h>\r\n#include <sys/inotify.h>\r\n#include <sys/mount.h>\r\n#include <sys/signalfd.h>\r\n#include <sys/sysmacros.h>\r\n#include <sys/wait.h>\r\n#include <sys/mman.h>\r\n#include <fcntl.h>\r\n#include <dirent.h>\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <libmount/libmount.h>\r\n#include \"lxtfs.h\"\r\n#include \"lxtmount.h\"\r\n\r\n#define LXT_NAME_FORMAT \"drvfs%d\"\r\n\r\n#define DRVFS_DRIVE \"C:\"\r\n#define DRVFS_FAT_MOUNT_POINT \"lxss_fat\"\r\n#define DRVFS_FAT_DRIVE \"C:/\" DRVFS_FAT_MOUNT_POINT\r\n#define DRVFS_UNC_PATH \"//localhost/C$\"\r\n#define DRVFS_FAT_TEST_MODE (3)\r\n#define DRVFS_SMB_TEST_MODE (4)\r\n#define DRVFS_METADATA_TEST_MODE (5)\r\n#define DRVFS_REFS_TEST_MODE (6)\r\n#define DRVFS_REFS_MOUNT_POINT \"lxss_refs\"\r\n#define DRVFS_REFS_DRIVE \"C:/\" DRVFS_REFS_MOUNT_POINT\r\n#define DRVFS_FS_TYPE \"drvfs\"\r\n#define DRVFS_MOUNT_OPTIONS (MS_NOATIME)\r\n#define DRVFS_PREFIX \"/mnt/c\"\r\n#define DRVFS_CS_PREFIX DRVFS_PREFIX \"/casesensitive\"\r\n#define DRVFS_BASIC_PREFIX DRVFS_PREFIX \"/basictest\"\r\n#define DRVFS_RENAME_PREFIX DRVFS_PREFIX \"/renametest\"\r\n#define DRVFS_REPARSE_PREFIX DRVFS_PREFIX \"/reparsetest\"\r\n#define DRVFS_MOUNT_TEST_DIR \"/data/mount_test\"\r\n#define DRVFS_ACCESS_TEST_DIR DRVFS_PREFIX \"/drvfstest\"\r\n#define DRVFS_ACCESS_RWX_TEST_FILE DRVFS_ACCESS_TEST_DIR \"/rwx\"\r\n#define DRVFS_ACCESS_READONLY_TEST_FILE DRVFS_ACCESS_TEST_DIR \"/readonly\"\r\n#define DRVFS_ACCESS_WRITEONLY_TEST_FILE DRVFS_ACCESS_TEST_DIR \"/writeonly\"\r\n#define DRVFS_ACCESS_EXECUTEONLY_TEST_FILE DRVFS_ACCESS_TEST_DIR \"/executeonly\"\r\n#define DRVFS_ACCESS_EXECUTEONLY_TEST_DIR DRVFS_ACCESS_TEST_DIR \"/executeonlydir\"\r\n#define DRVFS_ACCESS_READONLYATTR_TEST_FILE DRVFS_ACCESS_TEST_DIR \"/readonlyattr\"\r\n#define DRVFS_ACCESS_READONLYATTRDEL_TEST_FILE DRVFS_ACCESS_TEST_DIR \"/readonlyattrdel\"\r\n#define DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD DRVFS_ACCESS_EXECUTEONLY_TEST_DIR \"/child\"\r\n#define DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2 DRVFS_ACCESS_EXECUTEONLY_TEST_DIR \"/child2\"\r\n#define DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD_LINK DRVFS_ACCESS_EXECUTEONLY_TEST_DIR \"/link\"\r\n#define DRVFS_ACCESS_READONLY_TEST_DIR DRVFS_ACCESS_TEST_DIR \"/noexecutedir\"\r\n#define DRVFS_INOTIFY_TEST_BASE_DIR DRVFS_PREFIX \"/inotify_test/\"\r\n#define DRVFS_UTIME_TEST_DIR DRVFS_PREFIX \"/utimensat_test\"\r\n#define DRVFS_WRITEV_TEST_DIR DRVFS_PREFIX \"/writev_test\"\r\n#define DRVFS_RENAMEAT_TEST_DIR DRVFS_PREFIX \"/renameat_test\"\r\n#define DRVFS_CASE_INSENSITIVE_TEST_DIR DRVFS_PREFIX \"/case_insensitive_test\"\r\n#define DRVFS_UNSUPPORTED_TEST_DIR DRVFS_PREFIX \"/unsupported_test\"\r\n#define DRVFS_HARDLINK_TEST_DIR DRVFS_PREFIX \"/hardlink_test\"\r\n#define DRVFS_DELETELOOP_PREFIX DRVFS_PREFIX \"/deleteloop\"\r\n#define DRVFS_GETDENTS_PREFIX DRVFS_PREFIX \"/getdents\"\r\n#define DRVFS_SYMLINK_TEST_DIR DRVFS_PREFIX \"/symlink\"\r\n#define DRVFS_METADATA_TEST_DIR DRVFS_PREFIX \"/metadatatest\"\r\n#define DRVFS_ESCAPE_TEST_DIR DRVFS_PREFIX \"/escaped\"\r\n#define DRVFS_ESCAPE_TEST_CHILD_NAME \"\\\\:\\b\\u8a9e\\uefff\\uf025\\uf100\\ufb00\\uf02f\"\r\n#define DRVFS_ESCAPE_TEST_CHILD DRVFS_ESCAPE_TEST_DIR \"/\" DRVFS_ESCAPE_TEST_CHILD_NAME\r\n#define DRVFS_ESCAPE_TEST_CHILD_ESCAPED DRVFS_ESCAPE_TEST_DIR \"/\\uf05c\\uf03a\\uf008\\u8a9e\\uefff\\uf025\\uf100\\ufb00\\uf02f\"\r\n\r\n//\r\n// The following macros are used by TestInotifyComprehensiveDrvfs.\r\n//\r\n\r\n#define DRVFS_INOTIFY_DIR_1 \"a/\"\r\n#define DRVFS_INOTIFY_DIR_2 \"a/b/\"\r\n#define DRVFS_INOTIFY_DIR_3 \"a/b/c/\"\r\n#define DRVFS_INOTIFY_FILE_1_NAME_ONLY \"a.txt\"\r\n#define DRVFS_INOTIFY_FILE_2_NAME_ONLY \"b.txt\"\r\n#define DRVFS_INOTIFY_FILE_3_NAME_ONLY \"c.txt\"\r\n#define DRVFS_INOTIFY_FILE_1 DRVFS_INOTIFY_DIR_3 DRVFS_INOTIFY_FILE_1_NAME_ONLY\r\n#define DRVFS_INOTIFY_FILE_2 DRVFS_INOTIFY_DIR_3 DRVFS_INOTIFY_FILE_2_NAME_ONLY\r\n#define DRVFS_INOTIFY_FILE_3 DRVFS_INOTIFY_DIR_3 DRVFS_INOTIFY_FILE_3_NAME_ONLY\r\n\r\n//\r\n// The following macros are used by TestInotifyStressUnlinkRenameDrvfs.\r\n//\r\n\r\n#define DRVFS_INOTIFY_STRESS_DIR \"stress/\"\r\n#define DRVFS_INOTIFY_STRESS_NUM_FILES 2\r\n#define DRVFS_INOTIFY_STRESS_NUM_TESTS 1000\r\n\r\n#define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)\r\n#define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)\r\n#define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)\r\n\r\n#define DRVFS_EXECVE_TEST_RESULT (123)\r\n\r\nint DrvFsCheckMode(const char* Filename, mode_t ExpectedMode);\r\n\r\nint DrvFsCheckStat(const char* Filename, uid_t ExpectedUid, gid_t ExpectedGid, mode_t ExpectedMode, dev_t ExpectedDevice);\r\n\r\nint DrvFsParseArgs(int Argc, char* Argv[], LXT_ARGS* Args);\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestAccess;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestBadMetadata;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestBasic;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestBlockCount;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestCaseSensitivity;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestCaseSensitivityRoot;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestDeleteCurrentWorkingDirectory;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestDeleteLoop;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestDeleteOpenFile;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestEscapedNames;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestExecve;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestFatCaseInsensitive;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestFatJunction;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestFatUnsupported;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestFatUtimensat;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestFatWslPath;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestFstat;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestStatx;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestGetDents64Alignment;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestGetDentsAlignment;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestHardLinks;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestHiddenLxFsDirs;\r\n\r\nint DrvFsTestHiddenLxFsDirsHelper(const char* Child, BOOLEAN DirectChild);\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestInotifyBasic;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestInotifyEpoll;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestInotifyPosixUnlinkRename;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestInotifyStressUnlinkRename;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestInotifyUnmountBind;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestLookupPath;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestMetadata;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestReFsWslPath;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestRename;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestRenameAt;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestRenameDir;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestReopenUnlinked;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestReparse;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestSeek;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestDirSeek;\r\n\r\nint DrvFsTestSeekHelper(int Fd, off_t Offset, int Whence, off_t ExpectedOffset, const char* TestData, int TestDataSize);\r\n\r\nint DrvFsTestSetup(PLXT_ARGS Args, int TestMode);\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestSmbUtimensat;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestSmbUnsupported;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestSmbWslPath;\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestSymlink;\r\n\r\nint DrvFsTestSymlinkHelper(char* Target, char* Path);\r\n\r\nint DrvFsTestUnsupportedCommon(int TestMode);\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestUtimensat;\r\n\r\nint DrvFsTestUtimensatCommon(int Flags);\r\n\r\nLXT_VARIATION_HANDLER DrvFsTestWritev;\r\n\r\n//\r\n// Globals.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"DrvFs - basic\", DrvFsTestBasic},\r\n    {\"DrvFs - lookup by path\", DrvFsTestLookupPath},\r\n    {\"DrvFs - writev\", DrvFsTestWritev},\r\n    {\"DrvFs - rename\", DrvFsTestRename},\r\n    {\"DrvFs - renameat\", DrvFsTestRenameAt},\r\n    {\"DrvFs - rename directory\", DrvFsTestRenameDir},\r\n    {\"DrvFs - deleting an open file\", DrvFsTestDeleteOpenFile},\r\n    {\"DrvFs - deleting the working directory\", DrvFsTestDeleteCurrentWorkingDirectory},\r\n    {\"DrvFs - case-sensitivity\", DrvFsTestCaseSensitivity},\r\n    {\"DrvFs - case-sensitivity (drive root)\", DrvFsTestCaseSensitivityRoot},\r\n    {\"DrvFs - reparse points\", DrvFsTestReparse},\r\n    {\"DrvFs - access checks\", DrvFsTestAccess},\r\n    {\"DrvFs - execve\", DrvFsTestExecve},\r\n    {\"DrvFs - hidden lxfs directories\", DrvFsTestHiddenLxFsDirs},\r\n    {\"DrvFs - inotify with epoll\", DrvFsTestInotifyEpoll},\r\n    {\"DrvFs - inotify watching basic paths\", DrvFsTestInotifyBasic},\r\n    {\"DrvFs - inotify unmounting of a bind mount\", DrvFsTestInotifyUnmountBind},\r\n    {\"DrvFs - inotify POSIX unlink/rename\", DrvFsTestInotifyPosixUnlinkRename},\r\n    {\"DrvFs - inotify stress test with unlink and rename\", DrvFsTestInotifyStressUnlinkRename},\r\n    {\"DrvFs - utimensat\", DrvFsTestUtimensat},\r\n    {\"DrvFs - hard links\", DrvFsTestHardLinks},\r\n    {\"DrvFs - block count\", DrvFsTestBlockCount},\r\n    {\"DrvFs - fstat\", DrvFsTestFstat},\r\n    {\"DrvFs - statx\", DrvFsTestStatx},\r\n    {\"DrvFs - reopen unlinked file\", DrvFsTestReopenUnlinked},\r\n    {\"DrvFs - delete loop\", DrvFsTestDeleteLoop},\r\n    {\"DrvFs - seek\", DrvFsTestSeek},\r\n    {\"DrvFs - dir seek\", DrvFsTestDirSeek},\r\n#ifdef __NR_getdents\r\n    {\"DrvFs - getdents alignment\", DrvFsTestGetDentsAlignment},\r\n#endif\r\n    {\"DrvFs - getdents64 alignment\", DrvFsTestGetDents64Alignment},\r\n    {\"DrvFs - LX and NT symlink creation\", DrvFsTestSymlink},\r\n    {\"DrvFs - escaped names\", DrvFsTestEscapedNames}};\r\n\r\n//\r\n// The following variations apply to FAT volumes.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtFatVariations[] = {\r\n    {\"DrvFs - basic\", DrvFsTestBasic},\r\n    {\"DrvFs - lookup by path\", DrvFsTestLookupPath},\r\n    {\"DrvFs - writev\", DrvFsTestWritev},\r\n    {\"DrvFs - rename\", DrvFsTestRename},\r\n    {\"DrvFs - renameat\", DrvFsTestRenameAt},\r\n    {\"DrvFs - inotify with epoll\", DrvFsTestInotifyEpoll},\r\n    {\"DrvFs - inotify unmounting of a bind mount\", DrvFsTestInotifyUnmountBind},\r\n    {\"DrvFs - block count\", DrvFsTestBlockCount},\r\n    {\"DrvFs - FAT32 case-insensitive\", DrvFsTestFatCaseInsensitive},\r\n    {\"DrvFs - FAT32 unsupported features\", DrvFsTestFatUnsupported},\r\n    {\"DrvFs - FAT32 utimensat\", DrvFsTestFatUtimensat},\r\n    {\"DrvFs - FAT32 mount point junction\", DrvFsTestFatJunction},\r\n    {\"DrvFs - fstat\", DrvFsTestFstat},\r\n    {\"DrvFs - delete loop\", DrvFsTestDeleteLoop},\r\n    {\"DrvFs - seek\", DrvFsTestSeek},\r\n#ifdef __NR_getdents\r\n    {\"DrvFs - getdents alignment\", DrvFsTestGetDentsAlignment},\r\n#endif\r\n    {\"DrvFs - getdents64 alignment\", DrvFsTestGetDents64Alignment},\r\n    {\"DrvFs - escaped names\", DrvFsTestEscapedNames},\r\n    {\"DrvFs - wslpath NTFS directory mount\", DrvFsTestFatWslPath}};\r\n\r\n//\r\n// The following variations apply to SMB shares.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtSmbVariations[] = {\r\n    {\"DrvFs - basic\", DrvFsTestBasic},\r\n    {\"DrvFs - lookup by path\", DrvFsTestLookupPath},\r\n    {\"DrvFs - writev\", DrvFsTestWritev},\r\n    {\"DrvFs - rename\", DrvFsTestRename},\r\n    {\"DrvFs - renameat\", DrvFsTestRenameAt},\r\n    {\"DrvFs - hard links\", DrvFsTestHardLinks},\r\n    {\"DrvFs - SMB case-insensitive\", DrvFsTestFatCaseInsensitive},\r\n    {\"DrvFs - SMB unsupported features\", DrvFsTestSmbUnsupported},\r\n    {\"DrvFs - SMB utimensat\", DrvFsTestSmbUtimensat},\r\n    {\"DrvFs - fstat\", DrvFsTestFstat},\r\n    {\"DrvFs - delete loop\", DrvFsTestDeleteLoop},\r\n    {\"DrvFs - seek\", DrvFsTestSeek},\r\n#ifdef __NR_getdents\r\n    {\"DrvFs - getdents alignment\", DrvFsTestGetDentsAlignment},\r\n#endif\r\n    {\"DrvFs - getdents64 alignment\", DrvFsTestGetDents64Alignment},\r\n    {\"DrvFs - escaped names\", DrvFsTestEscapedNames},\r\n    {\"DrvFs - wslpath UNC\", DrvFsTestSmbWslPath}};\r\n\r\n//\r\n// The following variations apply when metadata is on.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtMetadataVariations[] = {\r\n    {\"DrvFs - basic\", DrvFsTestBasic},\r\n    {\"DrvFs - lookup by path\", DrvFsTestLookupPath},\r\n    {\"DrvFs - writev\", DrvFsTestWritev},\r\n    {\"DrvFs - rename\", DrvFsTestRename},\r\n    {\"DrvFs - renameat\", DrvFsTestRenameAt},\r\n    {\"DrvFs - rename directory\", DrvFsTestRenameDir},\r\n    {\"DrvFs - deleting an open file\", DrvFsTestDeleteOpenFile},\r\n    {\"DrvFs - deleting the working directory\", DrvFsTestDeleteCurrentWorkingDirectory},\r\n    {\"DrvFs - case-sensitivity\", DrvFsTestCaseSensitivity},\r\n    {\"DrvFs - case-sensitivity (drive root)\", DrvFsTestCaseSensitivityRoot},\r\n    {\"DrvFs - reparse points\", DrvFsTestReparse},\r\n    {\"DrvFs - access checks\", DrvFsTestAccess},\r\n    {\"DrvFs - execve\", DrvFsTestExecve},\r\n    {\"DrvFs - hidden lxfs directories\", DrvFsTestHiddenLxFsDirs},\r\n    {\"DrvFs - inotify with epoll\", DrvFsTestInotifyEpoll},\r\n    {\"DrvFs - inotify watching basic paths\", DrvFsTestInotifyBasic},\r\n    {\"DrvFs - inotify unmounting of a bind mount\", DrvFsTestInotifyUnmountBind},\r\n    {\"DrvFs - inotify POSIX unlink/rename\", DrvFsTestInotifyPosixUnlinkRename},\r\n    {\"DrvFs - inotify stress test with unlink and rename\", DrvFsTestInotifyStressUnlinkRename},\r\n    {\"DrvFs - utimensat\", DrvFsTestUtimensat},\r\n    {\"DrvFs - hard links\", DrvFsTestHardLinks},\r\n    {\"DrvFs - block count\", DrvFsTestBlockCount},\r\n    {\"DrvFs - fstat\", DrvFsTestFstat},\r\n    {\"DrvFs - statx\", DrvFsTestStatx},\r\n    {\"DrvFs - reopen unlinked file\", DrvFsTestReopenUnlinked},\r\n    {\"DrvFs - delete loop\", DrvFsTestDeleteLoop},\r\n    {\"DrvFs - seek\", DrvFsTestSeek},\r\n    {\"DrvFs - dir seek\", DrvFsTestDirSeek},\r\n#ifdef __NR_getdents\r\n    {\"DrvFs - getdents alignment\", DrvFsTestGetDentsAlignment},\r\n#endif\r\n    {\"DrvFs - getdents64 alignment\", DrvFsTestGetDents64Alignment},\r\n    {\"DrvFs - LX and NT symlink creation\", DrvFsTestSymlink},\r\n    {\"DrvFs - bad metadata\", DrvFsTestBadMetadata},\r\n    {\"DrvFs - metadata\", DrvFsTestMetadata},\r\n    {\"DrvFs - escaped names\", DrvFsTestEscapedNames}};\r\n\r\nstatic const LXT_VARIATION g_LxtReFsVariations[] = {\r\n    {\"DrvFs - basic\", DrvFsTestBasic},\r\n    {\"DrvFs - lookup by path\", DrvFsTestLookupPath},\r\n    {\"DrvFs - writev\", DrvFsTestWritev},\r\n    {\"DrvFs - rename\", DrvFsTestRename},\r\n    {\"DrvFs - renameat\", DrvFsTestRenameAt},\r\n    {\"DrvFs - rename directory\", DrvFsTestRenameDir},\r\n    {\"DrvFs - deleting an open file\", DrvFsTestDeleteOpenFile},\r\n    {\"DrvFs - deleting the working directory\", DrvFsTestDeleteCurrentWorkingDirectory},\r\n    {\"DrvFs - case-sensitivity\", DrvFsTestCaseSensitivity},\r\n    {\"DrvFs - case-sensitivity (drive root)\", DrvFsTestCaseSensitivityRoot},\r\n    {\"DrvFs - hidden lxfs directories\", DrvFsTestHiddenLxFsDirs},\r\n    {\"DrvFs - inotify with epoll\", DrvFsTestInotifyEpoll},\r\n    {\"DrvFs - inotify unmounting of a bind mount\", DrvFsTestInotifyUnmountBind},\r\n    {\"DrvFs - inotify POSIX unlink/rename\", DrvFsTestInotifyPosixUnlinkRename},\r\n    {\"DrvFs - utimensat\", DrvFsTestUtimensat},\r\n    {\"DrvFs - hard links\", DrvFsTestHardLinks},\r\n    {\"DrvFs - block count\", DrvFsTestBlockCount},\r\n    {\"DrvFs - fstat\", DrvFsTestFstat},\r\n    {\"DrvFs - statx\", DrvFsTestStatx},\r\n    {\"DrvFs - reopen unlinked file\", DrvFsTestReopenUnlinked},\r\n    {\"DrvFs - delete loop\", DrvFsTestDeleteLoop},\r\n    {\"DrvFs - seek\", DrvFsTestSeek},\r\n    {\"DrvFs - dir seek\", DrvFsTestDirSeek},\r\n#ifdef __NR_getdents\r\n    {\"DrvFs - getdents alignment\", DrvFsTestGetDentsAlignment},\r\n#endif\r\n    {\"DrvFs - getdents64 alignment\", DrvFsTestGetDents64Alignment},\r\n    {\"DrvFs - LX and NT symlink creation\", DrvFsTestSymlink},\r\n    //{\"DrvFs - escaped names\", DrvFsTestEscapedNames},// TODO: enable this variation when lxutil is fixed\r\n    {\"DrvFs - wslpath ReFS\", DrvFsTestReFsWslPath}};\r\n\r\nstatic char* VfsAccessLxssDir;\r\nstatic int DrvFsTestMode;\r\n\r\nint DrvfsTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    if ((Argc == 2) && (strcmp(Argv[1], \"execvetest\") == 0))\r\n    {\r\n        return DRVFS_EXECVE_TEST_RESULT;\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n    LxtCheckResult(DrvFsParseArgs(Argc, Argv, &Args));\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY();\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint DrvFsCheckMode(const char* Filename, mode_t ExpectedMode)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if a file's mode matches the expected value.\r\n\r\nArguments:\r\n\r\n    Filename - Supplies the file name.\r\n\r\n    ExpectedMode - Supplies the mode.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(Filename, &Stat));\r\n    LxtLogInfo(\"%s: mode 0%o\", Filename, Stat.st_mode);\r\n    LxtCheckEqual(Stat.st_mode, ExpectedMode, \"0%o\");\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsCheckStat(const char* Filename, uid_t ExpectedUid, gid_t ExpectedGid, mode_t ExpectedMode, dev_t ExpectedDevice)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks whether the attributes of a file match the expected\r\n    values.\r\n\r\nArguments:\r\n\r\n    Filename - Supplies the name of the file.\r\n\r\n    ExpectedUid - Supplies the expected value for the uid.\r\n\r\n    ExpectedGid - Supplies the expected value for the uid.\r\n\r\n    ExpectedMode - Supplies the expected value for the mode.\r\n\r\n    ExpectedDevice - Supplies the expected value for the device.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(Filename, &Stat));\r\n    LxtCheckEqual(Stat.st_uid, ExpectedUid, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, ExpectedGid, \"%d\");\r\n    LxtCheckEqual(Stat.st_mode, ExpectedMode, \"0%o\");\r\n    LxtCheckEqual(Stat.st_rdev, ExpectedDevice, \"0x%lx\");\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsParseArgs(int Argc, char* Argv[], LXT_ARGS* Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine parses command line arguments for the vfsaccess tests.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of arguments.\r\n\r\n    Argv - Supplies an array of arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ArgvIndex;\r\n    char Name[100];\r\n    int Result;\r\n    int TestMode;\r\n    int ValidArguments;\r\n    unsigned int VariationCount;\r\n    const LXT_VARIATION* Variations;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    Variations = g_LxtVariations;\r\n    VariationCount = LXT_COUNT_OF(g_LxtVariations);\r\n    TestMode = 0;\r\n    ValidArguments = 0;\r\n    if (Argc < 1)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (ArgvIndex = 1; ArgvIndex < Argc; ++ArgvIndex)\r\n    {\r\n        if (Argv[ArgvIndex][0] != '-')\r\n        {\r\n            printf(\"Unexpected character %s\", Argv[ArgvIndex]);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        switch (Argv[ArgvIndex][1])\r\n        {\r\n        case 'v':\r\n        case 'l':\r\n\r\n            //\r\n            // This was already taken care of by LxtInitialize.\r\n            //\r\n\r\n            ++ArgvIndex;\r\n\r\n            break;\r\n\r\n        case 'd':\r\n            ++ArgvIndex;\r\n            if (ArgvIndex < Argc)\r\n            {\r\n                ValidArguments = 1;\r\n                VfsAccessLxssDir = Argv[ArgvIndex];\r\n            }\r\n\r\n            break;\r\n\r\n        case 'm':\r\n            ++ArgvIndex;\r\n            if (ArgvIndex < Argc)\r\n            {\r\n                ValidArguments = -1;\r\n                TestMode = atoi(Argv[ArgvIndex]);\r\n                switch (TestMode)\r\n                {\r\n                case DRVFS_FAT_TEST_MODE:\r\n                    LxtLogInfo(\"Running FAT variations.\");\r\n                    Variations = g_LxtFatVariations;\r\n                    VariationCount = LXT_COUNT_OF(g_LxtFatVariations);\r\n                    break;\r\n\r\n                case DRVFS_SMB_TEST_MODE:\r\n                    LxtLogInfo(\"Running SMB variations.\");\r\n                    Variations = g_LxtSmbVariations;\r\n                    VariationCount = LXT_COUNT_OF(g_LxtSmbVariations);\r\n                    break;\r\n\r\n                case DRVFS_METADATA_TEST_MODE:\r\n                    LxtLogInfo(\"Running metadata variations.\");\r\n                    Variations = g_LxtMetadataVariations;\r\n                    VariationCount = LXT_COUNT_OF(g_LxtMetadataVariations);\r\n                    break;\r\n\r\n                case DRVFS_REFS_TEST_MODE:\r\n                    LxtLogInfo(\"Running ReFs variations.\");\r\n                    Variations = g_LxtReFsVariations;\r\n                    VariationCount = LXT_COUNT_OF(g_LxtReFsVariations);\r\n                    break;\r\n                }\r\n            }\r\n\r\n            break;\r\n\r\n        case 'h':\r\n        case 'a':\r\n            break;\r\n\r\n        default:\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // If -c was not specified, just run the tests\r\n    //\r\n\r\n    ValidArguments = 1;\r\n    DrvFsTestMode = TestMode;\r\n    snprintf(Name, sizeof(Name), LXT_NAME_FORMAT, TestMode);\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, Args, Name));\r\n    LxtCheckResult(DrvFsTestSetup(Args, TestMode));\r\n    LxtCheckResult(LxtRunVariations(Args, Variations, VariationCount));\r\n\r\nErrorExit:\r\n\r\n    //\r\n    // Remount drvfs normally.\r\n    //\r\n\r\n    chdir(\"/\");\r\n    umount(DRVFS_PREFIX);\r\n    LxtFsMountDrvFs(DRVFS_DRIVE, DRVFS_PREFIX, NULL);\r\n    if (ValidArguments == 0)\r\n    {\r\n        printf(\"\\nuse: %s <One of the below arguments>\\n\", Argv[0]);\r\n        printf(\"\\t-d : lxfs directory\\n\");\r\n        printf(\"\\t-m : test mode\\n\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestAccess(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests access permissions on DrvFs.\r\n\r\n    N.B. Test files with proper permissions are created by the TAEF DLL.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t Bytes;\r\n    char Buf[100] = {0};\r\n    int Fd;\r\n    int Result;\r\n\r\n    //\r\n    // File with read/write/execute access.\r\n    //\r\n\r\n    Fd = -1;\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_RWX_TEST_FILE, S_IFREG | S_IRUGO | S_IWUGO | S_IXUGO));\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_RWX_TEST_FILE, O_RDWR));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Readonly file. This file can only be opened for read, and writing should\r\n    // fail. O_PATH always works.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_READONLY_TEST_FILE, S_IFREG | S_IRUGO));\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_READONLY_TEST_FILE, O_RDONLY));\r\n    LxtCheckErrno(Bytes = read(Fd, Buf, sizeof(Buf)));\r\n    LxtCheckEqual(Bytes, 0L, \"%ld\");\r\n    LxtCheckErrnoFailure(write(Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrnoFailure(Fd = open(DRVFS_ACCESS_READONLY_TEST_FILE, O_WRONLY), EACCES);\r\n\r\n    LxtCheckErrnoFailure(Fd = open(DRVFS_ACCESS_READONLY_TEST_FILE, O_RDWR), EACCES);\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_READONLY_TEST_FILE, O_PATH));\r\n    LxtCheckErrnoFailure(read(Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrnoFailure(write(Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // File with read/write/execute access, but the read-only attribute set.\r\n    // This file can only be opened for read, and writing should fail.\r\n    // O_PATH always works.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_READONLYATTR_TEST_FILE, S_IFREG | S_IRUGO | S_IXUGO));\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_READONLY_TEST_FILE, O_RDONLY));\r\n    LxtCheckErrno(Bytes = read(Fd, Buf, sizeof(Buf)));\r\n    LxtCheckEqual(Bytes, 0L, \"%ld\");\r\n    LxtCheckErrnoFailure(write(Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrnoFailure(Fd = open(DRVFS_ACCESS_READONLY_TEST_FILE, O_WRONLY), EACCES);\r\n\r\n    LxtCheckErrnoFailure(Fd = open(DRVFS_ACCESS_READONLY_TEST_FILE, O_RDWR), EACCES);\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_READONLY_TEST_FILE, O_PATH));\r\n    LxtCheckErrnoFailure(read(Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrnoFailure(write(Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Second file with the read-only attribute set, used to check if that\r\n    // file can be deleted.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_READONLYATTRDEL_TEST_FILE, S_IFREG | S_IRUGO | S_IXUGO));\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_ACCESS_READONLYATTRDEL_TEST_FILE));\r\n\r\n    //\r\n    // Writeonly file. This file can only be opened for write and read should\r\n    // fail. O_PATH always works.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_WRITEONLY_TEST_FILE, S_IFREG | S_IWUGO));\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_WRITEONLY_TEST_FILE, O_WRONLY));\r\n    LxtCheckErrnoFailure(read(Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrno(Bytes = write(Fd, Buf, sizeof(Buf)));\r\n    LxtCheckEqual(Bytes, 100L, \"%ld\");\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrnoFailure(Fd = open(DRVFS_ACCESS_WRITEONLY_TEST_FILE, O_RDONLY), EACCES);\r\n\r\n    LxtCheckErrnoFailure(Fd = open(DRVFS_ACCESS_WRITEONLY_TEST_FILE, O_RDWR), EACCES);\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_READONLY_TEST_FILE, O_PATH));\r\n    LxtCheckErrnoFailure(read(Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrnoFailure(write(Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Directory with add/delete file and traverse permissions. Opening a file\r\n    // in the directory should succeed, but opening the directory itself should\r\n    // not. Adding and deleting a file should work, but adding a subdirectory\r\n    // should not. O_PATH always works.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR, S_IFDIR | S_IWUGO | S_IXUGO));\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD, S_IFREG | S_IRUGO));\r\n\r\n    LxtCheckErrnoFailure(Fd = open(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR, O_RDONLY | O_DIRECTORY), EACCES);\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD, O_RDONLY));\r\n\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR, O_PATH | O_DIRECTORY));\r\n\r\n    LxtCheckErrnoFailure(syscall(SYS_getdents64, Fd, Buf, sizeof(Buf)), EBADF);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Creating a readonly file should work, but the mode specified should not\r\n    // take effect, unless metadata is being used.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR));\r\n\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    if (DrvFsTestMode == DRVFS_METADATA_TEST_MODE)\r\n    {\r\n        LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2, S_IFREG | 0400));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2, S_IFREG | S_IRUGO | S_IXUGO));\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2));\r\n\r\n    //\r\n    // Creating a writable file should work, but the mode specified should not\r\n    // take effect, unless metadata is being used.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR));\r\n\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    if (DrvFsTestMode == DRVFS_METADATA_TEST_MODE)\r\n    {\r\n        LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2, S_IFREG | 0200));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2, S_IFREG | S_IRUGO | S_IWUGO | S_IXUGO));\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2));\r\n\r\n    //\r\n    // Creating a link should work, but creating a directory should not.\r\n    //\r\n\r\n    LxtCheckErrno(symlink(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD, DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD_LINK));\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD_LINK, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO));\r\n\r\n    LxtCheckErrnoFailure(mkdir(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2, S_IRWXU), EACCES);\r\n\r\n    //\r\n    // Directory with only list permissions.\r\n    //\r\n    // Although traverse permissions were not set on this directory, Windows\r\n    // still acts as if it can be traversed due to the bypass traverse checking\r\n    // privilege, and LX should report the directory as traversable.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_READONLY_TEST_DIR, S_IFDIR | S_IRUGO | S_IXUGO));\r\n\r\n    //\r\n    // Test chmod on DrvFs. Setting any write bit on the read-only attribute\r\n    // file will clear the read-only attribute, which gets reported back with\r\n    // all write bits set because DrvFs doesn't distinguish user, group and\r\n    // other. Other bits cannot be affected by chmod, unless metadata is\r\n    // enabled.\r\n    //\r\n    // N.B. The Windows side of this test will further verify the read-only\r\n    //      attribute.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chmod(DRVFS_ACCESS_READONLYATTR_TEST_FILE, S_IWUSR));\r\n\r\n    if (DrvFsTestMode == DRVFS_METADATA_TEST_MODE)\r\n    {\r\n        LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_READONLYATTR_TEST_FILE, S_IFREG | 0200));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_READONLYATTR_TEST_FILE, S_IFREG | S_IRUGO | S_IWUGO | S_IXUGO));\r\n    }\r\n\r\n    //\r\n    // Conversely, removing all write bits will set the read-only attribute.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chmod(DRVFS_ACCESS_RWX_TEST_FILE, 0));\r\n\r\n    if (DrvFsTestMode == DRVFS_METADATA_TEST_MODE)\r\n    {\r\n        LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_RWX_TEST_FILE, S_IFREG));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_RWX_TEST_FILE, S_IFREG | S_IRUGO | S_IXUGO));\r\n    }\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD2);\r\n    unlink(DRVFS_ACCESS_EXECUTEONLY_TEST_DIR_CHILD_LINK);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestBadMetadata(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests files that have invalid metadata attributes.\r\n\r\n    N.B. These files were created by the Windows side of the test.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    //\r\n    // Any bad metadata field will be ignored. The other fields are used.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/baduid\", 0, 3001, S_IFREG | 0644, 0));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/badgid\", 3000, 0, S_IFREG | 0644, 0));\r\n\r\n    //\r\n    // NTFS does not return EffectiveAccess if a file has mode metadata, so\r\n    // the access will be zero if it is corrupt.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/badmode\", 3000, 3001, S_IFREG, 0));\r\n\r\n    //\r\n    // If the file type in the mode doesn't match the actual file type, this\r\n    // also gets treated as invalid\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/badtype1\", 3000, 3001, S_IFREG, 0));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/badtype2\", 3000, 3001, S_IFREG, 0));\r\n\r\n    //\r\n    // If the file is not a device, the device ID should not be reported even\r\n    // if it's present in the metadata.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/nondevice\", 3000, 3001, S_IFREG | 0644, 0));\r\n\r\n    //\r\n    // Changing metadata on a file with corrupt metadata should work.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chown(DRVFS_METADATA_TEST_DIR \"/baduid\", 1000, -1));\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/baduid\", 1000, 3001, S_IFREG | 0644, 0));\r\n\r\n    //\r\n    // Also when the field being changed is not the corrupt one.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chown(DRVFS_METADATA_TEST_DIR \"/badgid\", 1000, -1));\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/badgid\", 1000, 0, S_IFREG | 0644, 0));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests basic drvfs functionality (reading/writing).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    struct stat FStat;\r\n    ssize_t Size;\r\n    struct stat Stat;\r\n    int Result;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Create a test directory.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_BASIC_PREFIX, 0777));\r\n    LxtCheckErrnoFailure(mkdir(DRVFS_BASIC_PREFIX, 0777), EEXIST);\r\n\r\n    //\r\n    // Verify the directory file size is equal to the file-system block-size,\r\n    // and that block count is zero.\r\n    //\r\n\r\n    LxtCheckErrno(stat(DRVFS_BASIC_PREFIX, &Stat));\r\n    LxtCheckGreater(Stat.st_size, 0ull, \"%llu\");\r\n    LxtCheckEqual(Stat.st_size, (unsigned long long)Stat.st_blksize, \"%llu\");\r\n    LxtCheckEqual(Stat.st_blocks, 0l, \"%ld\");\r\n\r\n    //\r\n    // Create a file and write to it.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(Fd = open(DRVFS_BASIC_PREFIX \"/test\", O_RDWR), ENOENT);\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_BASIC_PREFIX \"/test\", O_CREAT | O_RDWR, 0666));\r\n    LxtCheckErrno(Size = write(Fd, \"hello\", 5));\r\n    LxtCheckEqual(Size, 5, \"%ld\");\r\n\r\n    //\r\n    // Check stat results.\r\n    //\r\n    // N.B. Block count is reported as zero because this file is small enough\r\n    //      to have its contents packed into the MFT by NTFS. This is not the\r\n    //      case for FAT.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_BASIC_PREFIX \"/test\", &Stat));\r\n    LxtCheckEqual(Stat.st_size, 5, \"%lld\");\r\n    LxtCheckGreater(Stat.st_ino, 0, \"%llu\");\r\n    if (DrvFsTestMode == DRVFS_FAT_TEST_MODE)\r\n    {\r\n        LxtCheckEqual(Stat.st_blocks, 2, \"%d\");\r\n    }\r\n    else\r\n    {\r\n        LxtCheckEqual(Stat.st_blocks, 0, \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(Stat.st_nlink, 1, \"%d\");\r\n    LxtCheckEqual(Stat.st_rdev, 0, \"%d\");\r\n\r\n    //\r\n    // Check fstat has the same results.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_BASIC_PREFIX \"/test\", &Stat));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &FStat));\r\n    LxtCheckMemoryEqual(&Stat, &FStat, sizeof(Stat));\r\n\r\n    //\r\n    // Read back the data.\r\n    //\r\n\r\n    memset(Buffer, 0, sizeof(Buffer));\r\n    LxtCheckErrno(lseek(Fd, 0, SEEK_SET));\r\n    LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Size, 5, \"%lld\");\r\n    LxtCheckStringEqual(Buffer, \"hello\");\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Test using O_APPEND.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_BASIC_PREFIX \"/test\", O_RDWR | O_APPEND, 0666));\r\n\r\n    LxtCheckErrno(Size = write(Fd, \"foo\", 3));\r\n    LxtCheckEqual(Size, 3, \"%ld\");\r\n    LxtCheckErrno(lseek(Fd, 0, SEEK_SET));\r\n    LxtCheckErrno(Size = write(Fd, \"bar\", 3));\r\n    LxtCheckEqual(Size, 3, \"%ld\");\r\n    memset(Buffer, 0, sizeof(Buffer));\r\n    LxtCheckErrno(lseek(Fd, 0, SEEK_SET));\r\n    LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Size, 11, \"%lld\");\r\n    LxtCheckStringEqual(Buffer, \"hellofoobar\");\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_BASIC_PREFIX \"/test\", &Stat));\r\n    LxtCheckEqual(Stat.st_size, 11, \"%lld\");\r\n\r\n    //\r\n    // Creating/removing items relative to the current working directory.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(chdir(DRVFS_BASIC_PREFIX));\r\n        LxtCheckErrnoZeroSuccess(mkdir(\"a\", 0777));\r\n        LxtCheckErrnoZeroSuccess(access(\"a\", F_OK));\r\n        LxtCheckErrnoZeroSuccess(access(DRVFS_BASIC_PREFIX \"/a\", F_OK));\r\n        LxtCheckErrnoZeroSuccess(rmdir(\"a\"));\r\n        LxtCheckErrnoFailure(access(\"a\", F_OK), ENOENT);\r\n        LxtCheckErrnoFailure(access(DRVFS_BASIC_PREFIX \"/a\", F_OK), ENOENT);\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // Creating regular files with mknod should always work even without\r\n    // metadata, though without metadata the exact mode won't be preserved.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mknod(DRVFS_BASIC_PREFIX \"/node\", S_IFREG | 0600, 0));\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_BASIC_PREFIX \"/node\", &Stat));\r\n    if (DrvFsTestMode == DRVFS_METADATA_TEST_MODE)\r\n    {\r\n        LxtCheckEqual(Stat.st_mode, S_IFREG | 0600, \"0%o\");\r\n    }\r\n    else\r\n    {\r\n        LxtCheckEqual(Stat.st_mode, S_IFREG | 0777, \"0%o\");\r\n    }\r\n\r\n    //\r\n    // Check that cleanup works.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_BASIC_PREFIX \"/node\"));\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_BASIC_PREFIX \"/test\"));\r\n    LxtCheckErrnoZeroSuccess(rmdir(DRVFS_BASIC_PREFIX));\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_BASIC_PREFIX \"/node\");\r\n    unlink(DRVFS_BASIC_PREFIX \"/test\");\r\n    rmdir(DRVFS_BASIC_PREFIX);\r\n\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestBlockCount(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the block count reported for files.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char* Buffer;\r\n    size_t BufferSize;\r\n    int Fd;\r\n    int Result;\r\n    struct stat Stat;\r\n    ssize_t Written;\r\n\r\n    Buffer = 0;\r\n    Fd = -1;\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_BASIC_PREFIX, 0777));\r\n\r\n    //\r\n    // Create the file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_BASIC_PREFIX \"/testfile\", 0666));\r\n\r\n    //\r\n    // Block count should be zero for an empty file.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 0, \"%lld\");\r\n    LxtCheckEqual(Stat.st_blocks, 0, \"%ld\");\r\n\r\n    //\r\n    // Write data to the file. Make sure it's a whole number of NTFS blocks.\r\n    //\r\n\r\n    BufferSize = 2 * Stat.st_blksize;\r\n    Buffer = malloc(BufferSize);\r\n    LxtCheckNotEqual(Buffer, NULL, \"%p\");\r\n    memset(Buffer, 0, BufferSize);\r\n    LxtCheckErrno(Written = write(Fd, Buffer, BufferSize));\r\n    LxtCheckEqual(Written, BufferSize, \"%ld\");\r\n\r\n    //\r\n    // Verify the block count, which should use 512 byte blocks regardless of\r\n    // the reported block size.\r\n    //\r\n    // N.B. NTFS can decide to allocate more blocks, so an exact test isn't\r\n    //      possible.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat));\r\n    LxtCheckEqual(Stat.st_size, BufferSize, \"%lld\");\r\n    LxtCheckGreaterOrEqual(Stat.st_blocks, BufferSize / 512, \"%ld\");\r\n\r\n    //\r\n    // Write one more byte so the size isn't divisible by 512.\r\n    //\r\n\r\n    LxtCheckErrno(Written = write(Fd, Buffer, 1));\r\n    LxtCheckEqual(Written, 1, \"%ld\");\r\n\r\n    //\r\n    // Verify the block count. Windows will have grown the file using its own\r\n    // internal logic, so the exact block count is hard to predict.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat));\r\n    LxtCheckEqual(Stat.st_size, BufferSize + 1, \"%lld\");\r\n    LxtCheckGreaterOrEqual(Stat.st_blocks, ((BufferSize + Stat.st_blksize) / 512), \"%ld\");\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_BASIC_PREFIX \"/testfile\");\r\n    rmdir(DRVFS_BASIC_PREFIX);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestCaseSensitivity(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the case-sensitivity support of DrvFS.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    struct stat StatA;\r\n    struct stat StatB;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_CS_PREFIX, 0777));\r\n\r\n    //\r\n    // Create 2 sub-directories differing only by case.\r\n    //\r\n\r\n    LxtCheckResult(mkdir(DRVFS_CS_PREFIX \"/dir\", 0777));\r\n    LxtCheckResult(mkdir(DRVFS_CS_PREFIX \"/Dir\", 0777));\r\n\r\n    //\r\n    // Create 2 files differing only by case.\r\n    //\r\n\r\n    LxtCheckResult(Fd = open(DRVFS_CS_PREFIX \"/file\", O_RDWR | O_CREAT | O_EXCL, 0777));\r\n\r\n    close(Fd);\r\n    LxtCheckResult(Fd = open(DRVFS_CS_PREFIX \"/File\", O_RDWR | O_CREAT | O_EXCL, 0777));\r\n\r\n    close(Fd);\r\n\r\n    //\r\n    // Stat both files and make sure the inode numbers are different.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_CS_PREFIX \"/file\", &StatA));\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_CS_PREFIX \"/File\", &StatB));\r\n    LxtCheckNotEqual(StatA.st_ino, StatB.st_ino, \"%llu\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    unlink(DRVFS_CS_PREFIX \"/File\");\r\n    unlink(DRVFS_CS_PREFIX \"/file\");\r\n    rmdir(DRVFS_CS_PREFIX \"/Dir\");\r\n    rmdir(DRVFS_CS_PREFIX \"/dir\");\r\n    rmdir(DRVFS_CS_PREFIX);\r\n\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestCaseSensitivityRoot(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether drvfs correctly disables case sensitivity in the\r\n    root of a drive for various operations.\r\n\r\n    N.B. This test requires administrator privileges in Windows.\r\n\r\n    N.B. Since the tests are run with case=dir, the root behaves as a case\r\n         insensitive file system.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    struct stat Stat1;\r\n    struct stat Stat2;\r\n\r\n    //\r\n    // Attempt to create two files that differ by case.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_PREFIX \"/testfile\", 0666));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat1));\r\n    LxtCheckNotEqual(Stat1.st_ino, 0, \"%llu\");\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoFailure(open(DRVFS_PREFIX \"/TestFile\", O_CREAT | O_EXCL, 0666), EEXIST);\r\n\r\n    //\r\n    // Without O_EXCL, O_CREAT succeeds because this directory is case\r\n    // insensitive. These should refer to the same file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_PREFIX \"/TestFile\", O_CREAT, 0666));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat2));\r\n    LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // O_CREAT with the original name should work, of course.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_PREFIX \"/testfile\", O_CREAT, 0666));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoFailure(mkdir(DRVFS_PREFIX \"/TestFile\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(symlink(DRVFS_PREFIX \"/testfile\", DRVFS_PREFIX \"/TestFile\"), EEXIST);\r\n\r\n    LxtCheckErrnoFailure(link(DRVFS_PREFIX \"/testfile\", DRVFS_PREFIX \"/TestFile\"), EEXIST);\r\n\r\n    //\r\n    // Verify mknod returns EEXIST because of the case collision.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mknod(DRVFS_PREFIX \"/TestFile\", S_IFREG | 0666, 0), EEXIST);\r\n\r\n    //\r\n    // Verify renaming to a different case.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_PREFIX \"/testdir\", 0777));\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_PREFIX \"/testdir\", &Stat1));\r\n    LxtCheckErrnoZeroSuccess(rename(DRVFS_PREFIX \"/testdir\", DRVFS_PREFIX \"/TestDir\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_PREFIX \"/TestDir\", &Stat2));\r\n    LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n    LxtCheckErrnoZeroSuccess(rmdir(DRVFS_PREFIX \"/TestDir\"));\r\n\r\nErrorExit:\r\n    unlink(DRVFS_PREFIX \"/TestFile\");\r\n    rmdir(DRVFS_PREFIX \"/TestFile\");\r\n    unlink(DRVFS_PREFIX \"/testfile\");\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestDeleteCurrentWorkingDirectory(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the behavior if the current working directory is\r\n    unlinked for DrvFs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(LxtFsDeleteCurrentWorkingDirectoryCommon(DRVFS_PREFIX, FS_DELETE_DRVFS));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestDeleteLoop(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests deleting files in a loop with multiple getdents calls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtFsDeleteLoopCommon(DRVFS_DELETELOOP_PREFIX));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestDeleteOpenFile(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests using unlink and rmdir on a DrvFs file/directory that's open.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(LxtFsDeleteOpenFileCommon(DRVFS_PREFIX, FS_DELETE_DRVFS));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestEscapedNames(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests using file names that need to be escaped.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_CHILD_INFO Child;\r\n    int Fd;\r\n    int Result;\r\n    struct stat Stat;\r\n    struct stat Stat2;\r\n\r\n    Fd = -1;\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_ESCAPE_TEST_DIR, 0777));\r\n\r\n    //\r\n    // Check creating a file and make sure it can be accessed.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_ESCAPE_TEST_CHILD, 0666));\r\n    LxtCheckResult(LxtCheckFdPath(Fd, DRVFS_ESCAPE_TEST_CHILD));\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_ESCAPE_TEST_CHILD, &Stat));\r\n    LxtCheckNotEqual(Stat.st_ino, 0, \"%llu\");\r\n\r\n    //\r\n    // It's possible to use the escaped characters directly.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_ESCAPE_TEST_CHILD_ESCAPED, &Stat2));\r\n    LxtCheckEqual(Stat.st_ino, Stat2.st_ino, \"%llu\");\r\n\r\n    //\r\n    // Make sure the name appears correctly in the directory listing.\r\n    //\r\n\r\n    Child.Name = DRVFS_ESCAPE_TEST_CHILD_NAME;\r\n    Child.FileType = DT_REG;\r\n    LxtCheckResult(LxtCheckDirectoryContentsEx(DRVFS_ESCAPE_TEST_DIR, &Child, 1, 0));\r\n\r\n    //\r\n    // Check unlinking.\r\n    //\r\n\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_ESCAPE_TEST_CHILD));\r\n\r\n    //\r\n    // Check various other ways of creating a file.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_ESCAPE_TEST_CHILD, 0777));\r\n    LxtCheckErrnoZeroSuccess(rmdir(DRVFS_ESCAPE_TEST_CHILD));\r\n    LxtCheckErrno(Fd = creat(DRVFS_ESCAPE_TEST_DIR \"/target\", 0666));\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Test rename with an escape character in the source and target.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rename(DRVFS_ESCAPE_TEST_DIR \"/target\", DRVFS_ESCAPE_TEST_CHILD));\r\n\r\n    LxtCheckErrnoZeroSuccess(rename(DRVFS_ESCAPE_TEST_CHILD, DRVFS_ESCAPE_TEST_DIR \"/target\"));\r\n\r\n    //\r\n    // Symlinks are not supported on SMB or FAT.\r\n    //\r\n\r\n    if ((DrvFsTestMode != DRVFS_FAT_TEST_MODE) && (DrvFsTestMode != DRVFS_SMB_TEST_MODE))\r\n    {\r\n\r\n        LxtCheckErrnoZeroSuccess(symlink(\"target\", DRVFS_ESCAPE_TEST_CHILD));\r\n        LxtCheckErrnoZeroSuccess(unlink(DRVFS_ESCAPE_TEST_CHILD));\r\n    }\r\n\r\n    //\r\n    // Hard links are not supported on FAT.\r\n    //\r\n\r\n    if (DrvFsTestMode != DRVFS_FAT_TEST_MODE)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(link(DRVFS_ESCAPE_TEST_DIR \"/target\", DRVFS_ESCAPE_TEST_CHILD));\r\n\r\n        LxtCheckErrnoZeroSuccess(unlink(DRVFS_ESCAPE_TEST_CHILD));\r\n    }\r\n\r\n    //\r\n    // Check mknod if metadata is supported.\r\n    //\r\n\r\n    if (DrvFsTestMode == DRVFS_METADATA_TEST_MODE)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(mknod(DRVFS_ESCAPE_TEST_CHILD, S_IFCHR, makedev(1, 3)));\r\n\r\n        LxtCheckErrnoZeroSuccess(unlink(DRVFS_ESCAPE_TEST_CHILD));\r\n    }\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_ESCAPE_TEST_DIR \"/target\");\r\n    unlink(DRVFS_ESCAPE_TEST_CHILD);\r\n    rmdir(DRVFS_ESCAPE_TEST_CHILD);\r\n    rmdir(DRVFS_ESCAPE_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestExecve(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests execve on DrvFs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char* Argv[4];\r\n    pid_t ChildPid;\r\n    char* Envp[1];\r\n    int Result;\r\n\r\n    //\r\n    // Check the mode of the files.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_EXECUTEONLY_TEST_FILE, S_IFREG | S_IRUGO | S_IXUGO));\r\n\r\n    LxtCheckResult(DrvFsCheckMode(DRVFS_ACCESS_READONLY_TEST_FILE, S_IFREG | S_IRUGO));\r\n\r\n    //\r\n    // Fork a child.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        Argv[0] = DRVFS_ACCESS_EXECUTEONLY_TEST_FILE;\r\n        Argv[1] = \"drvfs\";\r\n        Argv[2] = \"execvetest\";\r\n        Argv[3] = NULL;\r\n        Envp[0] = NULL;\r\n\r\n        //\r\n        // Attempt to execve the read only file, which should fail.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(execve(DRVFS_ACCESS_READONLY_TEST_FILE, Argv, Envp), EACCES);\r\n\r\n        //\r\n        // Attempt to execve the execute only file, which should succeed even\r\n        // though the user has no read access.\r\n        //\r\n\r\n        LxtCheckErrno(execve(DRVFS_ACCESS_EXECUTEONLY_TEST_FILE, Argv, Envp));\r\n\r\n        LxtLogError(\"Execve returned\");\r\n        _exit(LXT_RESULT_FAILURE);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, DRVFS_EXECVE_TEST_RESULT << 8));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestFatCaseInsensitive(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the case-insensitive behavior of FAT.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    const LXT_CHILD_INFO Children[] = {\"foo\", DT_REG};\r\n    const LXT_CHILD_INFO ChildrenPlan9Smb[] = {\"FOO\", DT_REG};\r\n    int Fd;\r\n    int Fd2;\r\n    int Result;\r\n    struct stat Stat1;\r\n    struct stat Stat2;\r\n\r\n    Fd = -1;\r\n    Fd2 = -1;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_CASE_INSENSITIVE_TEST_DIR, 0777));\r\n\r\n    //\r\n    // Create a file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_CASE_INSENSITIVE_TEST_DIR \"/foo\", 0666));\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Stat the file with its original name and a different case, and verify\r\n    // they are the same file.\r\n    //\r\n\r\n    LxtCheckErrno(lstat(DRVFS_CASE_INSENSITIVE_TEST_DIR \"/foo\", &Stat1));\r\n    LxtCheckErrno(lstat(DRVFS_CASE_INSENSITIVE_TEST_DIR \"/FOO\", &Stat2));\r\n    LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%lld\");\r\n\r\n    //\r\n    // Check name collisions on create.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(Fd2 = open(DRVFS_CASE_INSENSITIVE_TEST_DIR \"/FOO\", O_CREAT | O_EXCL, 0666), EEXIST);\r\n\r\n    LxtCheckErrnoFailure(mkdir(DRVFS_CASE_INSENSITIVE_TEST_DIR \"/FOO\", 0666), EEXIST);\r\n\r\n    //\r\n    // Rename to the same file, which works but has no effect (the case is not\r\n    // changed). This matches real Linux, and normal Windows behavior when\r\n    // renaming on FAT.\r\n    //\r\n    // N.B. With SMB over Plan 9, because Linux doesn't know the file system is\r\n    //      case-insensitive and NTFS does let you change the case on rename,\r\n    //      this actually does change the case.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rename(DRVFS_CASE_INSENSITIVE_TEST_DIR \"/foo\", DRVFS_CASE_INSENSITIVE_TEST_DIR \"/FOO\"));\r\n\r\n    //\r\n    // If the file is opened with two different cases, both fd's report the\r\n    // path with the first case. This behavior matches Linux, except Linux\r\n    // \"remembers\" the first case used after the file is closed because the\r\n    // directory entries are cached.\r\n    //\r\n    // N.B. This is not the case with Plan 9 because Linux doesn't know the\r\n    //      file system is case-insensitive.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypePlan9)\r\n    {\r\n        LxtCheckErrno(Fd = open(DRVFS_CASE_INSENSITIVE_TEST_DIR \"/foo\", O_RDONLY));\r\n        LxtCheckErrno(Fd2 = open(DRVFS_CASE_INSENSITIVE_TEST_DIR \"/FOO\", O_RDONLY));\r\n        LxtCheckResult(LxtCheckFdPath(Fd, DRVFS_CASE_INSENSITIVE_TEST_DIR \"/foo\"));\r\n        LxtCheckResult(LxtCheckFdPath(Fd2, DRVFS_CASE_INSENSITIVE_TEST_DIR \"/foo\"));\r\n    }\r\n\r\n    //\r\n    // Listing the directory shows the file with the correct case.\r\n    //\r\n    // N.B. As remarked above, for SMB on Plan 9, the case will have changed.\r\n    //\r\n\r\n    if ((g_LxtFsInfo.FsType == LxtFsTypePlan9) && (DrvFsTestMode == DRVFS_SMB_TEST_MODE))\r\n    {\r\n\r\n        LxtCheckResult(LxtCheckDirectoryContentsEx(DRVFS_CASE_INSENSITIVE_TEST_DIR, ChildrenPlan9Smb, LXT_COUNT_OF(Children), 0));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtCheckDirectoryContentsEx(DRVFS_CASE_INSENSITIVE_TEST_DIR, Children, LXT_COUNT_OF(Children), 0));\r\n    }\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (Fd2 >= 0)\r\n    {\r\n        close(Fd2);\r\n    }\r\n\r\n    unlink(DRVFS_CASE_INSENSITIVE_TEST_DIR \"/foo\");\r\n    rmdir(DRVFS_CASE_INSENSITIVE_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestFatJunction(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether the NTFS mount point for the FAT volume can be\r\n    used as a symlink to the mounted volume.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    //\r\n    // This test does not apply to VM mode because Plan 9 doesn't support\r\n    // junction point symlinks.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9)\r\n    {\r\n        LxtLogInfo(\"This test is not relevant in VM mode.\");\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Because the real C: drive was unmounted to mount the FAT volume, mount\r\n    // it in a different location.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_MOUNT_TEST_DIR, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(DRVFS_DRIVE, DRVFS_MOUNT_TEST_DIR, DRVFS_FS_TYPE, DRVFS_MOUNT_OPTIONS, NULL));\r\n\r\n    LxtCheckResult(LxtCheckLinkTarget(DRVFS_MOUNT_TEST_DIR \"/\" DRVFS_FAT_MOUNT_POINT, DRVFS_PREFIX \"/\"));\r\n\r\nErrorExit:\r\n    umount(DRVFS_MOUNT_TEST_DIR);\r\n    rmdir(DRVFS_MOUNT_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestFatUnsupported(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests unsupported functionality on FAT.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return DrvFsTestUnsupportedCommon(DRVFS_FAT_TEST_MODE);\r\n}\r\n\r\nint DrvFsTestFatUtimensat(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the utimensat system call on drvfs for FAT volumes.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return DrvFsTestUtimensatCommon(FS_UTIME_FAT | FS_UTIME_NO_SYMLINKS);\r\n}\r\n\r\nint DrvFsTestFatWslPath(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests using the wslpath utility against the FAT mount point.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    //\r\n    // Previous tests may have messed up the cwd such that getcwd fails, which\r\n    // wslpath won't like.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(\"/\"));\r\n\r\n    //\r\n    // The FAT volume in Windows is mounted on an NTFS directory mount, which\r\n    // makes it an interesting case to test with wslpath.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(DRVFS_FAT_DRIVE, DRVFS_PREFIX, true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(DRVFS_PREFIX, \"C:\\\\\" DRVFS_FAT_MOUNT_POINT, false));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestFstat(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the fstat system call on drvfs files.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int OPathFd;\r\n    int Result;\r\n    struct stat Stat1;\r\n    struct stat Stat2;\r\n\r\n    Fd = -1;\r\n    OPathFd = -1;\r\n\r\n    //\r\n    // Create a test file.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_BASIC_PREFIX, 0777));\r\n    LxtCheckErrno(Fd = creat(DRVFS_BASIC_PREFIX \"/testfile\", 0666));\r\n    LxtCheckErrno(OPathFd = open(DRVFS_BASIC_PREFIX \"/testfile\", O_PATH));\r\n\r\n    //\r\n    // Stat and fstat should have the same result.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_BASIC_PREFIX \"/testfile\", &Stat1));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat2));\r\n    LxtCheckMemoryEqual(&Stat1, &Stat2, sizeof(Stat1));\r\n    LxtCheckErrnoZeroSuccess(fstat(OPathFd, &Stat2));\r\n    LxtCheckMemoryEqual(&Stat1, &Stat2, sizeof(Stat1));\r\n\r\n    //\r\n    // Fstat should still work after unlink.\r\n    //\r\n    // N.B. This currently doesn't work on plan9 or virtiofs.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_BASIC_PREFIX \"/testfile\"));\r\n    LxtCheckErrnoFailure(stat(DRVFS_BASIC_PREFIX \"/testfile\", &Stat2), ENOENT);\r\n    if (g_LxtFsInfo.FsType != LxtFsTypePlan9 && g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat2));\r\n\r\n        //\r\n        // The result should be the same except for link count and time stamps.\r\n        //\r\n        // N.B. On FAT, which does not support posix unlink, the link count will\r\n        //      still be one.\r\n        //\r\n\r\n        LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n        LxtCheckEqual(Stat1.st_size, Stat2.st_size, \"%llu\");\r\n        LxtCheckEqual(Stat1.st_mode, Stat2.st_mode, \"0%o\");\r\n        if (DrvFsTestMode == DRVFS_FAT_TEST_MODE)\r\n        {\r\n            LxtCheckEqual(Stat1.st_nlink, Stat2.st_nlink, \"%lu\");\r\n        }\r\n        else\r\n        {\r\n            LxtCheckNotEqual(Stat1.st_nlink, Stat2.st_nlink, \"%lu\");\r\n            LxtCheckEqual(Stat2.st_nlink, 0l, \"%lu\");\r\n        }\r\n\r\n        //\r\n        // Check fstat on the O_PATH descriptor.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(fstat(OPathFd, &Stat1));\r\n        LxtCheckMemoryEqual(&Stat2, &Stat1, sizeof(Stat1));\r\n\r\n        //\r\n        // Fstatat should still work after unlink.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(fstatat(Fd, \"\", &Stat1, AT_EMPTY_PATH));\r\n        LxtCheckMemoryEqual(&Stat2, &Stat1, sizeof(Stat1));\r\n        LxtCheckErrnoZeroSuccess(fstatat(OPathFd, \"\", &Stat1, AT_EMPTY_PATH));\r\n        LxtCheckMemoryEqual(&Stat2, &Stat1, sizeof(Stat1));\r\n    }\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (OPathFd >= 0)\r\n    {\r\n        close(OPathFd);\r\n    }\r\n\r\n    unlink(DRVFS_BASIC_PREFIX \"/testfile\");\r\n    rmdir(DRVFS_BASIC_PREFIX);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestStatx(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the statx system call on drvfs files.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    struct stat Stat1;\r\n    struct statx Statx1;\r\n    struct statx Statx2;\r\n\r\n    Fd = -1;\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypeDrvFs)\r\n    {\r\n        LxtLogInfo(\"statx is not supported on drvfs in WSL1.\");\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Create a test file and symlink.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_BASIC_PREFIX, 0777));\r\n    LxtCheckErrno(Fd = creat(DRVFS_BASIC_PREFIX \"/testfile\", 0666));\r\n    LxtCheckErrnoZeroSuccess(symlink(DRVFS_BASIC_PREFIX \"/testfile\", DRVFS_BASIC_PREFIX \"/testlink\"));\r\n\r\n    //\r\n    // Stat and statx should have consistent results.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_BASIC_PREFIX \"/testfile\", &Stat1));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_BASIC_STATS, &Statx1));\r\n    LxtCheckEqual(Stat1.st_ino, Statx1.stx_ino, \"%llu\");\r\n    LxtCheckEqual(Stat1.st_size, Statx1.stx_size, \"%llu\");\r\n    LxtCheckEqual(Stat1.st_mode, Statx1.stx_mode, \"0%o\");\r\n\r\n    //\r\n    // Statx with AT_EMPTY_PATH on an fd.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(statx(Fd, \"\", AT_EMPTY_PATH, STATX_BASIC_STATS, &Statx2));\r\n    LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, \"%llu\");\r\n\r\n    //\r\n    // Test AT_SYMLINK_NOFOLLOW flag.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testlink\", AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS, &Statx2));\r\n    LxtCheckTrue(S_ISLNK(Statx2.stx_mode));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testlink\", 0, STATX_BASIC_STATS, &Statx2));\r\n    LxtCheckTrue(S_ISREG(Statx2.stx_mode));\r\n\r\n    //\r\n    // Test STATX_BTIME (birth/creation time).\r\n    //\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_BASIC_STATS | STATX_BTIME, &Statx2));\r\n    if (Statx2.stx_mask & STATX_BTIME)\r\n    {\r\n        LxtLogInfo(\"Birth time supported: tv_sec=%lld\", (long long)Statx2.stx_btime.tv_sec);\r\n    }\r\n\r\n    //\r\n    // Test sync flags.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", AT_STATX_FORCE_SYNC, STATX_BASIC_STATS, &Statx2));\r\n    LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, \"%llu\");\r\n\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", AT_STATX_DONT_SYNC, STATX_BASIC_STATS, &Statx2));\r\n    LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, \"%llu\");\r\n\r\n    //\r\n    // Test individual field masks.\r\n    //\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_TYPE, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_TYPE) != 0);\r\n    LxtCheckTrue(S_ISREG(Statx2.stx_mode));\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_MODE, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_MODE) != 0);\r\n    LxtCheckEqual((Statx1.stx_mode & 0777), (Statx2.stx_mode & 0777), \"0%o\");\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_NLINK, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_NLINK) != 0);\r\n    LxtCheckEqual(Statx1.stx_nlink, Statx2.stx_nlink, \"%u\");\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_UID, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_UID) != 0);\r\n    LxtCheckEqual(Statx1.stx_uid, Statx2.stx_uid, \"%u\");\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_GID, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_GID) != 0);\r\n    LxtCheckEqual(Statx1.stx_gid, Statx2.stx_gid, \"%u\");\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_ATIME, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_ATIME) != 0);\r\n    LxtCheckEqual(Statx1.stx_atime.tv_sec, Statx2.stx_atime.tv_sec, \"%lld\");\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_MTIME, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_MTIME) != 0);\r\n    LxtCheckEqual(Statx1.stx_mtime.tv_sec, Statx2.stx_mtime.tv_sec, \"%lld\");\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_CTIME, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_CTIME) != 0);\r\n    LxtCheckEqual(Statx1.stx_ctime.tv_sec, Statx2.stx_ctime.tv_sec, \"%lld\");\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_INO, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_INO) != 0);\r\n    LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, \"%llu\");\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_SIZE, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_SIZE) != 0);\r\n    LxtCheckEqual(Statx1.stx_size, Statx2.stx_size, \"%llu\");\r\n\r\n    memset(&Statx2, 0, sizeof(Statx2));\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/testfile\", 0, STATX_BLOCKS, &Statx2));\r\n    LxtCheckTrue((Statx2.stx_mask & STATX_BLOCKS) != 0);\r\n    LxtCheckEqual(Statx1.stx_blocks, Statx2.stx_blocks, \"%llu\");\r\n\r\n    //\r\n    // Test on a directory.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX, 0, STATX_BASIC_STATS, &Statx2));\r\n    LxtCheckTrue(S_ISDIR(Statx2.stx_mode));\r\n\r\n    //\r\n    // Test error case.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(statx(AT_FDCWD, DRVFS_BASIC_PREFIX \"/nonexistent\", 0, STATX_BASIC_STATS, &Statx2), ENOENT);\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_BASIC_PREFIX \"/testlink\");\r\n    unlink(DRVFS_BASIC_PREFIX \"/testfile\");\r\n    rmdir(DRVFS_BASIC_PREFIX);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestGetDents64Alignment(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether directory entries are correctly aligned and\r\n    padded.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtFsGetDentsAlignmentCommon(DRVFS_GETDENTS_PREFIX, FS_TEST_GETDENTS64));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestGetDentsAlignment(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether directory entries are correctly aligned and\r\n    padded.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtFsGetDentsAlignmentCommon(DRVFS_GETDENTS_PREFIX, 0));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestHardLinks(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the creation of hard links.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    struct stat Stat1;\r\n    struct stat Stat2;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_HARDLINK_TEST_DIR, 0777));\r\n    LxtCheckErrno(Fd = creat(DRVFS_HARDLINK_TEST_DIR \"/target\", 0666));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoZeroSuccess(link(DRVFS_HARDLINK_TEST_DIR \"/target\", DRVFS_HARDLINK_TEST_DIR \"/link\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(DRVFS_HARDLINK_TEST_DIR \"/target\", &Stat1));\r\n    LxtCheckErrnoZeroSuccess(lstat(DRVFS_HARDLINK_TEST_DIR \"/link\", &Stat2));\r\n    LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%lld\");\r\n\r\nErrorExit:\r\n    unlink(DRVFS_HARDLINK_TEST_DIR \"/link\");\r\n    unlink(DRVFS_HARDLINK_TEST_DIR \"/target\");\r\n    rmdir(DRVFS_HARDLINK_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestHiddenLxFsDirs(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether various VoLFs mounts are inaccessible from\r\n    DrvFs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Child[4096];\r\n    int Children;\r\n    DIR* Dir;\r\n    struct dirent* Entry;\r\n    void* PointerResult;\r\n    int Result;\r\n    char TempDirectory[4096];\r\n\r\n    Dir = NULL;\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9 || g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)\r\n    {\r\n        LxtLogInfo(\"This test is not relevant for plan9 or virtiofs.\");\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (VfsAccessLxssDir == NULL)\r\n    {\r\n        LxtLogError(\"Lxss directory not specified.\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(DrvFsTestHiddenLxFsDirsHelper(\"rootfs\", TRUE));\r\n    LxtCheckResult(DrvFsTestHiddenLxFsDirsHelper(\"rootfs/etc\", FALSE));\r\n    LxtCheckResult(DrvFsTestHiddenLxFsDirsHelper(\"rootfs/cache\", FALSE));\r\n    LxtCheckResult(DrvFsTestHiddenLxFsDirsHelper(\"rootfs/data\", FALSE));\r\n    LxtCheckResult(DrvFsTestHiddenLxFsDirsHelper(\"rootfs/home\", FALSE));\r\n    LxtCheckResult(DrvFsTestHiddenLxFsDirsHelper(\"rootfs/mnt\", FALSE));\r\n    LxtCheckResult(DrvFsTestHiddenLxFsDirsHelper(\"rootfs/root\", FALSE));\r\n\r\n    //\r\n    // Ensure that the temp directory cannot be opened.\r\n    //\r\n    // N.B. There should only be a single directory entry under the temp\r\n    //      directory. The only case when this will not be true is if handles\r\n    //      were leaked by a previous instance and are still open by the driver.\r\n    //\r\n\r\n    Children = 0;\r\n    sprintf(TempDirectory, \"%s/%s\", VfsAccessLxssDir, \"temp\");\r\n    LxtCheckNullErrno(Dir = opendir(TempDirectory));\r\n    while ((Entry = readdir(Dir)) != NULL)\r\n    {\r\n        if ((strcmp(Entry->d_name, \".\") == 0) || (strcmp(Entry->d_name, \"..\") == 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        sprintf(Child, \"temp/%s\", Entry->d_name);\r\n        LxtCheckResult(DrvFsTestHiddenLxFsDirsHelper(Child, TRUE));\r\n        Children += 1;\r\n    }\r\n\r\n    LxtCheckEqual(Children, 1, \"%d\");\r\n\r\nErrorExit:\r\n    if (Dir != NULL)\r\n    {\r\n        closedir(Dir);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestHiddenLxFsDirsHelper(const char* Child, BOOLEAN DirectChild)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests if the specified child of the LXSS directory is\r\n    inaccessible.\r\n\r\nArguments:\r\n\r\n    Child - Supplies the path of the child.\r\n\r\n    DirectChild - Supplies a value that indicates whether the child name is\r\n        a direct child.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    char Path[4096];\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    Fd = -1;\r\n    sprintf(Path, \"%s/%s\", VfsAccessLxssDir, Child);\r\n    LxtLogInfo(\"Attempting to access %s\", Path);\r\n\r\n    //\r\n    // Open directly.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(open(Path, O_RDONLY), EACCES);\r\n\r\n    //\r\n    // Open through openat.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(VfsAccessLxssDir, O_RDONLY | O_DIRECTORY));\r\n    LxtCheckErrnoFailure(openat(Fd, Child, O_RDONLY), EACCES);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // On a direct child O_PATH and stat should work. On a deeper descendant\r\n    // it will not.\r\n    //\r\n\r\n    if (DirectChild != FALSE)\r\n    {\r\n        LxtCheckErrno(Fd = open(Path, O_PATH));\r\n        LxtCheckErrnoZeroSuccess(close(Fd));\r\n        Fd = -1;\r\n        LxtCheckErrnoZeroSuccess(stat(Path, &Stat));\r\n        LxtCheckEqual((Stat.st_mode & ~S_IFMT), 0, \"%o\");\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrnoFailure(open(Path, O_PATH), EACCES);\r\n        LxtCheckErrnoFailure(stat(Path, &Stat), EACCES);\r\n    }\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestInotifyBasic(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int Id;\r\n    int Result;\r\n    int Wd[10];\r\n\r\n    //\r\n    // Test watching basic Drvfs paths.\r\n    //\r\n\r\n    LxtCheckErrno(Id = inotify_init());\r\n    LxtCheckErrno(Wd[0] = inotify_add_watch(Id, DRVFS_PREFIX, IN_ALL_EVENTS));\r\n    LxtCheckErrno(Wd[1] = inotify_add_watch(Id, DRVFS_PREFIX \"/Users\", IN_ALL_EVENTS));\r\n    LxtCheckErrno(Wd[2] = inotify_add_watch(Id, DRVFS_PREFIX \"/Windows\", IN_ALL_EVENTS));\r\n    LxtCheckErrno(Wd[3] = inotify_add_watch(Id, DRVFS_PREFIX \"/Windows/System32\", IN_ALL_EVENTS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    close(Id);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestInotifyEpoll(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    //\r\n    // TODO: Investigate why this doesn't work on Plan 9. May be a Linux 9p bug.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9)\r\n    {\r\n        LxtLogInfo(\"This test fails in VM mode.\");\r\n        return 0;\r\n    }\r\n\r\n    return LxtFsInotifyEpollCommon(DRVFS_INOTIFY_TEST_BASE_DIR);\r\n}\r\n\r\nint DrvFsTestInotifyPosixUnlinkRename(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    return LxtFsInotifyPosixUnlinkRenameCommon(DRVFS_INOTIFY_TEST_BASE_DIR);\r\n}\r\n\r\nint DrvFsTestInotifyStressUnlinkRename(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Id;\r\n    char Buf[10];\r\n    int Result;\r\n    int Index;\r\n    int UnlinkIndex;\r\n    int Tests;\r\n    char BaseDir[PATH_MAX];\r\n    char TestDir[PATH_MAX];\r\n    int ChildPid;\r\n    int SignalFd;\r\n    struct signalfd_siginfo SignalInfo;\r\n    sigset_t SignalMask;\r\n    int Status;\r\n    char TestFiles[DRVFS_INOTIFY_STRESS_NUM_FILES][PATH_MAX];\r\n    bool UseDirs;\r\n\r\n    //\r\n    // TODO: Remove once the vb test image has the fix.\r\n    //\r\n\r\n    const char* disableTest = getenv(\"WSL_DISABLE_VB_UNSTABLE_TESTS\");\r\n    if (disableTest != NULL && strcmp(disableTest, \"1\") == 0)\r\n    {\r\n        LxtLogInfo(\"WSL_DISABLE_VB_UNSTABLE_TESTS set, skipping inotify stress test\");\r\n        return 0;\r\n    }\r\n\r\n    //\r\n    // Initialize and also do cleanup if the files have not been removed.\r\n    //\r\n\r\n    sprintf(BaseDir, \"%s\", DRVFS_INOTIFY_TEST_BASE_DIR);\r\n    sprintf(TestDir, \"%s%s\", BaseDir, DRVFS_INOTIFY_STRESS_DIR);\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir(TestDir, 0777));\r\n    for (Index = 0; Index < DRVFS_INOTIFY_STRESS_NUM_FILES; Index++)\r\n    {\r\n        sprintf(TestFiles[Index], \"%sunlink_%d\", TestDir, Index);\r\n    }\r\n\r\n    //\r\n    // One thread repeatedly adds and removes inotify watches.\r\n    // Another thread repeatedly creates, modifies, renames, and unlinks the files.\r\n    //\r\n\r\n    //\r\n    // LX_TODO: There is a race in some scenarios where a previously deleted\r\n    //          directory is still being torn down and will cause creation of a\r\n    //          file of the same name to fail. In the below loop this can result\r\n    //          in spurious errors.\r\n    //\r\n\r\n#if 0\r\n#define WITH_ERROR(_op_) LxtCheckErrno(_op_)\r\n#else\r\n#define WITH_ERROR(_op_) _op_\r\n#endif\r\n\r\n    UseDirs = false;\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalBlock(SIGQUIT));\r\n        sigemptyset(&SignalMask);\r\n        sigaddset(&SignalMask, SIGQUIT);\r\n        LxtCheckErrno(SignalFd = signalfd(-1, &SignalMask, SFD_NONBLOCK));\r\n        while (1)\r\n        {\r\n            for (Index = 0; Index < DRVFS_INOTIFY_STRESS_NUM_FILES; Index++)\r\n            {\r\n                if (UseDirs)\r\n                {\r\n                    WITH_ERROR(mkdir(TestFiles[Index], 0777));\r\n                }\r\n                else\r\n                {\r\n                    WITH_ERROR(Fd = open(TestFiles[Index], O_CREAT | O_RDWR, 0600));\r\n                }\r\n\r\n                LXT_SYNCHRONIZATION_POINT();\r\n                if (read(SignalFd, &SignalInfo, sizeof(SignalInfo)) > 0)\r\n                {\r\n                    LxtLogInfo(\"Exiting child on signal\");\r\n                    goto ErrorExit;\r\n                }\r\n                else if (errno != EAGAIN)\r\n                {\r\n                    LxtLogError(\"Read of signalfd gave unexpected error: %d\", errno);\r\n                    Result = -1;\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                usleep(random() % 50);\r\n                if (!UseDirs)\r\n                {\r\n                    write(Fd, Buf, 10);\r\n                    close(Fd);\r\n                }\r\n\r\n                if (random() % 2 == 0)\r\n                {\r\n                    UnlinkIndex = (Index + 1) % DRVFS_INOTIFY_STRESS_NUM_FILES;\r\n                    WITH_ERROR(rename(TestFiles[Index], TestFiles[UnlinkIndex]));\r\n                }\r\n                else\r\n                {\r\n                    UnlinkIndex = Index;\r\n                }\r\n\r\n                if (UseDirs)\r\n                {\r\n                    WITH_ERROR(rmdir(TestFiles[UnlinkIndex]));\r\n                }\r\n                else\r\n                {\r\n                    WITH_ERROR(unlink(TestFiles[UnlinkIndex]));\r\n                }\r\n            }\r\n\r\n            UseDirs = !UseDirs;\r\n        }\r\n    }\r\n\r\n    for (Tests = 0; Tests < DRVFS_INOTIFY_STRESS_NUM_TESTS; Tests++)\r\n    {\r\n        Id = inotify_init();\r\n        for (Index = 0; Index < DRVFS_INOTIFY_STRESS_NUM_FILES; Index++)\r\n        {\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            inotify_add_watch(Id, TestFiles[Index], IN_ALL_EVENTS);\r\n        }\r\n\r\n        close(Id);\r\n    }\r\n\r\n    kill(ChildPid, SIGQUIT);\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n\r\n    for (Index = 0; Index < DRVFS_INOTIFY_STRESS_NUM_FILES; Index++)\r\n    {\r\n        rmdir(TestFiles[Index]);\r\n        unlink(TestFiles[Index]);\r\n    }\r\n\r\n    rmdir(TestDir);\r\n    rmdir(BaseDir);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestInotifyUnmountBind(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    return LxtFsInotifyUnmountBindCommon(DRVFS_INOTIFY_TEST_BASE_DIR);\r\n}\r\n\r\nint DrvFsTestLookupPath(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests path lookup for drive FS, testing corner cases of the\r\n    fast path lookup.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Fd2;\r\n    int Result;\r\n\r\n    //\r\n    // Make test directories.\r\n    //\r\n\r\n    Fd = 0;\r\n    Fd2 = 0;\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_PREFIX \"/a\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_PREFIX \"/a/b\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_PREFIX \"/a/b/c\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_PREFIX \"/a/b/c/d\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_PREFIX \"/a/b/c/d/e\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_PREFIX \"/a/b/c/d/e/f\"), 0777));\r\n\r\n    //\r\n    // Open a directory, which should use fast path lookup.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(DRVFS_PREFIX \"/a/b/c/d/e\", O_RDONLY | O_DIRECTORY));\r\n\r\n    LxtCheckResult(LxtCheckFdPath(Fd, DRVFS_PREFIX \"/a/b/c/d/e\"));\r\n\r\n    //\r\n    // Open the parent of the directory.\r\n    //\r\n\r\n    LxtCheckErrno(Fd2 = openat(Fd, \"..\", O_RDONLY | O_DIRECTORY));\r\n    LxtCheckResult(LxtCheckFdPath(Fd2, DRVFS_PREFIX \"/a/b/c/d\"));\r\n    LxtCheckErrno(close(Fd2));\r\n\r\n    //\r\n    // Open two levels up.\r\n    //\r\n\r\n    LxtCheckErrno(Fd2 = openat(Fd, \"../..\", O_RDONLY | O_DIRECTORY));\r\n    LxtCheckResult(LxtCheckFdPath(Fd2, DRVFS_PREFIX \"/a/b/c\"));\r\n    LxtCheckErrno(close(Fd2));\r\n\r\n    //\r\n    // Open three levels up.\r\n    //\r\n\r\n    LxtCheckErrno(Fd2 = openat(Fd, \"../../..\", O_RDONLY | O_DIRECTORY));\r\n    LxtCheckResult(LxtCheckFdPath(Fd2, DRVFS_PREFIX \"/a/b\"));\r\n    LxtCheckErrno(close(Fd2));\r\n\r\n    //\r\n    // Up and down.\r\n    //\r\n\r\n    LxtCheckErrno(Fd2 = openat(Fd, \"f/../../..\", O_RDONLY | O_DIRECTORY));\r\n    LxtCheckResult(LxtCheckFdPath(Fd2, DRVFS_PREFIX \"/a/b/c\"));\r\n    LxtCheckErrno(close(Fd2));\r\n\r\n    //\r\n    // Up beyond the fast path entry.\r\n    //\r\n\r\n    LxtCheckErrno(Fd2 = openat(Fd, \"../../../../..\", O_RDONLY | O_DIRECTORY));\r\n    LxtCheckResult(LxtCheckFdPath(Fd2, DRVFS_PREFIX));\r\n    LxtCheckErrno(close(Fd2));\r\n    LxtCheckErrno(Fd2 = openat(Fd, \"../../../../../..\", O_RDONLY | O_DIRECTORY));\r\n    LxtCheckResult(LxtCheckFdPath(Fd2, \"/mnt\"));\r\n    LxtCheckErrno(close(Fd2));\r\n\r\n    //\r\n    // Create a file with a relative path\r\n    //\r\n\r\n    LxtCheckErrno(Fd2 = openat(Fd, \"../../../foo\", O_RDWR | O_CREAT, 0666));\r\n    LxtCheckResult(LxtCheckFdPath(Fd2, DRVFS_PREFIX \"/a/b/foo\"));\r\n    LxtCheckErrno(close(Fd2));\r\n\r\nErrorExit:\r\n    if (Fd > 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (Fd2 > 0)\r\n    {\r\n        close(Fd2);\r\n    }\r\n\r\n    unlink(DRVFS_PREFIX \"/a/b/foo\");\r\n    rmdir(DRVFS_PREFIX \"/a/b/c/d/e/f\");\r\n    rmdir(DRVFS_PREFIX \"/a/b/c/d/e\");\r\n    rmdir(DRVFS_PREFIX \"/a/b/c/d\");\r\n    rmdir(DRVFS_PREFIX \"/a/b/c\");\r\n    rmdir(DRVFS_PREFIX \"/a/b\");\r\n    rmdir(DRVFS_PREFIX \"/a\");\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestMetadata(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests basic metadata functionality.\r\n\r\n    N.B. This test uses the metadata directory created by the Windows side.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    int Result;\r\n\r\n    Fd = -1;\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(LxtSetfsuid(2000));\r\n        LxtCheckErrno(LxtSetfsgid(2001));\r\n\r\n        //\r\n        // Make sure umask won't change the mode values.\r\n        //\r\n\r\n        LxtCheckErrno(umask(0));\r\n\r\n        //\r\n        // Check if file creation uses the thread's owner information and the\r\n        // specified mode.\r\n        //\r\n\r\n        LxtCheckErrno(Fd = creat(DRVFS_METADATA_TEST_DIR \"/testfile\", 0644));\r\n        LxtCheckClose(Fd);\r\n        LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testfile\", 2000, 2001, S_IFREG | 0644, 0));\r\n\r\n        //\r\n        // Same for a directory.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(mkdir(DRVFS_METADATA_TEST_DIR \"/testdir\", 0755));\r\n        LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testdir\", 2000, 2001, S_IFDIR | 0755, 0));\r\n\r\n        //\r\n        // Same for symlinks.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(symlink(DRVFS_METADATA_TEST_DIR \"/testfile\", DRVFS_METADATA_TEST_DIR \"/testlink\"));\r\n\r\n        LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testlink\", 2000, 2001, S_IFLNK | 0777, 0));\r\n\r\n        //\r\n        // And files created by mknod.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(mknod(DRVFS_METADATA_TEST_DIR \"/testnodereg\", S_IFREG | 0640, 0));\r\n\r\n        LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testnodereg\", 2000, 2001, S_IFREG | 0640, 0));\r\n\r\n        LxtCheckErrnoZeroSuccess(mknod(DRVFS_METADATA_TEST_DIR \"/testnodefifo\", S_IFIFO | 0660, 0));\r\n\r\n        LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testnodefifo\", 2000, 2001, S_IFIFO | 0660, 0));\r\n\r\n        LxtCheckErrnoZeroSuccess(mknod(DRVFS_METADATA_TEST_DIR \"/testnodesock\", S_IFSOCK | 0600, 0));\r\n\r\n        LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testnodesock\", 2000, 2001, S_IFSOCK | 0600, 0));\r\n\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // Device files are not tested in the child because the required capability\r\n    // is dropped when setfsuid is used.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mknod(DRVFS_METADATA_TEST_DIR \"/testnodechr\", S_IFCHR | 0666, makedev(1, 2)));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testnodechr\", 0, 0, S_IFCHR | 0666, makedev(1, 2)));\r\n\r\n    LxtCheckErrnoZeroSuccess(mknod(DRVFS_METADATA_TEST_DIR \"/testnodeblk\", S_IFBLK | 0606, makedev(3, 4)));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testnodeblk\", 0, 0, S_IFBLK | 0606, makedev(3, 4)));\r\n\r\n    //\r\n    // Check using chmod.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chmod(DRVFS_METADATA_TEST_DIR \"/testfile\", 0400));\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testfile\", 2000, 2001, S_IFREG | 0400, 0));\r\n\r\n    //\r\n    // Check using chown.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chown(DRVFS_METADATA_TEST_DIR \"/testfile\", 3000, 3001));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testfile\", 3000, 3001, S_IFREG | 0400, 0));\r\n\r\n    //\r\n    // Chown with no changes should succeed.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chown(DRVFS_METADATA_TEST_DIR \"/testfile\", -1, -1));\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testfile\", 3000, 3001, S_IFREG | 0400, 0));\r\n\r\n    //\r\n    // Set the set-group-id bit on the directory.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chmod(DRVFS_METADATA_TEST_DIR \"/testdir\", S_ISGID | 0755));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testdir\", 2000, 2001, S_IFDIR | S_ISGID | 0755, 0));\r\n\r\n    //\r\n    // Check items created in the directory inherit the group id.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_METADATA_TEST_DIR \"/testdir/childfile\", 0600));\r\n\r\n    LxtCheckClose(Fd);\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testdir/childfile\", 0, 2001, S_IFREG | 0600, 0));\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_METADATA_TEST_DIR \"/testdir/childdir\", 0700));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testdir/childdir\", 0, 2001, S_IFDIR | S_ISGID | 0700, 0));\r\n\r\n    //\r\n    // The set-user-id bit doesn't have that effect.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chmod(DRVFS_METADATA_TEST_DIR \"/testdir\", S_ISUID | 0755));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testdir\", 2000, 2001, S_IFDIR | S_ISUID | 0755, 0));\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_METADATA_TEST_DIR \"/testdir/childfile2\", 0600));\r\n\r\n    LxtCheckClose(Fd);\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testdir/childfile2\", 0, 0, S_IFREG | 0600, 0));\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_METADATA_TEST_DIR \"/testdir/childdir2\", 0700));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR \"/testdir/childdir2\", 0, 0, S_IFDIR | 0700, 0));\r\n\r\n    //\r\n    // Test adding metadata to an item that doesn't have it already.\r\n    //\r\n    // N.B. The Windows portion of the test will attempt to read this metadata\r\n    //      using NtQueryEaFile.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chmod(DRVFS_METADATA_TEST_DIR, 0775));\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR, 0, 0, S_IFDIR | 0775, 0));\r\n\r\n    LxtCheckErrnoZeroSuccess(chown(DRVFS_METADATA_TEST_DIR, 0x11223344, 0x55667788));\r\n\r\n    LxtCheckResult(DrvFsCheckStat(DRVFS_METADATA_TEST_DIR, 0x11223344, 0x55667788, S_IFDIR | 0775, 0));\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_METADATA_TEST_DIR \"/testdir/childfile\");\r\n    unlink(DRVFS_METADATA_TEST_DIR \"/testdir/childfile2\");\r\n    rmdir(DRVFS_METADATA_TEST_DIR \"/testdir/childdir\");\r\n    rmdir(DRVFS_METADATA_TEST_DIR \"/testdir/childdir2\");\r\n    unlink(DRVFS_METADATA_TEST_DIR \"/testnodereg\");\r\n    unlink(DRVFS_METADATA_TEST_DIR \"/testnodefifo\");\r\n    unlink(DRVFS_METADATA_TEST_DIR \"/testnodesock\");\r\n    unlink(DRVFS_METADATA_TEST_DIR \"/testnodechr\");\r\n    unlink(DRVFS_METADATA_TEST_DIR \"/testnodeblk\");\r\n    unlink(DRVFS_METADATA_TEST_DIR \"/testlink\");\r\n    rmdir(DRVFS_METADATA_TEST_DIR \"/testdir\");\r\n    unlink(DRVFS_METADATA_TEST_DIR \"/testfile\");\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestReFsWslPath(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests using the wslpath utility against the FAT mount point.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    //\r\n    // Previous tests may have messed up the cwd such that getcwd fails, which\r\n    // wslpath won't like.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(\"/\"));\r\n\r\n    //\r\n    // The ReFS volume in Windows is mounted on an NTFS directory mount, which\r\n    // makes it an interesting case to test with wslpath.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(DRVFS_REFS_DRIVE, DRVFS_PREFIX, true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(DRVFS_PREFIX, \"C:\\\\\" DRVFS_REFS_MOUNT_POINT, false));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestRename(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests some basic rename scenarios on drvfs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    int Result;\r\n    struct stat Stat1;\r\n    struct stat Stat2;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_RENAME_PREFIX, 0777));\r\n\r\n    //\r\n    // Create two files, and rename one over the other.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_RENAME_PREFIX \"/a\", 0777));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat1));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrno(Fd = creat(DRVFS_RENAME_PREFIX \"/b\", 0777));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat2));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckNotEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n    LxtCheckErrnoZeroSuccess(rename(DRVFS_RENAME_PREFIX \"/a\", DRVFS_RENAME_PREFIX \"/b\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_RENAME_PREFIX \"/b\", &Stat2));\r\n    LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n    LxtCheckErrnoFailure(access(DRVFS_RENAME_PREFIX \"/a\", F_OK), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_RENAME_PREFIX \"/b\"));\r\n\r\n    //\r\n    // Create two read-only files, and rename one over the other.\r\n    //\r\n\r\n    //\r\n    // Windows 10 builds are missing a fix for this test to pass.\r\n    //\r\n\r\n    const char* disableReadOnlyRenameTest = getenv(\"WSL_DISABLE_VB_UNSTABLE_TESTS\");\r\n    if (disableReadOnlyRenameTest == NULL || strcmp(disableReadOnlyRenameTest, \"1\") != 0)\r\n    {\r\n        LxtCheckErrno(Fd = open(DRVFS_RENAME_PREFIX \"/a\", O_CREAT | O_EXCL | O_RDONLY, 0444));\r\n\r\n        LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat1));\r\n        LxtCheckClose(Fd);\r\n        LxtCheckErrno(Fd = open(DRVFS_RENAME_PREFIX \"/b\", O_CREAT | O_EXCL | O_RDONLY, 0444));\r\n\r\n        LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat2));\r\n        LxtCheckClose(Fd);\r\n        LxtCheckNotEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n        LxtCheckErrnoZeroSuccess(rename(DRVFS_RENAME_PREFIX \"/a\", DRVFS_RENAME_PREFIX \"/b\"));\r\n\r\n        LxtCheckErrnoZeroSuccess(stat(DRVFS_RENAME_PREFIX \"/b\", &Stat2));\r\n        LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n        LxtCheckErrnoFailure(access(DRVFS_RENAME_PREFIX \"/a\", F_OK), ENOENT);\r\n        LxtCheckErrnoZeroSuccess(unlink(DRVFS_RENAME_PREFIX \"/b\"));\r\n\r\n        //\r\n        // Create two directories and rename one over the other.\r\n        //\r\n        // N.B. This is tested separately because non-POSIX rename on Windows needs\r\n        //      extra steps to supersede the directory.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(mkdir(DRVFS_RENAME_PREFIX \"/a\", 0777));\r\n        LxtCheckErrnoZeroSuccess(mkdir(DRVFS_RENAME_PREFIX \"/a/foo\", 0777));\r\n        LxtCheckErrnoZeroSuccess(stat(DRVFS_RENAME_PREFIX \"/a\", &Stat1));\r\n        LxtCheckErrnoZeroSuccess(mkdir(DRVFS_RENAME_PREFIX \"/b\", 0777));\r\n        LxtCheckErrnoZeroSuccess(stat(DRVFS_RENAME_PREFIX \"/b\", &Stat2));\r\n        LxtCheckNotEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n        LxtCheckErrnoZeroSuccess(rename(DRVFS_RENAME_PREFIX \"/a\", DRVFS_RENAME_PREFIX \"/b\"));\r\n\r\n        LxtCheckErrnoZeroSuccess(stat(DRVFS_RENAME_PREFIX \"/b\", &Stat2));\r\n        LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n\r\n        LxtCheckErrnoFailure(access(DRVFS_RENAME_PREFIX \"/a\", F_OK), ENOENT);\r\n        LxtCheckErrnoZeroSuccess(access(DRVFS_RENAME_PREFIX \"/b/foo\", F_OK));\r\n        LxtCheckErrnoZeroSuccess(rmdir(DRVFS_RENAME_PREFIX \"/b/foo\"));\r\n        LxtCheckErrnoZeroSuccess(rmdir(DRVFS_RENAME_PREFIX \"/b\"));\r\n\r\n        //\r\n        // Rename with different case.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(mkdir(DRVFS_RENAME_PREFIX \"/a\", 0777));\r\n        LxtCheckErrnoZeroSuccess(stat(DRVFS_RENAME_PREFIX \"/a\", &Stat1));\r\n        LxtCheckErrnoZeroSuccess(rename(DRVFS_RENAME_PREFIX \"/a\", DRVFS_RENAME_PREFIX \"/A\"));\r\n\r\n        LxtCheckErrnoZeroSuccess(stat(DRVFS_RENAME_PREFIX \"/A\", &Stat2));\r\n        LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n        LxtCheckErrnoZeroSuccess(rmdir(DRVFS_RENAME_PREFIX \"/A\"));\r\n    }\r\n    else\r\n    {\r\n        LxtLogInfo(\"WSL_DISABLE_VB_UNSTABLE_TESTS set, skipping read-only rename tests\");\r\n    }\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(chdir(DRVFS_RENAME_PREFIX));\r\n\r\n        //\r\n        // Repeat, but with relative paths.\r\n        //\r\n\r\n        LxtCheckErrno(Fd = creat(\"a\", 0777));\r\n        LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat1));\r\n        LxtCheckClose(Fd);\r\n        LxtCheckErrno(Fd = creat(\"b\", 0777));\r\n        LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat2));\r\n        LxtCheckClose(Fd);\r\n        LxtCheckNotEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n        LxtCheckErrnoZeroSuccess(rename(\"a\", \"b\"));\r\n        LxtCheckErrnoZeroSuccess(stat(\"b\", &Stat2));\r\n        LxtCheckEqual(Stat1.st_ino, Stat2.st_ino, \"%llu\");\r\n        LxtCheckErrnoFailure(access(\"a\", F_OK), ENOENT);\r\n        LxtCheckErrnoZeroSuccess(unlink(\"b\"));\r\n\r\n        //\r\n        // Rename and delete the working directory.\r\n        //\r\n        // N.B. This will not work correctly on plan 9 and virtiofs.\r\n        //\r\n\r\n        if (g_LxtFsInfo.FsType != LxtFsTypePlan9 && g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n        {\r\n            LxtCheckErrnoZeroSuccess(mkdir(\"a\", 0777));\r\n            LxtCheckErrnoZeroSuccess(chdir(\"a\"));\r\n            LxtCheckErrnoZeroSuccess(mkdir(\"b\", 0777));\r\n            LxtCheckErrnoZeroSuccess(access(\"b\", F_OK));\r\n            LxtCheckErrnoZeroSuccess(access(DRVFS_RENAME_PREFIX \"/a/b\", F_OK));\r\n            LxtCheckErrnoZeroSuccess(rename(DRVFS_RENAME_PREFIX \"/a\", DRVFS_RENAME_PREFIX \"/b\"));\r\n\r\n            LxtCheckErrnoZeroSuccess(access(\"b\", F_OK));\r\n            LxtCheckErrnoZeroSuccess(access(DRVFS_RENAME_PREFIX \"/b/b\", F_OK));\r\n            LxtCheckErrnoFailure(access(DRVFS_RENAME_PREFIX \"/a/b\", F_OK), ENOENT);\r\n\r\n            LxtCheckErrnoZeroSuccess(rmdir(\"b\"));\r\n            LxtCheckErrnoZeroSuccess(rmdir(DRVFS_RENAME_PREFIX \"/b\"));\r\n            LxtCheckResult(LxtCheckLinkTarget(\"/proc/self/cwd\", DRVFS_RENAME_PREFIX \"/b (deleted)\"));\r\n\r\n            LxtCheckErrnoZeroSuccess(chdir(\"..\"));\r\n            LxtCheckResult(LxtCheckLinkTarget(\"/proc/self/cwd\", DRVFS_RENAME_PREFIX));\r\n\r\n            LxtCheckErrnoFailure(access(\"a\", F_OK), ENOENT);\r\n            LxtCheckErrnoFailure(access(\"b\", F_OK), ENOENT);\r\n\r\n            //\r\n            // Rename an open directory.\r\n            //\r\n            // N.B. This will not work correctly on plan 9.\r\n            //\r\n\r\n            LxtCheckErrnoZeroSuccess(mkdir(\"a\", 0777));\r\n            LxtCheckErrnoZeroSuccess(mkdir(\"a/b\", 0777));\r\n            LxtCheckErrno(Fd = open(\"a\", O_RDONLY | O_DIRECTORY));\r\n            LxtCheckErrnoZeroSuccess(rename(\"a\", \"b\"));\r\n            LxtCheckErrnoZeroSuccess(faccessat(Fd, \"b\", F_OK, 0));\r\n            LxtCheckErrnoFailure(access(\"a/b\", F_OK), ENOENT);\r\n            LxtCheckErrnoFailure(access(DRVFS_RENAME_PREFIX \"/a/b\", F_OK), ENOENT);\r\n\r\n            LxtCheckErrnoZeroSuccess(access(\"b/b\", F_OK));\r\n            LxtCheckErrnoZeroSuccess(access(DRVFS_RENAME_PREFIX \"/b/b\", F_OK));\r\n            LxtCheckErrnoZeroSuccess(rmdir(\"b/b\"));\r\n            LxtCheckErrnoZeroSuccess(rmdir(\"b\"));\r\n        }\r\n\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    rmdir(DRVFS_RENAME_PREFIX \"/a/foo\");\r\n    rmdir(DRVFS_RENAME_PREFIX \"/a/b\");\r\n    rmdir(DRVFS_RENAME_PREFIX \"/a\");\r\n    rmdir(DRVFS_RENAME_PREFIX \"/A\");\r\n    unlink(DRVFS_RENAME_PREFIX \"/a\");\r\n    rmdir(DRVFS_RENAME_PREFIX \"/b/b\");\r\n    rmdir(DRVFS_RENAME_PREFIX \"/b\");\r\n    unlink(DRVFS_RENAME_PREFIX \"/b\");\r\n    rmdir(DRVFS_RENAME_PREFIX);\r\n\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestRenameAt(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the renameat system call on drivefs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int DirFd1;\r\n    int DirFd2;\r\n    int Result;\r\n\r\n    DirFd1 = -1;\r\n    DirFd2 = -1;\r\n\r\n    //\r\n    // Create a directory structure to use for the test.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_RENAMEAT_TEST_DIR, 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_RENAMEAT_TEST_DIR \"/a\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_RENAMEAT_TEST_DIR \"/a/b\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_RENAMEAT_TEST_DIR \"/a/b/c\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_RENAMEAT_TEST_DIR \"/a/b/c/d\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_RENAMEAT_TEST_DIR \"/a/b/c/d/e\"), 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir((DRVFS_RENAMEAT_TEST_DIR \"/a/b/c/d/e/f\"), 0777));\r\n\r\n    LxtCheckErrno(DirFd1 = open(DRVFS_RENAMEAT_TEST_DIR \"/a\", O_DIRECTORY));\r\n    LxtCheckErrno(DirFd2 = open(DRVFS_RENAMEAT_TEST_DIR \"/a/b/c\", O_DIRECTORY));\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(DRVFS_RENAMEAT_TEST_DIR));\r\n\r\n    LxtCheckErrno(LxtFsRenameAtCommon(DirFd1, DirFd2));\r\n\r\nErrorExit:\r\n    if (DirFd1 >= 0)\r\n    {\r\n        LxtClose(DirFd1);\r\n    }\r\n\r\n    if (DirFd2 >= 0)\r\n    {\r\n        LxtClose(DirFd2);\r\n    }\r\n\r\n    rmdir(DRVFS_RENAMEAT_TEST_DIR \"/a/b/c/d/e/f\");\r\n    rmdir(DRVFS_RENAMEAT_TEST_DIR \"/a/b/c/d/e\");\r\n    rmdir(DRVFS_RENAMEAT_TEST_DIR \"/a/b/c/d\");\r\n    rmdir(DRVFS_RENAMEAT_TEST_DIR \"/a/b/c\");\r\n    rmdir(DRVFS_RENAMEAT_TEST_DIR \"/a/b\");\r\n    rmdir(DRVFS_RENAMEAT_TEST_DIR \"/a\");\r\n    rmdir(DRVFS_RENAMEAT_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestRenameDir(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the rename system call for DrvFs directories.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtFsRenameDirCommon(DRVFS_PREFIX));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestReopenUnlinked(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether unlinked files can still be accessed\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Fd2;\r\n    char Path[100];\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    Fd = -1;\r\n    Fd2 = -1;\r\n\r\n    //\r\n    // This functionality is not supported on Plan 9 or virtiofs.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9 || g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)\r\n    {\r\n        LxtLogInfo(\"This test is not supported for plan9 or virtiofs.\");\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_BASIC_PREFIX, 0777));\r\n    LxtCheckErrno(Fd = creat(DRVFS_BASIC_PREFIX \"/testfile\", 0666));\r\n    snprintf(Path, sizeof(Path), \"/proc/self/fd/%d\", Fd);\r\n\r\n    //\r\n    // Unlink the file, and try to access it through procfs.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_BASIC_PREFIX \"/testfile\"));\r\n    LxtCheckErrnoFailure(access(DRVFS_BASIC_PREFIX \"/testfile\", F_OK), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(stat(Path, &Stat));\r\n    LxtCheckEqual(Stat.st_nlink, 0, \"%lu\");\r\n\r\n    //\r\n    // Try to reopen through procfs.\r\n    //\r\n\r\n    LxtCheckErrno(Fd2 = open(Path, O_RDONLY));\r\n\r\nErrorExit:\r\n    if (Fd2 >= 0)\r\n    {\r\n        close(Fd2);\r\n    }\r\n\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_BASIC_PREFIX \"/testfile\");\r\n    rmdir(DRVFS_BASIC_PREFIX);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestReparse(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether drvfs correctly blocks access to reparse points\r\n    other than its own symlinks.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool AbsoluteLinkFound;\r\n    bool AppExecLinkFound;\r\n    ssize_t BytesRead;\r\n    char Buffer[100];\r\n    DIR* Dir;\r\n    int DirFd;\r\n    struct dirent* Entry;\r\n    int Fd;\r\n    bool FileLinkFound;\r\n    bool JunctionFound;\r\n    void* Mapping;\r\n    void* MapResult;\r\n    void* PointerResult;\r\n    bool RelativeLinkFound;\r\n    int Result;\r\n    struct stat Stat;\r\n    bool V1LinkFound;\r\n\r\n    //\r\n    // First make sure the reparse point does not show up in the directory\r\n    // listing.\r\n    //\r\n\r\n    DirFd = -1;\r\n    Fd = -1;\r\n    Mapping = MAP_FAILED;\r\n    LxtCheckNullErrno(Dir = opendir(DRVFS_REPARSE_PREFIX));\r\n    errno = 0;\r\n    AbsoluteLinkFound = false;\r\n    RelativeLinkFound = false;\r\n    FileLinkFound = false;\r\n    JunctionFound = false;\r\n    V1LinkFound = false;\r\n    AppExecLinkFound = false;\r\n    while ((Entry = readdir(Dir)) != NULL)\r\n    {\r\n        if (strcmp(Entry->d_name, \"absolutelink\") == 0)\r\n        {\r\n            AbsoluteLinkFound = true;\r\n            LxtCheckEqual(Entry->d_type, DT_LNK, \"%d\");\r\n        }\r\n\r\n        if (strcmp(Entry->d_name, \"relativelink\") == 0)\r\n        {\r\n            RelativeLinkFound = true;\r\n            LxtCheckEqual(Entry->d_type, DT_LNK, \"%d\");\r\n        }\r\n\r\n        if (strcmp(Entry->d_name, \"filelink\") == 0)\r\n        {\r\n            FileLinkFound = true;\r\n            LxtCheckEqual(Entry->d_type, DT_LNK, \"%d\");\r\n        }\r\n\r\n        if (strcmp(Entry->d_name, \"junction\") == 0)\r\n        {\r\n            JunctionFound = true;\r\n            LxtCheckEqual(Entry->d_type, DT_LNK, \"%d\");\r\n        }\r\n\r\n        if (strcmp(Entry->d_name, \"v1link\") == 0)\r\n        {\r\n            V1LinkFound = true;\r\n            LxtCheckEqual(Entry->d_type, DT_LNK, \"%d\");\r\n        }\r\n\r\n        if (strcmp(Entry->d_name, \"appexeclink\") == 0)\r\n        {\r\n            AppExecLinkFound = true;\r\n            LxtCheckEqual(Entry->d_type, DT_REG, \"%d\");\r\n        }\r\n    }\r\n\r\n    if (errno != 0)\r\n    {\r\n        LxtLogError(\"readdir failed, errno %d: %s\", errno, strerror(errno));\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(closedir(Dir));\r\n    Dir = NULL;\r\n    LxtCheckTrue(AbsoluteLinkFound);\r\n    LxtCheckTrue(RelativeLinkFound);\r\n    LxtCheckTrue(FileLinkFound);\r\n    LxtCheckTrue(JunctionFound);\r\n    LxtCheckTrue(V1LinkFound);\r\n    LxtCheckTrue(AppExecLinkFound);\r\n\r\n    //\r\n    // Check the absolute link can be resolved, and that the target is correct\r\n    // and uses Linux separators.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckLinkTarget(DRVFS_REPARSE_PREFIX \"/absolutelink\", DRVFS_REPARSE_PREFIX \"/test/linktarget\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(DRVFS_REPARSE_PREFIX \"/absolutelink\", &Stat));\r\n    LxtCheckEqual((Stat.st_mode & S_IFMT), S_IFLNK, \"0%o\");\r\n    LxtCheckEqual(Stat.st_size, strlen(DRVFS_REPARSE_PREFIX \"/test/linktarget\"), \"%ull\");\r\n\r\n    LxtCheckErrno(DirFd = open(DRVFS_REPARSE_PREFIX \"/absolutelink\", O_DIRECTORY | O_RDONLY));\r\n\r\n    LxtCheckResult(LxtCheckFdPath(DirFd, DRVFS_REPARSE_PREFIX \"/test/linktarget\"));\r\n    LxtCheckClose(DirFd);\r\n\r\n    //\r\n    // Check the relative link can be resolved, and that the target is correct\r\n    // and uses Linux separators.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckLinkTarget(DRVFS_REPARSE_PREFIX \"/relativelink\", \"test/linktarget\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(DRVFS_REPARSE_PREFIX \"/relativelink\", &Stat));\r\n    LxtCheckEqual((Stat.st_mode & S_IFMT), S_IFLNK, \"0%o\");\r\n    LxtCheckEqual(Stat.st_size, strlen(\"test/linktarget\"), \"%ull\");\r\n\r\n    LxtCheckErrno(DirFd = open(DRVFS_REPARSE_PREFIX \"/relativelink\", O_DIRECTORY | O_RDONLY));\r\n\r\n    LxtCheckResult(LxtCheckFdPath(DirFd, DRVFS_REPARSE_PREFIX \"/test/linktarget\"));\r\n    LxtCheckClose(DirFd);\r\n\r\n    //\r\n    // Check the relative link to a file can be resolved.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckLinkTarget(DRVFS_REPARSE_PREFIX \"/filelink\", \"test/filetarget\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(DRVFS_REPARSE_PREFIX \"/filelink\", &Stat));\r\n    LxtCheckEqual((Stat.st_mode & S_IFMT), S_IFLNK, \"0%o\");\r\n    LxtCheckEqual(Stat.st_size, strlen(\"test/filetarget\"), \"%ull\");\r\n\r\n    //\r\n    // Check the junction can be resolved, and that the target is correct\r\n    // and uses Linux separators.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckLinkTarget(DRVFS_REPARSE_PREFIX \"/junction\", DRVFS_REPARSE_PREFIX \"/test/linktarget\"));\r\n\r\n    LxtCheckErrno(DirFd = open(DRVFS_REPARSE_PREFIX \"/junction\", O_DIRECTORY | O_RDONLY));\r\n\r\n    LxtCheckResult(LxtCheckFdPath(DirFd, DRVFS_REPARSE_PREFIX \"/test/linktarget\"));\r\n    LxtCheckClose(DirFd);\r\n\r\n    //\r\n    // Check the target of the V1 link can be resolved, and that its reported\r\n    // size is correct.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckLinkTarget(DRVFS_REPARSE_PREFIX \"/v1link\", \"/v1/symlink/target\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(DRVFS_REPARSE_PREFIX \"/v1link\", &Stat));\r\n    LxtCheckEqual((Stat.st_mode & S_IFMT), S_IFLNK, \"0%o\");\r\n    LxtCheckEqual(Stat.st_size, strlen(\"/v1/symlink/target\"), \"%ull\");\r\n\r\n    //\r\n    // Check that the back-compat symlink in the drive root can be resolved.\r\n    //\r\n    // N.B. This file explicitly denies FILE_READ_DATA permissions.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckLinkTarget(DRVFS_PREFIX \"/Documents and Settings\", \"/mnt/c/Users\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(DRVFS_PREFIX \"/Documents and Settings\", &Stat));\r\n\r\n    LxtCheckEqual((Stat.st_mode & S_IFMT), S_IFLNK, \"0%o\");\r\n    LxtCheckEqual(Stat.st_size, strlen(\"/mnt/c/Users\"), \"%ull\");\r\n\r\n    //\r\n    // Ensure rename works with an NT link in the path.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_REPARSE_PREFIX \"/renametest\", 0666));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoZeroSuccess(rename(DRVFS_REPARSE_PREFIX \"/renametest\", DRVFS_REPARSE_PREFIX \"/absolutelink/renametest\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(rename(DRVFS_REPARSE_PREFIX \"/absolutelink/renametest\", DRVFS_REPARSE_PREFIX \"/relativelink/renametest\"));\r\n\r\n    //\r\n    // Ensure unlink works with an NT link in the path.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_REPARSE_PREFIX \"/relativelink/renametest\"));\r\n\r\n    LxtCheckErrnoFailure(access(DRVFS_REPARSE_PREFIX \"/relativelink/renametest\", F_OK), ENOENT);\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_REPARSE_PREFIX \"/absolutelink/renametest\", 0666));\r\n\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_REPARSE_PREFIX \"/absolutelink/renametest\"));\r\n\r\n    LxtCheckErrnoFailure(access(DRVFS_REPARSE_PREFIX \"/absolutelink/renametest\", F_OK), ENOENT);\r\n\r\n    //\r\n    // Although Windows uses a directory for the first link and a file for the\r\n    // second, from WSL it should be possible to delete either using unlink,\r\n    // while rmdir should not work.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rmdir(DRVFS_REPARSE_PREFIX \"/relativelink\"), ENOTDIR);\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_REPARSE_PREFIX \"/relativelink\"));\r\n    LxtCheckErrnoFailure(faccessat(AT_FDCWD, DRVFS_REPARSE_PREFIX \"/relativelink\", F_OK, AT_SYMLINK_NOFOLLOW), ENOENT);\r\n\r\n    LxtCheckErrnoFailure(rmdir(DRVFS_REPARSE_PREFIX \"/filelink\"), ENOTDIR);\r\n    LxtCheckErrnoZeroSuccess(unlink(DRVFS_REPARSE_PREFIX \"/filelink\"));\r\n    LxtCheckErrnoFailure(faccessat(AT_FDCWD, DRVFS_REPARSE_PREFIX \"/filelink\", F_OK, AT_SYMLINK_NOFOLLOW), ENOENT);\r\n\r\n    //\r\n    // App execution aliases are treated as regular files but have generated\r\n    // contents with a fake PE header for interop purposes.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(DRVFS_REPARSE_PREFIX \"/appexeclink\", &Stat));\r\n    LxtCheckEqual(Stat.st_mode & S_IFMT, S_IFREG, \"0%o\");\r\n    LxtCheckEqual(Stat.st_size, 2, \"%llu\");\r\n    LxtCheckErrno(Fd = open(DRVFS_REPARSE_PREFIX \"/appexeclink\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = read(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(BytesRead, 2, \"%ld\");\r\n    LxtCheckMemoryEqual(Buffer, \"MZ\", 2);\r\n\r\n    //\r\n    // Check using mapping as well as this is a different code path in WSL1 and\r\n    // is what execve uses.\r\n    //\r\n\r\n    LxtCheckMapErrno(Mapping = mmap(NULL, 2, PROT_READ, MAP_PRIVATE, Fd, 0));\r\n    LxtCheckMemoryEqual(Mapping, \"MZ\", 2);\r\n    LxtCheckResult(munmap(Mapping, 2));\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        LxtCheckMapErrno(Mapping = mmap(NULL, 2, PROT_READ, MAP_SHARED, Fd, 0));\r\n        LxtCheckMemoryEqual(Mapping, \"MZ\", 2);\r\n    }\r\n    else\r\n    {\r\n        LxtLogInfo(\"TODO: virtiofs does not support MAP_SHARED\");\r\n    }\r\n\r\nErrorExit:\r\n    if (Mapping != MAP_FAILED)\r\n    {\r\n        munmap(Mapping, 2);\r\n    }\r\n\r\n    if (Dir != NULL)\r\n    {\r\n        closedir(Dir);\r\n    }\r\n\r\n    if (DirFd >= 0)\r\n    {\r\n        close(DirFd);\r\n    }\r\n\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_REPARSE_PREFIX \"/renametest\");\r\n    unlink(DRVFS_REPARSE_PREFIX \"/absolutelink/renametest\");\r\n    unlink(DRVFS_REPARSE_PREFIX \"/relativelink/renametest\");\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestSeek(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests seeking in drvfs files.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    const char TestData[] = \"abcdefghijklmnopqrstuvwxyz0123456789\";\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Create a test file.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_BASIC_PREFIX, 0777));\r\n    LxtCheckErrno(Fd = open(DRVFS_BASIC_PREFIX \"/testfile\", (O_RDWR | O_CREAT), 0666));\r\n\r\n    LxtCheckErrno(write(Fd, TestData, sizeof(TestData)));\r\n\r\n    //\r\n    // SEEK_SET.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, 0, SEEK_SET, 0, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, 100, SEEK_SET, 100, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, 10, SEEK_SET, 10, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckErrnoFailure(lseek(Fd, -100, SEEK_SET), EINVAL);\r\n\r\n    //\r\n    // SEEK_CUR.\r\n    //\r\n    // N.B. Start offset is 15 because of the read above.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, 0, SEEK_CUR, 15, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, 5, SEEK_CUR, 25, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, -10, SEEK_CUR, 20, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, 100, SEEK_CUR, 125, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckErrnoFailure(lseek(Fd, -200, SEEK_SET), EINVAL);\r\n\r\n    //\r\n    // SEEK_END.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, -10, SEEK_END, sizeof(TestData) - 10, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, 10, SEEK_END, sizeof(TestData) + 10, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckResult(DrvFsTestSeekHelper(Fd, -sizeof(TestData), SEEK_END, 0, TestData, sizeof(TestData)));\r\n\r\n    LxtCheckErrnoFailure(lseek(Fd, -100, SEEK_END), EINVAL);\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_BASIC_PREFIX \"/testfile\");\r\n    rmdir(DRVFS_BASIC_PREFIX);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestSeekHelper(int Fd, off_t Offset, int Whence, off_t ExpectedOffset, const char* TestData, int TestDataSize)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine is a helper for the seek test.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    off_t ActualOffset;\r\n    char Buffer[5];\r\n    ssize_t BytesRead;\r\n    int Result;\r\n\r\n    LxtCheckErrno(ActualOffset = lseek(Fd, Offset, Whence));\r\n    LxtCheckEqual(ActualOffset, ExpectedOffset, \"%ld\");\r\n    LxtCheckErrno(BytesRead = read(Fd, Buffer, sizeof(Buffer)));\r\n    if (ExpectedOffset >= TestDataSize)\r\n    {\r\n        LxtCheckEqual(BytesRead, 0, \"%ld\");\r\n    }\r\n    else\r\n    {\r\n        LxtCheckEqual(BytesRead, sizeof(Buffer), \"%ld\");\r\n        LxtCheckMemoryEqual(Buffer, &TestData[ExpectedOffset], sizeof(Buffer));\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestDirSeek(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests seeking in drvfs directory.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    LxtCheckResult(LxtFsDirSeekCommon(DRVFS_GETDENTS_PREFIX));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestSetup(PLXT_ARGS Args, int TestMode)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine performs setup for the drvfs tests.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\n    TestMode - Supplies the test mode.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    char CombinedOptions[100];\r\n    LXT_FS_INFO FsInfo;\r\n    char FsOptions[100];\r\n    char Options[100];\r\n    int ParentId;\r\n    int Result;\r\n\r\n    //\r\n    // Don't perform setup if help was requested.\r\n    //\r\n\r\n    if (Args->HelpRequested != false)\r\n    {\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(LxtFsGetFsInfo(DRVFS_PREFIX, &FsInfo));\r\n\r\n    //\r\n    // For the FAT test, check the FAT volume mount point can be accessed but\r\n    // not resolved (because the FAT volume is not mounted in WSL yet).\r\n    //\r\n    // N.B. This doesn't work on Plan 9 because it doesn't support junction\r\n    //      symlinks.\r\n    //\r\n\r\n    if (TestMode == DRVFS_FAT_TEST_MODE)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(faccessat(AT_FDCWD, DRVFS_PREFIX \"/\" DRVFS_FAT_MOUNT_POINT, F_OK, AT_SYMLINK_NOFOLLOW));\r\n\r\n        if (FsInfo.FsType != LxtFsTypePlan9)\r\n        {\r\n            LxtCheckErrnoFailure(readlink(DRVFS_PREFIX \"/\" DRVFS_FAT_MOUNT_POINT, Buffer, sizeof(Buffer)), EIO);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Unmount drvfs first so a new instance will be created, which allows the\r\n    // fallback mode to be set.\r\n    //\r\n    // N.B. Make sure the cwd is not inside drvfs.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(\"/\"));\r\n    LxtCheckErrnoZeroSuccess(umount(DRVFS_PREFIX));\r\n    ParentId = MountGetMountId(DRVFS_PREFIX);\r\n\r\n    //\r\n    // For the FAT tests, fallback options aren't used (FAT doesn't support\r\n    // NtQueryInformationByName or FILE_STAT_INFORMATION anyway).\r\n    //\r\n\r\n    switch (TestMode)\r\n    {\r\n    case DRVFS_FAT_TEST_MODE:\r\n        LxtCheckResult(LxtFsMountDrvFs(DRVFS_FAT_DRIVE, DRVFS_PREFIX, \"noatime,case=off\"));\r\n\r\n        LxtCheckResult(LxtFsCheckDrvFsMount(DRVFS_FAT_DRIVE, DRVFS_PREFIX, \"case=off\", ParentId, \"/\"));\r\n\r\n        Result = LXT_RESULT_SUCCESS;\r\n        break;\r\n\r\n    case DRVFS_SMB_TEST_MODE:\r\n        LxtCheckResult(LxtFsMountDrvFs(DRVFS_UNC_PATH, DRVFS_PREFIX, \"noatime,case=off\"));\r\n\r\n        LxtCheckResult(LxtFsCheckDrvFsMount(DRVFS_UNC_PATH, DRVFS_PREFIX, \"case=off\", ParentId, \"/\"));\r\n\r\n        Result = LXT_RESULT_SUCCESS;\r\n        break;\r\n\r\n    case DRVFS_METADATA_TEST_MODE:\r\n        LxtCheckResult(LxtFsMountDrvFs(DRVFS_DRIVE, DRVFS_PREFIX, \"noatime,metadata,case=dir\"));\r\n\r\n        LxtCheckResult(LxtFsCheckDrvFsMount(DRVFS_DRIVE, DRVFS_PREFIX, \"metadata,case=dir\", ParentId, \"/\"));\r\n\r\n        Result = LXT_RESULT_SUCCESS;\r\n        break;\r\n\r\n    case DRVFS_REFS_TEST_MODE:\r\n        LxtCheckResult(LxtFsMountDrvFs(DRVFS_REFS_DRIVE, DRVFS_PREFIX, \"noatime,case=dir\"));\r\n\r\n        LxtCheckResult(LxtFsCheckDrvFsMount(DRVFS_REFS_DRIVE, DRVFS_PREFIX, \"case=dir\", ParentId, \"/\"));\r\n\r\n        Result = LXT_RESULT_SUCCESS;\r\n        break;\r\n\r\n    default:\r\n\r\n        //\r\n        // Plan 9 and virtiofs don't support fallback modes, so just remount with default options in that case.\r\n        //\r\n\r\n        if (FsInfo.FsType == LxtFsTypePlan9 || FsInfo.FsType == LxtFsTypeVirtioFs)\r\n        {\r\n            LxtCheckResult(LxtFsMountDrvFs(DRVFS_DRIVE, DRVFS_PREFIX, \"noatime,case=dir\"));\r\n\r\n            LxtCheckResult(LxtFsCheckDrvFsMount(DRVFS_DRIVE, DRVFS_PREFIX, \"case=dir\", ParentId, \"/\"));\r\n        }\r\n        else\r\n        {\r\n\r\n            //\r\n            // Remount with the desired fallback mode.\r\n            //\r\n\r\n            snprintf(Options, sizeof(Options), \"case=dir,fallback=%d\", TestMode);\r\n            LxtCheckErrnoZeroSuccess(mount(DRVFS_DRIVE, DRVFS_PREFIX, DRVFS_FS_TYPE, DRVFS_MOUNT_OPTIONS, Options));\r\n\r\n            //\r\n            // Check if drvfs actually used the requested fallback mode. This guards\r\n            // against a preexisting instance (e.g. in another mount namespace)\r\n            // preventing the options from changing, or file system limitations causing\r\n            // drvfs to use a different fallback mode than requested.\r\n            //\r\n\r\n            snprintf(FsOptions, sizeof(FsOptions), \"rw,%s\", Options);\r\n            snprintf(CombinedOptions, sizeof(CombinedOptions), \"rw,noatime,%s\", Options);\r\n\r\n            LxtCheckResult(MountCheckIsMount(DRVFS_PREFIX, ParentId, DRVFS_DRIVE, DRVFS_FS_TYPE, \"/\", \"rw,noatime\", FsOptions, CombinedOptions, 0));\r\n        }\r\n\r\n        break;\r\n    }\r\n\r\n    LxtCheckResult(LxtFsGetFsInfo(DRVFS_PREFIX, &g_LxtFsInfo));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestSmbUtimensat(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the utimensat system call on drvfs for SMB shares.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return DrvFsTestUtimensatCommon(FS_UTIME_NO_SYMLINKS);\r\n}\r\n\r\nint DrvFsTestSmbUnsupported(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests unsupported functionality on SMB.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return DrvFsTestUnsupportedCommon(DRVFS_SMB_TEST_MODE);\r\n}\r\n\r\nint DrvFsTestSmbWslPath(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests using the wslpath utility against the FAT mount point.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    //\r\n    // Previous tests may have messed up the cwd such that getcwd fails, which\r\n    // wslpath won't like.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(\"/\"));\r\n\r\n    //\r\n    // Make sure translating UNC paths works.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(DRVFS_UNC_PATH, DRVFS_PREFIX, true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(DRVFS_PREFIX, \"\\\\\\\\localhost\\\\C$\", false));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestSymlink(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests symlink creation.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n\r\n    Fd = 0;\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_SYMLINK_TEST_DIR, 0777));\r\n\r\n    //\r\n    // Create a dir and a file to serve as targets.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_SYMLINK_TEST_DIR \"/dir\", 0777));\r\n    LxtCheckErrno(Fd = creat(DRVFS_SYMLINK_TEST_DIR \"/file.txt\", 0666));\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Test the scenarios that should create NT symlinks.\r\n    //\r\n    // Relative link to file.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"file.txt\", DRVFS_SYMLINK_TEST_DIR \"/ntlink1\"));\r\n\r\n    //\r\n    // Relative link to dir.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"dir\", DRVFS_SYMLINK_TEST_DIR \"/ntlink2\"));\r\n\r\n    //\r\n    // Relative links with .. components.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"..\", DRVFS_SYMLINK_TEST_DIR \"/ntlink3\"));\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"../symlink/file.txt\", DRVFS_SYMLINK_TEST_DIR \"/ntlink4\"));\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"dir/../file.txt\", DRVFS_SYMLINK_TEST_DIR \"/ntlink5\"));\r\n\r\n    //\r\n    // Relative link to another NT link (file and dir).\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"ntlink1\", DRVFS_SYMLINK_TEST_DIR \"/ntlink6\"));\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"ntlink2\", DRVFS_SYMLINK_TEST_DIR \"/ntlink7\"));\r\n\r\n    //\r\n    // Relative link to a file whose name contains escaped characters.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_SYMLINK_TEST_DIR \"/foo:bar\", 0666));\r\n    LxtCheckClose(Fd);\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"foo:bar\", DRVFS_SYMLINK_TEST_DIR \"/ntlink8\"));\r\n\r\n    //\r\n    // Test the scenarios that should create LX symlinks.\r\n    //\r\n    // Absolute link.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(DRVFS_SYMLINK_TEST_DIR \"/file.txt\", DRVFS_SYMLINK_TEST_DIR \"/lxlink1\"));\r\n\r\n    //\r\n    // Relative link crossing mount with ..\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"../..\", DRVFS_SYMLINK_TEST_DIR \"/lxlink2\"));\r\n\r\n    //\r\n    // Relative link traversing an NT link.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"ntlink2/../file.txt\", DRVFS_SYMLINK_TEST_DIR \"/lxlink3\"));\r\n\r\n    //\r\n    // Relative link to another LX link.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"lxlink1\", DRVFS_SYMLINK_TEST_DIR \"/lxlink4\"));\r\n\r\n    //\r\n    // Target doesn't exist.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"foo\", DRVFS_SYMLINK_TEST_DIR \"/lxlink5\"));\r\n\r\n    //\r\n    // Symlink points to itself.\r\n    //\r\n    // N.B. The main purpose of this check is to make sure this doesn't\r\n    //      deadlock.\r\n    //\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"lxlink6\", DRVFS_SYMLINK_TEST_DIR \"/lxlink6\"));\r\n\r\n    //\r\n    // Trying to create a symlink to itself if the target file exists should\r\n    // return EEXIST and not deadlock.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(DRVFS_SYMLINK_TEST_DIR \"/link_exist\", 0777));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoFailure(symlink(\"link_exist\", DRVFS_SYMLINK_TEST_DIR \"/link_exist\"), EEXIST);\r\n\r\n    //\r\n    // Relative link crossing mount on subdir.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(\"mytmp\", DRVFS_SYMLINK_TEST_DIR \"/dir\", \"tmpfs\", 0, NULL));\r\n\r\n    LxtCheckResult(DrvFsTestSymlinkHelper(\"dir/../file.txt\", DRVFS_SYMLINK_TEST_DIR \"/lxlink7\"));\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DRVFS_SYMLINK_TEST_DIR \"/link_exist\");\r\n    umount(DRVFS_SYMLINK_TEST_DIR \"/dir\");\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestSymlinkHelper(char* Target, char* Path)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests if a symlink can be successfully created and its path\r\n    is returned correctly.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path of the link to create.\r\n\r\n    Target - Supplies the link target.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    LxtCheckErrnoZeroSuccess(symlink(Target, Path));\r\n    LxtCheckErrnoZeroSuccess(lstat(Path, &Stat));\r\n    LxtCheckEqual(Stat.st_mode, S_IFLNK | 0777, \"0%o\");\r\n    LxtCheckResult(LxtCheckLinkTarget(Path, Target));\r\n    LxtCheckEqual(Stat.st_size, strlen(Target), \"%ull\");\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestUnsupportedCommon(int TestMode)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests unsupported functionality on FAT and SMB.\r\n\r\nArguments:\r\n\r\n    TestMode - Supplies the test mode.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[1024];\r\n    int Fd;\r\n    int Result;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_UNSUPPORTED_TEST_DIR, 0777));\r\n    LxtCheckErrno(Fd = creat(DRVFS_UNSUPPORTED_TEST_DIR \"/target\", 0666));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoFailure(symlink(DRVFS_UNSUPPORTED_TEST_DIR \"/target\", DRVFS_UNSUPPORTED_TEST_DIR \"/foo\"), EPERM);\r\n\r\n    LxtCheckErrnoFailure(mknod(DRVFS_UNSUPPORTED_TEST_DIR \"/foo\", S_IFIFO | 0666, 0), EPERM);\r\n\r\n    if (TestMode == DRVFS_FAT_TEST_MODE)\r\n    {\r\n        LxtCheckErrnoFailure(link(DRVFS_UNSUPPORTED_TEST_DIR \"/target\", DRVFS_UNSUPPORTED_TEST_DIR \"/foo\"), EPERM);\r\n\r\n        LxtCheckErrnoFailure(LxtGetxattr(DRVFS_UNSUPPORTED_TEST_DIR \"/target\", \"user.test\", Buffer, sizeof(Buffer)), ENOTSUP);\r\n\r\n        LxtCheckErrnoFailure(LxtSetxattr(DRVFS_UNSUPPORTED_TEST_DIR \"/target\", \"user.test\", Buffer, sizeof(Buffer), 0), ENOTSUP);\r\n\r\n        LxtCheckErrnoFailure(LxtListxattr(DRVFS_UNSUPPORTED_TEST_DIR \"/target\", Buffer, sizeof(Buffer)), ENOTSUP);\r\n    }\r\n\r\nErrorExit:\r\n    unlink(DRVFS_UNSUPPORTED_TEST_DIR \"/target\");\r\n    rmdir(DRVFS_UNSUPPORTED_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestUtimensat(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the utimensat system call on drvfs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return DrvFsTestUtimensatCommon(0);\r\n}\r\n\r\nint DrvFsTestUtimensatCommon(int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the utimensat system call on drvfs.\r\n\r\nArguments:\r\n\r\n    Flags - Supplies the flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    Flags |= FS_UTIME_NT_PRECISION;\r\n    LxtCheckResult(LxtFsUtimeCreateTestFiles(DRVFS_UTIME_TEST_DIR, Flags));\r\n    LxtCheckResult(LxtFsUtimeBasicCommon(DRVFS_UTIME_TEST_DIR, Flags));\r\n\r\nErrorExit:\r\n    LxtFsUtimeCleanupTestFiles(DRVFS_UTIME_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint DrvFsTestWritev(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DRVFS_WRITEV_TEST_DIR, 0777));\r\n    LxtCheckResult(LxtFsWritevCommon(DRVFS_WRITEV_TEST_DIR \"/fs_writev_test.bin\"));\r\n\r\nErrorExit:\r\n    unlink(DRVFS_WRITEV_TEST_DIR \"/fs_writev_test.bin\");\r\n    rmdir(DRVFS_WRITEV_TEST_DIR);\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/dup.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    dup.c\r\n\r\nAbstract:\r\n\r\n    This file is a test for the dup, dup2 system call.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <errno.h>\r\n#include <sys/types.h>\r\n#include <sys/wait.h>\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n#include <sys/limits.h>\r\n#endif\r\n\r\n#include <fcntl.h>\r\n\r\n#define LXT_NAME \"Dup\"\r\n#define FD_INVALID (-1)\r\n#define FD_STDIN 0\r\n#define FD_STDOUT 1\r\n#define FD_ERR 2\r\n#define MY_F_DUPFD_CLOEXEC 1030\r\n\r\nint Dup0(PLXT_ARGS Args);\r\n\r\nint Dup1(PLXT_ARGS Args);\r\n\r\nint Dup2(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {{\"Dup Basic\", Dup0}, {\"Dup Descriptor Flags\", Dup1}, {\"FCNTL Dup Error Cases\", Dup2}};\r\n\r\nint DupTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine main entry point for the test for dup, dup2 system call.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint Dup0(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates that the dup and dup2 system call and the\r\n    various parameter variances.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int DupStdIn;\r\n    int DupStdOut;\r\n    int DupFd2;\r\n\r\n    LxtCheckErrnoFailure(dup(FD_INVALID), EBADF);\r\n    LxtCheckErrnoFailure(dup(__SHRT_MAX__), EBADF);\r\n    LxtCheckResult((DupStdIn = dup(FD_STDIN)));\r\n    LxtCheckResult(close(FD_STDIN));\r\n    LxtCheckResult((DupStdOut = dup(FD_STDOUT)));\r\n\r\n    //\r\n    // Since we just closed STDIN, duping STDOUT should take the position\r\n    // of STDIN\r\n    //\r\n\r\n    if (DupStdOut != FD_STDIN)\r\n    {\r\n        LxtLogError(\"Dup, expected FD return value(%d), actual(%d).\", FD_STDIN, DupStdOut);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Now forcefully restore STDIN to its rightful position using dup2\r\n    //\r\n\r\n    LxtCheckResult((DupFd2 = dup2(DupStdIn, FD_STDIN)));\r\n    if (DupFd2 != FD_STDIN)\r\n    {\r\n        LxtLogError(\"Dup, expected FD return value(%d), actual(%d).\", FD_STDIN, DupFd2);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint Dup1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates that the variations of the dup system calls,\r\n    and their behavior w.r.t sharing file descriptor flags.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int FdProcSelf;\r\n    int FdProcSelfFlags;\r\n    int FdDup;\r\n    int FdDupFlags;\r\n    int FdTemp;\r\n\r\n    LxtCheckResult((FdProcSelf = open(\"/proc/self\", O_RDONLY | O_CLOEXEC)));\r\n    LxtCheckResult((FdDup = dup(FdProcSelf)));\r\n    LxtCheckResult((FdProcSelfFlags = fcntl(FdProcSelf, F_GETFD)));\r\n    LxtCheckResult((FdDupFlags = fcntl(FdDup, F_GETFD)));\r\n\r\n    if ((FdProcSelfFlags & FD_CLOEXEC) == 0)\r\n    {\r\n        LxtLogError(\"/proc/self FD should have the 'FD_CLOEXEC' flag set, \", \"but it is not. FD Flags(%d).\", FdProcSelfFlags);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Dup should not share file descriptor flags\r\n    //\r\n\r\n    if ((FdDupFlags & FD_CLOEXEC) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"Dup should not share File Descriptor Flags between \"\r\n            \"the old and new descriptor. New Descriptor Flags(%d).\",\r\n            FdDupFlags);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(close(FdDup));\r\n    LxtCheckResult((FdTemp = fcntl(FdProcSelf, F_DUPFD, FdDup)));\r\n    if (FdTemp != FdDup)\r\n    {\r\n        LxtLogError(\r\n            \"fcntl(F_DUPFD) should return(%d), but \"\r\n            \"it returned fd(%d).\",\r\n            FdDup,\r\n            FdTemp);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult((FdDupFlags = fcntl(FdDup, F_GETFD)));\r\n\r\n    //\r\n    // fcntl(F_DUPFD) should not set the FD_CLOEXEC in the new descriptor\r\n    //\r\n\r\n    if ((FdDupFlags & FD_CLOEXEC) != 0)\r\n    {\r\n        LxtLogError(\r\n            \"fcntl(F_DUPFD) should not share File Descriptor Flags \"\r\n            \"between the old and new descriptor. New Descriptor \"\r\n            \"Flags(%d).\",\r\n            FdDupFlags);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(close(FdDup));\r\n    LxtCheckResult((FdTemp = fcntl(FdProcSelf, MY_F_DUPFD_CLOEXEC, FdDup)));\r\n    if (FdTemp != FdDup)\r\n    {\r\n        LxtLogError(\r\n            \"fcntl(F_DUPFD_CLOEXEC) should return(%d), but \"\r\n            \"it returned fd(%d).\",\r\n            FdDup,\r\n            FdTemp);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult((FdDupFlags = fcntl(FdDup, F_GETFD)));\r\n\r\n    //\r\n    // fcntl(F_DUPFD_CLOEXEC) should set the FD_CLOEXEC in the new descriptor\r\n    //\r\n\r\n    if ((FdDupFlags & FD_CLOEXEC) == 0)\r\n    {\r\n        LxtLogError(\r\n            \"fcntl(F_DUPFD_CLOEXEC) should set the FD_CLOEXEC flag \"\r\n            \"in the new descriptor. New Descriptor \"\r\n            \"Flags(%d).\",\r\n            FdDupFlags);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint Dup2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the error cases for FCNTL calls related to dup.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrnoFailure(fcntl(FD_INVALID, F_DUPFD, 0), EBADF);\r\n    LxtCheckErrnoFailure(fcntl(FD_INVALID, MY_F_DUPFD_CLOEXEC, 0), EBADF);\r\n    LxtCheckErrnoFailure(fcntl(FD_STDIN, F_DUPFD, FD_INVALID), EINVAL);\r\n    LxtCheckErrnoFailure(fcntl(FD_STDIN, MY_F_DUPFD_CLOEXEC, FD_INVALID), EINVAL);\r\n\r\n    //\r\n    // TODO: Add cases where the FD to dup into in FCNTL is > max allowed\r\n    //\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}"
  },
  {
    "path": "test/linux/unit_tests/epoll.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    epoll.c\r\n\r\nAbstract:\r\n\r\n    This file is the epoll test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/epoll.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n#include <poll.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/socket.h>\r\n#include <sys/un.h>\r\n#include <netinet/in.h>\r\n#include <netdb.h>\r\n#include \"common.h\"\r\n\r\n#include <sys/wait.h>\r\n\r\n#define LXT_NAME \"Epoll\"\r\n#define SOCKET_NAME \"PartyInTheUsa\"\r\n#define EPOLL_DUP2_FD_COUNT 100\r\n\r\ntypedef struct _EPOLL_DUP2_CONTEXT\r\n{\r\n    int EpollFd;\r\n    int Fd[EPOLL_DUP2_FD_COUNT];\r\n} EPOLL_DUP2_CONTEXT, *PEPOLL_DUP2_CONTEXT;\r\n\r\nLXT_VARIATION_HANDLER EpollAddTest;\r\nLXT_VARIATION_HANDLER EpollBasic;\r\n\r\nint EpollBasicVariation(unsigned short ReadFlags, unsigned short WriteFlags);\r\n\r\nLXT_VARIATION_HANDLER EpollDeleteCloseFdLoop;\r\nLXT_VARIATION_HANDLER EpollDeleteTest;\r\nLXT_VARIATION_HANDLER EpollDup2FdLoop;\r\nLXT_VARIATION_HANDLER EpollHangupTestSimple;\r\nLXT_VARIATION_HANDLER EpollHangupTestUnix;\r\nLXT_VARIATION_HANDLER PPollInvalidArgument;\r\nLXT_VARIATION_HANDLER EpollModifyWhilePollingTest;\r\nLXT_VARIATION_HANDLER EpollModTest;\r\nLXT_VARIATION_HANDLER EpollPhantomEventsTest;\r\nLXT_VARIATION_HANDLER EpollRecursionTest;\r\nLXT_VARIATION_HANDLER EpollRecursionLimitTest;\r\nLXT_VARIATION_HANDLER EpollRelatedFileStress;\r\nLXT_VARIATION_HANDLER EpollSequenceTestUnix;\r\nLXT_VARIATION_HANDLER EpollSocketAcceptTest;\r\nLXT_VARIATION_HANDLER EpollSocketReadTest;\r\nLXT_VARIATION_HANDLER EpollUnalignedTest;\r\nLXT_VARIATION_HANDLER EpollVariation0;\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Basic_Variations\", EpollBasic},\r\n    {\"Epoll\", EpollVariation0},\r\n    {\"Epoll_Read\", EpollSocketReadTest},\r\n    {\"Epoll_Hangup\", EpollHangupTestSimple},\r\n    {\"Epoll_Accept\", EpollSocketAcceptTest},\r\n    {\"Epoll_Add\", EpollAddTest},\r\n    {\"Epoll_Delete\", EpollDeleteTest},\r\n    {\"Epoll_Modify_WhilePolling\", EpollModifyWhilePollingTest},\r\n    {\"Epoll_Related_File_Stress\", EpollRelatedFileStress},\r\n    {\"Epoll_Mod\", EpollModTest},\r\n    {\"Epoll_PhantomEvents\", EpollPhantomEventsTest},\r\n    {\"Ppoll invalid argument\", PPollInvalidArgument},\r\n    {\"Epoll unaligned\", EpollUnalignedTest},\r\n    {\"Epoll delete, close FD loop\", EpollDeleteCloseFdLoop},\r\n    {\"Epoll dup2 FD loop\", EpollDup2FdLoop},\r\n    {\"Epoll basic recursion\", EpollRecursionTest},\r\n    {\"Epoll recursion limit\", EpollRecursionLimitTest}};\r\n\r\nint EpollTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY();\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\n#ifndef EPOLLONESHOT\r\n#define EPOLLONESHOT (1 << 30)\r\n#endif\r\n\r\nint EpollBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    unsigned short ReadFlags[] = {EPOLLIN, EPOLLRDNORM, (EPOLLIN | EPOLLRDNORM)};\r\n    int ReadVariation;\r\n    int Result;\r\n    unsigned short WriteFlags[] = {EPOLLOUT, EPOLLWRNORM, (EPOLLOUT | EPOLLWRNORM)};\r\n    int WriteVariation;\r\n\r\n    for (ReadVariation = 0; ReadVariation < LXT_COUNT_OF(ReadFlags); ReadVariation += 1)\r\n    {\r\n\r\n        for (WriteVariation = 0; WriteVariation < LXT_COUNT_OF(ReadFlags); WriteVariation += 1)\r\n        {\r\n\r\n            Result = EpollBasicVariation(ReadFlags[ReadVariation], WriteFlags[WriteVariation]);\r\n\r\n            if (Result < 0)\r\n            {\r\n                LxtLogError(\"Failed basic variation (%d, %d)\", ReadVariation, WriteVariation);\r\n\r\n                goto cleanup;\r\n            }\r\n        }\r\n    }\r\n\r\ncleanup:\r\n    return Result;\r\n}\r\n\r\nint EpollBasicVariation(unsigned short ReadFlags, unsigned short WriteFlags)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct epoll_event EpollControlEvent;\r\n    int EpollFileDescriptor;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    struct epoll_event* InputEvent;\r\n    struct epoll_event* OutputEvent;\r\n    int PipeFileDescriptors[2] = {};\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    EpollFileDescriptor = -1;\r\n\r\n    //\r\n    // Open a pipe to test epoll.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));\r\n\r\n    //\r\n    // Pend a write.\r\n    //\r\n\r\n    LxtCheckErrno(write(PipeFileDescriptors[1], \"\\n\", 1));\r\n\r\n    //\r\n    // Create an epoll.\r\n    //\r\n\r\n    LxtCheckErrno(EpollFileDescriptor = epoll_create(1));\r\n\r\n    //\r\n    // Add the file to the epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = ReadFlags;\r\n    EpollControlEvent.data.fd = PipeFileDescriptors[0];\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));\r\n\r\n    EpollControlEvent.events = WriteFlags | EPOLLPRI;\r\n    EpollControlEvent.data.fd = PipeFileDescriptors[1];\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors[1], &EpollControlEvent));\r\n\r\n    //\r\n    // Verify the epoll is triggered.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 0));\r\n    if (Result != 2)\r\n    {\r\n        LxtLogError(\"Waiting on epoll returned %d events (expecting 2)!\", Result);\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (EpollWaitEvent[0].data.fd == PipeFileDescriptors[1])\r\n    {\r\n        InputEvent = &EpollWaitEvent[1];\r\n        OutputEvent = &EpollWaitEvent[0];\r\n    }\r\n    else\r\n    {\r\n        InputEvent = &EpollWaitEvent[0];\r\n        OutputEvent = &EpollWaitEvent[1];\r\n    }\r\n\r\n    LxtCheckEqual(InputEvent->data.fd, PipeFileDescriptors[0], \"%d\");\r\n    LxtCheckEqual(InputEvent->events, ReadFlags, \"%hd\");\r\n    LxtCheckEqual(OutputEvent->data.fd, PipeFileDescriptors[1], \"%d\");\r\n    LxtCheckEqual(OutputEvent->events, WriteFlags, \"%hd\");\r\n\r\nErrorExit:\r\n    if (EpollFileDescriptor != -1)\r\n    {\r\n        close(EpollFileDescriptor);\r\n    }\r\n\r\n    if (PipeFileDescriptors[1] != -1)\r\n    {\r\n        close(PipeFileDescriptors[1]);\r\n    }\r\n\r\n    if (PipeFileDescriptors[0] != -1)\r\n    {\r\n        close(PipeFileDescriptors[0]);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollCreateClientSocket(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n    struct sockaddr_in ServerAddress = {0};\r\n    int Socket;\r\n\r\n    //\r\n    // Create a socket.\r\n    //\r\n\r\n    Socket = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket(AF_INET, SOCK_STREAM, 0) - %s\", strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Connect to the server.\r\n    //\r\n\r\n    ServerAddress.sin_family = AF_INET;\r\n    ServerAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r\n    ServerAddress.sin_port = htons(LXT_SOCKET_DEFAULT_PORT);\r\n\r\n    Result = connect(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));\r\n\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"connect(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    Result = Socket;\r\n    Socket = 0;\r\n\r\ncleanup:\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n            Result = LXT_RESULT_FAILURE;\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollCreateClientUnixSocket(int SocketType)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n    struct sockaddr_un ServerAddress = {0};\r\n    int Socket;\r\n\r\n    //\r\n    // Create a socket.\r\n    //\r\n\r\n    Socket = socket(AF_UNIX, SocketType, 0);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket(AF_UNIX, SocketType, 0) - %s\", strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Connect to the server.\r\n    //\r\n\r\n    ServerAddress.sun_family = AF_UNIX;\r\n    strcpy(ServerAddress.sun_path, SOCKET_NAME);\r\n\r\n    Result = connect(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));\r\n\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"connect(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    Result = Socket;\r\n    Socket = 0;\r\n\r\ncleanup:\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n            Result = LXT_RESULT_FAILURE;\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollCreateListenSocket(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_in ServerAddress = {0};\r\n    int Result;\r\n    int Socket;\r\n\r\n    //\r\n    // Create a socket.\r\n    //\r\n\r\n    Socket = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket - %s\", strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Bind the socket to an ipv4 socket.\r\n    //\r\n\r\n    ServerAddress.sin_family = AF_INET;\r\n    ServerAddress.sin_addr.s_addr = INADDR_ANY;\r\n    ServerAddress.sin_port = htons(LXT_SOCKET_DEFAULT_PORT);\r\n\r\n    Result = bind(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));\r\n\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"bind(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Mark the socket as a listen socket.\r\n    //\r\n\r\n    Result = listen(Socket, LXT_SOCKET_SERVER_MAX_BACKLOG_NUM);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"listen(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    Result = Socket;\r\n    Socket = 0;\r\n\r\ncleanup:\r\n\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollCreateListenUnixSocket(int SocketType)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_un ServerAddress = {0};\r\n    int Result;\r\n    int Socket;\r\n\r\n    //\r\n    // Create a socket.\r\n    //\r\n\r\n    Socket = socket(AF_UNIX, SocketType, 0);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket - %s\", strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Bind the socket to an ipv4 socket.\r\n    //\r\n\r\n    ServerAddress.sun_family = AF_UNIX;\r\n    strcpy(ServerAddress.sun_path, SOCKET_NAME);\r\n    unlink(SOCKET_NAME);\r\n    Result = bind(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));\r\n\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"bind(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Mark the socket as a listen socket.\r\n    //\r\n\r\n    Result = listen(Socket, LXT_SOCKET_SERVER_MAX_BACKLOG_NUM);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"listen(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    Result = Socket;\r\n    Socket = 0;\r\n\r\ncleanup:\r\n\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\nint EpollHandleClientAccept(int Socket)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_in ClientAddress = {0};\r\n    socklen_t ClientLength;\r\n    int ClientSocket;\r\n\r\n    ClientLength = sizeof(ClientAddress);\r\n    ClientSocket = accept(Socket, (struct sockaddr*)&ClientAddress, &ClientLength);\r\n    if (ClientSocket < 0)\r\n    {\r\n        LxtLogError(\"accept(%d) - %s\", Socket, strerror(errno));\r\n        ClientSocket = -1;\r\n        goto cleanup;\r\n    }\r\n\r\ncleanup:\r\n\r\n    return ClientSocket;\r\n}\r\n\r\nconst char* DataToWrite[] = {\r\n    \"<This is the first message> \",\r\n    \"<This is another message> \",\r\n    \"<Dumbledore is dead> \",\r\n    \"<Harry Potter must not go back to Hogwarts> \",\r\n    \"<There must always be a stark in Winterfell>\",\r\n};\r\n\r\nconst int WriteItemCount = sizeof(DataToWrite) / sizeof(DataToWrite[0]);\r\n\r\nint EpollSocketReadTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[256];\r\n    int Result;\r\n    int EpollFileDescriptor;\r\n    int FileDescriptor1;\r\n    int FileDescriptor2;\r\n    struct epoll_event EpollControlEvent;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    int ChildPid;\r\n    int Index;\r\n    int ChildStatus;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileDescriptor1 = -1;\r\n    FileDescriptor2 = -1;\r\n    EpollFileDescriptor = -1;\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Create the server socket.\r\n    //\r\n\r\n    LxtLogInfo(\"[Setup] About to create server socket...\");\r\n\r\n    FileDescriptor1 = EpollCreateListenSocket();\r\n    if (FileDescriptor1 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"[Setup] Could not create socket! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Fork to create a server and a client.\r\n    //\r\n\r\n    LxtLogInfo(\"[Setup] About to fork...\");\r\n\r\n    ChildPid = fork();\r\n\r\n    if (ChildPid == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"[Setup] Fork failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        LxtLogInfo(\"[Client] Waiting 2 seconds to let server block...\");\r\n\r\n        usleep(2 * 1000 * 1000);\r\n\r\n        LxtLogInfo(\"[Client] Connecting to server...\");\r\n\r\n        FileDescriptor2 = EpollCreateClientSocket();\r\n\r\n        LxtLogInfo(\"[Client] Connected to server, fd = %d\", FileDescriptor2);\r\n\r\n        LxtLogInfo(\"[Client] Sleeping for 5 seconds with open socket\");\r\n\r\n        usleep(5 * 1000 * 1000);\r\n\r\n        //\r\n        // Create an epoll container.\r\n        //\r\n\r\n        EpollFileDescriptor = epoll_create(1);\r\n\r\n        if (EpollFileDescriptor == -1)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"[Client] Could not create Epoll! %d\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        //\r\n        // Add the connected socket to the epoll.\r\n        //\r\n\r\n        EpollControlEvent.events = EPOLLIN;\r\n        EpollControlEvent.data.fd = FileDescriptor2;\r\n\r\n        Result = epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent);\r\n\r\n        if (Result == -1)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"[Client] Could not add file to epoll! %d\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        //\r\n        // Wait for data to be available with a timeout.\r\n        //\r\n\r\n        while (1)\r\n        {\r\n\r\n            LxtLogInfo(\"[Client] Waiting on epoll with 15 second timeout ...\");\r\n\r\n            Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 15000);\r\n\r\n            LxtLogInfo(\"[Client] Epoll returned %d events\", Result);\r\n\r\n            if (Result != 1)\r\n            {\r\n                LxtLogError(\"[Client] Wait on epoll failed! %d\", Result);\r\n                Result = -1;\r\n                goto cleanup;\r\n            }\r\n\r\n            LxtLogInfo(\"[Client] Event: {%d, %x} \", EpollWaitEvent[0].data.fd, EpollWaitEvent[0].events);\r\n\r\n            if (EpollWaitEvent[0].data.fd != FileDescriptor2)\r\n            {\r\n                LxtLogError(\"[Client] Epoll wait satisfied with wrong user data! %d\", EpollWaitEvent[0].data.fd);\r\n                Result = -1;\r\n                goto cleanup;\r\n            }\r\n\r\n            if (EpollWaitEvent[0].events != EPOLLIN)\r\n            {\r\n                LxtLogError(\"[Client] Epoll wait satisfied with wrong events! 0x%x\", EpollWaitEvent[0].events);\r\n                Result = -1;\r\n                goto cleanup;\r\n            }\r\n\r\n            memset(Buffer, 0, sizeof(Buffer));\r\n            Result = read(FileDescriptor2, Buffer, sizeof(Buffer));\r\n            if (Result < 0)\r\n            {\r\n                Result = -1;\r\n                LxtLogError(\"[Client] Read on socket failed! %d\", Result);\r\n                goto cleanup;\r\n            }\r\n\r\n            LxtLogInfo(\"[Client] read %d bytes: %s ...\", Result, Buffer);\r\n\r\n            if (Result == 0)\r\n            {\r\n                LxtLogInfo(\"[Client] exiting ...\");\r\n                goto cleanup;\r\n            }\r\n        }\r\n    }\r\n\r\n    //\r\n    // Accept an incoming connection.\r\n    //\r\n\r\n    FileDescriptor2 = EpollHandleClientAccept(FileDescriptor1);\r\n\r\n    LxtLogInfo(\"[Server] Writing to socket %d times!\", WriteItemCount);\r\n\r\n    for (Index = 0; Index < WriteItemCount; Index += 1)\r\n    {\r\n\r\n        Result = write(FileDescriptor2, DataToWrite[Index], strlen(DataToWrite[Index]));\r\n\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"[Server] Write %d failed %d\", Index, Result);\r\n\r\n            goto cleanup;\r\n        }\r\n\r\n        LxtLogInfo(\"[Server] Write (%d, %s, %d) -> %d!\", FileDescriptor2, DataToWrite[Index], strlen(DataToWrite[Index]) + (Index == WriteItemCount - 1), Result);\r\n\r\n        if ((Index % 5) == 0)\r\n        {\r\n            usleep(5 * 1000 * 1000);\r\n        }\r\n    }\r\n\r\n    usleep(5 * 1000 * 1000);\r\n\r\n    LxtLogInfo(\"[Server] Closing client fd = %d\", FileDescriptor2);\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n        FileDescriptor2 = -1;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Waiting for child to exit\");\r\n\r\n    ChildStatus = 0;\r\n    wait(&ChildStatus);\r\n\r\n    LxtLogInfo(\"[Server] Child WIFEXITED=%d WEXITSTATUS=%d\", WIFEXITED(ChildStatus), WEXITSTATUS(ChildStatus));\r\n\r\n    //\r\n    // Determine if the test passed or failed.\r\n    //\r\n\r\n    if ((Result < 0) || (WIFEXITED(ChildStatus) == 0) || (WEXITSTATUS(ChildStatus) != 0))\r\n    {\r\n\r\n        LxtLogInfo(\"[Server] Test failed!\");\r\n        Result = -1;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Done\");\r\n\r\ncleanup:\r\n\r\n    if (FileDescriptor1 != -1)\r\n    {\r\n        close(FileDescriptor1);\r\n    }\r\n\r\n    if (EpollFileDescriptor != -1)\r\n    {\r\n        close(EpollFileDescriptor);\r\n    }\r\n\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtLogInfo(\"[Child] Exit with %d!\", Result);\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollHangupTestSimple(PLXT_ARGS Args)\r\n{\r\n\r\n    char Buffer[256];\r\n    int Result;\r\n    int EpollFileDescriptor;\r\n    int FileDescriptor1;\r\n    int FileDescriptor2;\r\n    struct epoll_event EpollControlEvent;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    int ChildPid;\r\n    int Index;\r\n    int ChildStatus;\r\n    int ReadAttempts;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileDescriptor1 = -1;\r\n    FileDescriptor2 = -1;\r\n    EpollFileDescriptor = -1;\r\n    ChildPid = -1;\r\n    LxtLogInfo(\"[Setup] Starting simple hangup test\");\r\n\r\n    //\r\n    // Create the server socket.\r\n    //\r\n\r\n    LxtLogInfo(\"[Setup] About to create server socket\");\r\n\r\n    FileDescriptor1 = EpollCreateListenSocket();\r\n    if (FileDescriptor1 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"[Setup] Could not create server socket %d!\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    LxtLogInfo(\"[Setup] Created server socket successfully\");\r\n\r\n    //\r\n    // Fork to create a server and a client.\r\n    //\r\n\r\n    LxtLogInfo(\"[Setup] About to fork\");\r\n\r\n    ChildPid = fork();\r\n\r\n    if (ChildPid == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"[Setup] Fork failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        LxtLogInfo(\"[Client] Connecting to server...\");\r\n\r\n        FileDescriptor2 = EpollCreateClientSocket();\r\n\r\n        LxtLogInfo(\"[Client] Connected to server, fd = %d\", FileDescriptor2);\r\n\r\n        LxtLogInfo(\"[Client] Sleeping for 3 seconds with open socket\");\r\n\r\n        usleep(3 * 1000 * 1000);\r\n\r\n        //\r\n        // Create an epoll container.\r\n        //\r\n\r\n        EpollFileDescriptor = epoll_create(1);\r\n\r\n        if (EpollFileDescriptor == -1)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"[Client] Could not create Epoll %d!\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        //\r\n        // Add the connected socket to the epoll.\r\n        //\r\n\r\n        EpollControlEvent.events = EPOLLIN;\r\n        EpollControlEvent.data.fd = FileDescriptor2;\r\n\r\n        Result = epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent);\r\n\r\n        if (Result == -1)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"[Client] Could not add file to epoll %d!\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        //\r\n        // Wait for data to be available with a timeout.\r\n        //\r\n\r\n        ReadAttempts = 0;\r\n        while (1)\r\n        {\r\n\r\n            LxtLogInfo(\"[Client] Waiting on epoll with 15 second timeout\");\r\n\r\n            Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 15000);\r\n\r\n            LxtLogInfo(\"[Client] Epoll returned %d events\", Result);\r\n\r\n            if (Result == 0)\r\n            {\r\n                LxtLogError(\"[Client] No events returned, exiting!\");\r\n                Result = -1;\r\n                goto cleanup;\r\n            }\r\n\r\n            if (Result != 1)\r\n            {\r\n                LxtLogError(\"[Client] Wait on epoll returned too many events, exiting!\");\r\n                Result = -1;\r\n                goto cleanup;\r\n            }\r\n\r\n            LxtLogInfo(\"[Client] Event: {%d, %x} \", EpollWaitEvent[0].data.fd, EpollWaitEvent[0].events);\r\n\r\n            if (EpollWaitEvent[0].data.fd != FileDescriptor2)\r\n            {\r\n                LxtLogError(\"[Client] Epoll wait satisfied with wrong user data! %d\", EpollWaitEvent[0].data.fd);\r\n                Result = -1;\r\n                goto cleanup;\r\n            }\r\n\r\n            if (EpollWaitEvent[0].events != EPOLLIN)\r\n            {\r\n                LxtLogError(\"[Client] Epoll wait satisfied with wrong events! 0x%x\", EpollWaitEvent[0].events);\r\n                Result = -1;\r\n                goto cleanup;\r\n            }\r\n\r\n            memset(Buffer, 0, sizeof(Buffer));\r\n            Result = read(FileDescriptor2, Buffer, sizeof(Buffer));\r\n            if (Result < 0)\r\n            {\r\n                Result = errno;\r\n                LxtLogError(\"[Client] Read on socket failed! %d\", Result);\r\n                goto cleanup;\r\n            }\r\n\r\n            LxtLogInfo(\"[Client] Read (%d) -> %d bytes: %s\", FileDescriptor2, Result, Buffer);\r\n\r\n            if (Result == 0)\r\n            {\r\n                ReadAttempts += 1;\r\n                if (ReadAttempts < 2)\r\n                {\r\n                    LxtLogInfo(\"[Client] Continuing even through read 0 bytes.\");\r\n                    continue;\r\n                }\r\n\r\n                LxtLogInfo(\"[Client] Exiting because read returned 0 bytes.\");\r\n                goto cleanup;\r\n            }\r\n        }\r\n    }\r\n\r\n    //\r\n    // Accept an incoming connection.\r\n    //\r\n\r\n    LxtLogInfo(\"[Server] Waiting for incoming connections...\");\r\n\r\n    FileDescriptor2 = EpollHandleClientAccept(FileDescriptor1);\r\n\r\n    LxtLogInfo(\"[Server] Connected to client, fd = %d\", FileDescriptor2);\r\n\r\n    Result = write(FileDescriptor2, \"Party On\", strlen(\"Party On\") + 1);\r\n\r\n    LxtLogInfo(\"[Server] Write (%d, %s, %d) -> %d\", FileDescriptor2, \"Party On\", strlen(\"Party On\") + 1, Result);\r\n\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"[Server] Write failed %s\", strerror(errno));\r\n        goto cleanup;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Closing client fd = %d\", FileDescriptor2);\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n        FileDescriptor2 = -1;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Waiting for child to exit\");\r\n\r\n    wait(&ChildStatus);\r\n\r\n    LxtLogInfo(\"[Server] Child WIFEXITED=%d WEXITSTATUS=%d\", WIFEXITED(ChildStatus), WEXITSTATUS(ChildStatus));\r\n\r\n    //\r\n    // Determine if the test passed or failed.\r\n    //\r\n\r\n    if ((Result < 0) || (WIFEXITED(ChildStatus) == 0) || (WEXITSTATUS(ChildStatus) != 0))\r\n    {\r\n\r\n        LxtLogInfo(\"[Server] Test failed!\");\r\n        Result = -1;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Done\");\r\n\r\ncleanup:\r\n\r\n    if (FileDescriptor1 != -1)\r\n    {\r\n        close(FileDescriptor1);\r\n    }\r\n\r\n    if (EpollFileDescriptor != -1)\r\n    {\r\n        close(EpollFileDescriptor);\r\n    }\r\n\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollSocketAcceptTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[256];\r\n    int Result;\r\n    int EpollFileDescriptor;\r\n    int FileDescriptor1;\r\n    int FileDescriptor2;\r\n    struct epoll_event EpollControlEvent;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    int ChildPid;\r\n    int Index;\r\n    int ChildStatus;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileDescriptor1 = -1;\r\n    FileDescriptor2 = -1;\r\n    EpollFileDescriptor = -1;\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Create a socket that will be added for epoll.\r\n    //\r\n\r\n    FileDescriptor1 = EpollCreateListenSocket();\r\n    if (FileDescriptor1 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create socket! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Create an epoll container.\r\n    //\r\n\r\n    EpollFileDescriptor = epoll_create(1);\r\n\r\n    if (EpollFileDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create Epoll! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Add the socket to the epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = FileDescriptor1;\r\n\r\n    Result = epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor1, &EpollControlEvent);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not add file to epoll! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Wait for data to be available with a timeout. No data should arrive.\r\n    //\r\n\r\n    LxtLogInfo(\"[Setup] Waiting on epoll to timeout for 5s...\");\r\n\r\n    Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 5000);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Waiting on epoll failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (Result != 0)\r\n    {\r\n        LxtLogError(\"Waiting on epoll succeeded but returned non-zero events! %d\", Result);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    LxtLogInfo(\"[Setup] Wait on epoll returned no data, as expected...\");\r\n\r\n    //\r\n    // Fork to create a server and a client.\r\n    //\r\n\r\n    LxtLogInfo(\"[Setup] About to fork...\");\r\n\r\n    ChildPid = fork();\r\n\r\n    if (ChildPid == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Fork failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        LxtLogInfo(\"[Client] Waiting 2 seconds to let server block...\");\r\n\r\n        usleep(2 * 1000 * 1000);\r\n\r\n        LxtLogInfo(\"[Client] Connecting to server...\");\r\n\r\n        FileDescriptor2 = EpollCreateClientSocket();\r\n\r\n        LxtLogInfo(\"[Client] Connected to server, fd =%d\", FileDescriptor2);\r\n\r\n        LxtLogInfo(\"[Client] Sleeping for 2 seconds with open socket\");\r\n\r\n        usleep(2 * 1000 * 1000);\r\n\r\n        Result = write(FileDescriptor2, \"Party On\", strlen(\"Party On\") + 1);\r\n\r\n        LxtLogInfo(\"[Client] Write (%d, %s, %d) -> %d\", FileDescriptor2, \"Party On\", strlen(\"Party On\") + 1, Result);\r\n\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"[Server] Write failed %s\", strerror(errno));\r\n            goto cleanup;\r\n        }\r\n\r\n        LxtLogInfo(\"[Client] Closing socket %d\", FileDescriptor2);\r\n\r\n        if (close(FileDescriptor2) != 0)\r\n        {\r\n            LxtLogError(\"[Client] Closing socket %d failed - %s\", FileDescriptor2, strerror(errno));\r\n        }\r\n\r\n        usleep(2 * 1000 * 1000);\r\n\r\n        FileDescriptor2 = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // The server should wait for data to become available on the socket.\r\n    //\r\n\r\n    LxtLogInfo(\"[Server] Waiting on epoll to timeout for 10s...\");\r\n\r\n    Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 10000);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"[Server] Waiting on epoll failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (Result != 1)\r\n    {\r\n        LxtLogError(\"[Server] Waiting on epoll returned unexpected events: %d!\", Result);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    if (EpollWaitEvent[0].data.fd != FileDescriptor1)\r\n    {\r\n        LxtLogError(\"[Server] Epoll wait satisfied with wrong user data! %d\", EpollWaitEvent[0].data.fd);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    if (EpollWaitEvent[0].events != EPOLLIN)\r\n    {\r\n        LxtLogError(\"[Server] Epoll wait satisfied with wrong events! 0x%x\", EpollWaitEvent[0].events);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    FileDescriptor2 = EpollHandleClientAccept(FileDescriptor1);\r\n    if (FileDescriptor2 < 0)\r\n    {\r\n        LxtLogError(\"[Server] Accept failed!\");\r\n        Result = -1;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Accepted a request successfully...\");\r\n\r\n    //\r\n    // The server should timeout now if it waits for data again.\r\n    //\r\n\r\n    LxtLogInfo(\"[Server] Waiting on epoll to timeout for 5s...\");\r\n\r\n    Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 5000);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Waiting on epoll failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (Result != 0)\r\n    {\r\n        LxtLogError(\"Waiting on epoll succeeded but returned non-zero events! %d\", Result);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Add the socket to the epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = FileDescriptor2;\r\n\r\n    Result = epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not add file to epoll! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Wait for data to be available with a timeout. No data should arrive.\r\n    //\r\n\r\n    while (1)\r\n    {\r\n\r\n        LxtLogInfo(\"[Setup] Waiting on epoll to timeout for 5s...\");\r\n\r\n        Result = epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 5000);\r\n\r\n        if (Result == -1)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"Waiting on epoll failed! %d\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        if (Result == 0)\r\n        {\r\n            LxtLogError(\"Waiting on epoll succeeded but returned zero events!\");\r\n            Result = -1;\r\n            goto cleanup;\r\n        }\r\n\r\n        LxtLogInfo(\"[Server] Event: {%d, %x} \", EpollWaitEvent[0].data.fd, EpollWaitEvent[0].events);\r\n\r\n        if (EpollWaitEvent[0].data.fd != FileDescriptor2)\r\n        {\r\n            LxtLogError(\"[Server] Epoll wait satisfied with wrong user data! %d\", EpollWaitEvent[0].data.fd);\r\n            Result = -1;\r\n            goto cleanup;\r\n        }\r\n\r\n        if (EpollWaitEvent[0].events != EPOLLIN)\r\n        {\r\n            LxtLogError(\"[Server] Epoll wait satisfied with wrong events! 0x%x\", EpollWaitEvent[0].events);\r\n            Result = -1;\r\n            goto cleanup;\r\n        }\r\n\r\n        memset(Buffer, 0, sizeof(Buffer));\r\n        Result = read(FileDescriptor2, Buffer, sizeof(Buffer));\r\n        if (Result < 0)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"[Client] Read on socket failed! %d\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        LxtLogInfo(\"[Server] read %d bytes: %s ...\", Result, Buffer);\r\n\r\n        if (Result == 0)\r\n        {\r\n            LxtLogInfo(\"[Server] exiting ...\");\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Waiting on child\");\r\n\r\n    wait(&ChildStatus);\r\n\r\n    LxtLogInfo(\"[Server] Child WIFEXITED=%d WEXITSTATUS=%d\", WIFEXITED(ChildStatus), WEXITSTATUS(ChildStatus));\r\n\r\n    //\r\n    // Determine if the test passed or failed.\r\n    //\r\n\r\n    if ((Result < 0) || (WIFEXITED(ChildStatus) == 0) || (WEXITSTATUS(ChildStatus) != 0))\r\n    {\r\n\r\n        LxtLogInfo(\"[Server] Test failed!\");\r\n        Result = -1;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Done\");\r\n\r\ncleanup:\r\n\r\n    if (FileDescriptor1 != -1)\r\n    {\r\n        close(FileDescriptor1);\r\n    }\r\n\r\n    if (EpollFileDescriptor != -1)\r\n    {\r\n        close(EpollFileDescriptor);\r\n    }\r\n\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollVariation0(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[10];\r\n    int BytesReadWrite;\r\n    int ChildPid;\r\n    struct epoll_event EpollControlEvent;\r\n    int FileDescriptor1;\r\n    int FileDescriptor2;\r\n    int EpollFileDescriptor;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    int Index;\r\n    int Master;\r\n    char PtsDevName[50];\r\n    int Result;\r\n    int Status;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    Master = -1;\r\n    FileDescriptor1 = -1;\r\n    FileDescriptor2 = -1;\r\n    EpollFileDescriptor = -1;\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Open a file that will be added to the epoll.\r\n    //\r\n\r\n    LxtCheckErrno(Master = open(\"/dev/ptmx\", O_RDWR));\r\n    LxtCheckErrno(grantpt(Master));\r\n    LxtCheckErrno(unlockpt(Master));\r\n    LxtCheckErrno(ptsname_r(Master, PtsDevName, sizeof(PtsDevName)));\r\n    LxtLogInfo(\"Subordinate Device is:%s\", PtsDevName);\r\n    LxtCheckErrno(FileDescriptor1 = open(PtsDevName, O_RDWR));\r\n    LxtCheckErrno(FileDescriptor2 = open(PtsDevName, O_RDWR));\r\n\r\n    //\r\n    // Create an epoll.\r\n    //\r\n\r\n    LxtCheckErrno(EpollFileDescriptor = epoll_create(1));\r\n\r\n    //\r\n    // Add the file to the epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = FileDescriptor1;\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor1, &EpollControlEvent));\r\n\r\n    //\r\n    // Add the file to the epoll again and it should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor1, &EpollControlEvent), EEXIST);\r\n\r\n    //\r\n    // Add the epoll file descriptor to itself and it should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, EpollFileDescriptor, &EpollControlEvent), EINVAL);\r\n\r\n    //\r\n    // Add the second file descriptor to the epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLOUT | EPOLLPRI;\r\n    EpollControlEvent.data.fd = FileDescriptor2;\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent));\r\n\r\n    //\r\n    // Remove the second file from the epoll.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_DEL, FileDescriptor2, NULL));\r\n\r\n    //\r\n    // Try adding back the first file descriptor as it should still be there.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor1, &EpollControlEvent), EEXIST);\r\n\r\n    //\r\n    // Add the second file descriptor back to the epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLOUT | EPOLLPRI;\r\n    EpollControlEvent.data.fd = FileDescriptor2;\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, FileDescriptor2, &EpollControlEvent));\r\n\r\n    //\r\n    // Modify the second file descriptor in the epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN | EPOLLERR | EPOLLPRI | EPOLLET;\r\n    EpollControlEvent.data.fd = FileDescriptor2;\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_MOD, FileDescriptor2, &EpollControlEvent));\r\n\r\n    //\r\n    // Wait for the epoll with a timeout.\r\n    //\r\n\r\n    LxtLogInfo(\"Waiting on epoll to timeout for 1s...\");\r\n\r\n    LxtCheckErrnoZeroSuccess(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 200));\r\n\r\n    //\r\n    // Fork to create another thread to signal the epoll.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Wait to allow parent to block on epoll.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtLogInfo(\"T2: Waiting to make read data available...\");\r\n        usleep(200 * 1000);\r\n        LxtLogInfo(\"T2: Making data available for read...\");\r\n        LxtCheckErrno((BytesReadWrite = write(Master, \"\\n\", 1)));\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        usleep(200 * 1000);\r\n        LxtLogInfo(\"T2: Making data available for read...\");\r\n        LxtCheckErrno(BytesReadWrite = read(FileDescriptor1, Buffer, sizeof(Buffer)));\r\n        LxtCheckEqual(BytesReadWrite, 1, \"%d\");\r\n        LxtCheckErrno((BytesReadWrite = write(Master, \"\\n\", 1)));\r\n\r\n        LxtLogInfo(\"T2: Waiting to allow T1 to wake, consume edge trigger, and wait again...\");\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        usleep(200 * 1000);\r\n        LxtLogInfo(\"T2: Clearing edge-trigger on descriptor2...\");\r\n        LxtCheckErrno(BytesReadWrite = read(FileDescriptor1, Buffer, sizeof(Buffer)));\r\n        LxtCheckEqual(BytesReadWrite, 1, \"%d\");\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtLogInfo(\"T2: Making data available for read...\");\r\n        LxtCheckErrno((BytesReadWrite = write(Master, \"\\n\", 1)));\r\n\r\n        Result = LXT_RESULT_SUCCESS;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait on epoll to be woken by the child. Do this twice and the second time\r\n    // should immediately return since the first epoll is still signalled.\r\n    //\r\n\r\n    for (Index = 0; Index < 2; Index += 1)\r\n    {\r\n\r\n        LxtLogInfo(\"T1: Waiting for epoll to be signaled for first descriptor...\");\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, -1));\r\n        LxtCheckEqual(Result, (2 - Index), \"%d\");\r\n        if (EpollWaitEvent[0].data.fd == FileDescriptor1)\r\n        {\r\n            LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor1, \"%d\");\r\n            LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, \"%d\");\r\n            if (Result > 1)\r\n            {\r\n                LxtCheckEqual(EpollWaitEvent[1].data.fd, FileDescriptor2, \"%d\");\r\n                LxtCheckEqual(EpollWaitEvent[1].events, EPOLLIN, \"%d\");\r\n            }\r\n        }\r\n        else\r\n        {\r\n            LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor2, \"%d\");\r\n            LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, \"%d\");\r\n            if (Result > 1)\r\n            {\r\n                LxtCheckEqual(EpollWaitEvent[1].data.fd, FileDescriptor1, \"%d\");\r\n                LxtCheckEqual(EpollWaitEvent[1].events, EPOLLIN, \"%d\");\r\n            }\r\n        }\r\n\r\n        // LxtCheckErrno(BytesReadWrite = read(FileDescriptor1, Buffer, sizeof(Buffer)));\r\n        // LxtCheckEqual(BytesReadWrite, 1, \"%d\");\r\n        if (Index == 0)\r\n        {\r\n\r\n            //\r\n            // Modify the first file descriptor in the epoll to be one shot. This\r\n            // way, when it's waited on again in the next iteration of the loop,\r\n            // it will be disabled.\r\n            //\r\n\r\n            EpollControlEvent.events = EPOLLIN | EPOLLONESHOT;\r\n            EpollControlEvent.data.fd = FileDescriptor1;\r\n            LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_MOD, FileDescriptor1, &EpollControlEvent));\r\n        }\r\n    }\r\n\r\n    //\r\n    // Now wait for the epoll to be signalled by the child for the second\r\n    // descriptor. That registration was with an edge trigger.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtLogInfo(\"T1: Waiting for epoll to be signaled for second descriptor...\");\r\n    LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, -1));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor2, \"%d\");\r\n    LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, \"%d\");\r\n\r\n    //\r\n    // Wait for the epoll again and it should timeout this time due to edge\r\n    // trigger.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtLogInfo(\"Waiting on epoll to timeout...\");\r\n    LxtCheckErrnoZeroSuccess(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 200));\r\n\r\n    //\r\n    // Signal the event again, but descriptor 1 is marked oneshot so it still\r\n    // won't deliver any notifications.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtLogInfo(\"Waiting on epoll (T1 to ready data) indefinitely...\");\r\n    LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, -1));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor2, \"%d\");\r\n    LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, \"%d\");\r\n\r\n    //\r\n    // Making data unavailable for both descriptors. And generating error on\r\n    // second descriptor.\r\n    //\r\n\r\n    LxtCheckClose(Master);\r\n    LxtLogInfo(\"Waiting on epoll for error indefinitely...\");\r\n    LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, -1));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    LxtCheckEqual(EpollWaitEvent[0].data.fd, FileDescriptor2, \"%d\");\r\n\r\n    //\r\n    // TODO_LX: Currently only signalling EPOLLHUP.\r\n    //\r\n    // LxtCheckEqual(EpollWaitEvent[0].events, (EPOLLHUP | EPOLLERR | EPOLLIN), \"%d\");\r\n    //\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    //\r\n    // Close the file descriptors in a specific order to exercise both code\r\n    // paths where a file is closed while in an epoll and epoll is closed while\r\n    // having files in it.\r\n    //\r\n\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n    }\r\n\r\n    if (EpollFileDescriptor != -1)\r\n    {\r\n        close(EpollFileDescriptor);\r\n    }\r\n\r\n    if (FileDescriptor1 != -1)\r\n    {\r\n        close(FileDescriptor1);\r\n    }\r\n\r\n    if (Master != -1)\r\n    {\r\n        close(Master);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\ntypedef struct _READD_TEST_DATA\r\n{\r\n    int Fd;\r\n    int EpollFd;\r\n} READD_TEST_DATA, *PREADD_TEST_DATA;\r\n\r\nstatic READD_TEST_DATA ReAddTestData;\r\n\r\nstatic int EpollReAddTestClone(void* Parameter)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct epoll_event EpollControlEvent;\r\n    int Result = -1;\r\n\r\n    //\r\n    // Try to close file descriptor already added to epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN | EPOLLET;\r\n    EpollControlEvent.data.fd = ReAddTestData.Fd;\r\n    LxtLogInfo(\"[Cloned] Trying to add existing fd (%d) to epoll context\", ReAddTestData.Fd);\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(ReAddTestData.EpollFd, EPOLL_CTL_ADD, ReAddTestData.Fd, &EpollControlEvent), EEXIST);\r\n\r\n    LxtLogInfo(\"[Cloned] Closing fd (%d) in shared file descriptor table\", ReAddTestData.Fd);\r\n\r\n    LxtClose(ReAddTestData.Fd);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    LxtLogInfo(\"[Cloned] Exiting...\");\r\n    exit(Result);\r\n}\r\n\r\nint EpollAddTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n    int ChildPid;\r\n    struct epoll_event EpollControlEvent;\r\n    int EpollFd;\r\n    int SocketFd1;\r\n    int SocketFdCopy;\r\n    int SocketFd2;\r\n    int SocketFd3;\r\n    int SocketFd4;\r\n    int Status;\r\n    LXT_CLONE_ARGS CloneArgs;\r\n\r\n    SocketFd1 = -1;\r\n    SocketFd2 = -1;\r\n    SocketFd3 = -1;\r\n    EpollFd = -1;\r\n    int Flags;\r\n\r\n    LxtCheckErrno(EpollFd = epoll_create1(EPOLL_CLOEXEC));\r\n    LxtCheckErrno(SocketFd1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n    LxtCheckErrno(SocketFd3 = dup(SocketFd1));\r\n    LxtLogInfo(\"Created fd1 (%d), duplicated into fd3 (%d)\", SocketFd1, SocketFd3);\r\n    EpollControlEvent.events = EPOLLIN | EPOLLET;\r\n    EpollControlEvent.data.fd = SocketFd1;\r\n    LxtLogInfo(\"Adding fd1 (%d) file descriptor to epoll context\", SocketFd1);\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd1, &EpollControlEvent));\r\n\r\n    LxtLogInfo(\"Adding fd3 (%d) file descriptor to epoll context\", SocketFd3);\r\n    EpollControlEvent.data.fd = SocketFd3;\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent));\r\n\r\n    LxtLogInfo(\"Closing fd1 (%d) file descriptor\", SocketFd1);\r\n    LxtClose(SocketFd1);\r\n    SocketFdCopy = SocketFd1;\r\n    SocketFd1 = -1;\r\n    LxtCheckErrno(SocketFd2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n    LxtLogInfo(\"Created fd2 (%d) file descriptor, that should be the same as closed fd1 (%d)\", SocketFd2, SocketFdCopy);\r\n\r\n    LxtCheckEqual(SocketFdCopy, SocketFd2, \"%d\");\r\n    LxtCheckNotEqual(SocketFd1, SocketFd3, \"%d\");\r\n    EpollControlEvent.data.fd = SocketFd2;\r\n    LxtLogInfo(\"Adding fd2 (%d) file descriptor to epoll context\", SocketFd2);\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd2, &EpollControlEvent));\r\n\r\n    LxtLogInfo(\"Trying to add fd3 (%d) file descriptor, expecting EEXIST\", SocketFd3);\r\n    EpollControlEvent.data.fd = SocketFd3;\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent), EEXIST);\r\n\r\n    LxtLogInfo(\"Forking...\");\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        LxtLogInfo(\"[Forked] Try to add fd3 (%d) already added to epoll context\", SocketFd3);\r\n        LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent), EEXIST);\r\n\r\n        LxtLogInfo(\"[Forked] Closing fd3 (%d), should not affect parent\", SocketFd3);\r\n        LxtClose(SocketFd3);\r\n        SocketFd3 = -1;\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Let the parent try to add already existing file descriptor, verifying\r\n        // that it hasn't been removed in both file descriptor tables.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        LxtCheckErrno(SocketFd3 = dup(SocketFd2));\r\n        LxtLogInfo(\"[Forked] Duplicated fd2 (%d) to fd3 (%d) and adding fd3 to epoll context\", SocketFd2, SocketFd3);\r\n        EpollControlEvent.data.fd = SocketFd3;\r\n        LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent));\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Let the child process close the file descriptor in the cloned file\r\n    // descriptor table.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    LxtLogInfo(\"Try to add fd3 (%d) already added to epoll context\", SocketFd3);\r\n    EpollControlEvent.data.fd = SocketFd3;\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd3, &EpollControlEvent), EEXIST);\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\nErrorExit:\r\n    if (SocketFd1 != -1)\r\n    {\r\n        close(SocketFd1);\r\n    }\r\n\r\n    if (SocketFd2 != -1)\r\n    {\r\n        close(SocketFd2);\r\n    }\r\n\r\n    if (SocketFd3 != -1)\r\n    {\r\n        close(SocketFd3);\r\n    }\r\n\r\n    if (EpollFd != -1)\r\n    {\r\n        close(EpollFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint EpollDeleteTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n    int ChildPid;\r\n    struct epoll_event EpollControlEvent;\r\n    int EpollFd;\r\n    int SocketFd;\r\n    int SocketFdCopy;\r\n    int SocketFdDup;\r\n    int Status;\r\n    LXT_CLONE_ARGS CloneArgs;\r\n\r\n    SocketFd = -1;\r\n    EpollFd = -1;\r\n    int Flags;\r\n\r\n    LxtCheckErrno(EpollFd = epoll_create1(EPOLL_CLOEXEC));\r\n    LxtCheckErrno(SocketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n    LxtLogInfo(\"Created socket file descriptor (%d)\", SocketFd);\r\n    EpollControlEvent.events = EPOLLIN | EPOLLET;\r\n    EpollControlEvent.data.fd = SocketFd;\r\n    LxtLogInfo(\"Adding socket file descriptor (%d) to epoll context\", SocketFd);\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent));\r\n\r\n    LxtLogInfo(\"1. Forking...\");\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtLogInfo(\"[Forked] Try to add socket file descriptor (%d) already added to epoll context\", SocketFd);\r\n        LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent), EEXIST);\r\n\r\n        LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_DEL, SocketFd, NULL));\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Let the parent re-add socket file descriptor.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Let the child process remove the file descriptor in the cloned file\r\n    // descriptor table.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtLogInfo(\"Try to add socket file descriptor (%d)\", SocketFd);\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent));\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    ChildPid = -1;\r\n    LxtCheckResult(Result);\r\n\r\n    LxtLogInfo(\"2. Forking...\");\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtLogInfo(\"[Forked] Closing original socket fd and creating a new socket fd, should be equal\");\r\n        SocketFdCopy = SocketFd;\r\n        LxtClose(SocketFd);\r\n        LxtCheckErrno(SocketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n        LxtCheckEqual(SocketFd, SocketFdCopy, \"%d\");\r\n        LxtLogInfo(\"[Forked] Trying to delete socket file descriptor (%d), should fail\", SocketFd);\r\n        LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_DEL, SocketFd, NULL), ENOENT);\r\n\r\n        LxtLogInfo(\"[Forked] Try to add socket file descriptor (%d)\", SocketFd);\r\n        EpollControlEvent.data.fd = SocketFd;\r\n        LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent));\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Let parent run.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        LxtLogInfo(\"[Forked] Trying to delete socket file descriptor (%d) again\", SocketFd);\r\n        LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_DEL, SocketFd, NULL));\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Let the parent re-add socket file descriptor.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Let the child process remove the file descriptor in the cloned file\r\n    // descriptor table.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    LxtLogInfo(\"Try to add socket file descriptor (%d), should fail\", SocketFd);\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent), EEXIST);\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Let child run.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    LxtLogInfo(\"Try to add socket file descriptor (%d), should fail again\", SocketFd);\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent), EEXIST);\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    ChildPid = -1;\r\n    LxtCheckResult(Result);\r\n\r\n    LxtLogInfo(\"3.Forking...\");\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtLogInfo(\"[Forked] Duplicating original socket fd and creating a new socket fd, should be equal\");\r\n        LxtCheckErrno(SocketFdDup = dup(SocketFd));\r\n        LxtLogInfo(\"[Forked] Closing original socket fd and creating a new socket fd, should be equal\");\r\n        SocketFdCopy = SocketFd;\r\n        LxtClose(SocketFd);\r\n        LxtLogInfo(\"[Forked] Duplicating original socket fd into new socket fd, should be equal as the closed socket fd\");\r\n        LxtCheckErrno(SocketFd = dup(SocketFdDup));\r\n        LxtCheckEqual(SocketFd, SocketFdCopy, \"%d\");\r\n        LxtLogInfo(\"[Forked] Trying to delete socket file descriptor (%d)\", SocketFd);\r\n        LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_DEL, SocketFd, NULL));\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Let the parent run.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Let the child process remove the file descriptor in the cloned file\r\n    // descriptor table.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    LxtLogInfo(\"Try to add socket file descriptor (%d)\", SocketFd);\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketFd, &EpollControlEvent));\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Let the child process finish.\r\n    //\r\n\r\nErrorExit:\r\n    if (SocketFd != -1)\r\n    {\r\n        close(SocketFd);\r\n    }\r\n\r\n    if (EpollFd != -1)\r\n    {\r\n        close(EpollFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint EpollModifyWhilePollingTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    struct epoll_event EpollControlEvent;\r\n    int EpollFileDescriptor;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    struct epoll_event* InputEvent;\r\n    struct epoll_event* OutputEvent;\r\n    int PipeFileDescriptors[2] = {-1, -1};\r\n    int Result;\r\n    int Status;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    ChildPid = -1;\r\n    EpollFileDescriptor = -1;\r\n\r\n    LxtCheckResult(LxtSignalInitialize());\r\n    LxtCheckResult(LxtSignalSetupHandler(SIGPIPE, SA_SIGINFO));\r\n\r\n    //\r\n    // Open a pipe to test epoll.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));\r\n\r\n    //\r\n    // Create an epoll.\r\n    //\r\n\r\n    LxtCheckErrno(EpollFileDescriptor = epoll_create(1));\r\n\r\n    //\r\n    // Add the file to the epoll.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckClose(PipeFileDescriptors[0]);\r\n        LxtCheckClose(PipeFileDescriptors[1]);\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtLogInfo(\"Waiting on epoll...\", Result);\r\n        LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 2000));\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n        LxtCheckEqual(EpollWaitEvent[0].events, EPOLLOUT, \"%d\");\r\n        LxtCheckEqual(EpollWaitEvent[0].data.fd, 0, \"%d\");\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtLogInfo(\"Waiting on epoll...\", Result);\r\n        LxtCheckErrnoZeroSuccess(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 2000));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    sleep(1);\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = 0;\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, 0, &EpollControlEvent));\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = PipeFileDescriptors[0];\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));\r\n\r\n    EpollControlEvent.events = EPOLLOUT | EPOLLONESHOT;\r\n    EpollControlEvent.data.fd = 0;\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_MOD, 0, &EpollControlEvent));\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // The child consumed the epoll event so it should no longer be available.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_wait(EpollFileDescriptor, EpollWaitEvent, 2, 0));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_DEL, 0, NULL));\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    sleep(1);\r\n    LxtLogInfo(\"Closing last file descriptor in epoll.\", Result);\r\n    LxtCheckClose(PipeFileDescriptors[0]);\r\n\r\n    //\r\n    // The epoll is active, but the pipe it was waiting on is now closed.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(write(PipeFileDescriptors[1], \"\\n\", 1), EPIPE);\r\n    LxtCheckResult(LxtSignalCheckReceived(SIGPIPE));\r\n    LxtSignalResetReceived();\r\n    LxtCheckClose(PipeFileDescriptors[1]);\r\n\r\nErrorExit:\r\n    if (EpollFileDescriptor != -1)\r\n    {\r\n        close(EpollFileDescriptor);\r\n    }\r\n\r\n    if (PipeFileDescriptors[1] != -1)\r\n    {\r\n        close(PipeFileDescriptors[1]);\r\n    }\r\n\r\n    if (PipeFileDescriptors[0] != -1)\r\n    {\r\n        close(PipeFileDescriptors[0]);\r\n    }\r\n\r\n    LxtLogInfo(\"Done, PID=%d, ChildPid=%d\", getpid(), ChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint EpollModTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int ChildPid;\r\n    struct epoll_event EpollControlEvent;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    int SocketFd;\r\n    int TmpFd;\r\n    int EpollFd;\r\n    LXT_SOCKET_PAIR SocketPair;\r\n    int Status;\r\n\r\n    SocketPair.Parent = -1;\r\n    SocketPair.Child = -1;\r\n\r\n    LxtCheckErrno(EpollFd = epoll_create1(EPOLL_CLOEXEC));\r\n    LxtCheckResult(LxtSocketPairCreate(&SocketPair));\r\n    LxtLogInfo(\"Created socket pair (%d, %d)\", SocketPair.Parent, SocketPair.Child);\r\n    EpollControlEvent.events = EPOLLET;\r\n    EpollControlEvent.data.fd = SocketPair.Parent;\r\n    LxtLogInfo(\"Adding socket pair (parent) (%d) file descriptor to epoll context\", SocketPair.Parent);\r\n\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketPair.Parent, &EpollControlEvent));\r\n\r\n    LxtLogInfo(\"Adding socket pair (child) (%d) file descriptor to epoll context\", SocketPair.Child);\r\n\r\n    EpollControlEvent.data.fd = SocketPair.Child;\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketPair.Child, &EpollControlEvent));\r\n\r\n    LxtLogInfo(\"Forking...\");\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        LxtLogInfo(\"[Child] Waiting on epoll (EPOLLIN), should timeout\");\r\n        LxtCheckErrnoZeroSuccess(epoll_wait(EpollFd, EpollWaitEvent, 2, 1000));\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        LxtLogInfo(\"[Child] Waiting on epoll (EPOLLIN)\");\r\n        LxtCheckErrno(Result = epoll_wait(EpollFd, EpollWaitEvent, 2, -1));\r\n        LxtLogInfo(\"[Child] EPOLLIN received\");\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n        LxtCheckEqual(EpollWaitEvent[0].data.fd, SocketPair.Child, \"%d\");\r\n        LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, \"0x%x\");\r\n        LxtCheckResult(LxtReceiveMessage(SocketPair.Child, \"data\"));\r\n        LxtLogInfo(\"[Child] Received message\");\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait for parent to send data.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Sending data over socketpair\");\r\n    LxtCheckResult(LxtSendMessage(SocketPair.Parent, \"data\"));\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Let the child wait/timeout.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    LxtLogInfo(\"Modifying the epoll to receive EPOLLIN events\");\r\n    EpollControlEvent.events = EPOLLIN | EPOLLET;\r\n    EpollControlEvent.data.fd = SocketPair.Child;\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_MOD, SocketPair.Child, &EpollControlEvent));\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Wait for child to receive data.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (SocketPair.Parent != -1)\r\n    {\r\n        close(SocketPair.Parent);\r\n    }\r\n\r\n    if (SocketPair.Child != -1)\r\n    {\r\n        close(SocketPair.Child);\r\n    }\r\n\r\n    if (EpollFd != -1)\r\n    {\r\n        close(EpollFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint EpollPhantomEventsTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int ChildPid;\r\n    struct epoll_event EpollControlEvent;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    int SocketFd;\r\n    int TmpFd;\r\n    int EpollFd;\r\n    LXT_SOCKET_PAIR SocketPair;\r\n    int Status;\r\n\r\n    SocketPair.Parent = -1;\r\n    SocketPair.Child = -1;\r\n\r\n    LxtCheckErrno(EpollFd = epoll_create1(EPOLL_CLOEXEC));\r\n    LxtCheckResult(LxtSocketPairCreate(&SocketPair));\r\n    LxtLogInfo(\"Created socket pair (%d, %d)\", SocketPair.Parent, SocketPair.Child);\r\n    EpollControlEvent.events = EPOLLIN | EPOLLET;\r\n    EpollControlEvent.data.fd = SocketPair.Parent;\r\n    LxtLogInfo(\"Adding socket pair (parent) (%d) file descriptor to epoll context\", SocketPair.Parent);\r\n\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketPair.Parent, &EpollControlEvent));\r\n\r\n    LxtLogInfo(\"Adding socket pair (child) (%d) file descriptor to epoll context\", SocketPair.Child);\r\n\r\n    EpollControlEvent.data.fd = SocketPair.Child;\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, SocketPair.Child, &EpollControlEvent));\r\n\r\n    LxtLogInfo(\"Forking...\");\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtLogInfo(\"[Child] Waiting on epoll (EPOLLIN)\");\r\n        LxtCheckErrno(Result = epoll_wait(EpollFd, EpollWaitEvent, 2, -1));\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n        LxtCheckEqual(EpollWaitEvent[0].data.fd, SocketPair.Child, \"%d\");\r\n        LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, \"0x%x\");\r\n        LxtLogInfo(\"[Child] Waiting on message\");\r\n        LxtCheckResult(LxtReceiveMessage(SocketPair.Child, \"data\"));\r\n\r\n        LxtLogInfo(\"Closing (%d) file descriptor\", SocketPair.Child);\r\n        TmpFd = SocketPair.Child;\r\n        LxtClose(SocketPair.Child);\r\n\r\n        LxtCheckErrno(SocketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n        LxtLogInfo(\"Created (%d) file descriptor, that should be the same as closed fd (%d)\", SocketFd, TmpFd);\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait for parent to send data.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        LxtLogInfo(\"Waiting on epoll phantom event (EPOLLIN)\");\r\n        LxtCheckErrno(Result = epoll_wait(EpollFd, EpollWaitEvent, 2, -1));\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n        LxtCheckEqual(EpollWaitEvent[0].data.fd, SocketPair.Child, \"%d\");\r\n        LxtCheckEqual(EpollWaitEvent[0].events, EPOLLIN, \"0x%x\");\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Sending data over socketpair\");\r\n    LxtCheckResult(LxtSendMessage(SocketPair.Parent, \"data\"));\r\n\r\n    //\r\n    // Verify that both only child receives the same EPOLLIN event.\r\n    //\r\n\r\n    sleep(1);\r\n    LxtLogInfo(\"Waiting on epoll (EPOLLIN) (should fail)\");\r\n    LxtCheckErrno(Result = epoll_wait(EpollFd, EpollWaitEvent, 2, 100));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    LxtCheckResult(LxtSendMessage(SocketPair.Parent, \"data\"));\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Wait for child to receive phantom event.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (SocketPair.Parent != -1)\r\n    {\r\n        close(SocketPair.Parent);\r\n    }\r\n\r\n    if (SocketPair.Child != -1)\r\n    {\r\n        close(SocketPair.Child);\r\n    }\r\n\r\n    if (EpollFd != -1)\r\n    {\r\n        close(EpollFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\nint PPollInvalidArgument(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int FileDescriptor1;\r\n    struct pollfd PollDescriptors[4];\r\n    int Result;\r\n    struct timespec Timeout;\r\n\r\n    LxtCheckResult(FileDescriptor1 = open(\"/data/test/poll_test.bin\", O_RDWR | O_CREAT, S_IRWXU));\r\n    PollDescriptors[0].fd = FileDescriptor1;\r\n    PollDescriptors[0].events = POLLIN;\r\n    PollDescriptors[0].revents = -1;\r\n\r\n    //\r\n    // Invalid argument variations.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    Timeout.tv_sec = -1;\r\n    LxtCheckErrnoFailure(ppoll(PollDescriptors, 1, &Timeout, NULL), EINVAL);\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    Timeout.tv_nsec = 999999999 + 1;\r\n    LxtCheckErrnoFailure(ppoll(PollDescriptors, 1, &Timeout, NULL), EINVAL);\r\n\r\nErrorExit:\r\n    if (FileDescriptor1 != -1)\r\n    {\r\n        LxtClose(FileDescriptor1);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollUnalignedTest(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    char Buffer[sizeof(struct pollfd) + 1];\r\n    int FileDescriptor1;\r\n    struct pollfd* PollDescriptors;\r\n    int Result;\r\n    struct timespec Timeout;\r\n\r\n    //\r\n    // Set up the poll descriptors array to be unaligned.\r\n    //\r\n\r\n    PollDescriptors = (struct pollfd*)&Buffer[1];\r\n    LxtLogInfo(\"PollDescriptors address %p\", PollDescriptors);\r\n\r\n    LxtCheckResult(FileDescriptor1 = open(\"/data/test/poll_test.bin\", O_RDWR | O_CREAT, S_IRWXU));\r\n    PollDescriptors[0].fd = FileDescriptor1;\r\n    PollDescriptors[0].events = POLLIN;\r\n    PollDescriptors[0].revents = -1;\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    Timeout.tv_sec = 1;\r\n    LxtCheckErrno(ppoll(PollDescriptors, 1, &Timeout, NULL));\r\n\r\nErrorExit:\r\n    if (FileDescriptor1 != -1)\r\n    {\r\n        LxtClose(FileDescriptor1);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollDeleteCloseFdLoop(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine loops\\stresses the removal of EPOLL FD and closing of the FD.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[10];\r\n    int ChildPid;\r\n    struct epoll_event EpollControlEvent;\r\n    const int NumFd = 100;\r\n    int EpollFd[NumFd];\r\n    int Iterator;\r\n    int Loop;\r\n    int NestedEpollFd[NumFd];\r\n    int PipeFileDescriptors[2] = {-1, -1};\r\n    int Result;\r\n    int SharedEpollFd;\r\n    int SocketFd[NumFd];\r\n    int Status;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    ChildPid = -1;\r\n    for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n    {\r\n        SocketFd[Iterator] = -1;\r\n        EpollFd[Iterator] = -1;\r\n        NestedEpollFd[Iterator] = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));\r\n    LxtCheckErrno(write(PipeFileDescriptors[1], \"\\n\", 1));\r\n    LxtCheckErrno(SharedEpollFd = epoll_create1(0));\r\n\r\n    //\r\n    // Start a loop to read the pipe, thereby triggering a notification.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckClose(PipeFileDescriptors[1]);\r\n        while ((Result = read(PipeFileDescriptors[0], Buffer, sizeof(Buffer))) > 0)\r\n            ;\r\n\r\n        //\r\n        // The loop should terminate when the other threads exit and\r\n        // close the write pipe handle.\r\n        //\r\n\r\n        LxtCheckErrno(Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (Loop = 0; Loop < 50; Loop++)\r\n    {\r\n        for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n        {\r\n            LxtCheckErrno(EpollFd[Iterator] = epoll_create1(0));\r\n            LxtCheckErrno(NestedEpollFd[Iterator] = epoll_create1(0));\r\n            LxtCheckErrno(SocketFd[Iterator] = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));\r\n\r\n            EpollControlEvent.events = EPOLLIN | EPOLLET;\r\n            EpollControlEvent.data.fd = SocketFd[Iterator];\r\n            LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, SocketFd[Iterator], &EpollControlEvent));\r\n\r\n            EpollControlEvent.events = EPOLLIN;\r\n            EpollControlEvent.data.fd = PipeFileDescriptors[0];\r\n            LxtCheckErrno(epoll_ctl(NestedEpollFd[Iterator], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));\r\n\r\n            EpollControlEvent.events = EPOLLIN;\r\n            EpollControlEvent.data.fd = NestedEpollFd[Iterator];\r\n            LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, NestedEpollFd[Iterator], &EpollControlEvent));\r\n\r\n            EpollControlEvent.events = EPOLLIN;\r\n            EpollControlEvent.data.fd = EpollFd[Iterator];\r\n            LxtCheckErrno(epoll_ctl(SharedEpollFd, EPOLL_CTL_ADD, EpollFd[Iterator], &EpollControlEvent));\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT_START();\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n\r\n            for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n            {\r\n                LxtClose(NestedEpollFd[Iterator]);\r\n                NestedEpollFd[Iterator] = -1;\r\n                LxtClose(SocketFd[Iterator]);\r\n                SocketFd[Iterator] = -1;\r\n            }\r\n\r\n            //\r\n            // The race is between releasing the last reference to the file\r\n            // descriptor here and releasing the last reference to EPOLL by\r\n            // the child. Synchronize to keep the race as close as possible.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n            {\r\n                LxtClose(EpollFd[Iterator]);\r\n                EpollFd[Iterator] = -1;\r\n            }\r\n\r\n            _exit(0);\r\n        }\r\n\r\n        for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n        {\r\n            LxtClose(EpollFd[Iterator]);\r\n            EpollFd[Iterator] = -1;\r\n        }\r\n\r\n        //\r\n        // See the above comment about synchronizing to keep the race close.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n        {\r\n            LxtCheckErrno(write(PipeFileDescriptors[1], \"\\n\", 1));\r\n            LxtClose(NestedEpollFd[Iterator]);\r\n            NestedEpollFd[Iterator] = -1;\r\n            LxtClose(SocketFd[Iterator]);\r\n            SocketFd[Iterator] = -1;\r\n        }\r\n\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (PipeFileDescriptors[1] != -1)\r\n    {\r\n        close(PipeFileDescriptors[1]);\r\n    }\r\n\r\n    if (PipeFileDescriptors[0] != -1)\r\n    {\r\n        close(PipeFileDescriptors[0]);\r\n    }\r\n\r\n    if (SharedEpollFd != -1)\r\n    {\r\n        close(SharedEpollFd);\r\n    }\r\n\r\n    for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n    {\r\n\r\n        //\r\n        // Only close socket FD's in the parent because socket FD's are created\r\n        // with CLOSE_ON_EXEC and so are only valid in the parent.\r\n        //\r\n\r\n        if ((SocketFd[Iterator] != -1) && (ChildPid != 0))\r\n        {\r\n            close(SocketFd[Iterator]);\r\n        }\r\n\r\n        if ((NestedEpollFd[Iterator] != -1) && (ChildPid != 0))\r\n        {\r\n            close(NestedEpollFd[Iterator]);\r\n        }\r\n\r\n        if (EpollFd[Iterator] != -1)\r\n        {\r\n            close(EpollFd[Iterator]);\r\n        }\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(0);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid* EpollDup2FdLoopThread(void* Parameter)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine is the thread handler for the EpollDup2FdLoop test.\r\n\r\nArguments:\r\n\r\n    Parameter - Supplies the thread parameter.\r\n\r\nReturn Value:\r\n\r\n    Returns the thread id on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    PEPOLL_DUP2_CONTEXT Context;\r\n    struct epoll_event EpollControlEvent;\r\n    int Iterator;\r\n    int Loop;\r\n    int Result;\r\n\r\n    Context = (PEPOLL_DUP2_CONTEXT)Parameter;\r\n    for (Loop = 0; Loop < 50; Loop++)\r\n    {\r\n        for (Iterator = 0; Iterator < EPOLL_DUP2_FD_COUNT; Iterator++)\r\n        {\r\n            LXT_SYNCHRONIZATION_POINT_CHILD();\r\n            EpollControlEvent.events = EPOLLIN;\r\n            EpollControlEvent.data.fd = Context->Fd[Iterator];\r\n            if (epoll_ctl(Context->EpollFd, EPOLL_CTL_DEL, Context->Fd[Iterator], &EpollControlEvent) != 0)\r\n            {\r\n\r\n                Result = errno;\r\n\r\n                //\r\n                // Race with parent is expected to cause the fd to be invalid\r\n                // at times (not the same file that was added).\r\n                //\r\n\r\n                if (Result == EINVAL)\r\n                {\r\n                    Result = LXT_RESULT_SUCCESS;\r\n                }\r\n\r\n                LxtCheckResult(Result);\r\n            }\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD();\r\n\r\n#pragma GCC diagnostic push\r\n#pragma GCC diagnostic ignored \"-Wint-to-pointer-cast\"\r\n\r\n    return (void*)Result;\r\n\r\n#pragma GCC diagnostic pop\r\n}\r\n\r\nint EpollDup2FdLoop(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine loops\\stresses epoll operations on fd's that are being closed\r\n    by other threads. The close is done via dup2 because dup2 also holds the\r\n    filetable lock, increasing the chances of hitting locking issues.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct epoll_event EpollControlEvent;\r\n    EPOLL_DUP2_CONTEXT Context;\r\n    int Iterator;\r\n    int Loop;\r\n    int NullFd;\r\n    const int NumFd = EPOLL_DUP2_FD_COUNT;\r\n    int Result;\r\n    void* Status;\r\n    pthread_t Thread = 0;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    Context.EpollFd = -1;\r\n    for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n    {\r\n        Context.Fd[Iterator] = -1;\r\n    }\r\n\r\n    LxtCheckErrno(NullFd = open(\"/dev/null\", O_RDWR));\r\n    LxtCheckErrno(Context.EpollFd = epoll_create1(0));\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckResultError(pthread_create(&Thread, NULL, EpollDup2FdLoopThread, &Context));\r\n\r\n    for (Loop = 0; Loop < 50; Loop++)\r\n    {\r\n        for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n        {\r\n            LxtCheckErrno(Context.Fd[Iterator] = open(\"/dev/null\", O_RDWR));\r\n            EpollControlEvent.events = EPOLLIN | EPOLLET;\r\n            EpollControlEvent.data.fd = Context.Fd[Iterator];\r\n            LxtCheckErrno(epoll_ctl(Context.EpollFd, EPOLL_CTL_ADD, Context.Fd[Iterator], &EpollControlEvent));\r\n        }\r\n\r\n        for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n        {\r\n            LXT_SYNCHRONIZATION_POINT_PARENT();\r\n            LxtCheckErrno(dup2(NullFd, Context.Fd[Iterator]));\r\n        }\r\n\r\n        for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n        {\r\n            LxtClose(Context.Fd[Iterator]);\r\n            Context.Fd[Iterator] = -1;\r\n        }\r\n    }\r\n\r\n    pthread_join(Thread, (void**)&Result);\r\n    Thread = 0;\r\n    LxtCheckResult(Result);\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_PTHREAD_END_PARENT(Thread);\r\n    for (Iterator = 0; Iterator < NumFd; Iterator++)\r\n    {\r\n        if (Context.Fd[Iterator] != -1)\r\n        {\r\n            close(Context.Fd[Iterator]);\r\n        }\r\n    }\r\n\r\n    if (Context.EpollFd != -1)\r\n    {\r\n        close(Context.EpollFd);\r\n    }\r\n\r\n    if (NullFd != -1)\r\n    {\r\n        close(NullFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollRelatedFileStress(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine loops\\stresses the usage of multiple epoll files containing\r\n    the same set of fds.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int AddRemoveLoop;\r\n    char Buffer[10];\r\n    int ChildPid;\r\n    struct epoll_event EpollControlEvent[3];\r\n    int EpollFd[2];\r\n    int Iterator;\r\n    int Loop;\r\n    int MasterEpollFd;\r\n    int Result;\r\n    int SharedEpollFd;\r\n    int Status;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    ChildPid = -1;\r\n    MasterEpollFd = -1;\r\n    for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)\r\n    {\r\n        EpollFd[Iterator] = -1;\r\n    }\r\n\r\n    LxtCheckErrno(MasterEpollFd = epoll_create1(0));\r\n    for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)\r\n    {\r\n        LxtCheckErrno(EpollFd[Iterator] = epoll_create1(0));\r\n        EpollControlEvent[0].events = EPOLLIN;\r\n        EpollControlEvent[0].data.fd = EpollFd[Iterator];\r\n        LxtCheckErrno(epoll_ctl(MasterEpollFd, EPOLL_CTL_ADD, EpollFd[Iterator], EpollControlEvent));\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    for (Loop = 0; Loop < 2000; Loop++)\r\n    {\r\n        if (ChildPid == 0)\r\n        {\r\n\r\n            //\r\n            // Synchronize with the wait loop to try to increase the\r\n            // chances of hitting a race.\r\n            //\r\n\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            for (AddRemoveLoop = 0; AddRemoveLoop < LXT_COUNT_OF(EpollControlEvent); AddRemoveLoop++)\r\n            {\r\n\r\n                for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)\r\n                {\r\n\r\n                    EpollControlEvent[0].events = EPOLLIN;\r\n                    EpollControlEvent[0].data.fd = 0;\r\n                    LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, 0, EpollControlEvent));\r\n\r\n                    EpollControlEvent[0].events = EPOLLOUT;\r\n                    EpollControlEvent[0].data.fd = 1;\r\n                    LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, 1, EpollControlEvent));\r\n\r\n                    EpollControlEvent[0].events = EPOLLOUT;\r\n                    EpollControlEvent[0].data.fd = 2;\r\n                    LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_ADD, 2, EpollControlEvent));\r\n                }\r\n\r\n                for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)\r\n                {\r\n\r\n                    LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_DEL, 0, NULL));\r\n\r\n                    LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_DEL, 1, NULL));\r\n\r\n                    LxtCheckErrno(epoll_ctl(EpollFd[Iterator], EPOLL_CTL_DEL, 2, NULL));\r\n                }\r\n            }\r\n        }\r\n        else\r\n        {\r\n            LXT_SYNCHRONIZATION_POINT();\r\n            (void)epoll_wait(MasterEpollFd, EpollControlEvent, LXT_COUNT_OF(EpollControlEvent), 1);\r\n\r\n            for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)\r\n            {\r\n                (void)epoll_wait(EpollFd[Iterator], EpollControlEvent, LXT_COUNT_OF(EpollControlEvent), 1);\r\n            }\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    for (Iterator = 0; Iterator < LXT_COUNT_OF(EpollFd); Iterator++)\r\n    {\r\n        if (EpollFd[Iterator] != -1)\r\n        {\r\n            close(EpollFd[Iterator]);\r\n        }\r\n    }\r\n\r\n    if (MasterEpollFd != -1)\r\n    {\r\n        close(MasterEpollFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollRecursionTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine verifies epoll file included in epoll file behavior.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[10];\r\n    struct epoll_event EpollControlEvent;\r\n    int EpollFileDescriptor;\r\n    int EpollContainerFd;\r\n    int EpollContainer2Fd;\r\n    struct epoll_event EpollWaitEvent;\r\n    struct epoll_event* InputEvent;\r\n    struct epoll_event* OutputEvent;\r\n    int PipeFileDescriptors[2] = {-1, -1};\r\n    int PipeFileDescriptors2[2] = {-1, -1};\r\n    fd_set ReadFds;\r\n    int Result;\r\n    struct timeval Timeout;\r\n    fd_set WriteFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    EpollFileDescriptor = -1;\r\n    EpollContainerFd = -1;\r\n    EpollContainer2Fd = -1;\r\n\r\n    //\r\n    // Create two epoll files.\r\n    //\r\n\r\n    LxtCheckErrno(EpollFileDescriptor = epoll_create(1));\r\n    LxtCheckErrno(EpollContainerFd = epoll_create(1));\r\n\r\n    //\r\n    // Open a pipe to test epoll.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));\r\n    LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors2));\r\n\r\n    //\r\n    // Pend a write.\r\n    //\r\n\r\n    LxtCheckErrno(write(PipeFileDescriptors[1], \"\\n\", 1));\r\n\r\n    //\r\n    // Add one epoll file to the other.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = EpollFileDescriptor;\r\n    LxtCheckErrno(epoll_ctl(EpollContainerFd, EPOLL_CTL_ADD, EpollFileDescriptor, &EpollControlEvent));\r\n\r\n    //\r\n    // Now attempt to add them in reverse order to create a simple loop.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = EpollContainerFd;\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, EpollContainerFd, &EpollControlEvent), ELOOP);\r\n\r\n    //\r\n    // Attempt to modify swapping the container/included descriptors.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = EpollContainerFd;\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_MOD, EpollContainerFd, &EpollControlEvent), ENOENT);\r\n\r\n    //\r\n    // Attempt to delete swapping the container/included descriptors.\r\n    //\r\n\r\n    EpollControlEvent.data.fd = EpollContainerFd;\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_DEL, EpollContainerFd, NULL), ENOENT);\r\n\r\n    //\r\n    // Add the read pipe end to the epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = PipeFileDescriptors[0];\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));\r\n\r\n    //\r\n    // Verify the epoll is signalled.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(EpollFileDescriptor, &ReadFds);\r\n    LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Add the second pipe, which is not read ready.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = PipeFileDescriptors[0];\r\n    LxtCheckErrno(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, PipeFileDescriptors2[0], &EpollControlEvent));\r\n\r\n    //\r\n    // Verify the epoll remains signalled.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(EpollFileDescriptor, &ReadFds);\r\n    LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Pend a write to the other pipe.\r\n    //\r\n\r\n    LxtCheckErrno(write(PipeFileDescriptors2[1], \"\\n\", 1));\r\n\r\n    //\r\n    // Verify the epoll remains signalled.\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(EpollFileDescriptor, &ReadFds);\r\n    LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Create another epoll containing the first.\r\n    //\r\n\r\n    LxtCheckErrno(EpollContainerFd = epoll_create(1));\r\n\r\n    //\r\n    // Add the first epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = EpollFileDescriptor;\r\n    LxtCheckErrno(epoll_ctl(EpollContainerFd, EPOLL_CTL_ADD, EpollFileDescriptor, &EpollControlEvent));\r\n\r\n    //\r\n    // Try to add the second back to the first to create a loop.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = EpollContainerFd;\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFileDescriptor, EPOLL_CTL_ADD, EpollContainerFd, &EpollControlEvent), ELOOP);\r\n\r\n    //\r\n    // The first epoll should trigger the second.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Add the second pipe directly to the container epoll.\r\n    //\r\n\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = PipeFileDescriptors2[0];\r\n    LxtCheckErrno(epoll_ctl(EpollContainerFd, EPOLL_CTL_ADD, PipeFileDescriptors2[0], &EpollControlEvent));\r\n\r\n    //\r\n    // The epoll should remain signalled.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Create yet another epoll to test non-read signals.\r\n    //\r\n\r\n    LxtCheckErrno(EpollContainer2Fd = epoll_create(1));\r\n    EpollControlEvent.events = EPOLLOUT;\r\n    EpollControlEvent.data.fd = EpollFileDescriptor;\r\n    LxtCheckErrno(epoll_ctl(EpollContainer2Fd, EPOLL_CTL_ADD, EpollFileDescriptor, &EpollControlEvent));\r\n\r\n    FD_ZERO(&WriteFds);\r\n    FD_SET(EpollContainer2Fd, &WriteFds);\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(select((EpollContainer2Fd + 1), NULL, &WriteFds, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    LxtCheckErrno(epoll_wait(EpollContainer2Fd, &EpollWaitEvent, 1, 0));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Clear the signal for the second pipe and verify signal state.\r\n    //\r\n\r\n    LxtCheckErrno(read(PipeFileDescriptors2[0], Buffer, sizeof(Buffer)));\r\n    FD_SET(EpollFileDescriptor, &ReadFds);\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Clear the signal for the first pipe and verify signal state.\r\n    //\r\n\r\n    LxtCheckErrno(read(PipeFileDescriptors[0], Buffer, sizeof(Buffer)));\r\n    FD_SET(EpollFileDescriptor, &ReadFds);\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Signal both pipes again.\r\n    //\r\n\r\n    LxtCheckErrno(write(PipeFileDescriptors2[1], \"\\n\", 1));\r\n    LxtCheckErrno(write(PipeFileDescriptors2[1], \"\\n\", 1));\r\n\r\n    //\r\n    // Verify signal.\r\n    //\r\n\r\n    FD_SET(EpollFileDescriptor, &ReadFds);\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Remove the second pipe from the container and check signal state again.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_ctl(EpollContainerFd, EPOLL_CTL_DEL, PipeFileDescriptors2[0], NULL));\r\n\r\n    FD_SET(EpollFileDescriptor, &ReadFds);\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(select((EpollFileDescriptor + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Close the nested epoll and verify signal state.\r\n    //\r\n\r\n    LxtCheckClose(EpollFileDescriptor);\r\n    LxtCheckErrno(epoll_wait(EpollContainerFd, &EpollWaitEvent, 1, 0));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\nErrorExit:\r\n    if (EpollContainer2Fd != -1)\r\n    {\r\n        close(EpollContainer2Fd);\r\n    }\r\n\r\n    if (EpollFileDescriptor != -1)\r\n    {\r\n        close(EpollFileDescriptor);\r\n    }\r\n\r\n    if (EpollContainerFd != -1)\r\n    {\r\n        close(EpollContainerFd);\r\n    }\r\n\r\n    if (PipeFileDescriptors2[1] != -1)\r\n    {\r\n        close(PipeFileDescriptors2[1]);\r\n    }\r\n\r\n    if (PipeFileDescriptors2[0] != -1)\r\n    {\r\n        close(PipeFileDescriptors2[0]);\r\n    }\r\n\r\n    if (PipeFileDescriptors[1] != -1)\r\n    {\r\n        close(PipeFileDescriptors[1]);\r\n    }\r\n\r\n    if (PipeFileDescriptors[0] != -1)\r\n    {\r\n        close(PipeFileDescriptors[0]);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint EpollRecursionLimitTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks for an upper limit of recursive epolls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n#define EPOLL_MAX_RECURSION_COUNT 6\r\n#define EPOLL_CHAIN_COUNT 50\r\n\r\n    int ChainIndex;\r\n    struct epoll_event EpollControlEvent;\r\n    int EpollFds[EPOLL_CHAIN_COUNT][EPOLL_MAX_RECURSION_COUNT];\r\n    int EpollContentFds[EPOLL_MAX_RECURSION_COUNT];\r\n    struct epoll_event EpollWaitEvent;\r\n    int ExtraEpollFd;\r\n    int Index;\r\n    int PipeFileDescriptors[2] = {-1, -1};\r\n    int Result;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    ExtraEpollFd = -1;\r\n    memset(EpollFds, -1, sizeof(EpollFds));\r\n    memset(EpollContentFds, -1, sizeof(EpollContentFds));\r\n    memset(&EpollControlEvent, 0, sizeof(EpollControlEvent));\r\n    memset(&EpollWaitEvent, 0, sizeof(EpollWaitEvent));\r\n    EpollControlEvent.events = EPOLLIN;\r\n    LxtCheckErrnoZeroSuccess(pipe(PipeFileDescriptors));\r\n\r\n    for (ChainIndex = 0; ChainIndex < EPOLL_CHAIN_COUNT; ChainIndex += 1)\r\n    {\r\n        for (Index = 0; Index < EPOLL_MAX_RECURSION_COUNT; Index += 1)\r\n        {\r\n\r\n            //\r\n            // Create a new epoll.\r\n            //\r\n\r\n            LxtCheckErrno(EpollFds[ChainIndex][Index] = epoll_create(1));\r\n\r\n            //\r\n            // Add the previous epoll to make a chain.\r\n            //\r\n\r\n            if (Index > 0)\r\n            {\r\n                LxtCheckErrno(epoll_ctl(EpollFds[ChainIndex][Index], EPOLL_CTL_ADD, EpollFds[ChainIndex][Index - 1], &EpollControlEvent));\r\n            }\r\n        }\r\n    }\r\n\r\n    LxtCheckErrno(ExtraEpollFd = epoll_create(1));\r\n\r\n    //\r\n    // Attempt to add an epoll exceeding the maximum depth.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(ExtraEpollFd, EPOLL_CTL_ADD, EpollFds[0][Index - 1], &EpollControlEvent), ELOOP);\r\n\r\n    //\r\n    // Add a pipe file descriptor.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_ctl(EpollFds[0][5], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));\r\n\r\n    //\r\n    // Try creating a chain with a regular file descriptor in it.\r\n    //\r\n\r\n    for (Index = 0; Index < (EPOLL_MAX_RECURSION_COUNT - 1); Index += 1)\r\n    {\r\n\r\n        //\r\n        // Create a new epoll.\r\n        //\r\n\r\n        LxtCheckErrno(EpollContentFds[Index] = epoll_create(1));\r\n\r\n        //\r\n        // Add a regular file descriptor.\r\n        //\r\n\r\n        LxtCheckErrno(epoll_ctl(EpollContentFds[Index], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));\r\n\r\n        //\r\n        // Add the previous epoll to make a chain.\r\n        //\r\n\r\n        if (Index > 0)\r\n        {\r\n            LxtCheckErrno(epoll_ctl(EpollContentFds[Index], EPOLL_CTL_ADD, EpollContentFds[Index - 1], &EpollControlEvent));\r\n        }\r\n    }\r\n\r\n    //\r\n    // A non-epoll file descriptor is not allowed to be nested deeper\r\n    // than EPOLL_MAX_RECURSION_COUNT. There is a pipe file descriptor\r\n    // at [0][0] so this add attempt is expected to fail.\r\n    //\r\n\r\n    LxtCheckErrno(EpollContentFds[Index] = epoll_create(1));\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollContentFds[Index], EPOLL_CTL_ADD, EpollContentFds[Index - 1], &EpollControlEvent), EINVAL);\r\n\r\n    //\r\n    // Attempt to link chains together, where each add stays below the limit\r\n    // but the total chain size becomes increasingly large.\r\n    //\r\n\r\n    for (ChainIndex = (EPOLL_CHAIN_COUNT - 1); ChainIndex > 0; ChainIndex -= 1)\r\n    {\r\n        LxtLogInfo(\"[%d][%d] -> [%d][%d]\", ChainIndex, 0, ChainIndex - 1, Index - 2);\r\n        LxtCheckErrno(epoll_ctl(EpollFds[ChainIndex][0], EPOLL_CTL_ADD, EpollFds[ChainIndex - 1][Index - 2], &EpollControlEvent));\r\n    }\r\n\r\n    //\r\n    // Now try to introduce a loop into this extra-large chain.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFds[0][0], EPOLL_CTL_ADD, EpollFds[1][0], &EpollControlEvent), ELOOP);\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFds[0][0], EPOLL_CTL_ADD, EpollFds[EPOLL_CHAIN_COUNT - 1][Index - 1], &EpollControlEvent), ELOOP);\r\n\r\n    //\r\n    // The resulting chain is essentially useless. Try to add a file descriptor\r\n    // to a node in the chain.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFds[0][0], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent), EINVAL);\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFds[EPOLL_CHAIN_COUNT - 1][0], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent), EINVAL);\r\n\r\n    LxtCheckErrno(epoll_ctl(EpollFds[EPOLL_CHAIN_COUNT - 1][1], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));\r\n\r\n    LxtCheckErrnoFailure(epoll_ctl(EpollFds[EPOLL_CHAIN_COUNT - 1][0], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent), EINVAL);\r\n\r\n    //\r\n    // Verify the same file descriptor can be added to two different epolls\r\n    // even when they are linked.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_ctl(EpollFds[EPOLL_CHAIN_COUNT - 1][2], EPOLL_CTL_ADD, PipeFileDescriptors[0], &EpollControlEvent));\r\n\r\n    //\r\n    // Test if the really long chain can be waited on.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_wait(EpollFds[EPOLL_CHAIN_COUNT - 1][Index - 1], &EpollWaitEvent, 1, 0));\r\n\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    LxtCheckErrno(write(PipeFileDescriptors[1], \"\\n\", 1));\r\n    LxtCheckErrno(epoll_wait(EpollFds[EPOLL_CHAIN_COUNT - 1][Index - 1], &EpollWaitEvent, 1, -1));\r\n\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Try removing a node deep in the chain.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_ctl(\r\n        EpollFds[EPOLL_CHAIN_COUNT / 2][EPOLL_MAX_RECURSION_COUNT / 2],\r\n        EPOLL_CTL_DEL,\r\n        EpollFds[EPOLL_CHAIN_COUNT / 2][EPOLL_MAX_RECURSION_COUNT / 2 - 1],\r\n        NULL));\r\n\r\nErrorExit:\r\n    if (ExtraEpollFd != -1)\r\n    {\r\n        close(ExtraEpollFd);\r\n    }\r\n\r\n    for (Index = 0; Index < EPOLL_MAX_RECURSION_COUNT; Index += 1)\r\n    {\r\n        if (EpollContentFds[Index] != -1)\r\n        {\r\n            close(EpollContentFds[Index]);\r\n        }\r\n    }\r\n\r\n    for (ChainIndex = 0; ChainIndex < EPOLL_CHAIN_COUNT; ChainIndex += 1)\r\n    {\r\n        for (Index = 0; Index < EPOLL_MAX_RECURSION_COUNT; Index += 1)\r\n        {\r\n            if (EpollFds[ChainIndex][Index] != -1)\r\n            {\r\n                close(EpollFds[ChainIndex][Index]);\r\n            }\r\n        }\r\n    }\r\n\r\n    if (PipeFileDescriptors[1] != -1)\r\n    {\r\n        close(PipeFileDescriptors[1]);\r\n    }\r\n\r\n    if (PipeFileDescriptors[0] != -1)\r\n    {\r\n        close(PipeFileDescriptors[0]);\r\n    }\r\n\r\n    return Result;\r\n}"
  },
  {
    "path": "test/linux/unit_tests/eventfd.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    EventFd.c\r\n\r\nAbstract:\r\n\r\n    This file is a eventfd test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/types.h>\r\n#include <sys/wait.h>\r\n#include <sys/epoll.h>\r\n#include <sys/eventfd.h>\r\n\r\n#define LXT_NAME \"EventFd\"\r\n\r\n#define DEFAULT_USLEEP 5000\r\n\r\nint EventFdVariationReadWrite(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {{\"EventFdVariation - read, write\", EventFdVariationReadWrite}};\r\n\r\nint EventfdTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY();\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint EventFdVariationReadWrite(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    struct epoll_event EpollEvent;\r\n    int EpollFd;\r\n    int Fd;\r\n    int Index;\r\n    fd_set ReadFds;\r\n    int Result;\r\n    ssize_t Size;\r\n    int Status;\r\n    struct timeval Timeout;\r\n    uint64_t Value;\r\n    uint64_t Values[] = {1, 2, 10, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFE};\r\n    struct\r\n    {\r\n        uint64_t Value;\r\n        char Buffer[32];\r\n    } LargeValue;\r\n\r\n    EpollFd = -1;\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n\r\n    //\r\n    // FIXME: Add 0 variation\r\n    //\r\n\r\n    //\r\n    // Write the values to the event and read them back out single threaded.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = eventfd(0, 0));\r\n    for (Index = 0; Index < LXT_COUNT_OF(Values); Index += 1)\r\n    {\r\n\r\n        //\r\n        // Verify that the event is not ready for read (value is zero).\r\n        //\r\n\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        FD_ZERO(&ReadFds);\r\n        FD_SET(Fd, &ReadFds);\r\n        LxtCheckErrno(select((Fd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n        LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n        Value = Values[Index];\r\n        Size = write(Fd, &Value, sizeof(Value));\r\n        LxtCheckEqual(Size, sizeof(Value), \"%lld\");\r\n        LxtCheckEqual(Value, Values[Index], \"%llu\");\r\n\r\n        //\r\n        // Verify that the event is ready for read (value is NOT zero).\r\n        //\r\n\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        FD_ZERO(&ReadFds);\r\n        FD_SET(Fd, &ReadFds);\r\n        LxtCheckErrno(select((Fd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n        //\r\n        // Check again to verify nothing has changed.\r\n        //\r\n\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        FD_ZERO(&ReadFds);\r\n        FD_SET(Fd, &ReadFds);\r\n        LxtCheckErrno(select((Fd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n        LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n        Value = 0;\r\n        Size = read(Fd, &Value, sizeof(Value));\r\n        LxtCheckEqual(Size, sizeof(Value), \"%lld\");\r\n        LxtCheckEqual(Value, Values[Index], \"%llu\");\r\n    }\r\n\r\n    //\r\n    // Verify that the event is not ready for read (value is zero).\r\n    //\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    FD_ZERO(&ReadFds);\r\n    FD_SET(Fd, &ReadFds);\r\n    LxtCheckErrno(select((Fd + 1), &ReadFds, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Write the values to the event and read them back out on another thread.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(EpollFd = epoll_create(1));\r\n        EpollEvent.events = EPOLLIN | EPOLLET;\r\n        EpollEvent.data.fd = Fd;\r\n        LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, Fd, &EpollEvent));\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    for (Index = 0; Index < LXT_COUNT_OF(Values); Index += 1)\r\n    {\r\n        if (ChildPid == 0)\r\n        {\r\n\r\n            //\r\n            // Ignore the first value completely and don't read the second\r\n            // value to verify that epoll edge-triggered behavior is working as\r\n            // expected.\r\n            //\r\n\r\n            if (Index > 0)\r\n            {\r\n                if (Index == 1)\r\n                {\r\n\r\n                    //\r\n                    // Since the first write was ignored, need to make sure\r\n                    // that the second has completed so that the edge-triggered\r\n                    // epoll logic works as expected instead of racing with the\r\n                    // next write.\r\n                    //\r\n\r\n                    LXT_SYNCHRONIZATION_POINT();\r\n                }\r\n\r\n                LxtLogInfo(\"Waiting from child thread...\");\r\n                LxtCheckErrno(epoll_wait(EpollFd, &EpollEvent, 1, -1));\r\n                LxtCheckEqual(Result, 1, \"%d\") LxtCheckEqual(EpollEvent.data.fd, Fd, \"%d\");\r\n\r\n                //\r\n                // The epoll should be blocked.\r\n                //\r\n\r\n                LxtCheckErrno(epoll_wait(EpollFd, &EpollEvent, 1, 0));\r\n                LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n                //\r\n                // Skip the first two values to verify epoll edge-triggered\r\n                // behavior and then start verifying the value data.\r\n                //\r\n\r\n                if (Index > 1)\r\n                {\r\n                    Value = 0;\r\n                    Size = read(Fd, &Value, sizeof(Value));\r\n                    LxtCheckEqual(Size, sizeof(Value), \"%lld\");\r\n                    if (Index == 2)\r\n                    {\r\n                        LxtCheckEqual(Value, (Values[0] + Values[1] + Values[2]), \"%llu\");\r\n                    }\r\n                    else\r\n                    {\r\n                        LxtCheckEqual(Value, Values[Index], \"%llu\");\r\n                    }\r\n                }\r\n\r\n                //\r\n                // The epoll should still be blocked.\r\n                //\r\n\r\n                LxtCheckErrno(epoll_wait(EpollFd, &EpollEvent, 1, 0));\r\n                LxtCheckEqual(Result, 0, \"%d\")\r\n            }\r\n        }\r\n        else\r\n        {\r\n            LxtLogInfo(\"Writing 0x%llx from parent thread...\", Values[Index]);\r\n            Value = Values[Index];\r\n            Size = write(Fd, &Value, sizeof(Value));\r\n            LxtCheckEqual(Size, sizeof(Value), \"%lld\");\r\n            LxtCheckEqual(Value, Values[Index], \"%llu\");\r\n            if (Index == 1)\r\n            {\r\n                LXT_SYNCHRONIZATION_POINT();\r\n            }\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read and write out with a buffer larger than the default.\r\n    //\r\n\r\n    memset(&LargeValue, 0, sizeof(LargeValue));\r\n    LargeValue.Value = 1;\r\n    Size = write(Fd, &LargeValue, sizeof(LargeValue));\r\n    LxtCheckEqual(Size, sizeof(LargeValue.Value), \"%lld\");\r\n    LxtCheckEqual(LargeValue.Value, 1, \"%llu\");\r\n\r\n    LargeValue.Value = 0;\r\n    Size = read(Fd, &LargeValue, sizeof(LargeValue));\r\n    LxtCheckEqual(Size, sizeof(LargeValue.Value), \"%lld\");\r\n    LxtCheckEqual(LargeValue.Value, 1, \"%llu\");\r\n\r\n    //\r\n    // Check the error code for writing the maximum value.\r\n    //\r\n\r\n    Value = 0xFFFFFFFFFFFFFFFF;\r\n    LxtCheckErrnoFailure(write(Fd, &Value, sizeof(Value)), EINVAL);\r\n\r\n    //\r\n    // Check the error code for reading and writing a size of 0.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(read(Fd, &Value, 0), EINVAL);\r\n    LxtCheckErrnoFailure(write(Fd, &Value, 0), EINVAL);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd > 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (EpollFd > 0)\r\n    {\r\n        close(EpollFd);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/execve.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    execve.c\r\n\r\nAbstract:\r\n\r\n    This file contains the execve tests.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <pthread.h>\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <unistd.h>\r\n#include <sys/types.h>\r\n#include <sys/wait.h>\r\n#include <linux/binfmts.h>\r\n#include \"lxtutil.h\"\r\n\r\n#define LXT_NAME \"Execve\"\r\n\r\n#define LXT_EXECV_TEST_DIRECTORY \"/data/test/execvDir\"\r\n\r\nint ExecveExecValidate(char* Path);\r\n\r\nint ExecveValidate(int ExpectedPid);\r\n\r\nint ExecveVariationArguments(PLXT_ARGS Args);\r\n\r\nint ExecveVariationSingle(PLXT_ARGS Args);\r\n\r\nint ExecveVariationMultipleWithThreads(PLXT_ARGS Args);\r\n\r\nint ExecveWaitForChild(void);\r\n\r\nvoid* ExecveWorkerThread(void* Arg);\r\n\r\nvoid* ExecveWorkerThread2(void* Arg);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Execve - Single\", ExecveVariationSingle},\r\n    {\"Execve - Multiple with threads\", ExecveVariationMultipleWithThreads},\r\n    {\"Execve - Arguments\", ExecveVariationArguments}};\r\n\r\nint ExecveTestEntry(int Argc, char* Argv[], char** Envp)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    //\r\n    // For a child invocation, validate that the PID/TID is as expected.\r\n    //\r\n\r\n    if ((Argc == 3) && (strcmp(Argv[1], \"-c\") == 0))\r\n    {\r\n\r\n        int Pid;\r\n\r\n        Pid = atoi(Argv[2]);\r\n        LxtCheckResult(ExecveValidate(Pid));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // For environment variable child validation, make sure a non-NULL pointer\r\n    // is passed but there are no arguments.\r\n    //\r\n\r\n    if ((Argc == 2) && (strcmp(Argv[1], \"-e\") == 0))\r\n    {\r\n        LxtCheckTrue(Envp != NULL);\r\n\r\n        char** Env;\r\n        int Count = 0;\r\n        for (Env = Envp; *Env != NULL; Env += 1)\r\n        {\r\n            Count += 1;\r\n        }\r\n\r\n        LxtCheckTrue(Count == 0);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Run the master test.\r\n    //\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint ExecveExecValidate(char* Path)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char* ExecArgs[5];\r\n    char ExpectedPid[16];\r\n    int Pid;\r\n    int Result;\r\n\r\n    Pid = getpid();\r\n    LxtLogInfo(\"Execve'ing validation for PID %d\", Pid);\r\n\r\n    sprintf(ExpectedPid, \"%d\", Pid);\r\n    ExecArgs[0] = Path;\r\n    ExecArgs[1] = \"execve\";\r\n    ExecArgs[2] = \"-c\";\r\n    ExecArgs[3] = ExpectedPid;\r\n    ExecArgs[4] = NULL;\r\n    execv(ExecArgs[0], ExecArgs);\r\n    LxtCheckTrue(FALSE);\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint ExecveValidate(int ExpectedPid)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Pid;\r\n    int Result;\r\n    int ThreadCount;\r\n    int Tid;\r\n\r\n    //\r\n    // Check that the PID/TID matches the expected value.\r\n    //\r\n\r\n    Pid = getpid();\r\n    Tid = gettid();\r\n    LxtCheckTrue(Pid == ExpectedPid);\r\n    LxtCheckTrue(Tid == ExpectedPid);\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\n#define COMMAND_LINE_SIZE (((size_t)MAX_ARG_STRINGS) + 1)\r\n#define LARGE_STRING_SIZE (((size_t)MAX_ARG_STRLEN) + 1)\r\n#define MAX_STACK_SIZE ((size_t)(2 * 1024 * 1024))\r\n\r\nint ExecveVariationArguments(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    char* ExecArgs[4];\r\n    char** ExecArgsLong;\r\n    size_t Index;\r\n    char* LongString;\r\n    int Result;\r\n    int Status;\r\n    size_t TotalSize;\r\n\r\n    ExecArgsLong = NULL;\r\n    LongString = NULL;\r\n\r\n    //\r\n    // Test a null environment block.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = \"/bin/true\";\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(ExecveWaitForChild());\r\n    }\r\n\r\n    //\r\n    // Test exec args with spaces and path.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = \"/bin/true with a space\";\r\n        ExecArgs[1] = NULL;\r\n        LxtCheckErrno(LxtExecve(\"/bin/true\", ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(ExecveWaitForChild());\r\n    }\r\n\r\n    //\r\n    // Validate that a null environment block results in zero entries for the\r\n    // environment argument to the main function.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = WSL_UNIT_TEST_BINARY;\r\n        ExecArgs[1] = \"execve\";\r\n        ExecArgs[2] = \"-e\";\r\n        ExecArgs[3] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(ExecveWaitForChild());\r\n    }\r\n\r\n    //\r\n    // Allocate a very long string of 'a' and null terminate to\r\n    // validate command line parsing limits.\r\n    //\r\n\r\n    LongString = malloc(LARGE_STRING_SIZE);\r\n    if (LongString == NULL)\r\n    {\r\n        LxtLogError(\"malloc\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(LongString, 'a', LARGE_STRING_SIZE);\r\n    LongString[LARGE_STRING_SIZE - 1] = '\\0';\r\n\r\n    //\r\n    // Create a child and verify that exec fails with too long of a string\r\n    // in the command line or environment variable array.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = \"/bin/false\";\r\n        ExecArgs[1] = LongString;\r\n        ExecArgs[2] = NULL;\r\n        LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), E2BIG);\r\n        LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], NULL, &ExecArgs[1]), E2BIG);\r\n\r\n        //\r\n        // Shorten the string and verify the exec call succeeds.\r\n        //\r\n\r\n        ExecArgs[0] = \"/bin/true\";\r\n        ExecArgs[1] = LongString;\r\n        ExecArgs[2] = NULL;\r\n        LongString[LARGE_STRING_SIZE - 2] = '\\0';\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(ExecveWaitForChild());\r\n    }\r\n\r\n    //\r\n    // Allocate a huge argument array.\r\n    //\r\n\r\n    ExecArgsLong = malloc(MAX_STACK_SIZE * sizeof(char*));\r\n    if (ExecArgsLong == NULL)\r\n    {\r\n        LxtLogError(\"malloc of %zd size buffer\", (MAX_STACK_SIZE * sizeof(char*)));\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (Index = 0; Index < MAX_STACK_SIZE; Index += 1)\r\n    {\r\n        ExecArgsLong[Index] = \"a\";\r\n    }\r\n\r\n    ExecArgsLong[MAX_STACK_SIZE - 1] = NULL;\r\n\r\n    //\r\n    // Create a child and verify that exec fails with too many arguments in the\r\n    // command line or environment variable array.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgsLong[0] = \"/bin/false\";\r\n        ExecArgsLong[MAX_STACK_SIZE - 1] = NULL;\r\n        LxtCheckErrnoFailure(LxtExecve(ExecArgsLong[0], ExecArgsLong, NULL), E2BIG);\r\n        LxtCheckErrnoFailure(LxtExecve(ExecArgsLong[0], NULL, &ExecArgsLong[1]), E2BIG);\r\n\r\n        //\r\n        // Shorten the argument list and verify that the command succeeds.\r\n        //\r\n\r\n        ExecArgsLong[0] = \"/bin/true\";\r\n        ExecArgsLong[(MAX_STACK_SIZE / 4)] = NULL;\r\n        LxtCheckErrno(LxtExecve(ExecArgsLong[0], ExecArgsLong, NULL));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(ExecveWaitForChild());\r\n    }\r\n\r\n    //\r\n    // Test an empty command line array.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = NULL;\r\n        LxtCheckErrno(LxtExecve(\"/bin/echo\", ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit with SIGABRT.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtLogInfo(\"Waiting for child to exit\");\r\n        wait(&Status);\r\n        LxtLogInfo(\"Status %d\", Status);\r\n        LxtCheckTrue((WIFSIGNALED(Status)) && (WTERMSIG(Status) == SIGABRT));\r\n        LxtLogInfo(\"Child exited with SIGABRT\");\r\n    }\r\n\r\n    //\r\n    // Test a null command line pointer.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(LxtExecve(\"/bin/echo\", NULL, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit SIGABRT.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtLogInfo(\"Waiting for child to exit\");\r\n        wait(&Status);\r\n        LxtLogInfo(\"Status %d\", Status);\r\n        LxtCheckTrue((WIFSIGNALED(Status)) && (WTERMSIG(Status) == SIGABRT));\r\n        LxtLogInfo(\"Child exited with SIGABRT\");\r\n    }\r\n\r\n    //\r\n    // Check that executing a directory fails with the expected error code.\r\n    //\r\n\r\n    mkdir(LXT_EXECV_TEST_DIRECTORY, 0777);\r\n    ExecArgs[0] = LXT_EXECV_TEST_DIRECTORY;\r\n    ExecArgs[1] = NULL;\r\n    LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), EACCES);\r\n\r\n    //\r\n    // Verify that exec with a NULL filename fails.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtExecve(NULL, NULL, NULL), EFAULT);\r\n    ExecArgs[0] = \"/bin/echo\";\r\n    ExecArgs[1] = NULL;\r\n    LxtCheckErrnoFailure(LxtExecve(NULL, ExecArgs, NULL), EFAULT);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    rmdir(LXT_EXECV_TEST_DIRECTORY);\r\n    if (LongString != NULL)\r\n    {\r\n        free(LongString);\r\n    }\r\n\r\n    if (ExecArgsLong != NULL)\r\n    {\r\n        free(ExecArgsLong);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint ExecveVariationSingle(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Result;\r\n\r\n    LxtLogInfo(\"Forking single child\");\r\n    LxtCheckResult(ChildPid = fork());\r\n\r\n    //\r\n    // The child process executes the validation program.\r\n    //\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(ExecveExecValidate(WSL_UNIT_TEST_BINARY));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(ExecveWaitForChild());\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint ExecveVariationMultipleWithThreads(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n#define NUM_CHILDREN 32\r\n\r\n    int ChildPid;\r\n    int ProcessIndex;\r\n    int Result;\r\n\r\n    //\r\n    // Launch all child processes.\r\n    //\r\n\r\n    for (ProcessIndex = 0; ProcessIndex < NUM_CHILDREN; ProcessIndex += 1)\r\n    {\r\n        LxtLogInfo(\"Forking child (#%d)\", ProcessIndex);\r\n        LxtCheckResult(ChildPid = fork());\r\n\r\n        //\r\n        // In the child, create worker threads and then exec the validation\r\n        // step from the leader.\r\n        //\r\n\r\n        if (ChildPid == 0)\r\n        {\r\n\r\n            pthread_t Thread;\r\n            int ThreadCount;\r\n            int ThreadIndex;\r\n\r\n            ThreadCount = ProcessIndex + 1;\r\n            LxtLogInfo(\"Creating %d thread(s) for PID %d\", ThreadCount, getpid());\r\n            for (ThreadIndex = 0; ThreadIndex < ThreadCount; ThreadIndex += 1)\r\n            {\r\n                LxtCheckResultError(pthread_create(&Thread, NULL, ExecveWorkerThread, NULL));\r\n            }\r\n\r\n            //\r\n            // Sleep for 100ms and then execute the validation step.\r\n            //\r\n\r\n            usleep(100000);\r\n            LxtCheckResult(ExecveExecValidate(WSL_UNIT_TEST_BINARY));\r\n        }\r\n    }\r\n\r\n    //\r\n    // Launch again, this time calling exec from the non-leader thread.\r\n    //\r\n\r\n    for (ProcessIndex = 0; ProcessIndex < NUM_CHILDREN; ProcessIndex += 1)\r\n    {\r\n        LxtLogInfo(\"Forking child (#%d)\", ProcessIndex);\r\n        LxtCheckResult(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            pthread_t Thread;\r\n            int ThreadCount;\r\n            int ThreadIndex;\r\n\r\n            ThreadCount = ProcessIndex + 1;\r\n            LxtLogInfo(\"Creating %d thread(s) for PID %d\", ThreadCount, getpid());\r\n            for (ThreadIndex = 0; ThreadIndex < ThreadCount; ThreadIndex += 1)\r\n            {\r\n                LxtCheckResultError(pthread_create(&Thread, NULL, ExecveWorkerThread2, Args));\r\n            }\r\n\r\n            //\r\n            // Continuously sleep for 100ms.\r\n            //\r\n\r\n            for (;;)\r\n            {\r\n                usleep(100000);\r\n            }\r\n        }\r\n    }\r\n\r\n    //\r\n    // Wait for all child processes to exit.\r\n    //\r\n\r\n    LxtLogInfo(\"Waiting for children to exit\");\r\n    for (ProcessIndex = 0; ProcessIndex < (2 * NUM_CHILDREN); ProcessIndex += 1)\r\n    {\r\n\r\n        LxtCheckResult(ExecveWaitForChild());\r\n        LxtLogInfo(\"Child exited (#%d)\", ProcessIndex);\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint ExecveWaitForChild(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int Status;\r\n\r\n    wait(&Status);\r\n    LxtCheckTrue((WIFEXITED(Status)) && (WEXITSTATUS(Status) == 0));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid* ExecveWorkerThread(void* Arg)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    //\r\n    // Continuously sleep for 100ms.\r\n    //\r\n\r\n    for (;;)\r\n    {\r\n        usleep(100000);\r\n    }\r\n\r\n    return NULL;\r\n}\r\n\r\nvoid* ExecveWorkerThread2(void* Arg)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    PLXT_ARGS Args = (PLXT_ARGS)Arg;\r\n\r\n    //\r\n    // Sleep for 100ms and then execute the validation step.\r\n    //\r\n\r\n    usleep(100000);\r\n    LxtCheckResult(ExecveExecValidate(WSL_UNIT_TEST_BINARY));\r\n\r\nErrorExit:\r\n    return (void*)(long)Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/flock.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    flock.c\r\n\r\nAbstract:\r\n\r\n    This file is a flock test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/file.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n#include <signal.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/stat.h>\r\n#include <sys/types.h>\r\n\r\n#define LXT_NAME \"Flock\"\r\n\r\nint FnctlLockingVariation0(PLXT_ARGS Args);\r\n\r\nint FlockVariation0(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {{\"Flock\", FlockVariation0}, {\"Fcntl Locking\", FnctlLockingVariation0}};\r\n\r\nint FlockTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint FnctlLockingVariation0(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests file locking through the FCNTL system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies a pointer to the test arguments.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[10];\r\n    char ByteAlignedBuffer[sizeof(struct flock) + sizeof(struct flock64)];\r\n    int ChildPid;\r\n    int FileDescriptor;\r\n    struct flock* LockDescriptor;\r\n    struct flock64* LockDescriptor64;\r\n    int Result;\r\n\r\n    FileDescriptor = -1;\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Initialize the file for this test.\r\n    //\r\n\r\n    LxtCheckErrno(FileDescriptor = open(\"/data/test/fcntl_lock_test.bin\", O_RDWR | O_CREAT, S_IRWXU));\r\n\r\n    LxtCheckErrno(write(FileDescriptor, Buffer, 10));\r\n\r\n    //\r\n    // Set the lock descriptor.\r\n    //\r\n\r\n    memset(ByteAlignedBuffer, 0, sizeof(ByteAlignedBuffer));\r\n    LockDescriptor = (struct flock*)&ByteAlignedBuffer[1];\r\n    LockDescriptor->l_type = F_WRLCK;\r\n    LockDescriptor->l_whence = SEEK_SET;\r\n    LockDescriptor->l_start = 0;\r\n    LockDescriptor->l_len = 10;\r\n\r\n    //\r\n    // Get the current lock state.\r\n    //\r\n\r\n    LxtLogInfo(\"Fnctl locking - Checking that lock can be set.\");\r\n    LxtCheckErrno(fcntl(FileDescriptor, F_GETLK, LockDescriptor));\r\n    LxtCheckEqual(LockDescriptor->l_type, F_UNLCK, \"%x\");\r\n\r\n    //\r\n    // Set the lock.\r\n    //\r\n\r\n    LxtLogInfo(\"Fcntl locking - Setting the read lock by the parent process\");\r\n    memset(ByteAlignedBuffer, 0, sizeof(ByteAlignedBuffer));\r\n    LockDescriptor->l_type = F_RDLCK;\r\n    LxtCheckErrno(fcntl(FileDescriptor, F_SETLK, LockDescriptor));\r\n\r\n    //\r\n    // Now change the lock to be a write lock.\r\n    //\r\n\r\n    memset(ByteAlignedBuffer, 0, sizeof(ByteAlignedBuffer));\r\n    LockDescriptor64 = (struct flock64*)&ByteAlignedBuffer[1];\r\n    LockDescriptor64->l_type = F_WRLCK;\r\n    LockDescriptor64->l_whence = SEEK_SET;\r\n    LockDescriptor64->l_start = 0;\r\n    LockDescriptor64->l_len = 10;\r\n\r\n    //\r\n    // Set the lock.\r\n    //\r\n\r\n    LxtLogInfo(\"Fcntl locking - Setting the write lock with 64 bit set lock\");\r\n    LxtCheckErrno(fcntl(FileDescriptor, F_SETLK64, LockDescriptor64));\r\n\r\n    //\r\n    // Fork the process.\r\n    //\r\n\r\n    LxtLogInfo(\"Creating child process to test the lock.\");\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Ensure that the lock was correctly set before. The lock descriptor is\r\n        // correctly set from before. The test expects to see the lock type be\r\n        // changed to exclusive, WRLCK, though.\r\n        //\r\n\r\n        LxtLogInfo(\"Fcntl child locking - Reading lock type\");\r\n        LxtCheckErrno(fcntl(FileDescriptor, F_GETLK, LockDescriptor));\r\n        LxtCheckEqual(LockDescriptor->l_type, F_WRLCK, \"%X\");\r\n\r\n        Result = LXT_RESULT_SUCCESS;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS);\r\n\r\nErrorExit:\r\n    if (FileDescriptor != 0)\r\n    {\r\n        close(FileDescriptor);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint FlockVariation0(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n    int FileDescriptor1;\r\n    int FileDescriptor2;\r\n    int FileDescriptor3;\r\n    int DupedDescriptor;\r\n    int ChildPid;\r\n    int Index;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileDescriptor1 = -1;\r\n    FileDescriptor2 = -1;\r\n    FileDescriptor3 = -1;\r\n    DupedDescriptor = -1;\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Open a file that will be locked.\r\n    //\r\n\r\n    FileDescriptor1 = open(\"/data/test/flock_test.bin\", O_RDWR | O_CREAT, S_IRWXU);\r\n\r\n    if (FileDescriptor1 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    FileDescriptor2 = open(\"/data/test/flock_test.bin\", O_RDWR | O_CREAT, S_IRWXU);\r\n\r\n    if (FileDescriptor2 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    Result = flock(FileDescriptor1, LOCK_EX);\r\n\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Lock failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    FileDescriptor3 = open(\"/data/test/flock_test.bin\", O_RDWR | O_CREAT, S_IRWXU);\r\n\r\n    if (FileDescriptor3 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Lock the file from another descriptor (non-blocking) and it should fail\r\n    // accordingly.\r\n    //\r\n\r\n    Result = flock(FileDescriptor2, LOCK_EX | LOCK_NB);\r\n\r\n    if (Result == 0)\r\n    {\r\n        Result = -1;\r\n        LxtLogError(\"Lock succeeded but should have failed!\");\r\n        goto cleanup;\r\n    }\r\n\r\n    Result = errno;\r\n    if (Result != EWOULDBLOCK)\r\n    {\r\n        LxtLogError(\"Lock failed but with wrong error! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Dupe the owner descriptor and lock the file shared. This should convert\r\n    // the file to shared.\r\n    //\r\n\r\n    DupedDescriptor = dup(FileDescriptor1);\r\n\r\n    if (DupedDescriptor < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not dup the descriptor! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    for (Index = 0; Index < 2; Index += 1)\r\n    {\r\n\r\n        Result = flock(DupedDescriptor, LOCK_EX);\r\n\r\n        if (Result < 0)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"Lock exclusive conversion failed! %d\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        Result = flock(DupedDescriptor, LOCK_SH);\r\n\r\n        if (Result < 0)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"Lock shared conversion failed! %d\", Result);\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    //\r\n    // The lock is now owned shared by descriptor1 (via duped descriptor) so now\r\n    // descriptor 2 should be able to acquire it shared.\r\n    //\r\n\r\n    Result = flock(FileDescriptor2, LOCK_SH | LOCK_NB);\r\n\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Lock shared failed for descriptor2! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Unlock via descriptor1. That leaves just descriptor2 shared.\r\n    //\r\n\r\n    Result = flock(FileDescriptor1, LOCK_UN);\r\n\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Unlock failed for descriptor1! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Fork to create another thread to wait for lock.\r\n    //\r\n\r\n    for (Index = 0; Index < 2; Index += 1)\r\n    {\r\n\r\n        ChildPid = fork();\r\n\r\n        if (ChildPid == -1)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"Fork failed! %d\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        if (ChildPid == 0)\r\n        {\r\n            int FileDescriptor;\r\n\r\n            if (Index == 0)\r\n            {\r\n                FileDescriptor = FileDescriptor1;\r\n            }\r\n            else\r\n            {\r\n                FileDescriptor = FileDescriptor3;\r\n            }\r\n\r\n            //\r\n            // Wait for exclusive lock.\r\n            //\r\n\r\n            close(FileDescriptor2);\r\n            FileDescriptor2 = -1;\r\n\r\n            LxtLogInfo(\"C%u: Waiting for lock on FileDescriptor...\", Index);\r\n\r\n            Result = flock(FileDescriptor, LOCK_EX);\r\n\r\n            if (Result < 0)\r\n            {\r\n                Result = errno;\r\n                LxtLogError(\"Lock acquire failed for descriptor1! %d\", Result);\r\n                goto cleanup;\r\n            }\r\n\r\n            LxtLogInfo(\"C%u: Lock acquired on FileDescriptor...\", Index);\r\n\r\n            Result = flock(FileDescriptor, LOCK_UN);\r\n\r\n            if (Result < 0)\r\n            {\r\n                Result = errno;\r\n                LxtLogError(\"Unlock failed for descriptor1! %d\", Result);\r\n                goto cleanup;\r\n            }\r\n\r\n            LxtLogInfo(\"C%u: Sleeping 3 secs...\", Index);\r\n\r\n            usleep(3 * 1000 * 1000);\r\n\r\n            LxtLogInfo(\"C%u: Waiting for lock shared on FileDescriptor...\", Index);\r\n\r\n            Result = flock(FileDescriptor, LOCK_SH);\r\n\r\n            if (Result < 0)\r\n            {\r\n                Result = errno;\r\n                LxtLogError(\"Lock acquire failed for descriptor1! %d\", Result);\r\n                goto cleanup;\r\n            }\r\n\r\n            LxtLogInfo(\"C%u: Lock acquired on FileDescriptor...\", Index);\r\n\r\n            Result = flock(FileDescriptor, LOCK_UN);\r\n\r\n            if (Result < 0)\r\n            {\r\n                Result = errno;\r\n                LxtLogError(\"Unlock failed for descriptor1! %d\", Result);\r\n                goto cleanup;\r\n            }\r\n\r\n            if (Index == 0)\r\n            {\r\n                Result = LXT_RESULT_SUCCESS;\r\n                goto cleanup;\r\n            }\r\n\r\n            LxtLogInfo(\"C%u: Sleeping 3 secs...\", Index);\r\n\r\n            usleep(3 * 1000 * 1000);\r\n\r\n            LxtLogInfo(\"C%u: Waiting for lock exclusive to be terminated...\", Index);\r\n\r\n            Result = flock(FileDescriptor, LOCK_EX);\r\n\r\n            if (Result == 0)\r\n            {\r\n                Result = -1;\r\n                LxtLogError(\"Lock acquisition succeeded but EINTR expected!\");\r\n                goto cleanup;\r\n            }\r\n\r\n            Result = errno;\r\n\r\n            if (Result != EINTR)\r\n            {\r\n                LxtLogError(\"Lock acquisition failed but not with EINTR! %d\", Result);\r\n                goto cleanup;\r\n            }\r\n\r\n            Result = LXT_RESULT_SUCCESS;\r\n\r\n            goto cleanup;\r\n        }\r\n    }\r\n    LxtLogInfo(\"P: Waiting 3 seconds before releasing lock shared...\");\r\n\r\n    usleep(3 * 1000 * 1000);\r\n\r\n    Result = flock(FileDescriptor2, LOCK_UN);\r\n\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Unlock failed for descriptor2! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    usleep(1 * 1000 * 1000);\r\n\r\n    LxtLogInfo(\"P: Waiting to acquire lock exclusive...\");\r\n\r\n    Result = flock(FileDescriptor2, LOCK_EX);\r\n\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Lock exclusive failed for descriptor2! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    LxtLogInfo(\"P: Acquired lock exclusive.\");\r\n\r\n    LxtLogInfo(\"P: Sleeping 5 secs...\");\r\n\r\n    usleep(5 * 1000 * 1000);\r\n\r\n    LxtLogInfo(\"P: Releasing lock exclusive...\");\r\n    Result = flock(FileDescriptor2, LOCK_UN);\r\n\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Unlock failed for descriptor2! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    LxtLogInfo(\"P: Waiting to acquire lock shared...\");\r\n\r\n    Result = flock(FileDescriptor2, LOCK_SH);\r\n\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Lock shared failed for descriptor2! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    LxtLogInfo(\"P: Sleeping 5 secs...\");\r\n\r\n    usleep(5 * 1000 * 1000);\r\n\r\n    //\r\n    // Force the child's file lock wait to be interrupted by a signal.\r\n    //\r\n\r\n    kill(ChildPid, SIGKILL);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\ncleanup:\r\n\r\n    if (FileDescriptor1 != -1)\r\n    {\r\n        close(FileDescriptor1);\r\n    }\r\n\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n    }\r\n\r\n    if (DupedDescriptor != -1)\r\n    {\r\n        close(DupedDescriptor);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(0);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/fork.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Fork.c\r\n\r\nAbstract:\r\n\r\n    This file is a Fork test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/types.h>\r\n#include <sys/wait.h>\r\n#include <sys/syscall.h>\r\n#include <linux/futex.h>\r\n#include <sys/time.h>\r\n#include <fpu_control.h>\r\n#include <alloca.h>\r\n#include <stdlib.h>\r\n#include <sys/prctl.h>\r\n\r\n#if defined(__amd64__)\r\n\r\n#include <asm/prctl.h>\r\n\r\n#endif\r\n\r\n#if defined(__i386__)\r\n#define GET_STACK_POINTER asm(\"esp\")\r\n#elif defined(__amd64__)\r\n#define GET_STACK_POINTER asm(\"rsp\")\r\n#elif defined(__arm__) || defined(__aarch64__)\r\n#define GET_STACK_POINTER asm(\"sp\")\r\n#else\r\n#error \"Unsupported architecture\"\r\n#endif\r\n\r\n#include <fcntl.h>\r\n#include <unistd.h>\r\n\r\n#define LXT_NAME \"Fork\"\r\n\r\n#define LXT_INVALID_TID_VALUE -1\r\n#define LXT_INVALID_TID_ADDRESS ((void*)LXT_INVALID_TID_VALUE)\r\n#define LXT_THREAD_UMASK 0555\r\n#define LXT_CONTROL_WORD_DEFAULT 0x37f\r\n#define LXT_CONTROL_WORD_NEW 0x40\r\n#define LXT_STACK_SIZE (1 * 1024 * 1024)\r\n#define LXT_TEST_CWD \"/\"\r\n\r\nint SetTidAddress(PLXT_ARGS Args);\r\n\r\nint ForkPids(PLXT_ARGS Args);\r\n\r\nint ExecvFailure(PLXT_ARGS Args);\r\n\r\nint RobustFutex(PLXT_ARGS Args);\r\n\r\nint CloneFs(PLXT_ARGS Args);\r\n\r\nint CloneInvalidFlags(PLXT_ARGS Args);\r\n\r\nint CloneThread(PLXT_ARGS Args);\r\n\r\nint VForkTestBasic(PLXT_ARGS Args);\r\n\r\nint VForkTest(PLXT_ARGS Args);\r\n\r\nint CloneTestFlags(PLXT_ARGS Args);\r\n\r\nint CloneTestSignalParent(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n// N.B. Test ordering is important for child process variations.\r\n//\r\n// TODO_LX: Enable fork tests.\r\n//\r\n\r\n#define LXT_DEFAULT_VARIATIONS ((unsigned long)(-1))\r\n#define LXT_CHILD_VARIATIONS 0\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Fork check pids\", ForkPids},\r\n    {\"Set tid address\", SetTidAddress},\r\n    {\"Execv failure\", ExecvFailure},\r\n    {\"Get / Set Robust Futex List\", RobustFutex},\r\n    {\"Clone CLONE_FS\", CloneFs},\r\n    {\"Clone invalid flags\", CloneInvalidFlags},\r\n    {\"VFork test basic\", VForkTestBasic},\r\n    {\"Clone thread\", CloneThread},\r\n    {\"Vfork behavior\", VForkTest},\r\n    {\"Clone test flags\", CloneTestFlags},\r\n    {\"Clone signal parent test\", CloneTestSignalParent},\r\n};\r\n\r\nint ForkTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    if (Args.VariationMask == LXT_DEFAULT_VARIATIONS)\r\n    {\r\n        Args.VariationMask &= ~LXT_CHILD_VARIATIONS;\r\n    }\r\n\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nlong my_set_tid_address(int* tid)\r\n\r\n{\r\n    return syscall(SYS_set_tid_address, tid);\r\n}\r\n\r\nint my_futex(int* uaddr, int op, int val, const struct timespec* timeout, int* uaddr2, int val3)\r\n\r\n{\r\n    return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);\r\n}\r\n\r\nint my_set_robust_list(struct robust_list_head* head, size_t len)\r\n\r\n{\r\n    return syscall(SYS_set_robust_list, head, len);\r\n}\r\n\r\nint my_get_robust_list(int pid, struct robust_list_head** head_ptr, size_t* len_ptr)\r\n\r\n{\r\n    return syscall(SYS_get_robust_list, pid, head_ptr, len_ptr);\r\n}\r\n\r\nmode_t my_umask(mode_t mask)\r\n\r\n{\r\n    return syscall(SYS_umask, mask);\r\n}\r\n\r\nvoid* SetChildTidThread(void* Args)\r\n\r\n{\r\n\r\n    int* TidPointer = Args;\r\n    int tid;\r\n    mode_t umaskTemp;\r\n\r\n    tid = my_set_tid_address(TidPointer);\r\n    LxtLogInfo(\"In pthread tid = %d\", tid);\r\n    if (TidPointer != LXT_INVALID_TID_ADDRESS)\r\n    {\r\n        *TidPointer = tid;\r\n    }\r\n\r\n    umaskTemp = my_umask(LXT_THREAD_UMASK);\r\n    LxtLogInfo(\"In pthread tid = %d, initial umask %u, umask set to %u\", tid, umaskTemp, LXT_THREAD_UMASK);\r\n\r\n    sleep(2);\r\n    return 0;\r\n}\r\n\r\nint SetTidAddress(PLXT_ARGS Args)\r\n{\r\n\r\n    pid_t ChildPid;\r\n    mode_t ChildUmask;\r\n    int ForkTid;\r\n    mode_t OriginalUmask;\r\n    int ParentTid;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    int Return;\r\n    int SavedThread1Tid = 0;\r\n    pthread_t Thread1 = {0};\r\n    int Thread1Tid;\r\n    pthread_t Thread2 = {0};\r\n    int Thread2Tid;\r\n    mode_t UmaskTemp;\r\n\r\n    ChildUmask = 0770;\r\n    OriginalUmask = 0777;\r\n    ForkTid = -1;\r\n    UmaskTemp = my_umask(OriginalUmask);\r\n    LxtLogInfo(\"Initial umask %u\", UmaskTemp);\r\n    LxtLogInfo(\"OriginalUmask %u\", OriginalUmask);\r\n    ParentTid = -1;\r\n    Thread1Tid = -1;\r\n    Thread2Tid = -1;\r\n    ParentTid = my_set_tid_address(&ParentTid);\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Set the clear child tid value for the new process.\r\n        //\r\n\r\n        ForkTid = my_set_tid_address(&ForkTid);\r\n        UmaskTemp = my_umask(ChildUmask);\r\n        LxtLogInfo(\"Before Thread1\");\r\n        LxtLogInfo(\"Initial child umask %u\", UmaskTemp);\r\n        LxtLogInfo(\"ChildUmask %u\", ChildUmask);\r\n        LxtLogInfo(\"ParentTid %d\", ParentTid);\r\n        LxtLogInfo(\"ForkTid %d\", ForkTid);\r\n        LxtLogInfo(\"Thread1Tid %d\", Thread1Tid);\r\n        LxtLogInfo(\"Thread2Tid %d\", Thread2Tid);\r\n\r\n        //\r\n        // Spawn two threads and set a different address for each child tid.\r\n        //\r\n\r\n        LxtCheckErrno(pthread_create(&Thread1, NULL, SetChildTidThread, &Thread1Tid));\r\n        LxtCheckErrno(pthread_create(&Thread2, NULL, SetChildTidThread, LXT_INVALID_TID_ADDRESS));\r\n        sleep(1);\r\n        SavedThread1Tid = Thread1Tid;\r\n        UmaskTemp = my_umask(ChildUmask);\r\n        LxtLogInfo(\"After thread creation\");\r\n        LxtLogInfo(\"Original umask %u\", UmaskTemp);\r\n        LxtLogInfo(\"set back to ChildUmask %u\", ChildUmask);\r\n        LxtLogInfo(\"ParentTid %d\", ParentTid);\r\n        LxtLogInfo(\"ForkTid %d\", ForkTid);\r\n        LxtLogInfo(\"Thread1Tid %d\", Thread1Tid);\r\n        LxtLogInfo(\"Thread2Tid %d\", Thread2Tid);\r\n        if (Thread1Tid == 0)\r\n        {\r\n            LxtLogError(\"Thread1Tid == 0 after calling set_tid_address\");\r\n        }\r\n\r\n        //\r\n        // Do a futex wait on Thread1Tid and validate that it has been set to 0\r\n        // by the kernel. Futex will fail with EAGAIN if the value has already\r\n        // been set.\r\n        //\r\n\r\n        Return = my_futex(&Thread1Tid, FUTEX_WAIT, SavedThread1Tid, NULL, NULL, 0);\r\n        if (Return != 0)\r\n        {\r\n            if (errno != EAGAIN)\r\n            {\r\n                LxtLogError(\"futex returned unexpected error %d - %s\", errno, strerror(errno));\r\n            }\r\n        }\r\n\r\n        //\r\n        // Don't join thread 1; pthread_join is implemented using the clear\r\n        // tid address, so since it has been changed it won't work.\r\n        //\r\n\r\n        pthread_join(Thread2, NULL);\r\n\r\n        LxtLogInfo(\"After Thread join and futex wait\");\r\n        LxtLogInfo(\"ParentTid %d\", ParentTid);\r\n        LxtLogInfo(\"ForkTid %d\", ForkTid);\r\n        LxtLogInfo(\"Thread1Tid %d\", Thread1Tid);\r\n        LxtLogInfo(\"Thread2Tid %d\", Thread2Tid);\r\n        if (Thread1Tid != 0)\r\n        {\r\n            LxtLogError(\"Thread1Tid != 0, was %d\", Thread1Tid);\r\n        }\r\n\r\n        if (Thread2Tid != LXT_INVALID_TID_VALUE)\r\n        {\r\n            LxtLogError(\"Thread2Tid != -1, was %d\", Thread2Tid);\r\n        }\r\n\r\n        _exit(0);\r\n    }\r\n\r\n    LxtWaitPidPollOptions(ChildPid, 0, 0, 5);\r\n    LxtLogInfo(\"Parent after fork\");\r\n    LxtLogInfo(\"ParentTid %d\", ParentTid);\r\n    LxtLogInfo(\"ForkTid %d\", ForkTid);\r\n    LxtLogInfo(\"Thread1Tid %d\", Thread1Tid);\r\n    LxtLogInfo(\"Thread2Tid %d\", Thread2Tid);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint ForkPids(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Result;\r\n    pid_t ParentPid;\r\n    pid_t Pid;\r\n    pid_t WaitPidResult;\r\n    int WaitPidStatus;\r\n\r\n    //\r\n    // Basic test for fork and vfork that confirms pids are incremented by 1 to\r\n    // ensure syscalls were plumbed correctly. Additional tests should be added\r\n    // to check the many other cases for fork.\r\n    //\r\n\r\n    Pid = getpid();\r\n    if (Pid == 0)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"getpid returned 0 for parent\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Fork should return parent + 1 (assumes no other processes are running).\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Result = LXT_RESULT_SUCCESS;\r\n        ChildPid = getpid();\r\n        if (ChildPid == 0)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"getpid returned 0 for child\");\r\n        }\r\n\r\n        ParentPid = getppid();\r\n        if (ParentPid != Pid)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"fork() Unexpected parent pid %d for child pid %d\", ParentPid, ChildPid);\r\n        }\r\n\r\n        _exit(Result);\r\n    }\r\n\r\n    LxtCheckResult((WaitPidResult = waitpid(ChildPid, &WaitPidStatus, 0)));\r\n    if ((WIFEXITED(WaitPidStatus) == 0) || (WEXITSTATUS(WaitPidStatus) != 0))\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Unexpected child pid exit status %d\", WaitPidStatus);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(ChildPid = vfork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Result = LXT_RESULT_SUCCESS;\r\n        ChildPid = getpid();\r\n        if (ChildPid == 0)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"getpid returned 0 for child\");\r\n        }\r\n\r\n        ParentPid = getppid();\r\n        if (ParentPid != Pid)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"vfork() Unexpected parent pid %d for child pid %d\", ParentPid, ChildPid);\r\n        }\r\n\r\n        _exit(Result);\r\n    }\r\n\r\n    LxtCheckResult((WaitPidResult = waitpid(ChildPid, &WaitPidStatus, 0)));\r\n    if ((WIFEXITED(WaitPidStatus) == 0) || (WEXITSTATUS(WaitPidStatus) != 0))\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Unexpected child pid exit status %d\", WaitPidStatus);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint ExecvFailure(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char* CommandLine1[] = {\"/foo/bar/foo/bar\", NULL};\r\n    char* CommandLine2[] = {\"/data/test/Makefile\", NULL};\r\n    int Result;\r\n\r\n    //\r\n    // Check that execv fails for an invalid file name and an invalid elf file.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(execv(CommandLine1[0], CommandLine1), ENOENT);\r\n    LxtCheckErrnoFailure(execv(CommandLine2[0], CommandLine2), ENOEXEC);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint RobustFutex(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct robust_list_head Head;\r\n    struct robust_list_head* HeadReturned;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    size_t SizeReturned;\r\n\r\n    //\r\n    // Set and get the robust list.\r\n    //\r\n\r\n    LxtCheckResult(my_set_robust_list(&Head, sizeof(struct robust_list_head)));\r\n    LxtCheckResult(my_get_robust_list(0, &HeadReturned, &SizeReturned));\r\n\r\n    if (HeadReturned != &Head)\r\n    {\r\n        LxtLogError(\"HeadReturned %p != &Head %p\", HeadReturned, &Head);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (SizeReturned != sizeof(struct robust_list_head))\r\n    {\r\n        LxtLogError(\"SizeReturned %u != sizeof(struct robust_list_head) %u\", SizeReturned, sizeof(struct robust_list_head));\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // set_robust_list validates the size of the buffer.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(my_set_robust_list(&Head, 0), EINVAL);\r\n    LxtCheckErrnoFailure(my_set_robust_list(&Head, (sizeof(struct robust_list_head) + 1)), EINVAL);\r\n\r\n    //\r\n    // get_robust_list validates the buffers.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(my_get_robust_list(0, NULL, &SizeReturned), EFAULT);\r\n    LxtCheckErrnoFailure(my_get_robust_list(0, &HeadReturned, NULL), EFAULT);\r\n\r\n    //\r\n    // No validation is done on the buffer for set_robust_list.\r\n    //\r\n\r\n    LxtCheckResult(my_set_robust_list(NULL, sizeof(struct robust_list_head)));\r\n    LxtCheckResult(my_set_robust_list((void*)-1, sizeof(struct robust_list_head)));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint CloneFs(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char BackupCwd[256];\r\n    pid_t ChildPid;\r\n    char Path[256];\r\n    bool RestoreCwd;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n    RestoreCwd = false;\r\n    LxtLogInfo(\"cwd = %s\", getcwd(BackupCwd, sizeof(BackupCwd)));\r\n    LxtCheckResult(ChildPid = LxtCloneSyscall((CLONE_FS | SIGCHLD), NULL, NULL, NULL, NULL));\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(chdir(LXT_TEST_CWD));\r\n        LxtLogInfo(\"child cwd = %s\", getcwd(Path, sizeof(Path)));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    RestoreCwd = true;\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Ensure the parent's current working directory was changed.\r\n    //\r\n\r\n    LxtLogInfo(\"parent cwd = %s\", getcwd(Path, sizeof(Path)));\r\n    LxtCheckStringEqual(Path, LXT_TEST_CWD);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (RestoreCwd != false)\r\n    {\r\n        chdir(BackupCwd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint CloneInvalidFlags(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Check for failure cases.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(ChildPid = LxtCloneSyscall(CLONE_SIGHAND, NULL, NULL, NULL, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(ChildPid = LxtCloneSyscall(CLONE_THREAD, NULL, NULL, NULL, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(ChildPid = LxtCloneSyscall((CLONE_FS | CLONE_NEWNS), NULL, NULL, NULL, NULL), EINVAL);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nstruct CloneThreadArgs\r\n{\r\n    long Fs0;\r\n    unsigned long FsBase;\r\n    unsigned long GsBase;\r\n};\r\n\r\nint CloneThreadEntry(void* Argument)\r\n\r\n{\r\n\r\n    struct CloneThreadArgs* Args;\r\n    unsigned long FsBase;\r\n    int Result;\r\n\r\n    Args = Argument;\r\n    Result = 0;\r\n\r\n    //\r\n    // Make sure TLS values can be read without SIGSEGV.\r\n    //\r\n\r\n#if defined(__amd64__)\r\n\r\n    __asm(\"movq %%fs:0, %0\" : \"=r\"(Args->Fs0));\r\n    syscall(SYS_arch_prctl, ARCH_GET_FS, &FsBase);\r\n    Result = (Args->FsBase != FsBase);\r\n    Args->FsBase = FsBase;\r\n    syscall(SYS_arch_prctl, ARCH_GET_GS, &Args->GsBase);\r\n\r\n#endif\r\n\r\n    syscall(SYS_exit, Result);\r\n    return Result;\r\n}\r\n\r\nint CloneThread(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct CloneThreadArgs ThreadArgs;\r\n    int Flags;\r\n    unsigned long FsBase;\r\n    pid_t Pid;\r\n    int Result;\r\n    char* Stack;\r\n    size_t StackSize;\r\n    int Status;\r\n    pid_t Tid;\r\n    long Tls[1];\r\n\r\n    StackSize = 1024 * 1024;\r\n    Stack = malloc(StackSize);\r\n    Tls[0] = (long)&Tls[0];\r\n    Flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;\r\n\r\n    //\r\n    // Clone without setting TLS.\r\n    //\r\n\r\n    LxtCheckErrno(clone(CloneThreadEntry, Stack + StackSize, Flags, &ThreadArgs, &Tid, NULL, &Tid));\r\n    LxtCheckErrno(LxtJoinThread(&Tid));\r\n\r\n#if defined(__amd64__)\r\n\r\n    LxtCheckErrno(syscall(SYS_arch_prctl, ARCH_GET_FS, &FsBase));\r\n    LxtCheckEqual(ThreadArgs.FsBase, FsBase, \"%lx\");\r\n\r\n#endif\r\n\r\n    //\r\n    // Clone and set TLS.\r\n    //\r\n\r\n    ThreadArgs.Fs0 = 0;\r\n    ThreadArgs.FsBase = 0;\r\n    ThreadArgs.GsBase = 0;\r\n\r\n#if defined(__amd64__)\r\n\r\n    LxtCheckErrno(syscall(SYS_arch_prctl, ARCH_SET_GS, &ThreadArgs));\r\n\r\n#endif\r\n\r\n    LxtCheckErrno(clone(CloneThreadEntry, Stack + StackSize, Flags | CLONE_SETTLS, &ThreadArgs, &Tid, Tls, &Tid));\r\n    LxtCheckErrno(LxtJoinThread(&Tid));\r\n\r\n#if defined(__amd64__)\r\n\r\n    LxtCheckEqual(ThreadArgs.Fs0, Tls[0], \"%ld\");\r\n    LxtCheckEqual(ThreadArgs.FsBase, (unsigned long)&Tls[0], \"%lx\");\r\n\r\n    //\r\n    // Ensure GS base is inherited.\r\n    //\r\n\r\n    LxtCheckEqual(ThreadArgs.GsBase, (unsigned long)&ThreadArgs, \"%lx\");\r\n\r\n#endif\r\n\r\n    //\r\n    // Disallow invalid TLS values.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(clone(CloneThreadEntry, Stack + StackSize, Flags | CLONE_SETTLS, NULL, &Tid, (void*)-1, &Tid), EPERM);\r\n    LxtCheckErrno(LxtJoinThread(&Tid));\r\n\r\n    //\r\n    // Set TLS on thread group clone too.\r\n    //\r\n\r\n    ThreadArgs.FsBase = (unsigned long)&Tls[0];\r\n    LxtCheckErrno(Pid = clone(CloneThreadEntry, Stack + StackSize, CLONE_SETTLS | SIGCHLD, &ThreadArgs, NULL, Tls, NULL));\r\n    LxtCheckErrno(waitpid(Pid, &Status, 0));\r\n    if (!WIFEXITED(Status) || WEXITSTATUS(Status) != 0)\r\n    {\r\n        LxtLogError(\"FS check failed: %x\", Status);\r\n    }\r\n\r\nErrorExit:\r\n    free(Stack);\r\n    return Result;\r\n}\r\n\r\nint VForkTestBasicChild(void* Param)\r\n\r\n{\r\n\r\n    return 0;\r\n}\r\n\r\nint VForkTestBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_CLONE_ARGS CloneArgs;\r\n    pid_t ChildPid;\r\n    pid_t Pid;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Check that vfork runs in a new threadgroup.\r\n    //\r\n\r\n    Pid = LxtGetTid();\r\n    LxtCheckErrno(ChildPid = vfork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckNotEqual(Pid, ChildPid, \"%d\");\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Repeat the above with the clone variant of vfork.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = LxtCloneSyscall(CLONE_VM | CLONE_VFORK | SIGCHLD, NULL, NULL, NULL, NULL));\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckNotEqual(Pid, ChildPid, \"%d\");\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if ((Result < 0) && (ChildPid != 0))\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VForkTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char* ChildCmdLine[] = {WSL_UNIT_TEST_BINARY, Args->Argv[0], \"-l\", \"2\", \"-v\", \"32\", NULL};\r\n\r\n    LxtLogInfo(\"VForkTest ChildCmdLine: %s %s\", ChildCmdLine[0], ChildCmdLine[1]);\r\n\r\n    pid_t ChildPid;\r\n    pid_t ChildPidFromChild;\r\n    pid_t ChildPidFromChildNested;\r\n    pid_t ChildPidNested;\r\n    unsigned short ControlWord;\r\n    unsigned short OriginalControlWord;\r\n    char* InvalidCmdLine[] = {\"/data/test/Makefile\", NULL};\r\n    pid_t Pid;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n    ChildPidFromChild = -1;\r\n    ChildPidFromChild = -1;\r\n    ChildPidFromChildNested = -1;\r\n    ChildPidNested = -1;\r\n    LxtCheckResult(LxtSignalInitialize());\r\n\r\n    //\r\n    // Check that vfork runs in a new threadgroup but in the same address space.\r\n    //\r\n\r\n    Pid = LxtGetTid();\r\n    LxtCheckErrno(ChildPid = vfork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ChildPidFromChild = LxtGetTid();\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckNotEqual(Pid, ChildPidFromChild, \"%d\");\r\n    LxtCheckEqual(ChildPid, ChildPidFromChild, \"%d\");\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Release the above with execv.\r\n    //\r\n\r\n    ChildPidFromChild = -1;\r\n    Pid = LxtGetTid();\r\n    LxtCheckErrno(ChildPid = vfork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ChildPidFromChild = LxtGetTid();\r\n        LxtCheckErrno(execv(ChildCmdLine[0], ChildCmdLine));\r\n        _exit(LXT_RESULT_FAILURE);\r\n    }\r\n\r\n    LxtCheckNotEqual(Pid, ChildPidFromChild, \"%d\");\r\n    LxtCheckEqual(ChildPid, ChildPidFromChild, \"%d\");\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Repeat the above with execv failure.\r\n    //\r\n\r\n    ChildPidFromChild = -1;\r\n    Pid = LxtGetTid();\r\n    LxtCheckErrno(ChildPid = vfork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ChildPidFromChild = LxtGetTid();\r\n        LxtCheckErrnoFailure(execv(InvalidCmdLine[0], InvalidCmdLine), ENOEXEC);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckNotEqual(Pid, ChildPidFromChild, \"%d\");\r\n    LxtCheckEqual(ChildPid, ChildPidFromChild, \"%d\");\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Check that signals sent to the parent after the\r\n    // parent releases the address space.\r\n    //\r\n\r\n    LxtCheckResult(LxtSignalSetupHandler(SIGUSR1, 0));\r\n    ChildPidFromChild = -1;\r\n    Pid = LxtGetTid();\r\n    LxtCheckErrno(ChildPid = vfork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ChildPidFromChild = LxtGetTid();\r\n        LxtSignalInitializeThread();\r\n        LxtCheckErrno(LxtTKill(Pid, SIGUSR1));\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckNotEqual(Pid, ChildPidFromChild, \"%d\");\r\n    LxtCheckEqual(ChildPid, ChildPidFromChild, \"%d\");\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckResult(LxtSignalCheckReceived(SIGUSR1));\r\n    LxtSignalResetReceived();\r\n\r\n    //\r\n    // Check the behavior for nested vfork.\r\n    //\r\n\r\n    Pid = LxtGetTid();\r\n    LxtCheckErrno(ChildPid = vfork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ChildPidFromChild = LxtGetTid();\r\n        LxtCheckErrno(ChildPidNested = vfork());\r\n        if (ChildPidNested == 0)\r\n        {\r\n            ChildPidFromChildNested = LxtGetTid();\r\n            _exit(LXT_RESULT_SUCCESS);\r\n        }\r\n\r\n        LxtCheckNotEqual(ChildPid, ChildPidFromChildNested, \"%d\");\r\n        LxtCheckEqual(ChildPidNested, ChildPidFromChildNested, \"%d\");\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPidNested, LXT_RESULT_SUCCESS));\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckNotEqual(Pid, ChildPidFromChild, \"%d\");\r\n    LxtCheckEqual(ChildPid, ChildPidFromChild, \"%d\");\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n#if defined(__i386__) || defined(__amd64__)\r\n\r\n    //\r\n    // Check that floating point context is preserved across vfork.\r\n    //\r\n\r\n    _FPU_GETCW(OriginalControlWord);\r\n    // LX_TODO: Initial control word is not being set correctly\r\n    // LxtCheckEqual(LXT_CONTROL_WORD_DEFAULT, OriginalControlWord, \"%hu\");\r\n    Pid = LxtGetTid();\r\n    LxtCheckErrno(ChildPid = vfork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ChildPidFromChild = LxtGetTid();\r\n        ControlWord = LXT_CONTROL_WORD_NEW;\r\n        _FPU_SETCW(ControlWord);\r\n        _FPU_GETCW(ControlWord);\r\n        LxtCheckEqual(LXT_CONTROL_WORD_NEW, ControlWord, \"%hu\");\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    _FPU_GETCW(ControlWord);\r\n    LxtCheckEqual(OriginalControlWord, ControlWord, \"%hu\");\r\n    LxtCheckNotEqual(Pid, ChildPidFromChild, \"%d\");\r\n    LxtCheckEqual(ChildPid, ChildPidFromChild, \"%d\");\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n#endif\r\n\r\n    //\r\n    // Check the stack pointer isn't modified across vfork.\r\n    //\r\n\r\n    {\r\n        register unsigned long Rsp GET_STACK_POINTER;\r\n        LxtCheckNotEqual(Rsp, 0, \"%lu\");\r\n        Pid = LxtGetTid();\r\n        alloca(1024);\r\n        LxtCheckErrno(ChildPid = vfork());\r\n        if (ChildPid == 0)\r\n        {\r\n            register unsigned long RspChild GET_STACK_POINTER;\r\n            LxtCheckEqual(Rsp, RspChild, \"%lu\");\r\n            ChildPidFromChild = LxtGetTid();\r\n            _exit(LXT_RESULT_SUCCESS);\r\n        }\r\n\r\n        LxtCheckNotEqual(Pid, ChildPidFromChild, \"%d\");\r\n        LxtCheckEqual(ChildPid, ChildPidFromChild, \"%d\");\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nstruct CloneFlagArgs\r\n{\r\n    int Test;\r\n    int Flags;\r\n    int Fd;\r\n};\r\n\r\nint CloneFlags[] = {\r\n    SIGCHLD,\r\n    SIGCHLD | CLONE_FS,\r\n    SIGCHLD | CLONE_FILES,\r\n    SIGCHLD | CLONE_FS | CLONE_FILES,\r\n    SIGCHLD | CLONE_VFORK,\r\n    SIGUSR1,\r\n    0,\r\n    125, // invalid signal\r\n    CLONE_THREAD | CLONE_VM | CLONE_SIGHAND,\r\n    CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_FS,\r\n    CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_FILES,\r\n    CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_FS | CLONE_FILES,\r\n    CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_VFORK,\r\n    SIGCHLD | CLONE_THREAD | CLONE_VM | CLONE_SIGHAND,\r\n    125 | CLONE_THREAD | CLONE_VM | CLONE_SIGHAND,\r\n};\r\n\r\nint CloneFlagEntry(void* Arg)\r\n\r\n{\r\n\r\n    struct CloneFlagArgs* FlagArgs = Arg;\r\n    close(FlagArgs->Fd);\r\n    chdir(\"/\");\r\n    syscall(SYS_exit, 0);\r\n    return 0;\r\n}\r\n\r\nint CloneTestFlags(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int Result;\r\n    char __attribute__((aligned(16))) Stack[65536];\r\n\r\n    int Root;\r\n    LxtCheckErrno(Root = open(\".\", O_RDONLY));\r\n\r\n    for (int Test = 0; Test < sizeof(CloneFlags) / sizeof(CloneFlags[0]); Test++)\r\n    {\r\n        int Flags = CloneFlags[Test];\r\n\r\n        int Fd;\r\n        LxtCheckErrno(Fd = dup(Root));\r\n\r\n        int TermSignal = Flags & 0xff;\r\n        if ((Flags & CLONE_THREAD) != 0 || TermSignal < 1 || TermSignal > 64)\r\n        {\r\n            TermSignal = 0;\r\n        }\r\n\r\n        if (TermSignal != 0 && TermSignal != SIGCHLD)\r\n        {\r\n            signal(TermSignal, SIG_IGN);\r\n        }\r\n\r\n        LxtLogInfo(\"Test %d: Flags 0x%x\", Test, Flags);\r\n\r\n        struct CloneFlagArgs FlagArgs = {0};\r\n        FlagArgs.Test = Test;\r\n        FlagArgs.Flags = Flags;\r\n        FlagArgs.Fd = Fd;\r\n\r\n        if (Flags & CLONE_THREAD)\r\n        {\r\n            Flags |= CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;\r\n        }\r\n\r\n        int Pid;\r\n        int Tid;\r\n        LxtCheckErrno(Pid = clone(CloneFlagEntry, Stack + sizeof(Stack), Flags, &FlagArgs, &Tid, NULL, &Tid));\r\n\r\n        if (Flags & CLONE_THREAD)\r\n        {\r\n            LxtCheckErrnoFailure(waitpid(Pid, NULL, __WALL), ECHILD);\r\n            LxtCheckErrno(LxtJoinThread(&Tid));\r\n        }\r\n        else if (TermSignal == SIGCHLD)\r\n        {\r\n            LxtCheckErrnoFailure(waitpid(Pid, NULL, __WCLONE), ECHILD);\r\n            LxtCheckErrno(waitpid(Pid, NULL, 0));\r\n        }\r\n        else\r\n        {\r\n            LxtCheckErrnoFailure(waitpid(Pid, NULL, 0), ECHILD);\r\n            LxtCheckErrno(waitpid(Pid, NULL, __WCLONE));\r\n        }\r\n\r\n        if (TermSignal != 0)\r\n        {\r\n            signal(TermSignal, SIG_DFL);\r\n        }\r\n\r\n        if (Flags & CLONE_FILES)\r\n        {\r\n            // Make sure Fd is not open.\r\n            LxtCheckErrnoFailure(fcntl(Fd, F_GETFL), EBADF);\r\n        }\r\n        else\r\n        {\r\n            // Make sure Fd is still open.\r\n            LxtCheckErrno(fcntl(Fd, F_GETFL));\r\n            close(Fd);\r\n        }\r\n\r\n        char Path[1024] = {0};\r\n        LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));\r\n        if (Flags & CLONE_FS)\r\n        {\r\n            if (strcmp(Path, \"/\") != 0)\r\n            {\r\n                LxtLogError(\"Root directory did not change from %s.\", Path);\r\n            }\r\n            fchdir(Root);\r\n        }\r\n        else\r\n        {\r\n            if (strcmp(Path, \"/\") == 0)\r\n            {\r\n                LxtLogError(\"Root directory changed.\");\r\n            }\r\n        }\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint CloneTestSignalEntry(void* Arg)\r\n{\r\n    //\r\n    // Make sure the two SIGCHLD signals don't coalesce.\r\n    //\r\n\r\n    usleep(500000);\r\n    syscall(SYS_exit, 0);\r\n    return 1;\r\n}\r\n\r\nstatic int CloneTestSignalArrived;\r\n\r\nvoid CloneTestSignalHandler(int Signal, siginfo_t* Info, void* Extra)\r\n{\r\n\r\n    CloneTestSignalArrived++;\r\n}\r\n\r\nint CloneTestSignalParent(PLXT_ARGS Args)\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(LxtSignalInitialize());\r\n    LxtCheckErrno(LxtSignalBlock(SIGCHLD));\r\n\r\n    int Pid = 0;\r\n    volatile int ChildPid = 0;\r\n    LxtCheckErrno(Pid = vfork());\r\n    if (Pid == 0)\r\n    {\r\n        char Stack[65536];\r\n\r\n        //\r\n        // Even though SIGUSR1 is passed here, SIGCHLD should be the\r\n        // received signal since CLONE_PARENT is also passed\r\n        // (SIGCHLD comes from the vfork call above).\r\n        //\r\n\r\n        ChildPid = clone(CloneTestSignalEntry, Stack + sizeof(Stack), SIGUSR1 | CLONE_PARENT, NULL);\r\n\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckErrno(LxtSignalWaitBlocked(SIGCHLD, Pid, 1));\r\n    int Status;\r\n    LxtCheckErrno(waitpid(Pid, &Status, 0));\r\n\r\n    LxtCheckErrno(ChildPid);\r\n    LxtCheckErrno(LxtSignalWaitBlocked(SIGCHLD, ChildPid, 1));\r\n    LxtCheckErrno(waitpid(ChildPid, NULL, 0));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}"
  },
  {
    "path": "test/linux/unit_tests/fscommon.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    fscommon.c\r\n\r\nAbstract:\r\n\r\n    This file contains common FS unit tests that are run on both LxFs and DrvFs.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <poll.h>\r\n#include <dirent.h>\r\n#include <sys/time.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/ioctl.h>\r\n#include <sys/uio.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n#include <sys/syscall.h>\r\n#include <fcntl.h>\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <linux/capability.h>\r\n#include <sys/mount.h>\r\n#include <sys/sysmacros.h>\r\n#include <libmount/libmount.h>\r\n#include \"lxtmount.h\"\r\n#include \"lxtfs.h\"\r\n\r\n#define LXT_NAME_LXFS \"fscommon_lxfs\"\r\n#define LXT_NAME_DRVFS \"fscommon_drvfs\"\r\n\r\n#define FS_TEST_DIR_PARENT \"/data/fstest\"\r\n#define FS_CLEX_TEST_DIR_NAME FS_TEST_DIR_PARENT \"/CLEX_test\"\r\n#define FS_READLINK_TEST_FILE FS_TEST_DIR_PARENT \"/readlink_testfile\"\r\n#define FS_READLINK_TEST_LINK FS_TEST_DIR_PARENT \"/readlink_testlink\"\r\n#define FS_RENAMEAT_TEST_DIR FS_TEST_DIR_PARENT \"/rename_test\"\r\n#define FS_TRAILING_TEST_FILE FS_TEST_DIR_PARENT \"/trailing_test_file\"\r\n#define FS_TRAILING_TEST_DIR FS_TEST_DIR_PARENT \"/trailing_test_dir\"\r\n#define FS_TRAILING_TEST_LINK FS_TEST_DIR_PARENT \"/trailing_test_link\"\r\n#define FS_MKNOD_TEST_FILE FS_TEST_DIR_PARENT \"/myzero\"\r\n#define FS_MKNOD_TEST_FILE2 FS_TEST_DIR_PARENT \"/myzero2\"\r\n#define FS_CHROOT_TEST_DIR FS_TEST_DIR_PARENT \"/chroot_test\"\r\n#define FS_CHROOT_TEST_DIR_CHILD_FROM_ROOT \"/child\"\r\n#define FS_CHROOT_TEST_DIR_CHILD FS_CHROOT_TEST_DIR FS_CHROOT_TEST_DIR_CHILD_FROM_ROOT\r\n#define FS_CHROOT_TEST_DIR_PROC FS_CHROOT_TEST_DIR \"/proc\"\r\n#define FS_FALLOCATE_TEST_FILE FS_TEST_DIR_PARENT \"/fallocate_test_file\"\r\n#define FS_RMDIR_TEST_DIR FS_TEST_DIR_PARENT \"/rmdir_test\"\r\n#define FS_POWERSHELL \"/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe\"\r\n#define FS_POWERSHELL_MOUNT_COMMAND \\\r\n    FS_POWERSHELL \" -Command \\\"& { (Mount-DiskImage \" FS_DRVFS_CD_TEST_ISO_NT \" -PassThru | Get-Volume).DriveLetter }\\\"\"\r\n#define FS_POWERSHELL_UNMOUNT_COMMAND FS_POWERSHELL \" -Command \\\"& { Dismount-DiskImage \" FS_DRVFS_CD_TEST_ISO_NT \" }\\\"\"\r\n#define FS_GENISOIMAGE_COMMAND \"genisoimage -JR -o \" FS_DRVFS_CD_TEST_ISO \" \" FS_DRVFS_CD_CONTENTS_DIR\r\n#define FS_LINKAT_TEST_DIR FS_TEST_DIR_PARENT \"/linkat_test\"\r\n#define FS_LINKAT_TEST_DIR2 FS_TEST_DIR_PARENT \"/linkat_test2\"\r\n#define FS_FCHOWNAT_TEST_DIR FS_TEST_DIR_PARENT \"/fchownat_test\"\r\n#define FS_DELETELOOP_TEST_DIR FS_TEST_DIR_PARENT \"/deleteloop\"\r\n#define FS_FSYNC_TEST_DIR FS_TEST_DIR_PARENT \"/fsync_test\"\r\n\r\nLXT_VARIATION_HANDLER FsCommonTestCreateAndRename;\r\nLXT_VARIATION_HANDLER FsCommonTestDeleteCurrentWorkingDirectory;\r\nLXT_VARIATION_HANDLER FsCommonTestDeleteLoop;\r\nLXT_VARIATION_HANDLER FsCommonTestDeleteOpenFile;\r\nLXT_VARIATION_HANDLER FsCommonTestChroot;\r\nLXT_VARIATION_HANDLER FsCommonTestClex;\r\nLXT_VARIATION_HANDLER FsCommonTestCreateSymlinkTarget;\r\nLXT_VARIATION_HANDLER FsCommonTestDeviceId;\r\nLXT_VARIATION_HANDLER FsCommonTestFchownAt;\r\nLXT_VARIATION_HANDLER FsCommonTestReadlinkat;\r\nLXT_VARIATION_HANDLER FsCommonTestRemoveSelfOrParent;\r\nLXT_VARIATION_HANDLER FsCommonTestRenameAt;\r\nLXT_VARIATION_HANDLER FsCommonTestRenameDir;\r\nLXT_VARIATION_HANDLER FsCommonTestSetEof;\r\nLXT_VARIATION_HANDLER FsCommonTestTrailingSlash;\r\nLXT_VARIATION_HANDLER FsCommonTestLinkAt;\r\nLXT_VARIATION_HANDLER FsCommonTestMkdir;\r\nLXT_VARIATION_HANDLER FsCommonTestMkDirAt;\r\nLXT_VARIATION_HANDLER FsCommonTestMknod;\r\nLXT_VARIATION_HANDLER FsCommonTestMknodSecurity;\r\nLXT_VARIATION_HANDLER FsCommonTestOpen;\r\nLXT_VARIATION_HANDLER FsCommonTestOpenAt;\r\nLXT_VARIATION_HANDLER FsCommonTestOpenCreateSymlink;\r\nLXT_VARIATION_HANDLER FsCommonTestOpenCreateSymlinkDir;\r\n#ifdef __NR_getdents\r\nLXT_VARIATION_HANDLER FsCommonTestGetDents;\r\n#endif\r\nLXT_VARIATION_HANDLER FsCommonTestGetDents64Alignment;\r\n#ifdef __NR_getdents\r\nLXT_VARIATION_HANDLER FsCommonTestGetDentsAlignment;\r\n#endif\r\nLXT_VARIATION_HANDLER FsCommonTestGetDentsTypes;\r\nLXT_VARIATION_HANDLER FsCommonTestChdir;\r\nLXT_VARIATION_HANDLER FsCommonTestUnlinkAt;\r\nLXT_VARIATION_HANDLER FsCommonTestFstatAt64;\r\nLXT_VARIATION_HANDLER FsCommonTestFchdir;\r\nLXT_VARIATION_HANDLER FsCommonTestNoatimeFlag;\r\nLXT_VARIATION_HANDLER FsCommonTestWritev;\r\nLXT_VARIATION_HANDLER FsCommonTestFallocate;\r\nLXT_VARIATION_HANDLER FsCommonTestDirSeek;\r\nLXT_VARIATION_HANDLER FsCommonTestFsync;\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Test mkdir/rmdir\", FsCommonTestMkdir},\r\n    {\"Test SetEof\", FsCommonTestSetEof},\r\n    {\"Test Create, Rename and unlink\", FsCommonTestCreateAndRename},\r\n    {\"Test Open\", FsCommonTestOpen},\r\n    {\"Test OpenAt\", FsCommonTestOpenAt},\r\n    {\"Test Open symlink with O_CREAT\", FsCommonTestOpenCreateSymlink},\r\n    {\"Test creating a symlink to a directory\", FsCommonTestOpenCreateSymlinkDir},\r\n    {\"Test Chdir\", FsCommonTestChdir},\r\n#ifdef __NR_getdents\r\n    {\"Test GetDents\", FsCommonTestGetDents},\r\n#endif\r\n    {\"Test UnlinkAt\", FsCommonTestUnlinkAt},\r\n    {\"Test fstatat64\", FsCommonTestFstatAt64},\r\n    {\"Test Fchdir\", FsCommonTestFchdir},\r\n    {\"Test mkdirat\", FsCommonTestMkDirAt},\r\n    {\"Test O_NOATIME flag\", FsCommonTestNoatimeFlag},\r\n    {\"Test deleting an open file\", FsCommonTestDeleteOpenFile},\r\n    {\"Test deleting the working directory\", FsCommonTestDeleteCurrentWorkingDirectory},\r\n    {\"Test rename directory\", FsCommonTestRenameDir},\r\n    {\"Test writev\", FsCommonTestWritev},\r\n    {\"Test readlinkat\", FsCommonTestReadlinkat},\r\n    {\"Test renameat\", FsCommonTestRenameAt},\r\n    {\"Test DeviceId\", FsCommonTestDeviceId},\r\n    {\"Test FIOCLEX/FIONCLEX\", FsCommonTestClex},\r\n    {\"Test create symlink target\", FsCommonTestCreateSymlinkTarget},\r\n    {\"Test trailing slash\", FsCommonTestTrailingSlash},\r\n    {\"Test mknod\", FsCommonTestMknod},\r\n    {\"Test mknod CAP_MKNOD\", FsCommonTestMknodSecurity},\r\n    {\"Test chroot\", FsCommonTestChroot},\r\n    {\"Test fallocate\", FsCommonTestFallocate},\r\n    {\"Test remove self or parent\", FsCommonTestRemoveSelfOrParent},\r\n    {\"Test linkat\", FsCommonTestLinkAt},\r\n    {\"Test fchownat\", FsCommonTestFchownAt},\r\n    {\"Test delete loop\", FsCommonTestDeleteLoop},\r\n#ifdef __NR_getdents\r\n    {\"Test getdents alignment\", FsCommonTestGetDentsAlignment},\r\n#endif\r\n    {\"Test getdents64 alignment\", FsCommonTestGetDents64Alignment},\r\n    {\"Test lseek on directory\", FsCommonTestDirSeek},\r\n    {\"Test getdents file types\", FsCommonTestGetDentsTypes},\r\n    {\"Test fsync\", FsCommonTestFsync}};\r\n\r\nint FsCommonTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Index;\r\n    const char* Name;\r\n    int Result;\r\n    bool UseDrvFs;\r\n\r\n    //\r\n    // Check if drvfs should be used.\r\n    //\r\n\r\n    Name = LXT_NAME_LXFS;\r\n    UseDrvFs = false;\r\n    for (Index = 1; Index < Argc; Index += 1)\r\n    {\r\n        if (strcmp(Argv[Index], \"drvfs\") == 0)\r\n        {\r\n            UseDrvFs = true;\r\n            Name = LXT_NAME_DRVFS;\r\n            break;\r\n        }\r\n    }\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, Name));\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n    LxtCheckResult(LxtFsTestSetup(&Args, FS_TEST_DIR_PARENT, \"/fstest\", UseDrvFs));\r\n\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtFsTestCleanup(FS_TEST_DIR_PARENT, \"/fstest\", UseDrvFs);\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY();\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n\r\nstruct dirent64\r\n{\r\n    __u64 d_ino;\r\n    __s64 d_off;\r\n    unsigned short d_reclen;\r\n    unsigned char d_type;\r\n    char d_name[1];\r\n};\r\n\r\n#endif\r\n\r\nstruct GetDentsPaths\r\n{\r\n    const char* Path;\r\n    int MinElements;\r\n    int MaxElements;\r\n};\r\n\r\n#define LXT_GET_DENTS_FOLDER FS_TEST_DIR_PARENT \"/getdents\"\r\n\r\nint FsCommonTestChroot(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests some chroot effects that are not covered by LTP.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    int Fd2;\r\n    char Path[PATH_MAX];\r\n    int Result;\r\n\r\n    Fd = -1;\r\n    Fd2 = -2;\r\n\r\n    //\r\n    // This test is not really relevant to VM mode, and currently doesn't pass\r\n    // because VM mode already runs in a chroot environment, changing some of\r\n    // paths.\r\n    //\r\n    // TODO_LX: Re-enable this when init switches to using pivot_root.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9)\r\n    {\r\n        LxtLogInfo(\"Skipping chroot test in VM mode.\");\r\n        Result = LXT_RESULT_SUCCESS;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Set up the directories needed for the chroot environment.\r\n    //\r\n\r\n    LxtCheckResult(LxtSignalBlock(SIGUSR1));\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_CHROOT_TEST_DIR, 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_CHROOT_TEST_DIR_CHILD, 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_CHROOT_TEST_DIR_PROC, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(\"/proc\", FS_CHROOT_TEST_DIR_PROC, NULL, MS_BIND, NULL));\r\n\r\n    //\r\n    // First test with the cwd inside the new root when chroot is called.\r\n    //\r\n    // N.B. The parent cwd is outside the new root.\r\n    //\r\n\r\n    LxtLogInfo(\"Cwd inside new root...\");\r\n    LxtCheckErrnoZeroSuccess(chdir(FS_TEST_DIR_PARENT));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        //\r\n        // Change current directory and open a file both in and outside the\r\n        // new root.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(chdir(FS_CHROOT_TEST_DIR_CHILD));\r\n        LxtCheckErrno(Fd = open(\"/etc/hosts\", O_RDONLY));\r\n        LxtCheckErrno(Fd2 = open(FS_CHROOT_TEST_DIR_CHILD, O_DIRECTORY));\r\n        LxtCheckErrnoZeroSuccess(access(\"../../../../etc\", F_OK));\r\n        LxtCheckErrnoZeroSuccess(faccessat(Fd2, \"../../../../etc\", F_OK, 0));\r\n\r\n        //\r\n        // Change the root directory.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(chroot(FS_CHROOT_TEST_DIR));\r\n\r\n        //\r\n        // The working directory path and fd inside the new root should\r\n        // reflect the new root.\r\n        //\r\n        // N.B. The working directory is not changed by chroot; because it is\r\n        //      inside the reported path is changed automatically.\r\n        //\r\n\r\n        LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));\r\n        LxtCheckStringEqual(Path, FS_CHROOT_TEST_DIR_CHILD_FROM_ROOT);\r\n        LxtCheckResult(LxtCheckLinkTarget(\"/proc/self/cwd\", FS_CHROOT_TEST_DIR_CHILD_FROM_ROOT));\r\n\r\n        LxtCheckResult(LxtCheckFdPath(Fd2, FS_CHROOT_TEST_DIR_CHILD_FROM_ROOT));\r\n\r\n        //\r\n        // The file descriptor outside the root still reports its old path.\r\n        //\r\n\r\n        LxtCheckResult(LxtCheckFdPath(Fd, \"/etc/hosts\"));\r\n\r\n        //\r\n        // Check that the root can't be escaped.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(access(\"/etc\", F_OK), ENOENT);\r\n        LxtCheckErrnoFailure(access(\"../../../../etc\", F_OK), ENOENT);\r\n        LxtCheckErrnoFailure(faccessat(Fd2, \"../../../../etc\", F_OK, 0), ENOENT);\r\n\r\n        //\r\n        // The root symlink should say /, and refer to the new root.\r\n        //\r\n\r\n        LxtCheckResult(LxtCheckLinkTarget(\"/proc/self/root\", \"/\"));\r\n        LxtCheckErrnoFailure(access(\"/proc/self/root/etc\", F_OK), ENOENT);\r\n\r\n        //\r\n        // The parent's root symlink also says /, even though it's not the same\r\n        // path. It can be used to escape the chroot jail.\r\n        //\r\n\r\n        sprintf(Path, \"/proc/%d/root\", getppid());\r\n        LxtCheckResult(LxtCheckLinkTarget(Path, \"/\"));\r\n        sprintf(Path, \"/proc/%d/root/etc\", getppid());\r\n        LxtCheckErrnoZeroSuccess(access(Path, F_OK));\r\n\r\n        //\r\n        // The parent's cwd is not inside the new root, so the link returns\r\n        // its actual path. It can also be used to escape the chroot jail.\r\n        //\r\n\r\n        sprintf(Path, \"/proc/%d/cwd\", getppid());\r\n        LxtCheckResult(LxtCheckLinkTarget(Path, FS_TEST_DIR_PARENT));\r\n        sprintf(Path, \"/proc/%d/cwd/chroot_test\", getppid());\r\n        LxtCheckErrnoZeroSuccess(access(Path, F_OK));\r\n\r\n        //\r\n        // Signal the parent.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(kill(getppid(), SIGUSR1));\r\n        LxtCheckResult(LxtSignalWaitBlocked(SIGUSR1, getppid(), 2));\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtSignalWaitBlocked(SIGUSR1, ChildPid, 2));\r\n\r\n    //\r\n    // Check the root symlink for the child returns the new root path.\r\n    //\r\n\r\n    sprintf(Path, \"/proc/%d/root\", ChildPid);\r\n    LxtCheckResult(LxtCheckLinkTarget(Path, FS_CHROOT_TEST_DIR));\r\n    sprintf(Path, \"/proc/%d/cwd\", ChildPid);\r\n    LxtCheckResult(LxtCheckLinkTarget(Path, FS_CHROOT_TEST_DIR_CHILD));\r\n    LxtCheckErrnoZeroSuccess(kill(ChildPid, SIGUSR1));\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // Now test with cwd outside the new root, in which case the path reported\r\n    // by getcwd should indicate unreachable, but /proc/self/cwd should give\r\n    // the normal path.\r\n    //\r\n    // N.B. The parent cwd is inside the new root for this test.\r\n    //\r\n\r\n    LxtLogInfo(\"Cwd outside new root...\");\r\n    LxtCheckErrnoZeroSuccess(chdir(FS_CHROOT_TEST_DIR_CHILD));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(chdir(FS_TEST_DIR_PARENT));\r\n        LxtCheckErrnoZeroSuccess(chroot(FS_CHROOT_TEST_DIR));\r\n\r\n        //\r\n        // Glibc getcwd in newer versions returns NULL if the path doesn't\r\n        // start with a /, which would be the case here, so call the syscall\r\n        // directly.\r\n        //\r\n\r\n        LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));\r\n        LxtCheckStringEqual(Path, \"(unreachable)\" FS_TEST_DIR_PARENT);\r\n        LxtCheckResult(LxtCheckLinkTarget(\"/proc/self/cwd\", FS_TEST_DIR_PARENT));\r\n\r\n        //\r\n        // The parent's cwd is reported using the new root.\r\n        //\r\n\r\n        sprintf(Path, \"/proc/%d/cwd\", getppid());\r\n        LxtCheckResult(LxtCheckLinkTarget(Path, FS_CHROOT_TEST_DIR_CHILD_FROM_ROOT));\r\n\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // Cwd matches the new root.\r\n    //\r\n\r\n    LxtLogInfo(\"Cwd exactly new root...\");\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(chdir(FS_CHROOT_TEST_DIR));\r\n        LxtCheckErrnoZeroSuccess(chroot(FS_CHROOT_TEST_DIR));\r\n        LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));\r\n        LxtCheckStringEqual(Path, \"/\");\r\n        LxtCheckResult(LxtCheckLinkTarget(\"/proc/self/cwd\", \"/\"));\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // Cwd is the old root.\r\n    //\r\n\r\n    LxtLogInfo(\"Cwd exactly old root...\");\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(chdir(\"/\"));\r\n        LxtCheckErrnoZeroSuccess(chroot(FS_CHROOT_TEST_DIR));\r\n\r\n        //\r\n        // Glibc getcwd in newer versions returns NULL if the path doesn't\r\n        // start with a /, which would be the case here, so call the syscall\r\n        // directly.\r\n        //\r\n\r\n        LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));\r\n        LxtCheckStringEqual(Path, \"(unreachable)/\");\r\n        memset(Path, 0, sizeof(Path));\r\n        LxtCheckErrno(readlink(\"/proc/self/cwd\", Path, sizeof(Path)));\r\n        LxtCheckStringEqual(Path, \"/\");\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (Fd2 >= 0)\r\n    {\r\n        close(Fd2);\r\n    }\r\n\r\n    umount(FS_CHROOT_TEST_DIR_PROC);\r\n    rmdir(FS_CHROOT_TEST_DIR_PROC);\r\n    rmdir(FS_CHROOT_TEST_DIR_CHILD);\r\n    rmdir(FS_CHROOT_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestClex(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the FIONCLEX / FIONCLEX file descriptor ioctls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Fd = -1;\r\n    int Result;\r\n\r\n    //\r\n    // Don't set close on exec on file descriptors in the main test process;\r\n    // this would cause later tests to fail.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First set the ioctls on stdin.\r\n        //\r\n\r\n        LxtCheckErrno(ioctl(0, FIONCLEX, NULL));\r\n        LxtCheckErrno(ioctl(0, FIOCLEX, NULL));\r\n\r\n        //\r\n        // Create a directory and open a file descriptor with O_PATH.\r\n        //\r\n\r\n        LxtCheckErrno(mkdir(FS_CLEX_TEST_DIR_NAME, 0777));\r\n        LxtCheckErrno(Fd = open(FS_CLEX_TEST_DIR_NAME, O_PATH | O_DIRECTORY));\r\n\r\n        //\r\n        // Setting the CLOEXEC flag with fcntl should work even though the file\r\n        // was opened with O_PATH.\r\n        //\r\n\r\n        LxtCheckErrno(fcntl(Fd, F_SETFD, FD_CLOEXEC));\r\n\r\n        //\r\n        // Setting FIONCLEX / FIOCLEX with the ioctl syscall should fail.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(ioctl(Fd, FIONCLEX, NULL), EBADF);\r\n        LxtCheckErrnoFailure(ioctl(Fd, FIOCLEX, NULL), EBADF);\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    rmdir(FS_CLEX_TEST_DIR_NAME);\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\ntypedef struct _LXSS_BYTE_ALIGNED_DIRENTS\r\n{\r\n    char Padding;\r\n    char Buffer[sizeof(struct dirent64)];\r\n} LXSS_BYTE_ALIGNED_DIRENTS;\r\n\r\n#ifdef __NR_getdents\r\nint FsCommonTestGetDents(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXSS_BYTE_ALIGNED_DIRENTS ByteAlignedDirents;\r\n    char* Buffer;\r\n    struct dirent64* BufferEntries[2000];\r\n    int BufferEntriesCount;\r\n    int BufferIndex;\r\n    int BufferLoopIndex;\r\n    int BufferSize = 2 * 1024 * 1024;\r\n    int BytePos;\r\n    int BytesRead;\r\n    int DirFd;\r\n    struct dirent64* Entry;\r\n    int ExpectedLength;\r\n    bool FoundDot;\r\n    bool FoundDotDot;\r\n    int NameLength;\r\n    int Pass;\r\n    size_t PathIndex;\r\n\r\n    //\r\n    // TODO: use cgroups path once it is mounted for 64 bit lxss.\r\n    //\r\n\r\n    struct GetDentsPaths Paths[] = {\r\n        {\"/proc/self/\", 18, 64},\r\n        {\"/proc/\", 10, 500},\r\n        {\"/dev/\", 14, 1000},\r\n        //{\"/acct/\", 7, 64},\r\n        {\"/\", 4, 64},\r\n        {LXT_GET_DENTS_FOLDER, 2, 2}};\r\n\r\n    Buffer = NULL;\r\n    DirFd = -1;\r\n    int Result;\r\n    char SingleEntry[100];\r\n    int SingleEntrySize;\r\n    rmdir(LXT_GET_DENTS_FOLDER);\r\n\r\n    //\r\n    // Check the expected getdents results for each directory;\r\n    //\r\n\r\n    Buffer = malloc(BufferSize);\r\n    if (Buffer == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"malloc\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(mkdir(LXT_GET_DENTS_FOLDER, 0777));\r\n    for (PathIndex = 0; PathIndex < LXT_COUNT_OF(Paths); PathIndex += 1)\r\n    {\r\n\r\n        //\r\n        // First read all of the entries in a single call.\r\n        //\r\n\r\n        memset(Buffer, 1, BufferSize);\r\n        LxtLogInfo(\"Opening %s...\", Paths[PathIndex].Path);\r\n        LxtCheckErrno(DirFd = open(Paths[PathIndex].Path, O_RDONLY | O_DIRECTORY));\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(DirFd, Buffer, BufferSize));\r\n        if (BytesRead == 0)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"BytesRead == 0\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtCheckErrno(LxtGetdents(DirFd, SingleEntry, sizeof(SingleEntry)));\r\n        if (Result != 0)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"BytesRead Result ! 0\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        FoundDot = false;\r\n        FoundDotDot = false;\r\n        BufferIndex = 0;\r\n        for (BytePos = 0; BytePos < BytesRead;)\r\n        {\r\n            Entry = (struct dirent64*)(Buffer + BytePos);\r\n            BufferEntries[BufferIndex] = Entry;\r\n            NameLength = strlen(Entry->d_name);\r\n\r\n            if (strcmp(Entry->d_name, \".\") == 0)\r\n            {\r\n                FoundDot = true;\r\n            }\r\n\r\n            if (strcmp(Entry->d_name, \"..\") == 0)\r\n            {\r\n                FoundDotDot = true;\r\n            }\r\n\r\n            BufferIndex += 1;\r\n            BytePos += Entry->d_reclen;\r\n        }\r\n\r\n        if ((FoundDot == false) || (FoundDotDot == false))\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Missing entries for . or .. or both.\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        BufferEntriesCount = BufferIndex;\r\n        if (BufferEntriesCount < Paths[PathIndex].MinElements)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpected number of elements %d < %d\", BufferEntriesCount, Paths[PathIndex].MinElements);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (BufferEntriesCount > Paths[PathIndex].MaxElements)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpected number of elements %d > %d\", BufferEntriesCount, Paths[PathIndex].MaxElements);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtClose(DirFd);\r\n        DirFd = -1;\r\n\r\n        //\r\n        // Then read each entry in a single call and make sure it matches the\r\n        // previous data returned.\r\n        //\r\n        // In pass 0, just read sequentially. In pass 1, seek to each offset\r\n        // in reverse order to ensure that seek works.\r\n        //\r\n\r\n        for (Pass = 0; Pass < 2; Pass++)\r\n        {\r\n            LxtLogInfo(\"Reopening %s...\", Paths[PathIndex].Path);\r\n            LxtCheckErrno(DirFd = open(Paths[PathIndex].Path, O_RDONLY | O_DIRECTORY));\r\n            for (BufferLoopIndex = 0; BufferLoopIndex < BufferEntriesCount; BufferLoopIndex += 1)\r\n            {\r\n                if (Pass == 0)\r\n                {\r\n                    BufferIndex = BufferLoopIndex;\r\n                }\r\n                else\r\n                {\r\n                    BufferIndex = BufferEntriesCount - BufferLoopIndex - 1;\r\n\r\n                    //\r\n                    // Plan 9 client in Linux has a bug where seek does not\r\n                    // take effect if not all entries were consumed. Reopen\r\n                    // the FD to allow seek to work.\r\n                    //\r\n                    // TODO_LX: Remove this one the plan 9 bug is fixed.\r\n                    //\r\n\r\n                    if (g_LxtFsInfo.FsType == LxtFsTypePlan9)\r\n                    {\r\n                        LxtCheckClose(DirFd);\r\n                        LxtCheckErrno(DirFd = open(Paths[PathIndex].Path, O_RDONLY | O_DIRECTORY));\r\n                    }\r\n\r\n                    LxtCheckErrno(lseek(DirFd, BufferIndex == 0 ? 0 : BufferEntries[BufferIndex - 1]->d_off, SEEK_SET));\r\n                }\r\n\r\n                SingleEntrySize = BufferEntries[BufferIndex]->d_reclen;\r\n                BytesRead = LxtGetdents64(DirFd, SingleEntry, SingleEntrySize);\r\n                if (BytesRead < 0)\r\n                {\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    LxtLogError(\"Failed on %s with %s\", BufferEntries[BufferIndex]->d_name, strerror(errno));\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                if (BytesRead == 0)\r\n                {\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    LxtLogError(\"BytesRead == 0\");\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                Entry = (struct dirent64*)SingleEntry;\r\n                if (Entry->d_reclen != BufferEntries[BufferIndex]->d_reclen)\r\n                {\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    LxtLogInfo(\"Unexpected d_reclen %d != %d\", Entry->d_reclen, BufferEntries[BufferIndex]->d_reclen);\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                if (strcmp(Entry->d_name, BufferEntries[BufferIndex]->d_name) != 0)\r\n                {\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    LxtLogError(\"Unexpected name %s != %s\", Entry->d_name, BufferEntries[BufferIndex]->d_name);\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                if (Entry->d_type != BufferEntries[BufferIndex]->d_type)\r\n                {\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    LxtLogInfo(\"Unexpected d_type %d != %d\", Entry->d_type, BufferEntries[BufferIndex]->d_type);\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                if (Entry->d_reclen != BufferEntries[BufferIndex]->d_reclen)\r\n                {\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    LxtLogInfo(\"Unexpected d_reclen %d != %d\", Entry->d_reclen, BufferEntries[BufferIndex]->d_reclen);\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                if (Entry->d_off != BufferEntries[BufferIndex]->d_off)\r\n                {\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    LxtLogInfo(\"Unexpected d_off %d != %d\", Entry->d_off, BufferEntries[BufferIndex]->d_off);\r\n                    goto ErrorExit;\r\n                }\r\n            }\r\n\r\n            if (Pass == 0)\r\n            {\r\n                LxtCheckErrno(LxtGetdents64(DirFd, SingleEntry, sizeof(SingleEntry)));\r\n                if (Result != 0)\r\n                {\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    LxtLogError(\"BytesRead Result ! 0\");\r\n                    goto ErrorExit;\r\n                }\r\n            }\r\n\r\n            LxtClose(DirFd);\r\n            DirFd = -1;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Test alignment of getdents syscall.\r\n    //\r\n\r\n    LxtCheckErrno(DirFd = open(\".\", O_RDONLY | O_DIRECTORY));\r\n    LxtLogInfo(\"Calling getdents with input buffer %p\", &ByteAlignedDirents.Buffer);\r\n    LxtCheckErrno(LxtGetdents64(DirFd, &ByteAlignedDirents.Buffer, sizeof(ByteAlignedDirents.Buffer)));\r\n\r\n    LxtLogInfo(\"getdents test successful!\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Buffer != NULL)\r\n    {\r\n        free(Buffer);\r\n    }\r\n\r\n    if (DirFd != -1)\r\n    {\r\n        LxtClose(DirFd);\r\n    }\r\n\r\n    rmdir(LXT_GET_DENTS_FOLDER);\r\n    return Result;\r\n}\r\n#endif\r\n\r\nint FsCommonTestGetDents64Alignment(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether directory entries are correctly aligned and\r\n    padded.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtFsGetDentsAlignmentCommon(LXT_GET_DENTS_FOLDER, FS_TEST_GETDENTS64));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\n#ifdef __NR_getdents\r\nint FsCommonTestGetDentsAlignment(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether directory entries are correctly aligned and\r\n    padded.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtFsGetDentsAlignmentCommon(LXT_GET_DENTS_FOLDER, 0));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n#endif\r\n\r\nint FsCommonTestGetDentsTypes(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether all files are reported as the correct types by\r\n    getdents.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_CHILD_INFO Children[] = {\r\n        {\"regchild\", DT_REG},\r\n        {\"dirchild\", DT_DIR},\r\n        {\"linkchild1\", DT_LNK},\r\n        {\"linkchild2\", DT_LNK},\r\n        {\"linkchild3\", DT_LNK},\r\n        {\"dirchild\", DT_DIR},\r\n        {\"fifochild\", DT_FIFO},\r\n        {\"sockchild\", DT_SOCK},\r\n        {\"chrchild\", DT_CHR},\r\n        {\"blkchild\", DT_BLK}};\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(LXT_GET_DENTS_FOLDER, 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir(LXT_GET_DENTS_FOLDER \"/dirchild\", 0777));\r\n    LxtCheckErrnoZeroSuccess(mknod(LXT_GET_DENTS_FOLDER \"/regchild\", S_IFREG | 0666, 0));\r\n    LxtCheckErrnoZeroSuccess(mknod(LXT_GET_DENTS_FOLDER \"/fifochild\", S_IFIFO | 0666, 0));\r\n    LxtCheckErrnoZeroSuccess(mknod(LXT_GET_DENTS_FOLDER \"/sockchild\", S_IFSOCK | 0666, 0));\r\n    LxtCheckErrnoZeroSuccess(mknod(LXT_GET_DENTS_FOLDER \"/chrchild\", S_IFCHR | 0666, makedev(1, 3)));\r\n    LxtCheckErrnoZeroSuccess(mknod(LXT_GET_DENTS_FOLDER \"/blkchild\", S_IFBLK | 0666, makedev(1, 1)));\r\n    LxtCheckErrnoZeroSuccess(symlink(\"regchild\", LXT_GET_DENTS_FOLDER \"/linkchild1\"));\r\n\r\n    //\r\n    // Directory symlinks and absolute symlinks may have different representations on DrvFs, so test them too.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(symlink(\"dirchild\", LXT_GET_DENTS_FOLDER \"/linkchild2\"));\r\n    LxtCheckErrnoZeroSuccess(symlink(\"/proc\", LXT_GET_DENTS_FOLDER \"/linkchild3\"));\r\n    LxtCheckResult(LxtCheckDirectoryContentsEx(LXT_GET_DENTS_FOLDER, Children, LXT_COUNT_OF(Children), 0));\r\n\r\nErrorExit:\r\n    unlink(LXT_GET_DENTS_FOLDER \"/linkchild1\");\r\n    unlink(LXT_GET_DENTS_FOLDER \"/linkchild2\");\r\n    unlink(LXT_GET_DENTS_FOLDER \"/linkchild3\");\r\n    unlink(LXT_GET_DENTS_FOLDER \"/fifochild\");\r\n    unlink(LXT_GET_DENTS_FOLDER \"/sockchild\");\r\n    unlink(LXT_GET_DENTS_FOLDER \"/chrchild\");\r\n    unlink(LXT_GET_DENTS_FOLDER \"/blkchild\");\r\n    unlink(LXT_GET_DENTS_FOLDER \"/regchild\");\r\n    rmdir(LXT_GET_DENTS_FOLDER \"/dirchild\");\r\n    rmdir(LXT_GET_DENTS_FOLDER);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestLinkAt(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the linkat system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    struct stat FileStat;\r\n    int Result;\r\n    int SourceDirFd;\r\n    struct stat Stat;\r\n    int SymlinkFd;\r\n    struct stat SymlinkStat;\r\n    int TargetDirFd;\r\n\r\n    SourceDirFd = -1;\r\n    TargetDirFd = -1;\r\n    Fd = -1;\r\n    SymlinkFd = -1;\r\n\r\n    //\r\n    // Set up the test files.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_LINKAT_TEST_DIR, 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_LINKAT_TEST_DIR2, 0777));\r\n    LxtCheckErrno(Fd = creat(FS_LINKAT_TEST_DIR \"/testfile\", 0666));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_LINKAT_TEST_DIR \"/testfile\", FS_LINKAT_TEST_DIR \"/testsymlink\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_LINKAT_TEST_DIR, FS_LINKAT_TEST_DIR \"/testdirsymlink\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR \"/testfile\", &FileStat));\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR \"/testsymlink\", &SymlinkStat));\r\n\r\n    //\r\n    // Create a regular hard link.\r\n    //\r\n\r\n    LxtCheckErrno(SourceDirFd = open(FS_LINKAT_TEST_DIR, O_RDONLY | O_DIRECTORY));\r\n    LxtCheckErrno(TargetDirFd = open(FS_LINKAT_TEST_DIR2, O_RDONLY | O_DIRECTORY));\r\n    LxtCheckErrnoZeroSuccess(linkat(SourceDirFd, \"testfile\", TargetDirFd, \"testlink\", 0));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR2 \"/testlink\", &Stat));\r\n    LxtCheckEqual(FileStat.st_ino, Stat.st_ino, \"%lld\");\r\n    LxtCheckTrue(S_ISREG(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n\r\n    //\r\n    // Using AT_FDCWD.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(FS_LINKAT_TEST_DIR));\r\n    LxtCheckErrnoZeroSuccess(linkat(AT_FDCWD, \"testfile\", TargetDirFd, \"testlink\", 0));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR2 \"/testlink\", &Stat));\r\n    LxtCheckEqual(FileStat.st_ino, Stat.st_ino, \"%lld\");\r\n    LxtCheckTrue(S_ISREG(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n    LxtCheckErrnoZeroSuccess(chdir(FS_LINKAT_TEST_DIR2));\r\n    LxtCheckErrnoZeroSuccess(linkat(SourceDirFd, \"testfile\", AT_FDCWD, \"testlink\", 0));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR2 \"/testlink\", &Stat));\r\n    LxtCheckEqual(FileStat.st_ino, Stat.st_ino, \"%lld\");\r\n    LxtCheckTrue(S_ISREG(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n\r\n    //\r\n    // Symlinks are not followed by default.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(linkat(SourceDirFd, \"testsymlink\", TargetDirFd, \"testlink\", 0));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR2 \"/testlink\", &Stat));\r\n    LxtCheckEqual(SymlinkStat.st_ino, Stat.st_ino, \"%lld\");\r\n    LxtCheckTrue(S_ISLNK(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n\r\n    //\r\n    // Use AT_SYMLINK_FOLLOW to follow the link.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(linkat(SourceDirFd, \"testsymlink\", TargetDirFd, \"testlink\", AT_SYMLINK_FOLLOW));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR2 \"/testlink\", &Stat));\r\n    LxtCheckEqual(FileStat.st_ino, Stat.st_ino, \"%lld\");\r\n    LxtCheckTrue(S_ISREG(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n\r\n    //\r\n    // Fd must a directory, not a symlink to a directory.\r\n    //\r\n\r\n    LxtCheckErrno(SymlinkFd = open(FS_LINKAT_TEST_DIR \"/testdirsymlink\", O_NOFOLLOW | O_PATH));\r\n\r\n    LxtCheckErrnoFailure(linkat(SymlinkFd, \"testfile\", TargetDirFd, \"testlink\", 0), ENOTDIR);\r\n\r\n    LxtCheckErrnoFailure(linkat(SymlinkFd, \"testfile\", TargetDirFd, \"testlink\", AT_SYMLINK_FOLLOW), ENOTDIR);\r\n\r\n    LxtCheckErrnoFailure(linkat(SourceDirFd, \"testfile\", SymlinkFd, \"testlink\", AT_SYMLINK_FOLLOW), ENOTDIR);\r\n\r\n    LxtCheckClose(SymlinkFd);\r\n\r\n    //\r\n    // AT_EMPTY_PATH creates a link to the specified item.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_LINKAT_TEST_DIR \"/testfile\", O_RDONLY));\r\n    LxtCheckErrnoZeroSuccess(linkat(Fd, \"\", TargetDirFd, \"testlink\", AT_EMPTY_PATH));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR2 \"/testlink\", &Stat));\r\n    LxtCheckEqual(FileStat.st_ino, Stat.st_ino, \"%lld\");\r\n    LxtCheckTrue(S_ISREG(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n    LxtCheckClose(Fd);\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)\r\n    {\r\n        LxtLogInfo(\"TODO: debug this test on virtiofs\");\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // If the fd is a symlink, it's not followed regardless of flags.\r\n    //\r\n\r\n    LxtCheckErrno(SymlinkFd = open(FS_LINKAT_TEST_DIR \"/testsymlink\", O_NOFOLLOW | O_PATH));\r\n\r\n    LxtCheckErrnoZeroSuccess(linkat(SymlinkFd, \"\", TargetDirFd, \"testlink\", AT_EMPTY_PATH));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR2 \"/testlink\", &Stat));\r\n    LxtCheckEqual(SymlinkStat.st_ino, Stat.st_ino, \"%lld\");\r\n    LxtCheckTrue(S_ISLNK(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n    LxtCheckErrnoZeroSuccess(linkat(SymlinkFd, \"\", TargetDirFd, \"testlink\", AT_EMPTY_PATH | AT_SYMLINK_FOLLOW));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_LINKAT_TEST_DIR2 \"/testlink\", &Stat));\r\n    LxtCheckEqual(SymlinkStat.st_ino, Stat.st_ino, \"%lld\");\r\n    LxtCheckTrue(S_ISLNK(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n\r\n    //\r\n    // Directory FD should not work.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(linkat(SourceDirFd, \"\", TargetDirFd, \"testlink\", AT_EMPTY_PATH), EPERM);\r\n\r\n    //\r\n    // AT_EMPTY_PATH only affects the source FD.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(linkat(SourceDirFd, \"testfile\", Fd, \"\", AT_EMPTY_PATH), ENOENT);\r\n\r\n    //\r\n    // Create a link when the original link is removed.\r\n    //\r\n    // N.B. In WSL 1, the inode keeps a handle to the original link even after\r\n    //      it has been removed, which is why this test is interesting.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(linkat(SourceDirFd, \"testfile\", TargetDirFd, \"testlink\", 0));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR \"/testfile\"));\r\n    LxtCheckErrnoZeroSuccess(linkat(TargetDirFd, \"testlink\", TargetDirFd, \"testlink2\", 0));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink2\"));\r\n\r\n    //\r\n    // Same using the original fd.\r\n    //\r\n    // N.B. This test does not pass on WSL 2 DrvFs, because the 9p server stores\r\n    //      a path in the fid and that path is no longer valid after the unlink.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypePlan9)\r\n    {\r\n        LxtCheckErrno(Fd = creat(FS_LINKAT_TEST_DIR \"/testfile\", 0666));\r\n        LxtCheckErrnoZeroSuccess(linkat(Fd, \"\", TargetDirFd, \"testlink\", AT_EMPTY_PATH));\r\n        LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR \"/testfile\"));\r\n        LxtCheckErrnoZeroSuccess(linkat(Fd, \"\", TargetDirFd, \"testlink2\", AT_EMPTY_PATH));\r\n        LxtCheckClose(Fd);\r\n        LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink\"));\r\n        LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR2 \"/testlink2\"));\r\n    }\r\n\r\n    //\r\n    // You cannot resurrect a file with link count 0.\r\n    //\r\n    // N.B. On real Linux, the error code this produces is ENOENT, but since\r\n    //      NTFS returns STATUS_ACCESS_DENIED for this, WSL gives EACCES\r\n    //      instead. On WSL 2, 9p gives the Linux error code.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(FS_LINKAT_TEST_DIR \"/testfile\", 0666));\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_LINKAT_TEST_DIR \"/testfile\"));\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9)\r\n    {\r\n        LxtCheckErrnoFailure(linkat(Fd, \"\", TargetDirFd, \"testlink\", AT_EMPTY_PATH), ENOENT);\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrnoFailure(linkat(Fd, \"\", TargetDirFd, \"testlink\", AT_EMPTY_PATH), EACCES);\r\n    }\r\n\r\n    LxtCheckClose(Fd);\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (SourceDirFd >= 0)\r\n    {\r\n        close(SourceDirFd);\r\n    }\r\n\r\n    if (TargetDirFd >= 0)\r\n    {\r\n        close(TargetDirFd);\r\n    }\r\n\r\n    if (SymlinkFd >= 0)\r\n    {\r\n        close(SymlinkFd);\r\n    }\r\n\r\n    unlink(FS_LINKAT_TEST_DIR \"/testdirsymlink\");\r\n    unlink(FS_LINKAT_TEST_DIR \"/testsymlink\");\r\n    unlink(FS_LINKAT_TEST_DIR \"/testfile\");\r\n    unlink(FS_LINKAT_TEST_DIR2 \"/testlink\");\r\n    unlink(FS_LINKAT_TEST_DIR2 \"/testlink2\");\r\n    rmdir(FS_LINKAT_TEST_DIR);\r\n    rmdir(FS_LINKAT_TEST_DIR2);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestOpen(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the open syscall.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    //\r\n    // Test that opening a directory with O_CREAT always fails with EISDIR.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(open(FS_TEST_DIR_PARENT, O_RDONLY | O_CREAT), EISDIR);\r\n    LxtCheckErrnoFailure(open(FS_TEST_DIR_PARENT, O_RDONLY | O_CREAT | O_EXCL), EEXIST);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestOpenAt(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int DirFd;\r\n    int ChildFd1;\r\n    int ChildFd2;\r\n    const char DirPath[] = FS_TEST_DIR_PARENT \"/test_openat\";\r\n    const char Child1[] = \"newfile\";\r\n    const char Child1FullPath[] = FS_TEST_DIR_PARENT \"/test_openat/newfile\";\r\n    const char* UnlinkName = NULL;\r\n    const char* RmdirPath = NULL;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    DirFd = -1;\r\n    ChildFd1 = -1;\r\n    ChildFd2 = -1;\r\n    UnlinkName = NULL;\r\n    RmdirPath = NULL;\r\n\r\n    //\r\n    // Make a directory.\r\n    //\r\n\r\n    LxtLogInfo(\"Creating test directory folder %s\", DirPath);\r\n\r\n    Result = mkdir(DirPath, 0777);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"Could not create test directory:  %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    LxtLogInfo(\"Created test directory folder!\");\r\n    RmdirPath = DirPath;\r\n\r\n    //\r\n    // Open the directory.\r\n    //\r\n\r\n    LxtLogInfo(\"Opening test directory folder %s\", DirPath);\r\n\r\n    Result = open(DirPath, O_RDONLY);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"Could not open test directory: %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    DirFd = Result;\r\n    LxtLogInfo(\"Opened test directory folder, fd = %d\", DirFd);\r\n\r\n    //\r\n    // Open a child relative to the directory. This should fail.\r\n    //\r\n\r\n    LxtLogInfo(\"Opening child %s without create flag\", Child1);\r\n\r\n    Result = openat(DirFd, Child1, O_RDONLY);\r\n    if (Result >= 0)\r\n    {\r\n        LxtLogError(\"Unexpectedly opened child: %d\", Result);\r\n        ChildFd1 = Result;\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Create child directory. This should succeed.\r\n    //\r\n\r\n    LxtLogInfo(\"Opening child %s with create flag\", Child1);\r\n\r\n    Result = openat(DirFd, Child1, O_RDONLY | O_CREAT, S_IRWXU);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"Failed to create child %d\", Result);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    ChildFd1 = Result;\r\n    LxtLogInfo(\"Created child, fd = %d\", ChildFd1);\r\n\r\n    UnlinkName = Child1FullPath;\r\n\r\n    //\r\n    // Open child directory using a full path. This should succeed.\r\n    //\r\n\r\n    LxtLogInfo(\"Opening child with full path %s\", Child1FullPath);\r\n\r\n    Result = open(Child1FullPath, O_RDONLY);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"Failed to open child full path %s: %d\", Child1FullPath, Result);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    ChildFd2 = Result;\r\n    Result = 0;\r\n\r\n    LxtLogInfo(\"Opened child with full path, fd = %d\", ChildFd2);\r\n\r\n    LxtLogInfo(\"FsCommonTestOpenAt succeeded! Party in the USA!\");\r\n\r\ncleanup:\r\n\r\n    if (ChildFd1 != -1)\r\n    {\r\n        close(ChildFd1);\r\n    }\r\n\r\n    if (ChildFd2 != -1)\r\n    {\r\n        close(ChildFd2);\r\n    }\r\n\r\n    if (DirFd != -1)\r\n    {\r\n        close(DirFd);\r\n    }\r\n\r\n    if (UnlinkName != NULL)\r\n    {\r\n        unlink(UnlinkName);\r\n    }\r\n\r\n    if (RmdirPath != NULL)\r\n    {\r\n        rmdir(RmdirPath);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestOpenCreateSymlink(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests opening files through existing symlinks with O_CREAT.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesWritten;\r\n    int Fd;\r\n    const char* LinkPath = FS_TEST_DIR_PARENT \"/test_opencreatelink\";\r\n    const char* Path = FS_TEST_DIR_PARENT \"/test_opencreate\";\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    //\r\n    // Create a test file and link.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(Path, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrnoZeroSuccess(symlink(Path, LinkPath));\r\n\r\n    //\r\n    // Try to open the file through the link with O_CREAT and write some data.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(LinkPath, O_RDWR | O_CREAT));\r\n    LxtCheckErrno(BytesWritten = write(Fd, \"test\", 4));\r\n    LxtCheckEqual(BytesWritten, 4, \"%ld\");\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check the file was written to.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(Path, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 4, \"%ld\");\r\n    LxtCheckErrnoZeroSuccess(stat(LinkPath, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 4, \"%ld\");\r\n\r\n    //\r\n    // Point the link at /dev/null and try again.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(LinkPath));\r\n    LxtCheckErrnoZeroSuccess(symlink(\"/dev/null\", LinkPath));\r\n    LxtCheckErrno(Fd = open(LinkPath, O_RDWR | O_CREAT));\r\n    LxtCheckErrno(BytesWritten = write(Fd, \"test\", 4));\r\n    LxtCheckEqual(BytesWritten, 4, \"%ld\");\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrnoZeroSuccess(stat(\"/dev/null\", &Stat));\r\n    LxtCheckEqual(Stat.st_size, 0, \"%ld\");\r\n    LxtCheckErrnoZeroSuccess(stat(LinkPath, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 0, \"%ld\");\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(LinkPath);\r\n    unlink(Path);\r\n\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestOpenCreateSymlinkDir(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests creating a symlink to a directory.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesWritten;\r\n    int Fd;\r\n    const char* LinkDir = FS_TEST_DIR_PARENT \"/test_dir_link\";\r\n    const char* Dir = FS_TEST_DIR_PARENT \"/test_dir/\";\r\n    const char* LinkFile = FS_TEST_DIR_PARENT \"/test_dir_link/test.txt\";\r\n    const char* File = FS_TEST_DIR_PARENT \"/test_dir/test.txt\";\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    //\r\n    // Create a new directory and a link to the directory.\r\n    // Note that the link's target directory contains a trailing slash.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(Dir, 0777));\r\n    LxtCheckErrnoZeroSuccess(symlink(Dir, LinkDir));\r\n\r\n    //\r\n    // Create a new file in the new directory (without using the\r\n    // directory symlink), and write 4 bytes to the file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(File, 0777));\r\n    LxtCheckErrno(BytesWritten = write(Fd, \"test\", 4));\r\n    LxtCheckEqual(BytesWritten, 4, \"%ld\");\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Check that the file was written to.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(File, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 4, \"%ld\");\r\n\r\n    //\r\n    // Check that accessing the file through the directory\r\n    // symlink works properly.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(LinkFile, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 4, \"%ld\");\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(LinkFile);\r\n    unlink(File);\r\n    unlink(LinkDir);\r\n    rmdir(Dir);\r\n\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestCreateAndRename(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int ExpectedError;\r\n    int FileDescriptor;\r\n    int Result;\r\n    const char Source[] = FS_TEST_DIR_PARENT \"/fs_test.bin\";\r\n    const char SourceLink[] = FS_TEST_DIR_PARENT \"/fs_test.bin.link\";\r\n    const char Target1[] = FS_TEST_DIR_PARENT \"/test/fs_test.bin\";\r\n    const char Target2[] = FS_TEST_DIR_PARENT \"/test/fs_test.bin.bak\";\r\n    const char TestPath[] = FS_TEST_DIR_PARENT \"/test\";\r\n    const char TestPathError[] = FS_TEST_DIR_PARENT \"/test/test\";\r\n    const char* UnlinkName = NULL;\r\n    const char* RmdirName = NULL;\r\n\r\n    //\r\n    // Open the test file; this should fail.\r\n    //\r\n\r\n    FileDescriptor = open(Source, O_RDWR);\r\n    if (FileDescriptor != -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Found '%s' at the start; it should not exist!\", Source);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (mkdir(TestPath, 0777) == 0)\r\n    {\r\n        RmdirName = TestPath;\r\n    }\r\n\r\n    //\r\n    // Create the test file; this should succeed.\r\n    //\r\n\r\n    FileDescriptor = open(Source, O_RDWR | O_CREAT, S_IRWXU);\r\n    if (FileDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create '%s', %d\", Source, Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    UnlinkName = Source;\r\n    close(FileDescriptor);\r\n    FileDescriptor = -1;\r\n\r\n    //\r\n    // Create the test link.\r\n    //\r\n\r\n    LxtCheckErrno(symlink(Source, SourceLink));\r\n\r\n    //\r\n    // Rename the file and directory to itself.\r\n    //\r\n\r\n    LxtCheckErrno(rename(TestPath, TestPath));\r\n    LxtCheckErrno(rename(Source, Source));\r\n    LxtCheckErrno(rename(SourceLink, SourceLink));\r\n    LxtCheckErrnoFailure(rename(TestPath, TestPathError), EINVAL);\r\n\r\n    //\r\n    // Various invalid renames (requires chroot).\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        const char* RelativePath = \"foo\";\r\n\r\n        LxtCheckErrno(chdir(TestPath));\r\n        LxtCheckErrno(chroot(\".\"));\r\n        mkdir(RelativePath, 0777);\r\n        LxtCheckErrnoFailure(rename(RelativePath, \".\"), EBUSY);\r\n        LxtCheckErrnoFailure(rename(RelativePath, \"..\"), EBUSY);\r\n        LxtCheckErrnoFailure(rename(RelativePath, \"/\"), EBUSY);\r\n        LxtCheckErrnoFailure(rename(RelativePath, \"./\"), EBUSY);\r\n        LxtCheckErrnoFailure(rename(RelativePath, \"../\"), EBUSY);\r\n        LxtCheckErrnoFailure(rename(RelativePath, \"//\"), EBUSY);\r\n        LxtCheckErrnoFailure(rename(\".\", RelativePath), EBUSY);\r\n        LxtCheckErrnoFailure(rename(\"..\", RelativePath), EBUSY);\r\n        LxtCheckErrnoFailure(rename(\"/\", RelativePath), EBUSY);\r\n        LxtCheckErrno(rmdir(RelativePath));\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // Rename across directories.\r\n    //\r\n\r\n    Result = rename(Source, Target1);\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not rename '%s' to '%s', %d\", Source, Target1, Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    UnlinkName = Target1;\r\n\r\n    //\r\n    // Rename within the same directory.\r\n    //\r\n\r\n    Result = rename(Target1, Target2);\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not rename '%s' to '%s', %d\", Target1, Target2, Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    UnlinkName = Target2;\r\n\r\n    //\r\n    // Rename with an open file in the directory.\r\n    //\r\n\r\n    LxtCheckErrno(FileDescriptor = open(Target2, O_RDONLY));\r\n    LxtCheckErrnoFailure(rename(TestPath, FS_TEST_DIR_PARENT \"/test_fail\"), EACCES);\r\n\r\n    LxtCheckClose(FileDescriptor);\r\n\r\n    //\r\n    // The previous failed rename may have flushed directory entries, so try\r\n    // another rename inside of the directory.\r\n    //\r\n\r\n    Result = rename(Target2, Target1);\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not rename '%s' to '%s', %d\", Source, Target1, Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    UnlinkName = Target1;\r\n\r\n    //\r\n    // Unlink the final file.\r\n    //\r\n\r\n    Result = unlink(UnlinkName);\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not unlink '%s', %d\", Target2, Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    UnlinkName = NULL;\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    if (FileDescriptor != -1)\r\n    {\r\n        close(FileDescriptor);\r\n    }\r\n\r\n    if (NULL != UnlinkName)\r\n    {\r\n        (void)unlink(UnlinkName);\r\n    }\r\n\r\n    if (NULL != RmdirName)\r\n    {\r\n        (void)rmdir(RmdirName);\r\n    }\r\n\r\n    remove(SourceLink);\r\n\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestCreateSymlinkTarget(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests creating the target of a symlink through the symlink.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    const char* SymlinkPath = FS_TEST_DIR_PARENT \"/fs_createsymlink\";\r\n    const char* SymlinkTarget = \"fs_createsymlinktarget\";\r\n    const char* SymlinkTargetAbsolute = FS_TEST_DIR_PARENT \"/fs_createsymlinktarget\";\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Create the symlink, and verify the target does not exist.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(symlink(SymlinkTarget, SymlinkPath));\r\n    LxtCheckErrnoFailure(open(SymlinkPath, O_RDONLY), ENOENT);\r\n    LxtCheckErrnoFailure(open(SymlinkTargetAbsolute, O_RDONLY), ENOENT);\r\n    LxtCheckErrno(Fd = open(SymlinkPath, O_PATH | O_NOFOLLOW));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Using O_EXCL will fail even if the target does not exist.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(Fd = open(SymlinkPath, O_CREAT | O_EXCL), EEXIST);\r\n\r\n    //\r\n    // Using O_NOFOLLOW will fail as usual.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(Fd = open(SymlinkPath, O_CREAT | O_NOFOLLOW), ELOOP);\r\n    LxtCheckErrnoFailure(open(SymlinkPath, O_RDONLY), ENOENT);\r\n    LxtCheckErrnoFailure(open(SymlinkTargetAbsolute, O_RDONLY), ENOENT);\r\n\r\n    //\r\n    // Create the target through the symlink, and check it got created.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(SymlinkPath, O_CREAT));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrnoZeroSuccess(access(SymlinkTargetAbsolute, F_OK));\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(SymlinkTargetAbsolute);\r\n    unlink(SymlinkPath);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestReadlinkat(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the readlinkat function.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[PATH_MAX];\r\n    ssize_t BytesRead;\r\n    int Fd;\r\n    int Result;\r\n\r\n    //\r\n    // Create a symlink to test.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(FS_READLINK_TEST_FILE, 0666));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_READLINK_TEST_FILE, FS_READLINK_TEST_LINK));\r\n\r\n    //\r\n    // Test the ability to operate directly on a symlink.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_READLINK_TEST_LINK, O_PATH | O_NOFOLLOW));\r\n    memset(Buffer, 0, sizeof(Buffer));\r\n    LxtCheckErrno(BytesRead = readlinkat(Fd, \"\", Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(BytesRead, strlen(FS_READLINK_TEST_FILE), \"%ld\");\r\n    LxtCheckStringEqual(Buffer, FS_READLINK_TEST_FILE);\r\n\r\n    //\r\n    // Path specified with symlink file descriptor.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(readlinkat(Fd, \"foo\", Buffer, sizeof(Buffer)), ENOTDIR);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Empty path with non-symlink file descriptor.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_READLINK_TEST_FILE, O_RDONLY));\r\n    LxtCheckErrnoFailure(readlinkat(Fd, \"\", Buffer, sizeof(Buffer)), ENOENT);\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(FS_READLINK_TEST_LINK);\r\n    unlink(FS_READLINK_TEST_FILE);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestRemoveSelfOrParent(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests some corner cases of the rmdir function.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n    Fd = -1;\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_RMDIR_TEST_DIR, 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_RMDIR_TEST_DIR \"/test\", 0777));\r\n\r\n    //\r\n    // Test relative to current working directory.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(chdir(FS_RMDIR_TEST_DIR \"/test\"));\r\n\r\n        //\r\n        // Can't remove . or ..\r\n        //\r\n\r\n        LxtCheckErrnoFailure(rmdir(\"..\"), ENOTEMPTY);\r\n        LxtCheckErrnoFailure(unlink(\"..\"), EISDIR);\r\n        LxtCheckErrnoFailure(rmdir(\".\"), EINVAL);\r\n        LxtCheckErrnoFailure(unlink(\".\"), EISDIR);\r\n\r\n        //\r\n        // Even when the directory is empty, rmdir(\"..\") says ENOTEMPTY.\r\n        //\r\n        // N.B. On Plan9, using the current working directory after deleting it\r\n        //      does not work, even if just to say \"..\".\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(rmdir(FS_RMDIR_TEST_DIR \"/test\"));\r\n        if (g_LxtFsInfo.FsType != LxtFsTypePlan9)\r\n        {\r\n            LxtCheckErrnoFailure(rmdir(\"..\"), ENOTEMPTY);\r\n        }\r\n\r\n        LxtCheckErrnoZeroSuccess(rmdir(FS_RMDIR_TEST_DIR));\r\n\r\n        //\r\n        // Root path.\r\n        //\r\n        // N.B. Cannot chroot to a deleted working directory on plan 9.\r\n        //\r\n\r\n        if (g_LxtFsInfo.FsType != LxtFsTypePlan9)\r\n        {\r\n            LxtCheckErrno(chroot(\".\"));\r\n        }\r\n\r\n        LxtCheckErrnoFailure(rmdir(\"/\"), EBUSY);\r\n        LxtCheckErrnoFailure(unlink(\"/\"), EISDIR);\r\n        LxtCheckErrnoFailure(rmdir(\"//\"), EBUSY);\r\n        LxtCheckErrnoFailure(unlink(\"//\"), EISDIR);\r\n        LxtCheckErrnoFailure(rmdir(\"/.\"), EINVAL);\r\n        LxtCheckErrnoFailure(unlink(\"/.\"), EISDIR);\r\n        LxtCheckErrnoFailure(rmdir(\"/..\"), ENOTEMPTY);\r\n        LxtCheckErrnoFailure(unlink(\"/..\"), EISDIR);\r\n\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_RMDIR_TEST_DIR, 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_RMDIR_TEST_DIR \"/test\", 0777));\r\n\r\n    //\r\n    // Same tests, with unlinkat.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_RMDIR_TEST_DIR \"/test\", O_DIRECTORY));\r\n    LxtCheckErrnoFailure(unlinkat(Fd, \"..\", AT_REMOVEDIR), ENOTEMPTY);\r\n    LxtCheckErrnoFailure(unlinkat(Fd, \"..\", 0), EISDIR);\r\n    LxtCheckErrnoFailure(unlinkat(Fd, \".\", AT_REMOVEDIR), EINVAL);\r\n    LxtCheckErrnoFailure(unlinkat(Fd, \".\", 0), EISDIR);\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Full paths ending in . or ..\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rmdir(FS_RMDIR_TEST_DIR \"/test/..\"), ENOTEMPTY);\r\n    LxtCheckErrnoFailure(unlink(FS_RMDIR_TEST_DIR \"/test/..\"), EISDIR);\r\n    LxtCheckErrnoFailure(rmdir(FS_RMDIR_TEST_DIR \"/test/.\"), EINVAL);\r\n    LxtCheckErrnoFailure(unlink(FS_RMDIR_TEST_DIR \"/test/.\"), EISDIR);\r\n\r\n    //\r\n    // Nonexistent paths.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rmdir(FS_RMDIR_TEST_DIR \"/test2/..\"), ENOENT);\r\n    LxtCheckErrnoFailure(rmdir(FS_RMDIR_TEST_DIR \"/test2/.\"), ENOENT);\r\n    LxtCheckErrnoFailure(unlink(FS_RMDIR_TEST_DIR \"/test2/..\"), ENOENT);\r\n    LxtCheckErrnoFailure(unlink(FS_RMDIR_TEST_DIR \"/test2/.\"), ENOENT);\r\n\r\n    //\r\n    // Having a . anywhere but the last component does work.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rmdir(FS_RMDIR_TEST_DIR \"/./test\"));\r\n    LxtCheckErrnoFailure(access(FS_RMDIR_TEST_DIR \"/test\", F_OK), ENOENT);\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        exit(LXT_RESULT_FAILURE);\r\n    }\r\n\r\n    rmdir(FS_RMDIR_TEST_DIR \"/test\");\r\n    rmdir(FS_RMDIR_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestRenameAt(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the renameat system call on volfs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int DirFd1;\r\n    int DirFd2;\r\n    int Result;\r\n\r\n    DirFd1 = -1;\r\n    DirFd2 = -1;\r\n\r\n    //\r\n    // Create a directory structure to use for the test.\r\n    //\r\n\r\n    LxtCheckResult(LxtFsCreateTestDir((FS_RENAMEAT_TEST_DIR)));\r\n    LxtCheckResult(LxtFsCreateTestDir((FS_RENAMEAT_TEST_DIR \"/a\")));\r\n    LxtCheckResult(LxtFsCreateTestDir((FS_RENAMEAT_TEST_DIR \"/a/b\")));\r\n    LxtCheckResult(LxtFsCreateTestDir((FS_RENAMEAT_TEST_DIR \"/a/b/c\")));\r\n    LxtCheckResult(LxtFsCreateTestDir((FS_RENAMEAT_TEST_DIR \"/a/b/c/d\")));\r\n    LxtCheckResult(LxtFsCreateTestDir((FS_RENAMEAT_TEST_DIR \"/a/b/c/d/e\")));\r\n    LxtCheckResult(LxtFsCreateTestDir((FS_RENAMEAT_TEST_DIR \"/a/b/c/d/e/f\")));\r\n\r\n    LxtCheckErrno(DirFd1 = open(FS_RENAMEAT_TEST_DIR \"/a\", O_DIRECTORY));\r\n    LxtCheckErrno(DirFd2 = open(FS_RENAMEAT_TEST_DIR \"/a/b/c\", O_DIRECTORY));\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(FS_RENAMEAT_TEST_DIR));\r\n\r\n    LxtCheckErrno(LxtFsRenameAtCommon(DirFd1, DirFd2));\r\n\r\nErrorExit:\r\n    if (DirFd1 >= 0)\r\n    {\r\n        LxtClose(DirFd1);\r\n    }\r\n\r\n    if (DirFd2 >= 0)\r\n    {\r\n        LxtClose(DirFd2);\r\n    }\r\n\r\n    rmdir(FS_RENAMEAT_TEST_DIR \"/a/b/c/d/e/f\");\r\n    rmdir(FS_RENAMEAT_TEST_DIR \"/a/b/c/d/e\");\r\n    rmdir(FS_RENAMEAT_TEST_DIR \"/a/b/c/d\");\r\n    rmdir(FS_RENAMEAT_TEST_DIR \"/a/b/c\");\r\n    rmdir(FS_RENAMEAT_TEST_DIR \"/a/b\");\r\n    rmdir(FS_RENAMEAT_TEST_DIR \"/a\");\r\n    rmdir(FS_RENAMEAT_TEST_DIR);\r\n\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestRenameDir(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the rename system call for LxFs directories.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(LxtFsRenameDirCommon(FS_TEST_DIR_PARENT));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestSetEofCheckTimeLessThan(struct timespec* X, struct timespec* Y)\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    if (X->tv_sec > Y->tv_sec)\r\n    {\r\n        LxtLogError(\"Unexpected seconds\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if ((X->tv_sec == Y->tv_sec) && (X->tv_nsec >= Y->tv_nsec))\r\n    {\r\n        LxtLogError(\"Unexpected nano seconds\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestSetEof(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result = LXT_RESULT_FAILURE;\r\n    int fd;\r\n    const char TestFileName[] = FS_TEST_DIR_PARENT \"/fs_test.bin\";\r\n    struct stat statbuf;\r\n    struct stat statbuf2;\r\n\r\n    //\r\n    // Create the test file.\r\n    //\r\n\r\n    LxtCheckErrno(fd = open(TestFileName, O_RDWR | O_CREAT, S_IRWXU));\r\n    LxtCheckErrno(ftruncate(fd, 54321));\r\n    LxtCheckErrno(stat(TestFileName, &statbuf));\r\n    LxtCheckErrnoFailure(stat(FS_TEST_DIR_PARENT \"/*\", &statbuf), ENOENT);\r\n    LxtCheckErrnoFailure(stat(FS_TEST_DIR_PARENT \"/*.bin\", &statbuf), ENOENT);\r\n    LxtCheckErrno(fstat(fd, &statbuf));\r\n    if (54321 != statbuf.st_size)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"size mismatch after ftruncate64.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(ftruncate(fd, 12345));\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"ftruncate('%fs') failed, %d\", TestFileName, Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(fstat(fd, &statbuf));\r\n    if (12345 != statbuf.st_size)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"size mismatch after ftruncate.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Check that setting the eof does change the file times even if there\r\n    // was no change.\r\n    //\r\n\r\n    LxtCheckErrno(ftruncate(fd, 0));\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"ftruncate('%fs') failed, %d\", TestFileName, Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(fstat(fd, &statbuf));\r\n    if (0 != statbuf.st_size)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"size mismatch after ftruncate.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    usleep(500000);\r\n    LxtCheckErrno(ftruncate(fd, 0));\r\n    if (Result < 0)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"ftruncate('%fs') failed, %d\", TestFileName, Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(fstat(fd, &statbuf2));\r\n    if (0 != statbuf.st_size)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"size mismatch after ftruncate.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // NTFS updates the atime when other timestamps are updated, even when\r\n    // access time is otherwise disabled.\r\n    //\r\n\r\n    if (g_LxtFsInfo.Flags.DrvFsBehavior == 0)\r\n    {\r\n        LxtCheckEqual(statbuf.st_atim.tv_sec, statbuf2.st_atim.tv_sec, \"%d\");\r\n        LxtCheckEqual(statbuf.st_atim.tv_nsec, statbuf2.st_atim.tv_nsec, \"%d\");\r\n    }\r\n\r\n    if (FS_IS_PLAN9_CACHED() == FALSE)\r\n    {\r\n        LxtCheckResult(FsCommonTestSetEofCheckTimeLessThan(&statbuf.st_mtim, &statbuf2.st_mtim));\r\n        LxtCheckResult(FsCommonTestSetEofCheckTimeLessThan(&statbuf.st_ctim, &statbuf2.st_ctim));\r\n    }\r\n\r\n    close(fd);\r\n    fd = -1;\r\n\r\n    usleep(500000);\r\n    LxtCheckErrno(fd = open(TestFileName, O_RDWR | O_TRUNC, S_IRWXU));\r\n    LxtCheckErrno(fstat(fd, &statbuf));\r\n    if (0 != statbuf.st_size)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"size mismatch after ftruncate.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (g_LxtFsInfo.Flags.DrvFsBehavior == 0)\r\n    {\r\n        LxtCheckEqual(statbuf.st_atim.tv_sec, statbuf2.st_atim.tv_sec, \"%d\");\r\n        LxtCheckEqual(statbuf.st_atim.tv_nsec, statbuf2.st_atim.tv_nsec, \"%d\");\r\n    }\r\n\r\n    if (FS_IS_PLAN9_CACHED() == FALSE)\r\n    {\r\n        LxtCheckResult(FsCommonTestSetEofCheckTimeLessThan(&statbuf2.st_mtim, &statbuf.st_mtim));\r\n        LxtCheckResult(FsCommonTestSetEofCheckTimeLessThan(&statbuf2.st_ctim, &statbuf.st_ctim));\r\n    }\r\n\r\n    close(fd);\r\n    fd = -1;\r\n\r\n    LxtCheckErrno(unlink(TestFileName));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    if (-1 != fd)\r\n    {\r\n        close(fd);\r\n        (void)unlink(TestFileName);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestTrailingSlash(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the behavior of open with trailing slashes.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Nonexistent file tests.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(creat(FS_TRAILING_TEST_FILE \"/\", 0666), EISDIR);\r\n    LxtCheckErrnoFailure(creat(FS_TRAILING_TEST_FILE \"/foo/\", 0666), ENOENT);\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_FILE \"/\", O_RDONLY), ENOENT);\r\n    LxtCheckErrnoFailure(stat(FS_TRAILING_TEST_FILE \"/\", &Stat), ENOENT);\r\n\r\n    //\r\n    // Create a directory.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_TRAILING_TEST_DIR \"/\", 0777));\r\n    LxtCheckErrno(Fd = open(FS_TRAILING_TEST_DIR \"/\", O_RDONLY | O_DIRECTORY));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = open(FS_TRAILING_TEST_DIR \"//\", O_RDONLY | O_DIRECTORY));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_DIR \"/\", O_RDONLY | O_CREAT), EISDIR);\r\n\r\n    //\r\n    // Create a symlink to a directory.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(symlink(FS_TRAILING_TEST_DIR, FS_TRAILING_TEST_LINK \"/\"), ENOENT);\r\n\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_TRAILING_TEST_DIR, FS_TRAILING_TEST_LINK));\r\n\r\n    LxtCheckErrnoFailure(symlink(FS_TRAILING_TEST_DIR, FS_TRAILING_TEST_LINK \"/\"), EEXIST);\r\n\r\n    //\r\n    // Test the symlink with and without trailing slash.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_TRAILING_TEST_LINK \"/\", &Stat));\r\n    LxtCheckTrue(S_ISDIR(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_TRAILING_TEST_LINK \"//\", &Stat));\r\n    LxtCheckTrue(S_ISDIR(Stat.st_mode));\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_TRAILING_TEST_LINK, &Stat));\r\n    LxtCheckTrue(S_ISLNK(Stat.st_mode));\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK, O_RDONLY | O_NOFOLLOW), ELOOP);\r\n\r\n    LxtCheckErrno(Fd = open(FS_TRAILING_TEST_LINK \"/\", O_RDONLY | O_NOFOLLOW | O_DIRECTORY));\r\n\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Create a file and test using it with a trailing slash.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(FS_TRAILING_TEST_FILE, 0666));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_FILE \"/\", O_RDONLY), ENOTDIR);\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_FILE \"//\", O_RDONLY), ENOTDIR);\r\n    LxtCheckErrnoFailure(stat(FS_TRAILING_TEST_FILE \"/\", &Stat), ENOTDIR);\r\n    LxtCheckErrnoFailure(stat(FS_TRAILING_TEST_FILE \"//\", &Stat), ENOTDIR);\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_FILE \"/\", O_RDONLY | O_CREAT), EISDIR);\r\n\r\n    //\r\n    // Create a symlink to a file and test using with a trailing slash.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_TRAILING_TEST_LINK));\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_TRAILING_TEST_FILE, FS_TRAILING_TEST_LINK));\r\n\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK \"/\", O_RDONLY), ENOTDIR);\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK \"/\", O_RDONLY | O_NOFOLLOW), ENOTDIR);\r\n\r\n    LxtCheckErrnoFailure(stat(FS_TRAILING_TEST_LINK \"/\", &Stat), ENOTDIR);\r\n    LxtCheckErrnoFailure(lstat(FS_TRAILING_TEST_LINK \"/\", &Stat), ENOTDIR);\r\n\r\n    //\r\n    // Create a symlink where the target has a trailing slash.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_TRAILING_TEST_LINK));\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_TRAILING_TEST_FILE \"/\", FS_TRAILING_TEST_LINK));\r\n\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK, O_RDONLY), ENOTDIR);\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK, O_RDONLY | O_CREAT), EISDIR);\r\n\r\n    LxtCheckErrnoFailure(stat(FS_TRAILING_TEST_LINK, &Stat), ENOTDIR);\r\n\r\n    //\r\n    // Mkdir over an existing file.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdir(FS_TRAILING_TEST_FILE \"/\", 0777), EEXIST);\r\n\r\n    //\r\n    // Unlink/rmdir\r\n    //\r\n\r\n    LxtCheckErrnoFailure(unlink(FS_TRAILING_TEST_FILE \"/\"), ENOTDIR);\r\n    LxtCheckErrnoFailure(unlink(FS_TRAILING_TEST_DIR \"/\"), EISDIR);\r\n    LxtCheckErrnoZeroSuccess(rmdir(FS_TRAILING_TEST_DIR \"/\"));\r\n    LxtCheckErrnoFailure(rmdir(FS_TRAILING_TEST_FILE \"/\"), ENOTDIR);\r\n\r\n    //\r\n    // Test a symlink to a nonexistent target.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_TRAILING_TEST_LINK));\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_TRAILING_TEST_FILE, FS_TRAILING_TEST_LINK));\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_TRAILING_TEST_FILE));\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK \"/\", O_RDONLY), ENOENT);\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK \"/\", O_RDONLY | O_NOFOLLOW), ENOENT);\r\n\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK \"/\", O_RDONLY | O_CREAT), EISDIR);\r\n\r\n    LxtCheckErrnoFailure(stat(FS_TRAILING_TEST_LINK \"/\", &Stat), ENOENT);\r\n    LxtCheckErrnoFailure(lstat(FS_TRAILING_TEST_LINK \"/\", &Stat), ENOENT);\r\n\r\n    //\r\n    // Symlink to a nonexistent target with trailing slash.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_TRAILING_TEST_LINK));\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_TRAILING_TEST_FILE \"/\", FS_TRAILING_TEST_LINK));\r\n\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK, O_RDONLY), ENOENT);\r\n    LxtCheckErrnoFailure(open(FS_TRAILING_TEST_LINK, O_RDONLY | O_CREAT), EISDIR);\r\n\r\n    LxtCheckErrnoFailure(stat(FS_TRAILING_TEST_LINK, &Stat), ENOENT);\r\n\r\n    //\r\n    // Other creation functions.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(link(FS_TRAILING_TEST_LINK, FS_TRAILING_TEST_FILE \"/\"), ENOENT);\r\n\r\n    LxtCheckErrnoFailure(mknod(FS_TRAILING_TEST_FILE \"/\", S_IFIFO | 0666, 0), ENOENT);\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_TRAILING_TEST_DIR, 0777));\r\n    LxtCheckErrnoFailure(link(FS_TRAILING_TEST_LINK, FS_TRAILING_TEST_DIR \"/\"), EEXIST);\r\n\r\n    LxtCheckErrnoFailure(mknod(FS_TRAILING_TEST_DIR \"/\", S_IFIFO | 0666, 0), EEXIST);\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(FS_TRAILING_TEST_FILE);\r\n    unlink(FS_TRAILING_TEST_LINK);\r\n    rmdir(FS_TRAILING_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestMkdir(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    const char* ParentDirName = FS_TEST_DIR_PARENT;\r\n    const char* TestDirName = FS_TEST_DIR_PARENT \"/test_dir\";\r\n    const char* TestSubDirName = FS_TEST_DIR_PARENT \"/test_dir/foo\";\r\n    const char* RelativeDirName = \"test_dir\";\r\n    const char* RelativeDotSlashDirName = \"./test_dir/\";\r\n    const char* RelativeSubDirName = \"test_dir/foo\";\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    //\r\n    // Ensure the dir doesn't exist.\r\n    //\r\n\r\n    unlink(TestDirName);\r\n    rmdir(TestSubDirName);\r\n    rmdir(TestDirName);\r\n\r\n    //\r\n    // Create the subdir while the parent doesn't exist.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdir(TestSubDirName, 0777), ENOENT);\r\n\r\n    //\r\n    // Create the test dir as a file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(TestDirName, O_RDWR | O_CREAT, S_IRWXU));\r\n\r\n    //\r\n    // Verify the file size is 0.\r\n    //\r\n\r\n    LxtCheckErrno(fstat(Fd, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 0, \"%ld\");\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Try to create a dir with this name, and a dir under that name.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdir(TestDirName, 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(TestSubDirName, 0777), ENOTDIR);\r\n    LxtCheckErrnoZeroSuccess(unlink(TestDirName));\r\n\r\n    //\r\n    // Create a dir with no collisions expected.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(TestDirName, 0777));\r\n\r\n    //\r\n    // Verify the directory file size is equal to the file-system block-size.\r\n    //\r\n    // N.B. Plan 9 in cached mode doesn't return the block size reported by the\r\n    //      server.\r\n    //\r\n\r\n    LxtCheckErrno(stat(TestDirName, &Stat));\r\n    if (FS_IS_PLAN9_CACHED() == FALSE)\r\n    {\r\n        LxtCheckEqual(Stat.st_size, Stat.st_blksize, \"%ld\");\r\n    }\r\n\r\n    //\r\n    // Test a directory name collision.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdir(TestDirName, 0777), EEXIST);\r\n\r\n    //\r\n    // Test the rmdir.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rmdir(TestDirName));\r\n    LxtCheckErrnoFailure(rmdir(TestDirName), ENOENT);\r\n\r\n    //\r\n    // Test mkdir with a relative path. Change the working directory first\r\n    // since it's normally / for tests which is not interesting.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(ParentDirName));\r\n    LxtCheckErrnoFailure(mkdir(RelativeSubDirName, 0777), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(mkdir(RelativeDirName, 0777));\r\n    LxtCheckErrnoFailure(mkdir(RelativeDirName, 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(TestDirName, 0777), EEXIST);\r\n    LxtCheckErrnoZeroSuccess(mkdir(RelativeSubDirName, 0777));\r\n    LxtCheckErrnoFailure(mkdir(RelativeSubDirName, 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(TestSubDirName, 0777), EEXIST);\r\n    LxtCheckErrnoZeroSuccess(rmdir(TestSubDirName));\r\n    LxtCheckErrnoZeroSuccess(rmdir(TestDirName));\r\n\r\n    //\r\n    // Relative path starting with \"./\" and ending in \"/\".\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(RelativeDotSlashDirName, 0777));\r\n    LxtCheckErrnoFailure(mkdir(RelativeDotSlashDirName, 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(TestDirName, 0777), EEXIST);\r\n\r\n    //\r\n    // Empty path should return ENOENT\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdir(\"\", 0777), ENOENT);\r\n\r\n    //\r\n    // Special path edge cases.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdir(\".\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(\"..\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(\"/\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(\"/.\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(\"/..\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(\"/data/\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(\"/data/.\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(\"/data/..\", 0777), EEXIST);\r\n\r\n    //\r\n    // Variation succeeded.\r\n    //\r\n\r\nErrorExit:\r\n    unlink(TestDirName);\r\n    rmdir(TestSubDirName);\r\n    rmdir(TestDirName);\r\n    rmdir(RelativeDirName);\r\n\r\n    //\r\n    // Restore working directory (other tests depend on it)\r\n    //\r\n\r\n    chdir(\"/\");\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestMkDirAt(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\nThis routine tests the mkdirat system call.\r\n\r\nArguments:\r\n\r\nArgs - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    const char* ParentDirName = FS_TEST_DIR_PARENT;\r\n    int ParentFd;\r\n    const char* TestDirName = FS_TEST_DIR_PARENT \"/test_dir\";\r\n    const char* TestSubDirName = FS_TEST_DIR_PARENT \"/test_dir/foo\";\r\n    const char* RelativeDirName = \"test_dir\";\r\n    const char* RelativeDotSlashDirName = \"./test_dir/\";\r\n    const char* RelativeSubDirName = \"test_dir/foo\";\r\n    int Result;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Ensure the dir doesn't exist.\r\n    //\r\n\r\n    unlink(TestDirName);\r\n    rmdir(TestSubDirName);\r\n    rmdir(TestDirName);\r\n\r\n    //\r\n    // Open the parent.\r\n    //\r\n\r\n    LxtCheckErrno(ParentFd = open(ParentDirName, O_DIRECTORY));\r\n\r\n    //\r\n    // Create the subdir while the parent doesn't exist.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, RelativeSubDirName, 0777), ENOENT);\r\n\r\n    //\r\n    // Create the test dir as a file.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(TestDirName, O_RDWR | O_CREAT, S_IRWXU));\r\n\r\n    //\r\n    // Try using the file fd as the parent.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdirat(Fd, RelativeDirName, 0777), ENOTDIR);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Try to create a dir with this name, and a dir under that name.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, RelativeDirName, 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, RelativeSubDirName, 0777), ENOTDIR);\r\n    LxtCheckErrnoZeroSuccess(unlink(TestDirName));\r\n\r\n    //\r\n    // Create a dir with no collisions expected.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdirat(ParentFd, RelativeDirName, 0777));\r\n\r\n    //\r\n    // Test a directory name collision.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, RelativeDirName, 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(TestDirName, 0777), EEXIST);\r\n\r\n    //\r\n    // Test the rmdir.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rmdir(TestDirName));\r\n    LxtCheckErrnoFailure(rmdir(TestDirName), ENOENT);\r\n\r\n    //\r\n    // Relative path starting with \"./\" and ending in \"/\".\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdirat(ParentFd, RelativeDotSlashDirName, 0777));\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, RelativeDotSlashDirName, 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(TestDirName, 0777), EEXIST);\r\n    LxtCheckErrnoZeroSuccess(rmdir(TestDirName));\r\n\r\n    //\r\n    // Test mkdirat with a AT_FDCWD. Change the working directory first\r\n    // since it's normally / for tests which is not interesting.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(FS_TEST_DIR_PARENT));\r\n    LxtCheckErrnoFailure(mkdirat(AT_FDCWD, RelativeSubDirName, 0777), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(mkdir(RelativeDirName, 0777));\r\n    LxtCheckErrnoFailure(mkdirat(AT_FDCWD, RelativeDirName, 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(TestDirName, 0777), EEXIST);\r\n    LxtCheckErrnoZeroSuccess(mkdir(RelativeSubDirName, 0777));\r\n    LxtCheckErrnoFailure(mkdirat(AT_FDCWD, RelativeSubDirName, 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdir(TestSubDirName, 0777), EEXIST);\r\n    LxtCheckErrnoZeroSuccess(rmdir(TestSubDirName));\r\n    LxtCheckErrnoZeroSuccess(rmdir(TestDirName));\r\n\r\n    //\r\n    // Empty path should return ENOENT, even with invalid fd.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdirat(AT_FDCWD, \"\", 0777), ENOENT);\r\n    LxtCheckErrnoFailure(mkdirat(-1, \"\", 0777), ENOENT);\r\n\r\n    //\r\n    // Invalid fd.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdirat(-1, RelativeDirName, 0777), EBADF);\r\n\r\n    //\r\n    // Special path edge cases.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, \".\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, \"..\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, \"/\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, \"/.\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, \"/..\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, \"/data/\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, \"/data/.\", 0777), EEXIST);\r\n    LxtCheckErrnoFailure(mkdirat(ParentFd, \"/data/..\", 0777), EEXIST);\r\n\r\n    //\r\n    // Variation succeeded.\r\n    //\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (ParentFd >= 0)\r\n    {\r\n        close(ParentFd);\r\n    }\r\n\r\n    unlink(TestDirName);\r\n    rmdir(TestSubDirName);\r\n    rmdir(TestDirName);\r\n    rmdir(RelativeDirName);\r\n\r\n    //\r\n    // Restore working directory (other tests depend on it)\r\n    //\r\n\r\n    chdir(\"/\");\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestChdir(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n    int FileDescriptor;\r\n    const char TestFilePath1[] = \"data/fstest/test_chdir.txt\";\r\n    const char TestFilePath2[] = \"test_chdir.txt\";\r\n    const char DataTestDirPath[] = FS_TEST_DIR_PARENT;\r\n    const char DataTestDirPath2[] = FS_TEST_DIR_PARENT \"/\";\r\n    const char FailureDirPath[] = \"/system12314/\";\r\n\r\n    LxtCheckErrno(Result = chdir(\"/\"));\r\n\r\n    //\r\n    // Since the working directory is \"/\", create a file under /data/fstest\r\n    //\r\n\r\n    LxtLogInfo(\"Creating file using path %s\", TestFilePath1);\r\n    LxtCheckErrno(FileDescriptor = open(TestFilePath1, O_RDWR | O_CREAT, S_IRWXU));\r\n    LxtLogInfo(\"Opened file using path %s, closing now\", TestFilePath1);\r\n    LxtCheckClose(FileDescriptor);\r\n\r\n    //\r\n    // Change working directory to /system\r\n    //\r\n\r\n    LxtLogInfo(\"Changing working dir to %s\", DataTestDirPath);\r\n    LxtCheckErrno(Result = chdir(DataTestDirPath));\r\n\r\n    //\r\n    // Open the same file now that the working directory is different.\r\n    //\r\n\r\n    LxtLogInfo(\"Opening file using path %s\", TestFilePath2);\r\n    LxtCheckErrno(FileDescriptor = open(TestFilePath2, O_RDWR));\r\n    LxtCheckClose(FileDescriptor);\r\n    LxtLogInfo(\"Opened file using path %s successfully!\", TestFilePath2);\r\n\r\n    //\r\n    // Change working directory to /system/\r\n    //\r\n\r\n    LxtLogInfo(\"Changing working dir to %s\", DataTestDirPath2);\r\n    LxtCheckErrno(Result = chdir(DataTestDirPath2));\r\n\r\n    //\r\n    // Open the same file now that the working directory is different.\r\n    //\r\n\r\n    LxtLogInfo(\"Opening file using path %s\", TestFilePath2);\r\n    LxtCheckErrno(FileDescriptor = open(TestFilePath2, O_RDWR));\r\n    LxtCheckClose(FileDescriptor);\r\n    LxtLogInfo(\"Opened file using path %s successfully!\", TestFilePath2);\r\n\r\n    //\r\n    // Change working directory to a bogus path; this should fail.\r\n    //\r\n\r\n    LxtLogInfo(\"Changing working dir to %s\", FailureDirPath);\r\n    Result = chdir(FailureDirPath);\r\n    if (Result != -1)\r\n    {\r\n        LxtLogError(\"Chdir to directory ('%s') succeeded unexpectedly, %d\", FailureDirPath, errno);\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Changing working dir to %s failed as expected\", FailureDirPath);\r\n\r\n    Result = 0;\r\n\r\n    LxtLogInfo(\"TEST SUCCESSFUL!\");\r\n\r\nErrorExit:\r\n\r\n    if (FileDescriptor != -1)\r\n    {\r\n        close(FileDescriptor);\r\n    }\r\n\r\n    unlink(FS_TEST_DIR_PARENT \"/test_chdir.txt\");\r\n    return Result;\r\n}\r\n\r\ntypedef struct _UNLINK_AT_VARIATION\r\n{\r\n    const char* Description;\r\n    const int* Fd;\r\n    const char* Path;\r\n    const unsigned int Flags;\r\n    const int DesiredResult;\r\n    const int DesiredError;\r\n} UNLINK_AT_VARIATION, *PUNLINK_AT_VARIATION;\r\n\r\nint FsCommonTestUnlinkAt(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\nThis routine runs tests associated with the unlinkat syscall.\r\n\r\nArguments:\r\n\r\nArgs - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\nReturns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    const char Child1[] = \"newfile\";\r\n    const char Child1FullPath[] = FS_TEST_DIR_PARENT \"/test_unlinkat/newfile\";\r\n    int DirFd;\r\n    const char DirPath[] = FS_TEST_DIR_PARENT \"/test_unlinkat\";\r\n    int Result;\r\n    const char* RmdirPath = NULL;\r\n    PUNLINK_AT_VARIATION ThisVariation;\r\n    UNLINK_AT_VARIATION UnlinkAtVariations[] = {\r\n        {\"unlinkat with invalid flags\", &DirFd, Child1, 0x80000000, -1, EINVAL},\r\n        {\"unlink via unlinkat with full path\", &DirFd, Child1FullPath, 0, 0, 0},\r\n        {\"unlink via unlinkat with relative path\", &DirFd, Child1, 0, 0, 0},\r\n        {\"rmdir via unlinkat with full path\", &DirFd, Child1FullPath, AT_REMOVEDIR, 0, 0},\r\n        {\"rmdir via unlinkat with relative path\", &DirFd, Child1, AT_REMOVEDIR, 0, 0}};\r\n    const char* UnlinkName = NULL;\r\n    unsigned int Variation;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    DirFd = -1;\r\n    RmdirPath = NULL;\r\n    UnlinkName = NULL;\r\n    Variation = 0;\r\n\r\n    //\r\n    // Make a directory.\r\n    //\r\n\r\n    LxtLogInfo(\"Creating test directory folder %s\", DirPath);\r\n    LxtCheckErrnoZeroSuccess(mkdir(DirPath, 0777));\r\n    RmdirPath = DirPath;\r\n\r\n    //\r\n    // Open the directory.\r\n    //\r\n\r\n    LxtCheckErrno(open(DirPath, O_RDONLY));\r\n    DirFd = Result;\r\n    LxtLogInfo(\"Opened test directory folder, fd = %d\", DirFd);\r\n\r\n    //\r\n    // Unlink a child that we haven't created yet. This should fail.\r\n    // umount\r\n\r\n    LxtLogInfo(\"Unlinking child %s without creating it\", Child1);\r\n    LxtCheckErrnoFailure(unlinkat(DirFd, Child1, 0), ENOENT);\r\n\r\n    //\r\n    // Test various things that should succeed.\r\n    //\r\n\r\n    for (Variation = 0; Variation < sizeof(UnlinkAtVariations) / sizeof(UnlinkAtVariations[0]); Variation += 1)\r\n    {\r\n\r\n        ThisVariation = &UnlinkAtVariations[Variation];\r\n\r\n        //\r\n        // Create child file. This should succeed.\r\n        //\r\n\r\n        LxtLogInfo(\"Attempting %s\", ThisVariation->Description);\r\n\r\n        if ((ThisVariation->Flags & AT_REMOVEDIR) == 0)\r\n        {\r\n            LxtLogInfo(\"Creating child file %s\", Child1);\r\n\r\n            LxtCheckErrno(openat(DirFd, Child1, O_RDWR | O_CREAT, S_IRWXU));\r\n            close(Result);\r\n        }\r\n        else\r\n        {\r\n            LxtLogInfo(\"Creating child directory %s\", Child1);\r\n\r\n            LxtCheckErrnoZeroSuccess(mkdir(Child1FullPath, S_IRWXU));\r\n        }\r\n\r\n        UnlinkName = Child1FullPath;\r\n\r\n        //\r\n        // Execute the desired test variation.\r\n        //\r\n\r\n        Result = unlinkat(*ThisVariation->Fd, ThisVariation->Path, ThisVariation->Flags);\r\n\r\n        if (Result != ThisVariation->DesiredResult)\r\n        {\r\n            LxtLogError(\"unlinkat returned unexpected result; returned %d, expected %d\", Result, ThisVariation->DesiredResult);\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (Result != 0 && errno != ThisVariation->DesiredError)\r\n        {\r\n            LxtLogError(\"unlinkat failed with unexpected error; errno %d, expected %d\", errno, ThisVariation->DesiredError);\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // If the variation expected success, we've already deleted the object.\r\n        // If not, we need to delete it below.\r\n        //\r\n\r\n        if (Result == 0)\r\n        {\r\n            UnlinkName = NULL;\r\n        }\r\n\r\n        if (UnlinkName != NULL)\r\n        {\r\n            unlink(UnlinkName);\r\n            UnlinkName = NULL;\r\n        }\r\n    }\r\n\r\n    LxtLogInfo(\"FsCommonTestUnlinkAt succeeded!\");\r\n\r\nErrorExit:\r\n    if (DirFd >= 0)\r\n    {\r\n        close(DirFd);\r\n    }\r\n\r\n    if (UnlinkName != NULL)\r\n    {\r\n        unlink(UnlinkName);\r\n    }\r\n\r\n    if (RmdirPath != NULL)\r\n    {\r\n        rmdir(RmdirPath);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestFchownAt(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the fchownat system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int DirFd;\r\n    int Fd;\r\n    struct stat Original;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    DirFd = -1;\r\n    Fd = -1;\r\n\r\n    //\r\n    // Set up the test environment.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_FCHOWNAT_TEST_DIR, 0777));\r\n    LxtCheckErrno(Fd = creat(FS_FCHOWNAT_TEST_DIR \"/testfile\", 0666));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_FCHOWNAT_TEST_DIR \"/testfile\", FS_FCHOWNAT_TEST_DIR \"/testlink\"));\r\n\r\n    LxtCheckErrnoZeroSuccess(symlink(FS_FCHOWNAT_TEST_DIR, FS_FCHOWNAT_TEST_DIR \"/dirlink\"));\r\n\r\n    LxtCheckErrno(DirFd = open(FS_FCHOWNAT_TEST_DIR, O_RDONLY | O_DIRECTORY));\r\n\r\n    //\r\n    // Change owner.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fchownat(DirFd, \"testfile\", 2000, 3000, 0));\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testfile\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2000, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3000, \"%d\");\r\n\r\n    //\r\n    // Using AT_FDCWD.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(FS_FCHOWNAT_TEST_DIR));\r\n    LxtCheckErrnoZeroSuccess(fchownat(AT_FDCWD, \"testfile\", 2001, 3001, 0));\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testfile\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2001, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3001, \"%d\");\r\n\r\n    //\r\n    // Symlinks should be followed.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testlink\", &Original));\r\n    LxtCheckErrnoZeroSuccess(fchownat(DirFd, \"testlink\", 2002, 3002, 0));\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testfile\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2002, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3002, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testlink\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, Original.st_uid, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, Original.st_gid, \"%d\");\r\n\r\n    //\r\n    // Not followed with AT_SYMLINK_NOFOLLOW.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fchownat(DirFd, \"testlink\", 2003, 3003, AT_SYMLINK_NOFOLLOW));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testfile\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2002, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3002, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testlink\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2003, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3003, \"%d\");\r\n\r\n    //\r\n    // Fd must be a directory, not a symlink to a directory.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_FCHOWNAT_TEST_DIR \"/dirlink\", O_NOFOLLOW | O_PATH));\r\n    LxtCheckErrnoFailure(fchownat(Fd, \"testlink\", 2004, 3004, 0), ENOTDIR);\r\n    LxtCheckErrnoFailure(fchownat(Fd, \"testlink\", 2004, 3004, AT_SYMLINK_NOFOLLOW), ENOTDIR);\r\n\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // AT_EMPTY_PATH changes the file itself.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_FCHOWNAT_TEST_DIR \"/testfile\", O_RDONLY));\r\n    LxtCheckErrnoZeroSuccess(fchownat(Fd, \"\", 2005, 3005, AT_EMPTY_PATH));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testfile\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2005, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3005, \"%d\");\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // If the symlink is an FD, it's not followed regardless of flags.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_FCHOWNAT_TEST_DIR \"/testlink\", O_NOFOLLOW | O_PATH));\r\n    LxtCheckErrnoZeroSuccess(fchownat(Fd, \"\", 2006, 3006, AT_EMPTY_PATH));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testlink\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2006, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3006, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testfile\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2005, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3005, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(fchownat(Fd, \"\", 2007, 3007, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testlink\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2007, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3007, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(lstat(FS_FCHOWNAT_TEST_DIR \"/testfile\", &Stat));\r\n    LxtCheckEqual(Stat.st_uid, 2005, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, 3005, \"%d\");\r\n\r\nErrorExit:\r\n    if (DirFd >= 0)\r\n    {\r\n        close(DirFd);\r\n    }\r\n\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(FS_FCHOWNAT_TEST_DIR \"/dirlink\");\r\n    unlink(FS_FCHOWNAT_TEST_DIR \"/testlink\");\r\n    unlink(FS_FCHOWNAT_TEST_DIR \"/testfile\");\r\n    rmdir(FS_FCHOWNAT_TEST_DIR);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestFstatAt64(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine runs tests associated with the fstatat64 syscall.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n#if !defined(__amd64__)\r\n    struct stat Buffer;\r\n#else\r\n    struct stat64 Buffer;\r\n#endif\r\n\r\n    const char Child1[] = \"newfile\";\r\n    const char Child1FullPath[] = FS_TEST_DIR_PARENT \"/test_fstatat64/newfile\";\r\n    int Child1Fd;\r\n    int DirFd;\r\n    const char DirPath[] = FS_TEST_DIR_PARENT \"/test_fstatat64\";\r\n    int Result;\r\n    const char Symlink[] = \"symlink1\";\r\n    int SymlinkFd;\r\n    const char SymlinkFullPath[] = FS_TEST_DIR_PARENT \"/test_fstatat64/symlink1\";\r\n    const char DirSymlinkPath[] = FS_TEST_DIR_PARENT \"/test_fstatat64/symlink2\";\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    Child1Fd = -1;\r\n    DirFd = -1;\r\n    SymlinkFd = -1;\r\n\r\n    //\r\n    // Make a directory.\r\n    //\r\n\r\n    LxtLogInfo(\"Creating test directory folder %s\", DirPath);\r\n    LxtCheckErrnoZeroSuccess(mkdir(DirPath, 0777));\r\n\r\n    //\r\n    // Open the directory.\r\n    //\r\n\r\n    LxtCheckErrno(open(DirPath, O_RDONLY));\r\n    DirFd = Result;\r\n    LxtLogInfo(\"Opened test directory folder, fd = %d\", DirFd);\r\n\r\n    //\r\n    // Create a file.\r\n    //\r\n\r\n    LxtCheckErrno(Child1Fd = creat(Child1FullPath, 0777 | S_IRWXU | S_ISGID | S_ISUID));\r\n\r\n    //\r\n    // Create the symlinks.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(symlink(Child1FullPath, SymlinkFullPath));\r\n    LxtCheckErrnoZeroSuccess(symlink(DirPath, DirSymlinkPath));\r\n\r\n    //\r\n    // Call fstatat64 with an absolute path.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFStatAt64(DirFd, Child1FullPath, &Buffer, 0));\r\n\r\n    //\r\n    // Call fstatat64 with a relative path.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFStatAt64(DirFd, Child1, &Buffer, 0));\r\n\r\n    //\r\n    // Call fstatat64 on the symlink.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFStatAt64(DirFd, Symlink, &Buffer, 0));\r\n    LxtLogInfo(\"symlink mode: %o\", Buffer.st_mode);\r\n    if ((Buffer.st_mode & S_IFMT) != S_IFREG)\r\n    {\r\n        LxtLogError(\"Expected regular file, got: %x\", Buffer.st_mode & S_IFMT);\r\n    }\r\n\r\n    //\r\n    // Call fstatat64 on the symlink with the AT_SYMLINK_NOFOLLOW flag.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFStatAt64(DirFd, Symlink, &Buffer, AT_SYMLINK_NOFOLLOW));\r\n    LxtLogInfo(\"symlink mode with AT_SYMLINK_NOFOLLOW: %o\", Buffer.st_mode);\r\n    if ((Buffer.st_mode & S_IFMT) != S_IFLNK)\r\n    {\r\n        LxtLogError(\"Expected symlink, got: %x\", Buffer.st_mode & S_IFMT);\r\n    }\r\n\r\n    //\r\n    // Ensure that fstatat fails if the file descriptor is not a directory.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtFStatAt64(Child1Fd, \"foo\", &Buffer, 0), ENOTDIR);\r\n\r\n    //\r\n    // Use AT_EMPTY_PATH to directly stat the file descriptor.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(LxtFStatAt64(DirFd, \"\", &Buffer, AT_EMPTY_PATH));\r\n    LxtLogInfo(\"dir mode with AT_EMPTY_PATH: %o\", Buffer.st_mode);\r\n    LxtCheckTrue(S_ISDIR(Buffer.st_mode));\r\n\r\n    //\r\n    // AT_EMPTY_PATH does nothing if the path is not empty.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(LxtFStatAt64(DirFd, Child1, &Buffer, AT_EMPTY_PATH));\r\n    LxtLogInfo(\"child mode with AT_EMPTY_PATH: %o\", Buffer.st_mode);\r\n    LxtCheckTrue(S_ISREG(Buffer.st_mode));\r\n\r\n    //\r\n    // AT_EMPTY_PATH on a symlink does not follow the link regardless of\r\n    // AT_SYMLINK_NOFOLLOW.\r\n    //\r\n\r\n    LxtCheckErrno(SymlinkFd = open(SymlinkFullPath, O_NOFOLLOW | O_PATH));\r\n    LxtCheckErrnoZeroSuccess(LxtFStatAt64(SymlinkFd, \"\", &Buffer, AT_EMPTY_PATH));\r\n    LxtLogInfo(\"symlink mode with AT_EMPTY_PATH: %o\", Buffer.st_mode);\r\n    LxtCheckTrue(S_ISLNK(Buffer.st_mode));\r\n    LxtCheckErrnoZeroSuccess(LxtFStatAt64(SymlinkFd, \"\", &Buffer, (AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)));\r\n\r\n    LxtLogInfo(\"symlink mode with AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW: %o\", Buffer.st_mode);\r\n\r\n    LxtCheckTrue(S_ISLNK(Buffer.st_mode));\r\n    LxtCheckClose(SymlinkFd);\r\n\r\n    //\r\n    // If the path is not empty, the FD must be a directory; a symlink\r\n    // to a directory does not work.\r\n    //\r\n\r\n    LxtCheckErrno(SymlinkFd = open(DirSymlinkPath, O_NOFOLLOW | O_PATH));\r\n    LxtCheckErrnoFailure(LxtFStatAt64(SymlinkFd, Child1, &Buffer, 0), ENOTDIR);\r\n\r\nErrorExit:\r\n    if (Child1Fd != -1)\r\n    {\r\n        LxtClose(Child1Fd);\r\n    }\r\n\r\n    if (DirFd != -1)\r\n    {\r\n        LxtClose(DirFd);\r\n    }\r\n\r\n    if (SymlinkFd != -1)\r\n    {\r\n        close(SymlinkFd);\r\n    }\r\n\r\n    unlink(DirSymlinkPath);\r\n    remove(Child1FullPath);\r\n    remove(SymlinkFullPath);\r\n    rmdir(DirPath);\r\n\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestDeleteCurrentWorkingDirectory(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the behavior if the current working directory is\r\n    unlinked for LxFs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(LxtFsDeleteCurrentWorkingDirectoryCommon(FS_TEST_DIR_PARENT, 0));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestDeleteLoop(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests deleting files in a loop with multiple getdents calls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtFsDeleteLoopCommon(FS_DELETELOOP_TEST_DIR));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestDeleteOpenFile(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests using unlink and rmdir on a LxFs file/directory that's open.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(LxtFsDeleteOpenFileCommon(FS_TEST_DIR_PARENT, 0));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestFchdir(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n    int FileDescriptor;\r\n    const char TestFilePath1[] = \"data/fstest/test_chdir.txt\";\r\n    const char TestFilePath2[] = \"test_chdir.txt\";\r\n    const char DataTestDirPath[] = FS_TEST_DIR_PARENT;\r\n    const char DataTestDirPath2[] = FS_TEST_DIR_PARENT \"/\";\r\n    const char FailureDirPath[] = \"/system12314/\";\r\n\r\n    FileDescriptor = -1;\r\n    LxtCheckErrno(chdir(\"/\"));\r\n\r\n    //\r\n    // Since the working directory is \"/\", create a file under /data/fstest\r\n    //\r\n\r\n    LxtLogInfo(\"Creating file using path %s\", TestFilePath1);\r\n    LxtCheckErrno(FileDescriptor = open(TestFilePath1, O_RDWR | O_CREAT, S_IRWXU));\r\n    LxtCheckClose(FileDescriptor);\r\n\r\n    //\r\n    // Change working directory to /data/fstest\r\n    //\r\n\r\n    LxtLogInfo(\"Changing working dir to %s\", DataTestDirPath);\r\n    LxtCheckErrno(FileDescriptor = open(DataTestDirPath, O_RDONLY | O_DIRECTORY, 0));\r\n    LxtCheckErrno(fchdir(FileDescriptor));\r\n    LxtCheckClose(FileDescriptor);\r\n\r\n    //\r\n    // Open the same file now that the working directory is different.\r\n    //\r\n\r\n    LxtLogInfo(\"Opening file using path %s\", TestFilePath2);\r\n    LxtCheckErrno(FileDescriptor = open(TestFilePath2, O_RDWR));\r\n    LxtLogInfo(\"Opened file using path %s successfully!\", TestFilePath2);\r\n    LxtCheckClose(FileDescriptor);\r\n\r\n    //\r\n    // Change working directory to /system/\r\n    //\r\n\r\n    LxtLogInfo(\"Changing working dir to %s\", DataTestDirPath2);\r\n    LxtCheckErrno(FileDescriptor = open(DataTestDirPath2, O_RDONLY | O_DIRECTORY, 0));\r\n    LxtCheckErrno(fchdir(FileDescriptor));\r\n    LxtCheckClose(FileDescriptor);\r\n\r\n    //\r\n    // Open the same file now that the working directory is different.\r\n    //\r\n\r\n    LxtLogInfo(\"Opening file using path %s\", TestFilePath2);\r\n    LxtCheckErrno(FileDescriptor = open(TestFilePath2, O_RDWR));\r\n    LxtCheckClose(FileDescriptor);\r\n    LxtLogInfo(\"Opened file using path %s successfully!\", TestFilePath2);\r\n\r\n    //\r\n    // Change working directory to a bogus fd; this should fail.\r\n    //\r\n\r\n    LxtLogInfo(\"Changing working dir to %s\", FailureDirPath);\r\n    LxtCheckErrnoFailure(fchdir(-1), EBADF);\r\n    LxtLogInfo(\"Changing working dir to %s failed as expected\", FailureDirPath);\r\n\r\n    Result = 0;\r\n\r\n    LxtLogInfo(\"TEST SUCCESSFUL!\");\r\n\r\nErrorExit:\r\n    if (FileDescriptor != -1)\r\n    {\r\n        close(FileDescriptor);\r\n    }\r\n\r\n    unlink(FS_TEST_DIR_PARENT \"/test_chdir.txt\");\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestMknod(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests creation of device nodes using mknod.\r\n\r\n    N.B. Creation of fifos is covered by the pipe unit tests, and other types\r\n         of files are sufficiently covered by the LTP tests.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[10];\r\n    ssize_t BytesRead;\r\n    int Fd;\r\n    struct stat FileStat;\r\n    int Result;\r\n    struct stat Stat;\r\n    char Zero[10];\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Test basic device node creation.\r\n    //\r\n\r\n    umask(0);\r\n    LxtCheckErrnoZeroSuccess(mknod(FS_MKNOD_TEST_FILE, S_IFCHR | 0666, makedev(1, 5)));\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(FS_MKNOD_TEST_FILE, &Stat));\r\n    LxtCheckEqual(Stat.st_mode, S_IFCHR | 0666, \"0%o\");\r\n    LxtCheckNotEqual(Stat.st_ino, 0, \"%llu\");\r\n    LxtCheckEqual(Stat.st_rdev, makedev(1, 5), \"0x%x\");\r\n    LxtCheckNotEqual(Stat.st_rdev, Stat.st_dev, \"0x%x\");\r\n\r\n    //\r\n    // Test using the device node.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_MKNOD_TEST_FILE, O_RDONLY));\r\n    memset(Zero, 0, sizeof(Buffer));\r\n    memset(Buffer, 1, sizeof(Buffer));\r\n    LxtCheckErrno(BytesRead = read(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(BytesRead, sizeof(Buffer), \"%d\");\r\n    LxtCheckMemoryEqual(Buffer, Zero, sizeof(Buffer));\r\n\r\n    //\r\n    // Check the fd's inode matches the stat results.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &FileStat));\r\n    LxtCheckEqual(FileStat.st_ino, Stat.st_ino, \"%llu\");\r\n    LxtCheckEqual(FileStat.st_dev, Stat.st_dev, \"0x%x\");\r\n    LxtCheckEqual(FileStat.st_rdev, Stat.st_rdev, \"0x%x\");\r\n    LxtCheckEqual(FileStat.st_mode, Stat.st_mode, \"0%o\");\r\n\r\n    //\r\n    // Check the fd's path follows renames.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckFdPath(Fd, FS_MKNOD_TEST_FILE));\r\n    LxtCheckErrnoZeroSuccess(rename(FS_MKNOD_TEST_FILE, FS_MKNOD_TEST_FILE2));\r\n    LxtCheckResult(LxtCheckFdPath(Fd, FS_MKNOD_TEST_FILE2));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check opening with O_PATH.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_MKNOD_TEST_FILE2, O_PATH));\r\n    LxtCheckErrnoFailure(read(Fd, Buffer, sizeof(Buffer)), EBADF);\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &FileStat));\r\n    LxtCheckEqual(FileStat.st_ino, Stat.st_ino, \"%llu\");\r\n    LxtCheckEqual(FileStat.st_dev, Stat.st_dev, \"0x%x\");\r\n    LxtCheckEqual(FileStat.st_rdev, Stat.st_rdev, \"0x%x\");\r\n    LxtCheckEqual(FileStat.st_mode, Stat.st_mode, \"0%o\");\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_MKNOD_TEST_FILE2));\r\n\r\n    //\r\n    // Check mknod applies the umask.\r\n    //\r\n\r\n    umask(022);\r\n    LxtCheckErrnoZeroSuccess(mknod(FS_MKNOD_TEST_FILE, S_IFCHR | 0666, makedev(1, 5)));\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(FS_MKNOD_TEST_FILE, &Stat));\r\n    LxtCheckEqual(Stat.st_mode, S_IFCHR | 0644, \"0%o\");\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_MKNOD_TEST_FILE));\r\n\r\n    //\r\n    // Create a device with a non-existing major number.\r\n    //\r\n    // N.B. This test could fail on real Linux if at any point a device is\r\n    //      added with this number.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mknod(FS_MKNOD_TEST_FILE, S_IFCHR | 0666, makedev(200, 0)));\r\n\r\n    LxtCheckErrnoFailure(open(FS_MKNOD_TEST_FILE, O_RDONLY), ENXIO);\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_MKNOD_TEST_FILE));\r\n\r\n    //\r\n    // Existing major number, non-existing minor number.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mknod(FS_MKNOD_TEST_FILE, S_IFCHR | 0666, makedev(1, 200)));\r\n\r\n    LxtCheckErrnoFailure(open(FS_MKNOD_TEST_FILE, O_RDONLY), ENXIO);\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_MKNOD_TEST_FILE));\r\n\r\n    //\r\n    // Major number 10 returns different error code for unknown devices.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mknod(FS_MKNOD_TEST_FILE, S_IFCHR | 0666, makedev(10, 100)));\r\n\r\n    LxtCheckErrnoFailure(open(FS_MKNOD_TEST_FILE, O_RDONLY), ENODEV);\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_MKNOD_TEST_FILE));\r\n\r\n    //\r\n    // Nonexistent block device.\r\n    //\r\n    // N.B. Currently, no block devices exist in WSL.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mknod(FS_MKNOD_TEST_FILE, S_IFBLK | 0666, makedev(200, 0)));\r\n\r\n    LxtCheckErrnoFailure(open(FS_MKNOD_TEST_FILE, O_RDONLY), ENXIO);\r\n    LxtCheckErrnoZeroSuccess(unlink(FS_MKNOD_TEST_FILE));\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(FS_MKNOD_TEST_FILE);\r\n    unlink(FS_MKNOD_TEST_FILE2);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestMknodSecurity(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether mknod correctly checks capabilities.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    pid_t ChildPid;\r\n    int Result;\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Drop the CAP_MKNOD capability.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapGet(&CapHeader, CapData)) CapData[CAP_TO_INDEX(CAP_MKNOD)].effective &= ~CAP_TO_MASK(CAP_MKNOD);\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Creating devices should fail.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(mknod(FS_MKNOD_TEST_FILE, S_IFCHR | 0666, makedev(1, 5)), EPERM);\r\n\r\n        LxtCheckErrnoFailure(mknod(FS_MKNOD_TEST_FILE, S_IFBLK | 0666, makedev(1, 5)), EPERM);\r\n\r\n        //\r\n        // Other file types should still succeed.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(mknod(FS_MKNOD_TEST_FILE, S_IFREG | 0666, 0));\r\n        LxtCheckErrnoZeroSuccess(unlink(FS_MKNOD_TEST_FILE));\r\n        LxtCheckErrnoZeroSuccess(mknod(FS_MKNOD_TEST_FILE, S_IFIFO | 0666, 0));\r\n        LxtCheckErrnoZeroSuccess(unlink(FS_MKNOD_TEST_FILE));\r\n        LxtCheckErrnoZeroSuccess(mknod(FS_MKNOD_TEST_FILE, S_IFSOCK | 0666, 0));\r\n        LxtCheckErrnoZeroSuccess(unlink(FS_MKNOD_TEST_FILE));\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    unlink(FS_MKNOD_TEST_FILE);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestNoatimeFlag(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    int Bytes;\r\n    char* DentsBuffer = NULL;\r\n    int DentsBufferSize = 2 * 1024 * 1024;\r\n    int FileDescriptor = -1;\r\n    int Result;\r\n    const char SourceGetdents[] = FS_TEST_DIR_PARENT \"/\";\r\n    const char SourceOpen[] = FS_TEST_DIR_PARENT \"/fs_access_time_test.bin\";\r\n    const char Content[] = \"I am your father! Noooo!\";\r\n    struct iovec Iov;\r\n    char SingleEntry[100];\r\n    int SingleEntrySize;\r\n    struct stat StatA;\r\n    struct stat StatB;\r\n\r\n    //\r\n    // Plan 9 and virtiofs do not forward O_NOATIME to the server.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9 || g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)\r\n    {\r\n        LxtLogInfo(\"This test is not supported for plan9 or virtiofs.\");\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Create the test file; this should succeed.\r\n    //\r\n\r\n    LxtCheckErrno(FileDescriptor = open(SourceOpen, O_RDWR | O_CREAT, S_IRWXU));\r\n\r\n    //\r\n    // Test O_NOATIME for read no access time changes.\r\n    //\r\n\r\n    LxtCheckErrno(fstat(FileDescriptor, &StatA));\r\n    LxtCheckErrno(close(FileDescriptor));\r\n    LxtCheckErrno(FileDescriptor = open(SourceOpen, O_RDWR));\r\n    LxtCheckErrno(fstat(FileDescriptor, &StatB));\r\n    LxtCheckMemoryEqual(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim));\r\n\r\n    usleep(10 * 1000);\r\n    LxtCheckErrno(Bytes = write(FileDescriptor, Content, sizeof(Content)));\r\n    LxtCheckEqual(Bytes, sizeof(Content), \"%d\");\r\n    LxtCheckErrno(fstat(FileDescriptor, &StatB));\r\n\r\n    //\r\n    // NTFS updates the atime when other timestamps are updated, even when\r\n    // O_NOATIME is specified.\r\n    //\r\n\r\n    if (g_LxtFsInfo.Flags.DrvFsBehavior == 0)\r\n    {\r\n        LxtCheckMemoryEqual(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim));\r\n    }\r\n\r\n    LxtCheckErrno(close(FileDescriptor));\r\n    LxtCheckErrno(FileDescriptor = open(SourceOpen, O_RDWR));\r\n    memset(&Buffer, 0, sizeof(Buffer));\r\n    usleep(10 * 1000);\r\n    LxtCheckErrno(Bytes = read(FileDescriptor, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Bytes, sizeof(Content), \"%d\");\r\n\r\n    //\r\n    // Close first; with DrvFs, in case NTFS has atime updates enabled it\r\n    // won't do it until the handle is closed.\r\n    //\r\n    // TODO_LX: Once NTFS timestamp updating is fixed, change this back.\r\n    //\r\n\r\n    LxtCheckErrno(close(FileDescriptor));\r\n    LxtCheckErrno(stat(SourceOpen, &StatA));\r\n\r\n    //\r\n    // TODO_LX: Uncomment when the file system is mounted without noatime.\r\n    //\r\n    // if (memcmp(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim)) == 0) {\r\n    //     LxtLogError(\"Access time was supposed to be updated.\");\r\n    //     Result = -1;\r\n    //     goto ErrorExit;\r\n    // }\r\n    //\r\n\r\n    LxtCheckErrno(FileDescriptor = open(SourceOpen, O_RDWR | O_NOATIME));\r\n    memset(&Buffer, 0, sizeof(Buffer));\r\n    usleep(10 * 1000);\r\n    LxtCheckErrno(Bytes = read(FileDescriptor, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Bytes, sizeof(Content), \"%d\");\r\n    LxtCheckErrno(fstat(FileDescriptor, &StatB));\r\n    LxtCheckMemoryEqual(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim));\r\n\r\n    LxtCheckErrno(close(FileDescriptor));\r\n    LxtCheckErrno(stat(SourceOpen, &StatB));\r\n    LxtCheckMemoryEqual(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim));\r\n\r\n    FileDescriptor = -1;\r\n\r\n    //\r\n    // Test O_NOATIME for readv no access time changes.\r\n    //\r\n\r\n    LxtCheckErrno(FileDescriptor = open(SourceOpen, O_RDWR | O_NOATIME));\r\n    Iov.iov_base = Buffer;\r\n    Iov.iov_len = sizeof(Buffer);\r\n    usleep(10 * 1000);\r\n    LxtCheckErrno(Bytes = readv(FileDescriptor, &Iov, 1));\r\n    LxtCheckEqual(Bytes, sizeof(Content), \"%d\");\r\n    LxtCheckErrno(fstat(FileDescriptor, &StatB));\r\n    LxtCheckMemoryEqual(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim));\r\n\r\n    LxtCheckErrno(close(FileDescriptor));\r\n    LxtCheckErrno(stat(SourceOpen, &StatB));\r\n    LxtCheckMemoryEqual(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim));\r\n\r\n    FileDescriptor = -1;\r\n\r\n    //\r\n    // Test O_NOATIME for getdents no access time changes.\r\n    //\r\n\r\n    rmdir(LXT_GET_DENTS_FOLDER);\r\n\r\n    //\r\n    // Check the expected getdents results for each directory;\r\n    //\r\n\r\n    DentsBuffer = malloc(DentsBufferSize);\r\n    if (DentsBuffer == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"malloc\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(mkdir(LXT_GET_DENTS_FOLDER, 0777));\r\n    memset(DentsBuffer, 1, DentsBufferSize);\r\n    LxtCheckErrno(FileDescriptor = open(SourceGetdents, O_RDONLY | O_DIRECTORY));\r\n\r\n    LxtCheckErrno(fstat(FileDescriptor, &StatA));\r\n    usleep(10 * 1000);\r\n    LxtCheckErrno(Bytes = LxtGetdents64(FileDescriptor, DentsBuffer, DentsBufferSize));\r\n\r\n    if (Bytes == 0)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"BytesRead == 0\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(fstat(FileDescriptor, &StatB));\r\n\r\n    //\r\n    // TODO_LX: Uncomment when the file system is mounted without noatime.\r\n    //\r\n    // if (memcmp(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim)) == 0) {\r\n    //     LxtLogError(\"Access time was supposed to be updated.\");\r\n    //     Result = -1;\r\n    //     goto ErrorExit;\r\n    // }\r\n    //\r\n\r\n    LxtCheckErrno(close(FileDescriptor));\r\n    FileDescriptor = -1;\r\n    LxtCheckErrno(FileDescriptor = open(SourceGetdents, O_RDONLY | O_DIRECTORY | O_NOATIME));\r\n\r\n    usleep(100 * 1000);\r\n    LxtCheckErrno(Bytes = LxtGetdents64(FileDescriptor, DentsBuffer, DentsBufferSize));\r\n\r\n    if (Bytes == 0)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"BytesRead == 0\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(fstat(FileDescriptor, &StatA));\r\n    LxtCheckMemoryEqual(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim));\r\n\r\n    LxtCheckClose(FileDescriptor);\r\n    LxtCheckErrno(stat(SourceGetdents, &StatA));\r\n    LxtCheckMemoryEqual(&StatA.st_atim, &StatB.st_atim, sizeof(StatA.st_atim));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    if (FileDescriptor != -1)\r\n    {\r\n        close(FileDescriptor);\r\n    }\r\n\r\n    if (DentsBuffer != NULL)\r\n    {\r\n        free(DentsBuffer);\r\n    }\r\n\r\n    unlink(SourceOpen);\r\n    rmdir(LXT_GET_DENTS_FOLDER);\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestWritev(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    //\r\n    // This test doesn't pass on real Linux, so it's skipped for VM mode.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9)\r\n    {\r\n        LxtLogInfo(\"Skipping writev test in VM mode.\");\r\n        return LXT_RESULT_SUCCESS;\r\n    }\r\n\r\n    return LxtFsWritevCommon(FS_TEST_DIR_PARENT \"/fs_writev_test.bin\");\r\n}\r\n\r\nint FsCommonTestDeviceId(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests that mounts have unique device id's.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    dev_t DevDeviceId;\r\n    dev_t ProcDeviceId;\r\n    int Result;\r\n    dev_t RootDeviceId;\r\n    struct stat Stat;\r\n\r\n    //\r\n    // Test that various directories device id's are reported correctly.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(\"/\", &Stat));\r\n    RootDeviceId = Stat.st_dev;\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(\"/proc\", &Stat));\r\n    ProcDeviceId = Stat.st_dev;\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(\"/dev\", &Stat));\r\n    DevDeviceId = Stat.st_dev;\r\n\r\n    LxtLogInfo(\"DeviceId's: / = %lld /proc = %lld /dev = %lld\", RootDeviceId, ProcDeviceId, DevDeviceId);\r\n\r\n    if ((RootDeviceId == ProcDeviceId) || (RootDeviceId == DevDeviceId) || (ProcDeviceId == DevDeviceId))\r\n    {\r\n\r\n        LxtLogError(\"Detected non-unique device id's\");\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestFallocate(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the fallocate system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    dev_t ProcDeviceId;\r\n    int Result;\r\n    dev_t RootDeviceId;\r\n    struct stat Stat;\r\n\r\n    unlink(FS_FALLOCATE_TEST_FILE);\r\n    LxtCheckErrno(Fd = creat(FS_FALLOCATE_TEST_FILE, 0666));\r\n\r\n    //\r\n    // Plan 9 and virtiofs do not support fallocate.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9 || g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)\r\n    {\r\n        LxtCheckErrnoFailure(fallocate(Fd, 0, 0, 1024), ENOTSUP);\r\n        LxtLogInfo(\"Fallocate is not supported on Plan 9.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Allocate some space.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fallocate(Fd, 0, 0, 1024));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 1024, \"%lld\");\r\n    LxtCheckGreaterOrEqual(Stat.st_blocks, 2, \"%ld\");\r\n\r\n    //\r\n    // Don't change the length.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fallocate(Fd, FALLOC_FL_KEEP_SIZE, 0, 16384));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 1024, \"%lld\");\r\n    LxtCheckGreaterOrEqual(Stat.st_blocks, 32, \"%ld\");\r\n\r\n    //\r\n    // Fallocate won't shrink the file.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fallocate(Fd, 0, 0, 512));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat));\r\n    LxtCheckEqual(Stat.st_size, 1024, \"%lld\");\r\n    LxtCheckGreaterOrEqual(Stat.st_blocks, 32, \"%ld\");\r\n\r\n    //\r\n    // Attempt to make the file very very large.\r\n    //\r\n    // N.B. On some machines with very large hard drives (larger than 1TB) this\r\n    //      can succeed.\r\n    //\r\n\r\n    Result = fallocate(Fd, 0, 0, 0xffffffffff);\r\n    if (Result < 0)\r\n    {\r\n        LxtCheckErrnoFailure(Result, ENOSPC);\r\n    }\r\n\r\nErrorExit:\r\n    unlink(FS_FALLOCATE_TEST_FILE);\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestDirSeek(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the seek operation on directory.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    LxtCheckResult(LxtFsDirSeekCommon(LXT_GET_DENTS_FOLDER));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint FsCommonTestFsync(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the fsync system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    struct stat St;\r\n\r\n    Fd = -1;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(FS_FSYNC_TEST_DIR, 0777));\r\n    LxtCheckErrno(Fd = creat(FS_FSYNC_TEST_DIR \"/testfile\", 0666));\r\n    LxtCheckErrnoZeroSuccess(fsync(Fd));\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Open the file as read-only and attempt to call fsync on it.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_FSYNC_TEST_DIR \"/testfile\", O_RDONLY));\r\n    LxtCheckErrnoZeroSuccess(fsync(Fd));\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Create a file with no write access and call fsync on it.\r\n\r\n    LxtCheckErrno(Fd = creat(FS_FSYNC_TEST_DIR \"/testfile2\", 0444));\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &St));\r\n    LxtCheckEqual(St.st_mode, S_IFREG | 0444, \"0%o\");\r\n    LxtCheckErrnoZeroSuccess(fsync(Fd));\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Open that file as read-only and attempt to call fsync on it.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(FS_FSYNC_TEST_DIR \"/testfile2\", O_RDONLY));\r\n    LxtCheckErrnoZeroSuccess(fsync(Fd));\r\n    LxtCheckClose(Fd);\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(FS_FSYNC_TEST_DIR \"/testfile2\");\r\n    unlink(FS_FSYNC_TEST_DIR \"/testfile\");\r\n    rmdir(FS_FSYNC_TEST_DIR);\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/fstab.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    fstab.c\r\n\r\nAbstract:\r\n\r\n    This file contains tests for fstab mounting.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <libmount/libmount.h>\r\n\r\n#define LXT_NAME \"fstab\"\r\n\r\nLXT_VARIATION_HANDLER FsTabTestMount;\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {{\"FsTab - DrvFs mounted through fstab\", FsTabTestMount}};\r\n\r\nint FstabTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine main entry point for the wslpath tests.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint FsTabTestMount(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests whether fstab mounting was performed correctly.\r\n\r\n    N.B. This test should be run after changing the /etc/fstab file and\r\n         restarting the instance.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct libmnt_fs* FileSystem;\r\n    bool Found;\r\n    const char* FsType;\r\n    struct libmnt_iter* Iterator;\r\n    const char* Options;\r\n    int Result;\r\n    const char* Source;\r\n    struct libmnt_table* Table;\r\n\r\n    Iterator = NULL;\r\n    Table = mnt_new_table_from_file(\"/proc/self/mountinfo\");\r\n    LxtCheckNotEqual(Table, NULL, \"%p\");\r\n    Iterator = mnt_new_iter(MNT_ITER_FORWARD);\r\n    LxtCheckNotEqual(Iterator, NULL, \"%p\");\r\n    Found = false;\r\n    while (mnt_table_next_fs(Table, Iterator, &FileSystem) == 0)\r\n    {\r\n        FsType = mnt_fs_get_fstype(FileSystem);\r\n        Options = mnt_fs_get_fs_options(FileSystem);\r\n\r\n        //\r\n        // Check that there is only one mount for C: (or any variation therefore, like C:\\ or c:),\r\n        // and that its mount uses the exact options specified in fstab.\r\n        //\r\n\r\n        if (strcmp(FsType, \"9p\") == 0)\r\n        {\r\n            if (strcasestr(Options, \"aname=drvfs;path=C:\") != NULL)\r\n            {\r\n                LxtCheckTrue(!Found);\r\n                LxtCheckNotEqual(strstr(Options, \"aname=drvfs;path=C:\\\\;metadata;\"), NULL, \"%p\");\r\n                Found = true;\r\n            }\r\n        }\r\n        else if (strcmp(FsType, \"drvfs\") == 0)\r\n        {\r\n            Source = mnt_fs_get_source(FileSystem);\r\n            if (strcasestr(Source, \"C:\") == Source)\r\n            {\r\n                LxtCheckTrue(!Found);\r\n                LxtCheckStringEqual(Source, \"C:\\\\\");\r\n                LxtCheckStringEqual(Options, \"rw,metadata,case=off\");\r\n                Found = true;\r\n            }\r\n        }\r\n        else if (strcmp(FsType, \"virtiofs\") == 0)\r\n        {\r\n            Source = mnt_fs_get_source(FileSystem);\r\n            if (strcasestr(Source, \"drvfsaC\") == Source)\r\n            {\r\n                LxtCheckTrue(!Found);\r\n                LxtCheckStringEqual(Options, \"rw\");\r\n                Found = true;\r\n            }\r\n        }\r\n    }\r\n\r\n    LxtCheckTrue(Found);\r\n\r\nErrorExit:\r\n    if (Iterator != NULL)\r\n    {\r\n        mnt_free_iter(Iterator);\r\n    }\r\n\r\n    if (Table != NULL)\r\n    {\r\n        mnt_free_table(Table);\r\n    }\r\n\r\n    return Result;\r\n}"
  },
  {
    "path": "test/linux/unit_tests/get_set_id.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    get_set_id.c\r\n\r\nAbstract:\r\n\r\n    This file is a test for the get*id and set*id system calls.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdio.h>\r\n#include <errno.h>\r\n#include <sys/types.h>\r\n#include <sys/socket.h>\r\n#include <sys/syscall.h>\r\n#include <sys/wait.h>\r\n#include <sys/un.h>\r\n#include <limits.h>\r\n\r\n#define LXT_NAME \"get_set_id\"\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n\r\n#define __NR_getuid16 24\r\n#define __NR_geteuid16 49\r\n#define __NR_getgid16 47\r\n#define __NR_getegid16 50\r\n#define __NR_getresuid16 165\r\n#define __NR_getresgid16 171\r\n\r\nint GetSetId16Bit(PLXT_ARGS Args);\r\n\r\n#endif\r\n\r\ntypedef unsigned short USHORT;\r\n\r\n//\r\n// uid16_t and gid16_t are unsigned 16-bit integers.\r\n//\r\n\r\ntypedef USHORT uid16_t;\r\n\r\n#define MAX_UID16_T USHRT_MAX\r\n\r\ntypedef USHORT gid16_t;\r\n\r\n#define MAX_GID16_T USHRT_MAX\r\n\r\nint GetSetId0(PLXT_ARGS Args);\r\n\r\nint GetSetId1(PLXT_ARGS Args);\r\n\r\nint GetSetIdParseArgs(int Argc, char* Argv[], LXT_ARGS* Args);\r\n\r\nint GetSetPgid(PLXT_ARGS Args);\r\n\r\nint GetSetPgidChildProcess(void);\r\n\r\nint GetSetPgidChildProcess2(pid_t GroupId);\r\n\r\nint GetSetPgidExecve(PLXT_ARGS Args);\r\n\r\nint GetSetPgidExecveChild(void);\r\n\r\nint GetSetSid(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"GetSetId Basic\", GetSetId0},\r\n    {\"GetResuid-GetResgid Basic\", GetSetId1},\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n    {\"GetSetId 16-bit versions\", GetSetId16Bit},\r\n#endif\r\n    {\"GetSetPgid Basic\", GetSetPgid},\r\n    {\"GetSetPgid with execve\", GetSetPgidExecve},\r\n    {\"GetSetSid Basic\", GetSetSid}};\r\n\r\nstatic const char* g_SocketPath = \"/data/test/lxt_get_set_id_sock\";\r\n\r\nint GetSetIdTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine main entry point for the test for get*id,set*id system call.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(GetSetIdParseArgs(Argc, Argv, &Args));\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint GetSetId0(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the various get*id and set*id system\r\n    calls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    gid_t Egid;\r\n    gid_t EgidFromGetResgid;\r\n    uid_t Euid;\r\n    uid_t EuidFromGetResuid;\r\n    gid_t Gid;\r\n    gid_t GidFromGetResgid;\r\n    pid_t Pgid;\r\n    pid_t Pgid0;\r\n    pid_t Pid;\r\n    pid_t Ppid;\r\n    int Result;\r\n    uid_t SuidFromGetResuid;\r\n    gid_t SgidFromGetResgid;\r\n    pid_t Tid;\r\n    uid_t Uid;\r\n    uid_t UidFromGetResuid;\r\n\r\n    Pid = getpid();\r\n    Ppid = getppid();\r\n    Gid = getgid();\r\n    Egid = getegid();\r\n    Uid = getuid();\r\n    Euid = geteuid();\r\n    Tid = gettid();\r\n    LxtCheckResult((Pgid0 = getpgid(0)));\r\n    LxtCheckResult((Pgid = getpgid(Pid)));\r\n    LxtCheckErrnoFailure(getpgid(-1), ESRCH);\r\n    LxtCheckResult(getresuid(&UidFromGetResuid, &EuidFromGetResuid, &SuidFromGetResuid));\r\n\r\n    LxtCheckResult(getresgid(&GidFromGetResgid, &EgidFromGetResgid, &SgidFromGetResgid));\r\n\r\n    LxtLogInfo(\r\n        \"ID Details. Pid:%d, Ppid:%d, Gid:%u, Egid:%u, Uid:%u, \"\r\n        \"Euid:%u, Tid:%u, Pgid:%d, Uid From GetResuid:%u, \"\r\n        \"Euid From GetResuid:%u, Suid From GetResuid:%u, Gid From \"\r\n        \"GetResgid:%u, Egid From GetResgid:%u, Sgid From GetResgid:%u\",\r\n        Pid,\r\n        Ppid,\r\n        Gid,\r\n        Egid,\r\n        Uid,\r\n        Euid,\r\n        Tid,\r\n        Pgid,\r\n        UidFromGetResuid,\r\n        EuidFromGetResuid,\r\n        SuidFromGetResuid,\r\n        GidFromGetResgid,\r\n        EgidFromGetResgid,\r\n        SgidFromGetResgid);\r\n\r\n    //\r\n    // getpgid(Pid) == getpgid(0)\r\n    //\r\n\r\n    if (Pgid != Pgid0)\r\n    {\r\n        LxtLogError(\r\n            \"getpgid(<self>) == getpgid(0). getpgid(<self>):%d, \"\r\n            \"getpgid(0): %d\",\r\n            Pgid,\r\n            Pgid0);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // For a single threaded process, Thread ID == Process ID\r\n    //\r\n\r\n    if (Pid != Tid)\r\n    {\r\n        LxtLogError(\r\n            \"For a single threaded process, Process ID == Thread ID.  \"\r\n            \"Process ID:%u, Thread ID:%u\",\r\n            Pid,\r\n            Tid);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // ID's from get*id and getresuid/getresgid should match\r\n    //\r\n\r\n    if (Uid != UidFromGetResuid)\r\n    {\r\n        LxtLogError(\r\n            \"UID from getuid and getresuid do not match.  \"\r\n            \"uid from getuid:%u, uid from getresuid:%u\",\r\n            Uid,\r\n            UidFromGetResuid);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (Euid != EuidFromGetResuid)\r\n    {\r\n        LxtLogError(\r\n            \"EUID from geteuid and getresuid do not match.  \"\r\n            \"euid from getuid:%u, euid from getresuid:%u\",\r\n            Euid,\r\n            EuidFromGetResuid);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (Gid != GidFromGetResgid)\r\n    {\r\n        LxtLogError(\r\n            \"GID from getgid and getresgid do not match.  \"\r\n            \"gid from getgid:%u, gid from getresgid:%u\",\r\n            Gid,\r\n            GidFromGetResgid);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if ((Egid != EgidFromGetResgid))\r\n    {\r\n        LxtLogError(\r\n            \"EGID from getegid and getresgid do not match.  \"\r\n            \"egid from getegid:%u, egid from getresgid:%u\",\r\n            Egid,\r\n            EgidFromGetResgid);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint GetSetId1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the GetResuid/GetResgid system calls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    gid_t EgidFromGetResgid;\r\n    uid_t EuidFromGetResuid;\r\n    gid_t GidFromGetResgid;\r\n    int Result;\r\n    uid_t SuidFromGetResuid;\r\n    gid_t SgidFromGetResgid;\r\n    uid_t UidFromGetResuid;\r\n\r\n    LxtCheckErrnoFailure(getresuid(NULL, NULL, NULL), EFAULT);\r\n\r\n    LxtCheckErrnoFailure(getresuid(&UidFromGetResuid, NULL, NULL), EFAULT);\r\n\r\n    LxtCheckErrnoFailure(getresuid(NULL, &EuidFromGetResuid, &SuidFromGetResuid), EFAULT);\r\n\r\n    LxtCheckResult(getresuid(&UidFromGetResuid, &EuidFromGetResuid, &SuidFromGetResuid));\r\n    LxtCheckErrnoFailure(getresgid(NULL, NULL, NULL), EFAULT);\r\n\r\n    LxtCheckErrnoFailure(getresgid(&GidFromGetResgid, NULL, NULL), EFAULT);\r\n\r\n    LxtCheckErrnoFailure(getresgid(NULL, &EgidFromGetResgid, &SgidFromGetResgid), EFAULT);\r\n\r\n    LxtCheckResult(getresgid(&GidFromGetResgid, &EgidFromGetResgid, &SgidFromGetResgid));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n\r\nint GetSetId16Bit(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the various 16-bit versions of the\r\n    get*id and set*id system calls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    gid_t Egid;\r\n    gid16_t Egid16;\r\n    gid16_t EgidFromGetResgid16;\r\n    uid_t Euid;\r\n    uid16_t Euid16;\r\n    uid16_t EuidFromGetResuid16;\r\n    gid_t Gid;\r\n    gid16_t Gid16;\r\n    gid16_t GidFromGetResgid16;\r\n    int Result;\r\n    uid16_t SuidFromGetResuid16;\r\n    gid16_t SgidFromGetResgid16;\r\n    uid_t Uid;\r\n    uid16_t Uid16;\r\n    uid16_t UidFromGetResuid16;\r\n\r\n    Gid = getgid();\r\n    Gid16 = 0;\r\n    Egid = getegid();\r\n    Uid = getuid();\r\n    Uid16 = 0;\r\n    Euid = geteuid();\r\n\r\n    LxtLogInfo(\"UID and GID Details. Gid:%d, Egid:%d, Uid:%u, Euid:%u\", Gid, Egid, Uid, Euid);\r\n\r\n    //\r\n    // Before calling the 16-bit versions, make sure the IDs are\r\n    // within the range.\r\n    //\r\n\r\n    if (Gid <= MAX_GID16_T)\r\n    {\r\n        Gid16 = syscall(__NR_getgid16);\r\n        if (Gid != Gid16)\r\n        {\r\n            LxtLogError(\r\n                \"GID from getgid and getgid16 do not match.  \"\r\n                \"gid from getgid:%d, gid from getgid16:%d\",\r\n                Gid,\r\n                Gid16);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    if (Egid <= MAX_GID16_T)\r\n    {\r\n        Egid16 = syscall(__NR_getegid16);\r\n        if (Egid != Egid16)\r\n        {\r\n            LxtLogError(\r\n                \"EGID from getegid and getegid16 do not match.  \"\r\n                \"egid from getegid:%d, egid from getegid16:%d\",\r\n                Egid,\r\n                Egid16);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    if ((Gid <= MAX_GID16_T) && (Egid <= MAX_GID16_T))\r\n    {\r\n        LxtCheckResult(syscall(__NR_getresgid16, &GidFromGetResgid16, &EgidFromGetResgid16, &SgidFromGetResgid16));\r\n\r\n        LxtLogInfo(\"SGID16:%d\", SgidFromGetResgid16);\r\n\r\n        if (Gid16 != GidFromGetResgid16)\r\n        {\r\n            LxtLogError(\r\n                \"GID from getgid16 and getresgid16 do not match.  \"\r\n                \"gid from getgid16:%d, gid from getresgid16:%d\",\r\n                Gid16,\r\n                GidFromGetResgid16);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if ((Egid16 != EgidFromGetResgid16))\r\n        {\r\n            LxtLogError(\r\n                \"EGID from getegid16 and getresgid16 do not match.  \"\r\n                \"egid from getegid16:%d, egid from getresgid16:%d\",\r\n                Egid16,\r\n                EgidFromGetResgid16);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtCheckErrnoFailure(syscall(__NR_getresgid16, NULL, NULL, NULL), EFAULT);\r\n\r\n        LxtCheckErrnoFailure(syscall(__NR_getresgid16, &GidFromGetResgid16, NULL, NULL), EFAULT);\r\n\r\n        LxtCheckErrnoFailure(syscall(__NR_getresgid16, NULL, &EgidFromGetResgid16, &SgidFromGetResgid16), EFAULT);\r\n    }\r\n\r\n    if (Uid <= MAX_UID16_T)\r\n    {\r\n        Uid16 = syscall(__NR_getuid16);\r\n        if (Uid != Uid16)\r\n        {\r\n            LxtLogError(\r\n                \"UID from getuid and getuid16 do not match.  \"\r\n                \"uid from getuid:%d, uid from getuid16:%d\",\r\n                Uid,\r\n                Uid16);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    if (Euid <= MAX_UID16_T)\r\n    {\r\n        Euid16 = syscall(__NR_geteuid16);\r\n        if (Euid != Euid16)\r\n        {\r\n            LxtLogError(\r\n                \"EUID from geteuid and geteuid16 do not match.  \"\r\n                \"egid from geteuid:%d, egid from geteuid16:%d\",\r\n                Euid,\r\n                Euid16);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    if ((Uid <= MAX_UID16_T) && (Euid <= MAX_UID16_T))\r\n    {\r\n        LxtCheckResult(syscall(__NR_getresuid16, &UidFromGetResuid16, &EuidFromGetResuid16, &SuidFromGetResuid16));\r\n\r\n        LxtLogInfo(\"SUID16:%d\", SuidFromGetResuid16);\r\n\r\n        if (Uid16 != UidFromGetResuid16)\r\n        {\r\n            LxtLogError(\r\n                \"UID from getuid16 and getresuid16 do not match.  \"\r\n                \"uid from getuid16:%d, uid from getresuid16:%d\",\r\n                Uid16,\r\n                UidFromGetResuid16);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (Euid16 != EuidFromGetResuid16)\r\n        {\r\n            LxtLogError(\r\n                \"EUID from geteuid16 and getresuid16 do not match.  \"\r\n                \"euid from getuid16:%d, euid from getresuid16:%d\",\r\n                Euid16,\r\n                EuidFromGetResuid16);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtCheckErrnoFailure(syscall(__NR_getresuid16, NULL, NULL, NULL), EFAULT);\r\n\r\n        LxtCheckErrnoFailure(syscall(__NR_getresuid16, &UidFromGetResuid16, NULL, NULL), EFAULT);\r\n\r\n        LxtCheckErrnoFailure(syscall(__NR_getresuid16, NULL, &EuidFromGetResuid16, &SuidFromGetResuid16), EFAULT);\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\n#endif\r\n\r\nint GetSetIdParseArgs(int Argc, char* Argv[], LXT_ARGS* Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine parses command line arguments for the get_set_id tests.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of arguments.\r\n\r\n    Argv - Supplies an array of arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ArgvIndex;\r\n    int Result;\r\n    int ValidArguments;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    ValidArguments = 0;\r\n\r\n    if (Argc < 1)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (ArgvIndex = 1; ArgvIndex < Argc; ++ArgvIndex)\r\n    {\r\n        if (Argv[ArgvIndex][0] != '-')\r\n        {\r\n            printf(\"Unexpected character %s\", Argv[ArgvIndex]);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        switch (Argv[ArgvIndex][1])\r\n        {\r\n        case 'c':\r\n\r\n            //\r\n            // Run the setpgid execve test child\r\n            //\r\n\r\n            ValidArguments = 1;\r\n            Result = GetSetPgidExecveChild();\r\n            goto ErrorExit;\r\n\r\n        case 'v':\r\n\r\n            //\r\n            // This was already taken care of by LxtInitialize.\r\n            //\r\n\r\n            ++ArgvIndex;\r\n\r\n            break;\r\n\r\n        default:\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // If -c was not specified, just run the tests\r\n    //\r\n\r\n    ValidArguments = 1;\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    if (ValidArguments == 0)\r\n    {\r\n        printf(\"\\nuse: get_set_id <One of the below arguments>\");\r\n        printf(\"\\t-c : Run getsetpgid execve test child (don't use directly)\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint GetSetPgid(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the getpgid and setpgid system calls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    const int Processes = 2;\r\n\r\n    pid_t Child;\r\n    pid_t GroupId;\r\n    pid_t NewGroup;\r\n    int Process;\r\n    pid_t ProcessIds[2];\r\n    int Result;\r\n    int Status;\r\n\r\n    //\r\n    // Check that a child process initially inherits our process group ID.\r\n    //\r\n\r\n    LxtCheckErrno(Child = fork());\r\n    if (Child == 0)\r\n    {\r\n        _exit(GetSetPgidChildProcess());\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(Child, 0));\r\n\r\n    //\r\n    // Create two processes; the first will be the group leader and the second\r\n    // will use the group id of the first. The method used here reflects how\r\n    // job control shells create groups for pipelines.\r\n    //\r\n\r\n    GroupId = 0;\r\n    for (Process = 0; Process < Processes; Process += 1)\r\n    {\r\n        LxtCheckErrno(Child = fork());\r\n        ProcessIds[Process] = Child;\r\n        if (Child == 0)\r\n        {\r\n            _exit(GetSetPgidChildProcess2(GroupId));\r\n        }\r\n\r\n        if (GroupId == 0)\r\n        {\r\n            GroupId = Child;\r\n        }\r\n\r\n        LxtCheckErrnoZeroSuccess(setpgid(Child, GroupId));\r\n        LxtCheckErrno(NewGroup = getpgid(Child));\r\n        if (NewGroup != GroupId)\r\n        {\r\n            LxtLogError(\r\n                \"getpgid() return value does not match the \"\r\n                \"value set by setpgid. Expected: %i; actual: %i\",\r\n                GroupId,\r\n                NewGroup);\r\n\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    for (Process = 0; Process < Processes; Process += 1)\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(ProcessIds[Process], 0));\r\n    }\r\n\r\n    //\r\n    // After the child processes were waited on, the process group is no longer\r\n    // valid, nor are the children pids. There is currently a race condition\r\n    // that causes waitpid to return before the process group is cleaned up\r\n    // so sleep before trying this.\r\n    //\r\n\r\n    sleep(1);\r\n    LxtCheckErrnoFailure(setpgid(0, GroupId), EPERM);\r\n    LxtCheckErrnoFailure(setpgid(ProcessIds[0], 0), ESRCH);\r\n    LxtCheckErrnoFailure(getpgid(ProcessIds[0]), ESRCH);\r\n\r\n    //\r\n    // Getpgid for non-child process should succeed, setpgid should fail\r\n    //\r\n\r\n    LxtCheckErrno(getpgid(getppid()));\r\n    LxtCheckErrnoFailure(setpgid(getppid(), 0), ESRCH);\r\n\r\n    //\r\n    // Cannot change process group of session leader.\r\n    //\r\n\r\n    LxtCheckResult(LxtSignalBlock(SIGUSR1));\r\n    LxtCheckErrno(Child = fork());\r\n    if (Child == 0)\r\n    {\r\n        LxtCheckErrno(setsid());\r\n        LxtCheckErrnoFailure(setpgid(0, getppid()), EPERM);\r\n        LxtCheckErrnoFailure(setpgid(0, 0), EPERM);\r\n        LxtCheckErrnoZeroSuccess(kill(getppid(), SIGUSR1));\r\n        LxtCheckResult(LxtSignalWaitBlocked(SIGUSR1, getppid(), 2));\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtSignalWaitBlocked(SIGUSR1, Child, 2));\r\n    LxtCheckErrnoFailure(setpgid(Child, 0), EPERM);\r\n    LxtCheckErrnoFailure(setpgid(Child, getpgid(0)), EPERM);\r\n\r\n    //\r\n    // Cannot change to process group which is in a different session.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(setpgid(0, Child), EPERM);\r\n\r\n    //\r\n    // Tell the child to exit.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(kill(Child, SIGUSR1));\r\n    LxtCheckResult(LxtWaitPidPoll(Child, 0));\r\n\r\n    //\r\n    // Bogus pid and pgid values. The fact that setpgid returns different\r\n    // errors for a negative pid if pgid==0 is consistent with Linux.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(getpgid(-1), ESRCH);\r\n    LxtCheckErrnoFailure(setpgid(-1, 1), ESRCH);\r\n    LxtCheckErrnoFailure(setpgid(-1, 0), EINVAL);\r\n    LxtCheckErrnoFailure(setpgid(0, -1), EINVAL);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint GetSetPgidChildProcess(void)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine runs the child process for GetSetPgid that checks its pgid.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t Parent;\r\n    pid_t ParentGroupId;\r\n    pid_t GroupId;\r\n    int Result;\r\n\r\n    Parent = getppid();\r\n    LxtCheckErrno(ParentGroupId = getpgid(Parent));\r\n    LxtCheckErrno(GroupId = getpgid(0));\r\n    LxtLogInfo(\"Process %i pgid: %i, parent %i pgid: %i\", getpid(), GroupId, Parent, ParentGroupId);\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    if (GroupId == 0)\r\n    {\r\n        LxtLogError(\"Group ID should never be zero.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (GroupId != ParentGroupId)\r\n    {\r\n        LxtLogError(\"Pgid %i did not match parent pgid %i\", GroupId, ParentGroupId);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint GetSetPgidChildProcess2(pid_t GroupId)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine runs the child processes for GetSetPgid that set their pgid.\r\n\r\nArguments:\r\n\r\n    GroupId - The process group ID for this child process (0 if this is the\r\n        new leader)\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int NewGroup;\r\n    int Pid;\r\n    int Result;\r\n    LxtCheckErrnoZeroSuccess(setpgid(0, GroupId));\r\n\r\n    //\r\n    // If we were passed 0, the expected result should match the process ID.\r\n    //\r\n\r\n    Pid = getpid();\r\n    if (GroupId == 0)\r\n    {\r\n        GroupId = Pid;\r\n    }\r\n\r\n    LxtCheckErrno(NewGroup = getpgid(0));\r\n    if (NewGroup != GroupId)\r\n    {\r\n        LxtLogError(\r\n            \"getpgid(0) return value does not match the value set by \"\r\n            \"setpgid. Expected: %i; actual: %i\",\r\n            GroupId,\r\n            NewGroup);\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Process %i pgid: %i\", Pid, NewGroup);\r\n\r\n    LxtCheckErrno(NewGroup = getpgid(Pid));\r\n    if (NewGroup != GroupId)\r\n    {\r\n        LxtLogError(\r\n            \"getpgid(getpid()) return value does not match the value \"\r\n            \"set by setpgid. Expected: %i; actual: %i\",\r\n            GroupId,\r\n            NewGroup);\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // The sleep is to keep the group alive until the second process\r\n    // runs. Remove it once zombie processes are implemented.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint GetSetPgidExecve(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks whether setpgid returns the correct failure if it is\r\n    called on a process that has already called execve.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_un Address;\r\n    int ClientSocket;\r\n    char* Argv[4];\r\n    char* Envp[1];\r\n    int Result;\r\n    int ServerSocket;\r\n    int Status;\r\n    int Child;\r\n\r\n    ClientSocket = -1;\r\n    Child = -1;\r\n\r\n    //\r\n    // To make sure we call setpgid after the child process runs execve and\r\n    // before it exits, we use a unix socket to let the child signal us when\r\n    // it's running, and then we signal the child to exit after the test.\r\n    //\r\n\r\n    LxtCheckErrno(ServerSocket = socket(AF_UNIX, SOCK_STREAM, 0));\r\n\r\n    //\r\n    // Remove the path name in case it exists from a failed prior test run;\r\n    // ignore errors.\r\n    //\r\n\r\n    unlink(g_SocketPath);\r\n\r\n    //\r\n    // Bind to the address and start listening\r\n    //\r\n\r\n    memset(&Address, 0, sizeof(Address));\r\n    Address.sun_family = AF_UNIX;\r\n    strncpy(Address.sun_path, g_SocketPath, sizeof(Address.sun_path) - 1);\r\n    Address.sun_path[sizeof(Address.sun_path) - 1] = 0;\r\n    LxtCheckErrnoZeroSuccess(bind(ServerSocket, (struct sockaddr*)&Address, sizeof(Address)));\r\n    LxtCheckErrnoZeroSuccess(listen(ServerSocket, 1));\r\n\r\n    //\r\n    // Start the child process.\r\n    //\r\n\r\n    LxtCheckErrno(Child = fork());\r\n    if (Child == 0)\r\n    {\r\n        Argv[0] = WSL_UNIT_TEST_BINARY; // adhere to new single test binary design\r\n        Argv[1] = Args->Argv[0];\r\n        Argv[2] = \"-c\";\r\n        Argv[3] = Envp[0] = NULL;\r\n        execve(Argv[0], Argv, Envp);\r\n        LxtLogError(\"Execve failed, errno: %d (%s)\", errno, strerror(errno));\r\n        _exit(LXT_RESULT_FAILURE);\r\n    }\r\n\r\n    //\r\n    // Wait for the client to tell us it's running; this guarantees we\r\n    // call setpgid after the execve call.\r\n    //\r\n\r\n    LxtCheckErrno(ClientSocket = accept(ServerSocket, NULL, NULL));\r\n    LxtCheckResult(LxtReceiveMessage(ClientSocket, \"execve\"));\r\n\r\n    //\r\n    // The child is now running inside the binary loaded by execve, so we\r\n    // can try to call setpgid, which should fail with EACCES.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(setpgid(Child, 0), EACCES);\r\n\r\n    //\r\n    // Tell the client it can exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtSendMessage(ClientSocket, \"exit\")) LxtCheckResult(LxtWaitPidPoll(Child, 0));\r\n\r\nErrorExit:\r\n    if (ClientSocket >= 0)\r\n    {\r\n        close(ClientSocket);\r\n    }\r\n\r\n    if (ServerSocket >= 0)\r\n    {\r\n        close(ServerSocket);\r\n    }\r\n\r\n    unlink(g_SocketPath);\r\n\r\n    return Result;\r\n}\r\n\r\nint GetSetPgidExecveChild(void)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine runs the child process for GetSetPgidExecve.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_un Address;\r\n    int ClientSocket;\r\n    int Result;\r\n\r\n    LxtLogInfo(\"Child executable running, pid = %i\", getpid());\r\n    LxtCheckErrno(ClientSocket = socket(AF_UNIX, SOCK_STREAM, 0));\r\n\r\n    //\r\n    // Connect to the parent process via AF_UNIX socket.\r\n    //\r\n\r\n    memset(&Address, 0, sizeof(Address));\r\n    Address.sun_family = AF_UNIX;\r\n    strncpy(Address.sun_path, g_SocketPath, sizeof(Address.sun_path) - 1);\r\n    Address.sun_path[sizeof(Address.sun_path) - 1] = 0;\r\n    LxtCheckErrnoZeroSuccess(connect(ClientSocket, (struct sockaddr*)&Address, sizeof(Address)));\r\n\r\n    //\r\n    // Tell the parent that the process is running inside the execve'd binary.\r\n    //\r\n\r\n    LxtCheckResult(LxtSendMessage(ClientSocket, \"execve\"));\r\n\r\n    //\r\n    // Wait for the parent to tell this process it can exit so it has the\r\n    // chance to call setpgid.\r\n    //\r\n\r\n    LxtCheckResult(LxtReceiveMessage(ClientSocket, \"exit\"));\r\n\r\n    //\r\n    // This process should be able to change its own process group.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(setpgid(0, 0));\r\n    LxtLogInfo(\"Child executable finished\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ClientSocket >= 0)\r\n    {\r\n        close(ClientSocket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint GetSetSid(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the getsid() and setsid() system calls..\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t ParentSid;\r\n    int Result;\r\n    pid_t Sid;\r\n\r\n    LxtCheckResult(LxtSignalBlock(SIGUSR1));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Initial values.\r\n        //\r\n\r\n        LxtCheckErrno(ParentSid = getsid(getppid()));\r\n        LxtCheckNotEqual(ParentSid, 0, \"%d\") LxtCheckErrno(Sid = getsid(0));\r\n        LxtCheckEqual(ParentSid, Sid, \"%d\");\r\n        LxtCheckErrno(Sid = getsid(getpid()));\r\n        LxtCheckEqual(ParentSid, Sid, \"%d\");\r\n\r\n        //\r\n        // Create a new session.\r\n        //\r\n\r\n        LxtCheckErrno(Sid = setsid());\r\n        LxtCheckNotEqual(ParentSid, Sid, \"%d\");\r\n        LxtCheckEqual(Sid, getpid(), \"%d\");\r\n        LxtCheckEqual(Sid, getpgid(0), \"%d\");\r\n\r\n        //\r\n        // Tell the parent that a new session was created.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(kill(getppid(), SIGUSR1));\r\n\r\n        //\r\n        // Wait for the signal to exit.\r\n        //\r\n\r\n        LxtCheckResult(LxtSignalWaitBlocked(SIGUSR1, getppid(), 2));\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    //\r\n    // Wait until the child has created the session.\r\n    //\r\n\r\n    LxtCheckResult(LxtSignalWaitBlocked(SIGUSR1, ChildPid, 2));\r\n    LxtCheckErrno(Sid = getsid(ChildPid));\r\n    LxtCheckErrno(ParentSid = getsid(0));\r\n    LxtCheckNotEqual(ParentSid, Sid, \"%d\");\r\n    LxtCheckEqual(Sid, ChildPid, \"%d\");\r\n    LxtCheckEqual(Sid, getpgid(ChildPid), \"%d\");\r\n\r\n    //\r\n    // Tell the child to exit.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(kill(ChildPid, SIGUSR1));\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    //\r\n    // If the process is a process group leader, it can't create a new\r\n    // session. The test is already the process group leader if it is launched\r\n    // from the shell but not from all test environments.\r\n    //\r\n\r\n    if (getpid() != getpgid(0))\r\n    {\r\n        LxtCheckResult(setpgid(getpid(), 0));\r\n    }\r\n\r\n    LxtCheckErrnoFailure(setsid(), EPERM);\r\n\r\n    //\r\n    // Getsid with invalid arguments.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(getsid(-1), ESRCH);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/getaddrinfo.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    getaddrinfo.c\r\n\r\nAbstract:\r\n\r\n    This file contains tests for getaddrinfo().\r\n\r\n--*/\r\n\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <stdlib.h>\r\n#include <netdb.h>\r\n#include <sys/types.h>\r\n#include <sys/socket.h>\r\n#include <arpa/inet.h>\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n\r\n#define LXT_NAME \"GetAddrInfo\"\r\n\r\nint LookupHost(const char* Host)\r\n{\r\n\r\n    char AddressString[100];\r\n    char* Argv[3];\r\n    char* Envp[1];\r\n    struct addrinfo Hints, *Info;\r\n    void* Ptr = NULL;\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    memset(&Hints, 0, sizeof(Hints));\r\n    Hints.ai_family = PF_UNSPEC;\r\n    Hints.ai_socktype = SOCK_STREAM;\r\n    Hints.ai_flags |= AI_CANONNAME;\r\n\r\n    LxtCheckErrno(getaddrinfo(Host, NULL, &Hints, &Info));\r\n    LxtLogInfo(\"Host: %s\", Host);\r\n    while (Info)\r\n    {\r\n        inet_ntop(Info->ai_family, Info->ai_addr->sa_data, AddressString, 100);\r\n        switch (Info->ai_family)\r\n        {\r\n        case AF_INET:\r\n            Ptr = &((struct sockaddr_in*)Info->ai_addr)->sin_addr;\r\n            break;\r\n\r\n        case AF_INET6:\r\n            Ptr = &((struct sockaddr_in6*)Info->ai_addr)->sin6_addr;\r\n            break;\r\n\r\n        default:\r\n            LxtLogError(\"ai_family unexpected %d\", Info->ai_family);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        inet_ntop(Info->ai_family, Ptr, AddressString, 100);\r\n        LxtLogInfo(\"IPv%d address: %s (%s)\", Info->ai_family == PF_INET6 ? 6 : 4, AddressString, Info->ai_canonname);\r\n\r\n        Info = Info->ai_next;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return 0;\r\n}\r\n\r\nint GetAddrInfoTestEntry(int Argc, char* Argv[])\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    LxtCheckErrno(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    if (Argc < 2)\r\n    {\r\n        LxtLogError(\"Requires HostName as argument\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LookupHost(Argv[1]);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/gettime.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    gettime.c\r\n\r\nAbstract:\r\n\r\n    This file is a test utility, not a formal test.\r\n\r\n--*/\r\n\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/types.h>\r\n#include <sys/socket.h>\r\n#include <sys/un.h>\r\n#include <netinet/in.h>\r\n#include <netdb.h>\r\n#include <time.h>\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n\r\n#define LXT_NAME \"gettime\"\r\n\r\nstruct timespec diff(struct timespec start, struct timespec end);\r\n\r\nint GetTimeTestEntry(int Argc, char* Argv[])\r\n{\r\n    LXT_ARGS Args;\r\n    int ClockId;\r\n    int i;\r\n    struct timespec ProcessTime1, ProcessTime2;\r\n    struct timespec Resolution;\r\n    struct timespec ThreadTime1, ThreadTime2;\r\n    int Result;\r\n    int temp;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n\r\n    //\r\n    // Get clock resolutions.\r\n    //\r\n\r\n    for (i = 0; i < 0x20; i++)\r\n    {\r\n        ClockId = (-1 & ~0x7) | i;\r\n        Result = LxtClockGetRes(ClockId, &Resolution);\r\n        LxtLogInfo(\"ClockId %x First 3 bits %x Result %d %d\", ClockId, i, Result, errno);\r\n        errno = 0;\r\n    }\r\n\r\n    //\r\n    // Test thread and process clocks.\r\n    //\r\n\r\n    LxtClockGetTime(CLOCK_THREAD_CPUTIME_ID, &ThreadTime1);\r\n    LxtClockGetTime(CLOCK_PROCESS_CPUTIME_ID, &ProcessTime1);\r\n    LxtLogInfo(\"ThreadTime1.tv_sec %d ThreadTime1.tv_nsec %d\", ThreadTime1.tv_sec, ThreadTime1.tv_nsec);\r\n\r\n    LxtLogInfo(\"ProcessTime1.tv_sec %d ProcessTime1.tv_nsec %d\", ProcessTime1.tv_sec, ProcessTime1.tv_nsec);\r\n\r\n    for (i = 0; i < 1000; i++)\r\n    {\r\n        LxtClockGetTime(CLOCK_THREAD_CPUTIME_ID, &ThreadTime2);\r\n        LxtLogInfo(\"ThreadTime2.tv_sec %d ThreadTime2.tv_nsec %d\", ThreadTime2.tv_sec, ThreadTime2.tv_nsec);\r\n\r\n        LxtClockGetTime(CLOCK_PROCESS_CPUTIME_ID, &ProcessTime2);\r\n        LxtLogInfo(\"ProcessTime2.tv_sec %d ProcessTime2.tv_nsec %d\", ProcessTime2.tv_sec, ProcessTime2.tv_nsec);\r\n    }\r\n\r\n    LxtLogInfo(\r\n        \"diff(ThreadTime1,ThreadTime2).tv_sec %d diff(ThreadTime1,ThreadTime2).tv_nsec %d\",\r\n        diff(ThreadTime1, ThreadTime2).tv_sec,\r\n        diff(ThreadTime1, ThreadTime2).tv_nsec);\r\n\r\n    LxtLogInfo(\r\n        \"diff(ProcessTime1,ProcessTime2).tv_sec %d diff(ProcessTime1,ProcessTime2).tv_nsec %d\",\r\n        diff(ProcessTime1, ProcessTime2).tv_sec,\r\n        diff(ProcessTime1, ProcessTime2).tv_nsec);\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return 0;\r\n}\r\n\r\nstruct timespec diff(struct timespec start, struct timespec end)\r\n{\r\n    struct timespec temp;\r\n    if ((end.tv_nsec - start.tv_nsec) < 0)\r\n    {\r\n        temp.tv_sec = end.tv_sec - start.tv_sec - 1;\r\n        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;\r\n    }\r\n    else\r\n    {\r\n        temp.tv_sec = end.tv_sec - start.tv_sec;\r\n        temp.tv_nsec = end.tv_nsec - start.tv_nsec;\r\n    }\r\n    return temp;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/inotify.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    inotify.c\r\n\r\nAbstract:\r\n\r\n    This file contains extensive inotify unit tests.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include \"lxtfs.h\"\r\n#include <poll.h>\r\n#include <dirent.h>\r\n#include <sys/epoll.h>\r\n#include <sys/time.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/inotify.h>\r\n#include <sys/mount.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <limits.h>\r\n#include <errno.h>\r\n\r\n#define LXT_NAME \"INOTIFY\"\r\n\r\n#define INOTIFY_TEST_BASE_DIR_LXFS \"/data/inotify_test/\"\r\n#define INOTIFY_TEST_PROCFS_MAX_QUEUED_EVENTS_FILE \"/proc/sys/fs/inotify/max_queued_events\"\r\n\r\nint TestInotifyComprehensive1Common(char* BaseDir);\r\n\r\nint TestInotifyComprehensive2Common(char* BaseDir);\r\n\r\nLXT_VARIATION_HANDLER TestInotifyNonBlockRead;\r\nLXT_VARIATION_HANDLER TestInotifyEventQueueOverflow;\r\nLXT_VARIATION_HANDLER TestInotifyEpollLxfs;\r\nLXT_VARIATION_HANDLER TestInotifyBasicLxfs;\r\nLXT_VARIATION_HANDLER TestInotifyComprehensive1Lxfs;\r\nLXT_VARIATION_HANDLER TestInotifyComprehensive2Lxfs;\r\nLXT_VARIATION_HANDLER TestInotifyPosixUnlinkRenameLxfs;\r\nLXT_VARIATION_HANDLER TestInotifyUnmountBindLxfs;\r\nLXT_VARIATION_HANDLER TestInotifyFtruncateLxfs;\r\nLXT_VARIATION_HANDLER TestInotifyPseudoPlugin;\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Test non-blocking read of inotify descriptor\", TestInotifyNonBlockRead},\r\n    {\"Test overflow of inotify event queue\", TestInotifyEventQueueOverflow},\r\n    {\"Test inotify with epoll - lxfs\", TestInotifyEpollLxfs},\r\n    {\"Test inotify watching basic paths - lxfs\", TestInotifyBasicLxfs},\r\n    {\"Comprehensive inotify tests 1 - lxfs\", TestInotifyComprehensive1Lxfs},\r\n    {\"Comprehensive inotify tests 2 - lxfs\", TestInotifyComprehensive2Lxfs},\r\n    {\"Test inotify with POSIX unlink/rename - lxfs\", TestInotifyPosixUnlinkRenameLxfs},\r\n    {\"Test unmounting of a bind mount - lxfs\", TestInotifyUnmountBindLxfs},\r\n    {\"Test ftruncate - lxfs\", TestInotifyFtruncateLxfs},\r\n    {\"Test inotify pseudo plugin\", TestInotifyPseudoPlugin}};\r\n\r\nint InotifyTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint TestInotifyNonBlockRead(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int Id;\r\n    char Buf[10];\r\n    int Result;\r\n\r\n    //\r\n    // There is nothing to read here, but should not block.\r\n    //\r\n\r\n    LxtCheckErrno(Id = inotify_init1(IN_NONBLOCK));\r\n    LxtCheckErrnoFailure(Result = read(Id, Buf, 1), EAGAIN);\r\n    LxtCheckErrnoZeroSuccess(close(Id));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TestInotifyEventQueueOverflow(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int Id;\r\n    int Fd;\r\n    int ProcFd;\r\n    char Buf[11];\r\n    int Result;\r\n    int OriginalMaxQueuedEvents;\r\n    char InotifyBuf[500];\r\n    struct inotify_event* Events[INOTIFY_TEST_EVENTS_BUF_SIZE];\r\n    int NumEvents;\r\n    char TestFile1[PATH_MAX];\r\n    char TestFile2[PATH_MAX];\r\n    char TestFile1Hlink[PATH_MAX];\r\n    char TestFile1Slink[PATH_MAX];\r\n\r\n    //\r\n    // Initialize and also do cleanup if the files have not been removed.\r\n    //\r\n\r\n    sprintf(TestFile1, \"%s%s\", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE1_NAME_ONLY);\r\n\r\n    sprintf(TestFile2, \"%s%s\", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE2_NAME_ONLY);\r\n\r\n    sprintf(TestFile1Hlink, \"%s%s\", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY);\r\n\r\n    sprintf(TestFile1Slink, \"%s%s\", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY);\r\n\r\n    unlink(TestFile1);\r\n    unlink(TestFile2);\r\n    unlink(TestFile1Hlink);\r\n    unlink(TestFile1Slink);\r\n    rmdir(INOTIFY_TEST_BASE_DIR_LXFS);\r\n    LxtCheckErrnoZeroSuccess(mkdir(INOTIFY_TEST_BASE_DIR_LXFS, 0777));\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Read the procFS value /proc/sys/fs/inotify/max_queued_events.\r\n    //\r\n\r\n    LxtCheckErrno(ProcFd = open(INOTIFY_TEST_PROCFS_MAX_QUEUED_EVENTS_FILE, O_RDWR));\r\n\r\n    LxtCheckErrno(Result = read(ProcFd, Buf, sizeof(Buf)));\r\n    OriginalMaxQueuedEvents = atoi(Buf);\r\n    LxtCheckNotEqual(OriginalMaxQueuedEvents, 0, \"%d\");\r\n\r\n    //\r\n    // Change the value to -1, verify failed.\r\n    //\r\n\r\n    sprintf(Buf, \"-1\");\r\n    LxtCheckErrnoFailure(Result = write(ProcFd, Buf, strlen(Buf)), EINVAL);\r\n\r\n    //\r\n    // Change the value to INT_MAX + 1 (2^31), verify failed.\r\n    //\r\n\r\n    sprintf(Buf, \"2147483648\");\r\n    LxtCheckErrnoFailure(Result = write(ProcFd, Buf, strlen(Buf)), EINVAL);\r\n\r\n    //\r\n    // Change the value to INT_MAX (2^31 - 1), verify succeeded.\r\n    //\r\n\r\n    sprintf(Buf, \"2147483647\");\r\n    LxtCheckErrno(Result = write(ProcFd, Buf, strlen(Buf)));\r\n    LxtCheckEqual(atoi(Buf), INT_MAX, \"%d\");\r\n\r\n    //\r\n    // Change the value to 2, and then read it back to verify.\r\n    //\r\n\r\n    sprintf(Buf, \"2\");\r\n    LxtCheckErrno(Result = write(ProcFd, Buf, strlen(Buf)));\r\n    LxtCheckErrno(Result = read(ProcFd, Buf, sizeof(Buf)));\r\n    LxtCheckEqual(atoi(Buf), 2, \"%d\");\r\n\r\n    //\r\n    // Generate 2 inotify events, verify that there is no overflow.\r\n    //\r\n\r\n    LxtCheckErrno(Id = inotify_init());\r\n    LxtCheckErrno(Result = inotify_add_watch(Id, TestFile1, IN_ALL_EVENTS));\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDWR));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 2, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_CLOSE_WRITE, \"%d\");\r\n\r\n    //\r\n    // Generate 3 inotify events, verify that there is an overflow.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDWR));\r\n    LxtCheckErrnoZeroSuccess(fchmod(Fd, 0666));\r\n    LxtCheckErrnoZeroSuccess(fchmod(Fd, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 3, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_ATTRIB, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_Q_OVERFLOW, \"%d\");\r\n    LxtCheckEqual(Events[2]->wd, -1, \"%d\");\r\n    LxtCheckEqual(Events[2]->cookie, 0, \"%d\");\r\n    LxtCheckEqual(Events[1]->len, 0, \"%d\");\r\n\r\n    //\r\n    // Restore the max_queued_events value to the original value read\r\n    // in the beginning, and verify.\r\n    //\r\n\r\n    sprintf(Buf, \"%d\", OriginalMaxQueuedEvents);\r\n    LxtCheckErrno(Result = write(ProcFd, Buf, strlen(Buf)));\r\n    LxtCheckErrno(Result = read(ProcFd, Buf, sizeof(Buf)));\r\n    LxtCheckEqual(atoi(Buf), OriginalMaxQueuedEvents, \"%d\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    close(Id);\r\n    close(Fd);\r\n    close(ProcFd);\r\n    unlink(TestFile1);\r\n    unlink(TestFile2);\r\n    unlink(TestFile1Hlink);\r\n    unlink(TestFile1Slink);\r\n    rmdir(INOTIFY_TEST_BASE_DIR_LXFS);\r\n    return Result;\r\n}\r\n\r\nint TestInotifyEpollLxfs(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    return LxtFsInotifyEpollCommon(INOTIFY_TEST_BASE_DIR_LXFS);\r\n}\r\n\r\nint TestInotifyBasicLxfs(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int Id;\r\n    int Result;\r\n    int Wd[10];\r\n\r\n    //\r\n    // Test watching basic Lxfs paths.\r\n    //\r\n\r\n    LxtCheckErrno(Id = inotify_init());\r\n    LxtCheckErrno(Wd[0] = inotify_add_watch(Id, \"/\", IN_ALL_EVENTS));\r\n    LxtCheckErrno(Wd[1] = inotify_add_watch(Id, \"/mnt\", IN_ALL_EVENTS));\r\n    LxtCheckErrno(Wd[2] = inotify_add_watch(Id, \"/mnt/\", IN_ALL_EVENTS));\r\n    LxtCheckErrno(Wd[3] = inotify_add_watch(Id, \"/proc\", IN_ALL_EVENTS));\r\n    LxtCheckErrno(Wd[4] = inotify_add_watch(Id, \"/sys\", IN_ALL_EVENTS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    close(Id);\r\n    return Result;\r\n}\r\n\r\nint TestInotifyComprehensive1Lxfs(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    return TestInotifyComprehensive1Common(INOTIFY_TEST_BASE_DIR_LXFS);\r\n}\r\n\r\nint TestInotifyComprehensive1Common(char* BaseDir)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Id1;\r\n    int Id2;\r\n    int Wd[10];\r\n    char Buf[10];\r\n    char InotifyBuf[500];\r\n    struct inotify_event* Events[INOTIFY_TEST_EVENTS_BUF_SIZE];\r\n    int NumEvents;\r\n    int Bytes;\r\n    int Result;\r\n    int Index;\r\n    int AttribEvent;\r\n    int CreateEvent;\r\n    char TestFile1[PATH_MAX];\r\n    char TestFile2[PATH_MAX];\r\n    char TestFile1Hlink[PATH_MAX];\r\n    char TestFile1Slink[PATH_MAX];\r\n\r\n    //\r\n    // Initialize and also do cleanup if the files have not been removed.\r\n    //\r\n\r\n    sprintf(TestFile1, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_NAME_ONLY);\r\n    sprintf(TestFile2, \"%s%s\", BaseDir, INOTIFY_TEST_FILE2_NAME_ONLY);\r\n    sprintf(TestFile1Hlink, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY);\r\n    sprintf(TestFile1Slink, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY);\r\n    unlink(TestFile1);\r\n    unlink(TestFile2);\r\n    unlink(TestFile1Hlink);\r\n    unlink(TestFile1Slink);\r\n    rmdir(BaseDir);\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Setup inotify.\r\n    //\r\n\r\n    LxtCheckErrno(Id1 = inotify_init());\r\n    LxtCheckErrno(Id2 = inotify_init());\r\n    LxtCheckErrno(Wd[0] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS));\r\n\r\n    //\r\n    // Check that \"output params\" can also be specified as input.\r\n    //\r\n\r\n    LxtCheckErrno(Wd[1] = inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS | IN_IGNORED | IN_ISDIR | IN_Q_OVERFLOW | IN_UNMOUNT));\r\n\r\n    LxtCheckEqual(Wd[0], 1, \"%d\");\r\n    LxtCheckEqual(Wd[1], 2, \"%d\");\r\n\r\n    //\r\n    // Test IN_OPEN, IN_ATTRIB, IN_MODIFY, IN_ACCESS, IN_CLOSE_WRITE.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDWR));\r\n    LxtCheckErrnoZeroSuccess(fchmod(Fd, 0666));\r\n    LxtCheckErrno(Bytes = write(Fd, Buf, 10));\r\n    LxtCheckEqual(Bytes, 10, \"%d\");\r\n    LxtCheckErrno(Bytes = write(Fd, Buf, 10));\r\n    LxtCheckEqual(Bytes, 10, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(lseek(Fd, 0, SEEK_SET));\r\n    LxtCheckErrno(Bytes = read(Fd, Buf, 10));\r\n    LxtCheckEqual(Bytes, 10, \"%d\");\r\n    LxtCheckErrno(Bytes = read(Fd, Buf, 10));\r\n    LxtCheckEqual(Bytes, 10, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(fchmod(Fd, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 16, \"%d\");\r\n    for (Index = 0; Index < 2; Index++)\r\n    {\r\n        LxtCheckEqual(Events[0 + Index]->mask, IN_OPEN, \"%d\");\r\n        LxtCheckEqual(Events[2 + Index]->mask, IN_ATTRIB, \"%d\");\r\n        LxtCheckEqual(Events[4 + Index]->mask, IN_MODIFY, \"%d\");\r\n        LxtCheckEqual(Events[6 + Index]->mask, IN_MODIFY, \"%d\");\r\n        LxtCheckEqual(Events[8 + Index]->mask, IN_ACCESS, \"%d\");\r\n        LxtCheckEqual(Events[10 + Index]->mask, IN_ACCESS, \"%d\");\r\n        LxtCheckEqual(Events[12 + Index]->mask, IN_ATTRIB, \"%d\");\r\n        LxtCheckEqual(Events[14 + Index]->mask, IN_CLOSE_WRITE, \"%d\");\r\n    }\r\n\r\n    for (Index = 0; Index < NumEvents; Index++)\r\n    {\r\n        LxtCheckEqual(Events[Index]->cookie, 0, \"%d\");\r\n        if ((Index % 2) == 0)\r\n        {\r\n\r\n            //\r\n            // The parent directory.\r\n            //\r\n\r\n            LxtCheckEqual(Events[Index]->wd, 2, \"%d\");\r\n            LxtCheckTrue(strcmp(Events[Index]->name, INOTIFY_TEST_FILE1_NAME_ONLY) == 0);\r\n            LxtCheckNotEqual(Events[Index]->len, 0, \"%d\");\r\n        }\r\n        else\r\n        {\r\n\r\n            //\r\n            // The file (child).\r\n            //\r\n\r\n            LxtCheckEqual(Events[Index]->wd, 1, \"%d\");\r\n            LxtCheckEqual(Events[Index]->len, 0, \"%d\");\r\n        }\r\n    }\r\n\r\n    //\r\n    // Test IN_CLOSE_NOWRITE.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDONLY));\r\n    LxtCheckErrno(Bytes = read(Fd, Buf, 10));\r\n    LxtCheckEqual(Bytes, 10, \"%d\");\r\n    LxtCheckErrno(Bytes = read(Fd, Buf, 10));\r\n    LxtCheckEqual(Bytes, 10, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 8, \"%d\");\r\n\r\n    //\r\n    // Test that opening an existing file with O_TRUNC generates IN_MODIFY,\r\n    // even if the open is for read-only access.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDONLY | O_TRUNC));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 6, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_MODIFY, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_MODIFY, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[3]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[4]->mask, IN_CLOSE_NOWRITE, \"%d\");\r\n    LxtCheckEqual(Events[5]->mask, IN_CLOSE_NOWRITE, \"%d\");\r\n\r\n    //\r\n    // Test that opening an existing file with only O_PATH generates IN_OPEN.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_PATH));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 4, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_CLOSE_NOWRITE, \"%d\");\r\n    LxtCheckEqual(Events[3]->mask, IN_CLOSE_NOWRITE, \"%d\");\r\n\r\n    //\r\n    // Test IN_MOVED_FROM, IN_MOVED_TO, IN_MOVE_SELF\r\n    // (rename with no overwrite).\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rename(TestFile1, TestFile2));\r\n    LxtCheckErrnoZeroSuccess(rename(TestFile2, TestFile1));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 6, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_MOVED_FROM, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_MOVED_TO, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_MOVE_SELF, \"%d\");\r\n    LxtCheckEqual(Events[0]->cookie, Events[1]->cookie, \"%d\");\r\n    LxtCheckTrue(strcmp(Events[0]->name, INOTIFY_TEST_FILE1_NAME_ONLY) == 0);\r\n    LxtCheckTrue(strcmp(Events[1]->name, INOTIFY_TEST_FILE2_NAME_ONLY) == 0);\r\n\r\n    //\r\n    // Test IN_DELETE and IN_DELETE_SELF.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(TestFile1));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 4, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_ATTRIB, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_DELETE_SELF, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_IGNORED, \"%d\");\r\n    LxtCheckEqual(Events[3]->mask, IN_DELETE, \"%d\");\r\n    LxtCheckEqual(Events[0]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[1]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[2]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[3]->wd, 2, \"%d\");\r\n    //\r\n    // Test IN_CREATE, and that inotify_rm_watch() generates IN_IGNORED.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrno(Wd[0] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS));\r\n    LxtCheckErrnoZeroSuccess(inotify_rm_watch(Id1, Wd[0]));\r\n    LxtCheckErrnoZeroSuccess(inotify_rm_watch(Id1, Wd[1]));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 5, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_CREATE, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_CLOSE_WRITE, \"%d\");\r\n    LxtCheckEqual(Events[3]->mask, IN_IGNORED, \"%d\");\r\n    LxtCheckEqual(Events[4]->mask, IN_IGNORED, \"%d\");\r\n    LxtCheckEqual(Events[0]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[1]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[2]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[3]->wd, 3, \"%d\");\r\n    LxtCheckEqual(Events[4]->wd, 2, \"%d\");\r\n\r\n    //\r\n    // Test that IN_ONESHOT generates only one event and then IN_IGNORED.\r\n    //\r\n\r\n    LxtCheckErrno(Wd[0] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS | IN_ONESHOT));\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDONLY));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 2, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_IGNORED, \"%d\");\r\n\r\n    //\r\n    // Test IN_ONLYDIR on file, should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(Wd[1] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS | IN_ONLYDIR), ENOTDIR);\r\n\r\n    //\r\n    // Test operations on directories.\r\n    //\r\n\r\n    LxtCheckErrno(Wd[1] = inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS | IN_ONLYDIR));\r\n\r\n    LxtCheckErrno(Fd = open(BaseDir, O_RDONLY));\r\n    LxtCheckErrnoZeroSuccess(fchmod(Fd, 0666));\r\n    LxtCheckErrnoZeroSuccess(fchmod(Fd, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 3, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_OPEN | IN_ISDIR, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_ATTRIB | IN_ISDIR, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_CLOSE_NOWRITE | IN_ISDIR, \"%d\");\r\n\r\n    //\r\n    // Test creating a symbolic link.\r\n    //\r\n\r\n    LxtCheckErrno(Wd[0] = inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS));\r\n    LxtCheckErrnoZeroSuccess(symlink(TestFile1, TestFile1Slink));\r\n\r\n    //\r\n    // Verify.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 1, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_CREATE, \"%d\");\r\n    LxtCheckEqual(Events[0]->wd, 5, \"%d\");\r\n    LxtCheckTrue(strcmp(Events[0]->name, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY) == 0);\r\n\r\n    //\r\n    // Test creating a hard link.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(link(TestFile1, TestFile1Hlink));\r\n\r\n    //\r\n    // Verify. Note that Ubuntu generates 2 events, whereas WSL generates 4 events.\r\n    // This is due to WSL performing unnecessary file opens, which will be fixed\r\n    // in the future. Also, the ordering of the events differs between Ubuntu and WSL.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckTrue((NumEvents == 2) || (NumEvents == 4));\r\n    if (Events[0]->mask == IN_ATTRIB)\r\n    {\r\n        AttribEvent = 0;\r\n        CreateEvent = 1;\r\n    }\r\n    else\r\n    {\r\n        AttribEvent = 1;\r\n        CreateEvent = 0;\r\n    }\r\n\r\n    LxtCheckEqual(Events[AttribEvent]->mask, IN_ATTRIB, \"%d\");\r\n    LxtCheckEqual(Events[AttribEvent]->wd, 6, \"%d\");\r\n    LxtCheckEqual(Events[CreateEvent]->mask, IN_CREATE, \"%d\");\r\n    LxtCheckEqual(Events[CreateEvent]->wd, 5, \"%d\");\r\n    LxtCheckTrue(strcmp(Events[CreateEvent]->name, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY) == 0);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    close(Id1);\r\n    close(Id2);\r\n    close(Fd);\r\n    unlink(TestFile1);\r\n    unlink(TestFile2);\r\n    unlink(TestFile1Hlink);\r\n    unlink(TestFile1Slink);\r\n    rmdir(BaseDir);\r\n    return Result;\r\n}\r\n\r\nint TestInotifyComprehensive2Lxfs(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    return TestInotifyComprehensive2Common(INOTIFY_TEST_BASE_DIR_LXFS);\r\n}\r\n\r\nint TestInotifyComprehensive2Common(char* BaseDir)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Id1;\r\n    int Id2;\r\n    int Wd[10];\r\n    char Buf[10];\r\n    char InotifyBuf[500];\r\n    struct inotify_event* Events[INOTIFY_TEST_EVENTS_BUF_SIZE];\r\n    int NumEvents;\r\n    int Bytes;\r\n    int Result;\r\n    char TestFile1[PATH_MAX];\r\n    char TestFile2[PATH_MAX];\r\n    char TestFile1Hlink[PATH_MAX];\r\n    char TestFile1Slink[PATH_MAX];\r\n\r\n    //\r\n    // Initialize and also do cleanup if the files have not been removed.\r\n    //\r\n\r\n    sprintf(TestFile1, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_NAME_ONLY);\r\n    sprintf(TestFile2, \"%s%s\", BaseDir, INOTIFY_TEST_FILE2_NAME_ONLY);\r\n    sprintf(TestFile1Hlink, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY);\r\n    sprintf(TestFile1Slink, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY);\r\n    unlink(TestFile1);\r\n    unlink(TestFile2);\r\n    unlink(TestFile1Hlink);\r\n    unlink(TestFile1Slink);\r\n    rmdir(BaseDir);\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Setup inotify.\r\n    //\r\n\r\n    LxtCheckErrno(Id1 = inotify_init());\r\n    LxtCheckErrno(Id2 = inotify_init());\r\n\r\n    //\r\n    // Test IN_EXCL_UNLINK on both the directory and the file to be unlinked.\r\n    // Also test deleting a file that has open handles to it.\r\n    //\r\n\r\n    LxtCheckErrno(\r\n        Wd[0] = // wd: 1\r\n        inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS | IN_EXCL_UNLINK));\r\n\r\n    LxtCheckErrno(\r\n        Wd[1] = // wd: 2\r\n        inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS | IN_EXCL_UNLINK));\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDWR));\r\n    unlink(TestFile1);\r\n    LxtCheckErrno(Bytes = write(Fd, Buf, 10));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify. Note that the write and close on the unlinked file did not\r\n    // generate any events on either the directory or the file since the\r\n    // IN_EXCL_UNLINK flag was set on both.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 6, \"%d\");\r\n    LxtCheckEqual(Wd[0], 1, \"%d\");\r\n    LxtCheckEqual(Wd[1], 2, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_ATTRIB, \"%d\");\r\n    LxtCheckEqual(Events[3]->mask, IN_DELETE, \"%d\");\r\n    LxtCheckEqual(Events[4]->mask, IN_DELETE_SELF, \"%d\");\r\n    LxtCheckEqual(Events[5]->mask, IN_IGNORED, \"%d\");\r\n    LxtCheckEqual(Events[0]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[1]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[2]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[3]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[4]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[5]->wd, 1, \"%d\");\r\n\r\n    //\r\n    // Test IN_EXCL_UNLINK on the directory only.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrnoZeroSuccess(close(Id1));\r\n    LxtCheckErrno(Id1 = inotify_init());\r\n    LxtCheckErrno(\r\n        Wd[0] = // wd: 1\r\n        inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS));\r\n\r\n    LxtCheckErrno(\r\n        Wd[1] = // wd: 2\r\n        inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS | IN_EXCL_UNLINK));\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDWR));\r\n    unlink(TestFile1);\r\n    LxtCheckErrno(Bytes = write(Fd, Buf, 10));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify. Note that the file events are still generated even though it was\r\n    // unlinked, because the file does not have the IN_EXCL_UNLINK flag set.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 8, \"%d\");\r\n    LxtCheckEqual(Wd[0], 1, \"%d\");\r\n    LxtCheckEqual(Wd[1], 2, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_ATTRIB, \"%d\");\r\n    LxtCheckEqual(Events[3]->mask, IN_DELETE, \"%d\");\r\n    LxtCheckEqual(Events[4]->mask, IN_MODIFY, \"%d\");\r\n    LxtCheckEqual(Events[5]->mask, IN_CLOSE_WRITE, \"%d\");\r\n    LxtCheckEqual(Events[6]->mask, IN_DELETE_SELF, \"%d\");\r\n    LxtCheckEqual(Events[7]->mask, IN_IGNORED, \"%d\");\r\n    LxtCheckEqual(Events[0]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[1]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[2]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[3]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[4]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[5]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[6]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[7]->wd, 1, \"%d\");\r\n\r\n    //\r\n    // Test IN_EXCL_UNLINK on the unlinked file only.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrnoZeroSuccess(close(Id1));\r\n    LxtCheckErrno(Id1 = inotify_init());\r\n    LxtCheckErrno(\r\n        Wd[0] = // wd: 1\r\n        inotify_add_watch(Id1, TestFile1, IN_ALL_EVENTS | IN_EXCL_UNLINK));\r\n\r\n    LxtCheckErrno(\r\n        Wd[1] = // wd: 2\r\n        inotify_add_watch(Id1, BaseDir, IN_ALL_EVENTS));\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDWR));\r\n    unlink(TestFile1);\r\n    LxtCheckErrno(Bytes = write(Fd, Buf, 10));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify. Note that the directory still receives the events from the unlinked\r\n    // child, because the directory does not have the IN_EXCL_UNLINK flag set.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 8, \"%d\");\r\n    LxtCheckEqual(Wd[0], 1, \"%d\");\r\n    LxtCheckEqual(Wd[1], 2, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[2]->mask, IN_ATTRIB, \"%d\");\r\n    LxtCheckEqual(Events[3]->mask, IN_DELETE, \"%d\");\r\n    LxtCheckEqual(Events[4]->mask, IN_MODIFY, \"%d\");\r\n    LxtCheckEqual(Events[5]->mask, IN_CLOSE_WRITE, \"%d\");\r\n    LxtCheckEqual(Events[6]->mask, IN_DELETE_SELF, \"%d\");\r\n    LxtCheckEqual(Events[7]->mask, IN_IGNORED, \"%d\");\r\n    LxtCheckEqual(Events[0]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[1]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[2]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[3]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[4]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[5]->wd, 2, \"%d\");\r\n    LxtCheckEqual(Events[6]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[7]->wd, 1, \"%d\");\r\n\r\n    //\r\n    // Test watching the same file twice.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrnoZeroSuccess(close(Id1));\r\n    LxtCheckErrno(Id1 = inotify_init());\r\n    LxtCheckErrno(\r\n        Wd[0] = // wd: 1\r\n        inotify_add_watch(Id1, TestFile1, IN_OPEN));\r\n\r\n    LxtCheckErrno(\r\n        Wd[1] = // wd: 1\r\n        inotify_add_watch(Id1, TestFile1, IN_CLOSE));\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_WRONLY));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify that IN_CLOSE_WRITE is received, and that IN_OPEN is not received.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 1, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_CLOSE_WRITE, \"%d\");\r\n    LxtCheckEqual(Events[0]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Wd[0], Wd[1], \"%d\");\r\n\r\n    //\r\n    // Test watching the same file twice, but with IN_MASK_ADD.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrnoZeroSuccess(close(Id1));\r\n    LxtCheckErrno(Id1 = inotify_init());\r\n    LxtCheckErrno(\r\n        Wd[0] = // wd: 1\r\n        inotify_add_watch(Id1, TestFile1, IN_OPEN));\r\n\r\n    LxtCheckErrno(\r\n        Wd[1] = // wd: 1\r\n        inotify_add_watch(Id1, TestFile1, IN_CLOSE | IN_MASK_ADD));\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_RDONLY));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Verify that both IN_OPEN and IN_CLOSE_NOWRITE are received.\r\n    //\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 2, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_OPEN, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_CLOSE_NOWRITE, \"%d\");\r\n    LxtCheckEqual(Events[0]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Events[1]->wd, 1, \"%d\");\r\n    LxtCheckEqual(Wd[0], Wd[1], \"%d\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    close(Id1);\r\n    close(Id2);\r\n    close(Fd);\r\n    unlink(TestFile1);\r\n    rmdir(BaseDir);\r\n    return Result;\r\n}\r\n\r\nint TestInotifyPosixUnlinkRenameLxfs(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    return LxtFsInotifyPosixUnlinkRenameCommon(INOTIFY_TEST_BASE_DIR_LXFS);\r\n}\r\n\r\nint TestInotifyUnmountBindLxfs(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    return LxtFsInotifyUnmountBindCommon(INOTIFY_TEST_BASE_DIR_LXFS);\r\n}\r\n\r\nint TestInotifyFtruncateLxfs(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Fd;\r\n    struct pollfd PollFd;\r\n    int Result;\r\n    char TestFile1[PATH_MAX];\r\n\r\n    ChildPid = -1;\r\n    Fd = -1;\r\n\r\n    //\r\n    // Initialize and also do cleanup if the files have not been removed.\r\n    //\r\n\r\n    sprintf(TestFile1, \"%s%s\", INOTIFY_TEST_BASE_DIR_LXFS, INOTIFY_TEST_FILE1_NAME_ONLY);\r\n\r\n    unlink(TestFile1);\r\n    rmdir(INOTIFY_TEST_BASE_DIR_LXFS);\r\n    LxtCheckErrnoZeroSuccess(mkdir(INOTIFY_TEST_BASE_DIR_LXFS, 0777));\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrno(ftruncate(Fd, 1024));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        sleep(2);\r\n        LxtCheckErrno(ftruncate(Fd, 1024));\r\n        fsync(Fd);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    int Id = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);\r\n    LxtCheckErrno(inotify_add_watch(Id, TestFile1, IN_ALL_EVENTS));\r\n    PollFd.fd = Id;\r\n    PollFd.events = POLLIN;\r\n    LxtCheckErrno(ppoll(&PollFd, 1, NULL, NULL));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TestInotifyPseudoPlugin(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int Id;\r\n    int Result;\r\n\r\n    LxtCheckErrno(Id = inotify_init());\r\n    LxtCheckErrno(inotify_add_watch(Id, \"/proc/self/ns/pid\", IN_ALL_EVENTS));\r\n\r\nErrorExit:\r\n    if (Id != -1)\r\n    {\r\n        LxtClose(Id);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/interop.c",
    "content": "/*++\r\n\r\n  Copyright (c) Microsoft. All rights reserved.\r\n\r\n  Module Name:\r\n\r\n  interop.c\r\n\r\n  Abstract:\r\n\r\n  This file is the interop test for WSL2.\r\n\r\n  --*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <unistd.h>\r\n#include <sys/types.h>\r\n#include <sys/wait.h>\r\n#include <pty.h>\r\n#include <poll.h>\r\n#include <errno.h>\r\n#include <fcntl.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n\r\n#define LXT_NAME \"interop\"\r\n\r\n#define BUF_SIZE 1024\r\n#define LARGE_MESSAGE_SIZE (4096)\r\n#define PIPE_READ_END 0\r\n#define PIPE_WRITE_END 1\r\n#define CMD_NT_BINARY \"/mnt/c/Windows/System32/cmd.exe\"\r\n#define CMD_SYMLINK \"/tmp/cmd\"\r\n#define HELLO_WORLD_STR \"hello world\"\r\n\r\nint BasicBasicTests(PLXT_ARGS Args);\r\n\r\nint InteropWithPtyTest(PLXT_ARGS Args);\r\n\r\nint InteropWithPipesTest(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Interop Test with pipes\", InteropWithPipesTest},\r\n    /* {\"Interop Test with pty\", InteropWithPtyTest}, */\r\n    {\"Basic interop tests\", BasicBasicTests}};\r\n\r\nint InteropTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n  --*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint readStringFrom(int fd, char* buffer, int buf_size)\r\n\r\n/*++\r\n\r\n  Routine Description:\r\n\r\n  Reads characters from given file descriptor until either we reach EOF\r\n  or reach the buffer limit.\r\n  This function adds the '\\0' character at the end of the string.\r\n\r\n  Arguments:\r\n\r\n  fd : the file descriptor from which data should be read.\r\n  buffer: the buffer into which data should be read.\r\n  buf_size: Size of the 'buffer'. This function will read at max\r\n  (buf_size - 1) bytes from fd. Last byte is reserved to write '\\0'\r\n  character.\r\n\r\n  Return Value:\r\n\r\n  Returns number of bytes read.\r\n\r\n  --*/\r\n\r\n{\r\n    if (fd < 0 || !buffer || (buf_size <= 0))\r\n    {\r\n        return 0;\r\n    }\r\n\r\n    char* readptr = buffer;\r\n    int remaining = buf_size - 1;\r\n    int rc = 0;\r\n    while (remaining > 0 && (rc = read(fd, readptr, remaining)) > 0)\r\n    {\r\n        readptr += rc;\r\n        remaining -= rc;\r\n    }\r\n    *readptr = 0;\r\n    // exclude '\\0' from length\r\n    return (buf_size - remaining - 1);\r\n}\r\n\r\nint InteropWithPtyTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\n  Routine Description:\r\n\r\n  Test for verifying that interop works as expected when the windows executable is started\r\n  inside a pseudoterminal.\r\n\r\n  Arguments:\r\n\r\n  Args - standard arguments provided to every test.\r\n\r\n  Return Value:\r\n\r\n  Returns 0 on success, -1 on failure.\r\n\r\n  --*/\r\n\r\n{\r\n    int Result;\r\n    int status;\r\n    int childpid;\r\n    int master_fd = -1;\r\n    char buf[BUF_SIZE];\r\n\r\n    LxtCheckErrno((childpid = forkpty(&master_fd, NULL, NULL, NULL)));\r\n    if (childpid == 0)\r\n    {\r\n        // child\r\n        LxtCheckErrno(execl(\"/mnt/c/Windows/System32/cmd.exe\", \"cmd.exe\", \"/c\", \"echo\", HELLO_WORLD_STR, (char*)NULL));\r\n    }\r\n    else\r\n    {\r\n        // parent\r\n\r\n        readStringFrom(master_fd, buf, sizeof(buf));\r\n\r\n        // output generated by echo command should match exactly with the\r\n        // expected string (along with the CRLF and quotation marks)\r\n        LxtCheckStringEqual(\r\n            buf,\r\n            \"\\\"\" HELLO_WORLD_STR\r\n            \"\\\"\"\r\n            \"\\r\\n\");\r\n\r\n        // make sure child exits normally\r\n        LxtCheckErrno(waitpid(childpid, &status, 0));\r\n        LxtCheckResult(WIFEXITED(status));\r\n        Result = LXT_RESULT_SUCCESS;\r\n    }\r\n\r\nErrorExit:\r\n    if (master_fd != -1)\r\n    {\r\n        LxtClose(master_fd);\r\n    }\r\n    if (childpid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n    return Result;\r\n}\r\n\r\nint InteropWithPipesTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\n  Routine Description:\r\n\r\n  Test for verifying that interop works as expected when stdout of the windows executable is redirected.\r\n\r\n  Arguments:\r\n\r\n  Args - standard arguments provided to every test.\r\n\r\n  Return Value:\r\n\r\n  Returns 0 on success, -1 on failure.\r\n\r\n  --*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int status;\r\n    int childpid;\r\n    int child_out_pipe[2];\r\n    char buf[BUF_SIZE];\r\n\r\n    // Pipe to which child's stdout is redirected\r\n    LxtCheckErrno(pipe(child_out_pipe));\r\n\r\n    LxtCheckErrno(childpid = fork());\r\n    if (childpid == 0)\r\n    {\r\n        // child\r\n        LxtCheckErrno(dup2(child_out_pipe[PIPE_WRITE_END], STDOUT_FILENO));\r\n        LxtClose(child_out_pipe[PIPE_READ_END]);\r\n        child_out_pipe[PIPE_READ_END] = -1;\r\n        LxtClose(child_out_pipe[PIPE_WRITE_END]);\r\n        child_out_pipe[PIPE_WRITE_END] = -1;\r\n        // execute a windows executable\r\n        LxtCheckErrno(execl(\"/mnt/c/Windows/System32/cmd.exe\", \"cmd.exe\", \"/c\", \"echo\", HELLO_WORLD_STR, (char*)NULL));\r\n    }\r\n    else\r\n    {\r\n        // parent\r\n\r\n        LxtClose(child_out_pipe[PIPE_WRITE_END]);\r\n        child_out_pipe[PIPE_WRITE_END] = -1;\r\n\r\n        readStringFrom(child_out_pipe[PIPE_READ_END], buf, sizeof(buf));\r\n\r\n        // output generated by echo command should match exactly with the\r\n        // expected string (along with the CRLF and quotation marks)\r\n        LxtCheckStringEqual(\r\n            buf,\r\n            \"\\\"\" HELLO_WORLD_STR\r\n            \"\\\"\"\r\n            \"\\r\\n\");\r\n\r\n        // make sure child exits normally\r\n        LxtCheckErrno(waitpid(childpid, &status, 0));\r\n        LxtCheckResult(WIFEXITED(status));\r\n        Result = LXT_RESULT_SUCCESS;\r\n    }\r\n\r\nErrorExit:\r\n    if (child_out_pipe[PIPE_READ_END] != -1)\r\n    {\r\n        LxtClose(child_out_pipe[PIPE_READ_END]);\r\n    }\r\n    if (child_out_pipe[PIPE_WRITE_END] != -1)\r\n    {\r\n        LxtClose(child_out_pipe[PIPE_WRITE_END]);\r\n    }\r\n    if (childpid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n    return Result;\r\n}\r\n\r\nint BasicBasicTests(PLXT_ARGS Args)\r\n/*++\r\n\r\n  Routine Description:\r\n\r\n  WSL interop tests from version 1 ported for WSL2.\r\n  These tests call some standard windows commands/executables in different ways and make sure\r\n  that interop is working as expected.\r\n\r\n  Arguments:\r\n\r\n  Args - standard arguments provided to every test.\r\n\r\n  Return Value:\r\n\r\n  Returns 0 on success, -1 on failure.\r\n\r\n  --*/\r\n\r\n{\r\n    int ChildPid;\r\n    char* ExecArgs[5];\r\n    int ExitStatus;\r\n    char* LargeArgument;\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    memset(ExecArgs, 0, sizeof(ExecArgs));\r\n    LargeArgument = NULL;\r\n\r\n    //\r\n    // Try to launch an invalid NT binary.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = \"/init\";\r\n        ExecArgs[1] = \"invalid_binary_name\";\r\n        LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), ENOENT);\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        wait(&ExitStatus);\r\n        LxtCheckTrue(WIFEXITED(ExitStatus));\r\n    }\r\n\r\n    //\r\n    // Launch the cmd.exe NT binary.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = CMD_NT_BINARY;\r\n        ExecArgs[1] = \"/c\";\r\n        ExecArgs[2] = \"exit 0\";\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    }\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = CMD_NT_BINARY;\r\n        ExecArgs[1] = \"/c\";\r\n        ExecArgs[2] = \"exit 1\";\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 1 << 8));\r\n    }\r\n\r\n    //\r\n    // Launch cmd.exe with a very long command line.\r\n    //\r\n\r\n    LargeArgument = LxtAlloc(LARGE_MESSAGE_SIZE);\r\n    if (LargeArgument == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(LargeArgument, 'a', LARGE_MESSAGE_SIZE);\r\n    LargeArgument[LARGE_MESSAGE_SIZE - 1] = '\\0';\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = CMD_NT_BINARY;\r\n        ExecArgs[1] = \"/c\";\r\n        ExecArgs[2] = \"echo\";\r\n        ExecArgs[3] = LargeArgument;\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    }\r\n\r\n    //\r\n    // Launch cmd.exe through a symlink.\r\n    //\r\n\r\n    LxtCheckErrno(symlink(CMD_NT_BINARY, CMD_SYMLINK));\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ExecArgs[0] = CMD_SYMLINK;\r\n        ExecArgs[1] = \"/c\";\r\n        ExecArgs[2] = \"exit 0\";\r\n        LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));\r\n\r\n        //\r\n        // The parent waits for the child to exit successfully.\r\n        //\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    }\r\n\r\nErrorExit:\r\n    if (ChildPid > 0)\r\n    {\r\n        unlink(CMD_SYMLINK);\r\n    }\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/ioprio.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Ioprio.c\r\n\r\nAbstract:\r\n\r\n    This file is a ioprio test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/time.h>\r\n#include <sys/resource.h>\r\n#include <sys/prctl.h>\r\n#include \"lxtutil.h\"\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n\r\n#include <sys/capability.h>\r\n\r\n#else\r\n\r\n#include <sys/cdefs.h>\r\n#include <linux/capability.h>\r\n\r\n#define _LINUX_CAPABILITY_VERSION_3 0x20080522\r\n\r\n#ifndef O_PATH\r\n#define O_PATH 010000000\r\n#endif\r\n\r\n#endif\r\n\r\n#define IOPRIO_UID 1007\r\n#define IOPRIO_GID 1007\r\n\r\n//\r\n// When the ioprio.h header is available, these types can be removed.\r\n//\r\n\r\n#define LX_IOPRIO_WHO_PROCESS 1\r\n#define LX_IOPRIO_WHO_PGRP 2\r\n#define LX_IOPRIO_WHO_USER 3\r\n\r\n#define LX_IOPRIO_CLASS_NONE 0\r\n#define LX_IOPRIO_CLASS_RT 1\r\n#define LX_IOPRIO_CLASS_BE 2\r\n#define LX_IOPRIO_CLASS_IDLE 3\r\n\r\n#define LX_IOPRIO_CLASS_SHIFT 13\r\n#define LX_IOPRIO_PRIO_MASK ((1 << LX_IOPRIO_CLASS_SHIFT) - 1)\r\n#define LX_IOPRIO_PRIO_CLASS(_mask) ((_mask) >> LX_IOPRIO_CLASS_SHIFT)\r\n#define LX_IOPRIO_PRIO_DATA(_mask) ((_mask) & LX_IOPRIO_PRIO_MASK)\r\n#define LX_IOPRIO_PRIO_VALUE(_class, _data) (((_class) << LX_IOPRIO_CLASS_SHIFT) | (_data))\r\n#define LX_IO_DEFAULT_PRIORITY 4\r\n\r\n#define LXT_NAME \"Ioprio\"\r\n\r\nint IoprioVariationGetProcess(PLXT_ARGS Args);\r\n\r\nint IoprioVariationSetProcess(PLXT_ARGS Args);\r\n\r\nint IoprioVariationGetSetPriority(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"ioprio_get process\", IoprioVariationGetProcess},\r\n    {\"getpriority / setpriority\", IoprioVariationGetSetPriority},\r\n    {\"ioprio_set process\", IoprioVariationSetProcess}};\r\n\r\nint IoprioTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtInitialize(Argc, Argv, &Args, LXT_NAME);\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint IoprioVariationGetProcess(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int DefaultPriority;\r\n    int IoPrio;\r\n    int Result;\r\n\r\n    //\r\n    // Negative variations.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, -1), ESRCH);\r\n    LxtCheckErrnoFailure(IoPrio = LxtIoprio_get(0, 0), EINVAL);\r\n\r\n    //\r\n    // Get the default priority for the current and parent thread.\r\n    //\r\n\r\n    LxtCheckErrno(DefaultPriority = getpriority(PRIO_PROCESS, 0));\r\n    LxtLogInfo(\"DefaultPriority: %d\", DefaultPriority);\r\n    LxtCheckErrno(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0));\r\n    LxtCheckEqual(IoPrio, LX_IO_DEFAULT_PRIORITY, \"%d\");\r\n    LxtCheckErrno(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, getpid()));\r\n    LxtCheckEqual(IoPrio, LX_IO_DEFAULT_PRIORITY, \"%d\");\r\n    LxtCheckErrno(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, getppid()));\r\n    LxtCheckEqual(IoPrio, LX_IO_DEFAULT_PRIORITY, \"%d\");\r\n\r\n    //\r\n    // Set the priority of the current thread and check that it does not change\r\n    // the io priority.\r\n    //\r\n\r\n    LxtCheckErrno(setpriority(PRIO_PROCESS, 0, LX_IO_DEFAULT_PRIORITY + 1));\r\n    LxtCheckErrno(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0));\r\n    LxtCheckNotEqual(IoPrio, LX_IO_DEFAULT_PRIORITY + 1, \"%d\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    setpriority(PRIO_PROCESS, 0, LX_IO_DEFAULT_PRIORITY);\r\n    return Result;\r\n}\r\n\r\nint IoprioVariationSetProcess(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int DefaultPriority;\r\n    int Index;\r\n    int InitialIoPrio;\r\n    int IoPrio;\r\n    int NewIoPrio;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Get the default priority for the current and parent thread.\r\n    //\r\n    // N.B. Setting the IO priority to this default value fails.\r\n    //\r\n\r\n    LxtCheckErrno(InitialIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0));\r\n    LxtLogInfo(\"InitialIoPrio = %d\", InitialIoPrio);\r\n    IoPrio = InitialIoPrio;\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n\r\n    //\r\n    // LX_IOPRIO_CLASS_NONE\r\n    //\r\n\r\n    for (Index = 0; Index < 1; Index += 1)\r\n    {\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, Index);\r\n        LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio));\r\n        LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0));\r\n        LxtCheckEqual(IoPrio, NewIoPrio, \"%d\");\r\n    }\r\n\r\n    IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, Index);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n\r\n    //\r\n    // LX_IOPRIO_CLASS_RT\r\n    //\r\n\r\n    for (Index = 0; Index < 8; Index += 1)\r\n    {\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, Index);\r\n        LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio));\r\n        LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0));\r\n        LxtCheckEqual(IoPrio, NewIoPrio, \"%d\");\r\n    }\r\n\r\n    IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, Index);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n\r\n    //\r\n    // LX_IOPRIO_CLASS_BE\r\n    //\r\n\r\n    for (Index = 0; Index < 8; Index += 1)\r\n    {\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_BE, Index);\r\n        LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio));\r\n        LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0));\r\n        LxtCheckEqual(IoPrio, NewIoPrio, \"%d\");\r\n    }\r\n\r\n    IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_BE, Index);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n\r\n    //\r\n    // LX_IOPRIO_CLASS_IDLE\r\n    //\r\n\r\n    for (Index = 0; Index < 0x8000; Index += 1)\r\n    {\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_IDLE, Index);\r\n        LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio));\r\n        LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0));\r\n        LxtCheckEqual(IoPrio, NewIoPrio, \"%d\");\r\n    }\r\n\r\n    IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_IDLE, Index);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0);\r\n        LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, getppid(), IoPrio));\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SYS_ADMIN)].permitted |= CAP_TO_MASK(CAP_SYS_ADMIN);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n\r\n        //\r\n        // Drop all privileges but CAP_SYS_ADMIN.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(IOPRIO_UID));\r\n        LxtCheckErrno(setuid(IOPRIO_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Attempt to change parent's io priority now that UID / GID do not match.\r\n        //\r\n\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0);\r\n        LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, getppid(), IoPrio), EPERM);\r\n\r\n        for (Index = 0; Index < 8; Index += 1)\r\n        {\r\n            IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, Index);\r\n            LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio));\r\n            LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0));\r\n            LxtCheckEqual(IoPrio, NewIoPrio, \"%d\");\r\n        }\r\n\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, Index);\r\n        LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n\r\n        //\r\n        // Drop all privileges.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, 0);\r\n        LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EPERM);\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, 8);\r\n        LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EPERM);\r\n\r\n        //\r\n        // LX_IOPRIO_CLASS_NONE\r\n        //\r\n\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0);\r\n        LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio));\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 1);\r\n        LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n\r\n        //\r\n        // LX_IOPRIO_CLASS_BE\r\n        //\r\n\r\n        for (Index = 0; Index < 8; Index += 1)\r\n        {\r\n            IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_BE, Index);\r\n            LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio));\r\n            LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0));\r\n            LxtCheckEqual(IoPrio, NewIoPrio, \"%d\");\r\n        }\r\n\r\n        IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_BE, Index);\r\n        LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Negative variations.\r\n    //\r\n\r\n    IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, -1, IoPrio), ESRCH);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(0, 0, IoPrio), EINVAL);\r\n    IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_IDLE + 1, 0);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n    IoPrio = LX_IOPRIO_PRIO_VALUE(-1, 0);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(0, 0, IoPrio), EINVAL);\r\n    IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_USER + 1, 0, IoPrio), EINVAL);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_USER + 1, -1, IoPrio), EINVAL);\r\n    IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 1);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_USER + 1, 0, IoPrio), EINVAL);\r\n    LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_USER + 1, -1, IoPrio), EINVAL);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint IoprioSetPriority(int Which, int Who, int Priority, int ExpectedPriority)\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(setpriority(Which, Who, Priority));\r\n    errno = 0;\r\n    Result = getpriority(Which, Who);\r\n    if ((Result != ExpectedPriority) || (errno != 0))\r\n    {\r\n        LxtLogError(\"getpriority returned %d expected %d - errno %d\", Result, ExpectedPriority, errno);\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint IoprioVariationGetSetPriority(PLXT_ARGS Args)\r\n\r\n{\r\n    int ChildPid;\r\n    int OriginalPriority;\r\n    pid_t ParentPid;\r\n    int Result;\r\n    struct rlimit Rlimit;\r\n\r\n    ChildPid = -1;\r\n    errno = 0;\r\n    OriginalPriority = getpriority(PRIO_PROCESS, 0);\r\n    LxtLogInfo(\"OriginalPriority %d\", OriginalPriority);\r\n\r\n    //\r\n    // Validate setting the priority to a negative number.\r\n    //\r\n\r\n    LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, -1, -1));\r\n\r\n    //\r\n    // Validate setting the priority to a positive number.\r\n    //\r\n\r\n    LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 5, 5));\r\n\r\n    //\r\n    // Validate the lower bound of the priority (-20).\r\n    //\r\n\r\n    LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, -20, -20));\r\n    LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, -21, -20));\r\n    LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, -444, -20));\r\n\r\n    //\r\n    // Validate the upper bound of the priority (19).\r\n    //\r\n\r\n    LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 19, 19));\r\n    LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 20, 19));\r\n    LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 444, 19));\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(getpriority(-1, 0), EINVAL);\r\n\r\n    //\r\n    // Permissions checks. Reset the priority to a known value.\r\n    //\r\n\r\n    LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 5, 5));\r\n    ParentPid = getpid();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Change the UID to drop CAP_SYS_NICE and verify that the priority can\r\n        // be set to the same value.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(1000));\r\n        LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 5, 5));\r\n\r\n        //\r\n        // Attempt to lower the priority.\r\n        //\r\n\r\n        LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 6, 6));\r\n\r\n        //\r\n        // Attempt to raise the priority (should fail).\r\n        //\r\n\r\n        LxtCheckErrnoFailure(setpriority(PRIO_PROCESS, 0, 4), EACCES);\r\n\r\n        //\r\n        // Attempt to modify the parent's priority (should fail).\r\n        //\r\n\r\n        LxtCheckErrnoFailure(setpriority(PRIO_PROCESS, ParentPid, 5), EPERM);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Change the nice rlimit value.\r\n        //\r\n\r\n        Rlimit.rlim_cur = 19;\r\n        Rlimit.rlim_max = 19;\r\n        LxtCheckErrno(setrlimit(RLIMIT_NICE, &Rlimit));\r\n\r\n        //\r\n        // Change the UID to drop CAP_SYS_NICE.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(1000));\r\n\r\n        //\r\n        // Setting the priority to 1 should succeed.\r\n        //\r\n        LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 1, 1));\r\n\r\n        //\r\n        // Setting the priority to 0 should fail.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(setpriority(PRIO_PROCESS, 0, 0), EACCES);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (OriginalPriority > 0)\r\n    {\r\n        setpriority(PRIO_PROCESS, 0, OriginalPriority);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/keymgmt.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Keymgmt.c\r\n\r\nAbstract:\r\n\r\n    This file is a keymgmt test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <linux/keyctl.h>\r\n\r\n#define LXT_NAME \"Keymgmt\"\r\n\r\n#define LXT_KEYMGMT_DESCRIBE_LENGTH 128\r\n\r\n#define LxtKeyCtl(_Cmd, _Arg2, _Arg3, _Arg4, _Arg5) syscall(SYS_keyctl, (_Cmd), (_Arg2), (_Arg3), (_Arg4), (_Arg5))\r\n#define LxtAdd_Key(_Type, _Desc, _Payload, _Length, _KeyRing) \\\r\n    syscall(SYS_add_key, (_Type), (_Desc), (_Payload), (_Length), (_KeyRing))\r\n#define LxtRequest_Key(_Type, _Desc, _Info, _KeyRing) syscall(SYS_request_key, (_Type), (_Desc), (_Info), (_KeyRing))\r\n\r\n#define KEY_POS_VIEW 0x01000000\r\n#define KEY_POS_READ 0x02000000\r\n#define KEY_POS_WRITE 0x04000000\r\n#define KEY_POS_SEARCH 0x08000000\r\n#define KEY_POS_LINK 0x10000000\r\n#define KEY_POS_SETATTR 0x20000000\r\n#define KEY_POS_ALL 0x3f000000\r\n#define KEY_USR_VIEW 0x00010000\r\n#define KEY_USR_READ 0x00020000\r\n#define KEY_USR_WRITE 0x00040000\r\n#define KEY_USR_SEARCH 0x00080000\r\n#define KEY_USR_LINK 0x00100000\r\n#define KEY_USR_SETATTR 0x00200000\r\n#define KEY_USR_ALL 0x003f0000\r\n#define KEY_GRP_VIEW 0x00000100\r\n#define KEY_GRP_READ 0x00000200\r\n#define KEY_GRP_WRITE 0x00000400\r\n#define KEY_GRP_SEARCH 0x00000800\r\n#define KEY_GRP_LINK 0x00001000\r\n#define KEY_GRP_SETATTR 0x00002000\r\n#define KEY_GRP_ALL 0x00003f00\r\n#define KEY_OTH_VIEW 0x00000001\r\n#define KEY_OTH_READ 0x00000002\r\n#define KEY_OTH_WRITE 0x00000004\r\n#define KEY_OTH_SEARCH 0x00000008\r\n#define KEY_OTH_LINK 0x00000010\r\n#define KEY_OTH_SETATTR 0x00000020\r\n#define KEY_OTH_ALL 0x0000003f\r\n\r\n#define KEY_INVALID -1\r\n\r\n#define LXT_KEYMGMT_ALLPERMS (KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL)\r\n\r\n#define LXT_KEYMGMT_DEFAULTPERMS (0x3f130000)\r\n#define LXT_KEYMGMT_DEFAULTPERMS_STRING \"3f130000\"\r\n\r\n#define LXT_KEYMGMT_NEWPERMS (0x3f3f0000)\r\n#define LXT_KEYMGMT_NEWPERMS_STRING \"3f3f0000\"\r\n\r\n#define LXT_KEYMGMT_SESIONKEYRING_NAME \"sessionkeyring\"\r\n#define LXT_KEYMGMT_SESIONKEYRING_DEFAULTPERMS \"keyring;0;0;\" LXT_KEYMGMT_DEFAULTPERMS_STRING \";\" LXT_KEYMGMT_SESIONKEYRING_NAME\r\n#define LXT_KEYMGMT_SESIONKEYRING_NEWPERMS \"keyring;0;0;\" LXT_KEYMGMT_NEWPERMS_STRING \";\" LXT_KEYMGMT_SESIONKEYRING_NAME\r\n\r\n#define LXT_KEYMGMT_SESIONKEYRING2_NAME \"sessionkeyring2\"\r\n#define LXT_KEYMGMT_SESIONKEYRING2_DEFAULTPERMS \"keyring;0;0;\" LXT_KEYMGMT_DEFAULTPERMS_STRING \";\" LXT_KEYMGMT_SESIONKEYRING2_NAME\r\n\r\n#define LX_KEYMGMT_LONG_NAME_SIZE (4096 + 1)\r\n\r\nLXT_VARIATION_HANDLER KeymgmtSessionKeyringAssociation;\r\n\r\nLXT_VARIATION_HANDLER KeymgmtJoinSessionKeyring;\r\n\r\nLXT_VARIATION_HANDLER KeymgmtDescribe;\r\n\r\nLXT_VARIATION_HANDLER KeymgmtSetPerm;\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\n//\r\n// TODO_LX: Enable KeymgmtSessionKeyringAssociation when supported.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Keymgmt - KEYCTL_JOIN_SESSION_KEYRING\", KeymgmtJoinSessionKeyring},\r\n    {\"Keymgmt - KEYCTL_DESCRIBE\", KeymgmtDescribe},\r\n    {\"Keymgmt - KEYCTL_SETPERM\", KeymgmtSetPerm},\r\n    /*{\"Keymgmt session keyring association\", KeymgmtSessionKeyringAssociation}*/};\r\n\r\nint KeymgmtTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nvoid* KeymgmtSessionKeyringAssociationThread(void* Args)\r\n\r\n{\r\n\r\n    char KeyBufferNew[LXT_KEYMGMT_DESCRIBE_LENGTH];\r\n    int32_t* KeySerial;\r\n    int32_t KeySerialNew;\r\n    int KeyType;\r\n    int Result;\r\n\r\n    KeyType = KEY_SPEC_SESSION_KEYRING;\r\n\r\n    //\r\n    // Check that the session keyring id didn't change.\r\n    //\r\n\r\n    KeySerial = Args;\r\n    LxtCheckErrno(KeySerialNew = LxtKeyCtl(KEYCTL_GET_KEYRING_ID, KeyType, 0, 0, 0));\r\n    LxtCheckEqual(*KeySerial, KeySerialNew, \"%d\");\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerialNew, LXT_KEYMGMT_ALLPERMS, 0, 0));\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerialNew, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n    LxtCheckStringNotEqual(LXT_KEYMGMT_SESIONKEYRING_DEFAULTPERMS, KeyBufferNew);\r\n\r\nErrorExit:\r\n    pthread_exit(&Result);\r\n}\r\n\r\nint KeymgmtSessionKeyringAssociation(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    char KeyBuffer[LXT_KEYMGMT_DESCRIBE_LENGTH];\r\n    char KeyBufferNew[LXT_KEYMGMT_DESCRIBE_LENGTH];\r\n    int32_t KeySerial;\r\n    int32_t KeySerialNew;\r\n    int32_t KeySerialOriginal;\r\n    int KeyType;\r\n    int Result;\r\n    pthread_t Thread = {0};\r\n\r\n    ChildPid = -1;\r\n    KeyType = KEY_SPEC_SESSION_KEYRING;\r\n\r\n    //\r\n    // This test checks to see where the session keyring is associated. The\r\n    // documentation is unclear if it is the threadgroup, thread, or user\r\n    // namespace. The test below validates that the session keyring is\r\n    // associated to the threadgroup and inherited across fork.\r\n    //\r\n\r\n    //\r\n    // Get the current session keyring and check that is changes when a new\r\n    // session keyring is created.\r\n    //\r\n\r\n    KeySerialOriginal = LxtKeyCtl(KEYCTL_GET_KEYRING_ID, KeyType, 0, 0, 0);\r\n    if (KeySerialOriginal == -1)\r\n    {\r\n        KeySerialOriginal = 0;\r\n    }\r\n\r\n    LxtCheckErrno(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LXT_KEYMGMT_SESIONKEYRING_NAME, 0, 0, 0));\r\n    LxtCheckNotEqual(KeySerialOriginal, KeySerial, \"%d\");\r\n    LxtLogInfo(\"Key %d\", KeySerial);\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBuffer, sizeof(KeyBuffer), 0));\r\n    LxtCheckStringEqual(KeyBuffer, LXT_KEYMGMT_SESIONKEYRING_DEFAULTPERMS);\r\n\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, LXT_KEYMGMT_ALLPERMS, 0, 0));\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n    LxtCheckStringNotEqual(KeyBuffer, KeyBufferNew);\r\n\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, LXT_KEYMGMT_DEFAULTPERMS, 0, 0));\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n    LxtCheckStringEqual(KeyBuffer, KeyBufferNew);\r\n\r\n    //\r\n    // Create a child process and thread, checking that the session keyring id\r\n    // continues to be associated.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(KeySerialNew = LxtKeyCtl(KEYCTL_GET_KEYRING_ID, KeyType, 0, 0, 0));\r\n        LxtCheckEqual(KeySerial, KeySerialNew, \"%d\");\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, LXT_KEYMGMT_ALLPERMS, 0, 0));\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n        LxtCheckStringNotEqual(KeyBuffer, KeyBufferNew);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // The changes from the child threadgroup should reflect into the parent.\r\n    //\r\n\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n    LxtCheckStringNotEqual(KeyBuffer, KeyBufferNew);\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, LXT_KEYMGMT_DEFAULTPERMS, 0, 0));\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n    LxtCheckStringEqual(KeyBuffer, KeyBufferNew);\r\n\r\n    //\r\n    // Repeat the scenario with a thread.\r\n    //\r\n\r\n    LxtCheckErrno(pthread_create(&Thread, NULL, KeymgmtSessionKeyringAssociationThread, &KeySerial));\r\n\r\n    pthread_join(Thread, NULL);\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n    LxtCheckStringNotEqual(KeyBuffer, KeyBufferNew);\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, LXT_KEYMGMT_DEFAULTPERMS, 0, 0));\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n    LxtCheckStringEqual(KeyBuffer, KeyBufferNew);\r\n\r\n    //\r\n    // Create a user namespace and check that the session keyring id continues\r\n    // to be associated.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(KeySerialNew = LxtKeyCtl(KEYCTL_GET_KEYRING_ID, KeyType, 0, 0, 0));\r\n        LxtCheckEqual(KeySerial, KeySerialNew, \"%d\");\r\n        LxtCheckErrno(unshare(CLONE_NEWUSER));\r\n        LxtCheckErrno(KeySerialNew = LxtKeyCtl(KEYCTL_GET_KEYRING_ID, KeyType, 0, 0, 0));\r\n        LxtCheckEqual(KeySerial, KeySerialNew, \"%d\");\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, LXT_KEYMGMT_ALLPERMS, 0, 0));\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n        LxtCheckStringNotEqual(KeyBuffer, KeyBufferNew);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // The changes from the child threadgroup should reflect into the parent.\r\n    //\r\n\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n    LxtCheckStringNotEqual(KeyBuffer, KeyBufferNew);\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, LXT_KEYMGMT_DEFAULTPERMS, 0, 0));\r\n    LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBufferNew, sizeof(KeyBufferNew), 0));\r\n    LxtCheckStringEqual(KeyBuffer, KeyBufferNew);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint KeymgmtJoinSessionKeyring(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid = -1;\r\n    int Index;\r\n    char* LongName = NULL;\r\n    char KeyBuffer[LXT_KEYMGMT_DESCRIBE_LENGTH];\r\n    int32_t KeySerial;\r\n    int32_t KeySerial2;\r\n    LXT_PIPE Pipe = {-1, -1};\r\n    int Result;\r\n    char* ValidNames[] = {\"1\", \"a\", \"1a\", \";\", \"name with a space \", \"name with a tab\\t\", \"name with a new line\\n\"};\r\n\r\n    //\r\n    // This test checks how KEYCTL_JOIN_SESSION_KEYRING handles keyrings.\r\n    //\r\n\r\n    //\r\n    // Check for valid names.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(ValidNames); ++Index)\r\n    {\r\n        LxtCheckErrno(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, ValidNames[Index], 0, 0, 0));\r\n    }\r\n\r\n    //\r\n    // Check for a really long name.\r\n    //\r\n\r\n    LongName = LxtAlloc(LX_KEYMGMT_LONG_NAME_SIZE);\r\n    if (LongName == 0)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(LongName, 'a', LX_KEYMGMT_LONG_NAME_SIZE);\r\n    LongName[LX_KEYMGMT_LONG_NAME_SIZE - 1] = 0;\r\n    LxtCheckErrnoFailure(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LongName, 0, 0, 0), EINVAL);\r\n    LongName[LX_KEYMGMT_LONG_NAME_SIZE - 2] = 0;\r\n    LxtCheckErrno(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LongName, 0, 0, 0));\r\n\r\n    //\r\n    // TODO_LX: Add support for NULL name when supported.\r\n    //\r\n\r\n    //\r\n    // Invalid parameters.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, (void*)0x1, 0, 0, 0), EFAULT);\r\n\r\n    //\r\n    // Check for lifetime.\r\n    //\r\n\r\n    LxtCheckResult(LxtCreatePipe(&Pipe));\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LXT_KEYMGMT_SESIONKEYRING_NAME, 0, 0, 0));\r\n        LxtCheckErrno(write(Pipe.Write, &KeySerial, sizeof(KeySerial)));\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckErrno(read(Pipe.Read, &KeySerial, sizeof(KeySerial)));\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    sleep(1);\r\n    LxtCheckErrnoFailure(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBuffer, sizeof(KeyBuffer), 0), ENOKEY);\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LXT_KEYMGMT_SESIONKEYRING_NAME, 0, 0, 0));\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBuffer, sizeof(KeyBuffer), 0));\r\n        LxtCheckStringEqual(KeyBuffer, LXT_KEYMGMT_SESIONKEYRING_DEFAULTPERMS);\r\n        LxtCheckErrno(KeySerial2 = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LXT_KEYMGMT_SESIONKEYRING2_NAME, 0, 0, 0));\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial2, KeyBuffer, sizeof(KeyBuffer), 0));\r\n        LxtCheckStringEqual(KeyBuffer, LXT_KEYMGMT_SESIONKEYRING2_DEFAULTPERMS);\r\n        sleep(1);\r\n        LxtCheckErrnoFailure(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBuffer, sizeof(KeyBuffer), 0), ENOKEY);\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (LongName != NULL)\r\n    {\r\n        LxtFree(LongName);\r\n    }\r\n\r\n    LxtClosePipe(&Pipe);\r\n    return Result;\r\n}\r\n\r\nint KeymgmtDescribe(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid = -1;\r\n    int BytesRequired;\r\n    char KeyBuffer[LXT_KEYMGMT_DESCRIBE_LENGTH];\r\n    int32_t KeySerial;\r\n    int Result;\r\n\r\n    //\r\n    // This test checks how KEYCTL_DESCRIBE handles parameters.\r\n    //\r\n\r\n    //\r\n    // Check for the default values.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LXT_KEYMGMT_SESIONKEYRING_NAME, 0, 0, 0));\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBuffer, sizeof(KeyBuffer), 0));\r\n        LxtCheckStringEqual(KeyBuffer, LXT_KEYMGMT_SESIONKEYRING_DEFAULTPERMS);\r\n        LxtCheckErrno(BytesRequired = LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, NULL, 0, 0));\r\n        LxtCheckEqual(BytesRequired, sizeof(LXT_KEYMGMT_SESIONKEYRING_DEFAULTPERMS), \"%d\");\r\n        LxtCheckErrno(BytesRequired = LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, (void*)0x1, 1, 0));\r\n        LxtCheckEqual(BytesRequired, sizeof(LXT_KEYMGMT_SESIONKEYRING_DEFAULTPERMS), \"%d\");\r\n        _exit(0);\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    //\r\n    // TODO_LX: Add support for NULL name when supported.\r\n    //\r\n\r\n    //\r\n    // Invalid parameters.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LXT_KEYMGMT_SESIONKEYRING_NAME, 0, 0, 0));\r\n        LxtCheckErrnoFailure(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, (void*)0x1, sizeof(KeyBuffer), 0), EFAULT);\r\n        LxtCheckErrnoFailure(LxtKeyCtl(KEYCTL_DESCRIBE, KEY_INVALID, KeyBuffer, sizeof(KeyBuffer), 0), ENOKEY);\r\n        _exit(0);\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint KeymgmtSetPerm(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid = -1;\r\n    int BytesRequired;\r\n    char KeyBuffer[LXT_KEYMGMT_DESCRIBE_LENGTH];\r\n    int32_t KeySerial;\r\n    int Result;\r\n\r\n    //\r\n    // This test checks how KEYCTL_SETPERM handles parameters.\r\n    //\r\n\r\n    //\r\n    // Check for the default values.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LXT_KEYMGMT_SESIONKEYRING_NAME, 0, 0, 0));\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBuffer, sizeof(KeyBuffer), 0));\r\n        LxtCheckStringEqual(KeyBuffer, LXT_KEYMGMT_SESIONKEYRING_DEFAULTPERMS);\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, LXT_KEYMGMT_DEFAULTPERMS, 0, 0));\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBuffer, sizeof(KeyBuffer), 0));\r\n        LxtCheckStringEqual(KeyBuffer, LXT_KEYMGMT_SESIONKEYRING_DEFAULTPERMS);\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, LXT_KEYMGMT_NEWPERMS, 0, 0));\r\n        LxtCheckErrno(LxtKeyCtl(KEYCTL_DESCRIBE, KeySerial, KeyBuffer, sizeof(KeyBuffer), 0));\r\n        LxtCheckStringEqual(KeyBuffer, LXT_KEYMGMT_SESIONKEYRING_NEWPERMS);\r\n        _exit(0);\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    //\r\n    // TODO_LX: Add support for NULL name when supported.\r\n    //\r\n\r\n    //\r\n    // Invalid parameters.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(KeySerial = LxtKeyCtl(KEYCTL_JOIN_SESSION_KEYRING, LXT_KEYMGMT_SESIONKEYRING_NAME, 0, 0, 0));\r\n        LxtCheckErrnoFailure(LxtKeyCtl(KEYCTL_SETPERM, 0, LXT_KEYMGMT_DEFAULTPERMS, 0, 0), EINVAL);\r\n        LxtCheckErrnoFailure(LxtKeyCtl(KEYCTL_SETPERM, KeySerial, -1, 0, 0), EINVAL);\r\n        _exit(0);\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/lxtcommon.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtcommon.h\r\n\r\nAbstract:\r\n\r\n    This is a common header file for lx tests.\r\n\r\n--*/\r\n\r\n#ifndef _LXT_COMMON\r\n#define _LXT_COMMON\r\n\r\n#include \"lxtlog.h\"\r\n#include \"lxtutil.h\"\r\n#include \"lxtevent.h\"\r\n\r\n#ifndef PAGE_SIZE\r\n#define PAGE_SIZE 0x1000\r\n#endif\r\n\r\n#define LXSS_DISTRO_NAME_TEST \"test_distro\"\r\n\r\n#endif // _LXT_COMMON\r\n"
  },
  {
    "path": "test/linux/unit_tests/lxtevent.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtevent.c\r\n\r\nAbstract:\r\n\r\n    This file contains synchronization event primitive support. It enables\r\n    simple synchronization across threads and forked processes.\r\n\r\n--*/\r\n\r\n#include \"lxtevent.h\"\r\n#include \"lxtlog.h\"\r\n#include <sys/mman.h>\r\n\r\nint LxtSynchronizationEventClear(PLXT_SYNCHRONIZATION_EVENT Event)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine clears the synchronization event.\r\n\r\nArguments:\r\n\r\n    Event - Supplies a pointer to synchronization event.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    LxtCheckResult(pthread_mutex_lock(&Event->Lock));\r\n    if (Event->Fail != 0)\r\n    {\r\n        LxtCheckResult(pthread_mutex_unlock(&Event->Lock));\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Event->Ready = 0;\r\n    LxtCheckResult(pthread_mutex_unlock(&Event->Lock));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSynchronizationEventDestroy(PLXT_SYNCHRONIZATION_EVENT* Event)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine frees all resources allocated for the event.\r\n\r\nArguments:\r\n\r\n    Event - Supplies a pointer to synchronization event.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    LxtCheckResult(pthread_mutex_destroy(&(*Event)->Lock));\r\n    LxtCheckResult(pthread_mutexattr_destroy(&(*Event)->LockAttribute));\r\n    LxtCheckResult(pthread_cond_destroy(&(*Event)->WaitConditionalVariable));\r\n    LxtCheckResult(pthread_condattr_destroy(&(*Event)->ConditionVariableAttribute));\r\n    LxtCheckResult(munmap(*Event, sizeof(*Event)));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSynchronizationEventFail(PLXT_SYNCHRONIZATION_EVENT Event)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets the fail flag and causes blocked event to be woken up and\r\n    return with error and also any further calls to set/wait on the event to\r\n    fail.\r\n\r\nArguments:\r\n\r\n    Event - Supplies a pointer to synchronization event.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    LxtCheckResult(pthread_mutex_lock(&Event->Lock));\r\n    Event->Fail = 1;\r\n    LxtCheckResult(pthread_mutex_unlock(&Event->Lock));\r\n    LxtCheckResult(pthread_cond_signal(&Event->WaitConditionalVariable));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSynchronizationEventInit(PLXT_SYNCHRONIZATION_EVENT* Event)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine initializes synchronization event.\r\n\r\nArguments:\r\n\r\n    Event - Supplies a pointer to where synchronization event will be allocated.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    void* MapResult;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    PLXT_SYNCHRONIZATION_EVENT LocalEvent = NULL;\r\n\r\n    LxtCheckMapErrno(LocalEvent = mmap(NULL, sizeof(*Event), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0));\r\n\r\n    LxtCheckResult(pthread_mutexattr_init(&LocalEvent->LockAttribute));\r\n    LxtCheckResult(pthread_mutexattr_setpshared(&LocalEvent->LockAttribute, PTHREAD_PROCESS_SHARED));\r\n\r\n    LxtCheckResult(pthread_mutex_init(&LocalEvent->Lock, &LocalEvent->LockAttribute));\r\n    LxtCheckResult(pthread_condattr_init(&LocalEvent->ConditionVariableAttribute));\r\n    LxtCheckResult(pthread_condattr_setpshared(&LocalEvent->ConditionVariableAttribute, PTHREAD_PROCESS_SHARED));\r\n\r\n    LxtCheckResult(pthread_cond_init(&LocalEvent->WaitConditionalVariable, &LocalEvent->ConditionVariableAttribute));\r\n\r\n    LocalEvent->Ready = 0;\r\n    LocalEvent->Fail = 0;\r\n    *Event = LocalEvent;\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSynchronizationEventReset(PLXT_SYNCHRONIZATION_EVENT Event)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine resets event to the initialized state.\r\n\r\nArguments:\r\n\r\n    Event - Supplies a pointer to synchronization event.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    LxtCheckResult(pthread_mutex_lock(&Event->Lock));\r\n    LxtCheckResult(pthread_cond_init(&Event->WaitConditionalVariable, &Event->ConditionVariableAttribute));\r\n\r\n    Event->Fail == 0;\r\n    Event->Ready = 0;\r\n    LxtCheckResult(pthread_mutex_unlock(&Event->Lock));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSynchronizationEventSet(PLXT_SYNCHRONIZATION_EVENT Event)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets the synchronization event.\r\n\r\nArguments:\r\n\r\n    Event - Supplies a pointer to synchronization event.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    LxtCheckResult(pthread_mutex_lock(&Event->Lock));\r\n    if (Event->Fail != 0)\r\n    {\r\n        LxtCheckResult(pthread_mutex_unlock(&Event->Lock));\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Event->Ready = 1;\r\n    LxtCheckResult(pthread_mutex_unlock(&Event->Lock));\r\n    LxtCheckResult(pthread_cond_signal(&Event->WaitConditionalVariable));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSynchronizationEventWait(PLXT_SYNCHRONIZATION_EVENT Event)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine blocks on event until it is signalled.\r\n\r\nArguments:\r\n\r\n    Event - Supplies a pointer to synchronization event.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    LxtCheckResult(pthread_mutex_lock(&Event->Lock));\r\n    while ((Event->Ready == 0) && (Event->Fail == 0))\r\n    {\r\n        LxtCheckResult(pthread_cond_wait(&Event->WaitConditionalVariable, &Event->Lock));\r\n    }\r\n\r\n    if (Event->Fail != 0)\r\n    {\r\n        LxtCheckResult(pthread_mutex_unlock(&Event->Lock));\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(pthread_mutex_unlock(&Event->Lock));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/lxtevent.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtevent.c\r\n\r\nAbstract:\r\n\r\n    This file contains synchronization event primitive support.\r\n\r\n--*/\r\n\r\n#include <pthread.h>\r\n\r\n//\r\n// Synchronization event usable for synchronizing threads and forked processes.\r\n//\r\n\r\ntypedef struct _LXT_SYNCHRONIZATION_EVENT\r\n{\r\n    pthread_cond_t WaitConditionalVariable;\r\n    pthread_condattr_t ConditionVariableAttribute;\r\n    pthread_mutex_t Lock;\r\n    pthread_mutexattr_t LockAttribute;\r\n    int Ready;\r\n    int Fail;\r\n} LXT_SYNCHRONIZATION_EVENT, *PLXT_SYNCHRONIZATION_EVENT;\r\n\r\nint LxtSynchronizationEventClear(PLXT_SYNCHRONIZATION_EVENT Event);\r\n\r\nint LxtSynchronizationEventDestroy(PLXT_SYNCHRONIZATION_EVENT* Event);\r\n\r\nint LxtSynchronizationEventFail(PLXT_SYNCHRONIZATION_EVENT Event);\r\n\r\nint LxtSynchronizationEventInit(PLXT_SYNCHRONIZATION_EVENT* Event);\r\n\r\nint LxtSynchronizationEventReset(PLXT_SYNCHRONIZATION_EVENT Event);\r\n\r\nint LxtSynchronizationEventSet(PLXT_SYNCHRONIZATION_EVENT Event);\r\n\r\nint LxtSynchronizationEventWait(PLXT_SYNCHRONIZATION_EVENT Event);\r\n\r\n//\r\n// Synchronization point based on synchronization event.\r\n//\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(_ChildPidVariable_) \\\r\n    PLXT_SYNCHRONIZATION_EVENT LxtSync##_ChildPidVariable_##Parent; \\\r\n    PLXT_SYNCHRONIZATION_EVENT LxtSync##_ChildPidVariable_##Child;\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_DECLARE_FOR_STATIC(_ChildPidVariable_) \\\r\n    static PLXT_SYNCHRONIZATION_EVENT LxtSync##_ChildPidVariable_##Parent; \\\r\n    static PLXT_SYNCHRONIZATION_EVENT LxtSync##_ChildPidVariable_##Child;\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_INIT_SYNCVARS(_ParentVar_, _ChildVar_) \\\r\n    (_ParentVar_) = NULL; \\\r\n    (_ChildVar_) = NULL; \\\r\n    LxtCheckResult(LxtSynchronizationEventInit(&_ParentVar_)); \\\r\n    LxtCheckResult(LxtSynchronizationEventInit(&_ChildVar_));\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_INIT_FOR(_ChildPidVariable_) \\\r\n    LXT_SYNCHRONIZATION_POINT_INIT_SYNCVARS(LxtSync##_ChildPidVariable_##Parent, LxtSync##_ChildPidVariable_##Child)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_INIT() LXT_SYNCHRONIZATION_POINT_INIT_FOR(ChildPid)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_DESTROY_SYNCVARS(_ParentVar_, _ChildVar_) \\\r\n    LxtSynchronizationEventDestroy(&(_ParentVar_)); \\\r\n    LxtSynchronizationEventDestroy(&(_ChildVar_));\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_DESTROY_FOR(_ChildPidVariable_) \\\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY_SYNCVARS(LxtSync##_ChildPidVariable_##Parent, LxtSync##_ChildPidVariable_##Child)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_DESTROY() LXT_SYNCHRONIZATION_POINT_DESTROY_FOR(ChildPid)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_START_SYNCVARS(_ParentVar_, _ChildVar_) \\\r\n    LxtSynchronizationEventReset(_ChildVar_); \\\r\n    LxtSynchronizationEventReset(_ParentVar_);\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_START_FOR(_ChildPidVariable_) \\\r\n    LXT_SYNCHRONIZATION_POINT_START_SYNCVARS(LxtSync##_ChildPidVariable_##Child, LxtSync##_ChildPidVariable_##Parent)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_START() LXT_SYNCHRONIZATION_POINT_START_FOR(ChildPid)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_END_SYNCVARS(_ChildId_, _ParentVar_, _ChildVar_, _Destroy_) \\\r\n    if (((_ChildId_) >= 0) && (Result < 0)) \\\r\n    { \\\r\n        LxtLogError(\"Failing synchronization points.\"); \\\r\n        LxtSynchronizationEventFail(_ChildVar_); \\\r\n        LxtSynchronizationEventFail(_ParentVar_); \\\r\n    } \\\r\n    if ((_ChildId_) > 0) \\\r\n    { \\\r\n        if (TEMP_FAILURE_RETRY(waitpid((_ChildId_), &Status, 0)) >= 0) \\\r\n        { \\\r\n            if (!WIFEXITED(Status)) \\\r\n            { \\\r\n                LxtLogInfo(\"Child exited uncleanly (Child = %d, Status = %x)\", (_ChildId_), Status); \\\r\n                Result = LXT_RESULT_FAILURE; \\\r\n            } \\\r\n            else \\\r\n            { \\\r\n                Result = (int)(char)WEXITSTATUS(Status); \\\r\n            } \\\r\n        } \\\r\n        else \\\r\n        { \\\r\n            Result = errno; \\\r\n            LxtLogInfo(\"Failed wait on child %d with errno %d\", (_ChildId_), Result); \\\r\n        } \\\r\n        if ((Result == LXT_RESULT_SUCCESS) && ((_ChildVar_)->Fail == 1)) \\\r\n        { \\\r\n            LxtLogInfo(\"Child failed\"); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n        } \\\r\n        if ((_Destroy_) != FALSE) \\\r\n        { \\\r\n            LXT_SYNCHRONIZATION_POINT_DESTROY_SYNCVARS(_ParentVar_, _ChildVar_) \\\r\n        } \\\r\n    } \\\r\n    else if ((_ChildId_) == 0) \\\r\n    { \\\r\n        _exit(Result); \\\r\n    }\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_END_FOR(_ChildPidVariable_, _Destroy_) \\\r\n    LXT_SYNCHRONIZATION_POINT_END_SYNCVARS((_ChildPidVariable_), LxtSync##_ChildPidVariable_##Parent, LxtSync##_ChildPidVariable_##Child, _Destroy_)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_END() LXT_SYNCHRONIZATION_POINT_END_FOR(ChildPid, FALSE)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD_SYNCVARS(_ParentVar_, _ChildVar_) \\\r\n    if (Result < 0) \\\r\n    { \\\r\n        LxtLogError(\"Failing synchronization points.\"); \\\r\n        LxtSynchronizationEventFail(_ChildVar_); \\\r\n        LxtSynchronizationEventFail(_ParentVar_); \\\r\n    }\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD_FOR(_SyncIdVariable_) \\\r\n    LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD_SYNCVARS(LxtSync##_SyncIdVariable_##Parent, LxtSync##_SyncIdVariable_##Child)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD() LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD_FOR(ChildPid)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_PTHREAD_END_PARENT_SYNCVARS(_ThreadId_, _ParentVar_, _ChildVar_) \\\r\n    if (Result < 0) \\\r\n    { \\\r\n        LxtLogError(\"Failing synchronization points.\"); \\\r\n        LxtSynchronizationEventFail(_ChildVar_); \\\r\n        LxtSynchronizationEventFail(_ParentVar_); \\\r\n    } \\\r\n    if (((_ThreadId_) > 0) && (pthread_join((_ThreadId_), &Status) == 0)) \\\r\n    { \\\r\n        if (Status != 0) \\\r\n        { \\\r\n            LxtLogInfo(\"Thread exited uncleanly (Thread = %d, Status = %x)\", (_ThreadId_), (int)(long)Status); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n        } \\\r\n    } \\\r\n    else if ((_ThreadId_) > 0) \\\r\n    { \\\r\n        Result = errno; \\\r\n        LxtLogInfo(\"Failed wait on thread %d with errno %d\", (_ThreadId_), Result); \\\r\n    } \\\r\n    if ((Result == LXT_RESULT_SUCCESS) && ((_ChildVar_)->Fail == 1)) \\\r\n    { \\\r\n        LxtLogInfo(\"Thread failed\"); \\\r\n        Result = LXT_RESULT_FAILURE; \\\r\n    }\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_PTHREAD_END_PARENT_FOR(_ThreadId_, _SyncIdVariable_) \\\r\n    LXT_SYNCHRONIZATION_POINT_PTHREAD_END_PARENT_SYNCVARS((_ThreadId_), LxtSync##_SyncIdVariable_##Parent, LxtSync##_SyncIdVariable_##Child)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_PTHREAD_END_PARENT(_ThreadId_) \\\r\n    LXT_SYNCHRONIZATION_POINT_PTHREAD_END_PARENT_FOR(_ThreadId_, ChildPid)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_CLEAR_FOR(_ChildPidVariable_) \\\r\n    if (Result < 0) \\\r\n    { \\\r\n        LxtCheckResult(LxtSynchronizationEventClear(LxtSync##_ChildPidVariable_##Child)); \\\r\n        LxtCheckResult(LxtSynchronizationEventClear(LxtSync##_ChildPidVariable_##Parent)); \\\r\n    }\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_CLEAR() LXT_SYNCHRONIZATION_POINT_CLEAR_FOR(ChildPid)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_SYNCVARS(_IsChild_, _ParentVar_, _ChildVar_) \\\r\n    if ((_IsChild_) != FALSE) \\\r\n    { \\\r\n        LxtCheckResult(LxtSynchronizationEventWait(_ChildVar_)); \\\r\n        LxtCheckResult(LxtSynchronizationEventClear(_ChildVar_)); \\\r\n        LxtCheckResult(LxtSynchronizationEventSet(_ParentVar_)); \\\r\n    } \\\r\n    else \\\r\n    { \\\r\n        LxtCheckResult(LxtSynchronizationEventSet(_ChildVar_)); \\\r\n        LxtCheckResult(LxtSynchronizationEventWait(_ParentVar_)); \\\r\n        LxtCheckResult(LxtSynchronizationEventClear(_ParentVar_)); \\\r\n    }\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_CHILD_FOR(_ChildPidVariable_) \\\r\n    LXT_SYNCHRONIZATION_POINT_SYNCVARS(TRUE, LxtSync##_ChildPidVariable_##Parent, LxtSync##_ChildPidVariable_##Child)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_PARENT_FOR(_ChildPidVariable_) \\\r\n    LXT_SYNCHRONIZATION_POINT_SYNCVARS(FALSE, LxtSync##_ChildPidVariable_##Parent, LxtSync##_ChildPidVariable_##Child)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_FOR(_ChildPidVariable_) \\\r\n    if ((_ChildPidVariable_) == 0) \\\r\n    { \\\r\n        LXT_SYNCHRONIZATION_POINT_CHILD_FOR(_ChildPidVariable_); \\\r\n    } \\\r\n    else \\\r\n    { \\\r\n        LXT_SYNCHRONIZATION_POINT_PARENT_FOR(_ChildPidVariable_); \\\r\n    }\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_CHILD() LXT_SYNCHRONIZATION_POINT_CHILD_FOR(ChildPid)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT_PARENT() LXT_SYNCHRONIZATION_POINT_PARENT_FOR(ChildPid)\r\n\r\n#define LXT_SYNCHRONIZATION_POINT() LXT_SYNCHRONIZATION_POINT_FOR(ChildPid);\r\n\r\n//\r\n// Declare global synchronization values for common ChildPid variable.\r\n//\r\n\r\nLXT_SYNCHRONIZATION_POINT_DECLARE_FOR_STATIC(ChildPid)"
  },
  {
    "path": "test/linux/unit_tests/lxtfs.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtfs.c\r\n\r\nAbstract:\r\n\r\n    This file contains common test functions for file system tests.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"lxtfs.h\"\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/uio.h>\r\n#include <sys/epoll.h>\r\n#include <sys/inotify.h>\r\n#include <sys/mount.h>\r\n#include <sys/xattr.h>\r\n#include <sys/wait.h>\r\n#include <sys/mman.h>\r\n#include <fcntl.h>\r\n#include <stdio.h>\r\n#include <limits.h>\r\n#include <dirent.h>\r\n#include <stdlib.h>\r\n#include <libmount/libmount.h>\r\n#include \"lxtmount.h\"\r\n\r\n#define FS_RENAMEAT_TEST_FILE \"file\"\r\n#define FS_RENAMEAT_TEST_FILE2 \"file2\"\r\n#define FS_UTIME_TESTFILE \"testfile\"\r\n#define FS_UTIME_TESTLINK \"testlink\"\r\n#define FS_NS_PER_SEC (1000000000ull)\r\n#define FS_NS_PER_NT_UNIT (100ull)\r\n#define FS_UNIX_TIME_2000 (946684800)\r\n#define FS_SECONDS_PER_DAY (86400)\r\n#define FS_FAT_MODIFIED_TIME_PRECISION (2)\r\n#define FS_CURRENT_TIME_ALLOWED_VARIANCE (2)\r\n\r\n//\r\n// The following macros are used by LxtFsDeleteCurrentWorkingDirectoryCommon\r\n// and LxtFsDeleteOpenFileCommon.\r\n//\r\n\r\n#define FS_DELETE_TEST_DIR_NAME \"delete_test\"\r\n#define FS_DELETE_TEST_DIR FS_TEST_DIR_PARENT \"/\" FS_DELETE_TEST_DIR_NAME\r\n#define FS_DELETE_TEST_RENAME_FILE_NAME \"/delete_test_file\"\r\n#define FS_DELETE_TEST_RENAME_FILE FS_TEST_DIR_PARENT FS_DELETE_TEST_RENAME_FILE_NAME\r\n#define FS_DELETE_TEST_DIR_AT \"../\" FS_DELETE_TEST_DIR_NAME\r\n#define FS_DELETE_TEST_CHILD \"child\"\r\n#define FS_DELETE_LINK_SUFFIX \" (deleted)\"\r\n#define FS_CHILD_PATH_FORMAT \"%s/%s\"\r\n#define FS_FD_PATH_FORMAT \"/proc/self/fd/%d\"\r\n#define FS_PROC_SELF_CWD \"/proc/self/cwd\"\r\n\r\n//\r\n// The following macros are used by LxtFsRenameDirCommon.\r\n//\r\n\r\n#define FS_RENAME_TEST_DIR \"/rename_test\"\r\n#define FS_RENAME_TEST_DIR2 \"/rename_test2\"\r\n#define FS_RENAME_TEST_DIR3 \"/rename_test3\"\r\n#define FS_RENAME_TEST_FILE \"/rename_test_file\"\r\n#define FS_RENAME_TEST_DIR_CHILD FS_RENAME_TEST_DIR \"/child\"\r\n#define FS_RENAME_TEST_DIR_CHILD2 FS_RENAME_TEST_DIR \"/child2\"\r\n#define FS_RENAME_TEST_DIR2_CHILD FS_RENAME_TEST_DIR2 \"/child\"\r\n#define FS_RENAME_TEST_DIR2_CHILD2 FS_RENAME_TEST_DIR2 \"/child2\"\r\n#define FS_RENAME_TEST_DIR_GRANDCHILD FS_RENAME_TEST_DIR_CHILD \"/child2\"\r\n#define FS_RENAME_TEST_DIR_SLASH \"/rename_test_slash/\"\r\n#define FS_RENAME_TEST_DIR_SLASH2 \"/rename_test_slash2\"\r\n#define FS_RENAME_TEST_DIR_SLASH_LINK \"/rename_test_slash_link\"\r\n#define FS_RENAME_TEST_DIR_SLASH_LINK2 \"/rename_test_slash_link2\"\r\n\r\n//\r\n// Flags for LxtFsTimestampCheckUpdate\r\n//\r\n\r\n#define FS_TIMESTAMP_ACCESS (0x1)\r\n#define FS_TIMESTAMP_MODIFY (0x2)\r\n#define FS_TIMESTAMP_CHANGE (0x4)\r\n\r\n#define FS_TIMESTAMP_SLEEP_TIME (100000)\r\n\r\n#define FS_MOUNT_DRVFS_COMMAND_FORMAT \"mount -t drvfs %s %s -o rw,noatime\"\r\n#define FS_MOUNT_DRVFS_OPTIONS_COMMAND_FORMAT FS_MOUNT_DRVFS_COMMAND_FORMAT \",%s\"\r\n#define FS_PLAN9_UNC_PREFIX \"UNC\\\\\"\r\n#define FS_PLAN9_UNC_PREFIX_LENGTH (sizeof(FS_PLAN9_UNC_PREFIX) - 1)\r\n#define FS_UNC_PATH_PREFIX_LENGTH (2)\r\n\r\ntypedef struct _BASIC_TEST_CASE\r\n{\r\n    struct timespec SetTime[2];\r\n    struct timespec ExpectTime[2];\r\n} BASIC_TEST_CASE, *PBASIC_TEST_CASE;\r\n\r\nenum\r\n{\r\n    NameVariationFullName,\r\n    NameVariationCwdRelative,\r\n    NameVariationRelative,\r\n    NameVariationDescriptor,\r\n    NameVariationFullFileViaLink,\r\n    NameVariationCwdRelativeViaLink,\r\n    NameVariationRelativeViaLink,\r\n    NameVariationDescriptorViaLink,\r\n    NameVariationFullFileOnLink,\r\n    NameVariationCwdRelativeOnLink,\r\n    NameVariationRelativeOnLink,\r\n    NameVariationMax = NameVariationRelativeOnLink,\r\n    NameVariationFatMax = NameVariationDescriptor\r\n};\r\n\r\nstruct linux_dirent\r\n{\r\n    unsigned long d_ino;\r\n    unsigned long d_off;\r\n    unsigned short d_reclen;\r\n    char d_name[];\r\n    // char           pad;\r\n    // char           d_type;\r\n};\r\n\r\nint LxtFsDeleteCurrentWorkingDirectoryHelper(char* BaseDir, char* DeleteTestDir, int Flags);\r\n\r\nint LxtFsDeleteOpenFileHelper(int Fd, char* BaseDir, char* DeleteTestDir, int Flags);\r\n\r\nint LxtFsTimestampCheckCurrent(const struct timespec* Timestamp);\r\n\r\nint LxtFsTimestampCheckEqual(const struct timespec* Timestamp1, const struct timespec* Timestamp2);\r\n\r\nint LxtFsTimestampCheckGreater(const struct timespec* Timestamp1, const struct timespec* Timestamp2);\r\n\r\nint LxtFsTimestampCheckUpdate(const char* Path, struct stat* PreviousStat, int Flags);\r\n\r\nlong long LxtFsTimestampDiff(const struct timespec* Timestamp1, const struct timespec* Timestamp2);\r\n\r\nbool LxtFsUtimeDoTimesMatch(const struct timespec* Actual, const struct timespec* Expected, int AllowedVarianceSeconds);\r\n\r\nvoid LxtFsUtimeRoundToFatAccessTime(struct timespec* Timespec);\r\n\r\nvoid LxtFsUtimeRoundToFatModifiedTime(struct timespec* Timespec);\r\n\r\nvoid LxtFsUtimeRoundToNt(struct timespec* Timespec);\r\n\r\n//\r\n// All real timestamps are offset from the year 2000 because FAT can only\r\n// accept timestamps afer 1980.\r\n\r\nBASIC_TEST_CASE BasicTestCases[] = {\r\n    {{{FS_UNIX_TIME_2000 + 1111111, 2222222}, {FS_UNIX_TIME_2000 + 3333333, 4444444}},\r\n     {{FS_UNIX_TIME_2000 + 1111111, 2222222}, {FS_UNIX_TIME_2000 + 3333333, 4444444}}},\r\n\r\n    {{{5555555, UTIME_OMIT}, {FS_UNIX_TIME_2000 + 6666666, 7777777}},\r\n     {{FS_UNIX_TIME_2000 + 1111111, 2222222}, {FS_UNIX_TIME_2000 + 6666666, 7777777}}},\r\n\r\n    {{{FS_UNIX_TIME_2000 + 5555555, 8888888}, {9999999, UTIME_OMIT}},\r\n     {{FS_UNIX_TIME_2000 + 5555555, 8888888}, {FS_UNIX_TIME_2000 + 6666666, 7777777}}},\r\n\r\n    {{{1111111, UTIME_NOW}, {FS_UNIX_TIME_2000 + 2222222, 3333333}}, {{5555555, UTIME_NOW}, {FS_UNIX_TIME_2000 + 2222222, 3333333}}},\r\n\r\n    {{{FS_UNIX_TIME_2000 + 1111111, 22222222}, {3333333, UTIME_NOW}}, {{FS_UNIX_TIME_2000 + 1111111, 22222222}, {4444444, UTIME_NOW}}},\r\n\r\n    {{{1111111, UTIME_NOW}, {3333333, UTIME_NOW}}, {{2222222, UTIME_NOW}, {4444444, UTIME_NOW}}},\r\n\r\n    {{{0, UTIME_NOW}, {3333333, UTIME_NOW}}, {{0, UTIME_NOW}, {4444444, UTIME_NOW}}},\r\n\r\n    //\r\n    // This time is at 1am UTC, which is likely to be in the previous day local\r\n    // time (if the test is run on a system in the US). Having this value here\r\n    // ensures the test handles that correctly for FAT timestamp rounding in\r\n    // case it occurs for the current time.\r\n    //\r\n\r\n    {{{1498440508, 22222222}, {3333333, UTIME_NOW}}, {{1498440508, 22222222}, {4444444, UTIME_NOW}}},\r\n};\r\n\r\nLXT_FS_INFO g_LxtFsInfo;\r\n\r\nint LxtFsCheckDrvFsMount(const char* Source, const char* Target, const char* Options, int ParentId, const char* MountRoot)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine verifies the mount options for a drvfs mount.\r\n\r\n    N.B. On WSL 2 this uses 9p mount options.\r\n\r\nArguments:\r\n\r\n    Source - Supplies the mount source.\r\n\r\n    Target - Supplies the mount target.\r\n\r\n    Options - Supplies optional mount options.\r\n\r\n    ParentId - Supplies the expected parent ID of the mount.\r\n\r\n    MountRoot - Supplies the expected mount root.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char ExpectedOptions[1024];\r\n    char ExpectedCombinedOptions[1024];\r\n    LXT_FS_INFO FsInfo;\r\n    size_t Index;\r\n    size_t Length;\r\n    char Plan9Options[1024];\r\n    const char* Plan9Source;\r\n    int Result;\r\n    char Temp[1024];\r\n    char* UncSource = NULL;\r\n\r\n    LxtCheckResult(LxtFsGetFsInfo(Target, &FsInfo));\r\n    if (FsInfo.FsType == LxtFsTypeDrvFs)\r\n    {\r\n        if (Options == NULL)\r\n        {\r\n            Options = \"case=off\";\r\n        }\r\n\r\n        snprintf(ExpectedOptions, sizeof(ExpectedOptions), \"rw,%s\", Options);\r\n        snprintf(ExpectedCombinedOptions, sizeof(ExpectedOptions), \"rw,noatime,%s\", Options);\r\n\r\n        LxtCheckResult(MountCheckIsMount(Target, ParentId, Source, \"drvfs\", MountRoot, \"rw,noatime\", ExpectedOptions, ExpectedCombinedOptions, 0));\r\n    }\r\n    else if (FsInfo.FsType == LxtFsTypePlan9)\r\n    {\r\n        if (Options == NULL)\r\n        {\r\n            Temp[0] = '\\0';\r\n        }\r\n        else\r\n        {\r\n            Temp[0] = ';';\r\n            strncpy(Temp + 1, Options, sizeof(Temp) - 1);\r\n            for (Index = 0; Index < strlen(Temp); Index += 1)\r\n            {\r\n                if (Temp[Index] == ',')\r\n                {\r\n                    Temp[Index] = ';';\r\n                }\r\n            }\r\n        }\r\n\r\n        Length = strlen(Source);\r\n        if ((Length >= FS_UNC_PATH_PREFIX_LENGTH) && ((Source[0] == '/') || (Source[0] == '\\\\')) &&\r\n            ((Source[1] == '/') || (Source[1] == '\\\\')))\r\n        {\r\n\r\n            Length -= FS_UNC_PATH_PREFIX_LENGTH;\r\n            UncSource = malloc(Length + FS_PLAN9_UNC_PREFIX_LENGTH + 1);\r\n            if (UncSource == NULL)\r\n            {\r\n                LxtLogError(\"malloc\");\r\n                Result = -1;\r\n                goto ErrorExit;\r\n            }\r\n\r\n            memcpy(UncSource, FS_PLAN9_UNC_PREFIX, FS_PLAN9_UNC_PREFIX_LENGTH);\r\n            memcpy(&UncSource[FS_PLAN9_UNC_PREFIX_LENGTH], &Source[FS_UNC_PATH_PREFIX_LENGTH], Length);\r\n\r\n            UncSource[Length + FS_PLAN9_UNC_PREFIX_LENGTH] = '\\0';\r\n            Plan9Source = UncSource;\r\n        }\r\n        else\r\n        {\r\n            Plan9Source = Source;\r\n        }\r\n\r\n        if (FsInfo.Flags.VirtIo != 0)\r\n        {\r\n            Source = \"drvfsa\";\r\n            snprintf(\r\n                Plan9Options,\r\n                sizeof(Plan9Options),\r\n                \"aname=drvfs;path=%s%s;symlinkroot=/mnt/,cache=5,access=client,msize=262144,trans=virtio\",\r\n                Plan9Source,\r\n                Temp);\r\n        }\r\n        else\r\n        {\r\n            snprintf(\r\n                Plan9Options,\r\n                sizeof(Plan9Options),\r\n                \"aname=drvfs;path=%s%s;symlinkroot=/mnt/,cache=5,access=client,msize=65536,trans=fd,rfd=*,wfd=*\",\r\n                Plan9Source,\r\n                Temp);\r\n        }\r\n\r\n        //\r\n        // The combined options aren't checked for 9p because the placement of\r\n        // noatime by libmount is inconsistent.\r\n        //\r\n\r\n        snprintf(ExpectedOptions, sizeof(ExpectedOptions), \"rw,%s\", Plan9Options);\r\n        LxtCheckResult(MountCheckIsMount(Target, ParentId, Source, \"9p\", MountRoot, \"rw,noatime\", ExpectedOptions, NULL, 0));\r\n    }\r\n    else if (FsInfo.FsType == LxtFsTypeVirtioFs)\r\n    {\r\n        snprintf(ExpectedOptions, sizeof(ExpectedOptions), \"rw\");\r\n        LxtCheckResult(MountCheckIsMount(Target, ParentId, NULL, \"virtiofs\", MountRoot, \"rw,noatime\", ExpectedOptions, NULL, 0));\r\n    }\r\n\r\nErrorExit:\r\n    free(UncSource);\r\n    return Result;\r\n}\r\n\r\nint LxtFsCreateTestDir(char* Directory)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine creates a test directory, succeeding if it already exists.\r\n\r\nArguments:\r\n\r\n    Directory - Supplies the directory name.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    Result = mkdir(Directory, 0777);\r\n    if ((Result < 0) && (errno != EEXIST))\r\n    {\r\n        LxtLogError(\"Failed to create directory %s\", Directory);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtFsDeleteCurrentWorkingDirectoryCommon(char* BaseDir, int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the behavior if the current working directory is\r\n    unlinked.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the top directory to use for the test.\r\n\r\n    Flags - Supplies various flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n    --*/\r\n\r\n{\r\n\r\n    char DeleteTestDir[PATH_MAX];\r\n    char DeleteTestRenameFile[PATH_MAX];\r\n    int Fd;\r\n    char Path[PATH_MAX];\r\n    char* PointerResult;\r\n    int Result;\r\n\r\n    sprintf(DeleteTestDir, \"%s/%s\", BaseDir, FS_DELETE_TEST_DIR_NAME);\r\n    sprintf(DeleteTestRenameFile, \"%s/%s\", BaseDir, FS_DELETE_TEST_RENAME_FILE_NAME);\r\n\r\n    //\r\n    // Create the directory, change to it, and do a sanity check on the normal\r\n    // return values of these functions.\r\n    //\r\n\r\n    Fd = -1;\r\n    memset(Path, 0, sizeof(Path));\r\n    LxtCheckErrno(Fd = creat(DeleteTestRenameFile, 0666));\r\n    LxtCheckErrno(close(Fd));\r\n    LxtCheckErrnoZeroSuccess(mkdir(DeleteTestDir, 0777));\r\n    LxtCheckErrnoZeroSuccess(chdir(DeleteTestDir));\r\n    LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));\r\n    LxtCheckStringEqual(Path, DeleteTestDir);\r\n    memset(Path, 0, sizeof(Path));\r\n    LxtCheckErrno(readlink(FS_PROC_SELF_CWD, Path, sizeof(Path)));\r\n    LxtCheckStringEqual(Path, DeleteTestDir);\r\n\r\n    //\r\n    // Unlinking the directory with \".\" should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rmdir(\".\"), EINVAL);\r\n\r\n    //\r\n    // Unlink the directory.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rmdir(DeleteTestDir));\r\n\r\n    //\r\n    // Check the behavior.\r\n    //\r\n\r\n    LxtCheckResult(LxtFsDeleteCurrentWorkingDirectoryHelper(BaseDir, DeleteTestDir, Flags));\r\n\r\n    //\r\n    // Nothing should change if a new directory is created with the same name.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DeleteTestDir, 0777));\r\n    LxtCheckResult(LxtFsDeleteCurrentWorkingDirectoryHelper(BaseDir, DeleteTestDir, Flags));\r\n    rmdir(DeleteTestDir);\r\n\r\n    //\r\n    // Opening the deleted directory should succeed.\r\n    //\r\n    // N.B. This currently doesn't work on Plan 9 or virtiofs.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypePlan9 && g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        LxtCheckErrno(Fd = open(\".\", O_DIRECTORY | O_RDONLY));\r\n        LxtCheckResult(LxtFsDeleteOpenFileHelper(Fd, BaseDir, DeleteTestDir, Flags));\r\n    }\r\n\r\n    //\r\n    // Try to chdir to the parent.\r\n    //\r\n    // N.B. This currently doesn't work on virtiofs.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(chdir(\"..\"));\r\n        LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));\r\n        LxtCheckStringEqual(Path, BaseDir);\r\n        memset(Path, 0, sizeof(Path));\r\n        LxtCheckErrno(readlink(FS_PROC_SELF_CWD, Path, sizeof(Path)));\r\n        LxtCheckStringEqual(Path, BaseDir);\r\n    }\r\n\r\n    //\r\n    // Try to chdir back to the deleted directory.\r\n    //\r\n    // N.B. This currently does not work on Plan 9 or virtiofs.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypePlan9 && g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(fchdir(Fd));\r\n        LxtCheckResult(LxtFsDeleteCurrentWorkingDirectoryHelper(BaseDir, DeleteTestDir, Flags));\r\n    }\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    rmdir(DeleteTestDir);\r\n    unlink(DeleteTestRenameFile);\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtFsDeleteCurrentWorkingDirectoryHelper(char* BaseDir, char* DeleteTestDir, int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if a deleted working directory behaves as expected.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the top directory to use for the test.\r\n\r\n    DeleteTestDir - Supplies the path to the delete test root directory.\r\n\r\n    Flags - Supplies various flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char DeleteTestDirDeleteSuffix[PATH_MAX];\r\n    char DeleteTestRenameFile[PATH_MAX];\r\n    char Path[PATH_MAX];\r\n    int ParentFd;\r\n    char* PointerResult;\r\n    int Result;\r\n\r\n    sprintf(DeleteTestDirDeleteSuffix, \"%s%s\", DeleteTestDir, FS_DELETE_LINK_SUFFIX);\r\n    sprintf(DeleteTestRenameFile, \"%s/%s\", BaseDir, FS_DELETE_TEST_RENAME_FILE_NAME);\r\n    ParentFd = -1;\r\n\r\n    //\r\n    // Check the result of getcwd and /proc/self/cwd\r\n    //\r\n\r\n    memset(Path, 0, sizeof(Path));\r\n    LxtCheckErrnoFailure(LxtGetcwd(Path, sizeof(Path)), ENOENT);\r\n    memset(Path, 0, sizeof(Path));\r\n    LxtCheckErrno(readlink(FS_PROC_SELF_CWD, Path, sizeof(Path)));\r\n    LxtCheckStringEqual(Path, DeleteTestDirDeleteSuffix);\r\n\r\n    //\r\n    // Creating a new item in the current working directory should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(open(FS_DELETE_TEST_CHILD, O_CREAT | O_WRONLY, 0777), ENOENT);\r\n\r\n    LxtCheckErrnoFailure(mkdir(FS_DELETE_TEST_CHILD, 0777), ENOENT);\r\n    LxtCheckErrnoFailure(link(BaseDir, FS_DELETE_TEST_CHILD), ENOENT);\r\n    LxtCheckErrnoFailure(symlink(\"/proc\", FS_DELETE_TEST_CHILD), ENOENT);\r\n    LxtCheckErrnoFailure(rename(DeleteTestRenameFile, \"./\" FS_DELETE_TEST_CHILD), ENOENT);\r\n\r\n    if ((Flags & FS_DELETE_DRVFS) == 0)\r\n    {\r\n        LxtCheckErrnoFailure(mknod(FS_DELETE_TEST_CHILD, S_IFIFO | 0777, 0), ENOENT);\r\n    }\r\n\r\n    //\r\n    // Opening the parent should succeed.\r\n    //\r\n\r\n    LxtCheckErrno(ParentFd = open(\"..\", O_DIRECTORY | O_RDONLY));\r\n    LxtCheckResult(LxtCheckFdPath(ParentFd, BaseDir));\r\n\r\nErrorExit:\r\n    if (ParentFd >= 0)\r\n    {\r\n        close(ParentFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtFsDeleteOpenFileCommon(char* BaseDir, int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests using unlink and rmdir on a file/directory that's open.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the top directory to use for the test.\r\n\r\n    Flags - Supplies various flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ChildFd;\r\n    char ChildPath[PATH_MAX];\r\n    char ChildPathDeleteSuffix[PATH_MAX];\r\n    char ChildPathSubpath[PATH_MAX];\r\n    char DeleteTestDir[PATH_MAX];\r\n    char DeleteTestDirAt[PATH_MAX];\r\n    char DeleteTestRenameFile[PATH_MAX];\r\n    int Fd;\r\n    char Path[PATH_MAX];\r\n    int ReopenFd;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    sprintf(DeleteTestDir, \"%s/%s\", BaseDir, FS_DELETE_TEST_DIR_NAME);\r\n    sprintf(DeleteTestDirAt, \"../%s\", FS_DELETE_TEST_DIR_NAME);\r\n    sprintf(DeleteTestRenameFile, \"%s/%s\", BaseDir, FS_DELETE_TEST_RENAME_FILE_NAME);\r\n    sprintf(ChildPath, \"%s/%s\", DeleteTestDir, FS_DELETE_TEST_CHILD);\r\n    sprintf(ChildPathDeleteSuffix, \"%s%s\", ChildPath, FS_DELETE_LINK_SUFFIX);\r\n    Fd = -1;\r\n    ChildFd = -1;\r\n    ReopenFd = -1;\r\n    LxtCheckErrno(Fd = creat(DeleteTestRenameFile, 0666));\r\n    LxtCheckErrno(close(Fd));\r\n    LxtCheckErrnoZeroSuccess(mkdir(DeleteTestDir, 0777));\r\n    LxtCheckErrno(Fd = open(DeleteTestDir, O_DIRECTORY | O_RDONLY));\r\n\r\n    //\r\n    // It should be possible to create a child in the directory.\r\n    //\r\n\r\n    LxtCheckErrno(ChildFd = openat(Fd, FS_DELETE_TEST_CHILD, O_CREAT | O_WRONLY, 0777));\r\n\r\n    LxtCheckResult(LxtCheckFdPath(ChildFd, ChildPath));\r\n\r\n    //\r\n    // Unlink the file and check the path indicates it's deleted.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlinkat(Fd, FS_DELETE_TEST_CHILD, 0));\r\n    LxtCheckResult(LxtCheckFdPath(ChildFd, ChildPathDeleteSuffix));\r\n\r\n    //\r\n    // Reopening through the file descriptor should work.\r\n    //\r\n    // N.B Reopening currently doesn't work on Plan 9 or virtiofs.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypePlan9 && g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        sprintf(Path, FS_FD_PATH_FORMAT, ChildFd);\r\n\r\n        LxtCheckErrno(ReopenFd = open(Path, O_RDONLY));\r\n        LxtCheckResult(LxtCheckFdPath(ReopenFd, ChildPathDeleteSuffix));\r\n\r\n        //\r\n        // Check that fstat on the deleted file returns 0 link count.\r\n        //\r\n        // N.B. Plan 9 will return ENOENT instead for all the below.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(fstat(ChildFd, &Stat));\r\n        LxtCheckEqual(Stat.st_nlink, 0, \"%d\");\r\n\r\n        //\r\n        // The target is a file so subpaths don't work.\r\n        //\r\n\r\n        sprintf(ChildPathSubpath, FS_CHILD_PATH_FORMAT, Path, \".\");\r\n        LxtCheckErrnoFailure(open(ChildPathSubpath, O_RDONLY), ENOTDIR);\r\n        sprintf(ChildPathSubpath, FS_CHILD_PATH_FORMAT, Path, \"..\");\r\n        LxtCheckErrnoFailure(open(ChildPathSubpath, O_RDONLY), ENOTDIR);\r\n        sprintf(ChildPathSubpath, FS_CHILD_PATH_FORMAT, Path, \"foo\");\r\n        LxtCheckErrnoFailure(open(ChildPathSubpath, O_RDONLY), ENOTDIR);\r\n        LxtCheckErrnoZeroSuccess(close(ReopenFd));\r\n        ReopenFd = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(close(ChildFd));\r\n    ChildFd = -1;\r\n\r\n    //\r\n    // Unlinking the directory through \".\" should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(unlinkat(Fd, \".\", AT_REMOVEDIR), EINVAL);\r\n\r\n    //\r\n    // Unlink the directory.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rmdir(DeleteTestDir));\r\n\r\n    //\r\n    // Trying to re-open the deleted directory should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(ChildFd = openat(Fd, DeleteTestDirAt, O_DIRECTORY | O_RDONLY), ENOENT);\r\n\r\n    //\r\n    // Check behavior is correct after deleting.\r\n    //\r\n\r\n    LxtCheckResult(LxtFsDeleteOpenFileHelper(Fd, BaseDir, DeleteTestDir, Flags));\r\n\r\n    //\r\n    // Even if a directory with the same name is created.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(DeleteTestDir, 0777));\r\n    LxtCheckResult(LxtFsDeleteOpenFileHelper(Fd, BaseDir, DeleteTestDir, Flags));\r\n\r\nErrorExit:\r\n    if (ReopenFd >= 0)\r\n    {\r\n        close(ReopenFd);\r\n    }\r\n\r\n    if (ChildFd >= 0)\r\n    {\r\n        close(ChildFd);\r\n    }\r\n\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(DeleteTestRenameFile);\r\n    unlink(ChildPath);\r\n    rmdir(DeleteTestDir);\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtFsDeleteOpenFileHelper(int Fd, char* BaseDir, char* DeleteTestDir, int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if a file descriptor pointing to a deleted directory\r\n    behaves as expected.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the file descriptor.\r\n\r\n    BaseDir - Supplies the top directory to use for the test.\r\n\r\n    DeleteTestDir - Supplies the path to the delete test root directory.\r\n\r\n    Flags - Supplies various flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char ChildPathSubpath[PATH_MAX];\r\n    char DeleteTestDirDeleteSuffix[PATH_MAX];\r\n    char DeleteTestRenameFile[PATH_MAX];\r\n    int ParentFd;\r\n    char Path[PATH_MAX];\r\n    int ReopenFd;\r\n    int Result;\r\n\r\n    sprintf(DeleteTestDirDeleteSuffix, \"%s%s\", DeleteTestDir, FS_DELETE_LINK_SUFFIX);\r\n    sprintf(DeleteTestRenameFile, \"%s/%s\", BaseDir, FS_DELETE_TEST_RENAME_FILE_NAME);\r\n    ParentFd = -1;\r\n    ReopenFd = -1;\r\n\r\n    //\r\n    // Check the path indicates it's deleted.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckFdPath(Fd, DeleteTestDirDeleteSuffix));\r\n\r\n    //\r\n    // Check that creating new items fails as expected.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(openat(Fd, FS_DELETE_TEST_CHILD, O_CREAT | O_WRONLY, 0666), ENOENT);\r\n\r\n    LxtCheckErrnoFailure(mkdirat(Fd, FS_DELETE_TEST_CHILD, 0777), ENOENT);\r\n    LxtCheckErrnoFailure(linkat(AT_FDCWD, BaseDir, Fd, FS_DELETE_TEST_CHILD, 0), ENOENT);\r\n\r\n    LxtCheckErrnoFailure(symlinkat(\"/proc\", Fd, FS_DELETE_TEST_CHILD), ENOENT);\r\n    LxtCheckErrnoFailure(renameat(AT_FDCWD, DeleteTestRenameFile, Fd, FS_DELETE_TEST_CHILD), ENOENT);\r\n\r\n    //\r\n    // Drvfs doesn't support mknod.\r\n    //\r\n\r\n    if ((Flags & FS_DELETE_DRVFS) == 0)\r\n    {\r\n        LxtCheckErrnoFailure(mknodat(Fd, FS_DELETE_TEST_CHILD, S_IFIFO | 0777, 0), ENOENT);\r\n    }\r\n\r\n    //\r\n    // Navigating to the parent from the deleted directory should succeed.\r\n    //\r\n\r\n    LxtCheckErrno(ParentFd = openat(Fd, \"..\", O_DIRECTORY | O_RDONLY));\r\n    LxtCheckResult(LxtCheckFdPath(ParentFd, BaseDir));\r\n\r\n    //\r\n    // Reopening through the file descriptor should work.\r\n    //\r\n\r\n    sprintf(ChildPathSubpath, FS_CHILD_PATH_FORMAT, Path, \"foo\");\r\n    LxtCheckErrnoFailure(open(ChildPathSubpath, O_RDONLY), ENOENT);\r\n\r\nErrorExit:\r\n    if (ParentFd >= 0)\r\n    {\r\n        close(ParentFd);\r\n    }\r\n\r\n    if (ReopenFd >= 0)\r\n    {\r\n        close(ReopenFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtFsDeleteLoopCommon(const char* BaseDir)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests deleting files in a loop with multiple getdents calls.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the base directory.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[512];\r\n    int BytesRead;\r\n    int Calls;\r\n    int Count;\r\n    struct dirent64* Entry;\r\n    int Fd;\r\n    const int FileCount = 500;\r\n    int Index;\r\n    char Path[PATH_MAX];\r\n    int Result;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Create the directory and the test files.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    for (Index = 0; Index < FileCount; Index += 1)\r\n    {\r\n        snprintf(Path, sizeof(Path), \"%s/file%d\", BaseDir, Index);\r\n        LxtCheckErrno(Fd = creat(Path, 0666));\r\n        LxtCheckClose(Fd);\r\n    }\r\n\r\n    //\r\n    // List the directory, and delete the files in between calls.\r\n    //\r\n\r\n    Calls = 0;\r\n    Count = 0;\r\n    LxtCheckErrno(Fd = open(BaseDir, O_RDONLY | O_DIRECTORY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, Buffer, sizeof(Buffer)));\r\n\r\n    while (BytesRead != 0)\r\n    {\r\n        Calls += 1;\r\n        Index = 0;\r\n        while (Index < BytesRead)\r\n        {\r\n            Entry = (struct dirent64*)&Buffer[Index];\r\n            if ((strcmp(Entry->d_name, \".\") != 0) && (strcmp(Entry->d_name, \"..\") != 0))\r\n            {\r\n\r\n                snprintf(Path, sizeof(Path), \"%s/%s\", BaseDir, Entry->d_name);\r\n                LxtCheckErrnoZeroSuccess(unlink(Path));\r\n                Count += 1;\r\n            }\r\n\r\n            Index += Entry->d_reclen;\r\n        }\r\n\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    //\r\n    // Make sure all files were deleted, and that more than one getdents call\r\n    // was used (otherwise the test is meaningless).\r\n    //\r\n\r\n    LxtCheckEqual(Count, FileCount, \"%d\");\r\n    LxtCheckGreater(Calls, 1, \"%d\");\r\n    LxtLogInfo(\"Calls: %d\", Calls);\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // The directory is now empty so it can be removed.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rmdir(BaseDir));\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    rmdir(BaseDir);\r\n    return Result;\r\n}\r\n\r\nint LxtFsGetDentsAlignmentCommon(const char* BaseDir, int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the alignment and padding of the entries returned by\r\n    getdents.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the directory to use.\r\n\r\n    Flags - Supplies the flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[4096];\r\n    int Count;\r\n    struct linux_dirent* Entry;\r\n    struct dirent64* Entry64;\r\n    int Fd;\r\n    int Fd2;\r\n    int Index;\r\n    int Length;\r\n    const int MaxChildLength = 16;\r\n    char Name[MaxChildLength + 1];\r\n    int Offset;\r\n    int Result;\r\n    int Size;\r\n\r\n    Fd = -1;\r\n    Fd2 = -1;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    LxtCheckErrno(Fd = open(BaseDir, O_RDONLY | O_DIRECTORY));\r\n\r\n    //\r\n    // No need to create entries with length 1 and 2 since the . and .. entries\r\n    // already take care of that.\r\n    //\r\n\r\n    for (Length = 3; Length <= MaxChildLength; Length += 1)\r\n    {\r\n        for (Index = 0; Index < Length; Index += 1)\r\n        {\r\n            Name[Index] = 'a' + Index;\r\n        }\r\n\r\n        Name[Length] = '\\0';\r\n        LxtCheckErrnoZeroSuccess(mkdirat(Fd, Name, 0777));\r\n    }\r\n\r\n    if ((Flags & FS_TEST_GETDENTS64) == 0)\r\n    {\r\n#ifdef __NR_getdents\r\n        LxtCheckErrno(Size = LxtGetdents(Fd, Buffer, sizeof(Buffer)));\r\n#else\r\n        LxtLogError(\"Test not supported on this architecture.\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n#endif\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(Size = LxtGetdents64(Fd, Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    LxtCheckGreater(Size, 0, \"%d\");\r\n    LxtCheckEqual(Size % sizeof(long), 0, \"%d\");\r\n    Offset = 0;\r\n    Count = 0;\r\n    while (Offset < Size)\r\n    {\r\n\r\n        //\r\n        // Verify the record length of each entry.\r\n        //\r\n        // N.B. These sizes are precalculated for amd64; they may need to be\r\n        //      adjusted on different architectures.\r\n        //\r\n\r\n        if ((Flags & FS_TEST_GETDENTS64) == 0)\r\n        {\r\n            Entry = (struct linux_dirent*)&Buffer[Offset];\r\n            Length = strlen(Entry->d_name);\r\n            LxtLogInfo(\"getdents %s: %d\", Entry->d_name, Entry->d_reclen);\r\n            if (Length <= 4)\r\n            {\r\n                LxtCheckEqual(Entry->d_reclen, 24, \"%d\");\r\n            }\r\n            else if (Length <= 12)\r\n            {\r\n                LxtCheckEqual(Entry->d_reclen, 32, \"%d\");\r\n            }\r\n            else\r\n            {\r\n                LxtCheckEqual(Entry->d_reclen, 40, \"%d\");\r\n            }\r\n\r\n            //\r\n            // Make sure the file type is in the last entry.\r\n            //\r\n\r\n            LxtCheckEqual(Buffer[Offset + Entry->d_reclen - 1], DT_DIR, \"%d\");\r\n            Offset += Entry->d_reclen;\r\n        }\r\n        else\r\n        {\r\n            Entry64 = (struct dirent64*)&Buffer[Offset];\r\n            Length = strlen(Entry64->d_name);\r\n            LxtLogInfo(\"getdents64 %s: %d\", Entry64->d_name, Entry64->d_reclen);\r\n            if (Length <= 4)\r\n            {\r\n                LxtCheckEqual(Entry64->d_reclen, 24, \"%d\");\r\n            }\r\n            else if (Length <= 12)\r\n            {\r\n                LxtCheckEqual(Entry64->d_reclen, 32, \"%d\");\r\n            }\r\n            else\r\n            {\r\n                LxtCheckEqual(Entry64->d_reclen, 40, \"%d\");\r\n            }\r\n\r\n            LxtCheckEqual(Entry64->d_type, DT_DIR, \"%d\");\r\n            Offset += Entry64->d_reclen;\r\n        }\r\n\r\n        Count += 1;\r\n    }\r\n\r\n    LxtCheckEqual(Count, MaxChildLength, \"%d\");\r\n\r\n    //\r\n    // Open a child for the buffer size tests; on real Linux, the entries are\r\n    // not in any specific order so the minimum entry size may not fit the\r\n    // actual first item returned. The child directory is empty so the entries\r\n    // will fit in the minimum size.\r\n    //\r\n\r\n    LxtCheckErrno(Fd2 = openat(Fd, \"abc\", O_RDONLY | O_DIRECTORY));\r\n    if ((Flags & FS_TEST_GETDENTS64) == 0)\r\n    {\r\n\r\n        //\r\n        // getdents variation (and not getdents64) won't run when\r\n        // __NR_getdents is not defined. So, commenting out the\r\n        // below is just for compilation safety.\r\n        //\r\n\r\n#ifdef __NR_getdents\r\n        LxtCheckErrnoFailure(LxtGetdents(Fd2, Buffer, 23), EINVAL);\r\n        LxtCheckErrno(Size = LxtGetdents(Fd2, Buffer, 24));\r\n        LxtCheckEqual(Size, 24, \"%d\");\r\n#endif\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrnoFailure(LxtGetdents64(Fd2, Buffer, 23), EINVAL);\r\n        LxtCheckErrno(Size = LxtGetdents64(Fd2, Buffer, 24));\r\n        LxtCheckEqual(Size, 24, \"%d\");\r\n    }\r\n\r\nErrorExit:\r\n    if (Fd2 >= 0)\r\n    {\r\n        close(Fd2);\r\n    }\r\n\r\n    if (Fd >= 0)\r\n    {\r\n        for (Length = 3; Length <= MaxChildLength; Length += 1)\r\n        {\r\n            for (Index = 0; Index < Length; Index += 1)\r\n            {\r\n                Name[Index] = 'a' + Index;\r\n            }\r\n\r\n            Name[Length] = '\\0';\r\n            unlinkat(Fd, Name, AT_REMOVEDIR);\r\n        }\r\n\r\n        close(Fd);\r\n    }\r\n\r\n    rmdir(BaseDir);\r\n    return Result;\r\n}\r\n\r\nint LxtFsGetFsInfo(const char* Path, PLXT_FS_INFO Info)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine gets file system information.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the test root path.\r\n\r\n    Info - Supplies a buffer which receives file system information.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char FsType[10];\r\n    LXT_FS_INFO LocalInfo;\r\n    char Options[1024];\r\n    int Result;\r\n\r\n    memset(&LocalInfo, 0, sizeof(LocalInfo));\r\n\r\n    LxtCheckResult(MountGetFileSystem(Path, FsType, sizeof(FsType), Options, sizeof(Options)));\r\n\r\n    if (strcmp(FsType, FS_DRVFS_NAME) == 0)\r\n    {\r\n        LocalInfo.FsType = LxtFsTypeDrvFs;\r\n        LocalInfo.Flags.DrvFsBehavior = 1;\r\n    }\r\n    else if (strcmp(FsType, FS_WSLFS_NAME) == 0)\r\n    {\r\n        LocalInfo.FsType = LxtFsTypeWslFs;\r\n        LocalInfo.Flags.DrvFsBehavior = 1;\r\n        LocalInfo.Flags.Cached = 1;\r\n    }\r\n    else if (strcmp(FsType, FS_9P_NAME) == 0)\r\n    {\r\n        LocalInfo.FsType = LxtFsTypePlan9;\r\n        LocalInfo.Flags.DrvFsBehavior = 1;\r\n        if (strstr(Options, \"loose\") != NULL)\r\n        {\r\n            LocalInfo.Flags.Cached = 1;\r\n        }\r\n\r\n        if (strstr(Options, \"trans=virtio\") != NULL)\r\n        {\r\n            LocalInfo.Flags.VirtIo = 1;\r\n        }\r\n    }\r\n    else if (strcmp(FsType, FS_VIRTIOFS_NAME) == 0)\r\n    {\r\n        LocalInfo.FsType = LxtFsTypeVirtioFs;\r\n        LocalInfo.Flags.DrvFsBehavior = 1;\r\n        if (strstr(Options, \"dax\") != NULL)\r\n        {\r\n            LocalInfo.Flags.Dax = 1;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        LocalInfo.FsType = LxtFsTypeLxFs;\r\n    }\r\n\r\n    *Info = LocalInfo;\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtFsInotifyEpollCommon(char* BaseDir)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine contains common inotify epoll tests that are shared between\r\n    lxfs and drvfs tests.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the base directory for the test.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Ed;\r\n    int Fd;\r\n    int Id;\r\n    int Wd;\r\n    int Result;\r\n    char Buf[10];\r\n    char InotifyBuf[500];\r\n    struct inotify_event* Events[INOTIFY_TEST_EVENTS_BUF_SIZE];\r\n    int NumEvents;\r\n    struct epoll_event EpollControlEvent;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    char TestFile1[PATH_MAX];\r\n    char TestFile2[PATH_MAX];\r\n    char TestFile1Hlink[PATH_MAX];\r\n    char TestFile1Slink[PATH_MAX];\r\n\r\n    //\r\n    // Initialize and also do cleanup if the files have not been removed.\r\n    //\r\n\r\n    sprintf(TestFile1, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_NAME_ONLY);\r\n    sprintf(TestFile2, \"%s%s\", BaseDir, INOTIFY_TEST_FILE2_NAME_ONLY);\r\n    sprintf(TestFile1Hlink, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY);\r\n    sprintf(TestFile1Slink, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY);\r\n    unlink(TestFile1);\r\n    unlink(TestFile2);\r\n    unlink(TestFile1Hlink);\r\n    unlink(TestFile1Slink);\r\n    rmdir(BaseDir);\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    LxtCheckErrno(Id = inotify_init());\r\n    LxtCheckErrno(Result = inotify_add_watch(Id, TestFile1, IN_ALL_EVENTS));\r\n\r\n    //\r\n    // Create an epoll container and add the inotify file descriptor to it.\r\n    //\r\n\r\n    LxtCheckErrno(Ed = epoll_create(1));\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = Id;\r\n    Result = epoll_ctl(Ed, EPOLL_CTL_ADD, Id, &EpollControlEvent);\r\n    LxtCheckErrno(Result);\r\n\r\n    //\r\n    // Wait for data to be available with a timeout. This should timeout since\r\n    // there is no data.\r\n    //\r\n\r\n    Result = epoll_wait(Ed, EpollWaitEvent, 2, 80);\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Generate some inotify events.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(TestFile1, O_WRONLY));\r\n    LxtCheckErrno(Result = write(Fd, Buf, 10));\r\n    LxtCheckEqual(Result, 10, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Now epoll should return 1 ready file descriptor.\r\n    //\r\n\r\n    Result = epoll_wait(Ed, EpollWaitEvent, 2, 1000);\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Drain the inotify events.\r\n    //\r\n\r\n    usleep(1000 * 80);\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckTrue(NumEvents > 0);\r\n\r\n    //\r\n    // Epoll should timeout again, since there are no events to be read.\r\n    //\r\n\r\n    Result = epoll_wait(Ed, EpollWaitEvent, 2, 80);\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    close(Id);\r\n    close(Ed);\r\n    unlink(TestFile1);\r\n    rmdir(BaseDir);\r\n    return Result;\r\n}\r\n\r\nint LxtFsInotifyPosixUnlinkRenameCommon(char* BaseDir)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine contains common inotify POSIX unlink/rename tests that are\r\n    shared between lxfs and drvfs tests.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the base directory for the test.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Id;\r\n    int Result;\r\n    char TestFile1[PATH_MAX];\r\n    char TestFile2[PATH_MAX];\r\n    char TestFile3[PATH_MAX];\r\n\r\n    //\r\n    // Initialize and also do cleanup if the files have not been removed.\r\n    //\r\n\r\n    sprintf(TestFile1, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_NAME_ONLY);\r\n    sprintf(TestFile2, \"%s%s\", BaseDir, INOTIFY_TEST_FILE2_NAME_ONLY);\r\n    sprintf(TestFile3, \"%s%s\", BaseDir, INOTIFY_TEST_FILE3_NAME_ONLY);\r\n    unlink(TestFile1);\r\n    unlink(TestFile2);\r\n    unlink(TestFile3);\r\n    rmdir(BaseDir);\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrno(Fd = creat(TestFile2, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrno(Fd = creat(TestFile3, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Setup inotify.\r\n    //\r\n\r\n    LxtCheckErrno(Id = inotify_init());\r\n    LxtCheckErrno(Result = inotify_add_watch(Id, TestFile1, IN_ALL_EVENTS));\r\n    LxtCheckErrno(Result = inotify_add_watch(Id, TestFile2, IN_ALL_EVENTS));\r\n    LxtCheckErrno(Result = inotify_add_watch(Id, TestFile3, IN_ALL_EVENTS));\r\n\r\n    //\r\n    // Test that POSIX unlinking a file watched by inotify succeeds. Verify that\r\n    // a new file with the same name can be created.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(TestFile1));\r\n    LxtCheckErrnoFailure(Fd = open(TestFile1, O_RDONLY), ENOENT);\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Test that renaming a file with overwrite succeeds when both files are\r\n    // being watched by inotify.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rename(TestFile2, TestFile3));\r\n    LxtCheckErrnoFailure(Fd = open(TestFile2, O_RDONLY), ENOENT);\r\n    LxtCheckErrno(Fd = creat(TestFile2, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrnoFailure(Fd = open(TestFile3, O_CREAT | O_EXCL), EEXIST);\r\n    LxtCheckErrno(Fd = open(TestFile3, O_RDONLY));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    close(Id);\r\n    close(Fd);\r\n    unlink(TestFile1);\r\n    unlink(TestFile2);\r\n    unlink(TestFile3);\r\n    rmdir(BaseDir);\r\n    return Result;\r\n}\r\n\r\nint LxtFsInotifyReadAndProcess(int Id, char* ReadBuf, int ReadBufSize, struct inotify_event** Events, int NumEventsIn, int* NumEventsOut, int IgnoreAttribModify)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine reads from the supplied inotify fd and returns a list\r\n    of pointers to all the inotify events read.\r\n\r\nArguments:\r\n\r\n    Id - Inotify file descriptor.\r\n\r\n    ReadBuf - Buffer to read the inotify events into.\r\n\r\n    ReadBufSize - Size of ReadBuf in bytes.\r\n\r\n    Events - An array of pointers to events to fill in.\r\n\r\n    NumEventsIn - Max number of event pointers that Events can hold.\r\n\r\n    NumEventsOut - Number of event pointers filled into Events.\r\n\r\n    IgnoreAttribModify - Whether to ignore IN_ATTRIB and IN_MODIFY for\r\n        directories (used for DrvFs only).\r\n\r\nReturn Value:\r\n\r\n    Returns the number of bytes read, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char* Ptr;\r\n    struct inotify_event* Event;\r\n    int Result;\r\n    int NumEvents;\r\n\r\n    NumEvents = 0;\r\n    Result = read(Id, ReadBuf, ReadBufSize);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (Ptr = ReadBuf; Ptr < ReadBuf + Result; Ptr += sizeof(struct inotify_event) + Event->len)\r\n    {\r\n\r\n        if (NumEvents >= NumEventsIn)\r\n        {\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Event = (struct inotify_event*)Ptr;\r\n\r\n        //\r\n        // Ignore IN_ATTRIB and IN_MODIFY for DrvFs directories, since Windows\r\n        // generates lots of irrelevant such notifications for DrvFs directories.\r\n        //\r\n\r\n        if ((IgnoreAttribModify != FALSE) && ((Event->mask & IN_ISDIR) != 0) && ((Event->mask & (IN_ATTRIB | IN_MODIFY)) != 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        Events[NumEvents] = Event;\r\n        NumEvents += 1;\r\n    }\r\n\r\nErrorExit:\r\n    *NumEventsOut = NumEvents;\r\n    return Result;\r\n}\r\n\r\nint LxtFsInotifyUnmountBindCommon(char* BaseDir)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine contains common inotify unmount tests that are shared between\r\n    lxfs and drvfs.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the base directory for the test.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Id1;\r\n    int Wd[10];\r\n    char Buf[10];\r\n    char InotifyBuf[500];\r\n    struct inotify_event* Events[INOTIFY_TEST_EVENTS_BUF_SIZE];\r\n    int NumEvents;\r\n    int Bytes;\r\n    int Result;\r\n    char TestFile1[PATH_MAX];\r\n    char TestFile2[PATH_MAX];\r\n    char TestFile1Hlink[PATH_MAX];\r\n    char TestFile1Slink[PATH_MAX];\r\n    char TestDir1[PATH_MAX];\r\n    char TestDir2[PATH_MAX];\r\n    char TestDir11[PATH_MAX];\r\n    char TestFile111[PATH_MAX];\r\n\r\n    //\r\n    // Initialize and also do cleanup if the files have not been removed.\r\n    //\r\n\r\n    sprintf(TestFile1, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_NAME_ONLY);\r\n    sprintf(TestFile2, \"%s%s\", BaseDir, INOTIFY_TEST_FILE2_NAME_ONLY);\r\n    sprintf(TestFile1Hlink, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_HLINK_NAME_ONLY);\r\n    sprintf(TestFile1Slink, \"%s%s\", BaseDir, INOTIFY_TEST_FILE1_SLINK_NAME_ONLY);\r\n    sprintf(TestDir1, \"%s%s\", BaseDir, \"bind_mount_tmp/\");\r\n    sprintf(TestDir2, \"%s%s\", BaseDir, \"bind_mount_tmp_2/\");\r\n    sprintf(TestDir11, \"%s%s\", TestDir1, \"subdir11/\");\r\n    sprintf(TestFile111, \"%s%s\", TestDir11, \"file111\");\r\n    unlink(TestFile1);\r\n    unlink(TestFile2);\r\n    unlink(TestFile1Hlink);\r\n    unlink(TestFile1Slink);\r\n    rmdir(TestDir11);\r\n    umount(TestDir2);\r\n    rmdir(TestDir2);\r\n    umount(TestDir1);\r\n    rmdir(TestDir1);\r\n    rmdir(BaseDir);\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    LxtCheckErrno(Fd = creat(TestFile1, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Create a tmpfs (TestDir1) and create some files inside it.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(TestDir1, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(\"tmpfs\", TestDir1, \"tmpfs\", 0, NULL));\r\n    LxtCheckErrnoZeroSuccess(mkdir(TestDir11, 0777));\r\n    LxtCheckErrno(Fd = creat(TestFile111, 0777));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Create a bind mount (TestDir2) to the tmpfs (TestDir1).\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(TestDir2, 0777));\r\n    LxtCheckErrnoZeroSuccess(mount(TestDir1, TestDir2, NULL, MS_BIND, NULL));\r\n\r\n    //\r\n    // Setup inotify and verify. Note that TestDir1 and TestDir2 point to the\r\n    // same inode (directory).\r\n    //\r\n\r\n    LxtCheckErrno(Id1 = inotify_init1(IN_NONBLOCK));\r\n\r\n    LxtCheckErrno(\r\n        Wd[0] = // wd: 1\r\n        inotify_add_watch(Id1, TestDir1, IN_ALL_EVENTS));\r\n\r\n    LxtCheckErrno(\r\n        Wd[1] = // wd: 2\r\n        inotify_add_watch(Id1, TestDir11, IN_ALL_EVENTS));\r\n\r\n    LxtCheckErrno(\r\n        Wd[2] = // wd: 3\r\n        inotify_add_watch(Id1, TestFile111, IN_ALL_EVENTS));\r\n\r\n    LxtCheckErrno(\r\n        Wd[3] = // wd: 1\r\n        inotify_add_watch(Id1, TestDir2, IN_ALL_EVENTS));\r\n\r\n    LxtCheckEqual(Wd[0], 1, \"%d\");\r\n    LxtCheckEqual(Wd[1], 2, \"%d\");\r\n    LxtCheckEqual(Wd[2], 3, \"%d\");\r\n    LxtCheckEqual(Wd[0], Wd[3], \"%d\");\r\n\r\n    //\r\n    // Umount TestDir1. Verify that nothing happens in inotify since the inodes\r\n    // are not really unmounted (TestDir2 is still mounted).\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(TestDir1));\r\n\r\n    LxtCheckErrnoFailure(\r\n        LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE), EAGAIN);\r\n\r\n    LxtCheckEqual(NumEvents, 0, \"%d\");\r\n\r\n    //\r\n    // Unmount TestDir2. Now, each file and directory inside TestDir2 should\r\n    // generate 2 inotify events (IN_UNMOUNT, then IN_IGNORED).\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(TestDir2));\r\n\r\n    LxtCheckErrno(LxtFsInotifyReadAndProcess(Id1, InotifyBuf, sizeof(InotifyBuf), Events, INOTIFY_TEST_EVENTS_BUF_SIZE, &NumEvents, FALSE));\r\n\r\n    LxtCheckEqual(NumEvents, 6, \"%d\");\r\n    LxtCheckEqual(Events[0]->mask, IN_UNMOUNT, \"%d\");\r\n    LxtCheckEqual(Events[1]->mask, IN_IGNORED, \"%d\");\r\n\r\n    //\r\n    // In Linux kernel 5.10 the IN_ISDIR flag is also returned.\r\n    // Ignore the presence of this flag so this test can be run on multiple\r\n    // versions of the kernel.\r\n    //\r\n\r\n    Events[2]->mask &= ~IN_ISDIR;\r\n    Events[4]->mask &= ~IN_ISDIR;\r\n    LxtCheckEqual(Events[2]->mask, IN_UNMOUNT, \"%d\");\r\n    LxtCheckEqual(Events[3]->mask, IN_IGNORED, \"%d\");\r\n    LxtCheckEqual(Events[4]->mask, IN_UNMOUNT, \"%d\");\r\n    LxtCheckEqual(Events[5]->mask, IN_IGNORED, \"%d\");\r\n    LxtCheckEqual(Events[0]->wd, Events[1]->wd, \"%d\");\r\n    LxtCheckEqual(Events[2]->wd, Events[3]->wd, \"%d\");\r\n    LxtCheckEqual(Events[4]->wd, Events[5]->wd, \"%d\");\r\n    LxtCheckNotEqual(Events[0]->wd, Events[2]->wd, \"%d\");\r\n    LxtCheckNotEqual(Events[2]->wd, Events[4]->wd, \"%d\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    close(Id1);\r\n    close(Fd);\r\n    unlink(TestFile1);\r\n    rmdir(TestDir11);\r\n    umount(TestDir2);\r\n    rmdir(TestDir2);\r\n    umount(TestDir1);\r\n    rmdir(TestDir1);\r\n    rmdir(BaseDir);\r\n    return Result;\r\n}\r\n\r\nint LxtFsMountDrvFs(const char* Source, const char* Target, const char* Options)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine mounts drvfs through the mount binary.\r\n\r\n    N.B. This allows drvfs to be mounted using 9p for WSL 2.\r\n\r\nArguments:\r\n\r\n    Source - Supplies the mount source.\r\n\r\n    Target - Supplies the mount target.\r\n\r\n    Options - Supplies optional mount options.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Command[4096];\r\n    size_t Index;\r\n    int Result;\r\n\r\n    if (Options == NULL)\r\n    {\r\n        snprintf(Command, sizeof(Command), FS_MOUNT_DRVFS_COMMAND_FORMAT, Source, Target);\r\n    }\r\n    else\r\n    {\r\n        snprintf(Command, sizeof(Command), FS_MOUNT_DRVFS_OPTIONS_COMMAND_FORMAT, Source, Target, Options);\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(system(Command));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtFsRenameAtCommon(int DirFd1, int DirFd2)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the renameat system call on volfs.\r\n\r\nArguments:\r\n\r\n    DirFd1 - Supplies a file descriptor for the first directory to use for the test.\r\n\r\n    DirFd2 - Supplies a file descriptor for the second directory to use for the test.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd1;\r\n    int Result;\r\n\r\n    Fd1 = -1;\r\n\r\n    //\r\n    // Create a file and rename it using AT_FDCWD.\r\n    //\r\n\r\n    LxtCheckErrno(Fd1 = open(FS_RENAMEAT_TEST_FILE, O_RDWR | O_CREAT, S_IRWXU));\r\n    LxtClose(Fd1);\r\n    Fd1 = -1;\r\n\r\n    LxtCheckErrno(renameat(AT_FDCWD, FS_RENAMEAT_TEST_FILE, AT_FDCWD, FS_RENAMEAT_TEST_FILE2));\r\n\r\n    LxtCheckErrno(Fd1 = open(FS_RENAMEAT_TEST_FILE, O_RDWR | O_CREAT, S_IRWXU));\r\n    LxtClose(Fd1);\r\n    Fd1 = -1;\r\n\r\n    LxtCheckErrno(renameat(AT_FDCWD, FS_RENAMEAT_TEST_FILE, DirFd2, FS_RENAMEAT_TEST_FILE));\r\n\r\n    LxtCheckErrno(Fd1 = openat(DirFd1, FS_RENAMEAT_TEST_FILE, O_RDWR | O_CREAT, S_IRWXU));\r\n    LxtClose(Fd1);\r\n    Fd1 = -1;\r\n\r\n    //\r\n    // Rename a file over an existing file, but add a trailing slash to the target filename, error.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(renameat(DirFd1, FS_RENAMEAT_TEST_FILE, DirFd2, FS_RENAMEAT_TEST_FILE \"/\"), ENOTDIR);\r\n\r\n    LxtCheckErrno(renameat(DirFd1, FS_RENAMEAT_TEST_FILE, AT_FDCWD, FS_RENAMEAT_TEST_FILE2));\r\n\r\n    //\r\n    // Create a file and rename it into a subdirectory.\r\n    //\r\n\r\n    LxtCheckErrno(Fd1 = openat(DirFd1, FS_RENAMEAT_TEST_FILE, O_RDWR | O_CREAT, S_IRWXU));\r\n    LxtClose(Fd1);\r\n    Fd1 = -1;\r\n\r\n    LxtCheckErrno(renameat(DirFd1, FS_RENAMEAT_TEST_FILE, DirFd2, FS_RENAMEAT_TEST_FILE));\r\n\r\n    //\r\n    // Create a new file and rename it into a deeper subdirectory.\r\n    // The rename originally renamed the file into a/b/c/d/e/f/, but due to Bug 8405643, the file\r\n    // is now renamed into a/b/c, so that the directory rename further below will not fail (It\r\n    // sometimes fails if the source directory contains a file).\r\n    //\r\n\r\n    LxtCheckErrno(Fd1 = openat(DirFd1, FS_RENAMEAT_TEST_FILE, O_RDWR | O_CREAT, S_IRWXU));\r\n    LxtClose(Fd1);\r\n    Fd1 = -1;\r\n\r\n    LxtCheckErrno(renameat(DirFd1, FS_RENAMEAT_TEST_FILE, DirFd2, FS_RENAMEAT_TEST_FILE));\r\n\r\n    //\r\n    // Attempt to rename a folder into a descendent of itself.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(renameat(DirFd1, \"b\", DirFd2, \"d/e/f/b\"), EINVAL);\r\n\r\n    //\r\n    // Rename folders that have trailing slashes.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(renameat(DirFd1, \"b/c/d/\", DirFd1, \"b/d\"));\r\n    LxtCheckErrnoZeroSuccess(renameat(DirFd1, \"b/d/\", DirFd1, \"b/c/d\"));\r\n    LxtCheckErrnoZeroSuccess(renameat(DirFd1, \"b/c/d/\", DirFd1, \"b/d/\"));\r\n    LxtCheckErrnoZeroSuccess(renameat(DirFd1, \"b/d/\", DirFd1, \"b/c/d/\"));\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(renameat(-1, \"b\", DirFd2, \"d/e/f/b\"), EBADF);\r\n    LxtCheckErrnoFailure(renameat(DirFd1, NULL, DirFd2, \"d/e/f/b\"), EFAULT);\r\n    LxtCheckErrnoFailure(renameat(DirFd1, \"b\", -1, \"d/e/f/b\"), EBADF);\r\n    LxtCheckErrnoFailure(renameat(DirFd1, \"b\", DirFd2, NULL), EFAULT);\r\n\r\nErrorExit:\r\n    if (Fd1 >= 0)\r\n    {\r\n        LxtClose(Fd1);\r\n    }\r\n\r\n    unlinkat(DirFd2, \"d/e/f/\" FS_RENAMEAT_TEST_FILE, 0);\r\n    unlinkat(DirFd2, FS_RENAMEAT_TEST_FILE, 0);\r\n    unlinkat(AT_FDCWD, FS_RENAMEAT_TEST_FILE2, 0);\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtFsRenameDirCommon(char* BaseDir)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the rename system call for directories.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the top directory to use for the test.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Attempts;\r\n    int Attempts2;\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    char RenameTestDir[PATH_MAX];\r\n    char RenameTestDirChild[PATH_MAX];\r\n    char RenameTestDirGrandchild[PATH_MAX];\r\n    char RenameTestDirSlash[PATH_MAX];\r\n    char RenameTestDirSlash2[PATH_MAX];\r\n    char RenameTestDirSlashLink[PATH_MAX];\r\n    char RenameTestDirSlashLinkSlash[PATH_MAX];\r\n    char RenameTestDirSlashLink2[PATH_MAX];\r\n    char RenameTestDir2[PATH_MAX];\r\n    char RenameTestDir2Child[PATH_MAX];\r\n    char RenameTestDir2Child2[PATH_MAX];\r\n    char RenameTestDir2Slash[PATH_MAX];\r\n    char RenameTestDir3[PATH_MAX];\r\n    char RenameTestDir3Slash[PATH_MAX];\r\n    char RenameTestFile[PATH_MAX];\r\n    int Result;\r\n    int Status;\r\n\r\n    ChildPid = -1;\r\n    Fd = -1;\r\n    sprintf(RenameTestDir, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR);\r\n    sprintf(RenameTestDirChild, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR_CHILD);\r\n    sprintf(RenameTestDirGrandchild, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR_GRANDCHILD);\r\n    sprintf(RenameTestDirSlash, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR_SLASH);\r\n    sprintf(RenameTestDirSlash2, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR_SLASH2);\r\n    sprintf(RenameTestDirSlashLink, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR_SLASH_LINK);\r\n    sprintf(RenameTestDirSlashLinkSlash, \"%s%s/\", BaseDir, FS_RENAME_TEST_DIR_SLASH_LINK);\r\n    sprintf(RenameTestDirSlashLink2, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR_SLASH_LINK2);\r\n    sprintf(RenameTestDir2, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR2);\r\n    sprintf(RenameTestDir2Child, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR2_CHILD);\r\n    sprintf(RenameTestDir2Child2, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR2_CHILD2);\r\n    sprintf(RenameTestDir2Slash, \"%s%s////\", BaseDir, FS_RENAME_TEST_DIR2);\r\n    sprintf(RenameTestDir3, \"%s%s\", BaseDir, FS_RENAME_TEST_DIR3);\r\n    sprintf(RenameTestDir3Slash, \"%s%s/\", BaseDir, FS_RENAME_TEST_DIR3);\r\n    sprintf(RenameTestFile, \"%s%s\", BaseDir, FS_RENAME_TEST_FILE);\r\n\r\n    //\r\n    // Renaming a directory to be its own parent should fail with ENOTEMPTY.\r\n    //\r\n\r\n    LxtCheckResult(LxtFsCreateTestDir(RenameTestDir));\r\n    LxtCheckErrnoZeroSuccess(mkdir(RenameTestDirChild, 0777));\r\n    LxtCheckErrnoFailure(rename(RenameTestDirChild, RenameTestDir), ENOTEMPTY);\r\n\r\n    //\r\n    // Or ancestor.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(RenameTestDirGrandchild, 0777));\r\n    LxtCheckErrnoFailure(rename(RenameTestDirGrandchild, RenameTestDir), ENOTEMPTY);\r\n\r\n    //\r\n    // Renaming it over an existing directory should succeed.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(RenameTestDir2, 0777));\r\n    LxtCheckErrnoZeroSuccess(rename(RenameTestDirChild, RenameTestDir2));\r\n\r\n    //\r\n    // Renaming to a non-existent directory where the target directory\r\n    // contains trailing slash(es).\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rename(RenameTestDir2, RenameTestDir3Slash));\r\n    LxtCheckErrnoZeroSuccess(rename(RenameTestDir3, RenameTestDir2Slash));\r\n\r\n    //\r\n    // Renaming a file to its parent should also fail.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(RenameTestDirChild, 0666));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrnoFailure(rename(RenameTestDirChild, RenameTestDir), ENOTEMPTY);\r\n\r\n    //\r\n    // Renaming a file over a directory should fail with EISDIR.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rename(RenameTestDirChild, RenameTestDir2), EISDIR);\r\n\r\n    //\r\n    // And directory over file should fail with ENOTDIR.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rename(RenameTestDir2, RenameTestDirChild), ENOTDIR);\r\n\r\n    //\r\n    // The following test cases deal with the old name containing a trailing slash.\r\n    //\r\n    // Renaming a directory to a nonexistent directory should succeed.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(RenameTestDirSlash, 0777));\r\n    LxtCheckErrnoZeroSuccess(rename(RenameTestDirSlash, RenameTestDirSlash2));\r\n\r\n    //\r\n    // Renaming a symlink with a trailing slash should fail with ENOTDIR.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(symlink(RenameTestDirSlash2, RenameTestDirSlashLink));\r\n    LxtCheckErrnoFailure(rename(RenameTestDirSlashLinkSlash, RenameTestDirSlashLink2), ENOTDIR);\r\n\r\n    //\r\n    // The following tests reproduce a bug in lxcore when renaming a directory\r\n    // fails due to open handles to a descendant of that directory. Since this\r\n    // scenario would not fail on real Linux in the first place, this test\r\n    // will not pass on real Linux.\r\n    //\r\n    // Once lxcore is able to rename directories with open handles to a\r\n    // descendant, this test will need to be updated.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(rmdir(RenameTestDir2Child2));\r\n    LxtCheckErrnoZeroSuccess(rmdir(RenameTestDir2));\r\n    LxtLogInfo(\"This test will not pass on real Linux.\");\r\n\r\n    //\r\n    // Keep the child open so renaming the directory will fail on lxcore.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(RenameTestDirChild, O_RDONLY));\r\n    LxtCheckErrnoFailure(rename(RenameTestDir, RenameTestDir2), EACCES);\r\n\r\n    //\r\n    // Attempt to unlink the child while it is still open.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(unlink(RenameTestDirChild));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n\r\n    //\r\n    // Repeat, but this time use the file as a rename target.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(RenameTestDirChild, 0666));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = creat(RenameTestFile, 0666));\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = open(RenameTestDirChild, O_RDONLY));\r\n    LxtCheckErrnoFailure(rename(RenameTestDir, RenameTestDir2), EACCES);\r\n    LxtCheckErrnoZeroSuccess(rename(RenameTestFile, RenameTestDirChild));\r\n    LxtCheckClose(Fd);\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(RenameTestDirSlashLink);\r\n    rmdir(RenameTestDirSlash);\r\n    rmdir(RenameTestDirSlash2);\r\n    rmdir(RenameTestDirGrandchild);\r\n    unlink(RenameTestDirChild);\r\n    unlink(RenameTestDir2Child);\r\n    unlink(RenameTestFile);\r\n    rmdir(RenameTestDirChild);\r\n    rmdir(RenameTestDir);\r\n    rmdir(RenameTestDir2Child2);\r\n    rmdir(RenameTestDir2);\r\n\r\n    return Result;\r\n}\r\n\r\nvoid LxtFsTestCleanup(const char* TestDir, const char* DrvFsDir, bool UseDrvFs)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine cleans up directories and mounts created by LxtFsTestSetup.\r\n\r\nArguments:\r\n\r\n    TestDir - Supplies the test parent directory.\r\n\r\n    DrvFsDir - Supplies the DrvFs test directory.\r\n\r\n    UseDrvFs - Supplies a value which indicates whether DrvFs was used for the\r\n        test.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char DrvFsPath[PATH_MAX];\r\n\r\n    //\r\n    // Clean up DrvFs if needed, and remount with default options.\r\n    //\r\n    // N.B. Make sure the cwd isn't in the dir, as this prevents unmounting.\r\n    //\r\n\r\n    if (UseDrvFs != false)\r\n    {\r\n        chdir(\"/\");\r\n        umount(TestDir);\r\n        snprintf(DrvFsPath, sizeof(DrvFsPath), \"%s%s\", FS_DRVFS_PREFIX, DrvFsDir);\r\n        rmdir(DrvFsPath);\r\n        umount(FS_DRVFS_PREFIX);\r\n        LxtFsMountDrvFs(FS_DRVFS_DRIVE, FS_DRVFS_PREFIX, NULL);\r\n    }\r\n\r\n    rmdir(TestDir);\r\n    return;\r\n}\r\n\r\nint LxtFsTestSetup(PLXT_ARGS Args, const char* TestDir, const char* DrvFsDir, bool UseDrvFs)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine sets up the test environment for tests that can run on either\r\n    lxfs or drvfs. It also populates the global FS info structure.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the arguments.\r\n\r\n    TestDir - Supplies the root directory for the tests.\r\n\r\n    DrvFsDir - Supplies the DrvFs directory to use for testing. This must\r\n        start with a slash, and be relative from the root of the DrvFs mount.\r\n\r\n    UseDrvFs - Supplies a pa value indicating whether DrvFs is being used.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char DrvFsPath[PATH_MAX];\r\n    int Index;\r\n    int ParentId;\r\n    int Result;\r\n\r\n    //\r\n    // Do nothing if the caller just wants to show help.\r\n    //\r\n\r\n    if (Args->HelpRequested != false)\r\n    {\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Create the test dir.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(TestDir, 0777));\r\n    if (UseDrvFs == FALSE)\r\n    {\r\n        LxtCheckResult(LxtFsGetFsInfo(TestDir, &g_LxtFsInfo));\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // If drvfs is used, metadata support is required. Unmount and remount\r\n    // drvfs with metadata support.\r\n    //\r\n    // N.B. Make sure the cwd isn't inside DrvFs as this prevents unmounting.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chdir(\"/\"));\r\n    LxtCheckErrnoZeroSuccess(umount(FS_DRVFS_PREFIX));\r\n    LxtCheckResult(ParentId = MountGetMountId(FS_DRVFS_PREFIX));\r\n    LxtCheckResult(LxtFsMountDrvFs(FS_DRVFS_DRIVE, FS_DRVFS_PREFIX, \"metadata,case=dir\"));\r\n\r\n    LxtCheckResult(LxtFsCheckDrvFsMount(FS_DRVFS_DRIVE, FS_DRVFS_PREFIX, \"metadata,case=dir\", ParentId, \"/\"));\r\n\r\n    //\r\n    // The tests can't be run on the root directory of the volume. Therefore,\r\n    // create a directory to run the tests in and bind mount it to the test\r\n    // root.\r\n    //\r\n\r\n    snprintf(DrvFsPath, sizeof(DrvFsPath), \"%s%s\", FS_DRVFS_PREFIX, DrvFsDir);\r\n    LxtLogInfo(\"mkdir(%s, 0777)\", DrvFsPath);\r\n    LxtCheckErrnoZeroSuccess(mkdir(DrvFsPath, 0777));\r\n    LxtCheckResult(ParentId = MountGetMountId(TestDir));\r\n    LxtCheckErrnoZeroSuccess(mount(DrvFsPath, TestDir, NULL, MS_BIND, NULL));\r\n\r\n    LxtCheckResult(LxtFsCheckDrvFsMount(FS_DRVFS_DRIVE, TestDir, \"metadata,case=dir\", ParentId, DrvFsDir));\r\n\r\n    LxtCheckResult(LxtFsGetFsInfo(TestDir, &g_LxtFsInfo));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtFsTimestampCheckCurrent(const struct timespec* Timestamp)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if a timestamp is close to the current time.\r\n\r\nArguments:\r\n\r\n    Timestamp - Supplies the timestamp to check.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct timespec CurrentTime;\r\n    int Result;\r\n\r\n    //\r\n    // File system timestamp updates use the coarse clock.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(clock_gettime(CLOCK_REALTIME_COARSE, &CurrentTime));\r\n\r\n    if (LxtFsUtimeDoTimesMatch(Timestamp, &CurrentTime, FS_FAT_MODIFIED_TIME_PRECISION) == false)\r\n    {\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtFsTimestampCheckEqual(const struct timespec* Timestamp1, const struct timespec* Timestamp2)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks whether two timestamps are equal.\r\n\r\nArguments:\r\n\r\n    Timestamp1 - Supplies the first timestamp.\r\n\r\n    Timestamp2 - Supplies the second timestamp.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    if (LxtFsUtimeDoTimesMatch(Timestamp1, Timestamp2, 0) == false)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtFsTimestampCheckGreater(const struct timespec* Timestamp1, const struct timespec* Timestamp2)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if timestamp 1 is larger than timestamp 2.\r\n\r\nArguments:\r\n\r\n    Timestamp1 - Supplies the first timestamp.\r\n\r\n    Timestamp2 - Supplies the second timestamp.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    if (LxtFsTimestampDiff(Timestamp1, Timestamp2) <= 0)\r\n    {\r\n        LxtLogError(\"Time %lld.%.9ld not greater than time %lld.%.9ld\", Timestamp1, Timestamp2);\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtFsTimestampCheckUpdate(const char* Path, struct stat* PreviousStat, int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if the specified timestamps have been updated.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path of the file to check.\r\n\r\n    PreviousStat - Supplies the previous timestamps, and gets updated with the\r\n        current timestamp on success.\r\n\r\n    Flags - Supplies the flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(Path, &Stat));\r\n    if ((Flags & FS_TIMESTAMP_ACCESS) != 0)\r\n    {\r\n        LxtCheckResult(LxtFsTimestampCheckGreater(&Stat.st_atim, &PreviousStat->st_atim));\r\n\r\n        LxtCheckResult(LxtFsTimestampCheckCurrent(&Stat.st_atim));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtFsTimestampCheckEqual(&Stat.st_atim, &PreviousStat->st_atim));\r\n    }\r\n\r\n    if ((Flags & FS_TIMESTAMP_MODIFY) != 0)\r\n    {\r\n        LxtCheckResult(LxtFsTimestampCheckGreater(&Stat.st_mtim, &PreviousStat->st_mtim));\r\n\r\n        LxtCheckResult(LxtFsTimestampCheckCurrent(&Stat.st_mtim));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtFsTimestampCheckEqual(&Stat.st_mtim, &PreviousStat->st_mtim));\r\n    }\r\n\r\n    if ((Flags & FS_TIMESTAMP_CHANGE) != 0)\r\n    {\r\n        LxtCheckResult(LxtFsTimestampCheckGreater(&Stat.st_ctim, &PreviousStat->st_ctim));\r\n\r\n        LxtCheckResult(LxtFsTimestampCheckCurrent(&Stat.st_ctim));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtFsTimestampCheckEqual(&Stat.st_ctim, &PreviousStat->st_ctim));\r\n    }\r\n\r\n    *PreviousStat = Stat;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtFsTimestampCommon(const char* BaseDir, int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests timestamp updates made by various file operations.\r\n\r\n    N.B. These timestamp updates were verified on Linux with the \"strictatime\"\r\n         mount flag, so they include all the correct access time updates. WSL\r\n         does not currently implement access time updates.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\n    Flags - Supplies the flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int AccessTime;\r\n    int Fd;\r\n    char LinkPath[PATH_MAX];\r\n    char* Map;\r\n    char Path[PATH_MAX];\r\n    void* PointerResult;\r\n    int Result;\r\n    struct stat Stat;\r\n    struct stat Stat2;\r\n    struct stat Stat3;\r\n    struct stat Stat4;\r\n    char Temp[PATH_MAX];\r\n\r\n    //\r\n    // Since WSL doesn't update access time, only include the access time\r\n    // checks when requested.\r\n    //\r\n\r\n    AccessTime = 0;\r\n    if ((Flags & FS_TIMESTAMP_NOATIME) == 0)\r\n    {\r\n        AccessTime = FS_TIMESTAMP_ACCESS;\r\n    }\r\n\r\n    snprintf(Path, sizeof(Path), \"%s/%s\", BaseDir, \"test\");\r\n    snprintf(LinkPath, sizeof(LinkPath), \"%s/%s\", BaseDir, \"testlink\");\r\n    Fd = -1;\r\n    LxtCheckErrnoZeroSuccess(stat(BaseDir, &Stat));\r\n\r\n    //\r\n    // Creating and removing items in a directory should update the time of\r\n    // the directory. The items themselves should initialize to all times set\r\n    // to the current time.\r\n    //\r\n    // First create a directory.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Create directory...\");\r\n    LxtCheckErrnoZeroSuccess(mkdir(Path, 0777));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(Path, &Stat2));\r\n    LxtCheckResult(LxtFsTimestampCheckCurrent(&Stat2.st_atim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_mtim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_ctim));\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Remove directory...\");\r\n    LxtCheckErrnoZeroSuccess(rmdir(Path));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    //\r\n    // Create a socket file.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Create socket...\");\r\n    LxtCheckErrnoZeroSuccess(mknod(Path, S_IFSOCK | 0666, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(Path, &Stat2));\r\n    LxtCheckResult(LxtFsTimestampCheckCurrent(&Stat2.st_atim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_mtim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_ctim));\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Remove socket...\");\r\n    LxtCheckErrnoZeroSuccess(unlink(Path));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    //\r\n    // Create a fifo file.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Create fifo...\");\r\n    LxtCheckErrnoZeroSuccess(mknod(Path, S_IFIFO | 0666, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(Path, &Stat2));\r\n    LxtCheckResult(LxtFsTimestampCheckCurrent(&Stat2.st_atim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_mtim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_ctim));\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Remove fifo...\");\r\n    LxtCheckErrnoZeroSuccess(unlink(Path));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n    //\r\n    // Create a character device file.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Create character device...\");\r\n    LxtCheckErrnoZeroSuccess(mknod(Path, S_IFCHR | 0666, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(Path, &Stat2));\r\n    LxtCheckResult(LxtFsTimestampCheckCurrent(&Stat2.st_atim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_mtim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_ctim));\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Remove character device...\");\r\n    LxtCheckErrnoZeroSuccess(unlink(Path));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    //\r\n    // Create a file.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Create file...\");\r\n    LxtCheckErrno(Fd = creat(Path, 0666));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(Path, &Stat2));\r\n    LxtCheckResult(LxtFsTimestampCheckCurrent(&Stat2.st_atim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_mtim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_ctim));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n\r\n    //\r\n    // Create a hard link.\r\n    //\r\n    // N.B. The hard link has the same time stamps as the original file, and\r\n    //      ctime gets updated both when the link is created and unlinked.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Create hard link...\");\r\n    LxtCheckErrnoZeroSuccess(link(Path, LinkPath));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(LinkPath, &Stat2, FS_TIMESTAMP_CHANGE));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Remove hard link...\");\r\n    LxtCheckErrnoZeroSuccess(unlink(LinkPath));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n\r\n    //\r\n    // Create a symbolic link.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Create symlink...\");\r\n    LxtCheckErrnoZeroSuccess(symlink(Path, LinkPath));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(LinkPath, &Stat2));\r\n    LxtCheckResult(LxtFsTimestampCheckCurrent(&Stat2.st_atim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_mtim));\r\n    LxtCheckResult(LxtFsTimestampCheckEqual(&Stat2.st_atim, &Stat2.st_ctim));\r\n\r\n    //\r\n    // Readlink updates the access time.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Readlink...\");\r\n    LxtCheckErrno(readlink(LinkPath, Temp, sizeof(Temp)));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(LinkPath, &Stat2, AccessTime));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Remove the link.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Remove symlink...\");\r\n    LxtCheckErrnoZeroSuccess(unlink(LinkPath));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    //\r\n    // Chmod updates the change time.\r\n    //\r\n    // N.B. For every operation, also check that the timestamp of the parent\r\n    //      directory is not updated.\r\n    //\r\n    // N.B. Timestamps are updated even if the values aren't actually changed.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(Path, &Stat2));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Chmod...\");\r\n    LxtCheckErrnoZeroSuccess(chmod(Path, 0600));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(chmod(Path, 0600));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Chown updates the change time.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Chown...\");\r\n    LxtCheckErrnoZeroSuccess(chown(Path, 1000, 1001));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(chown(Path, 1000, 1001));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Stat does not change any timestamps (not even atime).\r\n    //\r\n    // N.B. The check function uses stat so just doing that twice is sufficient\r\n    //      to test this.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Stat...\");\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Setxattr updates the change time.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Setxattr...\");\r\n    LxtCheckErrnoZeroSuccess(setxattr(Path, \"user.test\", \"value\", 5, XATTR_CREATE));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(setxattr(Path, \"user.test\", \"value2\", 6, XATTR_REPLACE));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Getxattr does not change any timestamps (not even atime).\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Getxattr...\");\r\n    LxtCheckErrno(getxattr(Path, \"user.test\", Temp, sizeof(Temp)));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // listxattr does not change any timestamps (not even atime).\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Listxattr...\");\r\n    LxtCheckErrno(listxattr(Path, Temp, sizeof(Temp)));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Removexattr updates the change time.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Removexattr...\");\r\n    LxtCheckErrno(removexattr(Path, \"user.test\"));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Rename to the same directory (reusing LinkPath for this since it doesn't\r\n    // exist at this point). This change the parent and target timestamps.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Rename (same directory)...\");\r\n    LxtCheckErrnoZeroSuccess(rename(Path, LinkPath));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(LinkPath, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    //\r\n    // Rename to a different directory.\r\n    //\r\n\r\n    snprintf(Temp, sizeof(Temp), \"%s/%s\", BaseDir, \"targetdir\");\r\n    LxtCheckErrnoZeroSuccess(mkdir(Temp, 0777));\r\n    LxtCheckErrnoZeroSuccess(lstat(Temp, &Stat3));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Rename (different directory)...\");\r\n    snprintf(Temp, sizeof(Temp), \"%s/%s\", BaseDir, \"targetdir/target\");\r\n    LxtCheckErrnoZeroSuccess(rename(LinkPath, Temp));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Temp, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    snprintf(Temp, sizeof(Temp), \"%s/%s\", BaseDir, \"targetdir\");\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Temp, &Stat3, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    //\r\n    // Create a file to overwrite with a rename, and create a link to that\r\n    // file. The overwrite should count as an unlink, and therefore update the\r\n    // change time.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(Path, 0666));\r\n    LxtCheckClose(Fd);\r\n    LxtCheckErrnoZeroSuccess(link(Path, LinkPath));\r\n    LxtCheckErrnoZeroSuccess(lstat(Path, &Stat4));\r\n    LxtCheckEqual(Stat4.st_nlink, 2, \"%d\");\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Rename (overwrite target)...\");\r\n    snprintf(Temp, sizeof(Temp), \"%s/%s\", BaseDir, \"targetdir/target\");\r\n    LxtCheckErrnoZeroSuccess(rename(Temp, Path));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    snprintf(Temp, sizeof(Temp), \"%s/%s\", BaseDir, \"targetdir\");\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Temp, &Stat3, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(LinkPath, &Stat4, FS_TIMESTAMP_CHANGE));\r\n\r\n    LxtCheckEqual(Stat4.st_nlink, 1, \"%d\");\r\n\r\n    //\r\n    // Open this file (which by itself doesn't update any timestamps).\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Open...\");\r\n    LxtCheckErrno(Fd = open(Path, O_RDWR));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Write updates the mtime and ctime (unless no data is written).\r\n    //\r\n    // N.B. This happens regardless of whether the write extends the file or\r\n    //      not.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Write...\");\r\n    LxtCheckErrno(write(Fd, \"test\\0\", 5));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrno(write(Fd, \"\", 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Sync doesn't update any timestamps.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Sync...\");\r\n    LxtCheckErrnoZeroSuccess(fsync(Fd));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Seek doesn't update any timestamps.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Seek...\");\r\n    LxtCheckErrno(lseek(Fd, 0, SEEK_SET));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Read updates the atime (even if no data is read, even with a zero size\r\n    // buffer).\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Read...\");\r\n    LxtCheckErrno(read(Fd, Temp, 2));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, AccessTime));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrno(read(Fd, Temp, sizeof(Temp)));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, AccessTime));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(read(Fd, Temp, sizeof(Temp)));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, AccessTime));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrno(lseek(Fd, 0, SEEK_SET));\r\n    LxtCheckErrnoZeroSuccess(read(Fd, Temp, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, AccessTime));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // This is the only ioctl supported by LxFs; it doesn't update any\r\n    // timestamps.\r\n    //\r\n\r\n    LxtCheckErrno(lseek(Fd, 0, SEEK_SET));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Ioctl (FIONREAD)...\");\r\n    LxtCheckErrnoZeroSuccess(ioctl(Fd, FIONREAD, Temp));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Mmap updates the access time immediately regardless of protection or\r\n    // other flags. The write/modify time is updated when the mapping is first\r\n    // written to, which WSL currently does not do correctly.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Mmap...\");\r\n    LxtCheckNullErrno(Map = mmap(NULL, 5, PROT_NONE, MAP_PRIVATE, Fd, 0));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, AccessTime));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(munmap(Map, 5));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckNullErrno(Map = mmap(NULL, 5, PROT_READ | PROT_WRITE, MAP_SHARED, Fd, 0));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, AccessTime));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Value = %s\", Map);\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    memcpy(Map, \"1234\", 4);\r\n\r\n    //\r\n    // TODO_LX: Enable this check one WSL correctly does write timestamps for\r\n    //          mapped files.\r\n    //\r\n\r\n    // LxtCheckResult(LxtFsTimestampCheckUpdate(Path,\r\n    //                                         &Stat2,\r\n    //                                         (FS_TIMESTAMP_CHANGE |\r\n    //                                          FS_TIMESTAMP_MODIFY)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    memcpy(Map, \"abcd\", 4);\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(msync(Map, 5, MS_SYNC));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(munmap(Map, 5));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, 0));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Fallocate updates change time, and write time only if the file's length\r\n    // is changed.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Fallocate...\");\r\n    LxtCheckErrnoZeroSuccess(fallocate(Fd, 0, 0, 1024));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(fallocate(Fd, 0, 0, 1024));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(fallocate(Fd, FALLOC_FL_KEEP_SIZE, 0, 2048));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, FS_TIMESTAMP_CHANGE));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Truncate updates modify time and change time, even if the size did\r\n    // not change.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Truncate...\");\r\n    LxtCheckErrnoZeroSuccess(truncate(Path, 2));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrnoZeroSuccess(truncate(Path, 2));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n\r\n    //\r\n    // Opening with O_TRUNC updates modify time and change time, even if the\r\n    // size does not change.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Open (O_TRUNC)...\");\r\n    LxtCheckErrno(Fd = open(Path, O_RDWR | O_TRUNC));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    LxtCheckClose(Fd);\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtCheckErrno(Fd = open(Path, O_RDWR | O_TRUNC));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(Path, &Stat2, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, 0));\r\n    LxtCheckClose(Fd);\r\n\r\n    //\r\n    // Unlink the file.\r\n    //\r\n\r\n    usleep(FS_TIMESTAMP_SLEEP_TIME);\r\n    LxtLogInfo(\"Remove file...\");\r\n    LxtCheckErrnoZeroSuccess(unlink(LinkPath));\r\n    LxtCheckResult(LxtFsTimestampCheckUpdate(BaseDir, &Stat, (FS_TIMESTAMP_MODIFY | FS_TIMESTAMP_CHANGE)));\r\n\r\n    //\r\n    // Note that:\r\n    // - utimensat updates the change time to the current time; this is test\r\n    //   by the utime test.\r\n    //\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    snprintf(Temp, sizeof(Temp), \"%s/%s\", BaseDir, \"targetdir/target\");\r\n    unlink(Temp);\r\n    snprintf(Temp, sizeof(Temp), \"%s/%s\", BaseDir, \"targetdir\");\r\n    rmdir(Temp);\r\n    rmdir(Path);\r\n    unlink(Path);\r\n    unlink(LinkPath);\r\n    return Result;\r\n}\r\n\r\nlong long LxtFsTimestampDiff(const struct timespec* Timestamp1, const struct timespec* Timestamp2)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine gets the difference between two timestamps, in nanoseconds.\r\n\r\nArguments:\r\n\r\n    Timestamp1 - Supplies the first timestamp.\r\n\r\n    Timestamp2 - Supplies the second timestamp.\r\n\r\nReturn Value:\r\n\r\n    The value of Timestamp1 - Timestamp2, in nanoseconds\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    long long TimestampNs1;\r\n    long long TimestampNs2;\r\n\r\n    TimestampNs1 = (Timestamp1->tv_sec * FS_NS_PER_SEC) + Timestamp1->tv_nsec;\r\n    TimestampNs2 = (Timestamp2->tv_sec * FS_NS_PER_SEC) + Timestamp2->tv_nsec;\r\n    return TimestampNs1 - TimestampNs2;\r\n}\r\n\r\nint LxtFsUtimeBasicCommon(const char* BaseDir, int Flags)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine executes basic test functions, including setting timestamps\r\n    to a range of values including UTIME_NOW or UTIME_OMIT, on a range of\r\n    different ways to specify the target file, including relative, absolute,\r\n    descriptor, via symbolic link, on a symbolic link, and validating that\r\n    the expected outcome occurs.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the directory containing the test files.\r\n\r\n    Flags - Supplies the flags.\r\n\r\nReturn Value:\r\n\r\n    0 if all variations complete successfully, -1 if they do not.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int AllowedAccessVariance;\r\n    int AllowedChangeVariance;\r\n    int AllowedModifiedVariance;\r\n    char ChildFileFullPath[100];\r\n    PBASIC_TEST_CASE CurrentTest;\r\n    struct timespec CurrentTime;\r\n    int DirFd;\r\n    struct timespec ExpectedAccessTime;\r\n    struct timespec ExpectedChangeTime;\r\n    struct timespec ExpectedModifiedTime;\r\n    char LinkFullPath[100];\r\n    int NameVariation;\r\n    int NameVariationLast;\r\n    struct stat NoChangeStatBufferOld;\r\n    struct stat NoChangeStatBufferNew;\r\n    int Result;\r\n    const char* SetFilename;\r\n    int SetFlags;\r\n    struct timespec* SetTime;\r\n    struct stat StatBuffer;\r\n    unsigned int TestCase;\r\n    const char* ValidateFilename;\r\n    const char* ValidateNoChangeFilename;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    DirFd = -1;\r\n    Result = 0;\r\n    SetFlags = 0;\r\n    ValidateNoChangeFilename = NULL;\r\n    snprintf(ChildFileFullPath, sizeof(ChildFileFullPath), \"%s/%s\", BaseDir, FS_UTIME_TESTFILE);\r\n\r\n    snprintf(LinkFullPath, sizeof(LinkFullPath), \"%s/%s\", BaseDir, FS_UTIME_TESTLINK);\r\n\r\n    //\r\n    // Sleep for 2 seconds to make sure that the initial ctime of the test file\r\n    // wouldn't pass the current time test if utimensat doesn't update it.\r\n    //\r\n\r\n    LxtLogInfo(\"Sleeping...\");\r\n    sleep(3);\r\n\r\n    //\r\n    // Try different combinations of specifying file names.\r\n    //\r\n\r\n    NameVariationLast = NameVariationMax;\r\n    if ((Flags & FS_UTIME_NO_SYMLINKS) != 0)\r\n    {\r\n        NameVariationLast = NameVariationFatMax;\r\n    }\r\n\r\n    for (NameVariation = 0; NameVariation <= NameVariationLast; NameVariation += 1)\r\n    {\r\n        DirFd = -1;\r\n        switch (NameVariation)\r\n        {\r\n        case NameVariationFullName:\r\n            SetFilename = ChildFileFullPath;\r\n            SetFlags = 0;\r\n            ValidateFilename = ChildFileFullPath;\r\n            ValidateNoChangeFilename = LinkFullPath;\r\n            break;\r\n        case NameVariationCwdRelative:\r\n            LxtCheckErrnoZeroSuccess(chdir(BaseDir));\r\n            DirFd = AT_FDCWD;\r\n            SetFilename = FS_UTIME_TESTFILE;\r\n            SetFlags = 0;\r\n            ValidateFilename = ChildFileFullPath;\r\n            ValidateNoChangeFilename = LinkFullPath;\r\n            break;\r\n        case NameVariationRelative:\r\n            LxtCheckErrno(DirFd = open(BaseDir, O_DIRECTORY | O_RDONLY, 0));\r\n            SetFilename = FS_UTIME_TESTFILE;\r\n            SetFlags = 0;\r\n            ValidateFilename = ChildFileFullPath;\r\n            ValidateNoChangeFilename = LinkFullPath;\r\n            break;\r\n        case NameVariationDescriptor:\r\n            LxtCheckErrno(DirFd = open(ChildFileFullPath, O_RDWR, 0));\r\n            SetFilename = NULL;\r\n            SetFlags = 0;\r\n            ValidateFilename = ChildFileFullPath;\r\n            ValidateNoChangeFilename = LinkFullPath;\r\n            break;\r\n        case NameVariationFullFileViaLink:\r\n            SetFilename = LinkFullPath;\r\n            SetFlags = 0;\r\n            ValidateFilename = ChildFileFullPath;\r\n            ValidateNoChangeFilename = LinkFullPath;\r\n            break;\r\n        case NameVariationCwdRelativeViaLink:\r\n            LxtCheckErrnoZeroSuccess(chdir(BaseDir));\r\n            DirFd = AT_FDCWD;\r\n            SetFilename = FS_UTIME_TESTLINK;\r\n            SetFlags = 0;\r\n            ValidateFilename = ChildFileFullPath;\r\n            ValidateNoChangeFilename = LinkFullPath;\r\n            break;\r\n        case NameVariationRelativeViaLink:\r\n            LxtCheckErrno(DirFd = open(BaseDir, O_DIRECTORY | O_RDONLY, 0));\r\n            SetFilename = FS_UTIME_TESTLINK;\r\n            SetFlags = 0;\r\n            ValidateFilename = ChildFileFullPath;\r\n            ValidateNoChangeFilename = LinkFullPath;\r\n            break;\r\n        case NameVariationDescriptorViaLink:\r\n            LxtCheckErrno(DirFd = open(LinkFullPath, O_RDWR, 0));\r\n            SetFilename = NULL;\r\n            SetFlags = 0;\r\n            ValidateFilename = ChildFileFullPath;\r\n            ValidateNoChangeFilename = LinkFullPath;\r\n            break;\r\n        case NameVariationFullFileOnLink:\r\n            SetFilename = LinkFullPath;\r\n            SetFlags = AT_SYMLINK_NOFOLLOW;\r\n            ValidateFilename = LinkFullPath;\r\n            ValidateNoChangeFilename = ChildFileFullPath;\r\n            break;\r\n        case NameVariationCwdRelativeOnLink:\r\n            LxtCheckErrnoZeroSuccess(chdir(BaseDir));\r\n            DirFd = AT_FDCWD;\r\n            SetFilename = FS_UTIME_TESTLINK;\r\n            SetFlags = AT_SYMLINK_NOFOLLOW;\r\n            ValidateFilename = LinkFullPath;\r\n            ValidateNoChangeFilename = ChildFileFullPath;\r\n            break;\r\n        case NameVariationRelativeOnLink:\r\n            LxtCheckErrno(DirFd = open(BaseDir, O_DIRECTORY | O_RDONLY, 0));\r\n            SetFilename = FS_UTIME_TESTLINK;\r\n            SetFlags = AT_SYMLINK_NOFOLLOW;\r\n            ValidateFilename = LinkFullPath;\r\n            ValidateNoChangeFilename = ChildFileFullPath;\r\n            break;\r\n        }\r\n\r\n        LxtLogInfo(\r\n            \"Name variation %i, SetFilename = %s, ValidateFileName = %s,\"\r\n            \" ValidateNoChangeFileName = %s\",\r\n            NameVariation,\r\n            SetFilename,\r\n            ValidateFilename,\r\n            ValidateNoChangeFilename);\r\n\r\n        //\r\n        // Check that different times of timestamp operations can succeed.\r\n        //\r\n\r\n        for (TestCase = 0; TestCase < LXT_COUNT_OF(BasicTestCases); TestCase += 1)\r\n        {\r\n            LxtLogInfo(\"Test case %i\", TestCase);\r\n\r\n            CurrentTest = &BasicTestCases[TestCase];\r\n            SetTime = CurrentTest->SetTime;\r\n            if (SetTime[0].tv_nsec == UTIME_NOW && SetTime[1].tv_nsec == UTIME_NOW && SetTime[0].tv_sec == 0 && SetTime[1].tv_sec == 0)\r\n            {\r\n                SetTime = NULL;\r\n            }\r\n\r\n            if ((Flags & FS_UTIME_NO_SYMLINKS) == 0)\r\n            {\r\n                LxtCheckErrnoZeroSuccess(lstat(ValidateNoChangeFilename, &NoChangeStatBufferOld));\r\n            }\r\n\r\n            if (SetFilename != NULL)\r\n            {\r\n                LxtCheckErrnoZeroSuccess(utimensat(DirFd, SetFilename, SetTime, SetFlags));\r\n            }\r\n            else\r\n            {\r\n                LxtCheckErrnoZeroSuccess(futimens(DirFd, SetTime));\r\n            }\r\n\r\n            LxtCheckErrnoZeroSuccess(lstat(ValidateFilename, &StatBuffer));\r\n            LxtCheckErrnoZeroSuccess(clock_gettime(CLOCK_REALTIME_COARSE, &CurrentTime));\r\n\r\n            ExpectedAccessTime = CurrentTest->ExpectTime[0];\r\n            AllowedAccessVariance = 0;\r\n            ExpectedModifiedTime = CurrentTest->ExpectTime[1];\r\n            AllowedModifiedVariance = 0;\r\n            ExpectedChangeTime = CurrentTime;\r\n            AllowedChangeVariance = FS_FAT_MODIFIED_TIME_PRECISION;\r\n            if (ExpectedAccessTime.tv_nsec == UTIME_NOW)\r\n            {\r\n                ExpectedAccessTime = CurrentTime;\r\n                AllowedAccessVariance = FS_FAT_MODIFIED_TIME_PRECISION;\r\n            }\r\n\r\n            if (ExpectedModifiedTime.tv_nsec == UTIME_NOW)\r\n            {\r\n                ExpectedModifiedTime = CurrentTime;\r\n                AllowedModifiedVariance = FS_FAT_MODIFIED_TIME_PRECISION;\r\n            }\r\n\r\n            if ((Flags & FS_UTIME_FAT) != 0)\r\n            {\r\n                LxtFsUtimeRoundToFatAccessTime(&ExpectedAccessTime);\r\n                LxtFsUtimeRoundToFatModifiedTime(&ExpectedModifiedTime);\r\n\r\n                //\r\n                // Since FAT32 doesn't support change time, WSL reports the\r\n                // same value as the modified time.\r\n                //\r\n\r\n                ExpectedChangeTime = ExpectedModifiedTime;\r\n                AllowedChangeVariance = AllowedModifiedVariance;\r\n            }\r\n            else if ((Flags & FS_UTIME_NT_PRECISION) != 0)\r\n            {\r\n                LxtFsUtimeRoundToNt(&ExpectedAccessTime);\r\n                LxtFsUtimeRoundToNt(&ExpectedModifiedTime);\r\n                LxtFsUtimeRoundToNt(&ExpectedChangeTime);\r\n            }\r\n\r\n            //\r\n            // Calling utime causes the ctime to be set to the current time,\r\n            // so test that too.\r\n            //\r\n\r\n            if ((LxtFsUtimeDoTimesMatch(&StatBuffer.st_atim, &ExpectedAccessTime, AllowedAccessVariance) == false) ||\r\n                (LxtFsUtimeDoTimesMatch(&StatBuffer.st_mtim, &ExpectedModifiedTime, AllowedModifiedVariance) == false) ||\r\n                (LxtFsUtimeDoTimesMatch(&StatBuffer.st_ctim, &ExpectedChangeTime, AllowedChangeVariance) == false))\r\n            {\r\n\r\n                LxtLogError(\r\n                    \"times do not match expected values for file %s, \"\r\n                    \"TestCase %i, NameVariation %i\",\r\n                    ValidateFilename,\r\n                    TestCase,\r\n                    NameVariation);\r\n\r\n                LxtLogError(\r\n                    \"atime set: %lld.%.9ld, expected: %lld.%.9ld%s, \"\r\n                    \"actual: %lld.%.9ld\",\r\n                    CurrentTest->SetTime[0].tv_sec,\r\n                    CurrentTest->SetTime[0].tv_nsec,\r\n                    ExpectedAccessTime.tv_sec,\r\n                    ExpectedAccessTime.tv_nsec,\r\n                    (CurrentTest->ExpectTime[0].tv_nsec == UTIME_NOW ? \" (UTIME_NOW)\" : \"\"),\r\n                    StatBuffer.st_atim.tv_sec,\r\n                    StatBuffer.st_atim.tv_nsec);\r\n\r\n                LxtLogError(\r\n                    \"mtime set: %lld.%.9ld, expected: %lld.%.9ld%s, \"\r\n                    \"actual: %lld.%.9ld\",\r\n                    CurrentTest->SetTime[1].tv_sec,\r\n                    CurrentTest->SetTime[1].tv_nsec,\r\n                    ExpectedModifiedTime.tv_sec,\r\n                    ExpectedModifiedTime.tv_nsec,\r\n                    (CurrentTest->ExpectTime[1].tv_nsec == UTIME_NOW ? \" (UTIME_NOW)\" : \"\"),\r\n                    StatBuffer.st_mtim.tv_sec,\r\n                    StatBuffer.st_mtim.tv_nsec);\r\n\r\n                LxtLogError(\r\n                    \"ctime expected: %lld.%.9ld (UTIME_NOW), actual: %lld.%.9ld\",\r\n                    ExpectedChangeTime.tv_sec,\r\n                    ExpectedChangeTime.tv_nsec,\r\n                    StatBuffer.st_ctim.tv_sec,\r\n                    StatBuffer.st_ctim.tv_nsec);\r\n\r\n                Result = -1;\r\n                goto ErrorExit;\r\n            }\r\n\r\n            if ((Flags & FS_UTIME_NO_SYMLINKS) == 0)\r\n            {\r\n                LxtCheckErrnoZeroSuccess(lstat(ValidateNoChangeFilename, &NoChangeStatBufferNew));\r\n\r\n                //\r\n                // This sometimes fails on real Linux since the access timestamp\r\n                // is updated when a link is read. With the default relatime mount\r\n                // option, this only happens the first time, since after that\r\n                // atime > mtime and won't be updated.\r\n                //\r\n                // TODO_LX: This test should probably be revisited when proper\r\n                //          atime updating happens on WSL.\r\n                //\r\n\r\n                LxtCheckMemoryEqual(&NoChangeStatBufferOld, &NoChangeStatBufferNew, sizeof(NoChangeStatBufferOld));\r\n            }\r\n        }\r\n\r\n        if (DirFd >= 0)\r\n        {\r\n            LxtCheckClose(DirFd);\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    if (DirFd >= 0)\r\n    {\r\n        close(DirFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid LxtFsUtimeCleanupTestFiles(const char* BaseDir)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine cleans up the utime test files.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the directory containing the test files.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char FilePath[100];\r\n    char LinkPath[100];\r\n\r\n    snprintf(FilePath, sizeof(FilePath), \"%s/%s\", BaseDir, FS_UTIME_TESTFILE);\r\n    unlink(FilePath);\r\n    snprintf(LinkPath, sizeof(LinkPath), \"%s/%s\", BaseDir, FS_UTIME_TESTLINK);\r\n    unlink(LinkPath);\r\n    rmdir(BaseDir);\r\n\r\nErrorExit:\r\n    return;\r\n}\r\n\r\nint LxtFsUtimeCreateTestFiles(const char* BaseDir, int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine creates test files for the utime tests.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the directory containing the test files.\r\n\r\n    Flags - Supplies the flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    char FilePath[100];\r\n    char LinkPath[100];\r\n    int Result;\r\n\r\n    //\r\n    // Make a directory, test file, and test link.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    LxtCheckResult(LxtFsGetFsInfo(BaseDir, &g_LxtFsInfo));\r\n    snprintf(FilePath, sizeof(FilePath), \"%s/%s\", BaseDir, FS_UTIME_TESTFILE);\r\n    LxtCheckErrno(Fd = creat(FilePath, 0666));\r\n    LxtCheckClose(Fd);\r\n    if ((Flags & FS_UTIME_NO_SYMLINKS) == 0)\r\n    {\r\n        snprintf(LinkPath, sizeof(LinkPath), \"%s/%s\", BaseDir, FS_UTIME_TESTLINK);\r\n        LxtCheckErrnoZeroSuccess(symlink(FilePath, LinkPath));\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nbool LxtFsUtimeDoTimesMatch(const struct timespec* Actual, const struct timespec* Expected, int AllowedVarianceSeconds)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine compares two timespec structures for equality.\r\n\r\nArguments:\r\n\r\n    Actual - Supplies a pointer to the times encountered on the file.\r\n\r\n    Expected - Supplies a pointer to the times expected on the file.\r\n\r\n    AllowedVarianceSeconds - The amount of seconds that the actual time may\r\n        be behind the expected time.\r\n\r\nReturn Value:\r\n\r\n    TRUE if the timestamps match, FALSE if they do not.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    unsigned long long FullExpectedTime;\r\n    unsigned long long FullTime;\r\n\r\n    FullTime = (Actual->tv_sec * FS_NS_PER_SEC) + Actual->tv_nsec;\r\n    FullExpectedTime = (Expected->tv_sec * FS_NS_PER_SEC) + Expected->tv_nsec;\r\n    if ((FullTime <= FullExpectedTime) && (FullTime >= (FullExpectedTime - (AllowedVarianceSeconds * FS_NS_PER_SEC))))\r\n    {\r\n        return true;\r\n    }\r\n\r\n    //\r\n    // In VM mode, also allow variance to the future to allow for clock skew between\r\n    // the host and guest.\r\n    //\r\n\r\n    if (((g_LxtFsInfo.FsType == LxtFsTypePlan9) || (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)) &&\r\n        (FullTime <= (FullExpectedTime + (AllowedVarianceSeconds * FS_NS_PER_SEC))))\r\n    {\r\n        return true;\r\n    }\r\n\r\n    LxtLogError(\r\n        \"Time %lld.%.9ld not within %ds window of expected time %lld.%.9ld\",\r\n        Actual->tv_sec,\r\n        Actual->tv_nsec,\r\n        AllowedVarianceSeconds,\r\n        Expected->tv_sec,\r\n        Expected->tv_nsec);\r\n\r\n    return false;\r\n}\r\n\r\nvoid LxtFsUtimeRoundToFatAccessTime(struct timespec* Timespec)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine rounds a timespec to the nearest day, which is what FAT32\r\n    uses for the last access time.\r\n\r\nArguments:\r\n\r\n    Timespec - Supplies the time value.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct timespec Now;\r\n    time_t Time;\r\n    struct tm TimeInfo;\r\n\r\n    //\r\n    // When using the current time, there is a slight chance of this test\r\n    // failing when the time set for the file and the time when the check\r\n    // is done straddle midnight.\r\n    //\r\n\r\n    clock_gettime(CLOCK_REALTIME_COARSE, &Now);\r\n    if (Timespec->tv_nsec == UTIME_NOW)\r\n    {\r\n        *Timespec = Now;\r\n    }\r\n\r\n    //\r\n    // FAT32 stores only the day for the last access time, and it stores the\r\n    // local time. Here's what happens:\r\n    // - When the file time is set, Windows converts it to a local time. It\r\n    //   uses the current DST setting, not the DST setting that was active at\r\n    //   the specified time.\r\n    // - The date portion of the local time is stored as the file time.\r\n    // - When reading the time, Windows converts it back to UTC. Again, this is\r\n    //   done using the current DST setting.\r\n    // - This means the returned time looks like 2017-06-26 17:00:00, based on\r\n    //   the UTC offset of the current timezone.\r\n    //\r\n    // Determine the UTC offset of the current time, which ensures we use the\r\n    // current DST value.\r\n    //\r\n\r\n    Time = Now.tv_sec;\r\n    localtime_r(&Time, &TimeInfo);\r\n\r\n    //\r\n    // Convert the specified time to local time, and round to the nearest day.\r\n    //\r\n\r\n    Timespec->tv_sec += TimeInfo.tm_gmtoff;\r\n    Timespec->tv_sec /= FS_SECONDS_PER_DAY;\r\n    Timespec->tv_sec *= FS_SECONDS_PER_DAY;\r\n\r\n    //\r\n    // Convert back to UTC.\r\n    //\r\n\r\n    Timespec->tv_sec -= TimeInfo.tm_gmtoff;\r\n\r\n    //\r\n    // Since FAT only stores the day, nsec is always zero.\r\n    //\r\n\r\n    Timespec->tv_nsec = 0;\r\n    return;\r\n}\r\n\r\nvoid LxtFsUtimeRoundToFatModifiedTime(struct timespec* Timespec)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine rounds a timespec to the next highest multiple of two seconds,\r\n    which FAT does for the last write time.\r\n\r\nArguments:\r\n\r\n    Timespec - Supplies the time value.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    time_t Time;\r\n    struct tm TimeInfo;\r\n\r\n    if (Timespec->tv_nsec == UTIME_NOW)\r\n    {\r\n        clock_gettime(CLOCK_REALTIME_COARSE, Timespec);\r\n    }\r\n\r\n    if (((Timespec->tv_sec % FS_FAT_MODIFIED_TIME_PRECISION) != 0) || (Timespec->tv_nsec != 0))\r\n    {\r\n\r\n        Timespec->tv_sec += FS_FAT_MODIFIED_TIME_PRECISION;\r\n    }\r\n\r\n    Timespec->tv_nsec = 0;\r\n    Timespec->tv_sec /= FS_FAT_MODIFIED_TIME_PRECISION;\r\n    Timespec->tv_sec *= FS_FAT_MODIFIED_TIME_PRECISION;\r\n    return;\r\n}\r\n\r\nvoid LxtFsUtimeRoundToNt(struct timespec* Timespec)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine rounds a timespec to NT precision.\r\n\r\nArguments:\r\n\r\n    Timespec - Supplies the time value.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    //\r\n    // WSL rounds up.\r\n    //\r\n\r\n    if (Timespec->tv_nsec != UTIME_NOW)\r\n    {\r\n        Timespec->tv_nsec += (FS_NS_PER_NT_UNIT - 1);\r\n        Timespec->tv_nsec /= FS_NS_PER_NT_UNIT;\r\n        Timespec->tv_nsec *= FS_NS_PER_NT_UNIT;\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint LxtFsWritevCommon(char* TestFile)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the writev system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the test arguments.\r\n\r\n    TestFile - Supplies the file to test on.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    char Buffer2[100];\r\n    int Bytes;\r\n    int FileDescriptor = -1;\r\n    int Result;\r\n    char ContentA[] = \"I am your father! Noooo!\";\r\n    char ContentB[] = \"Go big or go home.\";\r\n    struct iovec Iov[3];\r\n\r\n    LxtCheckErrno(FileDescriptor = open(TestFile, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));\r\n    Iov[0].iov_base = ContentA;\r\n    Iov[0].iov_len = sizeof(ContentA);\r\n    Iov[1].iov_base = ContentB;\r\n    Iov[1].iov_len = sizeof(ContentB);\r\n    LxtCheckErrno(Bytes = writev(FileDescriptor, Iov, 2));\r\n    LxtCheckEqual(Bytes, sizeof(ContentA) + sizeof(ContentB), \"%d\");\r\n    LxtCheckErrno(close(FileDescriptor));\r\n\r\n    //\r\n    // Validate the data with read\r\n    //\r\n\r\n    LxtCheckErrno(FileDescriptor = open(TestFile, O_RDWR, S_IRWXU));\r\n    memset(Buffer, 0, sizeof(Buffer));\r\n    LxtCheckErrno(Bytes = read(FileDescriptor, Buffer, sizeof(ContentA)));\r\n    LxtCheckEqual(Bytes, sizeof(ContentA), \"%d\");\r\n    LxtCheckMemoryEqual(Buffer, ContentA, sizeof(ContentA));\r\n    memset(Buffer, 0, sizeof(Buffer));\r\n    LxtCheckErrno(Bytes = read(FileDescriptor, Buffer, sizeof(ContentB)));\r\n    LxtCheckEqual(Bytes, sizeof(ContentB), \"%d\");\r\n    LxtCheckMemoryEqual(Buffer, ContentB, sizeof(ContentB));\r\n    LxtCheckErrno(close(FileDescriptor));\r\n\r\n    //\r\n    // Validate the data with readv\r\n    //\r\n\r\n    LxtCheckErrno(FileDescriptor = open(TestFile, O_RDWR, S_IRWXU));\r\n    memset(Iov, 0, sizeof(Iov));\r\n    memset(Buffer, 0, sizeof(Buffer));\r\n    memset(Buffer2, 0, sizeof(Buffer2));\r\n    Iov[0].iov_base = Buffer;\r\n    Iov[0].iov_len = sizeof(ContentA);\r\n    Iov[1].iov_base = Buffer2;\r\n    Iov[1].iov_len = sizeof(ContentB);\r\n    LxtCheckErrno(Bytes = readv(FileDescriptor, Iov, 2));\r\n    LxtCheckEqual(Bytes, sizeof(ContentA) + sizeof(ContentB), \"%d\");\r\n    LxtCheckMemoryEqual(Iov[0].iov_base, ContentA, sizeof(ContentA));\r\n    LxtCheckMemoryEqual(Iov[1].iov_base, ContentB, sizeof(ContentB));\r\n    LxtCheckErrno(close(FileDescriptor));\r\n\r\n    //\r\n    // Validate that readv\\writev returns the right error code when an invalid\r\n    // count is passed.\r\n    //\r\n\r\n    LxtLogInfo(\"invalid vector count\");\r\n    LxtCheckErrno(FileDescriptor = open(TestFile, O_RDWR, S_IRWXU));\r\n\r\n#ifdef __x86_64__\r\n\r\n#define READV_SYSCALL_NR 19\r\n\r\n#elif __arm__\r\n\r\n#define READV_SYSCALL_NR 65\r\n\r\n#endif\r\n\r\n    LxtCheckErrnoFailure(syscall(READV_SYSCALL_NR, FileDescriptor, Iov, -1), EINVAL);\r\n    LxtCheckErrnoFailure(syscall(READV_SYSCALL_NR, FileDescriptor, Iov, -1), EINVAL);\r\n    LxtCheckErrno(close(FileDescriptor));\r\n\r\n    //\r\n    // Test a zero-length iovector at the beginning of the array, it should be skipped.\r\n    //\r\n\r\n    LxtCheckErrno(FileDescriptor = open(TestFile, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));\r\n    memset(Iov, 0, sizeof(Iov));\r\n    Iov[0].iov_base = ContentA;\r\n    Iov[1].iov_base = ContentB;\r\n    Iov[1].iov_len = sizeof(ContentB);\r\n    LxtCheckErrno(Bytes = writev(FileDescriptor, Iov, 2));\r\n    LxtCheckEqual(Bytes, sizeof(ContentB), \"%d\");\r\n    LxtCheckErrno(close(FileDescriptor));\r\n\r\n    LxtCheckErrno(FileDescriptor = open(TestFile, O_RDWR, S_IRWXU));\r\n    memset(Buffer, 0, sizeof(Buffer));\r\n    LxtCheckErrno(Bytes = read(FileDescriptor, Buffer, sizeof(ContentB)));\r\n    LxtCheckEqual(Bytes, sizeof(ContentB), \"%d\");\r\n    LxtCheckMemoryEqual(Buffer, ContentB, sizeof(ContentB));\r\n    LxtCheckErrno(close(FileDescriptor));\r\n\r\n    //\r\n    // Test an invalid buffer after a valid buffer. The writev call should return the\r\n    // number of bytes written until the invalid buffer.\r\n    //\r\n    // N.B. The plan 9 client does not follow this behavior, and will write nothing.\r\n    //\r\n\r\n    LxtCheckErrno(FileDescriptor = open(TestFile, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU));\r\n    memset(Iov, 0, sizeof(Iov));\r\n    Iov[0].iov_base = ContentA;\r\n    Iov[0].iov_len = sizeof(ContentA);\r\n    Iov[1].iov_base = NULL;\r\n    Iov[1].iov_len = sizeof(ContentB);\r\n    Iov[2].iov_base = ContentB;\r\n    Iov[2].iov_len = sizeof(ContentB);\r\n    LxtLogInfo(\"%d\", g_LxtFsInfo.FsType);\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9)\r\n    {\r\n        LxtCheckErrnoFailure(Bytes = writev(FileDescriptor, Iov, 3), EFAULT);\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(Bytes = writev(FileDescriptor, Iov, 3));\r\n        LxtCheckEqual(Bytes, sizeof(ContentA), \"%d\");\r\n        LxtCheckErrno(close(FileDescriptor));\r\n\r\n        LxtCheckErrno(FileDescriptor = open(TestFile, O_RDWR, S_IRWXU));\r\n        memset(Buffer, 0, sizeof(Buffer));\r\n        LxtCheckErrno(Bytes = read(FileDescriptor, Buffer, sizeof(ContentA)));\r\n        LxtCheckEqual(Bytes, sizeof(ContentA), \"%d\");\r\n        LxtCheckErrno(Bytes = read(FileDescriptor, Buffer, sizeof(ContentB)));\r\n        LxtCheckEqual(Bytes, 0, \"%d\");\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (FileDescriptor != -1)\r\n    {\r\n        close(FileDescriptor);\r\n    }\r\n\r\n    unlink(TestFile);\r\n    return Result;\r\n}\r\n\r\nint LxtFsDirSeekCommon(const char* BaseDir)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the seek operation on a directory.\r\n\r\nArguments:\r\n\r\n    BaseDir - Supplies the directory to use. for the test. The directory should\r\n        not already exist.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[4096];\r\n    int DirFd;\r\n    int Result;\r\n    int Size;\r\n    int FirstSize;\r\n\r\n    //\r\n    // Create the base directory.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(BaseDir, 0777));\r\n    LxtCheckErrno(DirFd = open(BaseDir, O_RDONLY | O_DIRECTORY));\r\n    LxtCheckErrno(Size = syscall(SYS_getdents64, DirFd, Buffer, sizeof(Buffer)));\r\n\r\n    //\r\n    // Directory should at least have one entry.\r\n    //\r\n\r\n    if (Size == 0)\r\n    {\r\n        LxtLogError(\"Directory is expected to at least have one entry.\");\r\n        Result = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Store the value returned from the first call to 'getdents'.\r\n    //\r\n\r\n    FirstSize = Size;\r\n\r\n    //\r\n    // Once the end of directory has reached, getdents should return 0.\r\n    //\r\n\r\n    LxtCheckErrno(Size = syscall(SYS_getdents64, DirFd, Buffer, sizeof(Buffer)));\r\n    if (Size != 0)\r\n    {\r\n        LxtLogError(\r\n            \"getdents should return 0 when end of directory is reached, \"\r\n            \"but it returned: %d.\",\r\n            Size);\r\n\r\n        Result = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(lseek(DirFd, 0, SEEK_SET));\r\n    LxtCheckErrno(Size = syscall(SYS_getdents64, DirFd, Buffer, sizeof(Buffer)));\r\n\r\n    //\r\n    // Directory should at least have one entry.\r\n    //\r\n\r\n    if (Size == 0)\r\n    {\r\n        LxtLogError(\r\n            \"lseek on a dir should rewind the cursor position, but it \"\r\n            \"did not.\");\r\n\r\n        Result = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (Size != FirstSize)\r\n    {\r\n        LxtLogError(\r\n            \"getdents value should not have changed from first call. \"\r\n            \"First getdents: %d, Second getdents: %d\",\r\n            FirstSize,\r\n            Size);\r\n\r\n        Result = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (DirFd >= 0)\r\n    {\r\n        close(DirFd);\r\n    }\r\n\r\n    rmdir(BaseDir);\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/lxtfs.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtfs.h\r\n\r\nAbstract:\r\n\r\n    This file contains common test functions for file system tests.\r\n\r\n--*/\r\n\r\n#include <sys/inotify.h>\r\n\r\n#define FS_DRVFS_PREFIX \"/mnt/c\"\r\n#define FS_DRVFS_DRIVE \"C:\"\r\n#define FS_DRVFS_NAME \"drvfs\"\r\n#define FS_WSLFS_NAME \"wslfs\"\r\n#define FS_9P_NAME \"9p\"\r\n#define FS_VIRTIOFS_NAME \"virtiofs\"\r\n#define INOTIFY_TEST_EVENTS_BUF_SIZE 50\r\n#define INOTIFY_TEST_FILE1_NAME_ONLY \"a.txt\"\r\n#define INOTIFY_TEST_FILE2_NAME_ONLY \"b.txt\"\r\n#define INOTIFY_TEST_FILE3_NAME_ONLY \"c.txt\"\r\n#define INOTIFY_TEST_FILE1_SLINK_NAME_ONLY \"as.txt\"\r\n#define INOTIFY_TEST_FILE1_HLINK_NAME_ONLY \"ah.txt\"\r\n#define LXT_XATTR_CASE_SENSITIVE \"system.wsl_case_sensitive\"\r\n\r\n//\r\n// Flags for the utime test.\r\n//\r\n\r\n#define FS_UTIME_NT_PRECISION (0x1)\r\n#define FS_UTIME_FAT (0x2)\r\n#define FS_UTIME_NO_SYMLINKS (0x4)\r\n\r\n//\r\n// Flags for the getdents alignment test.\r\n//\r\n\r\n#define FS_TEST_GETDENTS64 (0x1)\r\n\r\n//\r\n// Flags for the timestamp test.\r\n//\r\n\r\n#define FS_TIMESTAMP_NOATIME (0x1)\r\n\r\n//\r\n// Flags for the delete tests.\r\n//\r\n\r\n#define FS_DELETE_DRVFS (0x1)\r\n\r\n#define FS_IS_PLAN9_CACHED() ((g_LxtFsInfo.FsType == LxtFsTypePlan9) && (g_LxtFsInfo.Flags.Cached != 0))\r\n\r\ntypedef enum _LXT_FS_TYPE\r\n{\r\n    LxtFsTypeLxFs,\r\n    LxtFsTypeWslFs,\r\n    LxtFsTypeDrvFs,\r\n    LxtFsTypePlan9,\r\n    LxtFsTypeVirtioFs\r\n} LXT_FS_TYPE,\r\n    *PLXT_FS_TYPE;\r\n\r\ntypedef struct _LXT_FS_INFO\r\n{\r\n    LXT_FS_TYPE FsType;\r\n    union\r\n    {\r\n        struct\r\n        {\r\n            int DrvFsBehavior : 1;\r\n            int Cached : 1;\r\n            int VirtIo : 1;\r\n            int Dax : 1;\r\n        };\r\n\r\n        int AllFlags;\r\n    } Flags;\r\n} LXT_FS_INFO, *PLXT_FS_INFO;\r\n\r\nint LxtFsCheckDrvFsMount(const char* Source, const char* Target, const char* Options, int ParentId, const char* MountRoot);\r\n\r\nint LxtFsCreateTestDir(char* Directory);\r\n\r\nint LxtFsDeleteCurrentWorkingDirectoryCommon(char* BaseDir, int Flags);\r\n\r\nint LxtFsDeleteOpenFileCommon(char* BaseDir, int Flags);\r\n\r\nint LxtFsDeleteLoopCommon(const char* BaseDir);\r\n\r\nint LxtFsGetDentsAlignmentCommon(const char* BaseDir, int Flags);\r\n\r\nint LxtFsGetFsInfo(const char* Path, PLXT_FS_INFO Info);\r\n\r\nint LxtFsInotifyEpollCommon(char* BaseDir);\r\n\r\nint LxtFsInotifyPosixUnlinkRenameCommon(char* BaseDir);\r\n\r\nint LxtFsInotifyReadAndProcess(int Id, char* ReadBuf, int ReadBufSize, struct inotify_event** Events, int NumEventsIn, int* NumEventsOut, int IgnoreAttribModify);\r\n\r\nint LxtFsInotifyUnmountBindCommon(char* BaseDir);\r\n\r\nint LxtFsMountDrvFs(const char* Source, const char* Target, const char* Options);\r\n\r\nint LxtFsRenameAtCommon(int DirFd1, int DirFd2);\r\n\r\nint LxtFsRenameDirCommon(char* BaseDir);\r\n\r\nvoid LxtFsTestCleanup(const char* TestDir, const char* DrvFsDir, bool UseDrvFs);\r\n\r\nint LxtFsTestSetup(PLXT_ARGS Args, const char* TestDir, const char* DrvFsDir, bool UseDrvFs);\r\n\r\nint LxtFsTimestampCommon(const char* BaseDir, int Flags);\r\n\r\nint LxtFsUtimeBasicCommon(const char* BaseDir, int Flags);\r\n\r\nvoid LxtFsUtimeCleanupTestFiles(const char* BaseDir);\r\n\r\nint LxtFsUtimeCreateTestFiles(const char* BaseDir, int Flags);\r\n\r\nint LxtFsWritevCommon(char* TestFile);\r\n\r\nint LxtFsDirSeekCommon(const char* BaseDir);\r\n\r\nextern LXT_FS_INFO g_LxtFsInfo;"
  },
  {
    "path": "test/linux/unit_tests/lxtlog.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtlog.c\r\n\r\nAbstract:\r\n\r\n    This file contains lx test logging routines.\r\n\r\n--*/\r\n\r\n#include <stdio.h>\r\n#include <stdarg.h>\r\n#include <unistd.h>\r\n#include <string.h>\r\n#include <time.h>\r\n#include <fcntl.h>\r\n#include \"lxtutil.h\"\r\n#include \"lxtlog.h\"\r\n\r\n#define LXT_LOG_TIMESTAMP_BUFFER_SIZE 64\r\n\r\nstatic LxtLogType g_LxtLogTypeMask = LXT_LOG_TYPE_DEFAULT_MASK;\r\nstatic const char* g_LxtTestName = \"\";\r\nstatic char g_LXTestLogFileName[64] = \"/data/test/log/\";\r\nstatic FILE* g_LxtFile = NULL;\r\n\r\nvoid LxtLog(LxtLogLevel LogLevel, const char* Message, ...)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    static volatile int Active = 0;\r\n    va_list Args;\r\n    char TimeFormat[LXT_LOG_TIMESTAMP_BUFFER_SIZE];\r\n    struct tm* TimeInfo;\r\n    static struct timespec TimeSpec;\r\n    char TimeStamp[LXT_LOG_TIMESTAMP_BUFFER_SIZE];\r\n\r\n    if ((LogLevel == LxtLogLevelResourceError) && ((g_LxtLogTypeMask & LxtLogTypeStress) != 0))\r\n    {\r\n\r\n        goto Exit;\r\n    }\r\n\r\n    //\r\n    // Create a timestamp string which will precede the provided log message.\r\n    // The format of the string is: [Hours:Minutes:Seconds.Milliseconds]\r\n    //\r\n    // N.B. The returns of strftime and snprintf are not checked because the\r\n    //      provided buffers are large enough for the format strings.\r\n    //\r\n\r\n    LxtClockGetTime(CLOCK_REALTIME, &TimeSpec);\r\n\r\n    //\r\n    // N.B. The signal handling code may use this function and not all of the\r\n    //      functions used here are reentrant safe, resulting in possible\r\n    //      deadlocks or crashes. To avoid these keep an active count and skip\r\n    //      functions known to be problematic in the signal case. The functions\r\n    //      identified at this point are:\r\n    //          - fflush\r\n    //          - localtime\r\n    //          - strftime\r\n    //\r\n\r\n    if (__sync_add_and_fetch(&Active, 1) == 1)\r\n    {\r\n        TimeInfo = localtime(&TimeSpec.tv_sec);\r\n        strftime(TimeFormat, sizeof(TimeFormat), \"[%H:%M:%S\", TimeInfo);\r\n    }\r\n\r\n    snprintf(TimeStamp, sizeof(TimeStamp), \"%s.%03u] \", TimeFormat, (unsigned int)TimeSpec.tv_nsec / (1000 * 1000));\r\n\r\n    //\r\n    // Print the timestamp string followed by the provided message.\r\n    //\r\n\r\n    if ((g_LxtLogTypeMask & LxtLogTypePrintf) != 0)\r\n    {\r\n        va_start(Args, Message);\r\n        printf(\"%s\", TimeStamp);\r\n        vprintf(Message, Args);\r\n        va_end(Args);\r\n    }\r\n\r\n    if (((g_LxtLogTypeMask & LxtLogTypeFile) != 0) && (g_LxtFile != NULL))\r\n    {\r\n        va_start(Args, Message);\r\n        fprintf(g_LxtFile, \"%s\", TimeStamp);\r\n        vfprintf(g_LxtFile, Message, Args);\r\n        va_end(Args);\r\n    }\r\n\r\n    if (__sync_sub_and_fetch(&Active, 1) == 0)\r\n    {\r\n        sigset_t PreviousSignals;\r\n        sigset_t SignalMask;\r\n        sigfillset(&SignalMask);\r\n\r\n        int MaskResult = pthread_sigmask(SIG_BLOCK, &SignalMask, &PreviousSignals);\r\n        if (((g_LxtLogTypeMask & LxtLogTypeFile) != 0) && (g_LxtFile != NULL))\r\n        {\r\n            fflush(g_LxtFile);\r\n        }\r\n\r\n        if ((g_LxtLogTypeMask & LxtLogTypePrintf) != 0)\r\n        {\r\n            fflush(stdout);\r\n        }\r\n\r\n        if (MaskResult == 0)\r\n        {\r\n            pthread_sigmask(SIG_SETMASK, &PreviousSignals, NULL);\r\n        }\r\n    }\r\n\r\nExit:\r\n    return;\r\n}\r\n\r\nint LxtLogInitialize(const char* TestName, LxtLogType LogTypeMask, bool LogAppend)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char* Mode;\r\n    int Result;\r\n\r\n    g_LxtTestName = TestName;\r\n    g_LxtLogTypeMask = LogTypeMask;\r\n    strcat(g_LXTestLogFileName, TestName);\r\n    if ((g_LxtLogTypeMask & LxtLogTypeFile) != 0)\r\n    {\r\n        Mode = \"w\";\r\n        if (LogAppend != false)\r\n        {\r\n            Mode = \"a\";\r\n        }\r\n\r\n        g_LxtFile = fopen(g_LXTestLogFileName, Mode);\r\n        if (g_LxtFile == NULL)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            g_LxtLogTypeMask &= ~LxtLogTypeFile;\r\n            LxtLogError(\"Failed to open %s: %s\", g_LXTestLogFileName, strerror(errno));\r\n\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid LxtLogUninitialize(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    //\r\n    // Close and flush the log file. This ensures that the file contents are\r\n    // able to be read from Windows.\r\n    //\r\n\r\n    if (g_LxtFile != NULL)\r\n    {\r\n        fflush(g_LxtFile);\r\n        fsync(fileno(g_LxtFile));\r\n        fclose(g_LxtFile);\r\n        g_LxtFile = NULL;\r\n    }\r\n\r\n    return;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/lxtlog.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtlog.h\r\n\r\nAbstract:\r\n\r\n    This file contains lx test logging routines.\r\n\r\n--*/\r\n\r\n#ifndef _LXT_LOG\r\n#define _LXT_LOG\r\n\r\n#include <unistd.h>\r\n#include <string.h>\r\n#include <errno.h>\r\n#include <stdbool.h>\r\n\r\ntypedef enum _LxtLogType\r\n{\r\n    LxtLogTypeFile = 0x1,\r\n    LxtLogTypePrintf = 0x2,\r\n    LxtLogTypeStress = 0x4,\r\n    LxtLogTypeMax\r\n} LxtLogType,\r\n    *PLxtLogType;\r\n\r\ntypedef enum _LxtLogLevel\r\n{\r\n    LxtLogLevelInfo = 0,\r\n    LxtLogLevelError,\r\n    LxtLogLevelResourceError,\r\n    LxtLogLevelPass,\r\n    LxtLogLevelStart,\r\n    LxtLogLevelMax\r\n} LxtLogLevel,\r\n    *PLxtLogLevel;\r\n\r\n#define LXT_LOG_TYPE_DEFAULT_MASK (LxtLogTypeFile | LxtLogTypePrintf)\r\n\r\nvoid LxtLog(LxtLogLevel LogLevel, const char* Message, ...);\r\n\r\nint LxtLogInitialize(const char* TestName, LxtLogType LogTypeMask, bool LogAppend);\r\n\r\nvoid LxtLogUninitialize(void);\r\n\r\n#define LXT_RESULT_SUCCESS (0)\r\n#define LXT_RESULT_FAILURE (-1)\r\n#define LXT_SUCCESS(Result) ((Result) != LXT_RESULT_FAILURE)\r\n\r\n#define LxtLogStart(str, ...) \\\r\n    { \\\r\n        LxtLog(LxtLogLevelStart, \"START: %s:%u: \" str \"\\n\", __FUNCTION__, __LINE__, ##__VA_ARGS__); \\\r\n    }\r\n\r\n#define LxtLogInfo(str, ...) \\\r\n    { \\\r\n        LxtLog(LxtLogLevelInfo, \"INFO: %s:%u: \" str \"\\n\", __FUNCTION__, __LINE__, ##__VA_ARGS__); \\\r\n    }\r\n\r\n#define LxtLogResourceError(str, ...) \\\r\n    { \\\r\n        LxtLog(LxtLogLevelResourceError, \"RESOURCE_ERROR: %s:%u: \" str \"\\n\", __FUNCTION__, __LINE__, ##__VA_ARGS__); \\\r\n    }\r\n\r\n#define LxtLogError(str, ...) \\\r\n    { \\\r\n        LxtLog(LxtLogLevelError, \"ERROR: %s:%u: \" str \"\\n\", __FUNCTION__, __LINE__, ##__VA_ARGS__); \\\r\n    }\r\n\r\n#define LxtLogPassed(str, ...) \\\r\n    { \\\r\n        LxtLog(LxtLogLevelPass, \"PASS: %s:%u: \" str \"\\n\", __FUNCTION__, __LINE__, ##__VA_ARGS__); \\\r\n    }\r\n\r\n#define LxtCheckErrno(fn) \\\r\n    { \\\r\n        Result = (fn); \\\r\n        if (LXT_SUCCESS(Result) == 0) \\\r\n        { \\\r\n            LxtLogError(\"%s failed: %d (%s)\", #fn, errno, strerror(errno)); \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n#define LxtCheckMapErrno(fn) \\\r\n    { \\\r\n        MapResult = (fn); \\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n        if (MapResult == MAP_FAILED) \\\r\n        { \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            LxtLogError(\"%s failed: %s\", #fn, strerror(errno)); \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n#define LxtCheckMapErrnoFailure(_Fn, _ExpectedErrno) \\\r\n    { \\\r\n        MapResult = (_Fn); \\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n        if (MapResult != MAP_FAILED) \\\r\n        { \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            LxtLogError(\"%s succeeded, expected errno %d\", #_Fn, (_ExpectedErrno)); \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        if (errno != (_ExpectedErrno)) \\\r\n        { \\\r\n            LxtLogError(\"%s unexpected failure status: %d != %d (%s)\", #_Fn, _ExpectedErrno, errno, strerror(errno)); \\\r\n\\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n#define LxtCheckNullErrno(_Fn) \\\r\n    { \\\r\n        PointerResult = (_Fn); \\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n        if (PointerResult == NULL) \\\r\n        { \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            LxtLogError(\"%s failed: %s\", #_Fn, strerror(errno)); \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n#define LxtCheckNullErrnoFailure(_Fn, _ExpectedErrno) \\\r\n    { \\\r\n        PointerResult = (_Fn); \\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n        if (PointerResult != NULL) \\\r\n        { \\\r\n            LxtLogError(\"%s succeeded, expected errno %d\", #_Fn, (_ExpectedErrno)); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        if (errno != (_ExpectedErrno)) \\\r\n        { \\\r\n            LxtLogError(\"%s unexpected failure status: %d != %d (%s)\", #_Fn, _ExpectedErrno, errno, strerror(errno)); \\\r\n\\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n//\r\n// The following macro is for functions where 0 is the only valid success value\r\n// (they don't return a pid or fd or anything), and where errno is set if an\r\n// error occurs.\r\n//\r\n\r\n#define LxtCheckErrnoZeroSuccess(_Fn) \\\r\n    { \\\r\n        Result = (_Fn); \\\r\n        if (Result != LXT_RESULT_SUCCESS) \\\r\n        { \\\r\n            if (LXT_SUCCESS(Result) != 0) \\\r\n            { \\\r\n                LxtLogError(\"%s succeeded with %d, expected 0.\", #_Fn, Result); \\\r\n            } \\\r\n            else \\\r\n            { \\\r\n                LxtLogError(\"%s failed: %d, errno %d (%s)\", #_Fn, Result, errno, strerror(errno)); \\\r\n            } \\\r\n\\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n#define LxtCheckErrnoFailure(_Fn, _ExpectedErrno) \\\r\n    { \\\r\n        Result = (_Fn); \\\r\n        if ((LXT_SUCCESS(Result) != 0)) \\\r\n        { \\\r\n            LxtLogError(\"%s succeeded with %d, expected errno %d\", #_Fn, Result, _ExpectedErrno); \\\r\n\\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        if ((_ExpectedErrno) != errno) \\\r\n        { \\\r\n            LxtLogError(\"%s unexpected failure status: %d, %d != %d (%s)\", #_Fn, Result, _ExpectedErrno, errno, strerror(errno)); \\\r\n\\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#define LxtCheckEqual(_Val1, _Val2, _Format) \\\r\n    { \\\r\n        if ((_Val1) != (_Val2)) \\\r\n        { \\\r\n            LxtLogError(\"{:%s (\" _Format \") != %s (\" _Format \"):}\", #_Val1, (_Val1), #_Val2, (_Val2)); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#define LxtCheckGreaterOrEqual(_Val1, _Val2, _Format) \\\r\n    { \\\r\n        if ((_Val1) < (_Val2)) \\\r\n        { \\\r\n            LxtLogError(\"{:%s (\" _Format \") < %s (\" _Format \"):}\", #_Val1, (_Val1), #_Val2, (_Val2)); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#define LxtCheckNotEqual(_Val1, _Val2, _Format) \\\r\n    { \\\r\n        if ((_Val1) == (_Val2)) \\\r\n        { \\\r\n            LxtLogError(\"{:%s (\" _Format \") == %s (\" _Format \"):}\", #_Val1, (_Val1), #_Val2, (_Val2)); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#define LxtCheckStringEqual(_Val1, _Val2) \\\r\n    { \\\r\n        if ((((_Val1) == NULL) && ((_Val2) != NULL)) || (((_Val1) != NULL) && ((_Val2) == NULL)) || \\\r\n            (((_Val1) != (_Val2)) && (strcmp((_Val1), (_Val2)) != 0))) \\\r\n        { \\\r\n\\\r\n            LxtLogError(\"%s (\\\"%s\\\") != %s (\\\"%s\\\")\", #_Val1, (_Val1), #_Val2, (_Val2)); \\\r\n\\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#define LxtCheckStringNotEqual(_Val1, _Val2) \\\r\n    { \\\r\n        if (strcmp((_Val1), (_Val2)) == 0) \\\r\n        { \\\r\n            LxtLogError(\"%s (\\\"%s\\\") == %s (\\\"%s\\\")\", #_Val1, (_Val1), #_Val2, (_Val2)); \\\r\n\\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#define LxtCheckGreater(_Val1, _Val2, _Format) \\\r\n    { \\\r\n        if ((_Val1) <= (_Val2)) \\\r\n        { \\\r\n            LxtLogError(\"{:%s (\" _Format \") <= %s (\" _Format \"):}\", #_Val1, (_Val1), #_Val2, (_Val2)); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#define LxtCheckMemoryEqual(_Ptr1, _Ptr2, _Size) \\\r\n    { \\\r\n        Result = LxtCompareMemory((_Ptr1), (_Ptr2), (_Size), #_Ptr1, #_Ptr2); \\\r\n        if (Result < 0) \\\r\n        { \\\r\n            LxtLogError(\"Memory contents were not equal\"); \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n#define LxtCheckTrue(_Val) \\\r\n    { \\\r\n        if ((_Val) == FALSE) \\\r\n        { \\\r\n            LxtLogError(\"The expression (\" #_Val \") does not equal true\"); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#define LxtCheckResult(fn) \\\r\n    { \\\r\n        Result = (fn); \\\r\n        if (LXT_SUCCESS(Result) == 0) \\\r\n        { \\\r\n            LxtLogError(\"%s failed\", #fn); \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n//\r\n// Macro for functions that return a positive error value on failure, like\r\n// pthread functions.\r\n//\r\n\r\n#define LxtCheckResultError(_Fn) \\\r\n    { \\\r\n        Result = (_Fn); \\\r\n        if (Result != 0) \\\r\n        { \\\r\n            LxtLogError(\"%s failed: %d (%s)\", #_Fn, Result, strerror(Result)); \\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n    }\r\n\r\n#define LxtCheckResultErrorFailure(_Fn, _ExpectedErrno) \\\r\n    { \\\r\n        Result = (_Fn); \\\r\n        if (Result == 0) \\\r\n        { \\\r\n            LxtLogError(\"%s succeeded, expected errno %d\", #_Fn, _ExpectedErrno); \\\r\n\\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        if ((_ExpectedErrno) != Result) \\\r\n        { \\\r\n            LxtLogError(\"%s unexpected failure status: %d != %d (%s)\", #_Fn, (_ExpectedErrno), Result, strerror(Result)); \\\r\n\\\r\n            Result = LXT_RESULT_FAILURE; \\\r\n            goto ErrorExit; \\\r\n        } \\\r\n\\\r\n        Result = LXT_RESULT_SUCCESS; \\\r\n    }\r\n\r\n#endif // _LXT_LOG\r\n"
  },
  {
    "path": "test/linux/unit_tests/lxtmount.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtmount.c\r\n\r\nAbstract:\r\n\r\n    This file contains mount primitive support.\r\n\r\n--*/\r\n\r\n#include \"lxtlog.h\"\r\n#include <sys/mman.h>\r\n#include \"lxtcommon.h\"\r\n#include <sys/mount.h>\r\n#include <linux/capability.h>\r\n#include <sys/sysmacros.h>\r\n#include <libgen.h>\r\n#include <fcntl.h>\r\n#include <stdlib.h>\r\n#include <libmount/libmount.h>\r\n#include <sys/mman.h>\r\n#include \"lxtmount.h\"\r\n\r\n#define PATH_MAX (4096)\r\n\r\nvoid MountEscapeString(const char* Source, char* Dest, size_t Length);\r\nint MountCheckFsOptionsPattern(const char* Pattern, const char* Actual);\r\n\r\nint MountCheckIsMount(\r\n    const char* Path,\r\n    int ExpectedParentId,\r\n    const char* ExpectedSource,\r\n    const char* ExpectedFsType,\r\n    const char* ExpectedRoot,\r\n    const char* ExpectedMountOptions,\r\n    const char* ExpectedFsOptions,\r\n    const char* ExpectedCombinedOptions,\r\n    int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if a path is a mount point.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns the mount ID on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Direction;\r\n    const char* ExpectedSourceActual;\r\n    struct libmnt_fs* FileSystem;\r\n    char LocalPath[PATH_MAX];\r\n    int MountId;\r\n    int Result;\r\n    struct stat Stat;\r\n    struct libmnt_table* Table;\r\n\r\n    Table = NULL;\r\n    if (strcmp(Path, \"/\") != 0)\r\n    {\r\n        LxtCheckResult(MountIsMount(AT_FDCWD, Path));\r\n        if (Result == 0)\r\n        {\r\n            LxtLogError(\"%s is not a mount point.\", Path);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(stat(Path, &Stat));\r\n\r\n    //\r\n    // Verify the mount in the /proc/self/mountinfo file.\r\n    //\r\n\r\n    if ((Flags & MOUNT_FIRST_MOUNT) != 0)\r\n    {\r\n        Direction = MNT_ITER_FORWARD;\r\n    }\r\n    else\r\n    {\r\n        Direction = MNT_ITER_BACKWARD;\r\n    }\r\n\r\n    LxtCheckResult(MountFindMount(MOUNT_PROC_MOUNTINFO, Path, 0, &Table, &FileSystem, Direction));\r\n\r\n    LxtCheckNotEqual(FileSystem, NULL, \"%p\");\r\n    LxtLogInfo(\"%s on %s fstype %s (%s)\", mnt_fs_get_source(FileSystem), mnt_fs_get_target(FileSystem), mnt_fs_get_fstype(FileSystem), mnt_fs_get_options(FileSystem));\r\n\r\n    ExpectedSourceActual = ExpectedSource;\r\n\r\n#if LIBMOUNT_MAJOR_VERSION >= 2\r\n\r\n    if ((ExpectedSourceActual == NULL) && (strcmp(ExpectedFsType, \"virtiofs\") != 0))\r\n    {\r\n        ExpectedSourceActual = \"none\";\r\n    }\r\n\r\n#endif\r\n\r\n    LxtCheckEqual(ExpectedParentId, mnt_fs_get_parent_id(FileSystem), \"%d\");\r\n    if (ExpectedSourceActual)\r\n    {\r\n        LxtCheckStringEqual(ExpectedSourceActual, mnt_fs_get_source(FileSystem));\r\n    }\r\n\r\n    LxtCheckStringEqual(ExpectedFsType, mnt_fs_get_fstype(FileSystem));\r\n    strcpy(LocalPath, ExpectedRoot);\r\n    if ((Flags & MOUNT_SOURCE_DELETED) != 0)\r\n    {\r\n        strcat(LocalPath, \"//deleted\");\r\n    }\r\n\r\n    LxtCheckStringEqual(LocalPath, mnt_fs_get_root(FileSystem));\r\n    LxtCheckStringEqual(ExpectedMountOptions, mnt_fs_get_vfs_options(FileSystem));\r\n    if (ExpectedFsOptions != NULL)\r\n    {\r\n        if (strchr(ExpectedFsOptions, '*') != NULL)\r\n        {\r\n            LxtCheckResult(MountCheckFsOptionsPattern(ExpectedFsOptions, mnt_fs_get_fs_options(FileSystem)));\r\n        }\r\n        else\r\n        {\r\n            LxtCheckStringEqual(ExpectedFsOptions, mnt_fs_get_fs_options(FileSystem));\r\n        }\r\n    }\r\n\r\n    LxtCheckEqual(Stat.st_dev, mnt_fs_get_devno(FileSystem), \"%lu\");\r\n    MountId = mnt_fs_get_id(FileSystem);\r\n    LxtCheckGreater(MountId, 0, \"%d\");\r\n    LxtCheckNotEqual(MountId, ExpectedParentId, \"%d\");\r\n    mnt_free_table(Table);\r\n    Table = NULL;\r\n\r\n    //\r\n    // Verify the mount in the /proc/mounts file.\r\n    //\r\n\r\n    LxtCheckResult(MountFindMount(MOUNT_PROC_MOUNTS, Path, 0, &Table, &FileSystem, Direction));\r\n\r\n    LxtCheckNotEqual(FileSystem, NULL, \"%p\");\r\n    if (ExpectedSourceActual)\r\n    {\r\n        LxtCheckStringEqual(ExpectedSourceActual, mnt_fs_get_source(FileSystem));\r\n    }\r\n\r\n    LxtCheckStringEqual(ExpectedFsType, mnt_fs_get_fstype(FileSystem));\r\n    if (ExpectedCombinedOptions != NULL)\r\n    {\r\n        LxtCheckStringEqual(ExpectedCombinedOptions, mnt_fs_get_options(FileSystem));\r\n    }\r\n\r\n    //\r\n    // Verify the mount in the /proc/self/mountstats file.\r\n    //\r\n\r\n    if (ExpectedSourceActual)\r\n    {\r\n        LxtCheckResult(MountFindMountStats(ExpectedSource, Path, ExpectedFsType));\r\n    }\r\n\r\n    Result = MountId;\r\n\r\nErrorExit:\r\n    if (Table != NULL)\r\n    {\r\n        mnt_free_table(Table);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MountCheckIsNotMount(const char* Path)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if a path is a mount point.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct libmnt_fs* FileSystem;\r\n    int Result;\r\n    struct libmnt_table* Table;\r\n\r\n    Table = NULL;\r\n    LxtCheckResult(MountIsMount(AT_FDCWD, Path));\r\n    if (Result != 0)\r\n    {\r\n        LxtLogError(\"%s is a mount point.\", Path);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Verify the mount is not in the /proc/self/mountinfo file.\r\n    //\r\n\r\n    LxtCheckResult(MountFindMount(MOUNT_PROC_MOUNTINFO, Path, 0, &Table, &FileSystem, MNT_ITER_BACKWARD));\r\n\r\n    LxtCheckEqual(FileSystem, NULL, \"%p\");\r\n    mnt_free_table(Table);\r\n    Table = NULL;\r\n\r\n    //\r\n    // Verify the mount in the /proc/mounts file.\r\n    //\r\n\r\n    LxtCheckResult(MountFindMount(MOUNT_PROC_MOUNTS, Path, 0, &Table, &FileSystem, MNT_ITER_BACKWARD));\r\n\r\n    LxtCheckEqual(FileSystem, NULL, \"%p\");\r\n\r\nErrorExit:\r\n    if (Table != NULL)\r\n    {\r\n        mnt_free_table(Table);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MountCheckFsOptionsPattern(const char* Pattern, const char* Actual)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if mount options match a pattern with wildcards.\r\n    The '*' wildcard matches any sequence of characters.\r\n\r\nArguments:\r\n\r\n    Pattern - Supplies the expected pattern (e.g., \"rfd=*,wfd=*\").\r\n\r\n    Actual - Supplies the actual mount options string.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    const char* ActualPtr;\r\n    const char* MatchPosition;\r\n    const char* PatternPtr;\r\n    int Result;\r\n    const char* StarPosition;\r\n\r\n    PatternPtr = Pattern;\r\n    ActualPtr = Actual;\r\n    StarPosition = NULL;\r\n    MatchPosition = NULL;\r\n    Result = LXT_RESULT_FAILURE;\r\n\r\n    while (*ActualPtr != '\\0')\r\n    {\r\n        if (*PatternPtr == '*')\r\n        {\r\n            //\r\n            // Remember position of * and where we are in actual string.\r\n            //\r\n\r\n            StarPosition = PatternPtr++;\r\n            MatchPosition = ActualPtr;\r\n        }\r\n        else if (*PatternPtr == *ActualPtr)\r\n        {\r\n            //\r\n            // Characters match, advance both.\r\n            //\r\n\r\n            PatternPtr++;\r\n            ActualPtr++;\r\n        }\r\n        else if (StarPosition != NULL)\r\n        {\r\n            //\r\n            // Mismatch but we have a *, backtrack and try matching one more character.\r\n            //\r\n\r\n            PatternPtr = StarPosition + 1;\r\n            ActualPtr = ++MatchPosition;\r\n        }\r\n        else\r\n        {\r\n            //\r\n            // Mismatch and no * to backtrack to.\r\n            //\r\n\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Consume any trailing *'s in pattern.\r\n    //\r\n\r\n    while (*PatternPtr == '*')\r\n    {\r\n        PatternPtr++;\r\n    }\r\n\r\n    //\r\n    // Both should be at end of string.\r\n    //\r\n\r\n    if (*PatternPtr != '\\0')\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (!LXT_SUCCESS(Result))\r\n    {\r\n        LxtLogError(\"Mount options mismatch: expected '%s', got '%s'\", Pattern, Actual);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid MountEscapeString(const char* Source, char* Dest, size_t Length)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine escapes a string using procfs rules.\r\n\r\nArguments:\r\n\r\n    Source - Supplies the string to escape.\r\n\r\n    Dest - Supplies the buffer to write the escaped string to.\r\n\r\n    Length - Supplies the length of the destination buffer.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    size_t Index;\r\n\r\n    for (Index = 0; (Index < Length - 1) && (*Source != '\\0'); Index += 1, Source += 1)\r\n    {\r\n        switch (*Source)\r\n        {\r\n        case ' ':\r\n        case '\\n':\r\n        case '\\t':\r\n        case '\\\\':\r\n            Index += snprintf(Dest + Index, Length - Index, \"\\\\%03o\", *Source) - 1;\r\n\r\n            break;\r\n\r\n        default:\r\n            Dest[Index] = *Source;\r\n            break;\r\n        }\r\n    }\r\n\r\n    Dest[Index] = '\\0';\r\n    return;\r\n}\r\n\r\nint MountFindMount(const char* MountsFile, const char* MountPoint, dev_t Device, struct libmnt_table** Table, struct libmnt_fs** FileSystem, int Direction)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine finds a mount in the specified file by either mount point\r\n    or device.\r\n\r\nArguments:\r\n\r\n    MountsFile - Supplies the file to use (e.g. /proc/self/mountinfo).\r\n\r\n    MountPoint - Supplies the mount point to search for. If NULL, device is\r\n        used instead.\r\n\r\n    Device - Supplies the device to search for if mount point is NULL.\r\n\r\n    Table - Supplies a pointer that receives the parsed table. The caller\r\n        must use mnt_free_table to free the table.\r\n\r\n    FileSystem - Supplies a pointer that receives the file system, or NULL\r\n        if none was found.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct libmnt_iter* Iterator;\r\n    struct libmnt_fs* LocalFileSystem;\r\n    struct libmnt_table* LocalTable;\r\n    int Result;\r\n\r\n    LocalFileSystem = NULL;\r\n    Iterator = NULL;\r\n    LocalTable = mnt_new_table_from_file(MountsFile);\r\n    LxtCheckNotEqual(LocalTable, NULL, \"%p\");\r\n\r\n    //\r\n    // A backwards iterator is used to find the most recent mount.\r\n    //\r\n\r\n    if (MountPoint != NULL)\r\n    {\r\n        *FileSystem = mnt_table_find_target(LocalTable, MountPoint, Direction);\r\n    }\r\n    else\r\n    {\r\n        *FileSystem = NULL;\r\n        Iterator = mnt_new_iter(MNT_ITER_FORWARD);\r\n        LxtCheckNotEqual(Iterator, NULL, \"%p\");\r\n        while (mnt_table_next_fs(LocalTable, Iterator, &LocalFileSystem) == 0)\r\n        {\r\n            if (mnt_fs_get_devno(LocalFileSystem) == Device)\r\n            {\r\n                *FileSystem = LocalFileSystem;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n\r\n    //\r\n    // Return the table so the memory for file system remains valid.\r\n    //\r\n\r\n    if (*FileSystem != NULL)\r\n    {\r\n        *Table = LocalTable;\r\n        LocalTable = NULL;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Iterator != NULL)\r\n    {\r\n        mnt_free_iter(Iterator);\r\n    }\r\n\r\n    if (LocalTable != NULL)\r\n    {\r\n        mnt_free_table(LocalTable);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MountFindMountStats(const char* Device, const char* MountPoint, const char* FsType)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine finds a mount with the specified options in the\r\n    /proc/self/mountstats file.\r\n\r\n    N.B. libmount does not support parsing the mountstats file so this is done\r\n         manually.\r\n\r\nArguments:\r\n\r\n    Device - Supplies the mount source.\r\n\r\n    MountPoint - Supplies the mount point.\r\n\r\n    FsType - Supplies the file system type.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char EscapedDevice[100];\r\n    char EscapedMountPoint[100];\r\n    char ExpectedLine[256];\r\n    int Result;\r\n\r\n    //\r\n    // Format the expected mountstats line.\r\n    //\r\n\r\n    MountEscapeString(MountPoint, EscapedMountPoint, LXT_COUNT_OF(EscapedMountPoint));\r\n    if (Device == NULL)\r\n    {\r\n        snprintf(ExpectedLine, sizeof(ExpectedLine), \"no device mounted on %s with fstype %s\", EscapedMountPoint, FsType);\r\n    }\r\n    else\r\n    {\r\n        MountEscapeString(Device, EscapedDevice, LXT_COUNT_OF(EscapedDevice));\r\n        snprintf(ExpectedLine, sizeof(ExpectedLine), \"device %s mounted on %s with fstype %s\", EscapedDevice, EscapedMountPoint, FsType);\r\n    }\r\n\r\n    LxtCheckResult(MountFindMountStatsLine(ExpectedLine));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint MountFindMountStatsLine(char* ExpectedLine)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if a specific line exists in the mountstats file.\r\n\r\nArguments:\r\n\r\n    ExpectedLine - Supplies the line to find.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool Found;\r\n    FILE* File;\r\n    char Line[256];\r\n    int Result;\r\n\r\n    File = fopen(MOUNT_PROC_MOUNTSTATS, \"r\");\r\n    LxtCheckNotEqual(File, NULL, \"%p\");\r\n    Found = FALSE;\r\n    while (feof(File) == 0)\r\n    {\r\n        if (fgets(Line, sizeof(Line), File) == NULL)\r\n        {\r\n            break;\r\n        }\r\n\r\n        //\r\n        // Strip the trailing \\n and compare.\r\n        //\r\n\r\n        Line[strlen(Line) - 1] = '\\0';\r\n        if (strncmp(ExpectedLine, Line, sizeof(Line)) == 0)\r\n        {\r\n            Found = TRUE;\r\n            break;\r\n        }\r\n    }\r\n\r\n    if (Found == false)\r\n    {\r\n        LxtLogError(\"'%s' not found in \" MOUNT_PROC_MOUNTSTATS, ExpectedLine);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (File != NULL)\r\n    {\r\n        fclose(File);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MountGetMountId(const char* Path)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine gets the mount ID for the specified path.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path. Does not have to be a mount point.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct libmnt_fs* FileSystem;\r\n    int Result;\r\n    struct stat Stat;\r\n    struct libmnt_table* Table;\r\n\r\n    //\r\n    // Find the mount ID of the  directory. This is done by device because\r\n    // it may not be a mount point.\r\n    //\r\n\r\n    FileSystem = NULL;\r\n    Table = NULL;\r\n    LxtCheckErrnoZeroSuccess(stat(Path, &Stat));\r\n    LxtCheckResult(MountFindMount(MOUNT_PROC_MOUNTINFO, NULL, Stat.st_dev, &Table, &FileSystem, MNT_ITER_BACKWARD));\r\n\r\n    LxtCheckNotEqual(FileSystem, NULL, \"%p\");\r\n    Result = mnt_fs_get_id(FileSystem);\r\n\r\nErrorExit:\r\n    if (Table != NULL)\r\n    {\r\n        mnt_free_table(Table);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MountGetFileSystem(const char* Path, char* FsType, int FsTypeLength, char* Options, int OptionsLength)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine gets the file system type of the mount containing the\r\n    specified path.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path.\r\n\r\n    FsType - Supplies a buffer that receives the name of the file system type.\r\n\r\n    Length - Supplies the size of the buffer.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct libmnt_fs* FileSystem;\r\n    const char* LocalFsType;\r\n    const char* LocalOptions;\r\n    int Result;\r\n    struct stat Stat;\r\n    struct libmnt_table* Table;\r\n\r\n    //\r\n    // Find the mount ID of the  directory. This is done by device because\r\n    // it may not be a mount point.\r\n    //\r\n\r\n    FileSystem = NULL;\r\n    Table = NULL;\r\n    LxtCheckErrnoZeroSuccess(stat(Path, &Stat));\r\n    LxtCheckResult(MountFindMount(MOUNT_PROC_MOUNTINFO, NULL, Stat.st_dev, &Table, &FileSystem, MNT_ITER_BACKWARD));\r\n\r\n    LxtCheckNotEqual(FileSystem, NULL, \"%p\");\r\n    LocalFsType = mnt_fs_get_fstype(FileSystem);\r\n    LocalOptions = mnt_fs_get_options(FileSystem);\r\n    LxtLogInfo(\"File system at %s uses fstype %s, options %s.\", Path, LocalFsType, LocalOptions);\r\n\r\n    if (strlen(LocalFsType) >= FsTypeLength)\r\n    {\r\n        LxtLogError(\"Buffer too small for file system name\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (strlen(LocalOptions) >= OptionsLength)\r\n    {\r\n        LxtLogError(\"Buffer too small for options.\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    strcpy(FsType, LocalFsType);\r\n    strcpy(Options, LocalOptions);\r\n\r\nErrorExit:\r\n    if (Table != NULL)\r\n    {\r\n        mnt_free_table(Table);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MountGetMountOptions(const char* Path, char* Options)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine gets mount options, which generally describe the mount peer\r\n    group information for the specified path.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path. Does not have to be a mount point.\r\n\r\n    Options - Supplies a string buffer to receive the options\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct libmnt_fs* FileSystem;\r\n    const char* LocalOptions;\r\n    int Result;\r\n    struct libmnt_table* Table;\r\n\r\n    //\r\n    // Find the mount ID of the directory.\r\n    //\r\n\r\n    FileSystem = NULL;\r\n    Table = NULL;\r\n    LxtCheckResult(MountFindMount(MOUNT_PROC_MOUNTINFO, Path, 0, &Table, &FileSystem, MNT_ITER_BACKWARD));\r\n\r\n    //\r\n    // shared:X, master:X, propagate_from:X, unbindable\r\n    //\r\n\r\n    LocalOptions = mnt_fs_get_optional_fields(FileSystem);\r\n    if (LocalOptions != NULL)\r\n    {\r\n        strcpy(Options, LocalOptions);\r\n    }\r\n    else\r\n    {\r\n        Options[0] = '\\0';\r\n    }\r\n\r\nErrorExit:\r\n    if (Table != NULL)\r\n    {\r\n        mnt_free_table(Table);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MountIsFileSystem(const char* Path, const char* FsType)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks the file system type of the mount containing the\r\n    specified path.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path.\r\n\r\n    FsType - Supplies the name of the file system type.\r\n\r\nReturn Value:\r\n\r\n    Returns 1 if the path uses the specified file system, 0 if not, and -1 on\r\n    failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    const char* ActualFsType;\r\n    struct libmnt_fs* FileSystem;\r\n    int Result;\r\n    struct stat Stat;\r\n    struct libmnt_table* Table;\r\n\r\n    //\r\n    // Find the mount ID of the  directory. This is done by device because\r\n    // it may not be a mount point.\r\n    //\r\n\r\n    FileSystem = NULL;\r\n    Table = NULL;\r\n    LxtCheckErrnoZeroSuccess(stat(Path, &Stat));\r\n    LxtCheckResult(MountFindMount(MOUNT_PROC_MOUNTINFO, NULL, Stat.st_dev, &Table, &FileSystem, MNT_ITER_BACKWARD));\r\n\r\n    LxtCheckNotEqual(FileSystem, NULL, \"%p\");\r\n    ActualFsType = mnt_fs_get_fstype(FileSystem);\r\n    LxtLogInfo(\"File system at %s uses fstype %s.\", Path, ActualFsType);\r\n    if (strcmp(ActualFsType, FsType) == 0)\r\n    {\r\n        Result = 1;\r\n    }\r\n    else\r\n    {\r\n        Result = 0;\r\n    }\r\n\r\nErrorExit:\r\n    if (Table != NULL)\r\n    {\r\n        mnt_free_table(Table);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MountIsMount(int DirFd, const char* Path)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if a path is a mount point.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the file descriptor to start resolving the path.\r\n\r\n    Path - Supplies the path.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 if the path is not a mount point, 1 if it is a mount point, or\r\n    -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char LocalPath[PATH_MAX];\r\n    char* ParentPath;\r\n    struct stat ParentStat;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    LxtCheckErrnoZeroSuccess(fstatat(DirFd, Path, &Stat, AT_SYMLINK_NOFOLLOW));\r\n    strncpy(LocalPath, Path, sizeof(LocalPath) - 1);\r\n    ParentPath = dirname(LocalPath);\r\n    LxtCheckErrnoZeroSuccess(fstatat(DirFd, ParentPath, &ParentStat, AT_SYMLINK_NOFOLLOW));\r\n\r\n    LxtLogInfo(\r\n        \"%s device: %u,%u; %s device: %u,%u\", ParentPath, major(ParentStat.st_dev), minor(ParentStat.st_dev), Path, major(Stat.st_dev), minor(Stat.st_dev));\r\n\r\n    if (Stat.st_dev == ParentStat.st_dev)\r\n    {\r\n        Result = 0;\r\n    }\r\n    else\r\n    {\r\n        Result = 1;\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint MountPrepareTmpfs(char* Path, char* Device, int ExpectedParentId)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine creates a tmpfs mount for testing.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the mount point path.\r\n\r\n    Device - Supplies the device name to use.\r\n\r\n    ExpectedParentId - Supplies the expected parent mount ID.\r\n\r\nReturn Value:\r\n\r\n    Returns the mount ID on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return MountPrepareTmpfsEx(Path, Device, ExpectedParentId, 0, \"rw,relatime\");\r\n}\r\n\r\nint MountPrepareTmpfsEx(char* Path, char* Device, int ExpectedParentId, int Flags, const char* ExpectedOptions)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine creates a tmpfs mount for testing.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the mount point path.\r\n\r\n    Device - Supplies the device name to use.\r\n\r\n    ExpectedParentId - Supplies the expected parent mount ID.\r\n\r\n    Flags - Supplies the mount flags.\r\n\r\n    ExpectedOptions - Supplies the expected mount option string.\r\n\r\nReturn Value:\r\n\r\n    Returns the mount ID on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int MountId;\r\n    int Result;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(Path, 0700));\r\n    LxtCheckErrnoZeroSuccess(mount(Device, Path, \"tmpfs\", Flags, NULL));\r\n    LxtCheckResult(MountId = MountCheckIsMount(Path, ExpectedParentId, Device, \"tmpfs\", \"/\", ExpectedOptions, \"rw\", ExpectedOptions, 0));\r\n\r\n    Result = MountId;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/lxtmount.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtmount.h\r\n\r\nAbstract:\r\n\r\n    This file contains mount primitive support.\r\n\r\n--*/\r\n\r\n#define MOUNT_PROC_MOUNTS \"/proc/mounts\"\r\n#define MOUNT_PROC_MOUNTINFO \"/proc/self/mountinfo\"\r\n#define MOUNT_PROC_MOUNTSTATS \"/proc/self/mountstats\"\r\n#define MOUNT_SOURCE_DELETED (0x1)\r\n#define MOUNT_FIRST_MOUNT (0x2)\r\n\r\n//\r\n// Flags for the unshare helper.\r\n//\r\n\r\n#define MOUNT_NAMESPACE_USE_CLONE (0x1)\r\n\r\nint MountCheckIsMount(\r\n    const char* Path,\r\n    int ExpectedParentId,\r\n    const char* ExpectedSource,\r\n    const char* ExpectedFsType,\r\n    const char* ExpectedRoot,\r\n    const char* ExpectedMountOptions,\r\n    const char* ExpectedFsOptions,\r\n    const char* ExpectedCombinedOptions,\r\n    int Flags);\r\n\r\nint MountCheckIsNotMount(const char* Path);\r\n\r\nint MountFindMount(const char* MountsFile, const char* MountPoint, dev_t Device, struct libmnt_table** Table, struct libmnt_fs** FileSystem, int Direction);\r\n\r\nint MountFindMountStats(const char* Device, const char* MountPoint, const char* FsType);\r\n\r\nint MountFindMountStatsLine(char* ExpectedLine);\r\n\r\nint MountGetFileSystem(const char* Path, char* FsType, int FsTypeLength, char* Options, int OptionsLength);\r\n\r\nint MountGetMountId(const char* Path);\r\n\r\nint MountGetMountOptions(const char* Path, char* Options);\r\n\r\nint MountIsFileSystem(const char* Path, const char* FsType);\r\n\r\nint MountIsMount(int DirFd, const char* Path);\r\n\r\nint MountPrepareTmpfs(char* Path, char* Device, int ExpectedParentId);\r\n\r\nint MountPrepareTmpfsEx(char* Path, char* Device, int ExpectedParentId, int Flags, const char* ExpectedOptions);\r\n"
  },
  {
    "path": "test/linux/unit_tests/lxtutil.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtlog.c\r\n\r\nAbstract:\r\n\r\n    This file contains lx test logging routines.\r\n\r\n--*/\r\n\r\n#include \"lxtutil.h\"\r\n#include \"lxtlog.h\"\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <unistd.h>\r\n#include <errno.h>\r\n#include <sched.h>\r\n#include <linux/futex.h>\r\n#include <sys/types.h>\r\n#include <sys/wait.h>\r\n#include <sys/socket.h>\r\n#include <sys/syscall.h>\r\n#include <sys/stat.h>\r\n#include <sys/mman.h>\r\n#include <sys/utsname.h>\r\n#include <fcntl.h>\r\n#include <dirent.h>\r\n#include <signal.h>\r\n\r\n#define SIGNAL_WAIT_COUNT (20)\r\n#define SIGNAL_WAIT_TIMEOUT_US (100000)\r\n#define SIGNAL_MAX_SIGNALS (10)\r\n#define SIGNAL_MAX_THREADS (5)\r\n\r\ntypedef struct _LXT_SIGNAL_INFO\r\n{\r\n    pid_t ThreadId;\r\n    int ReceivedSignal[SIGNAL_MAX_SIGNALS];\r\n    siginfo_t SignalInfo[SIGNAL_MAX_SIGNALS];\r\n    BOOLEAN AllowMultipleSignals;\r\n    int SignalCount;\r\n} LXT_SIGNAL_INFO, *PLXT_SIGNAL_INFO;\r\n\r\ntypedef struct _LXT_TYPE_MAPPING\r\n{\r\n    char Type;\r\n    mode_t Mode;\r\n} LXT_TYPE_MAPPING, *PLXT_TYPE_MAPPING;\r\n\r\nvoid LxtPrintPartialMemory(const unsigned char* Buffer, size_t Size, size_t BufferIndex, const char* Prefix);\r\n\r\nvoid LxtShowUsage(PCLXT_VARIATION Variations, unsigned int VariationCount);\r\n\r\nPLXT_SIGNAL_INFO\r\nLxtSignalFindThreadInfo(void);\r\n\r\nvoid LxtSignalHandler(int Signal);\r\n\r\nvoid LxtSignalHandlerSigAction(int Signal, siginfo_t* SigInfo, void* UContext);\r\n\r\n//\r\n// The multi-threaded signal tests require that information about the last\r\n// signal received is stored per-thread, however using thread local storage\r\n// is not safe in a signal handler (TLS support may take locks, if a signal\r\n// arrives while the lock is held and the signal handler then tries to take\r\n// the same lock, it leads to deadlock). Instead, an array is used that\r\n// stores information for each thread.\r\n//\r\n\r\nstatic LXT_SIGNAL_INFO g_ThreadSignalInfo[SIGNAL_MAX_THREADS];\r\nstatic int g_NextSignalThread = 0;\r\nstatic LXT_TYPE_MAPPING g_TypeMapping[] = {\r\n    {DT_REG, S_IFREG}, {DT_DIR, S_IFDIR}, {DT_LNK, S_IFLNK}, {DT_FIFO, S_IFIFO}, {DT_SOCK, S_IFSOCK}, {DT_CHR, S_IFCHR}, {DT_BLK, S_IFBLK}};\r\n\r\nstatic int g_WslVersion = 0;\r\n\r\n//\r\n// Test framework code\r\n//\r\n\r\nint LxtCheckDirectoryContents(const char* Path, const LXT_CHILD_INFO* Children, size_t Count)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests if the specified children are present in the directory.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path of the directory.\r\n\r\n    Children - Supplies the list of expected children.\r\n\r\n    Count - Supplies the number of children.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return LxtCheckDirectoryContentsEx(Path, Children, Count, LXT_CHECK_DIRECTORY_CONTENTS_READ_FILES);\r\n}\r\n\r\nint LxtCheckDirectoryContentsEx(const char* Path, const LXT_CHILD_INFO* Children, size_t Count, int Flags)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests if the specified children are present in the directory.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path of the directory.\r\n\r\n    Children - Supplies the list of expected children.\r\n\r\n    Count - Supplies the number of children.\r\n\r\n    Flags - Supplies the flags.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    DIR* Directory;\r\n    struct dirent* Entry;\r\n    BOOLEAN* FoundEntries;\r\n    char FullPath[1024];\r\n    size_t Index;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    FoundEntries = malloc(Count * sizeof(BOOLEAN));\r\n    memset(FoundEntries, 0, Count * sizeof(BOOLEAN));\r\n    Directory = opendir(Path);\r\n    if (Directory == NULL)\r\n    {\r\n        LxtLogError(\"opendir failed, errno: %d (%s)\", errno, strerror(errno));\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    errno = 0;\r\n    while ((Entry = readdir(Directory)) != NULL)\r\n    {\r\n        LxtLogInfo(\r\n            \"Entry %p - d_name: %s d_ino: %llu d_type: %d d_off: %d d_reclen: %d\",\r\n            Entry,\r\n            Entry->d_name,\r\n            Entry->d_ino,\r\n            Entry->d_type,\r\n            Entry->d_off,\r\n            Entry->d_reclen);\r\n\r\n        for (Index = 0; Index < Count; Index += 1)\r\n        {\r\n            if (strcmp(Children[Index].Name, Entry->d_name) == 0)\r\n            {\r\n                if (FoundEntries[Index] != FALSE)\r\n                {\r\n                    LxtLogError(\"Duplicate entry '%s'\", Entry->d_name);\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                LxtCheckEqual(FoundEntries[Index], FALSE, \"%d\");\r\n                LxtCheckGreater(Entry->d_ino, 0, \"%llu\");\r\n                LxtCheckEqual(Entry->d_type, Children[Index].FileType, \"%d\");\r\n                FoundEntries[Index] = TRUE;\r\n                strcpy(FullPath, Path);\r\n                strcat(FullPath, \"/\");\r\n                strcat(FullPath, Entry->d_name);\r\n                LxtCheckResult(LxtCheckStat(FullPath, Entry->d_ino, Children[Index].FileType));\r\n\r\n                if ((Flags & LXT_CHECK_DIRECTORY_CONTENTS_READ_FILES) != 0)\r\n                {\r\n                    LxtCheckResult(LxtCheckRead(FullPath, Children[Index].FileType));\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    if (errno != 0)\r\n    {\r\n        LxtLogError(\"readdir failed; errno: %d (%s)\", errno, strerror(errno));\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Check if all the required entries have been found.\r\n    //\r\n\r\n    for (Index = 0; Index < Count; Index += 1)\r\n    {\r\n        if (FoundEntries[Index] == FALSE)\r\n        {\r\n            LxtLogError(\"Entry '%s' is missing\", Children[Index].Name);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Directory != NULL)\r\n    {\r\n        closedir(Directory);\r\n    }\r\n\r\n    if (FoundEntries != NULL)\r\n    {\r\n        free(FoundEntries);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtCheckFdPath(int Fd, char* ExpectedPath)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks if the file descriptor has the specified path.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the file descriptor.\r\n\r\n    ExpectedPath - Supplies the expected path.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char ProcFsPath[PATH_MAX];\r\n    char Path[PATH_MAX];\r\n    int Result;\r\n\r\n    sprintf(ProcFsPath, \"/proc/self/fd/%d\", Fd);\r\n    LxtCheckResult(LxtCheckLinkTarget(ProcFsPath, ExpectedPath));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtCheckLinkTarget(const char* Path, const char* ExpectedTarget)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the target of the specified link.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path of the link.\r\n\r\n    ExpectedTarget - Supplies the expected target of the link.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[256] = {0};\r\n    int Result;\r\n    ssize_t Size;\r\n\r\n    LxtCheckErrno(Size = readlink(Path, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual((size_t)Size, strlen(Buffer), \"%d\");\r\n    LxtCheckStringEqual(ExpectedTarget, Buffer);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtCheckRead(const char* FullPath, unsigned char FileType)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks that the specified file can be read.\r\n\r\n    N.B. This only checks that the file can be opened and read, it doesn't\r\n         check if the contents match what's expected. Write additional tests\r\n         for a specific file if necessary.\r\n\r\nArguments:\r\n\r\n    FullPath - Supplies the full path of the file or directory.\r\n\r\n    FileType - Supplies the file type.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[1024];\r\n    int Fd;\r\n    int Result;\r\n    ssize_t Size;\r\n    struct stat Stat;\r\n\r\n    Fd = 0;\r\n    switch (FileType)\r\n    {\r\n    case DT_REG:\r\n\r\n        //\r\n        // Skip files that aren't readable.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(lstat(FullPath, &Stat));\r\n        if ((Stat.st_mode & S_IRUSR) == 0)\r\n        {\r\n            Result = LXT_RESULT_SUCCESS;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(FullPath, O_RDONLY));\r\n        LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer)));\r\n        LxtCheckGreater(Size, 0, \"%d\");\r\n        break;\r\n\r\n    case DT_LNK:\r\n        LxtCheckErrno(Size = readlink(FullPath, Buffer, sizeof(Buffer)));\r\n        LxtCheckGreater(Size, 0, \"%d\");\r\n        break;\r\n\r\n    case DT_DIR:\r\n\r\n        //\r\n        // Nothing to check.\r\n        //\r\n\r\n        Result = LXT_RESULT_SUCCESS;\r\n        break;\r\n\r\n    default:\r\n        LxtLogError(\"Unexpected file type %d\", FileType);\r\n        Result = LXT_RESULT_FAILURE;\r\n        break;\r\n    }\r\n\r\nErrorExit:\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"Error reading %s\", FullPath);\r\n    }\r\n\r\n    if (Fd > 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtCheckStat(const char* FullPath, unsigned long long ExpectedInode, unsigned char FileType)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks the stat information for a file or directory.\r\n\r\nArguments:\r\n\r\n    FullPath - Supplies the full path of the file or directory.\r\n\r\n    ExpectedInode - Supplies the expected inode number.\r\n\r\n    FileType - Supplies the file type.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    LxtCheckErrnoZeroSuccess(lstat(FullPath, &Stat));\r\n    LxtCheckEqual(Stat.st_ino, ExpectedInode, \"%llu\");\r\n    LxtCheckGreater(Stat.st_nlink, 0, \"%ud\");\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_TypeMapping); Index += 1)\r\n    {\r\n        if (g_TypeMapping[Index].Type == FileType)\r\n        {\r\n            LxtCheckEqual((Stat.st_mode & S_IFMT), g_TypeMapping[Index].Mode, \"0%o\");\r\n\r\n            break;\r\n        }\r\n    }\r\n\r\n    if (Index == LXT_COUNT_OF(g_TypeMapping))\r\n    {\r\n        LxtLogError(\"Unexpected file type %d\", FileType);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtCheckWrite(const char* FullPath, const char* Value)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks that the specified file can be written to.\r\n\r\n    N.B. This function is meant for writable files in /proc and /sys. It's\r\n         primarily used for files that currently don't have a real write\r\n         implementation (which allow but silently ignore the write) since the\r\n         effects of the write are not checked.\r\n\r\nArguments:\r\n\r\n    FullPath - Supplies the full path of the file or directory.\r\n\r\n    FileType - Supplies the file type.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesWritten;\r\n    int Fd;\r\n    int Result;\r\n\r\n    LxtCheckErrno(Fd = open(FullPath, O_WRONLY));\r\n    LxtCheckErrno(BytesWritten = write(Fd, Value, strlen(Value)));\r\n    LxtCheckEqual((size_t)BytesWritten, strlen(Value), \"%d\");\r\n\r\nErrorExit:\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"Error writing %s\", FullPath);\r\n    }\r\n\r\n    if (Fd > 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtCheckWslPathTranslation(char* Path, const char* ExpectedPath, bool WinPath)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks whether translating a path with wslpath matches the\r\n    specified result.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path to translate.\r\n\r\n    ExpectedPath - Supplies the expected translated path.\r\n\r\n    WinPath - Supplies a value that indicates whether the specified path is a\r\n        Windows path. When true, the expected path must be a Linux path and\r\n        vice versa.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    char TranslatedPath[4096];\r\n\r\n    LxtCheckResult(LxtExecuteWslPath(Path, WinPath, TranslatedPath, sizeof(TranslatedPath)));\r\n    LxtCheckStringEqual(ExpectedPath, TranslatedPath);\r\n    LxtLogInfo(\"%s => %s\", Path, TranslatedPath);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtExecuteAndReadOutput(char** Argv, char* OutputBuffer, size_t OutputBufferSize)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine runs an executable, and reads stdout into the specified buffer.\r\n\r\n    N.B. If the process produces more output than fits in the buffer, this\r\n         function will fail.\r\n\r\nArguments:\r\n\r\n    Argv - Supplies the arguments to pass to the executable. The first element\r\n        is the executable to run.\r\n\r\n    OutputBuffer - Supplies the buffer to hold the process's stdout.\r\n\r\n    OutputBufferSize - Supplies the size of the output buffer.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesRead;\r\n    pid_t ChildPid;\r\n    int Result;\r\n    LXT_PIPE Pipe = {-1, -1};\r\n\r\n    LxtCheckResult(LxtCreatePipe(&Pipe));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckClose(Pipe.Read);\r\n        LxtCheckErrno(dup2(Pipe.Write, STDOUT_FILENO));\r\n        LxtCheckClose(Pipe.Write);\r\n        LxtCheckErrno(execve(Argv[0], Argv, environ));\r\n        _exit(LXT_RESULT_FAILURE);\r\n    }\r\n\r\n    LxtCheckClose(Pipe.Write);\r\n    while ((BytesRead = read(Pipe.Read, OutputBuffer, OutputBufferSize)) > 0)\r\n    {\r\n        OutputBuffer += BytesRead;\r\n        OutputBufferSize -= BytesRead;\r\n        LxtCheckGreater(OutputBufferSize, 0, \"%lu\");\r\n    }\r\n\r\n    //\r\n    // Make sure the result did not exceed the buffer size and NULL-terminate\r\n    // it.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(BytesRead);\r\n    LxtCheckGreater(OutputBufferSize, BytesRead, \"%lu\");\r\n    OutputBuffer[BytesRead] = '\\0';\r\n\r\n    //\r\n    // Make sure the executable exited successfully.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    LxtClosePipe(&Pipe);\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtExecuteWslPath(char* Path, bool WinPath, char* OutputBuffer, size_t OutputBufferSize)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine runs wslpath, and reads stdout into the specified buffer.\r\n\r\n    N.B. If the process produces more output than fits in the buffer, this\r\n         function will fail.\r\n\r\nArguments:\r\n\r\n    Path - Supplies the path to translate.\r\n\r\n    WinPath - Supplies a value that indicates whether the specified path is a\r\n        Windows path. When true, the output will be a Linux path and vice versa.\r\n\r\n    OutputBuffer - Supplies the buffer to hold the process's stdout.\r\n\r\n    OutputBufferSize - Supplies the size of the output buffer.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char* Argv[4];\r\n    int Index = 0;\r\n    int Result;\r\n    size_t OutputLength;\r\n\r\n    //\r\n    // Construct the arguments to invoke wslpath.\r\n    //\r\n\r\n    Argv[Index++] = \"/bin/wslpath\";\r\n    if (WinPath == false)\r\n    {\r\n        Argv[Index++] = \"-w\";\r\n    }\r\n\r\n    Argv[Index++] = Path;\r\n    Argv[Index] = NULL;\r\n\r\n    //\r\n    // Execute wslpath.\r\n    //\r\n\r\n    LxtCheckResult(LxtExecuteAndReadOutput(Argv, OutputBuffer, OutputBufferSize));\r\n\r\n    //\r\n    // Wslpath outputs a new line at the end. Strip it to make things easier on\r\n    // the caller.\r\n    //\r\n\r\n    OutputLength = strlen(OutputBuffer);\r\n    if ((OutputLength > 0) && (OutputBuffer[OutputLength - 1] == '\\n'))\r\n    {\r\n        OutputBuffer[OutputLength - 1] = '\\0';\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtInitialize(int Argc, char* Argv[], PLXT_ARGS Args, const char* TestName)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Opt;\r\n    int OriginalOptErr;\r\n    int Result;\r\n\r\n    //\r\n    // Set umask to 0 so files created by tests have the expected permissions.\r\n    //\r\n\r\n    Result = umask(0);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"umask failed %d\", errno);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Parse the command line, ignore unrecognized options since variations can\r\n    // specify their own options, and initialize logging.\r\n    //\r\n\r\n    Args->LogType = LXT_LOG_TYPE_DEFAULT_MASK;\r\n    Args->LogAppend = false;\r\n    Args->HelpRequested = false;\r\n    Args->VariationMask = -1;\r\n    Args->Argc = Argc;\r\n    Args->Argv = Argv;\r\n    OriginalOptErr = opterr;\r\n    opterr = 0;\r\n    while ((Opt = getopt(Argc, Argv, \"l:v:a:h\")) != LXT_RESULT_FAILURE)\r\n    {\r\n        switch (Opt)\r\n        {\r\n        case 'a':\r\n            Args->LogAppend = true;\r\n            break;\r\n\r\n        case 'l':\r\n            Args->LogType = atoi(optarg);\r\n            if (Args->LogType >= LxtLogTypeMax)\r\n            {\r\n                Result = LXT_RESULT_FAILURE;\r\n                LxtLogError(\"Invalid LxtLogType %d\", Args->LogType);\r\n                goto ErrorExit;\r\n            }\r\n\r\n            break;\r\n\r\n        case 'v':\r\n            Args->VariationMask = atoll(optarg);\r\n            break;\r\n\r\n        case 'h':\r\n            Args->HelpRequested = true;\r\n            break;\r\n        }\r\n    }\r\n\r\n    opterr = OriginalOptErr;\r\n    Result = LxtLogInitialize(TestName, Args->LogType, Args->LogAppend);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtRunVariations(PLXT_ARGS Args, PCLXT_VARIATION Variations, unsigned int VariationCount)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    unsigned int Itr;\r\n    unsigned long long ThisVariation;\r\n    int Result;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    if (Args->HelpRequested != false)\r\n    {\r\n        LxtShowUsage(Variations, VariationCount);\r\n        LxtLogError(\"No tests executed.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (Itr = 0; Itr < VariationCount; Itr++)\r\n    {\r\n        ThisVariation = (1ull << Itr);\r\n\r\n        //\r\n        // TODO: Currently, variation mask is only supported for the first 64\r\n        //       variations.\r\n        //\r\n\r\n        if ((Args->VariationMask != 0) && ((ThisVariation & Args->VariationMask) == 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtLogStart(\"%s\", Variations[Itr].Name);\r\n        Result = Variations[Itr].Variation(Args);\r\n        if (LXT_SUCCESS(Result) == 0)\r\n        {\r\n            LxtLogError(\"%s\", Variations[Itr].Name);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtLogPassed(\"%s\", Variations[Itr].Name);\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtRunVariationsForked(PLXT_ARGS Args, PCLXT_VARIATION Variations, unsigned int VariationCount)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine runs test variations, with each variation executing in its\r\n    own child process. Use this function if a test may change process state\r\n    that interferes with other tests.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\n    Variations - Supplies a pointer to an array of variations.\r\n\r\n    VariationCount - Supplies the number items in the variations array.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    unsigned int Itr;\r\n    unsigned long long ThisVariation;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n    Result = LXT_RESULT_FAILURE;\r\n    if (Args->HelpRequested != false)\r\n    {\r\n        LxtShowUsage(Variations, VariationCount);\r\n        LxtLogError(\"No tests executed.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (Itr = 0; Itr < VariationCount; Itr++)\r\n    {\r\n        ThisVariation = (1ull << Itr);\r\n\r\n        //\r\n        // TODO: Currently, variation mask is only supported for the first 64\r\n        //       variations.\r\n        //\r\n\r\n        if ((Args->VariationMask != 0) && ((ThisVariation & Args->VariationMask) == 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckResult(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            LxtLogStart(\"%s\", Variations[Itr].Name);\r\n            Result = Variations[Itr].Variation(Args);\r\n            if (LXT_SUCCESS(Result) == 0)\r\n            {\r\n                LxtLogError(\"%s\", Variations[Itr].Name);\r\n                goto ErrorExit;\r\n            }\r\n\r\n            LxtLogPassed(\"%s\", Variations[Itr].Name);\r\n            _exit(0);\r\n        }\r\n\r\n        Result = LxtWaitPidPollOptions(ChildPid, 0, 0, 120);\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"Test execution timed out.\");\r\n            kill(ChildPid, SIGKILL);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid LxtUninitialize(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LxtLogUninitialize();\r\n    return;\r\n}\r\n\r\n//\r\n// stdlib wrappers\r\n//\r\n\r\nvoid* LxtAlloc(size_t Size)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    void* Allocation;\r\n\r\n    Allocation = malloc(Size);\r\n    if (Allocation == NULL)\r\n    {\r\n        LxtLogResourceError(\"malloc failed for size %d\", Size);\r\n    }\r\n\r\n    return Allocation;\r\n}\r\n\r\nvoid LxtFree(void* Allocation)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    free(Allocation);\r\n    return;\r\n}\r\n\r\n//\r\n// syscall wrappers\r\n//\r\n\r\n#define LXT_WAITPID_WAIT_TIMEOUT_US 100000\r\n#define LXT_MESSAGE_WAIT_TIMEOUT_US 100000\r\n#define LXT_MESSAGE_WAIT_COUNT 20\r\n\r\nint LxtClone(int (*Entry)(void* Parameter), void* Parameter, int Flags, PLXT_CLONE_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char* ChildStack;\r\n    int Result;\r\n\r\n    Args->Stack = LxtAlloc(LXT_CLONE_STACK_SIZE);\r\n    if (Args->Stack == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(Args->Stack, 0, LXT_CLONE_STACK_SIZE);\r\n    ChildStack = Args->Stack + LXT_CLONE_STACK_SIZE;\r\n    LxtCheckErrno(Args->CloneId = clone(Entry, ChildStack, Flags, Parameter, 0, 0, 0));\r\n\r\nErrorExit:\r\n    if (LXT_SUCCESS(Result) == 0)\r\n    {\r\n        LxtFree(Args->Stack);\r\n        Args->Stack = NULL;\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtClosePipe(PLXT_PIPE Pipe)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    if (Pipe->Read != -1)\r\n    {\r\n        LxtCheckErrno(close(Pipe->Read));\r\n        Pipe->Read = -1;\r\n    }\r\n\r\n    if (Pipe->Write != -1)\r\n    {\r\n        LxtCheckErrno(close(Pipe->Write));\r\n        Pipe->Write = -1;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtCompareMemory(const void* First, const void* Second, size_t Size, const char* FirstDescription, const char* SecondDescription)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine compares two memory locations, and if they are different logs\r\n    information about where they are different.\r\n\r\nArguments:\r\n\r\n    First - Supplies the address of the first memory location.\r\n\r\n    Second - Supplies the address of the second memory location.\r\n\r\n    Size - Supplies the size of the memory locations.\r\n\r\n    FirstDescription - Supplies the description of the first memory location.\r\n\r\n    SecondDescription - Supplies the description of the second memory location.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    size_t End;\r\n    const unsigned char* FirstBytes;\r\n    size_t DifferentIndex;\r\n    size_t Index;\r\n    int Result;\r\n    const unsigned char* SecondBytes;\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n    FirstBytes = First;\r\n    SecondBytes = Second;\r\n    for (Index = 0; Index < Size; Index += 1)\r\n    {\r\n        if (FirstBytes[Index] != SecondBytes[Index])\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            break;\r\n        }\r\n    }\r\n\r\n    if (Result != LXT_RESULT_SUCCESS)\r\n    {\r\n        LxtLogError(\r\n            \"Memory contents of '%s' [1] differ from '%s' [2] at \"\r\n            \"offset %ld\",\r\n            FirstDescription,\r\n            SecondDescription,\r\n            Index);\r\n\r\n        LxtPrintPartialMemory(FirstBytes, Size, Index, \"[1]:\");\r\n        LxtPrintPartialMemory(SecondBytes, Size, Index, \"[2]:\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtCopyFile(const char* Source, const char* Destination)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine copies a file.\r\n\r\nArguments:\r\n\r\n    Source - Supplies the source.\r\n\r\n    Destination - Supplies the destination.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[4096];\r\n    ssize_t BytesRead;\r\n    int FdDest;\r\n    int FdSource;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    FdDest = -1;\r\n    FdSource = -1;\r\n    LxtCheckErrno(FdSource = open(Source, O_RDONLY));\r\n    LxtCheckErrnoZeroSuccess(fstat(FdSource, &Stat));\r\n    LxtCheckErrno(FdDest = creat(Destination, Stat.st_mode & ~S_IFMT));\r\n    do\r\n    {\r\n        LxtCheckErrno(BytesRead = read(FdSource, Buffer, sizeof(Buffer)));\r\n        if (BytesRead > 0)\r\n        {\r\n            LxtCheckErrno(write(FdDest, Buffer, BytesRead));\r\n        }\r\n    } while (BytesRead > 0);\r\n\r\nErrorExit:\r\n    if (FdDest >= 0)\r\n    {\r\n        close(FdDest);\r\n    }\r\n\r\n    if (FdSource >= 0)\r\n    {\r\n        close(FdSource);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint LxtCreatePipe(PLXT_PIPE Pipe)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    memset(Pipe, -1, sizeof(*Pipe));\r\n    LxtCheckErrno(pipe((int*)Pipe));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtJoinThread(pid_t* Tid)\r\n\r\n{\r\n\r\n    pid_t CurrentTid;\r\n\r\n    while ((CurrentTid = *(volatile pid_t*)Tid) != 0)\r\n    {\r\n        if (syscall(SYS_futex, Tid, FUTEX_WAIT, CurrentTid, NULL, NULL, 0) < 0 && errno != EAGAIN)\r\n        {\r\n            return -1;\r\n        }\r\n    }\r\n\r\n    return 0;\r\n}\r\n\r\nint LxtReceiveMessage(int Socket, const char* ExpectedMessage)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine receives a message from a socket and checks if it was the\r\n    expected message\r\n\r\nArguments:\r\n\r\n    Socket - Supplies a file descriptor for the socket to send on.\r\n\r\n    ExpectedMessage - Supplies a pointer to a zero-terminated string containing\r\n        the expected message.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ExpectedMessageSize;\r\n    char Message[100];\r\n    int MessageSize;\r\n    int Result;\r\n    int WaitCount;\r\n\r\n    ExpectedMessageSize = strlen(ExpectedMessage);\r\n    memset(Message, 0, sizeof(Message));\r\n    for (WaitCount = 0; WaitCount < LXT_MESSAGE_WAIT_COUNT; WaitCount += 1)\r\n    {\r\n        MessageSize = recv(Socket, Message, sizeof(Message), MSG_DONTWAIT);\r\n        if (MessageSize >= 0 || (errno != EAGAIN && errno != EWOULDBLOCK))\r\n        {\r\n            break;\r\n        }\r\n\r\n        usleep(LXT_MESSAGE_WAIT_TIMEOUT_US);\r\n    }\r\n\r\n    if (WaitCount == LXT_MESSAGE_WAIT_COUNT)\r\n    {\r\n        LxtLogError(\"Receiving the message timed out.\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(MessageSize);\r\n    if (MessageSize != ExpectedMessageSize)\r\n    {\r\n        LxtLogError(\"Received %i bytes, expected %i\", MessageSize, ExpectedMessageSize);\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (strncmp(Message, ExpectedMessage, ExpectedMessageSize) != 0)\r\n    {\r\n        LxtLogError(\"Received '%s', expected '%s'\", Message, ExpectedMessage);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid LxtPrintPartialMemory(const unsigned char* Buffer, size_t Size, size_t BufferIndex, const char* Prefix)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine prints the contents of a memory buffer at the specified index\r\n    with some context.\r\n\r\nArguments:\r\n\r\n    Buffer - Supplies the memory buffer to print.\r\n\r\n    Size - Supplies the size of the buffer.\r\n\r\n    BufferIndex - Supplies the index at which to print.\r\n\r\n    Prefix - Supplies the prefix for the message.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    size_t End;\r\n    size_t Index;\r\n    char Message[256];\r\n    char Temp[10];\r\n\r\n    memset(Message, 0, sizeof(Message));\r\n    Index = BufferIndex - 5;\r\n    if (Index > BufferIndex)\r\n    {\r\n        Index = 0;\r\n    }\r\n\r\n    End = Index + 11;\r\n    if (End > Size)\r\n    {\r\n        End = Size;\r\n    }\r\n\r\n    if (Prefix != NULL)\r\n    {\r\n        strcat(Message, Prefix);\r\n        strcat(Message, \" \");\r\n    }\r\n\r\n    if (Index > 0)\r\n    {\r\n        strcat(Message, \"...\");\r\n    }\r\n\r\n    for (; Index < End; Index += 1)\r\n    {\r\n        strcat(Message, \" \");\r\n        if (Index == BufferIndex)\r\n        {\r\n            strcat(Message, \"(\");\r\n        }\r\n\r\n        sprintf(Temp, \"%02x\", Buffer[Index]);\r\n        strcat(Message, Temp);\r\n        if (Index == BufferIndex)\r\n        {\r\n            strcat(Message, \")\");\r\n        }\r\n    }\r\n\r\n    if (End < Size)\r\n    {\r\n        strcat(Message, \" ...\");\r\n    }\r\n\r\n    LxtLogInfo(\"%s\", Message);\r\n    return;\r\n}\r\n\r\nint LxtSendMessage(int Socket, const char* Message)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a message to a socket and checks if it was successfully\r\n    sent.\r\n\r\nArguments:\r\n\r\n    Socket - Supplies a file descriptor for the socket to send on.\r\n\r\n    Message - Supplies a pointer to a zero-terminated buffer containing the\r\n        message.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int MessageSize;\r\n    int Result;\r\n    int SentSize;\r\n\r\n    MessageSize = strlen(Message);\r\n    LxtCheckErrno(SentSize = send(Socket, Message, MessageSize, 0));\r\n    if (SentSize != MessageSize)\r\n    {\r\n        LxtLogError(\"Sent %i bytes, expected %i\", SentSize, MessageSize);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid LxtShowUsage(PCLXT_VARIATION Variations, unsigned int VariationCount)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine shows usage for the variations.\r\n\r\nArguments:\r\n\r\n    Variations - Supplies the variations.\r\n\r\n    VariationCount - Supplies the number of variations.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    size_t Index;\r\n\r\n    LxtLogInfo(\"Usage: ./test_name [-v <variation_mask>] [-l <log_type>] [-a] [-?]\");\r\n    LxtLogInfo(\"Variations:\");\r\n    for (Index = 0; Index < VariationCount; Index += 1)\r\n    {\r\n        LxtLogInfo(\"%s: %llu\", Variations[Index].Name, 1ull << Index);\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint LxtSignalBlock(int Signal)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine blocks the specified signal.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the signal number.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    sigset_t Signals;\r\n\r\n    sigemptyset(&Signals);\r\n    sigaddset(&Signals, Signal);\r\n    LxtCheckErrnoZeroSuccess(sigprocmask(SIG_BLOCK, &Signals, NULL));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSignalDefault(int Signal)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine reverts to the default action for the specified signal.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the signal number.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct sigaction Action;\r\n    int Result;\r\n\r\n    memset(&Action, 0, sizeof(Action));\r\n    Action.sa_handler = SIG_DFL;\r\n    LxtCheckErrnoZeroSuccess(sigaction(Signal, &Action, NULL));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSignalIgnore(int Signal)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine ignores the specified signal.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the signal number.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct sigaction Action;\r\n    int Result;\r\n\r\n    memset(&Action, 0, sizeof(Action));\r\n    Action.sa_handler = SIG_IGN;\r\n    LxtCheckErrnoZeroSuccess(sigaction(Signal, &Action, NULL));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSignalCheckInfoReceived(int Signal, int Code, pid_t Pid, uid_t Uid)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks if the specified signal was received by the signal\r\n    handlers, with the specified info values.\r\n\r\n    N.B. The signal handler must have been established with SA_SIGINFO for this\r\n         to work.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the expected signal number.\r\n\r\n    Code - Supplies the expected signal code.\r\n\r\n    Pid - Supplies the expected process ID.\r\n\r\n    Uid - Supplies the expected user ID.\r\n\r\nReturn Value:\r\n\r\n    Returns the index in the received signals array on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n    PLXT_SIGNAL_INFO Info;\r\n    int Result;\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(Index = LxtSignalCheckReceived(Signal));\r\n    LxtCheckEqual(Signal, Info->SignalInfo[Index].si_signo, \"%d\");\r\n    LxtCheckEqual(Code, Info->SignalInfo[Index].si_code, \"%d\");\r\n    LxtCheckEqual(Pid, Info->SignalInfo[Index].si_pid, \"%d\");\r\n    LxtCheckEqual(Uid, Info->SignalInfo[Index].si_uid, \"%d\");\r\n    Result = Index;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSignalCheckNoSignal(void)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks if no signal was received.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    PLXT_SIGNAL_INFO Info;\r\n    int Result;\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (Info->SignalCount == 0)\r\n    {\r\n        Result = LXT_RESULT_SUCCESS;\r\n    }\r\n    else\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Unexpected signal.\");\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSignalCheckReceived(int Signal)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks if the specified signal was received by the signal\r\n    handler.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the expected signal number.\r\n\r\nReturn Value:\r\n\r\n    Returns the index in the received signals array on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n    PLXT_SIGNAL_INFO Info;\r\n    int Result;\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto SignalCheckReceivedEnd;\r\n    }\r\n\r\n    if (Info->SignalCount == 0)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Signal %d was not received.\", Signal);\r\n        goto SignalCheckReceivedEnd;\r\n    }\r\n\r\n    for (Index = 0; Index < Info->SignalCount; Index += 1)\r\n    {\r\n        if (Info->ReceivedSignal[Index] == -1)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"An error occurred in the signal handler\");\r\n            goto SignalCheckReceivedEnd;\r\n        }\r\n\r\n        if (Info->ReceivedSignal[Index] == Signal)\r\n        {\r\n            Result = Index;\r\n            goto SignalCheckReceivedEnd;\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    LxtLogError(\"Signal %d was not received!\", Signal);\r\n\r\nSignalCheckReceivedEnd:\r\n    return Result;\r\n}\r\n\r\nint LxtSignalCheckSigChldReceived(int Code, pid_t Pid, uid_t Uid, int Status)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks if the SIGCHLD signal was received by the signal\r\n    handlers, with the specified info values.\r\n\r\n    N.B. The signal handler must have been established with SA_SIGINFO for this\r\n         to work.\r\n\r\nArguments:\r\n\r\n    Code - Supplies the expected signal code.\r\n\r\n    Pid - Supplies the expected process ID.\r\n\r\n    Uid - Supplies the expected user ID.\r\n\r\n    Status - Supplies the expected process status.\r\n\r\nReturn Value:\r\n\r\n    Returns the index in the received signals array on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n    PLXT_SIGNAL_INFO Info;\r\n    int Result;\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(Index = LxtSignalCheckInfoReceived(SIGCHLD, Code, Pid, Uid));\r\n    LxtCheckEqual(Status, Info->SignalInfo[Index].si_status, \"%d\");\r\n    Result = Index;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nPLXT_SIGNAL_INFO\r\nLxtSignalFindThreadInfo(void)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine finds the signal test info for the current thread.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    A pointer to the signal info, or NULL if the signal info was not\r\n    initialized.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    size_t Index;\r\n    PLXT_SIGNAL_INFO Result;\r\n    pid_t ThreadId;\r\n\r\n    Result = NULL;\r\n    ThreadId = gettid();\r\n    for (Index = 0; Index < SIGNAL_MAX_THREADS; Index += 1)\r\n    {\r\n        if (g_ThreadSignalInfo[Index].ThreadId == ThreadId)\r\n        {\r\n            Result = &g_ThreadSignalInfo[Index];\r\n            break;\r\n        }\r\n    }\r\n\r\n    if (Result == NULL)\r\n    {\r\n        LxtLogError(\"LxtSignalInitializeThread not called for this thread.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSignalGetCount(void)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine returns the number of received signals.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    The number of received signals, or -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    PLXT_SIGNAL_INFO Info;\r\n    int Result;\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = Info->SignalCount;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSignalGetInfo(siginfo_t* SignalInfo)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine gets a copy of the last received signal info.\r\n\r\nArguments:\r\n\r\n    SignalInfo - Supplies a pointer which receives the signal info.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    PLXT_SIGNAL_INFO Info;\r\n    int Result;\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    *SignalInfo = Info->SignalInfo[0];\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid LxtSignalHandler(int Signal)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine handles signals for the process.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the signal that was received\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int AllowedSignals;\r\n    PLXT_SIGNAL_INFO Info;\r\n    int Result;\r\n\r\n    Info = NULL;\r\n\r\n#if defined(__i386__)\r\n\r\n    register int Eax asm(\"eax\");\r\n    register void* Ecx asm(\"ecx\");\r\n    register void* Edx asm(\"edx\");\r\n\r\n    //\r\n    // Verify register contents.\r\n    //\r\n\r\n    LxtCheckEqual(Eax, Signal, \"%d\");\r\n    LxtCheckEqual(Edx, NULL, \"%p\");\r\n    LxtCheckEqual(Ecx, NULL, \"%p\");\r\n\r\n    //\r\n    // Verify stack alignment.\r\n    //\r\n\r\n    LxtCheckEqual((uintptr_t)&Signal & 0xf, 0, \"%p\");\r\n\r\n#endif\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (Info->AllowMultipleSignals != FALSE)\r\n    {\r\n        AllowedSignals = SIGNAL_MAX_SIGNALS;\r\n    }\r\n    else\r\n    {\r\n        AllowedSignals = 1;\r\n    }\r\n\r\n    if (Info->SignalCount < AllowedSignals)\r\n    {\r\n        LxtLogInfo(\"Process %d got signal %d (%s)\", getpid(), Signal, strsignal(Signal));\r\n\r\n        Result = Signal;\r\n    }\r\n    else\r\n    {\r\n        LxtLogError(\"Unexpected signal %d (%s)\", Signal, strsignal(Signal));\r\n        Result = LXT_RESULT_FAILURE;\r\n    }\r\n\r\nErrorExit:\r\n    if (Info != NULL)\r\n    {\r\n        if (Result < 0)\r\n        {\r\n            Info->ReceivedSignal[0] = LXT_RESULT_FAILURE;\r\n            Info->SignalCount = 1;\r\n        }\r\n        else if (Info->SignalCount < AllowedSignals)\r\n        {\r\n            Info->ReceivedSignal[Info->SignalCount] = Result;\r\n            Info->SignalCount += 1;\r\n        }\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nvoid LxtSignalHandlerSigAction(int Signal, siginfo_t* SigInfo, void* UContext)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine handles signals for the process using the SA_SIGINFO flag.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the signal that was received.\r\n\r\n    SigInfo - Supplies additional information about the signal.\r\n\r\n    UContext - Supplies the scheduling context from the process before the\r\n        signal handler was invoked.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int AllowedSignals;\r\n    PLXT_SIGNAL_INFO Info;\r\n    int Result;\r\n\r\n    Info = NULL;\r\n\r\n#if defined(__i386__)\r\n\r\n    register int Eax asm(\"eax\");\r\n    register void* Ecx asm(\"ecx\");\r\n    register void* Edx asm(\"edx\");\r\n\r\n    //\r\n    // Verify register contents.\r\n    //\r\n\r\n    LxtCheckEqual(Eax, Signal, \"%d\");\r\n    LxtCheckEqual(Edx, SigInfo, \"%p\");\r\n    LxtCheckEqual(Ecx, UContext, \"%p\");\r\n\r\n    //\r\n    // Verify stack alignment.\r\n    //\r\n\r\n    LxtCheckEqual((uintptr_t)&Signal & 0xf, 0, \"%p\");\r\n\r\n#endif\r\n\r\n    LxtCheckEqual(Signal, SigInfo->si_signo, \"%d\");\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (Info->AllowMultipleSignals != FALSE)\r\n    {\r\n        AllowedSignals = SIGNAL_MAX_SIGNALS;\r\n    }\r\n    else\r\n    {\r\n        AllowedSignals = 1;\r\n    }\r\n\r\n    if (Info->SignalCount < AllowedSignals)\r\n    {\r\n        if (Signal == SIGCHLD)\r\n        {\r\n            LxtLogInfo(\r\n                \"Process %d(%d) got signal %d (%s), code %d, pid %d, \"\r\n                \"uid %d, status %d\",\r\n                getpid(),\r\n                gettid(),\r\n                SigInfo->si_signo,\r\n                strsignal(SigInfo->si_signo),\r\n                SigInfo->si_code,\r\n                SigInfo->si_pid,\r\n                SigInfo->si_uid,\r\n                SigInfo->si_status);\r\n        }\r\n        else\r\n        {\r\n            LxtLogInfo(\r\n                \"Process %d(%d) got signal %d (%s), code %d, pid %d, uid %d\",\r\n                getpid(),\r\n                gettid(),\r\n                SigInfo->si_signo,\r\n                strsignal(SigInfo->si_signo),\r\n                SigInfo->si_code,\r\n                SigInfo->si_pid,\r\n                SigInfo->si_uid);\r\n        }\r\n\r\n        Result = Signal;\r\n    }\r\n    else\r\n    {\r\n        LxtLogError(\r\n            \"Process %d got unexpected signal %d (%s), code %d, pid %d, uid %d\",\r\n            getpid(),\r\n            SigInfo->si_signo,\r\n            strsignal(SigInfo->si_signo),\r\n            SigInfo->si_code,\r\n            SigInfo->si_pid,\r\n            SigInfo->si_uid);\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n    }\r\n\r\nErrorExit:\r\n    if (Info != NULL)\r\n    {\r\n        if (Result < 0)\r\n        {\r\n            Info->ReceivedSignal[0] = LXT_RESULT_FAILURE;\r\n            Info->SignalCount = 1;\r\n        }\r\n        else if (Info->SignalCount < AllowedSignals)\r\n        {\r\n            Info->ReceivedSignal[Info->SignalCount] = Result;\r\n            Info->SignalInfo[Info->SignalCount] = *SigInfo;\r\n            Info->SignalCount += 1;\r\n        }\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint LxtSignalInitialize(void)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine initializes the signal test infrastructure for the current\r\n    process.\r\n\r\n    N.B. Run this function for any process that uses the signal test\r\n         infrastructure. If a test uses fork(), you must run this function\r\n         again in the child process.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    g_NextSignalThread = 0;\r\n    memset(g_ThreadSignalInfo, 0, sizeof(g_ThreadSignalInfo));\r\n    return LxtSignalInitializeThread();\r\n}\r\n\r\nint LxtSignalInitializeThread(void)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine initializes the signal test infrastructure for the current\r\n    thread.\r\n\r\n    N.B. Run this function for any thread that uses the signal test\r\n         infrastructure, except the main thread of the process; for the main\r\n         thread, run LxtSignalInitialize instead.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n    int Result;\r\n\r\n    Index = __sync_fetch_and_add(&g_NextSignalThread, 1);\r\n    if (Index >= SIGNAL_MAX_THREADS)\r\n    {\r\n        LxtLogError(\"Too many threads in signal test.\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (g_ThreadSignalInfo[Index].ThreadId != 0)\r\n    {\r\n        LxtLogError(\"Invalid signal test state.\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    g_ThreadSignalInfo[Index].ThreadId = gettid();\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid LxtSignalResetReceived(void)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine resets the global variables used by the signal handlers.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    PLXT_SIGNAL_INFO Info;\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Info->SignalCount = 0;\r\n\r\nErrorExit:\r\n    return;\r\n}\r\n\r\nint LxtSignalSetupHandler(int Signal, int Flags)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets up a signal handler.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the signal.\r\n\r\n    Flags - Supplies the flags.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct sigaction Action;\r\n    int Result;\r\n\r\n    //\r\n    // Check that the signal infrastructure was initialized properly.\r\n    //\r\n\r\n    if (LxtSignalFindThreadInfo() == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(&Action, 0, sizeof(Action));\r\n    if ((Flags & SA_SIGINFO) != 0)\r\n    {\r\n        Action.sa_sigaction = LxtSignalHandlerSigAction;\r\n    }\r\n    else\r\n    {\r\n        Action.sa_handler = LxtSignalHandler;\r\n    }\r\n\r\n    Action.sa_flags = Flags;\r\n    LxtCheckErrnoZeroSuccess(sigaction(Signal, &Action, NULL));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid LxtSignalSetAllowMultiple(BOOLEAN AllowMultiple)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets whether or not receiving another signal when one was\r\n    already received should be not considered an error.\r\n\r\nArguments:\r\n\r\n    AllowMultiple - Supplies a value that indicates whether multiple signals\r\n        are allowed.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    PLXT_SIGNAL_INFO Info;\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Info->AllowMultipleSignals = AllowMultiple;\r\n\r\nErrorExit:\r\n    return;\r\n}\r\n\r\nint LxtSignalTimedWait(sigset_t* Set, siginfo_t* SignalInfo, struct timespec* Timeout)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine calls the rt_sigtimedwait system call.\r\n\r\n    N.B. In glibc, the sigtimedwait function is available as a wrapper for\r\n         this system call, but in bionic only sigwait is available which\r\n         prevents access to some of the parameters of rt_sigtimedwait.\r\n         Even in glibc the sigtimedwait wrapper should not be used for testing\r\n         since it silently converts SI_TKILL to SI_USER.\r\n\r\nArguments:\r\n\r\n    Set - Supplies a pointer to the set of signals to wait for.\r\n\r\n    SignalInfo - Supplies a pointer that receives information about the signal.\r\n\r\n    Timeout - Supplies a pointer to a timeout value.\r\n\r\nReturn Value:\r\n\r\n    The signal number on success, -1 on failure with errno set appropriately.\r\n\r\n--*/\r\n\r\n{\r\n\r\n#if defined(__GLIBC__)\r\n    sigset_t* SignalSetPointer;\r\n\r\n    SignalSetPointer = Set;\r\n#else\r\n    kernel_sigset_t SignalSet;\r\n    kernel_sigset_t* SignalSetPointer;\r\n\r\n    //\r\n    // Convert to the 64-bit signal set size that the kernel expects.\r\n    //\r\n\r\n    SignalSetPointer = NULL;\r\n    if (Set != NULL)\r\n    {\r\n        SignalSet = *Set;\r\n        SignalSetPointer = &SignalSet;\r\n    }\r\n\r\n#endif\r\n\r\n    return syscall(SYS_rt_sigtimedwait, SignalSetPointer, SignalInfo, Timeout, _NSIG / 8);\r\n}\r\n\r\nint LxtSignalUnblock(int Signal)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine unblocks the specified signal.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the signal number.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    sigset_t Signals;\r\n\r\n    sigemptyset(&Signals);\r\n    sigaddset(&Signals, Signal);\r\n    LxtCheckErrnoZeroSuccess(sigprocmask(SIG_UNBLOCK, &Signals, NULL));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid LxtSignalWait(void)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine waits until a signal has been received, or a timeout expires.\r\n\r\n    N.B. This function does not return status to indicate whether a signal was\r\n         received or not. Use the signal check functions after this\r\n         function returns.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    PLXT_SIGNAL_INFO Info;\r\n    int WaitCount;\r\n\r\n    Info = LxtSignalFindThreadInfo();\r\n    if (Info == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // N.B. It would be possible to implement this function using sigsuspend\r\n    //      but only after signal blocking is implemented. In order to avoid\r\n    //      a race where sigsuspend might hang if the signal arrives before\r\n    //      the call, the relevant signal should be blocked before doing the\r\n    //      operation that generates the signal, then call sigsuspend with a\r\n    //      mask that unblocks the signal.\r\n    //\r\n\r\n    for (WaitCount = 0; (WaitCount < SIGNAL_WAIT_COUNT) && (Info->SignalCount == 0); WaitCount += 1)\r\n    {\r\n\r\n        usleep(SIGNAL_WAIT_TIMEOUT_US);\r\n    }\r\n\r\nErrorExit:\r\n    return;\r\n}\r\n\r\nint LxtSignalWaitBlocked(int Signal, pid_t FromPid, int TimeoutSeconds)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine waits for a specific blocked signal.\r\n\r\nArguments:\r\n\r\n    Signal - Supplies the signal number.\r\n\r\n    FromPid - Supplies the expected origin of the signal.\r\n\r\n    TimeoutSeconds - Supplies the timeout, in seconds.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ReceivedSignal;\r\n    int Result;\r\n    siginfo_t SignalInfo;\r\n    sigset_t Signals;\r\n    struct timespec Timeout;\r\n\r\n    sigemptyset(&Signals);\r\n    sigaddset(&Signals, Signal);\r\n    Timeout.tv_sec = TimeoutSeconds;\r\n    Timeout.tv_nsec = 0;\r\n    LxtCheckErrno(ReceivedSignal = LxtSignalTimedWait(&Signals, &SignalInfo, &Timeout));\r\n\r\n    LxtCheckEqual(Signal, ReceivedSignal, \"%d\");\r\n    LxtCheckEqual(SignalInfo.si_pid, FromPid, \"%d\");\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSocketPairClose(PLXT_SOCKET_PAIR SocketPair)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine closes a socket pair.\r\n\r\nArguments:\r\n\r\n    SocketPair - Supplies a pointer to the socket pair.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtSocketPairCloseChild(SocketPair));\r\n    LxtCheckResult(LxtSocketPairCloseParent(SocketPair));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSocketPairCloseChild(PLXT_SOCKET_PAIR SocketPair)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine closes the child socket of a socket pair.\r\n\r\nArguments:\r\n\r\n    SocketPair - Supplies a pointer to the socket pair.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    if (SocketPair->Child != 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(close(SocketPair->Child));\r\n        SocketPair->Child = 0;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSocketPairCloseParent(PLXT_SOCKET_PAIR SocketPair)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine closes the parent socket of a socket pair.\r\n\r\nArguments:\r\n\r\n    SocketPair - Supplies a pointer to the socket pair.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    if (SocketPair->Parent != 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(close(SocketPair->Parent));\r\n        SocketPair->Parent = 0;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtSocketPairCreate(PLXT_SOCKET_PAIR SocketPair)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates a socket pair.\r\n\r\nArguments:\r\n\r\n    SocketPair - Supplies a pointer to the socket pair.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    memset(SocketPair, 0, sizeof(*SocketPair));\r\n    LxtCheckErrnoZeroSuccess(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, (int*)SocketPair));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtWaitPidPoll(pid_t ChildPid, int ExpectedWaitStatus)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine waits until the specified child exits by polling its wait\r\n    status repeatedly.\r\n\r\nArguments:\r\n\r\n    ChildPid - Supplies the thread group ID of the child to wait on.\r\n\r\n    ExpectedWaitStatus - Supplies the expected value of the child's status.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    return LxtWaitPidPollOptions(ChildPid, ExpectedWaitStatus, 0, LXT_WAITPID_DEFAULT_TIMEOUT);\r\n}\r\n\r\nint LxtWaitPidPollOptions(pid_t ChildPid, int ExpectedWaitStatus, int Options, int TimeoutSeconds)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine waits until the specified child exits by polling its wait\r\n    status repeatedly.\r\n\r\nArguments:\r\n\r\n    ChildPid - Supplies the thread group ID of the child to wait on.\r\n\r\n    ExpectedWaitStatus - Supplies the expected value of the child's status.\r\n\r\n    Options - Supplies wait options to pass to waitpid.\r\n\r\n    TimeoutSeconds - Supplies the number of seconds to wait for the child.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int SecondWaitPidStatus;\r\n    int Result;\r\n    int WaitCount;\r\n    int WaitCountTotal;\r\n    int WaitPidResult;\r\n    int WaitPidStatus;\r\n\r\n    //\r\n    // Only WNOHANG is supported right now, so poll for the result and check the\r\n    // status.\r\n    //\r\n\r\n    Options |= WNOHANG;\r\n    WaitCountTotal = (TimeoutSeconds * 1000000) / LXT_WAITPID_WAIT_TIMEOUT_US;\r\n    for (WaitCount = 0; WaitCount < WaitCountTotal; ++WaitCount)\r\n    {\r\n        LxtCheckErrno((WaitPidResult = waitpid(ChildPid, &WaitPidStatus, Options)));\r\n\r\n        if (WaitPidResult != 0)\r\n        {\r\n            if ((WaitPidStatus & 0x80000000) != 0)\r\n            {\r\n                Result = LXT_RESULT_FAILURE;\r\n                LxtLogError(\"Unexpected high bit: %x - %x\", WaitPidStatus, ExpectedWaitStatus);\r\n\r\n                goto ErrorExit;\r\n            }\r\n\r\n            if (WaitPidStatus != ExpectedWaitStatus)\r\n            {\r\n                Result = LXT_RESULT_FAILURE;\r\n                LxtLogError(\"Unexpected status: %x != %x\", WaitPidStatus, ExpectedWaitStatus);\r\n\r\n                goto ErrorExit;\r\n            }\r\n\r\n            if (WIFEXITED(WaitPidStatus) != 0)\r\n            {\r\n                LxtCheckErrnoFailure(waitpid(ChildPid, &SecondWaitPidStatus, WNOHANG), ECHILD);\r\n            }\r\n\r\n            Result = WaitPidResult;\r\n            break;\r\n        }\r\n\r\n        usleep(LXT_WAITPID_WAIT_TIMEOUT_US);\r\n    }\r\n\r\n    if (WaitCount == WaitCountTotal)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Failed to receive status %d from child {:%d:}\", ExpectedWaitStatus, ChildPid);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtClose(int FileDescriptor)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n\r\n    LxtCheckErrnoZeroSuccess(close(FileDescriptor));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtMunmap(void* Address, size_t Length)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n\r\n    LxtCheckErrno(munmap(Address, Length));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtWslVersion(void)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine determines whether the tests are running in WSL1 or 2.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    The WSL version number, 1 or 2, or 0 if an error occurred.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct utsname UnameBuffer;\r\n\r\n    if (g_WslVersion == 0)\r\n    {\r\n        memset(&UnameBuffer, 0, sizeof(UnameBuffer));\r\n        LxtCheckErrno(uname(&UnameBuffer));\r\n        if (strstr(UnameBuffer.release, \"Microsoft\") == NULL)\r\n        {\r\n            g_WslVersion = 2;\r\n        }\r\n        else\r\n        {\r\n            g_WslVersion = 1;\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    return g_WslVersion;\r\n}"
  },
  {
    "path": "test/linux/unit_tests/lxtutil.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxtutil.h\r\n\r\nAbstract:\r\n\r\n    This file contains lx test common utility functions that log appropriately.\r\n\r\n--*/\r\n\r\n#ifndef _LXT_UTIL\r\n#define _LXT_UTIL\r\n\r\n#include \"lxtlog.h\"\r\n\r\n#if defined(__aarch64__)\r\n\r\n#define __ARCH_WANT_SYSCALL_DEPRECATED\r\n\r\n#endif\r\n\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/syscall.h>\r\n#include <signal.h>\r\n#include <sched.h>\r\n#include <stdbool.h>\r\n#include <stdint.h>\r\n\r\n#define LXT_WAITPID_DEFAULT_TIMEOUT 10\r\n\r\n#define LXT_CHECK_DIRECTORY_CONTENTS_READ_FILES (0x1)\r\n\r\n#define LXT_ROUND_UP_COUNT(Count, Pow2) (((Count) + (Pow2) - 1) & (~(((Pow2)) - 1)))\r\n\r\n#define LXT_CLONE_STACK_SIZE (1024 * 1024)\r\n\r\n#define LxtCheckClose(_Fd) \\\r\n    { \\\r\n        LxtCheckResult(LxtClose(_Fd)); \\\r\n        (_Fd) = -1; \\\r\n    }\r\n\r\ntypedef struct _LXT_ARGS\r\n{\r\n    LxtLogType LogType;\r\n    bool LogAppend;\r\n    unsigned long long VariationMask;\r\n    bool HelpRequested;\r\n    int Argc;\r\n    char** Argv;\r\n} LXT_ARGS, *PLXT_ARGS;\r\n\r\ntypedef int LXT_VARIATION_HANDLER(PLXT_ARGS Args);\r\n\r\ntypedef struct _LXT_VARIATION\r\n{\r\n    const char* Name;\r\n    LXT_VARIATION_HANDLER* Variation;\r\n} LXT_VARIATION, *PLXT_VARIATION;\r\n\r\ntypedef const LXT_VARIATION* PCLXT_VARIATION;\r\n\r\ntypedef unsigned char BOOLEAN;\r\n\r\n#define TRUE 1\r\n#define FALSE 0\r\n\r\n#define LXT_COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))\r\n\r\n#define LXT_INIT_PID ((pid_t)1)\r\n\r\n#define LXT_DEFAULT_EXIT_CODE 0\r\n\r\ntypedef struct _LXT_CHILD_INFO\r\n{\r\n    const char* Name;\r\n    unsigned char FileType;\r\n} LXT_CHILD_INFO, *PLXT_CHILD_INFO;\r\n\r\n//\r\n// Test framework code\r\n//\r\n\r\nint LxtCheckDirectoryContents(const char* Path, const LXT_CHILD_INFO* Children, size_t Count);\r\n\r\nint LxtCheckDirectoryContentsEx(const char* Path, const LXT_CHILD_INFO* Children, size_t Count, int Flags);\r\n\r\nint LxtCheckFdPath(int Fd, char* ExpectedPath);\r\n\r\nint LxtCheckLinkTarget(const char* Path, const char* ExpectedTarget);\r\n\r\nint LxtCheckRead(const char* FullPath, unsigned char FileType);\r\n\r\nint LxtCheckStat(const char* FullPath, unsigned long long ExpectedInode, unsigned char FileType);\r\n\r\nint LxtCheckWslPathTranslation(char* Path, const char* ExpectedPath, bool WinPath);\r\n\r\nint LxtCompareMemory(const void* First, const void* Second, size_t Size, const char* FirstDescription, const char* SecondDescription);\r\n\r\nint LxtCopyFile(const char* Source, const char* Destination);\r\n\r\nint LxtInitialize(int Argc, char* Argv[], PLXT_ARGS Args, const char* TestName);\r\n\r\nint LxtRunVariations(PLXT_ARGS Args, PCLXT_VARIATION Variations, unsigned int VariationCount);\r\n\r\nint LxtRunVariationsForked(PLXT_ARGS Args, PCLXT_VARIATION Variations, unsigned int VariationCount);\r\n\r\nint LxtCheckWrite(const char* FullPath, const char* Value);\r\n\r\nvoid LxtUninitialize(void);\r\n\r\n//\r\n// stdlib wrappers\r\n//\r\n\r\nvoid* LxtAlloc(size_t Size);\r\n\r\nvoid LxtFree(void* Allocation);\r\n\r\n//\r\n// syscall wrappers\r\n//\r\n\r\n#define LxtExit(_status) (syscall(1, _status))\r\n#define LxtRt_SigAction(_signal, _action, _oaction, _setsize) (syscall(SYS_rt_sigaction, _signal, _action, _oaction, _setsize))\r\n#define LxtRt_SigProcMask(_how, _set, _oset, _setsize) (syscall(SYS_rt_sigprocmask, _how, _set, _oset, _setsize))\r\n#define LxtTKill(_tid, _sig) (syscall(SYS_tkill, _tid, _sig))\r\n#define LxtTgKill(_tgid, _tid, _sig) (syscall(SYS_tgkill, _tgid, _tid, _sig))\r\n#define LxtRead(_fd, _buffer, _count) (syscall(SYS_read, _fd, _buffer, _count))\r\n#define LxtWrite(_fd, _buffer, _count) (syscall(SYS_write, _fd, _buffer, _count))\r\n#define LxtGetTid() (syscall(SYS_gettid))\r\n#define gettid() LxtGetTid()\r\n#define LxtPipe2(_pipefds, _flags) (syscall(__NR_pipe2, _pipefds, _flags))\r\n#define LxtFutex(_uaddr, _op, _val, _timeout, _uaddr2, _val3) (syscall(SYS_futex, _uaddr, _op, _val, _timeout, _uaddr2, _val3))\r\n#define LxtCapGet(_header, _data) (syscall(SYS_capget, _header, _data))\r\n#define LxtCapSet(_header, _data) (syscall(SYS_capset, _header, _data))\r\n#define LxtExecve(_Filename, _Argv, _Envp) syscall(SYS_execve, (_Filename), (_Argv), (_Envp))\r\n#define LxtWaitId(_idtype, _id, _infop, _options, _rusage) syscall(SYS_waitid, (_idtype), (_id), (_infop), (_options), (_rusage))\r\n#define LxtSetUid(_uid) syscall(SYS_setuid, (_uid))\r\n#if !defined(__amd64__)\r\n#define LxtCloneSyscall(_flags, _stack, _ptid, _ctid, _tls) (syscall(SYS_clone, _flags, _stack, _ptid, _tls, _ctid))\r\n#else\r\n#define LxtCloneSyscall(_flags, _stack, _ptid, _ctid, _tls) (syscall(SYS_clone, _flags, _stack, _ptid, _ctid, _tls))\r\n#endif\r\n#define LxtTimer_Create(_clockid, _sevp, _timerid) syscall(SYS_timer_create, (_clockid), (_sevp), (_timerid))\r\n#define LxtTimer_SetTime(_timerid, _flags, _new_value, _old_value) \\\r\n    syscall(SYS_timer_settime, (_timerid), (_flags), (_new_value), (_old_value))\r\n#define LxtTimer_GetTime(_timerid, _curr_value) syscall(SYS_timer_gettime, (_timerid), (_curr_value))\r\n#define LxtTimer_GetOverrun(_timerid) syscall(SYS_timer_getoverrun, (_timerid))\r\n#define LxtTimer_Delete(_timerid) syscall(SYS_timer_delete, (_timerid))\r\n#define LxtClock_Nanosleep(_clockid, _flags, _request, _remain) \\\r\n    syscall(SYS_clock_nanosleep, (_clockid), (_flags), (_request), (_remain))\r\n#define LxtGetrandom(_buffer, _size, _flags) syscall(SYS_getrandom, (_buffer), (_size), (_flags))\r\n#define LxtShmAt(_id, _address, _flags) (void*)(syscall(SYS_shmat, (_id), (_address), (_flags)))\r\n#define LxtShmCtl(_id, _cmd, _buffer) syscall(SYS_shmctl, (_id), (_cmd), (_buffer))\r\n#define LxtShmDt(_address) syscall(SYS_shmdt, (_address))\r\n#define LxtShmGet(_key, _size, _flags) syscall(SYS_shmget, (_key), (_size), (_flags))\r\n#define LxtMremap(_oldAddress, _oldSize, _newSize, _flags, _newAddress) \\\r\n    (void*)(syscall(SYS_mremap, _oldAddress, _oldSize, _newSize, _flags, _newAddress))\r\n#define LxtSemCtl(_id, _number, _command, _buffer) syscall(SYS_semctl, (_id), (_number), (_command), (_buffer))\r\n#define LxtSemGet(_key, _count, _flags) syscall(SYS_semget, (_key), (_count), (_flags))\r\n#define LxtSemOp(_id, _operations, _opcount) syscall(SYS_semop, (_id), (_operations), (_opcount))\r\n#define LxtSemTimedOp(_id, _operations, _opcount, _timeout) syscall(SYS_semtimedop, (_id), (_operations), (_opcount), (_timeout))\r\n#define LxtIoprio_get(_which, _who) syscall(__NR_ioprio_get, (_which), (_who))\r\n#define LxtIoprio_set(_which, _who, _prio) syscall(__NR_ioprio_set, (_which), (_who), (_prio))\r\n#define LxtSched_GetAffinity(_pid, _cpusetsize, _mask) syscall(__NR_sched_getaffinity, (_pid), (_cpusetsize), (_mask))\r\n#define LxtSched_SetAffinity(_pid, _cpusetsize, _mask) syscall(__NR_sched_setaffinity, (_pid), (_cpusetsize), (_mask))\r\n#define LxtListxattr(_path, _buffer, _size) (syscall(SYS_listxattr, (_path), (_buffer), (_size)))\r\n#define LxtLlistxattr(_path, _buffer, _size) (syscall(SYS_llistxattr, (_path), (_buffer), (_size)))\r\n#define LxtFlistxattr(_fd, _buffer, _size) (syscall(SYS_flistxattr, (_fd), (_buffer), (_size)))\r\n#define LxtGetxattr(_path, _name, _buffer, _size) (syscall(SYS_getxattr, (_path), (_name), (_buffer), (_size)))\r\n#define LxtLgetxattr(_path, _name, _buffer, _size) (syscall(SYS_lgetxattr, (_path), (_name), (_buffer), (_size)))\r\n#define LxtFgetxattr(_fd, _name, _buffer, _size) (syscall(SYS_fgetxattr, (_fd), (_name), (_buffer), (_size)))\r\n#define LxtSetxattr(_path, _name, _buffer, _size, _flags) (syscall(SYS_setxattr, (_path), (_name), (_buffer), (_size), (_flags)))\r\n#define LxtLsetxattr(_path, _name, _buffer, _size, _flags) \\\r\n    (syscall(SYS_lsetxattr, (_path), (_name), (_buffer), (_size), (_flags)))\r\n#define LxtFsetxattr(_fd, _name, _buffer, _size, _flags) (syscall(SYS_fsetxattr, (_fd), (_name), (_buffer), (_size), (_flags)))\r\n#define LxtRemovexattr(_path, _name) (syscall(SYS_removexattr, (_path), (_name)))\r\n#define LxtLremovexattr(_path, _name) (syscall(SYS_lremovexattr, (_path), (_name)))\r\n#define LxtFremovexattr(_fd, _name) (syscall(SYS_fremovexattr, (_fd), (_name)))\r\n#define LxtGetresuid(_real, _effective, _saved) (syscall(SYS_getresuid, (_real), (_effective), (_saved)))\r\n#define LxtSetresuid(_real, _effective, _saved) (syscall(SYS_setresuid, (_real), (_effective), (_saved)))\r\n#define LxtSetresgid(_real, _effective, _saved) (syscall(SYS_setresgid, (_real), (_effective), (_saved)))\r\n#define LxtSetfsgid(_gid) syscall(__NR_setfsgid, (_gid))\r\n#define LxtSetfsuid(_uid) syscall(__NR_setfsuid, (_uid))\r\n#define LxtPrlimit64(_pid, _resource, _newvalue, _oldvalue) syscall(__NR_prlimit64, (_pid), (_resource), (_newvalue), (_oldvalue))\r\n//__NR_getdents has been removed from newer versions of libc\r\n#ifdef __NR_getdents\r\n#define LxtGetdents(_fd, _buffer, _size) syscall(__NR_getdents, (_fd), (_buffer), (_size))\r\n#endif\r\n#define LxtGetdents64(_fd, _buffer, _size) syscall(__NR_getdents64, (_fd), (_buffer), (_size))\r\n#define LxtGetcwd(_buffer, _size) syscall(SYS_getcwd, (_buffer), (_size))\r\n\r\n#if defined(__aarch64__)\r\n#define LxtFStatAt64(_dirfd, _path, _buffer, _flags) fstatat(_dirfd, _path, _buffer, _flags)\r\n#elif !defined(__amd64__)\r\n#define LxtFStatAt64(_dirfd, _path, _buffer, _flags) syscall(SYS_fstatat64, _dirfd, _path, _buffer, _flags)\r\n#else\r\n#define LxtFStatAt64(_dirfd, _path, _buffer, _flags) fstatat64(_dirfd, _path, _buffer, _flags)\r\n#endif\r\n\r\n#ifndef clock_gettime\r\n#define LxtClockGetTime(_clockid, _timespec) syscall(__NR_clock_gettime, _clockid, _timespec)\r\n#else\r\n#define LxtClockGetTime(_clockid, _timespec) clock_gettime(_clockid, _timespec)\r\n#endif\r\n\r\n#define LxtClockGetRes(_clockid, _timespec) syscall(__NR_clock_getres, _clockid, _timespec)\r\n\r\n#ifndef timerfd_create\r\n#define timerfd_create(ClockId, Flags) syscall(__NR_timerfd_create, ClockId, Flags)\r\n#endif\r\n\r\n#ifndef timerfd_gettime\r\n#define timerfd_gettime(Fd, CurrentValue) syscall(__NR_timerfd_gettime, Fd, CurrentValue)\r\n#endif\r\n\r\n#ifndef timerfd_settime\r\n#define timerfd_settime(Fd, Flags, NewValue, OldValue) syscall(__NR_timerfd_settime, Fd, Flags, NewValue, OldValue)\r\n#endif\r\n\r\n#define LXT_CLONE_FLAGS_DEFAULT (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM)\r\n\r\ntypedef long long kernel_sigset_t;\r\n\r\ntypedef struct _LXT_RT_SIG_ACTION\r\n{\r\n    void* Handler;\r\n    int Flags;\r\n    void* Restorer;\r\n    kernel_sigset_t Mask;\r\n} LXT_RT_SIG_ACTION, *PLXT_RT_SIG_ACTION;\r\n\r\ntypedef struct _LXT_CLONE_ARGS\r\n{\r\n    char* Stack;\r\n    pid_t CloneId;\r\n} LXT_CLONE_ARGS, *PLXT_CLONE_ARGS;\r\n\r\ntypedef struct _LXT_PIPE\r\n{\r\n    int Read;\r\n    int Write;\r\n} LXT_PIPE, *PLXT_PIPE;\r\n\r\ntypedef struct _LXT_SOCKET_PAIR\r\n{\r\n    int Parent;\r\n    int Child;\r\n} LXT_SOCKET_PAIR, *PLXT_SOCKET_PAIR;\r\n\r\nint LxtAcceptPoll(int Socket);\r\n\r\nint LxtClone(int (*Entry)(void* Parameter), void* Parameter, int Flags, PLXT_CLONE_ARGS Args);\r\n\r\nint LxtClosePipe(PLXT_PIPE Pipe);\r\n\r\nint LxtCreatePipe(PLXT_PIPE Pipe);\r\n\r\nint LxtExecuteAndReadOutput(char** Argv, char* OutputBuffer, size_t OutputBufferSize);\r\n\r\nint LxtExecuteWslPath(char* Path, bool WinPath, char* OutputBuffer, size_t OutputBufferSize);\r\n\r\nint LxtJoinThread(pid_t* Tid);\r\n\r\nint LxtReceiveMessage(int Socket, const char* ExpectedMessage);\r\n\r\nint LxtSendMessage(int Socket, const char* Message);\r\n\r\nint LxtSignalBlock(int Signal);\r\n\r\nint LxtSignalDefault(int Signal);\r\n\r\nint LxtSignalIgnore(int Signal);\r\n\r\nint LxtSignalCheckInfoReceived(int Signal, int Code, pid_t Pid, uid_t Uid);\r\n\r\nint LxtSignalCheckNoSignal(void);\r\n\r\nint LxtSignalCheckReceived(int Signal);\r\n\r\nint LxtSignalCheckSigChldReceived(int Code, pid_t Pid, uid_t Uid, int Status);\r\n\r\nint LxtSignalGetCount(void);\r\n\r\nint LxtSignalGetInfo(siginfo_t* SignalInfo);\r\n\r\nint LxtSignalInitialize(void);\r\n\r\nint LxtSignalInitializeThread(void);\r\n\r\nvoid LxtSignalResetReceived(void);\r\n\r\nvoid LxtSignalSetAllowMultiple(BOOLEAN AllowMultiple);\r\n\r\nint LxtSignalSetupHandler(int Signal, int Flags);\r\n\r\nint LxtSignalTimedWait(sigset_t* Set, siginfo_t* SignalInfo, struct timespec* Timeout);\r\n\r\nint LxtSignalUnblock(int Signal);\r\n\r\nvoid LxtSignalWait(void);\r\n\r\nint LxtSignalWaitBlocked(int Signal, pid_t FromPid, int TimeoutSeconds);\r\n\r\nint LxtSignalCheckSigChldReceived(int Code, pid_t Pid, uid_t Uid, int Status);\r\n\r\nint LxtSocketPairClose(PLXT_SOCKET_PAIR SocketPair);\r\n\r\nint LxtSocketPairCloseChild(PLXT_SOCKET_PAIR SocketPair);\r\n\r\nint LxtSocketPairCloseParent(PLXT_SOCKET_PAIR SocketPair);\r\n\r\nint LxtSocketPairCreate(PLXT_SOCKET_PAIR SocketPair);\r\n\r\nint LxtWaitPidPoll(pid_t ChildPid, int ExpectedWaitStatus);\r\n\r\nint LxtWaitPidPollOptions(pid_t ChildPid, int ExpectedWaitStatus, int Options, int TimeoutSeconds);\r\n\r\nint LxtClose(int FileDescriptor);\r\n\r\nint LxtMunmap(void* Address, size_t Length);\r\n\r\nint LxtWslVersion(void);\r\n\r\n#endif // _LXT_UTIL\r\n"
  },
  {
    "path": "test/linux/unit_tests/madvise.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    madvise.c\r\n\r\nAbstract:\r\n\r\n    This file is a madvise test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <string.h>\r\n#include <sys/mman.h>\r\n#include <sched.h>\r\n#include <signal.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/ioctl.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n#include <time.h>\r\n\r\n#define LXT_NAME \"madvise\"\r\n\r\nint MadviseVariation2(PLXT_ARGS Args);\r\n\r\nint MadviseVariation3(PLXT_ARGS Args);\r\n\r\nint MadviseVariation4(PLXT_ARGS Args);\r\n\r\nint MadviseVariation5(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Anonymous mapping\", MadviseVariation2},\r\n    {\"Anonymous mapping with fork\", MadviseVariation3},\r\n    {\"File-backed mapping\", MadviseVariation4},\r\n    {\"File-backed with fork\", MadviseVariation5}};\r\n\r\nint MadviseTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\n#define MADVISE_TEST_BUFFER_SIZE 0x5000\r\n\r\nint MadviseRunTest(int FileId, int BufferSize, int DoFork)\r\n{\r\n    int Result;\r\n    unsigned char* SharedBuffer;\r\n    unsigned char* PrivateBuffer;\r\n    unsigned char* Ptr;\r\n    unsigned char* EndPtr;\r\n    unsigned char ExpectedByte;\r\n    int MapAnonymousFlag;\r\n    int IsChild;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    SharedBuffer = MAP_FAILED;\r\n    PrivateBuffer = MAP_FAILED;\r\n    IsChild = 0;\r\n\r\n    MapAnonymousFlag = 0;\r\n\r\n    if (FileId == -1)\r\n    {\r\n        MapAnonymousFlag = MAP_ANONYMOUS;\r\n    }\r\n\r\n    //\r\n    // Allocate the shared buffer.\r\n    //\r\n\r\n    SharedBuffer = mmap(NULL, BufferSize, PROT_READ | PROT_WRITE, MAP_SHARED | MapAnonymousFlag, FileId, 0);\r\n\r\n    if (SharedBuffer == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not map shared! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Allocate the private buffer.\r\n    //\r\n\r\n    PrivateBuffer = mmap(NULL, BufferSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MapAnonymousFlag, FileId, 0);\r\n\r\n    if (PrivateBuffer == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not map private! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Fill the shared buffer.\r\n    //\r\n\r\n    Ptr = SharedBuffer;\r\n    EndPtr = Ptr + BufferSize;\r\n\r\n    for (; Ptr < EndPtr; Ptr += 1)\r\n    {\r\n        *Ptr = 0xDE;\r\n    }\r\n\r\n    //\r\n    // Make sure the private buffer sees the shared buffer.\r\n    //\r\n\r\n    if (FileId != -1)\r\n    {\r\n\r\n        Ptr = PrivateBuffer;\r\n        EndPtr = Ptr + BufferSize;\r\n\r\n        for (; Ptr < EndPtr; Ptr += 1)\r\n        {\r\n            if (*Ptr != 0xDE)\r\n            {\r\n                *((volatile int*)0) = 0xDEADBEEF;\r\n            }\r\n        }\r\n    }\r\n\r\n    //\r\n    // Fill the private buffer.\r\n    //\r\n\r\n    Ptr = PrivateBuffer;\r\n    EndPtr = Ptr + BufferSize;\r\n\r\n    for (; Ptr < EndPtr; Ptr += 1)\r\n    {\r\n        *Ptr = 0xCA;\r\n    }\r\n\r\n    //\r\n    // Protect one of the two pages that will be reverted.\r\n    //\r\n\r\n    Result = mprotect(PrivateBuffer + PAGE_SIZE, PAGE_SIZE, PROT_READ);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not set protection on buffer! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Fork the process if desired.\r\n    //\r\n\r\n    if (DoFork)\r\n    {\r\n\r\n        Result = fork();\r\n\r\n        if (Result == -1)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"Could not fork the child! %d\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        if (Result == 0)\r\n        {\r\n            IsChild = 1;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Revert the middle two pages back to the shared buffer.\r\n    //\r\n\r\n    Result = madvise((PrivateBuffer + PAGE_SIZE), (PAGE_SIZE * 2), MADV_DONTNEED);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not madvise on private buffer! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Make sure the first page contains private data.\r\n    //\r\n\r\n    Ptr = PrivateBuffer;\r\n    EndPtr = Ptr + PAGE_SIZE;\r\n\r\n    for (; Ptr < EndPtr; Ptr += 1)\r\n    {\r\n\r\n        if (*Ptr != 0xCA)\r\n        {\r\n            LxtLogError(\"Private buffer not contain expected data (1), %d\", *Ptr);\r\n            Result = EFAULT;\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Make sure the next two pages contain shared data or zeroes in the private\r\n    // allocation case.\r\n    //\r\n\r\n    EndPtr = Ptr + (PAGE_SIZE * 2);\r\n\r\n    for (; Ptr < EndPtr; Ptr += 1)\r\n    {\r\n        if (FileId == -1)\r\n        {\r\n            ExpectedByte = 0;\r\n        }\r\n        else\r\n        {\r\n            ExpectedByte = 0xDE;\r\n        }\r\n\r\n        if (*Ptr != ExpectedByte)\r\n        {\r\n            LxtLogError(\"Private buffer not contain expected data (2), %d\", *Ptr);\r\n            Result = EFAULT;\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Make sure the remaining pages contain private data.\r\n    //\r\n\r\n    EndPtr = PrivateBuffer + BufferSize;\r\n\r\n    for (; Ptr < EndPtr; Ptr += 1)\r\n    {\r\n        if (*Ptr != 0xCA)\r\n        {\r\n            LxtLogError(\"Private buffer not contain expected data (3), %d\", *Ptr);\r\n            Result = EFAULT;\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Make sure that the shared buffer contains all shared data.\r\n    //\r\n\r\n    Ptr = SharedBuffer;\r\n    EndPtr = Ptr + BufferSize;\r\n\r\n    for (; Ptr < EndPtr; Ptr += 1)\r\n    {\r\n        if (*Ptr != 0xDE)\r\n        {\r\n            LxtLogError(\"Shared buffer not contain expected data (4), %d\", *Ptr);\r\n            Result = EFAULT;\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    Result = 0;\r\n\r\ncleanup:\r\n\r\n    if (SharedBuffer != MAP_FAILED)\r\n    {\r\n        munmap(SharedBuffer, BufferSize);\r\n    }\r\n\r\n    if (PrivateBuffer != MAP_FAILED)\r\n    {\r\n        munmap(PrivateBuffer, BufferSize);\r\n    }\r\n\r\n    if (IsChild)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MadviseVariation2(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int FileId;\r\n    int BufferSize;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileId = -1;\r\n    BufferSize = MADVISE_TEST_BUFFER_SIZE;\r\n\r\n    //\r\n    // Run the test on private memory.\r\n    //\r\n\r\n    Result = MadviseRunTest(FileId, BufferSize, 0);\r\n\r\n    if (Result != 0)\r\n    {\r\n        LxtLogError(\"Variation2 failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    if (FileId != -1)\r\n    {\r\n        close(FileId);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MadviseVariation3(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int FileId;\r\n    int BufferSize;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileId = -1;\r\n    BufferSize = MADVISE_TEST_BUFFER_SIZE;\r\n\r\n    //\r\n    // Run the test on private memory with fork.\r\n    //\r\n\r\n    Result = MadviseRunTest(FileId, BufferSize, 1);\r\n\r\n    if (Result != 0)\r\n    {\r\n        LxtLogError(\"Variation3 failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    if (FileId != -1)\r\n    {\r\n        close(FileId);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MadviseVariation4(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int FileId;\r\n    int BufferSize;\r\n    unsigned int WriteData;\r\n    int BytesWritten;\r\n    int ByteIndex;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileId = -1;\r\n    BufferSize = MADVISE_TEST_BUFFER_SIZE;\r\n\r\n    //\r\n    // Open the file and fill it to the appropriate size.\r\n    //\r\n\r\n    FileId = open(\"/data/test/madvise_test.bin\", O_RDWR | O_CREAT, S_IRWXU);\r\n\r\n    if (FileId == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    WriteData = (unsigned int)-1;\r\n\r\n    for (ByteIndex = 0; ByteIndex < BufferSize; ByteIndex += sizeof(WriteData))\r\n    {\r\n\r\n        BytesWritten = write(FileId, &WriteData, sizeof(WriteData));\r\n\r\n        if (BytesWritten != sizeof(WriteData))\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"Could not write to file! %d\", Result);\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Run the test on file-backed memory.\r\n    //\r\n\r\n    Result = MadviseRunTest(FileId, BufferSize, 0);\r\n\r\n    if (Result != 0)\r\n    {\r\n        LxtLogError(\"Variation4 failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    if (FileId != -1)\r\n    {\r\n        close(FileId);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MadviseVariation5(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int FileId;\r\n    int BufferSize;\r\n    unsigned int WriteData;\r\n    int BytesWritten;\r\n    int ByteIndex;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileId = -1;\r\n    BufferSize = MADVISE_TEST_BUFFER_SIZE;\r\n\r\n    //\r\n    // Open the file and fill it to the appropriate size.\r\n    //\r\n\r\n    FileId = open(\"/data/test/madvise_test.bin\", O_RDWR | O_CREAT, S_IRWXU);\r\n\r\n    if (FileId == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    WriteData = (unsigned int)-1;\r\n\r\n    for (ByteIndex = 0; ByteIndex < BufferSize; ByteIndex += sizeof(WriteData))\r\n    {\r\n\r\n        BytesWritten = write(FileId, &WriteData, sizeof(WriteData));\r\n\r\n        if (BytesWritten != sizeof(WriteData))\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"Could not write to file! %d\", Result);\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Run the test on file-backed memory with fork.\r\n    //\r\n\r\n    Result = MadviseRunTest(FileId, BufferSize, 1);\r\n\r\n    if (Result != 0)\r\n    {\r\n        LxtLogError(\"Variation5 failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    if (FileId != -1)\r\n    {\r\n        close(FileId);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/mprotect.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    mprotect.c\r\n\r\nAbstract:\r\n\r\n    This file contains test cases for mprotect().\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <sys/mman.h>\r\n#include <sched.h>\r\n#include <signal.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/ioctl.h>\r\n#include <sys/syscall.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n#include <time.h>\r\n\r\n#define LXT_NAME \"mprotect\"\r\n#define MAPPING_PAGE_SIZE 4096\r\n#define MAX_BUF_SIZE 256\r\n#define PROCFS_MNT \"/proc\"\r\n\r\nint MprotectMainVariation(PLXT_ARGS Args);\r\n\r\nint MunmapMainVariation(PLXT_ARGS Args);\r\n\r\nint MsyncMainVariation(PLXT_ARGS Args);\r\n\r\nint MadviseMainVariation(PLXT_ARGS Args);\r\n\r\nint MremapMainVariation(PLXT_ARGS Args);\r\n\r\nint MprotectStackVariation(PLXT_ARGS Args);\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"mprotect main variation\", MprotectMainVariation},\r\n    {\"munmap main variation\", MunmapMainVariation},\r\n    {\"msync main variation\", MsyncMainVariation},\r\n    {\"madvise main variation\", MadviseMainVariation},\r\n    {\"mremap main variation\", MremapMainVariation},\r\n    {\"mprotect stack variation\", MprotectStackVariation},\r\n};\r\n\r\nint MprotectMainVariation(PLXT_ARGS Args)\r\n{\r\n    int Result;\r\n    int ROFileDescriptor;\r\n    int RWFileDescriptor;\r\n    char FileBuffer[3 * MAPPING_PAGE_SIZE];\r\n    char* ROMapping;\r\n    char* RWMapping;\r\n    int MapSize;\r\n    char* RemappedMemory;\r\n\r\n    MapSize = sizeof(FileBuffer);\r\n\r\n    ROFileDescriptor = open(\"/data/mprotect_test.bin\", O_RDONLY | O_CREAT | O_TRUNC, S_IRWXU);\r\n\r\n    if (ROFileDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RWFileDescriptor = open(\"/data/mprotect_test.bin\", O_RDWR);\r\n\r\n    if (RWFileDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    write(RWFileDescriptor, FileBuffer, MapSize);\r\n\r\n    ROMapping = mmap(NULL, MapSize, PROT_READ, MAP_SHARED, ROFileDescriptor, 0);\r\n\r\n    if (ROMapping == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"ROMapping allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"ROMapping: %p\", ROMapping);\r\n\r\n    RWMapping = mmap(NULL, MapSize, PROT_READ, MAP_SHARED, RWFileDescriptor, 0);\r\n\r\n    if (RWMapping == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"ROMapping allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"RWMapping: %p\", RWMapping);\r\n\r\n    //\r\n    // Checking behavior of mprotect with zero size mappings.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking mprotect behavior with zero size mappings\") LxtCheckErrno(mprotect(NULL, 0, PROT_WRITE));\r\n    LxtCheckErrno(mprotect(NULL, 0, 0xDEADBEEF));\r\n    LxtCheckErrno(mprotect(RWMapping, 0, 0xDEADBEEF));\r\n    LxtCheckErrno(mprotect(ROMapping, 0, PROT_WRITE));\r\n    LxtCheckErrno(mprotect(ROMapping, 0, PROT_READ));\r\n    LxtCheckErrno(mprotect(RWMapping, 0, PROT_WRITE));\r\n    LxtCheckErrno(mprotect(RWMapping, 0, PROT_READ));\r\n\r\n    //\r\n    // Check behavior of mprotect with a bad address.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking mprotect behavior with a bad address\") LxtCheckErrnoFailure(mprotect(NULL, MAPPING_PAGE_SIZE, PROT_WRITE), ENOMEM);\r\n    LxtCheckErrnoFailure(mprotect(RWMapping + 300, MAPPING_PAGE_SIZE, PROT_WRITE), EINVAL);\r\n\r\n    //\r\n    // Check behavior with bad protection flags.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking mprotect behavior with bad protection flags\")\r\n        LxtCheckErrnoFailure(mprotect(RWMapping, MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL);\r\n\r\n    //\r\n    // Checking mprotect on non-zero size mappings.\r\n    //\r\n\r\n    Result = mprotect(RWMapping, MAPPING_PAGE_SIZE, PROT_WRITE);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Protection change failed unexpectedly! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"RWMapping protection succeeded\");\r\n\r\n    Result = mprotect(ROMapping, MAPPING_PAGE_SIZE, PROT_WRITE);\r\n\r\n    if (Result != -1)\r\n    {\r\n        LxtLogError(\"Protection change on RO file succeeded unexpectedly!\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = errno;\r\n    if (Result != EACCES)\r\n    {\r\n        LxtLogError(\"RO protection change failed but not with EACCES! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"ROMapping protection failed as expected\");\r\n\r\n    RemappedMemory = mremap(ROMapping, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Remap on RO file failed unexpectedly! %d!\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Remapping succeeded\");\r\n\r\n    RemappedMemory = mremap(RWMapping, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Remap on RO file failed unexpectedly! %d!\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Remapping succeeded\");\r\n    LxtLogPassed(\"Success!\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n\r\n    return Result;\r\n}\r\n\r\nint MunmapMainVariation(PLXT_ARGS Args)\r\n{\r\n    char FileBuffer[3 * MAPPING_PAGE_SIZE];\r\n    int MapSize;\r\n    int Result;\r\n    int RWFileDescriptor;\r\n    char* RWMapping;\r\n\r\n    //\r\n    // Create a file and write garbage data to it.\r\n    //\r\n\r\n    MapSize = sizeof(FileBuffer);\r\n    RWFileDescriptor = open(\"/data/mprotect_test.bin\", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);\r\n\r\n    if (RWFileDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    write(RWFileDescriptor, FileBuffer, MapSize);\r\n\r\n    //\r\n    // Map a memory segment that will be backed by the file.\r\n    //\r\n\r\n    RWMapping = mmap(NULL, MapSize, PROT_READ, MAP_SHARED, RWFileDescriptor, 0);\r\n\r\n    if (RWMapping == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"ROMapping allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"RWMapping: %p\", RWMapping);\r\n\r\n    //\r\n    // Check different variations of bad arguments with munmap.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking munmap with bad arguments\");\r\n    LxtCheckErrnoFailure(munmap(NULL, 0), EINVAL);\r\n    LxtCheckErrnoFailure(munmap(RWMapping, 0), EINVAL);\r\n    LxtCheckErrnoFailure(munmap(RWMapping + 300, MapSize), EINVAL);\r\n    LxtCheckErrno(munmap(NULL, MapSize));\r\n\r\n    //\r\n    // Unmap the memory mapping.\r\n    //\r\n\r\n    LxtLogInfo(\"Unmapping the mapping\");\r\n    LxtCheckErrno(munmap(RWMapping, MapSize));\r\n\r\n    //\r\n    // All tests passed at this point.\r\n    //\r\n\r\n    LxtLogPassed(\"Success!\") Result = 0;\r\n\r\nErrorExit:\r\n    if (RWFileDescriptor >= 0)\r\n    {\r\n        if (LxtClose(RWFileDescriptor) < 0)\r\n        {\r\n            LxtLogInfo(\"Failed to close test file at the end of the test\");\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MsyncMainVariation(PLXT_ARGS Args)\r\n{\r\n    char FileBuffer[3 * MAPPING_PAGE_SIZE];\r\n    int MapSize;\r\n    int Result;\r\n    int RWFileDescriptor;\r\n    char* RWMapping;\r\n\r\n    //\r\n    // Create a file and write garbage data to it.\r\n    //\r\n\r\n    MapSize = sizeof(FileBuffer);\r\n    RWFileDescriptor = open(\"/data/mprotect_test.bin\", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);\r\n\r\n    if (RWFileDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    write(RWFileDescriptor, FileBuffer, MapSize);\r\n\r\n    //\r\n    // Map a memory segment that will be backed by the file.\r\n    //\r\n\r\n    RWMapping = mmap(NULL, MapSize, PROT_READ | PROT_WRITE, MAP_SHARED, RWFileDescriptor, 0);\r\n\r\n    if (RWMapping == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"ROMapping allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"RWMapping: %p\", RWMapping);\r\n\r\n    //\r\n    // Read the first byte from the file, increment it and write it back.\r\n    //\r\n\r\n    *RWMapping = *RWMapping + 1;\r\n\r\n    //\r\n    // Checking behavior of msync with zero length.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking msync behavior with zero length\") LxtCheckErrno(msync(NULL, 0, MS_SYNC));\r\n    LxtCheckErrno(msync(RWMapping, 0, MS_SYNC));\r\n    LxtCheckErrnoFailure(msync(NULL, 0, 0xDEADBEEF), EINVAL);\r\n    LxtCheckErrnoFailure(msync(RWMapping, 0, 0xDEADBEEF), EINVAL);\r\n\r\n    //\r\n    // Check behavior of mprotect with a bad address.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking msync behavior with a bad address\") LxtCheckErrnoFailure(msync(NULL, MAPPING_PAGE_SIZE, MS_SYNC), ENOMEM);\r\n    LxtCheckErrnoFailure(msync(RWMapping + 300, MAPPING_PAGE_SIZE, MS_SYNC), EINVAL);\r\n\r\n    //\r\n    // Check behavior of msync with bad flags.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking msync behavior with bad protection flags\")\r\n        LxtCheckErrnoFailure(msync(RWMapping, MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL);\r\n\r\n    //\r\n    // Sync the changes.\r\n    //\r\n\r\n    LxtLogInfo(\"Sync the changes to the file\");\r\n    LxtCheckErrno(msync(RWMapping, MAPPING_PAGE_SIZE, MS_SYNC));\r\n\r\n    //\r\n    // All tests passed at this point.\r\n    //\r\n\r\n    LxtLogPassed(\"Success!\") Result = 0;\r\n\r\nErrorExit:\r\n    if (RWFileDescriptor >= 0)\r\n    {\r\n        if (LxtClose(RWFileDescriptor) < 0)\r\n        {\r\n            LxtLogInfo(\"Failed to close test file at the end of the test\");\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MadviseMainVariation(PLXT_ARGS Args)\r\n{\r\n    char FileBuffer[3 * MAPPING_PAGE_SIZE];\r\n    int MapSize;\r\n    int Result;\r\n    int RWFileDescriptor;\r\n    char* RWMapping;\r\n    char DummyByte;\r\n\r\n    //\r\n    // Create a file and write garbage data to it.\r\n    //\r\n\r\n    MapSize = sizeof(FileBuffer);\r\n    RWFileDescriptor = open(\"/data/mprotect_test.bin\", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);\r\n\r\n    if (RWFileDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    write(RWFileDescriptor, FileBuffer, MapSize);\r\n\r\n    //\r\n    // Map a memory segment that will be backed by the file.\r\n    //\r\n\r\n    RWMapping = mmap(NULL, MapSize, PROT_READ | PROT_WRITE, MAP_SHARED, RWFileDescriptor, 0);\r\n\r\n    if (RWMapping == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"ROMapping allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"RWMapping: %p\", RWMapping);\r\n\r\n    //\r\n    // Checking behavior of mremap with zero length.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking madvise behavior with zero length\") LxtCheckErrno(madvise(NULL, 0, MADV_RANDOM));\r\n    LxtCheckErrno(madvise(RWMapping, 0, MADV_RANDOM));\r\n    LxtCheckErrnoFailure(madvise(NULL, 0, 0xDEADBEEF), EINVAL);\r\n    LxtCheckErrnoFailure(madvise(RWMapping, 0, 0xDEADBEEF), EINVAL);\r\n\r\n    //\r\n    // Check behavior of madvise with a bad address.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking madvise behavior with a bad address\") LxtCheckErrnoFailure(madvise(NULL, MAPPING_PAGE_SIZE, MADV_RANDOM), ENOMEM);\r\n    LxtCheckErrnoFailure(madvise(RWMapping + 300, MAPPING_PAGE_SIZE, MADV_RANDOM), EINVAL);\r\n\r\n    //\r\n    // Check behavior of madvise with bad flags.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking madvise behavior with bad protection flags\")\r\n        LxtCheckErrnoFailure(madvise(RWMapping, MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL);\r\n\r\n    //\r\n    // Advise the kernel on access partners.\r\n    //\r\n\r\n    LxtLogInfo(\"Advise the kernel on access patterns.\");\r\n    LxtCheckErrno(madvise(RWMapping, MAPPING_PAGE_SIZE, MADV_RANDOM));\r\n\r\n    //\r\n    // All tests passed at this point.\r\n    //\r\n\r\n    LxtLogPassed(\"Success!\") Result = 0;\r\n\r\nErrorExit:\r\n    if (RWFileDescriptor >= 0)\r\n    {\r\n        if (LxtClose(RWFileDescriptor) < 0)\r\n        {\r\n            LxtLogInfo(\"Failed to close test file at the end of the test\");\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MremapMainVariation(PLXT_ARGS Args)\r\n{\r\n    char FileBuffer[3 * MAPPING_PAGE_SIZE];\r\n    int MapSize;\r\n    char* RemappedMemory;\r\n    int Result;\r\n    int RWFileDescriptor;\r\n    char* RWMapping;\r\n    char DummyByte;\r\n\r\n    //\r\n    // Create a file and write garbage data to it.\r\n    //\r\n\r\n    MapSize = sizeof(FileBuffer);\r\n    RWFileDescriptor = open(\"/data/mprotect_test.bin\", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);\r\n\r\n    if (RWFileDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    write(RWFileDescriptor, FileBuffer, MapSize);\r\n\r\n    //\r\n    // Map a memory segment that will be backed by the file.\r\n    //\r\n\r\n    RWMapping = mmap(NULL, MapSize, PROT_READ | PROT_WRITE, MAP_SHARED, RWFileDescriptor, 0);\r\n\r\n    if (RWMapping == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"ROMapping allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"RWMapping: %p\", RWMapping);\r\n\r\n    //\r\n    // Checking behavior of mremap with bad arguments.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking mremap behavior with bad arguments\");\r\n\r\n#pragma GCC diagnostic push\r\n#pragma GCC diagnostic ignored \"-Wpointer-to-int-cast\"\r\n\r\n    LxtCheckErrnoFailure((int)mremap(NULL, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE), EFAULT);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(RWMapping, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(RWMapping, MAPPING_PAGE_SIZE, 0, 0xDEADBEEF), EINVAL);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(NULL, 0, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE), EFAULT);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(NULL, 0, 0, 0xDEADBEEF), EINVAL);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(NULL, 0, 2 * MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(RWMapping, 0, 0, MREMAP_MAYMOVE), EINVAL);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(NULL, 0, 0, MREMAP_MAYMOVE), EINVAL);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(NULL, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, 0xDEADBEEF), EINVAL);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(RWMapping + 300, MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE), EINVAL);\r\n\r\n    LxtCheckErrnoFailure((int)mremap(RWMapping, MAPPING_PAGE_SIZE, 0, MREMAP_MAYMOVE), EINVAL);\r\n\r\n#pragma GCC diagnostic pop\r\n\r\n    //\r\n    // Success cases.\r\n    //\r\n\r\n    //\r\n    // Shrink the mapped memory.\r\n    //\r\n\r\n    RemappedMemory = (char*)mremap(RWMapping, 3 * MAPPING_PAGE_SIZE, 2 * MAPPING_PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n        LxtLogInfo(\"Mapping Failed\");\r\n        Result = errno;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"RemappedMemory = %p\", RemappedMemory);\r\n\r\n    //\r\n    // All tests passed at this point.\r\n    //\r\n\r\n    LxtLogPassed(\"Success!\") Result = 0;\r\n\r\nErrorExit:\r\n    if (RWFileDescriptor >= 0)\r\n    {\r\n        if (LxtClose(RWFileDescriptor) < 0)\r\n        {\r\n            LxtLogInfo(\"Failed to close test file at the end of the test\");\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint MprotectStackVariation(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    void* Address;\r\n    int Result;\r\n    char StackBuffer[20000];\r\n\r\n    //\r\n    // Ensure PROT_GROWSDOWN doesn't work on normal allocations.\r\n    //\r\n\r\n    Address = mmap(NULL, 4096, PROT_NONE, MAP_ANONYMOUS, -1, 0);\r\n    LxtCheckErrnoFailure(mprotect(Address, 4096, PROT_READ | PROT_WRITE | PROT_GROWSDOWN), EINVAL);\r\n    munmap(Address, 4096);\r\n\r\n    //\r\n    // Make sure PROT_GROWSDOWN works on the stack. There's not an easy\r\n    // way to validate that it actually worked without parsing /proc/self/maps...\r\n    //\r\n\r\n    StackBuffer[0] = 'x';\r\n    Address = (void*)(((long)StackBuffer + sizeof(StackBuffer)) & ~4095);\r\n    LxtCheckErrno(mprotect(Address, 4096, PROT_READ | PROT_WRITE | PROT_GROWSDOWN));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint MprotectTestEntry(int Argc, char* Argv[])\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return LXT_RESULT_SUCCESS;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/mremap.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    mremap.c\r\n\r\nAbstract:\r\n\r\n    This file contains test cases for mremap().\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <sys/mman.h>\r\n#include <sched.h>\r\n#include <signal.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/ioctl.h>\r\n#include <sys/syscall.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n#include <time.h>\r\n#include <pthread.h>\r\n\r\n#define LXT_NAME \"mremap\"\r\n\r\nvoid FillMemory(char* Memory, int Size, unsigned char Value)\r\n{\r\n    int i;\r\n\r\n    LxtLogInfo(\"FillMemory: %p, 0x%x, %u\", Memory, Size, Value);\r\n\r\n    for (i = 0; i < Size; i += 1)\r\n    {\r\n\r\n        if ((i != 0) && ((i % PAGE_SIZE) == 0))\r\n        {\r\n            Value += 1;\r\n        }\r\n\r\n        Memory[i] = Value;\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint CheckMemory(unsigned char* Memory, int Size, unsigned char Value)\r\n{\r\n    int i;\r\n\r\n    LxtLogInfo(\"CheckMemory: %p, 0x%x, %u\", Memory, Size, Value);\r\n\r\n    for (i = 0; i < Size; i += 1)\r\n    {\r\n\r\n        if ((i != 0) && ((i % PAGE_SIZE) == 0))\r\n        {\r\n            Value += 1;\r\n        }\r\n\r\n        if (Memory[i] != Value)\r\n        {\r\n            LxtLogError(\"Mismatched byte %d! Value: %d\", i, (int)Memory[i]);\r\n            break;\r\n        }\r\n    }\r\n\r\n    return i;\r\n}\r\n\r\nvoid* ThreadWorker(void* Context)\r\n{\r\n    char* Memory;\r\n    int Result;\r\n\r\n    LxtLogInfo(\"Thread started.\");\r\n\r\n    for (;;)\r\n    {\r\n\r\n        Memory = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);\r\n\r\n        if (Memory == MAP_FAILED)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"Thread memory allocation failed! %d\", Result);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        munmap(Memory, PAGE_SIZE);\r\n    }\r\n\r\nErrorExit:\r\n\r\n    return NULL;\r\n}\r\n\r\nint MremapTestEntry(int Argc, char* Argv[])\r\n{\r\n    LXT_ARGS Args;\r\n    char* PrivateMemory;\r\n    char* SharedPrivateMemory;\r\n    char* FileMemory;\r\n    char* FileMemory2;\r\n    char* RemappedMemory;\r\n    char* SpanMemory1;\r\n    char* SpanMemory2;\r\n    char* SpanMemory3;\r\n    int Result;\r\n    int FileDescriptor;\r\n    int DevZeroDescriptor;\r\n    int AllocationSize;\r\n    char FileBuffer[3 * PAGE_SIZE];\r\n    int FailByte;\r\n    int MapsDescriptor;\r\n    int BytesRead;\r\n    char* MapsBuffer;\r\n    pthread_t Thread;\r\n    int FileDescriptor2;\r\n    int FileSize;\r\n    struct timespec Time;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n\r\n    LxtLogStart(\"Start Prep:\");\r\n\r\n    Result = pthread_create(&Thread, NULL, ThreadWorker, NULL);\r\n\r\n    if (Result != 0)\r\n    {\r\n        LxtLogError(\"Thread creation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Thread created.\");\r\n\r\n    AllocationSize = 2 * PAGE_SIZE;\r\n\r\n    PrivateMemory = mmap(NULL, AllocationSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);\r\n\r\n    if (PrivateMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"PrivateMemory allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(PrivateMemory, AllocationSize, 1);\r\n\r\n    SharedPrivateMemory = mmap(NULL, AllocationSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);\r\n\r\n    if (SharedPrivateMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"SharedPrivateMemory allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(SharedPrivateMemory, AllocationSize, 10);\r\n\r\n    FileDescriptor = open(\"/data/test.bin\", O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);\r\n\r\n    if (FileDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    write(FileDescriptor, FileBuffer, sizeof(FileBuffer));\r\n\r\n    FileMemory = mmap(NULL, AllocationSize, PROT_READ | PROT_WRITE, MAP_SHARED, FileDescriptor, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"FileMemory allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(FileMemory, AllocationSize, 20);\r\n    SpanMemory3 = mmap(NULL, AllocationSize * 3, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);\r\n\r\n    if (SpanMemory3 == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"SpanMemory1 allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(SpanMemory3, AllocationSize * 3, 60);\r\n\r\n    SpanMemory1 = mmap(SpanMemory3, AllocationSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);\r\n\r\n    if (SpanMemory1 == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"SpanMemory1 allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(SpanMemory1, AllocationSize, 40);\r\n\r\n    SpanMemory2 =\r\n        mmap(SpanMemory1 + AllocationSize, AllocationSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);\r\n\r\n    if (SpanMemory2 == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"SpanMemory2 allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (SpanMemory2 != (SpanMemory1 + AllocationSize))\r\n    {\r\n        LxtLogError(\"SpanMemory2 allocation isn't in the right place!\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(SpanMemory2, AllocationSize, 50);\r\n    LxtLogPassed(\"Prep complete!\");\r\n\r\n    LxtLogStart(\"Start Test Cases:\");\r\n\r\n    //\r\n    // Case 1: Extend private memory.\r\n    //\r\n\r\n    RemappedMemory = mremap(PrivateMemory, AllocationSize, AllocationSize + PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 1 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 1 succeeded. %p -> %p\", PrivateMemory, RemappedMemory);\r\n        PrivateMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, AllocationSize, 1);\r\n\r\n        if (FailByte != AllocationSize)\r\n        {\r\n            LxtLogError(\"Case 1 memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FillMemory(RemappedMemory + AllocationSize, PAGE_SIZE, 3);\r\n    }\r\n\r\n    //\r\n    // Case 3: Extend file mapping.\r\n    //\r\n\r\n    RemappedMemory = mremap(FileMemory, AllocationSize, AllocationSize + PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 3 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 3 succeeded. %p -> %p\", FileMemory, RemappedMemory);\r\n        FileMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, AllocationSize, 20);\r\n\r\n        if (FailByte != AllocationSize)\r\n        {\r\n            LxtLogError(\"Case 3 memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FillMemory(RemappedMemory + AllocationSize, PAGE_SIZE, 22);\r\n    }\r\n\r\n    //\r\n    // Case 7: Move range that spans two private allocations (same type).\r\n    //\r\n\r\n    RemappedMemory = mremap(SpanMemory1 + PAGE_SIZE, AllocationSize, AllocationSize + (2 * PAGE_SIZE), MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 7 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 7 succeeded. %p -> %p\", SpanMemory1 + PAGE_SIZE, RemappedMemory);\r\n\r\n        FailByte = CheckMemory(RemappedMemory, PAGE_SIZE, 41);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 7 first page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + PAGE_SIZE, PAGE_SIZE, 50);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 7 second page memory doesn't match at byte %d!!! V: %d\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 8: Move range that spans two different allocations (private and shared anonymous).\r\n    //\r\n\r\n    RemappedMemory = mremap(SpanMemory2 + PAGE_SIZE, AllocationSize, AllocationSize + (2 * PAGE_SIZE), MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogPassed(\"Case 8 failed as expected. %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogError(\"Case 8 succeeded not expected! %p -> %p\", SpanMemory2 + PAGE_SIZE, RemappedMemory);\r\n\r\n        FailByte = CheckMemory(RemappedMemory, PAGE_SIZE, 51);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 8 first page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + PAGE_SIZE, PAGE_SIZE, 60);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 8 second page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 9: Shrink private allocation.\r\n    //\r\n\r\n    RemappedMemory = mremap(PrivateMemory, AllocationSize + PAGE_SIZE, AllocationSize, 0);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 9 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n        LxtLogPassed(\"Case 9 succeeded. %p -> %p\", PrivateMemory, RemappedMemory);\r\n        PrivateMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, AllocationSize, 1);\r\n\r\n        if (FailByte != AllocationSize)\r\n        {\r\n            LxtLogError(\"Case 9 memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 10: Move and extend with different protections.\r\n    //\r\n\r\n    Result = mprotect(PrivateMemory, PAGE_SIZE, PROT_READ);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 10 protection change failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RemappedMemory = mremap(PrivateMemory, AllocationSize, AllocationSize + PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogPassed(\"Case 10 failed as expected. %d\", Result);\r\n    }\r\n    else\r\n    {\r\n        LxtLogError(\"Case 10 succeeded not expected!. %p -> %p\", PrivateMemory, RemappedMemory);\r\n        PrivateMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, AllocationSize, 1);\r\n\r\n        if (FailByte != AllocationSize)\r\n        {\r\n            LxtLogError(\"Case 10 memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 11: Move discontiguous (by mapping offset not VA) file mappings.\r\n    //\r\n\r\n    FileMemory = mmap(NULL, AllocationSize, PROT_READ | PROT_WRITE, MAP_SHARED, FileDescriptor, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 11 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(FileMemory, AllocationSize, 70);\r\n\r\n    RemappedMemory = mmap(FileMemory + PAGE_SIZE, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, FileDescriptor, 0x2000);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 11 map2 failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RemappedMemory = mremap(FileMemory, AllocationSize, AllocationSize + (2 * PAGE_SIZE), MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogPassed(\"Case 11 failed as expected. %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogError(\"Case 11 succeeded not expected!. %p -> %p\", FileMemory, RemappedMemory);\r\n\r\n        FailByte = CheckMemory(RemappedMemory, AllocationSize, 70);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 11 memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 12: Extend within existing VAD (section).\r\n    //\r\n\r\n    FileMemory = mmap(NULL, 3 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FileDescriptor, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 12 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(FileMemory, 3 * PAGE_SIZE, 80);\r\n\r\n    RemappedMemory = mremap(FileMemory, PAGE_SIZE, 3 * PAGE_SIZE, 0);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogPassed(\"Case 12 failed as expected. %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogError(\"Case 12 succeeded not expected! %p -> %p\", FileMemory, RemappedMemory);\r\n\r\n        FailByte = CheckMemory(RemappedMemory, 3 * PAGE_SIZE, 80);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 12 memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 13: Extend within existing VAD (private).\r\n    //\r\n\r\n    PrivateMemory = mmap(NULL, PAGE_SIZE * 3, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);\r\n\r\n    if (PrivateMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 13 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(PrivateMemory, 3 * PAGE_SIZE, 90);\r\n\r\n    RemappedMemory = mremap(PrivateMemory, PAGE_SIZE, 3 * PAGE_SIZE, 0);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogPassed(\"Case 13 failed as expected. %d\", Result);\r\n    }\r\n    else\r\n    {\r\n        LxtLogError(\"Case 13 succeeded not expected! %p -> %p\", FileMemory, RemappedMemory);\r\n    }\r\n\r\n    //\r\n    // Case 14: Split VAD during remap shrinking.\r\n    //\r\n\r\n    RemappedMemory = mremap(PrivateMemory, 2 * PAGE_SIZE, PAGE_SIZE, 0);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 14 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 14 succeeded. %p -> %p\", PrivateMemory, RemappedMemory);\r\n        PrivateMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, PAGE_SIZE, 90);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 14 first page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + (2 * PAGE_SIZE), PAGE_SIZE, 92);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 14 third page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 15: Partially committed and no-access private memory.\r\n    //\r\n\r\n    PrivateMemory = mmap(NULL, PAGE_SIZE * 3, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);\r\n\r\n    if (PrivateMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 15 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(PrivateMemory, PAGE_SIZE, 100);\r\n    FillMemory(PrivateMemory + (2 * PAGE_SIZE), PAGE_SIZE, 102);\r\n\r\n    Result = mprotect(PrivateMemory, PAGE_SIZE * 3, PROT_NONE);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 15 protection change failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RemappedMemory = mremap(PrivateMemory, 3 * PAGE_SIZE, 33 * PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 15 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 15 succeeded. %p -> %p\", PrivateMemory, RemappedMemory);\r\n        PrivateMemory = RemappedMemory;\r\n\r\n        Result = mprotect(PrivateMemory, (33 * PAGE_SIZE), PROT_READ | PROT_WRITE);\r\n\r\n        if (Result == -1)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"Case 15 protection change failed! %d\", Result);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory, PAGE_SIZE, 100);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 15 first page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + (2 * PAGE_SIZE), PAGE_SIZE, 102);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 15 third page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FillMemory(PrivateMemory + (3 * PAGE_SIZE), (30 * PAGE_SIZE), 103);\r\n    }\r\n\r\n    //\r\n    // Case 16: Remap private and shared file mapping of the same file.\r\n    //\r\n\r\n    FileMemory = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FileDescriptor, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 16 first page map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RemappedMemory = mmap(FileMemory + PAGE_SIZE, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, FileDescriptor, PAGE_SIZE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 16 second page map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RemappedMemory = mremap(FileMemory, 2 * PAGE_SIZE, 3 * PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogPassed(\"Case 16 failed as expected. %d\", Result);\r\n    }\r\n    else\r\n    {\r\n        LxtLogError(\"Case 16 succeeded not expected! %p -> %p\", FileMemory, RemappedMemory);\r\n    }\r\n\r\n    //\r\n    // Case 17: Remap private section view with no pages copy-on-written.\r\n    //\r\n\r\n    write(FileDescriptor, FileBuffer, PAGE_SIZE);\r\n\r\n    FileMemory = mmap(NULL, 4 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FileDescriptor, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 17 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(FileMemory, 4 * PAGE_SIZE, 110);\r\n\r\n    FileMemory = mmap(FileMemory, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, FileDescriptor, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 17 private map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RemappedMemory = mremap(FileMemory, 2 * PAGE_SIZE, 4 * PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 17 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 17 succeeded. %p -> %p\", FileMemory, RemappedMemory);\r\n        FileMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, 4 * PAGE_SIZE, 110);\r\n\r\n        if (FailByte != (4 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 17 memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 18: Remap private section view with some pages copy-on-written.\r\n    //\r\n\r\n    FillMemory((FileMemory + PAGE_SIZE), PAGE_SIZE, 120);\r\n\r\n    RemappedMemory = mremap(FileMemory, 3 * PAGE_SIZE, 4 * PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 18 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 18 succeeded. %p -> %p\", FileMemory, RemappedMemory);\r\n        FileMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, PAGE_SIZE, 110);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 18 first page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + PAGE_SIZE, PAGE_SIZE, 120);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 18 second page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + (2 * PAGE_SIZE), (2 * PAGE_SIZE), 112);\r\n\r\n        if (FailByte != (2 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 18 third/fourth page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 19: Large region of copy-on-written pages with same protection.\r\n    //\r\n\r\n    FileSize = (512 * 1024);\r\n\r\n    FileDescriptor2 = open(\"/data/test2.bin\", O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);\r\n\r\n    if (FileDescriptor2 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (FailByte = 0; FailByte < (FileSize / PAGE_SIZE); FailByte += 1)\r\n    {\r\n        write(FileDescriptor2, FileBuffer, PAGE_SIZE);\r\n    }\r\n\r\n    FileMemory = mmap(NULL, FileSize, PROT_READ | PROT_WRITE, MAP_SHARED, FileDescriptor2, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 19 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(FileMemory, FileSize, 0);\r\n\r\n    FileMemory = mmap(FileMemory, FileSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, FileDescriptor2, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 19 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(FileMemory + (3 * PAGE_SIZE), FileSize - (6 * PAGE_SIZE), 130);\r\n\r\n    Result = mprotect(FileMemory, FileSize, PROT_READ);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 19 protection change failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RemappedMemory = mremap(FileMemory, FileSize - PAGE_SIZE, FileSize, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 19 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 19 succeeded. %p -> %p\", FileMemory, RemappedMemory);\r\n        FileMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, (3 * PAGE_SIZE), 0);\r\n\r\n        if (FailByte != (3 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 19 first pages memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + (3 * PAGE_SIZE), FileSize - (6 * PAGE_SIZE), 130);\r\n\r\n        if (FailByte != FileSize - (6 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 19 middle pages memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + FileSize - (3 * PAGE_SIZE), (3 * PAGE_SIZE), ((FileSize / PAGE_SIZE) - 3));\r\n\r\n        if (FailByte != (3 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 19 third pages memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 20: Remap an inconsistent DONT_FORK range.\r\n    //\r\n\r\n    PrivateMemory = mmap(NULL, PAGE_SIZE * 3, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);\r\n\r\n    if (PrivateMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 20 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(PrivateMemory, PAGE_SIZE * 3, 140);\r\n\r\n    Result = madvise(PrivateMemory, 1 * PAGE_SIZE, MADV_DONTFORK);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 20 madvise failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RemappedMemory = mremap(PrivateMemory, PAGE_SIZE * 2, PAGE_SIZE * 3, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogPassed(\"Case 20 failed as expected. %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogError(\"Case 20 succeeded not expected! %p -> %p\", PrivateMemory, RemappedMemory);\r\n        PrivateMemory = RemappedMemory;\r\n    }\r\n\r\n    //\r\n    // Case 21: Remap a DONT_FORK range.\r\n    //\r\n\r\n    Result = madvise(PrivateMemory, 3 * PAGE_SIZE, MADV_DONTFORK);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 21 madvise failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RemappedMemory = mremap(PrivateMemory, PAGE_SIZE * 2, PAGE_SIZE * 3, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 21 failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 21 succeeded. %p -> %p\", PrivateMemory, RemappedMemory);\r\n        PrivateMemory = RemappedMemory;\r\n    }\r\n\r\n    //\r\n    // \\Dev\\Zero tests.\r\n    //\r\n\r\n    DevZeroDescriptor = open(\"/dev/zero\", O_RDWR);\r\n\r\n    if (DevZeroDescriptor == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not open \\\\dev\\\\zero device! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(FileBuffer, 1, PAGE_SIZE);\r\n\r\n    Result = write(DevZeroDescriptor, FileBuffer, PAGE_SIZE);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Failed to write to \\\\dev\\\\zero! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n        LxtLogInfo(\"Write to \\\\dev\\\\zero result: %d\", Result);\r\n    }\r\n\r\n    memset(FileBuffer, 5, PAGE_SIZE);\r\n\r\n    Result = read(DevZeroDescriptor, FileBuffer, PAGE_SIZE);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Failed to read from \\\\dev\\\\zero! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        FailByte = CheckMemory(FileBuffer, PAGE_SIZE, 0);\r\n\r\n        if (FailByte == PAGE_SIZE)\r\n        {\r\n            LxtLogInfo(\"Read all zeroes from \\\\dev\\\\zero as expected.\");\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 22: Remap private \\dev\\zero mapping.\r\n    //\r\n\r\n    FileMemory = mmap(NULL, 1 * 1024 * 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE, DevZeroDescriptor, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 22 first map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(FileMemory, 50 * PAGE_SIZE, 1);\r\n\r\n    RemappedMemory = mremap(FileMemory, 100 * PAGE_SIZE, 10 * 1024 * 1024, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 22 remap failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 22 succeeded. %p -> %p\", FileMemory, RemappedMemory);\r\n        FileMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, (50 * PAGE_SIZE), 1);\r\n\r\n        if (FailByte != (50 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 22 first pages memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + (50 * PAGE_SIZE), PAGE_SIZE, 0);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 22 second pages not zero!\");\r\n        }\r\n\r\n        FillMemory(RemappedMemory, 10 * 1024 * 1024, 1);\r\n\r\n        FailByte = CheckMemory(FileMemory, 10 * 1024 * 1024, 1);\r\n\r\n        if (FailByte != (10 * 1024 * 1024))\r\n        {\r\n            LxtLogError(\"Case 22 memory doesn't match!\");\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 23: Second private mapping of \\dev\\zero of same file descriptor\r\n    //          does not share memory with the first private mapping.\r\n    //\r\n\r\n    FileMemory2 = mmap(NULL, 2 * 1024 * 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE, DevZeroDescriptor, 0);\r\n\r\n    if (FileMemory2 == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 23 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FailByte = CheckMemory(FileMemory2, PAGE_SIZE, 0);\r\n\r\n    if (FailByte != PAGE_SIZE)\r\n    {\r\n        LxtLogError(\"Case 23 expected second private mapping to be full of zeroes!\");\r\n    }\r\n\r\n    //\r\n    // Case 24: Remap shared \\dev\\zero mapping.\r\n    //\r\n\r\n    FileMemory = mmap(NULL, 1 * 1024 * 1024, PROT_READ | PROT_WRITE, MAP_SHARED, DevZeroDescriptor, 0);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 24 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(FileMemory, 50 * PAGE_SIZE, 1);\r\n\r\n    RemappedMemory = mremap(FileMemory, 100 * PAGE_SIZE, 1 * 1024 * 1024, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 24 remap failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 24 succeeded. %p -> %p\", FileMemory, RemappedMemory);\r\n        FileMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, (50 * PAGE_SIZE), 1);\r\n\r\n        if (FailByte != (50 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 24 first pages memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + (50 * PAGE_SIZE), PAGE_SIZE, 0);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 24 second pages not zero!\");\r\n        }\r\n\r\n        FailByte = CheckMemory(RemappedMemory + (100 * PAGE_SIZE), PAGE_SIZE, 0);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 24 third pages not zero!\");\r\n        }\r\n\r\n        FillMemory(RemappedMemory, 1 * 1024 * 1024, 1);\r\n\r\n        FailByte = CheckMemory(FileMemory, 1 * 1024 * 1024, 1);\r\n\r\n        if (FailByte != (1 * 1024 * 1024))\r\n        {\r\n            LxtLogError(\"Case 24 memory doesn't match!\");\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 25: Remap shared \\dev\\zero mapping smaller.\r\n    //\r\n\r\n    RemappedMemory = mremap(FileMemory, 1 * 1024 * 1024, PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 25 remap failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 25 succeeded. %p -> %p\", FileMemory, RemappedMemory);\r\n        FileMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, PAGE_SIZE, 1);\r\n\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 25 page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Case 26: Map \\dev\\zero with a section offset.\r\n    //\r\n\r\n    FileMemory = mmap(NULL, 20 * 1024 * 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE, DevZeroDescriptor, 0x5000);\r\n\r\n    if (FileMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 26 first map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n    FillMemory(FileMemory, (20 * 1024 * 1024), 1);\r\n\r\n    LxtLogPassed(\"Case 26 succeeded.\");\r\n\r\n    //\r\n    // Case 27: Remap shared anonymous mapping.\r\n    //\r\n\r\n    SharedPrivateMemory = mmap(NULL, 3 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);\r\n\r\n    if (SharedPrivateMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 27 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(SharedPrivateMemory, (2 * PAGE_SIZE), 10);\r\n\r\n    RemappedMemory = mremap(SharedPrivateMemory, 2 * PAGE_SIZE, 3 * PAGE_SIZE, MREMAP_MAYMOVE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 27 remap failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 27 succeeded. %p -> %p\", SharedPrivateMemory, RemappedMemory);\r\n        SharedPrivateMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, (2 * PAGE_SIZE), 10);\r\n\r\n        if (FailByte != (2 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 27 page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FillMemory(RemappedMemory + (2 * PAGE_SIZE), PAGE_SIZE, 12);\r\n    }\r\n\r\n    //\r\n    // Case 28: Remap while chopping off tail.\r\n    //\r\n\r\n    SharedPrivateMemory = mmap(NULL, 10 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);\r\n\r\n    if (SharedPrivateMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 28 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(SharedPrivateMemory, (10 * PAGE_SIZE), 10);\r\n\r\n    RemappedMemory = (char*)LxtMremap(\r\n        SharedPrivateMemory, 3 * PAGE_SIZE, 20 * PAGE_SIZE, MREMAP_MAYMOVE | MREMAP_FIXED, SharedPrivateMemory + (5 * PAGE_SIZE));\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 28 remap failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 28 succeeded. %p -> %p\", SharedPrivateMemory, RemappedMemory);\r\n        SharedPrivateMemory = RemappedMemory;\r\n\r\n        FailByte = CheckMemory(RemappedMemory, (3 * PAGE_SIZE), 10);\r\n\r\n        if (FailByte != (3 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 28 page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FillMemory(RemappedMemory + (3 * PAGE_SIZE), (17 * PAGE_SIZE), 13);\r\n    }\r\n\r\n    //\r\n    // Case 29: Remap while splitting source VAD.\r\n    //\r\n\r\n    SharedPrivateMemory = mmap(NULL, 10 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);\r\n\r\n    if (SharedPrivateMemory == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 29 map failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FillMemory(SharedPrivateMemory, (10 * PAGE_SIZE), 10);\r\n\r\n    RemappedMemory = (char*)LxtMremap(\r\n        SharedPrivateMemory + (5 * PAGE_SIZE), 4 * PAGE_SIZE, 2 * PAGE_SIZE, MREMAP_MAYMOVE | MREMAP_FIXED, SharedPrivateMemory + PAGE_SIZE);\r\n\r\n    if (RemappedMemory == MAP_FAILED)\r\n    {\r\n\r\n        Result = errno;\r\n        LxtLogError(\"Case 29 remap failed! %d\", Result);\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogPassed(\"Case 29 succeeded. %p -> %p\", SharedPrivateMemory, RemappedMemory);\r\n\r\n        FailByte = CheckMemory(SharedPrivateMemory, PAGE_SIZE, 10);\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 29 first page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(SharedPrivateMemory + PAGE_SIZE, (2 * PAGE_SIZE), 15);\r\n        if (FailByte != (2 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 29 second pages memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(SharedPrivateMemory + (3 * PAGE_SIZE), (2 * PAGE_SIZE), 13);\r\n        if (FailByte != (2 * PAGE_SIZE))\r\n        {\r\n            LxtLogError(\"Case 29 third page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n\r\n        FailByte = CheckMemory(SharedPrivateMemory + (9 * PAGE_SIZE), PAGE_SIZE, 19);\r\n        if (FailByte != PAGE_SIZE)\r\n        {\r\n            LxtLogError(\"Case 29 last page memory doesn't match at byte %d!!!\", FailByte);\r\n        }\r\n    }\r\n\r\n    fflush(NULL);\r\n\r\n    Thread = fork();\r\n\r\n    if (Thread == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Case 20 fork failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (Thread == 0)\r\n    {\r\n        LxtLogInfo(\"Child\");\r\n    }\r\n    else\r\n    {\r\n        usleep(1000 * 500);\r\n        LxtLogInfo(\"Parent slept\");\r\n    }\r\n\r\n    MapsBuffer = mmap(NULL, (10 * 1024 * 1024), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);\r\n\r\n    if (MapsBuffer == MAP_FAILED)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"MapsBuffer allocation failed! %d\", Result);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Maps (%p):\", MapsBuffer);\r\n\r\n    if (Thread == 0)\r\n    {\r\n        LxtLogPassed(\"Child Done!\");\r\n    }\r\n    else\r\n    {\r\n        LxtLogPassed(\"Parent Done!\");\r\n    }\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/namespace.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Namespace.c\r\n\r\nAbstract:\r\n\r\n    This file is a Namespace test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <fcntl.h>\r\n#include <unistd.h>\r\n#include <sys/mount.h>\r\n#include <sys/sysmacros.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/socket.h>\r\n#include <fcntl.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <stddef.h>\r\n#include <dirent.h>\r\n#include <ctype.h>\r\n#include <sys/mman.h>\r\n#include <sys/utsname.h>\r\n#include <sys/ipc.h>\r\n#include <sys/shm.h>\r\n#include <sys/ioctl.h>\r\n#include <sys/wait.h>\r\n#include <net/if.h>\r\n#include <bits/local_lim.h>\r\n#include <linux/capability.h>\r\n#include <linux/futex.h>\r\n#include <linux/net_namespace.h>\r\n#include <linux/netlink.h>\r\n#include <linux/rtnetlink.h>\r\n#include <linux/reboot.h>\r\n\r\n#define LXT_NAME \"Namespace\"\r\n#define SOCKET_LOOPBACK_IF_NAME \"lo\"\r\n\r\n#define LxtReboot(_magic1, _magic2, _cmd, _arg) (syscall(SYS_reboot, (_magic1), (_magic2), (_cmd), (_arg)))\r\n\r\nint NamespaceNetwork(PLXT_ARGS Args);\r\n\r\nint NamespaceNetworkProcfs(PLXT_ARGS Args);\r\n\r\nint NamespaceNetworkProcfsCheckFile(char* File, int ExpectedLineCount);\r\n\r\nint NamespaceSetNs(PLXT_ARGS Args);\r\n\r\nint NamespaceStat(PLXT_ARGS Args);\r\n\r\nint NamespacePid(PLXT_ARGS Args);\r\n\r\nint NamespacePidCheckProcPidStatStatusFiles(char* Dir);\r\n\r\nvoid* NamespacePidChildPThread(void* Args);\r\n\r\nint NamespacePidGetProcPidFolderCount(char* Dir, int* Count);\r\n\r\nint NamespacePidBasic(int Level);\r\n\r\nint NamespacePidBasicChild(void* Param);\r\n\r\nint NamespacePidParentPThread(void);\r\n\r\nint NamespaceUts(PLXT_ARGS Args);\r\n\r\nint NamespaceIpc(PLXT_ARGS Args);\r\n\r\nint NamespaceCloneInvalid(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"NamespaceSetNs\", NamespaceSetNs},\r\n    {\"NamespaceStat\", NamespaceStat},\r\n    {\"Namespace UTS\", NamespaceUts},\r\n    {\"Namespace PID\", NamespacePid},\r\n    {\"Namespace Network\", NamespaceNetwork},\r\n    {\"Namespace Network - reading /proc/<pid>/net\", NamespaceNetworkProcfs},\r\n    {\"Namespace IPC\", NamespaceIpc},\r\n    {\"Namespace Clone - invalid namespace flags\", NamespaceCloneInvalid}};\r\n\r\ntypedef struct _LXT_NAMESPACE_DATA\r\n{\r\n    const char* Name;\r\n    int NsType;\r\n} LXT_NAMESPACE_DATA, *PLXT_NAMESPACE_DATA;\r\n\r\nstatic const LXT_NAMESPACE_DATA g_LxtNamespaces[] = {\r\n    {\"ipc\", CLONE_NEWIPC}, {\"mnt\", CLONE_NEWNS}, {\"net\", CLONE_NEWNET}, {\"pid\", CLONE_NEWPID}, {\"user\", CLONE_NEWUSER}, {\"uts\", CLONE_NEWUTS}};\r\n\r\nint NamespaceTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nvoid NamespaceSetNsChild(int NsFd)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int Result;\r\n\r\n    //\r\n    // Drop the CAP_SYS_ADMIN capability.\r\n    //\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n    LxtCheckErrno(LxtCapGet(&CapHeader, CapData)) CapData[CAP_TO_INDEX(CAP_SYS_ADMIN)].effective &= ~CAP_TO_MASK(CAP_SYS_ADMIN);\r\n    LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n    //\r\n    // Try to setns without CAP_SYS_ADMIN.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(setns(NsFd, 0), EPERM);\r\n\r\nErrorExit:\r\n    _exit(Result);\r\n}\r\n\r\nint NamespaceSetNs(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[32];\r\n    pid_t ChildPid;\r\n    int Index;\r\n    int NsFd;\r\n    int Result;\r\n\r\n    NsFd = -1;\r\n\r\n    //\r\n    // Pass invalid parameters to setns\r\n    //\r\n\r\n    LxtCheckErrnoFailure(setns(0, CLONE_NEWPID), EINVAL);\r\n    LxtCheckErrnoFailure(setns(0, -1), EINVAL);\r\n    LxtCheckErrnoFailure(setns(-1, 0), EBADF);\r\n\r\n    //\r\n    // Pass the self fds to sets.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_LxtNamespaces); ++Index)\r\n    {\r\n        snprintf(Buffer, sizeof(Buffer), \"/proc/self/ns/%s\", g_LxtNamespaces[Index].Name);\r\n        printf(\"%s\\n\", Buffer);\r\n        LxtCheckErrno(NsFd = open(Buffer, O_RDONLY));\r\n        if (g_LxtNamespaces[Index].NsType != CLONE_NEWUSER)\r\n        {\r\n            LxtCheckErrno(setns(NsFd, 0));\r\n            LxtCheckErrno(setns(NsFd, g_LxtNamespaces[Index].NsType));\r\n            LxtCheckErrno(ChildPid = fork());\r\n            if (ChildPid == 0)\r\n            {\r\n                NamespaceSetNsChild(NsFd);\r\n            }\r\n\r\n            LxtWaitPidPoll(ChildPid, 0);\r\n        }\r\n        else\r\n        {\r\n            LxtCheckErrnoFailure(setns(NsFd, 0), EINVAL);\r\n            LxtCheckErrnoFailure(setns(NsFd, g_LxtNamespaces[Index].NsType), EINVAL);\r\n        }\r\n\r\n        LxtCheckErrnoFailure(setns(NsFd, g_LxtNamespaces[(Index + 1) % LXT_COUNT_OF(g_LxtNamespaces)].NsType), EINVAL);\r\n        LxtCheckErrnoFailure(setns(NsFd, -1), EINVAL);\r\n        LxtClose(NsFd);\r\n        NsFd = -1;\r\n    }\r\n\r\nErrorExit:\r\n    if (NsFd != -1)\r\n    {\r\n        LxtClose(NsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint NamespaceStat(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    char Buffer[32];\r\n    int Index;\r\n    int NsFd;\r\n    int Result;\r\n    struct stat StatData;\r\n\r\n    NsFd = -1;\r\n\r\n    //\r\n    // stat each namespace file and check the result.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_LxtNamespaces); ++Index)\r\n    {\r\n        snprintf(Buffer, sizeof(Buffer), \"/proc/self/ns/%s\", g_LxtNamespaces[Index].Name);\r\n        printf(\"%s\\n\", Buffer);\r\n        LxtCheckErrno(NsFd = open(Buffer, O_RDONLY));\r\n        LxtCheckErrno(fstat(NsFd, &StatData));\r\n\r\n        //\r\n        // TODO: st_dev is reported as 0 for files.\r\n        //\r\n\r\n        LxtCheckEqual(major(StatData.st_dev), 0, \"%d\");\r\n        LxtCheckNotEqual(StatData.st_ino, 0, \"%d\");\r\n        LxtCheckNotEqual(StatData.st_mode, 0, \"%d\");\r\n        LxtCheckEqual(StatData.st_nlink, 1, \"%d\");\r\n        LxtCheckEqual(StatData.st_uid, 0, \"%d\");\r\n        LxtCheckEqual(StatData.st_gid, 0, \"%d\");\r\n        LxtCheckEqual(StatData.st_rdev, 0, \"%d\");\r\n        LxtCheckEqual(StatData.st_size, 0, \"%d\");\r\n        LxtCheckEqual(StatData.st_blksize, 4096, \"%d\");\r\n        LxtCheckEqual(StatData.st_blocks, 0, \"%d\");\r\n        LxtClose(NsFd);\r\n        NsFd = -1;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (NsFd != -1)\r\n    {\r\n        LxtClose(NsFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VerifyUtsData(struct utsname* ExpectedValues)\r\n\r\n{\r\n\r\n    struct utsname ActualValues;\r\n    int Fd;\r\n    int Length;\r\n    char ProcfsDomain[HOST_NAME_MAX];\r\n    char ProcfsHost[HOST_NAME_MAX];\r\n    int Result;\r\n\r\n    memset(&ActualValues, 0, sizeof(ActualValues));\r\n    LxtCheckErrno(uname(&ActualValues));\r\n    LxtCheckMemoryEqual(ExpectedValues, &ActualValues, sizeof(ActualValues));\r\n    LxtCheckErrno(Fd = open(\"/proc/sys/kernel/hostname\", O_RDONLY));\r\n    LxtCheckErrno(Length = read(Fd, ProcfsHost, sizeof(ProcfsHost)));\r\n    ProcfsHost[Length - 1] = 0;\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckStringEqual(ExpectedValues->nodename, ProcfsHost);\r\n    LxtCheckErrno(Fd = open(\"/proc/sys/kernel/domainname\", O_RDONLY));\r\n    LxtCheckErrno(Length = read(Fd, ProcfsDomain, sizeof(ProcfsDomain)));\r\n    ProcfsDomain[Length - 1] = 0;\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckStringEqual(ExpectedValues->domainname, ProcfsDomain);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\n#define CHILD_HOST \"childmachine\"\r\n#define CHILD_DOMAIN \"childdomain\"\r\n\r\nint NamespaceUtsChild(struct utsname* ParentValues)\r\n\r\n{\r\n\r\n    int Fd;\r\n    char Path[64];\r\n    pid_t Ppid;\r\n    int Result;\r\n    struct utsname UtsBuffer;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check the uts namespace behavior for before\\after unshare.\r\n    //\r\n\r\n    memcpy(&UtsBuffer, ParentValues, sizeof(UtsBuffer));\r\n    LxtCheckResult(VerifyUtsData(&UtsBuffer));\r\n    LxtCheckErrno(unshare(CLONE_NEWUTS));\r\n    LxtCheckResult(VerifyUtsData(&UtsBuffer));\r\n    LxtCheckErrno(sethostname(CHILD_HOST, sizeof(CHILD_HOST)));\r\n    LxtCheckErrno(setdomainname(CHILD_DOMAIN, sizeof(CHILD_DOMAIN)));\r\n    memset(UtsBuffer.nodename, 0, sizeof(UtsBuffer.nodename));\r\n    memcpy(UtsBuffer.nodename, CHILD_HOST, sizeof(CHILD_HOST));\r\n    memset(UtsBuffer.domainname, 0, sizeof(UtsBuffer.domainname));\r\n    memcpy(UtsBuffer.domainname, CHILD_DOMAIN, sizeof(CHILD_DOMAIN));\r\n    LxtCheckResult(VerifyUtsData(&UtsBuffer));\r\n\r\n    //\r\n    // Check the uts namespace behavior after switching back to the parent\r\n    // uts namespace.\r\n    //\r\n\r\n    Ppid = getppid();\r\n    sprintf(Path, \"/proc/%d/ns/uts\", Ppid);\r\n    LxtCheckErrno(Fd = open(Path, O_RDONLY));\r\n    LxtCheckErrno(setns(Fd, CLONE_NEWUTS));\r\n    memcpy(&UtsBuffer, ParentValues, sizeof(UtsBuffer));\r\n    LxtCheckResult(VerifyUtsData(&UtsBuffer));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid NamespaceUtsFork(struct utsname* ParentValues)\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    Result = NamespaceUtsChild(ParentValues);\r\n    _exit(Result);\r\n}\r\n\r\nvoid* NamespaceUtsThread(void* Args)\r\n\r\n{\r\n\r\n    struct utsname* ParentValues;\r\n    int Result;\r\n\r\n    ParentValues = Args;\r\n    Result = NamespaceUtsChild(Args);\r\n    pthread_exit(&Result);\r\n}\r\n\r\nint NamespaceUts(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    char* Name;\r\n    char NameBuffer[HOST_NAME_MAX];\r\n    int Result;\r\n    pthread_t ThreadId = {0};\r\n    struct utsname UtsBuffer;\r\n\r\n    //\r\n    // Check the UTS behavior for fork()\r\n    //\r\n\r\n    memset(&UtsBuffer, 0, sizeof(UtsBuffer));\r\n    LxtCheckErrno(uname(&UtsBuffer));\r\n    LxtCheckResult(VerifyUtsData(&UtsBuffer));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        NamespaceUtsFork(&UtsBuffer);\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n    LxtCheckResult(VerifyUtsData(&UtsBuffer));\r\n\r\n    //\r\n    // Check the UTS behavior for a pthread.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(pthread_create(&ThreadId, NULL, NamespaceUtsThread, &UtsBuffer));\r\n\r\n    pthread_join(ThreadId, NULL);\r\n    LxtCheckResult(VerifyUtsData(&UtsBuffer));\r\n\r\n    //\r\n    // Test behavior for NULL names.\r\n    //\r\n\r\n    Name = NULL;\r\n    LxtCheckErrno(sethostname(Name, 0));\r\n    LxtCheckErrno(setdomainname(Name, 0));\r\n    memset(NameBuffer, 1, sizeof(NameBuffer));\r\n    LxtCheckErrno(gethostname(NameBuffer, sizeof(NameBuffer)));\r\n    LxtCheckEqual(NameBuffer[0], 0, \"%c\");\r\n    memset(NameBuffer, 1, sizeof(NameBuffer));\r\n    LxtCheckErrno(getdomainname(NameBuffer, sizeof(NameBuffer)));\r\n    LxtCheckEqual(NameBuffer[0], 0, \"%c\");\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint NamespacePidCheckProcPidStatStatusFiles(char* Dir)\r\n\r\n{\r\n\r\n    char Command[80];\r\n    int Gid;\r\n    int IntValue;\r\n    char Line[40];\r\n    char Name[20];\r\n    int Pid;\r\n    int Ppid;\r\n    int Result;\r\n    char State[10];\r\n    FILE* StatFile;\r\n    char StatFileName[30];\r\n    FILE* StatusFile;\r\n    char StatusFileName[30];\r\n    int Tid;\r\n    int Tgid;\r\n\r\n    //\r\n    // Check the stat file in ProcFs.\r\n    //\r\n\r\n    sprintf(StatFileName, \"%s/%s\", Dir, \"/stat\");\r\n    StatFile = fopen(StatFileName, \"r\");\r\n    LxtCheckNotEqual(StatFile, NULL, \"%p\");\r\n    LxtCheckEqual(fscanf(StatFile, \"%d %s %s %d %d\", &Tid, Command, State, &Ppid, &Gid), 5, \"%d\");\r\n    LxtLogInfo(\"%d %s %s %d %d\", Tid, Command, State, Ppid, Gid);\r\n    LxtCheckEqual(Tid, 1, \"%d\");\r\n    LxtCheckEqual(Ppid, 0, \"%d\");\r\n    LxtCheckEqual(Gid, 0, \"%d\");\r\n\r\n    //\r\n    // Check the status file in ProcFs.\r\n    //\r\n\r\n    sprintf(StatusFileName, \"%s/%s\", Dir, \"/status\");\r\n    StatusFile = fopen(StatusFileName, \"r\");\r\n    LxtCheckNotEqual(StatusFile, NULL, \"%p\");\r\n    Tgid = -1;\r\n    Pid = -1;\r\n    Ppid = -1;\r\n    while (fgets(Line, LXT_COUNT_OF(Line), StatusFile) != NULL)\r\n    {\r\n        if (sscanf(Line, \"%s %d\", Name, &IntValue) == 2)\r\n        {\r\n            if (strcmp(Name, \"Tgid:\") == 0)\r\n            {\r\n                Tgid = IntValue;\r\n            }\r\n            else if (strcmp(Name, \"Pid:\") == 0)\r\n            {\r\n                Pid = IntValue;\r\n            }\r\n            else if (strcmp(Name, \"PPid:\") == 0)\r\n            {\r\n                Ppid = IntValue;\r\n            }\r\n        }\r\n    }\r\n\r\n    LxtCheckEqual(Tgid, 1, \"%d\");\r\n    LxtCheckEqual(Pid, 1, \"%d\");\r\n    LxtCheckEqual(Ppid, 0, \"%d\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    //\r\n    // Intentionally do not close the files. These files are leaked to ensure\r\n    // the private procfs instance is cleaned up correctly when there are\r\n    // file descriptors that need to be closed during thread group end.\r\n    //\r\n\r\n    return Result;\r\n}\r\n\r\nvoid* NamespacePidChildPThread(void* Args)\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtLogError(\"Child pthread ran unexpectedly in new namespace\");\r\n    Result = -1;\r\n    pthread_exit(&Result);\r\n}\r\n\r\nint NamespacePidGetProcPidFolderCount(char* Dir, int* Count)\r\n\r\n{\r\n\r\n    int CountLocal;\r\n    struct dirent* DirEnt;\r\n    DIR* Fd;\r\n    char FullPath[257];\r\n    int Result;\r\n    struct stat StatBuffer;\r\n\r\n    //\r\n    // Get the number of /proc/<pid> folders.\r\n    //\r\n\r\n    Fd = opendir(Dir);\r\n    if (Fd == NULL)\r\n    {\r\n        LxtLogError(\"opendir failed, errno: %d (%s)\", errno, strerror(errno));\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    CountLocal = 0;\r\n    while ((DirEnt = readdir(Fd)) != NULL)\r\n    {\r\n        sprintf(FullPath, \"%s/%s\", Dir, DirEnt->d_name);\r\n        LxtLogInfo(\"Calling stat() on proc folder %s\", FullPath);\r\n        LxtCheckErrnoZeroSuccess(stat(FullPath, &StatBuffer));\r\n\r\n        //\r\n        // Must be a directory, and its name must start with a digit.\r\n        //\r\n\r\n        if ((StatBuffer.st_mode & S_IFDIR) != S_IFDIR)\r\n        {\r\n            continue;\r\n        }\r\n\r\n        if (isdigit(DirEnt->d_name[0]) == FALSE)\r\n        {\r\n            continue;\r\n        }\r\n\r\n        CountLocal += 1;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != NULL)\r\n    {\r\n        closedir(Fd);\r\n    }\r\n\r\n    *Count = CountLocal;\r\n    return Result;\r\n}\r\n\r\nint PidBasicPipesA[2];\r\nint PidBasicPipesB[2];\r\n\r\n#define NAMESPACE_PID_BASIC_TOKEN (0x12345678)\r\n\r\n#define PID_BASIC_PARENT_PIPE_READ (PidBasicPipesA[0])\r\n#define PID_BASIC_PARENT_PIPE_WRITE (PidBasicPipesB[1])\r\n#define PID_BASIC_CHILD_PIPE_READ (PidBasicPipesB[0])\r\n#define PID_BASIC_CHILD_PIPE_WRITE (PidBasicPipesA[1])\r\n\r\nint NamespacePidBasic(int Level)\r\n\r\n{\r\n\r\n    pid_t ChildId;\r\n    LXT_CLONE_ARGS CloneArgs;\r\n    pid_t CurrentId;\r\n    int Result;\r\n    int Size;\r\n    int Token;\r\n\r\n    //\r\n    // Create a set of pipes to synchronize with the child PID namespaces.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(pipe(PidBasicPipesA));\r\n    LxtCheckErrnoZeroSuccess(pipe(PidBasicPipesB));\r\n\r\n    //\r\n    // Clone a child into a new PID namespace.\r\n    //\r\n\r\n    LxtCheckResult(LxtClone(NamespacePidBasicChild, &Level, CLONE_NEWPID | SIGCHLD, &CloneArgs));\r\n\r\n    //\r\n    // Close the child pipes.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(close(PID_BASIC_CHILD_PIPE_READ));\r\n    PID_BASIC_CHILD_PIPE_READ = -1;\r\n    LxtCheckErrnoZeroSuccess(close(PID_BASIC_CHILD_PIPE_WRITE));\r\n    PID_BASIC_CHILD_PIPE_WRITE = -1;\r\n\r\n    //\r\n    // Wait for the entire hierarchy to be created.\r\n    //\r\n\r\n    LxtCheckErrno(Size = read(PID_BASIC_PARENT_PIPE_READ, &Token, sizeof(Token)));\r\n\r\n    LxtCheckEqual(Size, sizeof(Token), \"%d\");\r\n    LxtCheckEqual(Token, NAMESPACE_PID_BASIC_TOKEN, \"%x\");\r\n\r\n    //\r\n    // Validate the PGID of the child.\r\n    //\r\n\r\n    LxtCheckErrno(CurrentId = getpgid(0));\r\n    LxtCheckErrno(ChildId = getpgid(CloneArgs.CloneId));\r\n    LxtCheckEqual(CurrentId, ChildId, \"%d\");\r\n\r\n    //\r\n    // Validate the SID of the child.\r\n    //\r\n\r\n    LxtCheckErrno(CurrentId = getsid(0));\r\n    LxtCheckErrno(ChildId = getsid(CloneArgs.CloneId));\r\n    LxtCheckEqual(CurrentId, ChildId, \"%d\");\r\n\r\n    //\r\n    // Notify the hierarchy to exit and wait.\r\n    //\r\n\r\n    LxtCheckErrno(Size = write(PID_BASIC_PARENT_PIPE_WRITE, &Token, sizeof(Token)));\r\n\r\n    LxtCheckEqual(Size, sizeof(Token), \"%d\");\r\n    LxtCheckResult(LxtWaitPidPoll(CloneArgs.CloneId, 0));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint NamespacePidBasicChild(void* Param)\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    LXT_CLONE_ARGS CloneArgs;\r\n    int Level;\r\n    pid_t Pgid;\r\n    pid_t Pid;\r\n    int PidFolderCount;\r\n    pid_t Ppid;\r\n    int Result;\r\n    pid_t Sid;\r\n    int Size;\r\n    pid_t Tid;\r\n    int Token;\r\n\r\n    Level = *((int*)Param);\r\n\r\n    //\r\n    // Close the parent pipes.\r\n    //\r\n\r\n    if (Level == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(close(PID_BASIC_PARENT_PIPE_READ));\r\n        PID_BASIC_PARENT_PIPE_READ = -1;\r\n        LxtCheckErrnoZeroSuccess(close(PID_BASIC_PARENT_PIPE_WRITE));\r\n        PID_BASIC_PARENT_PIPE_WRITE = -1;\r\n    }\r\n\r\n    usleep(1000 * 80);\r\n\r\n    //\r\n    // Validate that the first thread/threadgroup in a PID namespace has PID 1.\r\n    //\r\n\r\n    LxtCheckErrno(Tid = gettid());\r\n    LxtCheckEqual(Tid, 1, \"%d\");\r\n    LxtCheckErrno(Pid = getpid());\r\n    LxtCheckEqual(Pid, 1, \"%d\");\r\n\r\n    //\r\n    // Validate that the first thread in a PID namespace cannot see its current\r\n    // process group, session or parent.\r\n    //\r\n\r\n    LxtCheckErrno(Pgid = getpgid(0));\r\n    LxtCheckEqual(Pgid, 0, \"%d\");\r\n    LxtCheckErrno(Sid = getsid(0));\r\n    LxtCheckEqual(Sid, 0, \"%d\");\r\n    LxtCheckErrno(Ppid = getppid());\r\n    LxtCheckEqual(Ppid, 0, \"%d\");\r\n\r\n    //\r\n    // Run the following in its own mount namespace and mark all mounts in the\r\n    // new namespace private so changes cannot propagate to the rest of the\r\n    // system.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNS));\r\n    LxtCheckErrnoZeroSuccess(mount(NULL, \"/\", NULL, MS_PRIVATE | MS_REC, NULL));\r\n\r\n    //\r\n    // Re-mount /proc. This version of /proc should not contain any PIDs from\r\n    // the parent PID namespace.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(NULL, \"/proc\", \"proc\", 0, NULL));\r\n\r\n    //\r\n    // Do some basic validation.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(access(\"/proc\", R_OK));\r\n    LxtCheckErrnoZeroSuccess(access(\"/proc/1\", R_OK));\r\n    LxtCheckErrnoZeroSuccess(access(\"/proc/1/cmdline\", R_OK));\r\n    LxtCheckErrnoZeroSuccess(access(\"/proc/self\", R_OK));\r\n    LxtCheckErrnoZeroSuccess(access(\"/proc/self/cmdline\", R_OK));\r\n    LxtCheckErrnoFailure(access(\"/proc/0\", R_OK), ENOENT);\r\n    LxtCheckErrnoFailure(access(\"/proc/1234567890\", R_OK), ENOENT);\r\n\r\n    //\r\n    // Check that there is only 1 /proc/<pid> folder.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking /proc/<pid> folders, before clone, nested level %d\", Level);\r\n    LxtCheckResult(NamespacePidGetProcPidFolderCount(\"/proc\", &PidFolderCount));\r\n    LxtCheckEqual(PidFolderCount, 1, \"%d\");\r\n\r\n    //\r\n    // Check the /proc/1/stat, /proc/1/status, /proc/1/task/1/stat\r\n    // and /proc/1/task/1/status files.\r\n    //\r\n\r\n    NamespacePidCheckProcPidStatStatusFiles(\"/proc/1/\");\r\n    NamespacePidCheckProcPidStatStatusFiles(\"/proc/1/task/1\");\r\n\r\n    //\r\n    // Test nested PID namespaces.\r\n    //\r\n\r\n    if (Level < 3)\r\n    {\r\n        Level += 1;\r\n        LxtCheckResult(LxtClone(NamespacePidBasicChild, &Level, CLONE_NEWPID | SIGCHLD, &CloneArgs));\r\n\r\n        //\r\n        // After the clone, check that there are now at least 2 /proc/<pid>\r\n        // folders.\r\n        //\r\n        // N.B. The cloned process will recursively create more cloned PID\r\n        //      namespaces, causing more PIDs to appear under this /proc mount.\r\n        //\r\n\r\n        LxtLogInfo(\"Checking /proc/<pid> folders, after clone, nested level %d\", Level);\r\n        LxtCheckResult(NamespacePidGetProcPidFolderCount(\"/proc\", &PidFolderCount));\r\n        LxtCheckGreaterOrEqual(PidFolderCount, 2, \"%d\");\r\n\r\n        //\r\n        // Check the /proc/1/stat, /proc/1/status, /proc/1/task/1/stat\r\n        // and /proc/1/task/1/status files.\r\n        //\r\n\r\n        NamespacePidCheckProcPidStatStatusFiles(\"/proc/1/\");\r\n        NamespacePidCheckProcPidStatStatusFiles(\"/proc/1/task/1\");\r\n\r\n        //\r\n        // Wait for the child to exit.\r\n        //\r\n\r\n        LxtCheckResult(LxtWaitPidPoll(CloneArgs.CloneId, 0));\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Signal to the test that the hierarchy is created.\r\n        //\r\n\r\n        Token = NAMESPACE_PID_BASIC_TOKEN;\r\n        LxtCheckErrno(Size = write(PID_BASIC_CHILD_PIPE_WRITE, &Token, sizeof(Token)));\r\n\r\n        LxtCheckEqual(Size, sizeof(Token), \"%d\");\r\n\r\n        //\r\n        // Wait for the notification to exit.\r\n        //\r\n\r\n        LxtCheckErrno(Size = read(PID_BASIC_CHILD_PIPE_READ, &Token, sizeof(Token)));\r\n\r\n        LxtCheckEqual(Size, sizeof(Token), \"%d\");\r\n        LxtCheckEqual(Token, NAMESPACE_PID_BASIC_TOKEN, \"%x\");\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint NamespacePidParentPThread(void)\r\n\r\n{\r\n\r\n    pthread_t ThreadId = {0};\r\n    int Result;\r\n\r\n    //\r\n    // Create a new child PID namespace and check that pthread creation fails.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWPID));\r\n    LxtCheckEqual(pthread_create(&ThreadId, NULL, NamespacePidChildPThread, NULL), EINVAL, \"%d\");\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint NamespacePidTerminate(int Level, int* Ready)\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Index;\r\n    int Result;\r\n\r\n    LxtLogInfo(\"[%d/%d] PID namespace leader\", Level, getpid());\r\n\r\n    //\r\n    // Create 10 child threadgroups that loop sleeping.\r\n    //\r\n\r\n    for (Index = 0; Index < 10; Index += 1)\r\n    {\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            for (;;)\r\n            {\r\n                sleep(-1);\r\n            }\r\n        }\r\n\r\n        LxtLogInfo(\"[%d/%d] PID namespace sleeper %d\", Level, getpid(), ChildPid);\r\n    }\r\n\r\n    //\r\n    // Create 3 levels of nested PID namespaces and then signal the ready futex.\r\n    //\r\n\r\n    if (Level < 3)\r\n    {\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            LxtLogInfo(\"[%d/%d] PID namespace trampoline\", Level, getpid());\r\n            LxtCheckErrno(unshare(CLONE_NEWPID));\r\n            LxtCheckErrno(ChildPid = fork());\r\n            if (ChildPid == 0)\r\n            {\r\n                _exit(NamespacePidTerminate(Level + 1, Ready));\r\n            }\r\n\r\n            _exit(0);\r\n        }\r\n    }\r\n    else\r\n    {\r\n        LxtLogInfo(\"[%d/%d] Signaling ready futex...\", Level, getpid());\r\n        *Ready = 1;\r\n        LxtCheckErrno(LxtFutex(Ready, FUTEX_WAKE, 1, NULL, NULL, 0));\r\n    }\r\n\r\n    //\r\n    // Sleep.\r\n    //\r\n\r\n    for (;;)\r\n    {\r\n        sleep(-1);\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint NamespacePidTestTerminate()\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    void* MapResult;\r\n    int* Ready;\r\n    int Result;\r\n    struct timespec Time;\r\n    int WaitStatus;\r\n\r\n    //\r\n    // Create the ready futex.\r\n    //\r\n\r\n    LxtCheckMapErrno(Ready = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0));\r\n    *Ready = 0;\r\n\r\n    //\r\n    // Create the top-level PID namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWPID));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(NamespacePidTerminate(0, Ready));\r\n    }\r\n\r\n    //\r\n    // Wait for the ready futex.\r\n    //\r\n\r\n    Time.tv_sec = 10;\r\n    Time.tv_nsec = 0;\r\n    LxtLogInfo(\"[%d] Waiting for all child threadgroups and namespaces to be created...\", getpid());\r\n    while (*Ready == 0)\r\n    {\r\n        Result = LxtFutex(Ready, FUTEX_WAIT, 0, &Time, NULL, 0);\r\n        if ((Result == -1) && (errno != EAGAIN) && (errno != EINTR))\r\n        {\r\n            LxtCheckErrno(Result);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Terminate the top-level PID namespace and wait on the leader.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(kill(ChildPid, SIGKILL));\r\n    LxtWaitPidPoll(ChildPid, SIGKILL);\r\n\r\n    //\r\n    // Sleep and make sure that there are no other waits pending.\r\n    //\r\n\r\n    sleep(1);\r\n    LxtCheckErrnoFailure(waitpid(-1, &WaitStatus, WNOHANG), ECHILD);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint NamespacePidTestReboot()\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t Pid;\r\n    int* Ready;\r\n    int Result;\r\n    int WaitStatus;\r\n\r\n    Pid = getpid();\r\n\r\n    //\r\n    // Create a child process of init that calls reboot.\r\n    //\r\n    // N.B. Init is terminated with SIGINT and the signal handler is not\r\n    //      invoked.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWPID));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrnoFailure(LxtReboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_CAD_ON, NULL), EINVAL);\r\n        LxtCheckErrnoFailure(LxtReboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_CAD_OFF, NULL), EINVAL);\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGINT, SA_SIGINFO));\r\n        LxtLogInfo(\"Forking...\");\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            LxtCheckResult(LxtReboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL));\r\n            _exit(0);\r\n        }\r\n\r\n        LxtLogInfo(\"Waiting...\");\r\n        LxtSignalWait();\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n        _exit(0);\r\n    }\r\n\r\n    //\r\n    // Wait for the reboot signal.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, SIGINT));\r\n\r\n    //\r\n    // Sleep and make sure that there are no other waits pending.\r\n    //\r\n\r\n    sleep(1);\r\n    LxtCheckErrnoFailure(waitpid(-1, &WaitStatus, WNOHANG), ECHILD);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Pid != getpid())\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint NamespacePid(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Result;\r\n\r\n    //\r\n    // Check basic PID namespace behavior.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(NamespacePidBasic(0));\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    //\r\n    // Check the pid namespace behavior for a pthread.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(NamespacePidParentPThread());\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    //\r\n    // Check the pid namespace behavior for signals, termination and waits.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(NamespacePidTestTerminate());\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    //\r\n    // Check the pid namespace behavior for reboot.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(NamespacePidTestReboot());\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint NamespaceNetworkGetNSID(int* NetworkNamespaceId)\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct rtattr* Attribute;\r\n    struct sockaddr_nl BindAddress;\r\n    struct nlmsgerr* Error;\r\n    struct nlmsghdr* Header;\r\n    int Index;\r\n    int NetworkNamespaceFd;\r\n    int ReceiveResult;\r\n    int RemainingLength;\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct rtgenmsg msg __attribute__((aligned(NLMSG_ALIGNTO)));\r\n        struct\r\n        {\r\n            struct rtattr rta __attribute__((aligned(RTA_ALIGNTO)));\r\n            int val __attribute__((aligned(RTA_ALIGNTO)));\r\n        } data[5];\r\n    } Request, Response;\r\n    int Result;\r\n    struct rtgenmsg* RtGenMsg;\r\n    int Socket;\r\n\r\n    //\r\n    // Open network namespace file descriptor.\r\n    //\r\n\r\n    LxtCheckErrno(NetworkNamespaceFd = open(\"/proc/self/ns/net\", O_RDONLY));\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETNSID request.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_SPACE(sizeof(Request.msg)) + RTA_SPACE(sizeof(int));\r\n\r\n    Request.nlh.nlmsg_type = RTM_GETNSID;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtgen_family = AF_UNSPEC;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    Request.data[0].rta.rta_len = RTA_LENGTH(sizeof(int));\r\n    Request.data[0].rta.rta_type = NETNSA_FD;\r\n    Request.data[0].val = NetworkNamespaceFd;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    memset(&Response, 0, sizeof(Response));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, &Response, sizeof(Response), 0, NULL, 0));\r\n\r\n    LxtCheckTrue(NLMSG_OK(&Response.nlh, ReceiveResult));\r\n    LxtCheckEqual(Response.nlh.nlmsg_type, RTM_NEWNSID, \"%hd\");\r\n    LxtCheckTrue(Response.nlh.nlmsg_len >= NLMSG_LENGTH(sizeof(Response.msg)));\r\n\r\n    Attribute = &Response.data[0].rta;\r\n    RemainingLength = Response.nlh.nlmsg_len - NLMSG_LENGTH(sizeof(Response.msg));\r\n\r\n    LxtCheckTrue(RTA_OK(Attribute, RemainingLength));\r\n    *NetworkNamespaceId = *(int*)RTA_DATA(Attribute);\r\n    Attribute = RTA_NEXT(Attribute, RemainingLength);\r\n    LxtCheckTrue(RTA_OK(Attribute, RemainingLength) == FALSE);\r\n    LxtCheckTrue(NLMSG_OK(NLMSG_NEXT(&Response.nlh, ReceiveResult), ReceiveResult) == FALSE);\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    if (NetworkNamespaceFd > 0)\r\n    {\r\n        close(NetworkNamespaceFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint NamespaceNetwork(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int NetworkNamespaceId;\r\n    int OriginalNetworkNamespaceFd;\r\n    int Result;\r\n    DIR* SysClassNetDirectory;\r\n\r\n    //\r\n    // Open file descriptor of default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Verify default namespace ID is set.\r\n    //\r\n\r\n    LxtCheckErrno(NamespaceNetworkGetNSID(&NetworkNamespaceId));\r\n    LxtCheckEqual(NetworkNamespaceId, -1, \"%d\");\r\n\r\n    //\r\n    // Switch to a new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Verify default namespace ID is set.\r\n    //\r\n\r\n    LxtCheckErrno(NamespaceNetworkGetNSID(&NetworkNamespaceId));\r\n    LxtCheckEqual(NetworkNamespaceId, -1, \"%d\");\r\n\r\n    //\r\n    // Switch back to original network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (OriginalNetworkNamespaceFd >= 0)\r\n    {\r\n        LxtClose(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint NamespaceNetworkProcfs(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    char FileName[50];\r\n    struct ifreq InterfaceUpRequest;\r\n    int Result;\r\n    int Socket;\r\n\r\n    //\r\n    // Create a child process and switch it to a new network namespace.\r\n    // From the parent, read the child's /proc/<pid>/net entries and verify\r\n    // that those entries reflect the network state of the child.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(unshare(CLONE_NEWNET));\r\n\r\n        //\r\n        // Bring the loopback up so that it shows up in procfs. (Needed for native\r\n        // Ubuntu only - on WSL the loopback is automatically UP on namespace creation.)\r\n        //\r\n\r\n        LxtCheckErrno(Socket = socket(AF_UNIX, SOCK_DGRAM, 0));\r\n        memset(&InterfaceUpRequest, 0, sizeof(InterfaceUpRequest));\r\n        strncpy(InterfaceUpRequest.ifr_name, SOCKET_LOOPBACK_IF_NAME, sizeof(InterfaceUpRequest.ifr_name) - 1);\r\n\r\n        usleep(1000 * 100);\r\n        LxtCheckErrnoZeroSuccess(ioctl(Socket, SIOCGIFFLAGS, &InterfaceUpRequest));\r\n        LxtCheckEqual(InterfaceUpRequest.ifr_flags & IFF_LOOPBACK, IFF_LOOPBACK, \"%d\");\r\n        InterfaceUpRequest.ifr_flags |= IFF_UP;\r\n        LxtCheckErrnoZeroSuccess(ioctl(Socket, SIOCSIFFLAGS, &InterfaceUpRequest));\r\n        close(Socket);\r\n\r\n        //\r\n        // Keep the child alive so that the parent can examine its /proc/<pid>/net entries.\r\n        //\r\n\r\n        while (1)\r\n            ;\r\n        exit(0);\r\n    }\r\n\r\n    //\r\n    // N.B. The sleep is because it can take some time for the lxcore cache to get\r\n    //      the new network interface notification.\r\n    //\r\n\r\n    usleep(1000 * 200);\r\n\r\n    //\r\n    // Check the /proc/<pid>/net/dev file. This file should have 3 lines\r\n    // (2 lines header and one line for lo).\r\n    //\r\n\r\n    sprintf(FileName, \"/proc/%d/net/dev\", ChildPid);\r\n    LxtCheckResult(NamespaceNetworkProcfsCheckFile(FileName, 3));\r\n\r\n    //\r\n    // Check the /proc/<pid>/net/if_inet6 file. This file should have 1 line,\r\n    // for lo.\r\n    //\r\n\r\n    sprintf(FileName, \"/proc/%d/net/if_inet6\", ChildPid);\r\n    LxtCheckResult(NamespaceNetworkProcfsCheckFile(FileName, 1));\r\n\r\n    //\r\n    // Check the /proc/<pid>/net/route file. This file has one line on Ubuntu\r\n    // (1 line header and no routing entries) and multiple lines on WSL. Therefore,\r\n    // don't check the line count for this file.\r\n    //\r\n\r\n    sprintf(FileName, \"/proc/%d/net/route\", ChildPid);\r\n    LxtCheckResult(NamespaceNetworkProcfsCheckFile(FileName, -1));\r\n\r\n    //\r\n    // All done, the child should die now.\r\n    //\r\n\r\n    kill(ChildPid, SIGKILL);\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, SIGKILL));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint NamespaceNetworkProcfsCheckFile(char* FileName, int ExpectedLineCount)\r\n\r\n{\r\n\r\n    char Buffer[200];\r\n    FILE* File;\r\n    int LineCount;\r\n    int Result;\r\n\r\n    //\r\n    // Open the file up and check how many lines it has.\r\n    //\r\n\r\n    LineCount = 0;\r\n    File = fopen(FileName, \"r\");\r\n    LxtCheckTrue(File != NULL);\r\n    while (fgets(Buffer, LXT_COUNT_OF(Buffer), File) != NULL)\r\n    {\r\n        LineCount += 1;\r\n\r\n        //\r\n        // The following strings should not be seen, since the only network\r\n        // interface in the file should be lo.\r\n        //\r\n\r\n        LxtCheckTrue(strstr(Buffer, \"eth\") == NULL);\r\n        LxtCheckTrue(strstr(Buffer, \"wlan\") == NULL);\r\n        LxtCheckTrue(strstr(Buffer, \"wifi\") == NULL);\r\n        LxtCheckTrue(strstr(Buffer, \"und\") == NULL);\r\n    }\r\n\r\n    if (ExpectedLineCount != -1)\r\n    {\r\n        LxtCheckEqual(LineCount, ExpectedLineCount, \"%d\");\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (File != NULL)\r\n    {\r\n        fclose(File);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\ntypedef struct _LXT_NAMESPACE_IPC_DATA\r\n{\r\n    int Id;\r\n    void* Address;\r\n} LXT_NAMESPACE_IPC_DATA, *PLXT_NAMESPACE_IPC_DATA;\r\n\r\nint NamespaceIpcChild(PLXT_NAMESPACE_IPC_DATA Data)\r\n\r\n{\r\n\r\n    int Fd;\r\n    char Path[64];\r\n    pid_t Ppid;\r\n    int Result;\r\n    struct shmid_ds Stat;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check the ipc namespace behavior for before\\after unshare.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmCtl(Data->Id, IPC_STAT, &Stat));\r\n    LxtCheckErrno(unshare(CLONE_NEWIPC));\r\n    LxtCheckErrnoFailure(LxtShmCtl(Data->Id, IPC_STAT, &Stat), EINVAL);\r\n\r\n    //\r\n    // shmdt should still succeed in the new namespace.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmDt(Data->Address));\r\n    //\r\n    // Check the ipc namespace behavior after switching back to the parent\r\n    // ipc namespace.\r\n    //\r\n\r\n    Ppid = getppid();\r\n    sprintf(Path, \"/proc/%d/ns/ipc\", Ppid);\r\n    LxtCheckErrno(Fd = open(Path, O_RDONLY));\r\n    LxtCheckErrno(setns(Fd, CLONE_NEWIPC));\r\n    LxtCheckErrno(LxtShmCtl(Data->Id, IPC_STAT, &Stat));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid NamespaceIpcFork(PLXT_NAMESPACE_IPC_DATA Data)\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    Result = NamespaceIpcChild(Data);\r\n    _exit(Result);\r\n}\r\n\r\nvoid* NamespaceIpcThread(void* Args)\r\n\r\n{\r\n\r\n    PLXT_NAMESPACE_IPC_DATA Data;\r\n    int Result;\r\n\r\n    Data = Args;\r\n    Result = NamespaceIpcChild(Data);\r\n    pthread_exit(&Result);\r\n}\r\n\r\nint NamespaceIpc(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    LXT_NAMESPACE_IPC_DATA Data;\r\n    void* MapResult;\r\n    int Result;\r\n    struct shmid_ds Stat;\r\n    pthread_t ThreadId = {0};\r\n\r\n    Data.Id = -1;\r\n    Data.Address = NULL;\r\n\r\n    //\r\n    // Check the IPC behavior for fork()\r\n    //\r\n\r\n    LxtCheckErrno(Data.Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0));\r\n    LxtCheckMapErrno(Data.Address = LxtShmAt(Data.Id, NULL, 0));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        NamespaceIpcFork(&Data);\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n    LxtCheckErrno(LxtShmCtl(Data.Id, IPC_STAT, &Stat));\r\n\r\n    //\r\n    // Verify shmdt succeeds and create a new mapping.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmDt(Data.Address));\r\n    LxtCheckMapErrno(Data.Address = LxtShmAt(Data.Id, NULL, 0));\r\n\r\n    //\r\n    // Check the IPC behavior for a pthread.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(pthread_create(&ThreadId, NULL, NamespaceIpcThread, &Data));\r\n\r\n    pthread_join(ThreadId, NULL);\r\n    LxtCheckErrno(LxtShmCtl(Data.Id, IPC_STAT, &Stat));\r\n\r\n    //\r\n    // Verify shmdt fails (was unmapped by the pthread).\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtShmDt(Data.Address), EINVAL);\r\n    Data.Address = NULL;\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Data.Id != -1)\r\n    {\r\n        LxtShmCtl(Data.Id, IPC_RMID, NULL);\r\n    }\r\n\r\n    if (Data.Address != NULL)\r\n    {\r\n        LxtShmDt(Data.Address);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint NamespaceCloneInvalidChild(unsigned long Flags, PLXT_PIPE Pipe)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine is the child process for WaitPidVariationCloneParent.\r\n\r\nArguments:\r\n\r\n    None..\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t GrandChildParent;\r\n    pid_t ChildParent;\r\n    int Result;\r\n    int WaitPidStatus;\r\n\r\n    Result = 0;\r\n\r\n    //\r\n    // Create a child process with the requested CLONE_PARENT and other flag.\r\n    //\r\n    // The new process should not be reported as a child.\r\n    //\r\n\r\n    ChildParent = getppid();\r\n    LxtLogInfo(\"ChildParent %d\", ChildParent);\r\n    LxtCheckResult(ChildPid = LxtCloneSyscall(Flags | SIGCHLD, NULL, NULL, NULL, NULL));\r\n    if (ChildPid == 0)\r\n    {\r\n        GrandChildParent = getppid();\r\n        if ((Flags & CLONE_NEWPID) != 0)\r\n        {\r\n            LxtCheckEqual(0, GrandChildParent, \"%d\");\r\n        }\r\n        else\r\n        {\r\n            LxtCheckEqual(ChildParent, GrandChildParent, \"%d\");\r\n        }\r\n\r\n        LxtLogInfo(\"Grand child %d exiting\", LxtGetTid());\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrnoFailure(waitpid(ChildPid, &WaitPidStatus, 0), ECHILD);\r\n        LxtCheckResult(write(Pipe->Write, &ChildPid, sizeof(ChildPid)));\r\n        LxtLogInfo(\"Child %d exiting\", LxtGetTid());\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    _exit(Result);\r\n}\r\n\r\nint NamespaceCloneInvalid(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    LXT_PIPE Pipe = {-1, -1};\r\n    int PipeData;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n    LxtCheckResult(LxtCreatePipe(&Pipe));\r\n\r\n    //\r\n    // CLONE_NEWPID and CLONE_NEWUSER can't be specified with CLONE_THREAD.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(ChildPid = LxtCloneSyscall(CLONE_NEWPID | CLONE_THREAD, NULL, NULL, NULL, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(ChildPid = LxtCloneSyscall(CLONE_NEWUSER | CLONE_THREAD, NULL, NULL, NULL, NULL), EINVAL);\r\n\r\n    //\r\n    // CLONE_NEWPID and CLONE_NEWUSER can be specified with CLONE_PARENT\r\n    // (incorrect man pages).\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        NamespaceCloneInvalidChild(CLONE_NEWPID | CLONE_PARENT, &Pipe);\r\n    }\r\n\r\n    LxtCheckResult(read(Pipe.Read, &PipeData, sizeof(PipeData)));\r\n    LxtCheckResult(LxtWaitPidPoll(PipeData, 0));\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // CLONE_NEWIPC and CLONE_SYSVSEM are not allowed together.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(ChildPid = LxtCloneSyscall(CLONE_NEWIPC | CLONE_SYSVSEM, NULL, NULL, NULL, NULL), EINVAL);\r\n\r\n    //\r\n    // TODO_LX: Enable the variation below when CLONE_NEWUSER is supported on\r\n    //          WSL.\r\n    //\r\n\r\n    /*\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0) {\r\n            NamespaceCloneInvalidChild(CLONE_NEWUSER | CLONE_PARENT, &Pipe);\r\n        }\r\n\r\n        LxtCheckResult(read(Pipe.Read, &PipeData, sizeof(PipeData)));\r\n        LxtCheckResult(LxtWaitPidPoll(PipeData, 0));\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    */\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    LxtClosePipe(&Pipe);\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/netlink.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    netlink.c\r\n\r\nAbstract:\r\n\r\n    Linux socket test for the AF_NETLINK family.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/types.h>\r\n#include <sys/socket.h>\r\n#include <sys/un.h>\r\n#include <sys/ioctl.h>\r\n#include <net/if.h>\r\n#include <net/if_arp.h>\r\n#include <arpa/inet.h>\r\n#include <netinet/in.h>\r\n#include <netinet/ip.h>\r\n#include <netinet/udp.h>\r\n#include <netinet/tcp.h>\r\n#include <linux/icmp.h>\r\n#include <netinet/icmp6.h>\r\n#include <net/if.h>\r\n#include <netdb.h>\r\n#include <fcntl.h>\r\n#include <time.h>\r\n#include <pthread.h>\r\n#include <poll.h>\r\n#include <sys/epoll.h>\r\n#include <sys/time.h>\r\n#include <sys/prctl.h>\r\n#include <asm/types.h>\r\n#include <linux/netlink.h>\r\n#include <linux/rtnetlink.h>\r\n#include \"common.h\"\r\n\r\n#define LXT_NAME \"Netlink\"\r\n\r\n#define ATTRIBUTE_DUMP_BUFFER_SIZE 60\r\n\r\n#define min(a, b) (((a) < (b)) ? (a) : (b))\r\n\r\n#define SOCKET_LOOPBACK_IF_NAME \"lo\"\r\n\r\n#define NLMSG_TAIL(nmsg) ((struct rtattr*)(((void*)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))\r\n\r\nconst int MessageTypes[] = {\r\n    RTM_DELADDR, RTM_DELLINK, RTM_DELROUTE, RTM_GETADDR, RTM_GETLINK, RTM_GETNSID, RTM_GETROUTE, RTM_NEWADDR, RTM_NEWLINK, RTM_NEWROUTE, RTM_SETLINK};\r\n\r\nconst int SupportedFamily[] = {NETLINK_ROUTE};\r\nconst int SupportedType[] = {SOCK_DGRAM, SOCK_RAW};\r\n\r\ntypedef struct _NetlinkRecvmmsgBlockedReaderParams\r\n{\r\n    int Socket;\r\n    int Option;\r\n} NetlinkRecvmmsgBlockedReaderParams;\r\n\r\ntypedef struct _BindChildThreadReturn\r\n{\r\n    pid_t nl_pid;\r\n} BindChildThreadReturn;\r\n\r\nLXT_VARIATION_HANDLER SocketNetlinkOpenClose;\r\nLXT_VARIATION_HANDLER SocketNetlinkBasic;\r\nLXT_VARIATION_HANDLER SocketNetlinkBind;\r\nLXT_VARIATION_HANDLER SocketNetlinkBindThread;\r\nLXT_VARIATION_HANDLER SocketNetlinkSendReceive;\r\nLXT_VARIATION_HANDLER SocketNetlinkSendReceiveOverflow;\r\nLXT_VARIATION_HANDLER SocketNetlinkBlockedReader;\r\nLXT_VARIATION_HANDLER SocketNetlinkEpoll;\r\nLXT_VARIATION_HANDLER SocketNetlinkRecvmmsg;\r\nLXT_VARIATION_HANDLER SocketNetlinkSendBadMessage;\r\nLXT_VARIATION_HANDLER SocketNetlinkRouteGetAddr;\r\nLXT_VARIATION_HANDLER SocketNetlinkRouteGetLink;\r\nLXT_VARIATION_HANDLER SocketNetlinkRouteGetRouteBestRoute;\r\nLXT_VARIATION_HANDLER SocketNetlinkRouteGetRouteDump;\r\nLXT_VARIATION_HANDLER SocketNetlinkRouteNewDelAddress;\r\nLXT_VARIATION_HANDLER SocketNetlinkRouteNewDelRoute;\r\nLXT_VARIATION_HANDLER SocketNetlinkSoPasscred;\r\n\r\nvoid* SocketNetlinkBindThreadChild(void* Arg);\r\n\r\nbool SocketNetlinkIsIpv6Configured();\r\n\r\nvoid* SocketNetlinkRecvmmsgBlockedReaderThread(void* Arg);\r\n\r\nvoid SocketNetlinkRouteDumpAttributeData(char* Buffer, int BufferSize, struct rtattr* Attribute);\r\n\r\nint SocketNetlinkRouteGetLinkCheckResponse(int Socket, int OnlyOneLoopback);\r\n\r\nint SocketNetlinkRouteGetRouteBestRouteCheckResponse(int Socket, int CheckGateway);\r\n\r\nint SocketNetlinkGetLoopbackIndex();\r\n\r\nint SocketNetlinkCheckResponseError(void* ReceiveBuffer, int ReceiveResult, int Error);\r\n\r\nint SocketNetlinkRouteAddAttribute(struct nlmsghdr* Msghdr, int MessageSize, int AttributeType, void* AttributeData, int AttributeSize);\r\n\r\nvoid SocketNetlinkRouteAddAddressAttributes(struct nlmsghdr* Msghdr, int MessageSize, struct in_addr* AddressIpv4);\r\n\r\nvoid SocketNetlinkRouteAddRouteAttributes(\r\n    struct nlmsghdr* Msghdr, int MessageSize, struct in_addr* DestinationIpv4, struct in_addr* GatewayIpv4, int InterfaceIndex);\r\n\r\nint SocketNetlinkSendAndWaitForExpectedError(int Socket, void* Request, int RequestSize, int ExpectedError);\r\n\r\nint SocketNetlinkSetAndVerifySocketOptionTimeout(int Socket, int SocketOption, int Usec);\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Open, close\", SocketNetlinkOpenClose},\r\n    {\"Basic netlink operations\", SocketNetlinkBasic},\r\n    {\"bind, getsockname (with fork)\", SocketNetlinkBind},\r\n    {\"bind, getsockname (with pthread_create)\", SocketNetlinkBindThread},\r\n    {\"Send and receive basic (sending to kernel)\", SocketNetlinkSendReceive},\r\n    {\"Send and receive where the receive buffer overflows (sending to kernel)\", SocketNetlinkSendReceiveOverflow},\r\n    {\"Blocked reader thread\", SocketNetlinkBlockedReader},\r\n    {\"epoll\", SocketNetlinkEpoll},\r\n    {\"Recvmmsg syscall\", SocketNetlinkRecvmmsg},\r\n    {\"Sending bad Netlink messages\", SocketNetlinkSendBadMessage},\r\n    {\"NETLINK_ROUTE RTM_GETADDR message\", SocketNetlinkRouteGetAddr},\r\n    {\"NETLINK_ROUTE RTM_GETLINK message\", SocketNetlinkRouteGetLink},\r\n    {\"NETLINK_ROUTE RTM_GETROUTE message - get best route\", SocketNetlinkRouteGetRouteBestRoute},\r\n    {\"NETLINK_ROUTE RTM_GETROUTE message - dump all routing entries\", SocketNetlinkRouteGetRouteDump},\r\n    {\"NETLINK_ROUTE RTM_NEWADDR and RTM_DELADDR message\", SocketNetlinkRouteNewDelAddress},\r\n    {\"NETLINK_ROUTE RTM_NEWROUTE and RTM_DELROUTE message\", SocketNetlinkRouteNewDelRoute},\r\n    {\"SO_PASSCRED\", SocketNetlinkSoPasscred},\r\n};\r\n\r\n//\r\n// Function definitions.\r\n//\r\n\r\nint NetlinkTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine main entry point for the test for get*id,set*id system call.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n\r\n    //\r\n    // Run test cases.\r\n    //\r\n\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nbool SocketNetlinkIsIpv6Configured()\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks if an IPv6 address has been configured.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns true if an IPv6 address is available.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct rtattr* Attribute;\r\n    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];\r\n    struct sockaddr_nl BindAddress;\r\n    int FoundDone;\r\n    struct nlmsghdr* Header;\r\n    struct ifaddrmsg* IfAddrMsg;\r\n    bool IpV6AddressValid;\r\n    int LoopbackIndex;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    int RemainingLength;\r\n    int Result;\r\n    int Socket;\r\n\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct ifinfomsg ifm;\r\n        struct rtattr ext_req __attribute__((aligned(NLMSG_ALIGNTO)));\r\n        __u32 ext_filter_mask;\r\n    } Request;\r\n\r\n    IpV6AddressValid = false;\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETADDR request.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = sizeof(Request);\r\n    Request.nlh.nlmsg_type = RTM_GETADDR;\r\n    Request.nlh.nlmsg_seq = 0x4563;\r\n    Request.ifm.ifi_family = AF_NETLINK;\r\n    Request.ext_req.rta_type = IFLA_EXT_MASK;\r\n    Request.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));\r\n    Request.ext_filter_mask = RTEXT_FILTER_VF;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    FoundDone = 0;\r\n    for (;;)\r\n    {\r\n        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));\r\n\r\n        Header = (struct nlmsghdr*)ReceiveBuffer;\r\n        while (!IpV6AddressValid && NLMSG_OK(Header, ReceiveResult))\r\n        {\r\n            if (Header->nlmsg_type == NLMSG_DONE)\r\n            {\r\n                FoundDone = 1;\r\n                break;\r\n            }\r\n\r\n            IfAddrMsg = (struct ifaddrmsg*)NLMSG_DATA(Header);\r\n            if ((IfAddrMsg->ifa_index != LoopbackIndex) && (IfAddrMsg->ifa_family == AF_INET6))\r\n            {\r\n\r\n                Attribute = (struct rtattr*)((char*)IfAddrMsg + sizeof(struct ifaddrmsg));\r\n\r\n                RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));\r\n\r\n                while (RTA_OK(Attribute, RemainingLength))\r\n                {\r\n                    SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);\r\n\r\n                    //\r\n                    // If the address is IPv6 and does not start with the link\r\n                    // local prefix, assume it is a valid address.\r\n                    //\r\n\r\n                    if ((Attribute->rta_type == IFA_ADDRESS) && strncmp(AttributeDump, \"fe 80\", 5) != 0)\r\n                    {\r\n\r\n                        LxtLogInfo(\"IpV6 address found: %s\", AttributeDump);\r\n                        IpV6AddressValid = true;\r\n                        break;\r\n                    }\r\n\r\n                    Attribute = RTA_NEXT(Attribute, RemainingLength);\r\n                }\r\n            }\r\n\r\n            Header = NLMSG_NEXT(Header, ReceiveResult);\r\n        }\r\n\r\n        if (IpV6AddressValid || (FoundDone == 1))\r\n        {\r\n            break;\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return IpV6AddressValid;\r\n}\r\n\r\nint SocketNetlinkOpenClose(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the socket() API.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int FamilyIterator;\r\n    int Result;\r\n    int Socket;\r\n    int TypeIterator;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    Socket = -1;\r\n\r\n    for (TypeIterator = 0; TypeIterator < sizeof(SupportedType) / sizeof(int); TypeIterator++)\r\n    {\r\n\r\n        for (FamilyIterator = 0; FamilyIterator < sizeof(SupportedFamily) / sizeof(int); FamilyIterator++)\r\n        {\r\n\r\n            LxtLogInfo(\"testing type: %d, netlink family: %d\", SupportedType[TypeIterator], SupportedFamily[FamilyIterator]);\r\n\r\n            //\r\n            // Success cases.\r\n            //\r\n\r\n            LxtCheckErrno((Socket = socket(AF_NETLINK, SupportedType[TypeIterator], SupportedFamily[FamilyIterator])));\r\n\r\n            LxtClose(Socket);\r\n\r\n            //\r\n            // Test case: Overloading 'type' field for netlink sockets.\r\n            //\r\n\r\n            LxtCheckErrno((Socket = socket(AF_NETLINK, SupportedType[TypeIterator] | O_NONBLOCK, SupportedFamily[FamilyIterator])));\r\n\r\n            LxtClose(Socket);\r\n            LxtCheckErrno((Socket = socket(AF_NETLINK, SupportedType[TypeIterator] | O_CLOEXEC, SupportedFamily[FamilyIterator])));\r\n\r\n            LxtClose(Socket);\r\n            LxtCheckErrno((Socket = socket(AF_NETLINK, SupportedType[TypeIterator] | O_NONBLOCK | O_CLOEXEC, SupportedFamily[FamilyIterator])));\r\n\r\n            LxtClose(Socket);\r\n        }\r\n    }\r\n\r\n    //\r\n    // Test case: Failure cases for socket(), unsupported type, family.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE + 100), EPROTONOSUPPORT);\r\n\r\n    LxtCheckErrnoFailure(socket(AF_NETLINK, SOCK_STREAM, NETLINK_ROUTE), ESOCKTNOSUPPORT);\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests basic operations on netlink sockets.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    int BufferSize;\r\n    int FamilyIterator;\r\n    socklen_t OptionLength;\r\n    int OptionInt;\r\n    char ReceiveBuffer[500];\r\n    int Result;\r\n    int Socket;\r\n    int SocketError;\r\n    struct timeval Timeout;\r\n    int TypeIterator;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    Socket = -1;\r\n\r\n    for (TypeIterator = 0; TypeIterator < sizeof(SupportedType) / sizeof(int); TypeIterator++)\r\n    {\r\n\r\n        for (FamilyIterator = 0; FamilyIterator < sizeof(SupportedFamily) / sizeof(int); FamilyIterator++)\r\n        {\r\n\r\n            LxtCheckErrno(Socket = socket(AF_NETLINK, SupportedType[TypeIterator], SupportedFamily[FamilyIterator]));\r\n\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n            OptionLength = sizeof(BufferSize);\r\n            BufferSize = 2345;\r\n            LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SO_SNDBUF, &BufferSize, OptionLength));\r\n\r\n            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_SNDBUF, &BufferSize, &OptionLength));\r\n\r\n            LxtCheckEqual(OptionLength, sizeof(BufferSize), \"%d\");\r\n            LxtCheckEqual(BufferSize, 2345 * 2, \"%d\");\r\n\r\n            OptionLength = sizeof(BufferSize);\r\n            BufferSize = 6345;\r\n            LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SO_RCVBUF, &BufferSize, OptionLength));\r\n\r\n            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_RCVBUF, &BufferSize, &OptionLength));\r\n\r\n            LxtCheckEqual(OptionLength, sizeof(BufferSize), \"%d\");\r\n            LxtCheckEqual(BufferSize, 6345 * 2, \"%d\");\r\n\r\n            //\r\n            // Set a timeout value of 8 milliseconds for the send.\r\n            //\r\n\r\n            LxtCheckResult(SocketNetlinkSetAndVerifySocketOptionTimeout(Socket, SO_SNDTIMEO, 8000));\r\n\r\n            //\r\n            // Set a timeout value of 8 milliseconds for the receive.\r\n            //\r\n\r\n            LxtCheckResult(SocketNetlinkSetAndVerifySocketOptionTimeout(Socket, SO_RCVTIMEO, 8000));\r\n\r\n            //\r\n            // Check that the blocking read will timeout with EAGAIN\r\n            // after 8 milliseconds.\r\n            //\r\n\r\n            LxtCheckErrnoFailure(read(Socket, ReceiveBuffer, sizeof(ReceiveBuffer)), EAGAIN);\r\n\r\n            //\r\n            // Check that the value of SO_ERROR is 0 (no error).\r\n            //\r\n\r\n            SocketError = 1234;\r\n            OptionLength = sizeof(SocketError);\r\n            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_ERROR, &SocketError, &OptionLength));\r\n\r\n            LxtCheckEqual(OptionLength, sizeof(SocketError), \"%d\");\r\n            LxtCheckEqual(SocketError, 0, \"%d\");\r\n\r\n            OptionLength = sizeof(OptionInt);\r\n            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_TYPE, &OptionInt, &OptionLength));\r\n\r\n            LxtCheckEqual(OptionLength, sizeof(OptionInt), \"%d\");\r\n            LxtCheckEqual(OptionInt, SupportedType[TypeIterator], \"%d\");\r\n\r\n            OptionLength = sizeof(OptionInt);\r\n            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_PROTOCOL, &OptionInt, &OptionLength));\r\n\r\n            LxtCheckEqual(OptionLength, sizeof(OptionInt), \"%d\");\r\n            LxtCheckEqual(OptionInt, SupportedFamily[FamilyIterator], \"%d\");\r\n\r\n            OptionLength = sizeof(OptionInt);\r\n            LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_DOMAIN, &OptionInt, &OptionLength));\r\n\r\n            LxtCheckEqual(OptionLength, sizeof(OptionInt), \"%d\");\r\n            LxtCheckEqual(OptionInt, AF_NETLINK, \"%d\");\r\n            LxtClose(Socket);\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkBind(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the bind() API.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char AddressBuffer[150];\r\n    socklen_t AddressLength;\r\n    int ArrayIterator;\r\n    int ArrayIterator2;\r\n    const int ArraySize = 10;\r\n    struct sockaddr_nl BindAddress;\r\n    struct sockaddr_nl* BindAddressP;\r\n    int ChildPid;\r\n    char DataBuffer[10];\r\n    int Family;\r\n    int FamilyIterator;\r\n    int Result;\r\n    uint32_t SavedPid;\r\n    int Socket;\r\n    int Socket2;\r\n    int Socket3;\r\n    int Socket4;\r\n    int SocketA[ArraySize];\r\n    int SocketChildA[ArraySize];\r\n    int SocketChildPid[ArraySize];\r\n    int SocketPid[ArraySize];\r\n    int Type;\r\n    int TypeIterator;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    Socket = -1;\r\n    Socket2 = -1;\r\n    Socket3 = -1;\r\n    Socket4 = -1;\r\n    ChildPid = -1;\r\n    for (TypeIterator = 0; TypeIterator < sizeof(SupportedType) / sizeof(int); TypeIterator++)\r\n    {\r\n\r\n        Type = SupportedType[TypeIterator];\r\n        for (FamilyIterator = 0; FamilyIterator < sizeof(SupportedFamily) / sizeof(int); FamilyIterator++)\r\n        {\r\n\r\n            //\r\n            // First thing; initialize the various array.\r\n            //\r\n\r\n            for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)\r\n            {\r\n                SocketA[ArrayIterator] = 0;\r\n                SocketPid[ArrayIterator] = 0;\r\n                SocketChildPid[ArrayIterator] = 0;\r\n            }\r\n\r\n            Family = SupportedFamily[FamilyIterator];\r\n            LxtLogInfo(\"testing type: %d, netlink family: %d\", SupportedType[TypeIterator], SupportedFamily[FamilyIterator]);\r\n\r\n            //\r\n            // Test case: bind with invalid family value in the address.\r\n            //\r\n\r\n            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_INET;\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrnoFailure(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength), EINVAL);\r\n\r\n            LxtClose(Socket);\r\n\r\n            //\r\n            // Test case: bind with invalid address length.\r\n            //\r\n\r\n            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n            AddressLength = sizeof(BindAddress.nl_family);\r\n            LxtCheckErrnoFailure(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength), EINVAL);\r\n\r\n            LxtClose(Socket);\r\n\r\n            //\r\n            // Test case: address length > sizeof(sockaddr_nl) is also invalid.\r\n            //\r\n\r\n            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));\r\n            BindAddressP = (struct sockaddr_nl*)AddressBuffer;\r\n            memset(AddressBuffer, 0, sizeof(AddressBuffer));\r\n            BindAddressP->nl_family = AF_NETLINK;\r\n            AddressLength = sizeof(AddressBuffer);\r\n            LxtCheckErrnoFailure(bind(Socket, (struct sockaddr*)BindAddressP, AddressLength), EINVAL);\r\n\r\n            LxtClose(Socket);\r\n\r\n            //\r\n            // Test case: getsockname on unbound socket returns nl_pid = 0.\r\n            //\r\n\r\n            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n            LxtCheckEqual(BindAddress.nl_pid, 0, \"%d\");\r\n            LxtCheckEqual(AddressLength, sizeof(BindAddress), \"%d\");\r\n            LxtClose(Socket);\r\n\r\n            //\r\n            // Test case: calling sendto() on unbound socket automatically\r\n            //            binds the socket (even if the sendto() fails).\r\n            //\r\n\r\n            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));\r\n            memset(DataBuffer, 0, sizeof(DataBuffer));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n            AddressLength = sizeof(BindAddress);\r\n            sendto(Socket, DataBuffer, sizeof(DataBuffer), 0, (struct sockaddr*)&BindAddress, AddressLength);\r\n\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n            LxtCheckEqual(BindAddress.nl_pid, getpid(), \"%d\");\r\n            LxtCheckEqual(AddressLength, sizeof(BindAddress), \"%d\");\r\n            LxtClose(Socket);\r\n\r\n            //\r\n            // Test case: Bind with nl_pid = 0, kernel should assign a unique\r\n            //            ID. The first netlink socket of the process is\r\n            //            assigned the process ID as the nl_pid. It also confirms\r\n            //            the pad value is ignored.\r\n            //\r\n\r\n            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n            BindAddress.nl_pad = 1;\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n            LxtCheckEqual(BindAddress.nl_pid, getpid(), \"%d\");\r\n            LxtCheckEqual(AddressLength, sizeof(BindAddress), \"%d\");\r\n\r\n            //\r\n            // Validate that a socket that is already bound, cannot be bound again.\r\n            //\r\n\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrnoFailure(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength), EINVAL);\r\n\r\n            //\r\n            // Test case: The user specifies a negative nl_pid (which is what the\r\n            //            kernel assigns to the non-first sockets of the process).\r\n            //            Test that the kernel will skip any negative nl_pid's that\r\n            //            the user already specified. For example, Socket2 gets\r\n            //            an auto-assigned nl_pid of -5. Socket3 is bound with\r\n            //            the user specifying nl_pid of -4. Socket4 gets an\r\n            //            auto-assigned nl_pid of -3, since -4 was already taken\r\n            //            by the user.\r\n            //\r\n\r\n            LxtCheckErrno(Socket2 = socket(AF_NETLINK, Type, Family));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrno(bind(Socket2, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            LxtCheckErrno(getsockname(Socket2, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n            LxtCheckTrue((int)BindAddress.nl_pid < 0);\r\n            LxtCheckEqual(AddressLength, sizeof(BindAddress), \"%d\");\r\n\r\n            LxtCheckErrno(Socket3 = socket(AF_NETLINK, Type, Family));\r\n            BindAddress.nl_pid += 1;\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrno(bind(Socket3, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            LxtCheckErrno(getsockname(Socket3, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n            LxtCheckTrue((int)BindAddress.nl_pid < 0);\r\n            LxtCheckEqual(AddressLength, sizeof(BindAddress), \"%d\");\r\n\r\n            LxtCheckErrno(Socket4 = socket(AF_NETLINK, Type, Family));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrno(bind(Socket4, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            LxtCheckErrno(getsockname(Socket4, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n            LxtCheckTrue((int)BindAddress.nl_pid < 0);\r\n            LxtCheckEqual(AddressLength, sizeof(BindAddress), \"%d\");\r\n            LxtClose(Socket3);\r\n            LxtClose(Socket4);\r\n\r\n            //\r\n            // Test case: The kernel assigned a negative custom PID for Socket2.\r\n            //            Save Socket2's custom PID, close Socket2, then bind a\r\n            //            new socket with the saved PID, and verify success.\r\n            //\r\n\r\n            AddressLength = sizeof(BindAddress);\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            LxtCheckErrno(getsockname(Socket2, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n            LxtCheckTrue((int)BindAddress.nl_pid < 0);\r\n            LxtCheckEqual(AddressLength, sizeof(BindAddress), \"%d\");\r\n            SavedPid = BindAddress.nl_pid;\r\n            LxtClose(Socket2);\r\n            LxtCheckErrno(Socket2 = socket(AF_NETLINK, Type, Family));\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrno(bind(Socket2, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            LxtCheckErrno(getsockname(Socket2, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n            LxtCheckEqual((int)BindAddress.nl_pid, SavedPid, \"%d\");\r\n            LxtCheckEqual(AddressLength, sizeof(BindAddress), \"%d\");\r\n            LxtClose(Socket2);\r\n\r\n            //\r\n            // Test case: close the first socket of the process and open a new\r\n            //            one. Being the first netlink socket of the process,\r\n            //            it should get the process ID as the 'nl_pid'.\r\n            //\r\n\r\n            LxtClose(Socket);\r\n            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n            LxtCheckEqual(BindAddress.nl_pid, getpid(), \"%d\");\r\n            LxtCheckEqual(AddressLength, sizeof(BindAddress), \"%d\");\r\n\r\n            //\r\n            // Test case: Kernel should assign a negative unique ID to any subsequent\r\n            //            (after the first one) netlink socket that the process\r\n            //            subsequently creates.\r\n            //\r\n\r\n            for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)\r\n            {\r\n                LxtCheckErrno(SocketA[ArrayIterator] = socket(AF_NETLINK, Type, Family));\r\n\r\n                memset(&BindAddress, 0, sizeof(BindAddress));\r\n                BindAddress.nl_family = AF_NETLINK;\r\n                AddressLength = sizeof(BindAddress);\r\n                LxtCheckErrno(bind(SocketA[ArrayIterator], (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n                memset(&BindAddress, 0, sizeof(BindAddress));\r\n                LxtCheckErrno(getsockname(SocketA[ArrayIterator], (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n                SocketPid[ArrayIterator] = BindAddress.nl_pid;\r\n                LxtCheckTrue(SocketPid[ArrayIterator] < 0);\r\n            }\r\n\r\n            //\r\n            // Validate that every socket PID is unique.\r\n            //\r\n\r\n            for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)\r\n            {\r\n                LxtLogInfo(\"parent, socket: %d, pid: %d\", ArrayIterator + 1, SocketPid[ArrayIterator]);\r\n\r\n                for (ArrayIterator2 = ArrayIterator + 1; ArrayIterator2 < ArraySize; ArrayIterator2++)\r\n                {\r\n\r\n                    LxtCheckNotEqual(SocketPid[ArrayIterator], SocketPid[ArrayIterator2], \"%d\");\r\n                }\r\n            }\r\n\r\n            //\r\n            // Test case: validate that the child process gets its own set of ID's.\r\n            //\r\n\r\n            ChildPid = fork();\r\n            if (ChildPid == 0)\r\n            {\r\n                LxtLogInfo(\"child, pid: %d\", getpid());\r\n                LxtClose(Socket);\r\n                LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));\r\n                memset(&BindAddress, 0, sizeof(BindAddress));\r\n                BindAddress.nl_family = AF_NETLINK;\r\n                AddressLength = sizeof(BindAddress);\r\n                LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n                memset(&BindAddress, 0, sizeof(BindAddress));\r\n                LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n                LxtCheckEqual(BindAddress.nl_pid, getpid(), \"%d\");\r\n                for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)\r\n                {\r\n                    LxtCheckErrno(SocketChildA[ArrayIterator] = socket(AF_NETLINK, Type, Family));\r\n\r\n                    memset(&BindAddress, 0, sizeof(BindAddress));\r\n                    BindAddress.nl_family = AF_NETLINK;\r\n                    AddressLength = sizeof(BindAddress);\r\n                    LxtCheckErrno(bind(SocketChildA[ArrayIterator], (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n                    memset(&BindAddress, 0, sizeof(BindAddress));\r\n                    LxtCheckErrno(getsockname(SocketChildA[ArrayIterator], (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n                    SocketChildPid[ArrayIterator] = BindAddress.nl_pid;\r\n                    LxtCheckTrue(SocketChildPid[ArrayIterator] < 0);\r\n                }\r\n\r\n                //\r\n                // Validate that every socket PID is unique, locally as well\r\n                // as globally.\r\n                //\r\n\r\n                for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)\r\n                {\r\n                    LxtLogInfo(\"child, socket: %d, pid: %d\", ArrayIterator + 1, SocketChildPid[ArrayIterator]);\r\n\r\n                    for (ArrayIterator2 = ArrayIterator + 1; ArrayIterator2 < ArraySize; ArrayIterator2++)\r\n                    {\r\n\r\n                        LxtCheckNotEqual(SocketChildPid[ArrayIterator], SocketChildPid[ArrayIterator2], \"%d\");\r\n\r\n                        LxtCheckNotEqual(SocketPid[ArrayIterator], SocketChildPid[ArrayIterator2], \"%d\");\r\n                    }\r\n                }\r\n\r\n                for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)\r\n                {\r\n                    LxtClose(SocketChildA[ArrayIterator]);\r\n                }\r\n\r\n                LxtClose(Socket);\r\n                Result = LXT_RESULT_SUCCESS;\r\n                goto ErrorExit;\r\n            }\r\n\r\n            LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n            for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)\r\n            {\r\n                LxtClose(SocketA[ArrayIterator]);\r\n            }\r\n\r\n            //\r\n            // Test case: validate that if the app provides a PID for the socket,\r\n            //            then it should be unique.\r\n            //\r\n\r\n            LxtCheckErrno(SocketA[0] = socket(AF_NETLINK, Type, Family));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n\r\n            //\r\n            // Since the very first socket of this process is still open,\r\n            // getpid() as the socket PID is already in use.\r\n            //\r\n\r\n            BindAddress.nl_pid = getpid();\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrnoFailure(bind(SocketA[0], (struct sockaddr*)&BindAddress, AddressLength), EADDRINUSE);\r\n\r\n            LxtClose(SocketA[0]);\r\n            LxtClose(Socket);\r\n\r\n            //\r\n            // Test case: validate that when there are no netlink sockets opened\r\n            //            by the process, the app should be able to provide the\r\n            //            PID of the process as the unique ID for the socket.\r\n            //\r\n\r\n            LxtCheckErrno(Socket = socket(AF_NETLINK, Type, Family));\r\n            memset(&BindAddress, 0, sizeof(BindAddress));\r\n            BindAddress.nl_family = AF_NETLINK;\r\n            BindAddress.nl_pid = getpid();\r\n            AddressLength = sizeof(BindAddress);\r\n            LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n            LxtClose(Socket);\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    if (Socket2 > 0)\r\n    {\r\n        close(Socket2);\r\n    }\r\n\r\n    if (Socket3 > 0)\r\n    {\r\n        close(Socket3);\r\n    }\r\n\r\n    if (Socket4 > 0)\r\n    {\r\n        close(Socket4);\r\n    }\r\n\r\n    for (ArrayIterator = 0; ArrayIterator < ArraySize; ArrayIterator++)\r\n    {\r\n        if (SocketA[ArrayIterator] != 0)\r\n        {\r\n            close(SocketA[ArrayIterator]);\r\n        }\r\n\r\n        if (SocketChildA[ArrayIterator] != 0)\r\n        {\r\n            close(SocketChildA[ArrayIterator]);\r\n        }\r\n    }\r\n\r\n    //\r\n    // If child, exit.\r\n    //\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkBindThread(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the bind() API with new threads\r\n    created by pthread_create.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    int ArrayIterator;\r\n    struct sockaddr_nl BindAddress;\r\n    pthread_t ChildThread;\r\n    pid_t ChildThreadNetlinkPid;\r\n    BindChildThreadReturn* ChildThreadReturn;\r\n    int Result;\r\n    int Socket;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    Socket = -1;\r\n    for (ArrayIterator = 0; ArrayIterator < 5; ArrayIterator++)\r\n    {\r\n\r\n        //\r\n        // Open a netlink socket. This is the first netlink socket of the\r\n        // threadgroup, so it should have a nl_pid equal to getpid() (which\r\n        // is the tgid).\r\n        //\r\n\r\n        LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n        memset(&BindAddress, 0, sizeof(BindAddress));\r\n        BindAddress.nl_family = AF_NETLINK;\r\n        AddressLength = sizeof(BindAddress);\r\n        LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n        memset(&BindAddress, 0, sizeof(BindAddress));\r\n        LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n        LxtLogInfo(\"nl_pid 1: %d pid: %d\", BindAddress.nl_pid, getpid());\r\n        LxtCheckTrue(BindAddress.nl_pid > 0);\r\n        LxtCheckEqual(BindAddress.nl_pid, getpid(), \"%d\");\r\n\r\n        //\r\n        // Create a new thread, which opens and closes a netlink socket in the\r\n        // new thread. This is not the first netlink socket of the threadgroup,\r\n        // so it should have a negative nl_pid.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(pthread_create(&ChildThread, NULL, SocketNetlinkBindThreadChild, NULL));\r\n\r\n        LxtCheckErrnoZeroSuccess(pthread_join(ChildThread, (void*)&ChildThreadReturn));\r\n        LxtCheckTrue(ChildThreadReturn != NULL);\r\n        ChildThreadNetlinkPid = ChildThreadReturn->nl_pid;\r\n        free(ChildThreadReturn);\r\n        LxtLogInfo(\"nl_pid 2: %d pid: %d\", ChildThreadNetlinkPid, getpid());\r\n        LxtCheckTrue(ChildThreadNetlinkPid < 0);\r\n\r\n        //\r\n        // Close the first netlink socket (leaving no open netlink sockets).\r\n        // Create a new thread, which opens and closes a netlink socket in the\r\n        // new thread. This is the first netlink socket of the threadgroup.\r\n        //\r\n\r\n        LxtClose(Socket);\r\n        LxtCheckErrnoZeroSuccess(pthread_create(&ChildThread, NULL, SocketNetlinkBindThreadChild, NULL));\r\n\r\n        LxtCheckErrnoZeroSuccess(pthread_join(ChildThread, (void*)&ChildThreadReturn));\r\n        LxtCheckTrue(ChildThreadReturn != NULL);\r\n        ChildThreadNetlinkPid = ChildThreadReturn->nl_pid;\r\n        free(ChildThreadReturn);\r\n        LxtLogInfo(\"nl_pid 3: %d pid: %d\", ChildThreadNetlinkPid, getpid());\r\n        LxtCheckTrue(ChildThreadNetlinkPid > 0);\r\n        LxtCheckEqual(ChildThreadNetlinkPid, getpid(), \"%d\");\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid* SocketNetlinkBindThreadChild(void* Arg)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine runs in a child thread and creates a netlink socket.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    On success, returns a struct containing the nl_pid of the netlink socket\r\n    created by this thread. On failure, returns NULL.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    int Result;\r\n    int Socket;\r\n    BindChildThreadReturn* ThreadReturn;\r\n\r\n    Socket = -1;\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    LxtCheckErrno(getsockname(Socket, (struct sockaddr*)&BindAddress, &AddressLength));\r\n\r\n    LxtLogInfo(\"Child thread: nl_pid: %d, getpid(): %d\", BindAddress.nl_pid, getpid());\r\n\r\n    LxtClose(Socket);\r\n    ThreadReturn = malloc(sizeof(*ThreadReturn));\r\n    LxtCheckTrue(ThreadReturn != NULL);\r\n    ThreadReturn->nl_pid = BindAddress.nl_pid;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    if (Result < 0)\r\n    {\r\n        return NULL;\r\n    }\r\n    else\r\n    {\r\n        return ThreadReturn;\r\n    }\r\n}\r\n\r\nint SocketNetlinkSendReceive(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the send and receive family of APIs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh1;\r\n        char dummy[4];\r\n        struct nlmsghdr nlh2;\r\n    } DoubleRequest;\r\n    struct nlmsgerr* Error;\r\n    int ExpectedReceiveLength;\r\n    struct iovec IoVec[10];\r\n    struct msghdr MessageHeader;\r\n    struct sockaddr_nl ReceiveAddress;\r\n    char ReceiveBuffer[500];\r\n    struct nlmsghdr* ReceiveHeader;\r\n    struct nlmsghdr* ReceiveHeader2;\r\n    int ReceiveResult;\r\n    struct nlmsghdr Request;\r\n    int Result;\r\n    struct sockaddr_nl SendAddress;\r\n    uint32_t Sequence;\r\n    int Socket;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    Socket = -1;\r\n    Sequence = 0x7435;\r\n\r\n    //\r\n    // Create and bind socket. Create a Netlink request with the ACK flag.\r\n    // Netlink should echo the same request back to us.\r\n    //\r\n    // TODO_LX: Test whether invalid messages should be ACK'd back.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlmsg_len = NLMSG_LENGTH(0);\r\n    Request.nlmsg_type = NLMSG_NOOP;\r\n    Request.nlmsg_flags = NLM_F_ACK;\r\n    Request.nlmsg_seq = Sequence;\r\n\r\n    //\r\n    // Test sendto() with invalid send addresses.\r\n    //\r\n\r\n    AddressLength = sizeof(SendAddress);\r\n    memset(&SendAddress, 0, sizeof(SendAddress));\r\n    SendAddress.nl_family = AF_NETLINK;\r\n    SendAddress.nl_pid = 0xFFFF1234;\r\n    LxtCheckErrnoFailure(sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength), ECONNREFUSED);\r\n\r\n    memset(&SendAddress, 0, sizeof(SendAddress));\r\n    SendAddress.nl_family = 0xFFFF;\r\n    LxtCheckErrnoFailure(sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength), EINVAL);\r\n\r\n    //\r\n    // Test sendto() with 0 nl_pid in send address. This should go to the kernel.\r\n    // This is message 0.\r\n    //\r\n\r\n    memset(&SendAddress, 0, sizeof(SendAddress));\r\n    Request.nlmsg_seq = Sequence;\r\n    SendAddress.nl_family = AF_NETLINK;\r\n    SendAddress.nl_pid = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength));\r\n\r\n    //\r\n    // Test sendto() with 0 buffer length. The return value should be 0\r\n    // and no response should be generated.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(sendto(Socket, &Request, 0, 0, (struct sockaddr*)&SendAddress, AddressLength));\r\n\r\n    //\r\n    // Test sendto() with NULL send address. This should go to the kernel\r\n    // by default. This is message 1.\r\n    //\r\n\r\n    Request.nlmsg_seq = Sequence + 1;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n\r\n    //\r\n    // Test send(). This should go to the kernel by default. This is message 2.\r\n    //\r\n\r\n    Request.nlmsg_seq = Sequence + 2;\r\n    LxtCheckErrno(send(Socket, &Request, sizeof(Request), 0));\r\n\r\n    //\r\n    // Test write(). This should go to the kernel by default. This is message 3.\r\n    //\r\n\r\n    Request.nlmsg_seq = Sequence + 3;\r\n    LxtCheckErrno(write(Socket, &Request, sizeof(Request)));\r\n\r\n    //\r\n    // Test sendmsg() with NULL send address. This is message 4.\r\n    // A NULL send address means the name size should be ignored, so also add\r\n    // an invalid name size to verify this.\r\n    //\r\n\r\n    memset(&IoVec, 0, sizeof(IoVec));\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    IoVec[0].iov_base = &Request;\r\n    IoVec[0].iov_len = sizeof(Request);\r\n    MessageHeader.msg_iov = IoVec;\r\n    MessageHeader.msg_iovlen = 1;\r\n    MessageHeader.msg_namelen = -1;\r\n    Request.nlmsg_seq = Sequence + 4;\r\n    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));\r\n\r\n    //\r\n    // Test sendmsg() with buffer split across vectors. This is message 5.\r\n    //\r\n\r\n    memset(&IoVec, 0, sizeof(IoVec));\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    memset(&DoubleRequest, 0, sizeof(DoubleRequest));\r\n    IoVec[0].iov_base = &DoubleRequest.nlh1;\r\n    IoVec[0].iov_len = __builtin_offsetof(struct nlmsghdr, nlmsg_flags);\r\n    IoVec[1].iov_base = &DoubleRequest.nlh2.nlmsg_flags;\r\n    IoVec[1].iov_len = sizeof(struct nlmsghdr) - __builtin_offsetof(struct nlmsghdr, nlmsg_flags);\r\n\r\n    DoubleRequest.nlh1.nlmsg_len = NLMSG_LENGTH(0);\r\n    DoubleRequest.nlh1.nlmsg_type = NLMSG_NOOP;\r\n    DoubleRequest.nlh2.nlmsg_flags = NLM_F_ACK;\r\n    DoubleRequest.nlh2.nlmsg_seq = Sequence + 5;\r\n    MessageHeader.msg_iov = IoVec;\r\n    MessageHeader.msg_iovlen = 2;\r\n    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));\r\n\r\n    //\r\n    // Test sendmsg() with MSG_DONTWAIT flag.\r\n    //\r\n\r\n    memset(&IoVec, 0, sizeof(IoVec));\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    IoVec[0].iov_base = &Request;\r\n    IoVec[0].iov_len = sizeof(Request);\r\n    MessageHeader.msg_iov = IoVec;\r\n    MessageHeader.msg_iovlen = 1;\r\n    Request.nlmsg_seq = Sequence + 6;\r\n    LxtCheckErrno(sendmsg(Socket, &MessageHeader, MSG_DONTWAIT));\r\n\r\n    //\r\n    // Test sendmsg() with invalid send address.\r\n    //\r\n\r\n    AddressLength = sizeof(SendAddress);\r\n    memset(&SendAddress, 0, sizeof(SendAddress));\r\n    SendAddress.nl_family = AF_NETLINK;\r\n    SendAddress.nl_pid = 0xFFFF1234;\r\n    MessageHeader.msg_name = &SendAddress;\r\n    MessageHeader.msg_namelen = AddressLength;\r\n    LxtCheckErrnoFailure(sendmsg(Socket, &MessageHeader, 0), ECONNREFUSED);\r\n\r\n    //\r\n    // Test sendmsg() with negative send address length.\r\n    //\r\n\r\n    AddressLength = sizeof(SendAddress);\r\n    memset(&SendAddress, 0, sizeof(SendAddress));\r\n    SendAddress.nl_family = AF_NETLINK;\r\n    SendAddress.nl_pid = 0xFFFF1234;\r\n    MessageHeader.msg_name = &SendAddress;\r\n    MessageHeader.msg_namelen = -1;\r\n    LxtCheckErrnoFailure(sendmsg(Socket, &MessageHeader, 0), EINVAL);\r\n\r\n    //\r\n    // Test sendmsg() with 0 nl_pid in send address.\r\n    //\r\n\r\n    AddressLength = sizeof(SendAddress);\r\n    memset(&SendAddress, 0, sizeof(SendAddress));\r\n    SendAddress.nl_family = AF_NETLINK;\r\n    SendAddress.nl_pid = 0;\r\n    MessageHeader.msg_name = &SendAddress;\r\n    MessageHeader.msg_namelen = AddressLength;\r\n    Request.nlmsg_seq = Sequence + 7;\r\n    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));\r\n\r\n    //\r\n    // Test read(). Verify the received contents. The response should be message 0.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    LxtCheckErrno(ReceiveResult = read(Socket, ReceiveBuffer, sizeof(ReceiveBuffer)));\r\n    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);\r\n    LxtCheckEqual(Error->error, 0, \"%d\");\r\n\r\n    //\r\n    // Test recv(). Verify the received contents. The response should be message 1.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 1, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);\r\n    LxtCheckEqual(Error->error, 0, \"%d\");\r\n\r\n    //\r\n    // Test recvfrom() with NULL receive address. Use MSG_PEEK flag, which\r\n    // should not remove the data from the socket. Use MSG_TRUNC flag, which\r\n    // should have no effect since the passed in buffer is larger than the\r\n    // response. Use MSG_WAITALL flag, which has no effect on datagram sockets.\r\n    // Verify the received contents. The response should be message 2.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_PEEK | MSG_TRUNC | MSG_WAITALL, NULL, 0));\r\n\r\n    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 2, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);\r\n    LxtCheckEqual(Error->error, 0, \"%d\");\r\n\r\n    //\r\n    // Test recvfrom(). Use MSG_TRUNC flag. Even though the passed in\r\n    // receive buffer is too small, the return value should be the full length\r\n    // of the response. The response should still be message 2, since the previous\r\n    // call was a MSK_PEEK.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, 2 * sizeof(uint32_t), MSG_TRUNC, NULL, 0));\r\n\r\n    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_seq, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_pid, 0, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);\r\n    LxtCheckEqual(Error->error, 0, \"%d\");\r\n\r\n    //\r\n    // Test recvfrom() with 0 buffer length. This should still advance the\r\n    // socket's internal receive buffer (making the next response message 4).\r\n    // This response should be message 3.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, 0, 0, NULL, 0));\r\n\r\n    ExpectedReceiveLength = 0;\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n\r\n    //\r\n    // Test recvfrom() with valid receive address. Verify the received contents.\r\n    // The response should be message 4.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));\r\n    AddressLength = sizeof(struct sockaddr_nl);\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength));\r\n\r\n    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    LxtCheckEqual(AddressLength, sizeof(struct sockaddr_nl), \"%d\");\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 4, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), \"%d\");\r\n    LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, \"%d\");\r\n    LxtCheckEqual(ReceiveAddress.nl_pid, 0, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);\r\n    LxtCheckEqual(Error->error, 0, \"%d\");\r\n    Request.nlmsg_seq = Sequence + 4;\r\n    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, \"%d\");\r\n\r\n    //\r\n    // Test recvmsg() with valid receive address. Verify the received contents.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));\r\n    memset(&IoVec, 0, sizeof(IoVec));\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    IoVec[0].iov_base = &ReceiveBuffer;\r\n    IoVec[0].iov_len = sizeof(ReceiveBuffer);\r\n    MessageHeader.msg_iov = IoVec;\r\n    MessageHeader.msg_iovlen = 1;\r\n    MessageHeader.msg_name = &ReceiveAddress;\r\n    MessageHeader.msg_namelen = AddressLength;\r\n\r\n    //\r\n    // Set some random value as the control length and check whether it gets\r\n    // properly (re)set by the kernel.\r\n    //\r\n\r\n    MessageHeader.msg_controllen = 100;\r\n    LxtCheckErrno(ReceiveResult = recvmsg(Socket, &MessageHeader, 0));\r\n    LxtCheckEqual(MessageHeader.msg_controllen, 0, \"%d\");\r\n    LxtCheckEqual(MessageHeader.msg_flags, 0, \"%d\");\r\n    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    LxtCheckEqual(MessageHeader.msg_namelen, AddressLength, \"%d\");\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 5, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), \"%d\");\r\n    LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, \"%d\");\r\n    LxtCheckEqual(ReceiveAddress.nl_pid, 0, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);\r\n    LxtCheckEqual(Error->error, 0, \"%d\");\r\n    Request.nlmsg_seq = Sequence + 5;\r\n    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, \"%d\");\r\n\r\n    //\r\n    // Test recvmsg() with passing in a small receive buffer.\r\n    // The response should be truncated.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    memset(&IoVec, 0, sizeof(IoVec));\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    IoVec[0].iov_base = &ReceiveBuffer;\r\n    IoVec[0].iov_len = 2 * sizeof(uint32_t);\r\n    MessageHeader.msg_iov = IoVec;\r\n    MessageHeader.msg_iovlen = 1;\r\n    LxtCheckErrno(ReceiveResult = recvmsg(Socket, &MessageHeader, 0));\r\n    ExpectedReceiveLength = IoVec[0].iov_len;\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_seq, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_pid, 0, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);\r\n    LxtCheckEqual(Error->error, 0, \"%d\");\r\n    memset(&Request, 0, sizeof(Request));\r\n    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, \"%d\");\r\n\r\n    //\r\n    // Test recvmsg() split across vectors.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    memset(&IoVec, 0, sizeof(IoVec));\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    ReceiveHeader2 =\r\n        (struct nlmsghdr*)&ReceiveBuffer[NLMSG_LENGTH(sizeof(struct nlmsgerr)) + __builtin_offsetof(struct nlmsghdr, nlmsg_flags)];\r\n\r\n    IoVec[0].iov_len = __builtin_offsetof(struct nlmsghdr, nlmsg_flags);\r\n    IoVec[0].iov_base = ReceiveHeader2;\r\n    IoVec[1].iov_base = ReceiveBuffer + __builtin_offsetof(struct nlmsghdr, nlmsg_flags);\r\n    IoVec[1].iov_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n    MessageHeader.msg_iov = IoVec;\r\n    MessageHeader.msg_iovlen = 2;\r\n    LxtCheckErrno(ReceiveResult = recvmsg(Socket, &MessageHeader, 0));\r\n    LxtCheckEqual(MessageHeader.msg_controllen, 0, \"%d\");\r\n    LxtCheckEqual(MessageHeader.msg_flags, 0, \"%d\");\r\n    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_len, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, 0, \"%d\");\r\n    ReceiveHeader->nlmsg_len = ReceiveHeader2->nlmsg_len;\r\n    ReceiveHeader->nlmsg_type = ReceiveHeader2->nlmsg_type;\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_len, ExpectedReceiveLength, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + 7, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), \"%d\");\r\n    LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, \"%d\");\r\n    LxtCheckEqual(ReceiveAddress.nl_pid, 0, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveBuffer);\r\n    LxtCheckEqual(Error->error, 0, \"%d\");\r\n    Request.nlmsg_len = NLMSG_LENGTH(0);\r\n    Request.nlmsg_type = NLMSG_NOOP;\r\n    Request.nlmsg_flags = NLM_F_ACK;\r\n    Request.nlmsg_seq = Sequence + 7;\r\n    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, \"%d\");\r\n\r\n    //\r\n    // Test recvfrom() when using the MSG_DONTWAIT flag. The socket has no data,\r\n    // so it should return EAGAIN immediately instead of blocking forever.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    LxtCheckErrnoFailure(recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT, NULL, 0), EAGAIN);\r\n\r\n    //\r\n    // Test specifying that the input buffer is much larger than it actually is.\r\n    //\r\n\r\n    memset(&DoubleRequest, 0, sizeof(DoubleRequest));\r\n    memset(&IoVec, 0, sizeof(IoVec));\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    DoubleRequest.nlh1.nlmsg_len = 0x30000;\r\n    DoubleRequest.nlh1.nlmsg_type = NLMSG_NOOP;\r\n    DoubleRequest.nlh1.nlmsg_flags = NLM_F_ACK;\r\n    DoubleRequest.nlh1.nlmsg_seq = Sequence;\r\n    IoVec[0].iov_base = &DoubleRequest;\r\n    IoVec[0].iov_len = 0x30000;\r\n    MessageHeader.msg_iov = IoVec;\r\n    MessageHeader.msg_iovlen = 1;\r\n    Request.nlmsg_seq = Sequence + 4;\r\n    LxtCheckErrnoFailure(sendmsg(Socket, &MessageHeader, 0), EFAULT);\r\n\r\n    //\r\n    // Test passing in garbage length values in the message headers.\r\n    //\r\n\r\n    DoubleRequest.nlh1.nlmsg_len = NLMSG_LENGTH(0) + 1;\r\n    DoubleRequest.nlh2.nlmsg_len = 0x3000;\r\n    IoVec[0].iov_len = sizeof(DoubleRequest.nlh1) + 1;\r\n    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));\r\n\r\n    //\r\n    // Again.\r\n    //\r\n\r\n    DoubleRequest.nlh1.nlmsg_len = 0x3000;\r\n    DoubleRequest.nlh2.nlmsg_len = 0x3000;\r\n    IoVec[0].iov_len = sizeof(DoubleRequest.nlh1) + 1;\r\n    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));\r\n\r\n    //\r\n    // Again.\r\n    //\r\n\r\n    DoubleRequest.nlh1.nlmsg_len = 0x3000;\r\n    DoubleRequest.nlh2.nlmsg_len = NLMSG_LENGTH(0) + 1;\r\n    IoVec[0].iov_len = sizeof(DoubleRequest.nlh1) + 1;\r\n    LxtCheckErrno(sendmsg(Socket, &MessageHeader, 0));\r\n\r\n    //\r\n    // TODO: Test multi-message send with different requests\r\n    //       bundled in the same send.\r\n    //\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkSendReceiveOverflow(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the send and receive family of APIs\r\n    in the context of the receive buffer overflowing.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    struct nlmsgerr* Error;\r\n    int ExpectedReceiveLength;\r\n    int Index;\r\n    struct sockaddr_nl ReceiveAddress;\r\n    char ReceiveBuffer[500];\r\n    struct nlmsghdr* ReceiveHeader;\r\n    int ReceiveResult;\r\n    struct nlmsghdr Request;\r\n    int Result;\r\n    struct sockaddr_nl SendAddress;\r\n    int SendResult;\r\n    uint32_t Sequence;\r\n    int Socket;\r\n    int SocketError;\r\n    socklen_t SocketErrorSize;\r\n    struct timeval Timeout;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    Socket = -1;\r\n    Sequence = 0x3468;\r\n\r\n    //\r\n    // Create and bind socket. Create a NOOP request with the ACK flag.\r\n    // Netlink should echo the same request back to us.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlmsg_len = NLMSG_LENGTH(0);\r\n    Request.nlmsg_type = NLMSG_NOOP;\r\n    Request.nlmsg_flags = NLM_F_ACK;\r\n\r\n    //\r\n    // Overflow the receive buffer by sending 3000 events.\r\n    // Each send should succeed with the return value being the full number of\r\n    // bytes the user sent.\r\n    //\r\n\r\n    AddressLength = sizeof(SendAddress);\r\n    memset(&SendAddress, 0, sizeof(SendAddress));\r\n    SendAddress.nl_family = AF_NETLINK;\r\n    SendAddress.nl_pid = 0;\r\n    for (Index = 0; Index < 3000; Index++)\r\n    {\r\n        Request.nlmsg_seq = Sequence + Index;\r\n        LxtCheckErrno(SendResult = sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength));\r\n\r\n        LxtCheckEqual(SendResult, sizeof(Request), \"%d\");\r\n    }\r\n\r\n    //\r\n    // The first call to recvfrom() after the overflow should return ENOBUFS.\r\n    // Also verify that the receive and address buffers were not changed.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));\r\n    ReceiveBuffer[1] = 0x56;\r\n    ReceiveAddress.nl_groups = 0x3456789a;\r\n    AddressLength = sizeof(struct sockaddr_nl);\r\n    LxtCheckErrnoFailure(recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength), ENOBUFS);\r\n\r\n    LxtCheckEqual(ReceiveBuffer[1], 0x56, \"%d\");\r\n    LxtCheckEqual(ReceiveAddress.nl_groups, 0x3456789a, \"%d\");\r\n\r\n    //\r\n    // The subsequent calls to recvfrom() pull out the responses before the\r\n    // overflow happened. Only call recvfrom() 3 times here, so that the receive\r\n    // buffer is not yet fully drained.\r\n    //\r\n\r\n    for (Index = 0; Index < 3; Index++)\r\n    {\r\n        memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n        memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));\r\n        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength));\r\n\r\n        ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n        LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n        LxtCheckEqual(AddressLength, sizeof(struct sockaddr_nl), \"%d\");\r\n        ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n        LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n        LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n        LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence + Index, \"%d\");\r\n        LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), \"%d\");\r\n        LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, \"%d\");\r\n        LxtCheckEqual(ReceiveAddress.nl_pid, 0, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Check the socket option SO_ERROR. This should be 0 (no error),\r\n    // since the error was cleared when the first recvfrom() returned ENOBUFS.\r\n    //\r\n\r\n    SocketErrorSize = sizeof(SocketError);\r\n    SocketError = 212;\r\n    LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_ERROR, &SocketError, &SocketErrorSize));\r\n\r\n    LxtCheckEqual(SocketErrorSize, sizeof(SocketError), \"%d\");\r\n    LxtCheckEqual(SocketError, 0, \"%d\");\r\n\r\n    //\r\n    // Test that the receive buffer is still considered to be \"overflown\"\r\n    // until the entire buffer is drained. This means that anything sent to\r\n    // the socket now will be ignored and no response generated, even though\r\n    // technically there is space in the receive buffer for the response.\r\n    // The test sends a message with a unique sequence number. Later on, the\r\n    // entire receive buffer will be drained and all responses checked to\r\n    // verify that this unique sequence number is not in any of the responses.\r\n    //\r\n\r\n    Request.nlmsg_seq = 0x98765432;\r\n    LxtCheckErrno(SendResult = sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength));\r\n\r\n    LxtCheckEqual(SendResult, sizeof(Request), \"%d\");\r\n\r\n    //\r\n    // Drain the entire response buffer. Set the recvfrom() timeout to 1 millisecond\r\n    // so that it does not block infinitely.\r\n    //\r\n\r\n    Timeout.tv_sec = 0;\r\n    Timeout.tv_usec = 1000;\r\n    LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, &Timeout, sizeof(Timeout)));\r\n\r\n    for (;;)\r\n    {\r\n        memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n        memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));\r\n        ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength);\r\n\r\n        if (ReceiveResult == -1)\r\n        {\r\n            LxtCheckErrnoFailure(ReceiveResult, EAGAIN);\r\n            break;\r\n        }\r\n        else\r\n        {\r\n            ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n            LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n            LxtCheckEqual(AddressLength, sizeof(struct sockaddr_nl), \"%d\");\r\n            ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n            LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n            LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n            LxtCheckNotEqual(ReceiveHeader->nlmsg_seq, 0, \"%d\");\r\n            LxtCheckNotEqual(ReceiveHeader->nlmsg_seq, 0x98765432, \"%d\");\r\n            LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), \"%d\");\r\n            LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, \"%d\");\r\n            LxtCheckEqual(ReceiveAddress.nl_pid, 0, \"%d\");\r\n        }\r\n    }\r\n\r\n    //\r\n    // Overflow the receive buffer again by sending 3000 events.\r\n    // Each send should succeed with the return value being the full number of\r\n    // bytes the user sent.\r\n    //\r\n\r\n    AddressLength = sizeof(SendAddress);\r\n    memset(&SendAddress, 0, sizeof(SendAddress));\r\n    SendAddress.nl_family = AF_NETLINK;\r\n    SendAddress.nl_pid = 0;\r\n    for (Index = 0; Index < 3000; Index++)\r\n    {\r\n        Request.nlmsg_seq = Sequence + Index;\r\n        LxtCheckErrno(SendResult = sendto(Socket, &Request, sizeof(Request), 0, (struct sockaddr*)&SendAddress, AddressLength));\r\n\r\n        LxtCheckEqual(SendResult, sizeof(Request), \"%d\");\r\n    }\r\n\r\n    //\r\n    // Check the socket option SO_ERROR. This should be ENOBUFS,\r\n    // since recvfrom() has not been called yet.\r\n    //\r\n\r\n    SocketErrorSize = sizeof(SocketError);\r\n    SocketError = 212;\r\n    LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SO_ERROR, &SocketError, &SocketErrorSize));\r\n\r\n    LxtCheckEqual(SocketErrorSize, sizeof(SocketError), \"%d\");\r\n    LxtCheckEqual(SocketError, ENOBUFS, \"%d\");\r\n\r\n    //\r\n    // The first recvfrom() should be successful, since the ENOBUFS error\r\n    // was already cleared when socket option SO_ERROR was retrieved.\r\n    //\r\n\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    memset(&ReceiveAddress, 0, sizeof(ReceiveAddress));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, (struct sockaddr*)&ReceiveAddress, &AddressLength));\r\n\r\n    ExpectedReceiveLength = NLMSG_LENGTH(sizeof(struct nlmsgerr));\r\n    LxtCheckEqual(ReceiveResult, ExpectedReceiveLength, \"%d\");\r\n    LxtCheckEqual(AddressLength, sizeof(struct sockaddr_nl), \"%d\");\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_seq, Sequence, \"%d\");\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_pid, getpid(), \"%d\");\r\n    LxtCheckEqual(ReceiveAddress.nl_family, AF_NETLINK, \"%d\");\r\n    LxtCheckEqual(ReceiveAddress.nl_pid, 0, \"%d\");\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkBlockedReader(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests that blocked readers do not get unblocked when the socket\r\n    is closed. Also verify that shutdown() is invalid on NETLINK sockets.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    void* PthreadResult;\r\n    int Result;\r\n    int Socket;\r\n    pthread_t Thread;\r\n    struct timespec Timeout = {0};\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    Socket = -1;\r\n\r\n    //\r\n    // Create and bind socket.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    //\r\n    // Create a reader thread that will block on 'recv'.\r\n    //\r\n\r\n    LxtCheckResultError(pthread_create(&Thread, NULL, SocketBlockedReaderThread, &Socket));\r\n\r\n    //\r\n    // Wait for sometime to allow the reader thread to block on read. There\r\n    // is no other elegant way of knowing whether the thread has blocked.\r\n    //\r\n\r\n    usleep(5000);\r\n\r\n    //\r\n    // Shutdown is NOT supported for NETLINK sockets.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(shutdown(Socket, SHUT_RD), EOPNOTSUPP);\r\n    LxtCheckErrnoFailure(shutdown(Socket, SHUT_WR), EOPNOTSUPP);\r\n    LxtCheckErrnoFailure(shutdown(Socket, SHUT_RDWR), EOPNOTSUPP);\r\n\r\n    //\r\n    // Closing the socket does not unblock the reader. The pthread_timedjoin_np()\r\n    // should timeout since the reader thread is still blocked.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(close(Socket));\r\n    usleep(5000);\r\n    Timeout.tv_nsec = 5000;\r\n    Result = pthread_timedjoin_np(Thread, &PthreadResult, &Timeout);\r\n    if (Result != ETIMEDOUT)\r\n    {\r\n        LxtLogError(\r\n            \"Expecting pthread_tryjoin_np to return ETIMEDOUT(%d), \"\r\n            \"but it returned with result: %d\",\r\n            ETIMEDOUT,\r\n            Result);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // No other choice but to kill the reader thread.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(pthread_cancel(Thread));\r\n    LxtCheckErrnoZeroSuccess(pthread_join(Thread, &PthreadResult));\r\n    LxtCheckEqual(PthreadResult, PTHREAD_CANCELED, \"%p\");\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkEpoll(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests that the epoll state is correct.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    int EdRead;\r\n    int EdWrite;\r\n    struct epoll_event EpollControlEvent;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    int Index;\r\n    char ReceiveBuffer[500];\r\n    int ReceiveResult;\r\n    struct nlmsghdr Request;\r\n    int Result;\r\n    int SendResult;\r\n    int Socket;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    Socket = -1;\r\n\r\n    //\r\n    // Create socket.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, 0));\r\n\r\n    //\r\n    // Create epoll containers for read and write and\r\n    // add the socket descriptor to them.\r\n    //\r\n\r\n    LxtCheckErrno(EdRead = epoll_create(1));\r\n    EpollControlEvent.events = EPOLLIN;\r\n    EpollControlEvent.data.fd = Socket;\r\n    Result = epoll_ctl(EdRead, EPOLL_CTL_ADD, Socket, &EpollControlEvent);\r\n    LxtCheckErrnoZeroSuccess(Result);\r\n\r\n    LxtCheckErrno(EdWrite = epoll_create(1));\r\n    EpollControlEvent.events = EPOLLOUT;\r\n    EpollControlEvent.data.fd = Socket;\r\n    Result = epoll_ctl(EdWrite, EPOLL_CTL_ADD, Socket, &EpollControlEvent);\r\n    LxtCheckErrnoZeroSuccess(Result);\r\n\r\n    //\r\n    // Wait for data to be available with a timeout. This should timeout since\r\n    // there is no data. Verify that write is available.\r\n    //\r\n\r\n    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Bind the socket.\r\n    //\r\n\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    //\r\n    // Wait for data to be available with a timeout. This should timeout since\r\n    // there is no data. Verify that write is available.\r\n    //\r\n\r\n    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Send 3000 messages. The receive buffer will overflow, but write is still\r\n    // available. Read is available now.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlmsg_len = NLMSG_LENGTH(0);\r\n    Request.nlmsg_type = NLMSG_NOOP;\r\n    Request.nlmsg_flags = NLM_F_ACK;\r\n    for (Index = 0; Index < 3000; Index++)\r\n    {\r\n        LxtCheckErrno(SendResult = sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n\r\n        LxtCheckEqual(SendResult, sizeof(Request), \"%d\");\r\n    }\r\n\r\n    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Drain all the events. Write is still available, but read is not.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0), ENOBUFS);\r\n\r\n    for (;;)\r\n    {\r\n        memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n        ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0);\r\n\r\n        if (ReceiveResult == -1)\r\n        {\r\n            LxtCheckErrnoFailure(ReceiveResult, EAGAIN);\r\n            break;\r\n        }\r\n    }\r\n\r\n    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n\r\n    //\r\n    // Close the socket. Both read and write are not available.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(close(Socket));\r\n    Result = epoll_wait(EdRead, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    Result = epoll_wait(EdWrite, EpollWaitEvent, 2, 50);\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    if (EdRead > 0)\r\n    {\r\n        close(EdRead);\r\n    }\r\n\r\n    if (EdWrite > 0)\r\n    {\r\n        close(EdWrite);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkRecvmmsg(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the recvmmsg() syscall.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    NetlinkRecvmmsgBlockedReaderParams BlockedReaderParams;\r\n    struct nlmsgerr* Error;\r\n    struct nlmsghdr* Header;\r\n    int Index;\r\n    socklen_t OptionLength;\r\n    void* PthreadResult;\r\n    char ReceiveBuffers[10][1000];\r\n    struct iovec ReceiveIovecs[10];\r\n    struct mmsghdr ReceiveMessages[10];\r\n    int ReceiveResult;\r\n    int Result;\r\n    int SendResult;\r\n    int Socket;\r\n    pthread_t Thread;\r\n    struct timespec Timeout;\r\n    struct timespec TimeoutPthread;\r\n\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct ifinfomsg ifm;\r\n        struct rtattr ext_req __attribute__((aligned(NLMSG_ALIGNTO)));\r\n        __u32 ext_filter_mask;\r\n    } Request;\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETLINK request. Note that the\r\n    // request is not sent yet at this point.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;\r\n    Request.nlh.nlmsg_len = sizeof(Request);\r\n    Request.nlh.nlmsg_type = RTM_GETLINK;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.ifm.ifi_family = AF_NETLINK;\r\n    Request.ext_req.rta_type = IFLA_EXT_MASK;\r\n    Request.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));\r\n    Request.ext_filter_mask = RTEXT_FILTER_VF;\r\n\r\n    //\r\n    // The timeout value passed to recvmmsg() is ignored - there is a bug in the\r\n    // Linux kernel where the timeout does not work at all. In lxcore we ignore\r\n    // this parameter.\r\n    //\r\n\r\n    Timeout.tv_sec = 800;\r\n    Timeout.tv_nsec = 0;\r\n\r\n    //\r\n    // Set up the receive buffers.\r\n    //\r\n\r\n    memset(ReceiveMessages, 0, sizeof(ReceiveMessages));\r\n    for (Index = 0; Index < 10; Index++)\r\n    {\r\n        ReceiveIovecs[Index].iov_base = ReceiveBuffers[Index];\r\n        ReceiveIovecs[Index].iov_len = 1000;\r\n        ReceiveMessages[Index].msg_hdr.msg_iov = &ReceiveIovecs[Index];\r\n        ReceiveMessages[Index].msg_hdr.msg_iovlen = 1;\r\n    }\r\n\r\n    //\r\n    // There is nothing to receive at this point. Test that MSG_DONTWAIT trumps\r\n    // MSG_WAITFORONE and MSG_WAITALL, therefore EAGAIN is returned due to not waiting.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_DONTWAIT | MSG_WAITFORONE | MSG_WAITALL, &Timeout), EAGAIN);\r\n\r\n    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_DONTWAIT | MSG_WAITFORONE, &Timeout), EAGAIN);\r\n\r\n    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_DONTWAIT | MSG_WAITALL, &Timeout), EAGAIN);\r\n\r\n    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_DONTWAIT, &Timeout), EAGAIN);\r\n\r\n    //\r\n    // Set a SO_RCVTIMEO timeout value of 8 milliseconds for the receive. This timeout\r\n    // applies to each individual recvmsg() call and actually works. Both calls to\r\n    // recvmmsg below should time out from the SO_RCVTIMEO.\r\n    //\r\n\r\n    LxtCheckResult(SocketNetlinkSetAndVerifySocketOptionTimeout(Socket, SO_RCVTIMEO, 8000));\r\n\r\n    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, MSG_WAITFORONE, &Timeout), EAGAIN);\r\n\r\n    LxtCheckErrnoFailure(Result = recvmmsg(Socket, ReceiveMessages, 1000, 0, &Timeout), EAGAIN);\r\n\r\n    //\r\n    // Restore the SO_RCVTIMEO timeout to 0 (never times out).\r\n    //\r\n\r\n    LxtCheckResult(SocketNetlinkSetAndVerifySocketOptionTimeout(Socket, SO_RCVTIMEO, 0));\r\n\r\n    //\r\n    // Test blocking behavior when zero flags (option 0) and when MSG_WAITFORONE\r\n    // (option 1) is passed to recvmmsg. Create a new thread that blocks forever,\r\n    // then cancel the thread after waiting for some time.\r\n    //\r\n\r\n    for (Index = 0; Index < 2; Index++)\r\n    {\r\n\r\n        //\r\n        // Create a reader thread that will block on 'recvmmsg'.\r\n        //\r\n\r\n        BlockedReaderParams.Socket = Socket;\r\n        BlockedReaderParams.Option = Index;\r\n        LxtCheckResultError(pthread_create(&Thread, NULL, SocketNetlinkRecvmmsgBlockedReaderThread, &BlockedReaderParams));\r\n\r\n        //\r\n        // Wait for sometime to allow the reader thread to block on read. There\r\n        // is no other elegant way of knowing whether the thread has blocked.\r\n        //\r\n\r\n        usleep(5000);\r\n\r\n        //\r\n        // The pthread_timedjoin_np() should timeout since the reader thread is\r\n        // still blocked.\r\n        //\r\n\r\n        TimeoutPthread.tv_sec = 0;\r\n        TimeoutPthread.tv_nsec = 5000;\r\n        Result = pthread_timedjoin_np(Thread, &PthreadResult, &TimeoutPthread);\r\n        if (Result != ETIMEDOUT)\r\n        {\r\n            LxtLogError(\r\n                \"Expecting pthread_tryjoin_np to return ETIMEDOUT(%d), \"\r\n                \"but it returned with result: %d for index: %d\",\r\n                ETIMEDOUT,\r\n                Result,\r\n                Index);\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // No other choice but to kill the reader thread.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(pthread_cancel(Thread));\r\n        LxtCheckErrnoZeroSuccess(pthread_join(Thread, &PthreadResult));\r\n        LxtCheckEqual(PthreadResult, PTHREAD_CANCELED, \"%p\");\r\n    }\r\n\r\n    //\r\n    // Now generate a lot of Netlink responses waiting to be read.\r\n    //\r\n\r\n    for (Index = 0; Index < 10; Index++)\r\n    {\r\n        LxtCheckErrno(SendResult = send(Socket, &Request, sizeof(Request), 0));\r\n        LxtCheckEqual(SendResult, sizeof(Request), \"%d\");\r\n    }\r\n\r\n    //\r\n    // Test various combinations of input parameters and verify the output.\r\n    //\r\n\r\n    LxtCheckErrno(Result = recvmmsg(Socket, ReceiveMessages, 3, MSG_DONTWAIT | MSG_WAITFORONE | MSG_WAITALL | MSG_PEEK | MSG_TRUNC, &Timeout));\r\n\r\n    LxtCheckEqual(Result, 3, \"%d\");\r\n    LxtCheckNotEqual(ReceiveMessages[0].msg_len, 0, \"%d\");\r\n    LxtCheckNotEqual(ReceiveMessages[1].msg_len, 0, \"%d\");\r\n    LxtCheckNotEqual(ReceiveMessages[2].msg_len, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveMessages[3].msg_len, 0, \"%d\");\r\n    LxtCheckErrno(Result = recvmmsg(Socket, ReceiveMessages, 2, MSG_DONTWAIT, &Timeout));\r\n    LxtCheckEqual(Result, 2, \"%d\");\r\n    LxtCheckNotEqual(ReceiveMessages[0].msg_len, 0, \"%d\");\r\n    LxtCheckNotEqual(ReceiveMessages[1].msg_len, 0, \"%d\");\r\n    LxtCheckNotEqual(ReceiveMessages[2].msg_len, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveMessages[3].msg_len, 0, \"%d\");\r\n    ReceiveMessages[0].msg_len = 0;\r\n    ReceiveMessages[1].msg_len = 0;\r\n    ReceiveMessages[2].msg_len = 0;\r\n    LxtCheckErrno(Result = recvmmsg(Socket, ReceiveMessages, 1, 0, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    LxtCheckNotEqual(ReceiveMessages[0].msg_len, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveMessages[1].msg_len, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveMessages[2].msg_len, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveMessages[3].msg_len, 0, \"%d\");\r\n    LxtCheckErrno(Result = recvmmsg(Socket, ReceiveMessages, 1, MSG_WAITFORONE, &Timeout));\r\n    LxtCheckEqual(Result, 1, \"%d\");\r\n    LxtCheckNotEqual(ReceiveMessages[0].msg_len, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveMessages[1].msg_len, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveMessages[2].msg_len, 0, \"%d\");\r\n    LxtCheckEqual(ReceiveMessages[3].msg_len, 0, \"%d\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid* SocketNetlinkRecvmmsgBlockedReaderThread(void* Arg)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine will call recvmmsg on the given socket fd and block.\r\n\r\nArguments:\r\n\r\n    Arg - Supplies the socket fd to read and the options.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    NetlinkRecvmmsgBlockedReaderParams* BlockedReaderParams;\r\n    int Flags;\r\n    int Index;\r\n    int MessagesRead;\r\n    char ReceiveBuffers[20][1000];\r\n    struct iovec ReceiveIovecs[20];\r\n    struct mmsghdr ReceiveMessages[20];\r\n    int Result = LXT_RESULT_FAILURE;\r\n    int Socket;\r\n    struct timespec Timeout;\r\n\r\n    //\r\n    // The timeout value passed to recvmmsg() is ignored. Set up the receive\r\n    // buffers.\r\n    //\r\n\r\n    BlockedReaderParams = (NetlinkRecvmmsgBlockedReaderParams*)Arg;\r\n    Socket = BlockedReaderParams->Socket;\r\n    Timeout.tv_sec = 800;\r\n    Timeout.tv_nsec = 0;\r\n    memset(ReceiveMessages, 0, sizeof(ReceiveMessages));\r\n    for (Index = 0; Index < 20; Index++)\r\n    {\r\n        ReceiveIovecs[Index].iov_base = ReceiveBuffers[Index];\r\n        ReceiveIovecs[Index].iov_len = 1000;\r\n        ReceiveMessages[Index].msg_hdr.msg_iov = &ReceiveIovecs[Index];\r\n        ReceiveMessages[Index].msg_hdr.msg_iovlen = 1;\r\n    }\r\n\r\n    //\r\n    // The flags passed to recvmmsg() depend on the option value passed into\r\n    // this function.\r\n    //\r\n\r\n    switch (BlockedReaderParams->Option)\r\n    {\r\n    case 0:\r\n        Flags = 0;\r\n        break;\r\n\r\n    case 1:\r\n        Flags = MSG_WAITFORONE;\r\n        break;\r\n\r\n    default:\r\n        LxtLogError(\"Incorrect option: %d\", BlockedReaderParams->Option);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(MessagesRead = recvmmsg(Socket, ReceiveMessages, 20, Flags, NULL));\r\n    if (MessagesRead != 0)\r\n    {\r\n        LxtLogError(\r\n            \"recvmmsg should return 0 messages read, \"\r\n            \"but it returned %d messages for flags %x\",\r\n            MessagesRead,\r\n            Flags);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"recvmmsg unblocked, flags %x\", Flags);\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    pthread_exit((void*)(ssize_t)Result);\r\n}\r\n\r\nint SocketNetlinkSendBadMessage(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends bad Netlink messages, to test that the system does not crash.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    char* Buffer;\r\n    struct nlmsghdr* Header;\r\n    char ReceiveBuffer;\r\n    int Result;\r\n    int Socket;\r\n    int TypeIterator;\r\n\r\n    Buffer = malloc(PAGE_SIZE * 2);\r\n    LxtCheckTrue(Buffer != NULL);\r\n    Header = (struct nlmsghdr*)(((uint64_t)(Buffer + PAGE_SIZE) & ~(PAGE_SIZE - 1)) - sizeof(struct nlmsghdr));\r\n    LxtLogInfo(\"Malloc end of page: %p\", Header);\r\n    for (TypeIterator = 0; TypeIterator < sizeof(MessageTypes) / sizeof(int); TypeIterator++)\r\n    {\r\n\r\n        LxtLogInfo(\"Checking message type: %d\", MessageTypes[TypeIterator]);\r\n        LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n        memset(&BindAddress, 0, sizeof(BindAddress));\r\n        BindAddress.nl_family = AF_NETLINK;\r\n        AddressLength = sizeof(BindAddress);\r\n        LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n        Header->nlmsg_flags = NLM_F_REQUEST;\r\n        Header->nlmsg_len = sizeof(struct nlmsghdr);\r\n        Header->nlmsg_type = MessageTypes[TypeIterator];\r\n        Header->nlmsg_seq = 0x4567;\r\n        sendto(Socket, Header, sizeof(struct nlmsghdr), 0, NULL, 0);\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    if (Buffer != NULL)\r\n    {\r\n        free(Buffer);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid SocketNetlinkRouteDumpAttributeData(char* Buffer, int BufferSize, struct rtattr* Attribute)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine dumps the data in the NETLINK_ROUTE protocol's RT attribute.\r\n\r\nArguments:\r\n\r\n    Buffer - Supplies a pointer to the buffer to write the dumped data to.\r\n\r\n    Attribute - Supplies a pointer to the RT attribute to dump.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    unsigned char* AttributeCurrent;\r\n    char* BufferCurrent;\r\n    int Index;\r\n\r\n    AttributeCurrent = (unsigned char*)RTA_DATA(Attribute);\r\n    BufferCurrent = Buffer;\r\n    memset(Buffer, 0, BufferSize);\r\n\r\n    //\r\n    // Each char takes 3 bytes in the dump buffer.\r\n    //\r\n\r\n    for (Index = 0; Index < min((BufferSize - 1) / 3, RTA_PAYLOAD(Attribute)); Index++)\r\n    {\r\n        sprintf(BufferCurrent, \"%02x \", *AttributeCurrent);\r\n        AttributeCurrent += 1;\r\n        BufferCurrent += 3;\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint SocketNetlinkRouteGetAddr(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the NETLINK_ROUTE protocol's RTM_GETADDR message.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct rtattr* Attribute;\r\n    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];\r\n    int AttributeSeenAddress;\r\n    int AttributeSeenCacheInfo;\r\n    struct sockaddr_nl BindAddress;\r\n    struct nlmsgerr* Error;\r\n    int FoundDone;\r\n    struct nlmsghdr* Header;\r\n    struct ifaddrmsg* IfAddrMsg;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    int RemainingLength;\r\n    int Result;\r\n    int Socket;\r\n\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct ifinfomsg ifm;\r\n        struct rtattr ext_req __attribute__((aligned(NLMSG_ALIGNTO)));\r\n        __u32 ext_filter_mask;\r\n    } Request;\r\n\r\n    AttributeSeenAddress = 0;\r\n    AttributeSeenCacheInfo = 0;\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETADDR request.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = sizeof(Request);\r\n    Request.nlh.nlmsg_type = RTM_GETADDR;\r\n    Request.nlh.nlmsg_seq = 0x4563;\r\n    Request.ifm.ifi_family = AF_NETLINK;\r\n    Request.ext_req.rta_type = IFLA_EXT_MASK;\r\n    Request.ext_req.rta_len = RTA_LENGTH(sizeof(__u32));\r\n    Request.ext_filter_mask = RTEXT_FILTER_VF;\r\n\r\n    //\r\n    // Test flags. Only passing the NLM_F_REQUEST flag returns an Error.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -EOPNOTSUPP, \"%d\");\r\n    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, \"%d\");\r\n\r\n    //\r\n    // Test flags. Only passing the NLM_F_REQUEST flag returns an Error.\r\n    // Verify that adding a NLM_F_ACK flag still returns an Error.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -EOPNOTSUPP, \"%d\");\r\n    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, \"%d\");\r\n\r\n    //\r\n    // Test flags. Only passing the NLM_F_ROOT and/or NLM_F_MATCH flag(s)\r\n    // result in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Test flags. Passing 0 flags or invalid flags result in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = 0x40;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Test flags. NLM_F_REQUEST and at least one of NLM_F_ROOT/NLM_F_MATCH\r\n    // results in the correct response.\r\n    // For the response, verify the presence of several attributes.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    FoundDone = 0;\r\n    for (;;)\r\n    {\r\n        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));\r\n\r\n        Header = (struct nlmsghdr*)ReceiveBuffer;\r\n        while (NLMSG_OK(Header, ReceiveResult))\r\n        {\r\n            if (Header->nlmsg_type == NLMSG_DONE)\r\n            {\r\n                FoundDone = 1;\r\n                break;\r\n            }\r\n\r\n            IfAddrMsg = (struct ifaddrmsg*)NLMSG_DATA(Header);\r\n            LxtLogInfo(\r\n                \"ifaddrmsg: ifa_family %d ifa_prefixlen %d \"\r\n                \"ifa_flags %d ifa_scope %d ifa_index %d\",\r\n                IfAddrMsg->ifa_family,\r\n                IfAddrMsg->ifa_prefixlen,\r\n                IfAddrMsg->ifa_flags,\r\n                IfAddrMsg->ifa_scope,\r\n                IfAddrMsg->ifa_index);\r\n\r\n            Attribute = (struct rtattr*)((char*)IfAddrMsg + sizeof(struct ifaddrmsg));\r\n\r\n            RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));\r\n\r\n            while (RTA_OK(Attribute, RemainingLength))\r\n            {\r\n                SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);\r\n\r\n                LxtLogInfo(\"RTATTR type: %2d len: %3d data: %s\", Attribute->rta_type, Attribute->rta_len, AttributeDump);\r\n\r\n                if (Attribute->rta_type == IFA_ADDRESS)\r\n                {\r\n                    AttributeSeenAddress += 1;\r\n                }\r\n                else if (Attribute->rta_type == IFA_CACHEINFO)\r\n                {\r\n                    LxtCheckEqual(Attribute->rta_len, RTA_LENGTH(sizeof(struct ifa_cacheinfo)), \"%d\");\r\n\r\n                    AttributeSeenCacheInfo += 1;\r\n                }\r\n\r\n                Attribute = RTA_NEXT(Attribute, RemainingLength);\r\n            }\r\n\r\n            Header = NLMSG_NEXT(Header, ReceiveResult);\r\n        }\r\n\r\n        if (FoundDone == 1)\r\n        {\r\n            break;\r\n        }\r\n    }\r\n\r\n    LxtCheckTrue(AttributeSeenAddress > 0);\r\n    LxtCheckTrue(AttributeSeenCacheInfo > 0);\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkRouteGetLink(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the NETLINK_ROUTE protocol's RTM_GETLINK message.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    pid_t ChildPid;\r\n    int ChildSocket;\r\n    struct nlmsgerr* Error;\r\n    struct nlmsghdr* Header;\r\n    int Index;\r\n    char InterfaceName[32];\r\n    int LoopbackIndex;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    int Result;\r\n    int SendResult;\r\n    int Socket;\r\n\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct ifinfomsg ifm;\r\n        char Attributes[200];\r\n    } Request;\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETLINK request.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = sizeof(Request);\r\n    Request.nlh.nlmsg_type = RTM_GETLINK;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.ifm.ifi_family = AF_NETLINK;\r\n\r\n    //\r\n    // Test flags. Only passing the NLM_F_REQUEST flag with no network interface\r\n    // specified returns an Error.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -EINVAL, \"%d\");\r\n    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, \"%d\");\r\n\r\n    //\r\n    // Test flags. Only passing the NLM_F_REQUEST flag with no network interface\r\n    // returns an Error. Verify that adding a NLM_F_ACK flag still returns an Error.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -EINVAL, \"%d\");\r\n    LxtCheckEqual(memcmp(&Error->msg, &Request, sizeof(Request)), 0, \"%d\");\r\n\r\n    //\r\n    // Test flags. Only passing the NLM_F_ROOT and/or NLM_F_MATCH flag(s)\r\n    // result in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Test flags. Passing 0 flags or invalid flags result in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = 0x40;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Test filter mode.\r\n    // When passing only the NLM_F_REQUEST flag, \"filter\" mode is used.\r\n    // In this mode, either ifinfomsg.ifi_index must be valid, or IFLA_IFNAME\r\n    // must be present to filter the response to one network interface.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n\r\n    //\r\n    // Passing a zero ifi_index returns an Error.\r\n    //\r\n\r\n    Request.ifm.ifi_index = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -EINVAL, \"%d\");\r\n\r\n    //\r\n    // Passing a negative ifi_index returns an Error.\r\n    //\r\n\r\n    Request.ifm.ifi_index = -1;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -EINVAL, \"%d\");\r\n\r\n    //\r\n    // Passing a bad ifi_index and the correct interface name returns an Error.\r\n    //\r\n\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));\r\n    Request.ifm.ifi_index = 100;\r\n    strcpy(InterfaceName, \"lo\");\r\n    LxtCheckErrnoZeroSuccess(\r\n        SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFLA_IFNAME, InterfaceName, strlen(InterfaceName) + 1));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -ENODEV, \"%d\");\r\n\r\n    //\r\n    // Passing a negative ifi_index and the correct interface name results in the\r\n    // correct response.\r\n    //\r\n\r\n    Request.ifm.ifi_index = -1;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, TRUE));\r\n\r\n    //\r\n    // Passing a zero ifi_index and the correct interface name results in the\r\n    // correct response.\r\n    //\r\n\r\n    Request.ifm.ifi_index = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, TRUE));\r\n\r\n    //\r\n    // Passing a bad ifi_index and a bad interface name returns an Error.\r\n    //\r\n\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));\r\n    Request.ifm.ifi_index = 100;\r\n    strcpy(InterfaceName, \"loooo\");\r\n    LxtCheckErrnoZeroSuccess(\r\n        SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFLA_IFNAME, InterfaceName, strlen(InterfaceName) + 1));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -ENODEV, \"%d\");\r\n\r\n    //\r\n    // Passing a zero ifi_index and a bad interface name returns an Error.\r\n    //\r\n\r\n    Request.ifm.ifi_index = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -ENODEV, \"%d\");\r\n\r\n    //\r\n    // Passing a negative ifi_index and a bad interface name returns an Error.\r\n    //\r\n\r\n    Request.ifm.ifi_index = -1;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    Error = (struct nlmsgerr*)NLMSG_DATA(Header);\r\n    LxtCheckEqual(Error->error, -ENODEV, \"%d\");\r\n\r\n    //\r\n    // Get the interface index of the loopback adapter.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));\r\n    LxtCheckTrue(LoopbackIndex > 0);\r\n\r\n    //\r\n    // Passing a correct ifi_index and a bad interface name results in the\r\n    // correct response.\r\n    //\r\n\r\n    Request.ifm.ifi_index = LoopbackIndex;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, TRUE));\r\n\r\n    //\r\n    // Passing a correct ifi_index and no interface name results in the\r\n    // correct response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, TRUE));\r\n\r\n    //\r\n    // Test dump mode.\r\n    // NLM_F_REQUEST and at least one of NLM_F_ROOT/NLM_F_MATCH\r\n    // results in the correct response.\r\n    // For the response, verify that at least one interface is present (loopback),\r\n    // and also verify the presence of several attributes.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(Socket, FALSE));\r\n\r\n    //\r\n    // Create a child process and switch it to a new network namespace.\r\n    // RTM_GETLINK should only return one network interface - loopback.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(unshare(CLONE_NEWNET));\r\n\r\n        //\r\n        // N.B. The sleep is because it can take some time for the lxcore cache to get\r\n        //      the new network interface notification.\r\n        //\r\n\r\n        usleep(1000 * 100);\r\n        LxtLogInfo(\"Now testing child socket\");\r\n        LxtCheckErrno(ChildSocket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n        LxtCheckErrno(sendto(ChildSocket, &Request, sizeof(Request), 0, NULL, 0));\r\n        LxtCheckResult(SocketNetlinkRouteGetLinkCheckResponse(ChildSocket, TRUE));\r\n        LxtClose(ChildSocket);\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // Check that sending is still successful even if the\r\n    // receive buffer has overflown.\r\n    //\r\n\r\n    for (Index = 0; Index < 1000; Index++)\r\n    {\r\n        LxtCheckErrno(SendResult = send(Socket, &Request, sizeof(Request), 0));\r\n        LxtCheckEqual(SendResult, sizeof(Request), \"%d\");\r\n    }\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkRouteGetLinkCheckResponse(int Socket, int OnlyOneInterface)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks the response of the NETLINK_ROUTE protocol's\r\n    RTM_GETLINK message.\r\n\r\nArguments:\r\n\r\n    Socket - Supplies the socket to read the response from.\r\n\r\n    OnlyOneInterface - Supplies a boolean indicating whether there should only\r\n        be one network interface in the response.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct rtattr* Attribute;\r\n    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];\r\n    int AttributeSeenAddress;\r\n    int AttributeSeenMtu;\r\n    int AttributeSeenName;\r\n    int FoundDone;\r\n    struct nlmsghdr* Header;\r\n    struct ifinfomsg* IfInfoMsg;\r\n    int InterfaceCount;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    int RemainingLength;\r\n    int Result;\r\n    int SeenLoopback;\r\n\r\n    AttributeSeenAddress = 0;\r\n    AttributeSeenMtu = 0;\r\n    AttributeSeenName = 0;\r\n    SeenLoopback = 0;\r\n    FoundDone = 0;\r\n    InterfaceCount = 0;\r\n    for (;;)\r\n    {\r\n        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));\r\n\r\n        Header = (struct nlmsghdr*)ReceiveBuffer;\r\n        while (NLMSG_OK(Header, ReceiveResult))\r\n        {\r\n            if (Header->nlmsg_type == NLMSG_DONE)\r\n            {\r\n                FoundDone = 1;\r\n                break;\r\n            }\r\n\r\n            IfInfoMsg = (struct ifinfomsg*)NLMSG_DATA(Header);\r\n            LxtLogInfo(\r\n                \"ifinfomsg: ifi_family %d ifi_type %d \"\r\n                \"ifi_index %d ifi_flags %d ifi_change %d\",\r\n                IfInfoMsg->ifi_family,\r\n                IfInfoMsg->ifi_type,\r\n                IfInfoMsg->ifi_index,\r\n                IfInfoMsg->ifi_flags,\r\n                IfInfoMsg->ifi_change);\r\n\r\n            InterfaceCount += 1;\r\n            if ((IfInfoMsg->ifi_flags & IFF_LOOPBACK) != 0)\r\n            {\r\n                SeenLoopback += 1;\r\n            }\r\n\r\n            Attribute = (struct rtattr*)((char*)IfInfoMsg + sizeof(struct ifinfomsg));\r\n\r\n            RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));\r\n\r\n            while (RTA_OK(Attribute, RemainingLength))\r\n            {\r\n                SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);\r\n\r\n                LxtLogInfo(\"RTATTR type: %2d len: %3d data: %s\", Attribute->rta_type, Attribute->rta_len, AttributeDump);\r\n\r\n                if (Attribute->rta_type == IFLA_ADDRESS)\r\n                {\r\n                    AttributeSeenAddress += 1;\r\n                }\r\n                else if (Attribute->rta_type == IFLA_MTU)\r\n                {\r\n                    AttributeSeenMtu += 1;\r\n                }\r\n                else if (Attribute->rta_type == IFLA_IFNAME)\r\n                {\r\n                    AttributeSeenName += 1;\r\n                }\r\n\r\n                Attribute = RTA_NEXT(Attribute, RemainingLength);\r\n            }\r\n\r\n            if ((Header->nlmsg_flags & NLM_F_MULTI) == 0)\r\n            {\r\n                goto LoopDone;\r\n            }\r\n\r\n            Header = NLMSG_NEXT(Header, ReceiveResult);\r\n        }\r\n\r\n        if (FoundDone == 1)\r\n        {\r\n            break;\r\n        }\r\n    }\r\n\r\nLoopDone:\r\n    LxtCheckTrue(AttributeSeenAddress > 0);\r\n    LxtCheckTrue(AttributeSeenMtu > 0);\r\n    LxtCheckTrue(AttributeSeenName > 0);\r\n    LxtCheckEqual(SeenLoopback, 1, \"%d\");\r\n    LxtLogInfo(\"Found %d interfaces total\", InterfaceCount);\r\n    if (OnlyOneInterface != FALSE)\r\n    {\r\n        LxtCheckEqual(InterfaceCount, 1, \"%d\");\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkRouteGetRouteBestRoute(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the NETLINK_ROUTE protocol's RTM_GETROUTE message's\r\n    get best route request.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    struct in_addr DestinationIpv4;\r\n    struct in6_addr DestinationIpv6;\r\n    struct in_addr GatewayIpv4;\r\n    struct in6_addr GatewayIpv6;\r\n    struct nlmsgerr* Error;\r\n    struct nlmsghdr* Header;\r\n    int Index;\r\n    bool IsIpV6Configured;\r\n    int LoopbackIndex;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    int Result;\r\n    int SendResult;\r\n    int Socket;\r\n\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct rtmsg msg;\r\n        char attr[200];\r\n    } Request;\r\n\r\n    //\r\n    // Get the interface index of the loopback adapter.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));\r\n    LxtCheckTrue(LoopbackIndex > 0);\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETROUTE request.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_GETROUTE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n\r\n    //\r\n    // NLM_F_REQUEST with no NLM_F_DUMP means \"get best route\" request.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n\r\n    //\r\n    // Test specifying an invalid address family.\r\n    //\r\n\r\n    Request.msg.rtm_family = AF_UNSPEC;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EOPNOTSUPP));\r\n\r\n    //\r\n    // Test specifying AF_INET6 while really giving an IPv4 destination address.\r\n    //\r\n\r\n    Request.msg.rtm_family = AF_INET6;\r\n    inet_aton(\"1.1.1.1\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, -1);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EINVAL));\r\n\r\n    IsIpV6Configured = SocketNetlinkIsIpv6Configured();\r\n\r\n    //\r\n    // Test not specifying the destination (both Ipv4 and Ipv6).\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_GETROUTE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    Request.msg.rtm_family = AF_INET;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, FALSE));\r\n    Request.msg.rtm_family = AF_INET6;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    if (IsIpV6Configured)\r\n    {\r\n        LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, FALSE));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n        LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENETUNREACH));\r\n    }\r\n\r\n    //\r\n    // Test specifying the destination (Ipv4).\r\n    //\r\n\r\n    inet_aton(\"1.1.1.1\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, -1);\r\n\r\n    Request.msg.rtm_family = AF_INET;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, TRUE));\r\n\r\n    //\r\n    // Test specifying the destination (Ipv6).\r\n    //\r\n    if (IsIpV6Configured)\r\n    {\r\n        memset(&Request, 0, sizeof(Request));\r\n        Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n        Request.nlh.nlmsg_type = RTM_GETROUTE;\r\n        Request.nlh.nlmsg_seq = 0x4567;\r\n        Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n        Request.msg.rtm_family = AF_INET6;\r\n        inet_pton(AF_INET6, \"12::\", &GatewayIpv6);\r\n        LxtCheckErrnoZeroSuccess(\r\n            SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));\r\n\r\n        LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n        LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, TRUE));\r\n    }\r\n\r\n    //\r\n    // Test specifying the destination and gateway (Ipv4).\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_GETROUTE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    Request.msg.rtm_family = AF_INET;\r\n    inet_aton(\"1.1.1.1\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.2\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, -1);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, TRUE));\r\n\r\n    //\r\n    // Test specifying the destination and gateway (Ipv6).\r\n    //\r\n\r\n    if (IsIpV6Configured)\r\n    {\r\n        memset(&Request, 0, sizeof(Request));\r\n        Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n        Request.nlh.nlmsg_type = RTM_GETROUTE;\r\n        Request.nlh.nlmsg_seq = 0x4567;\r\n        Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n        Request.msg.rtm_family = AF_INET6;\r\n        inet_pton(AF_INET6, \"12::\", &DestinationIpv6);\r\n        inet_pton(AF_INET6, \"13::\", &GatewayIpv6);\r\n        LxtCheckErrnoZeroSuccess(\r\n            SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));\r\n\r\n        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv6, sizeof(GatewayIpv6)));\r\n\r\n        LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n        LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, TRUE));\r\n    }\r\n\r\n    //\r\n    // Test specifying the destination, gateway and interface index (Ipv4).\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_GETROUTE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    Request.msg.rtm_family = AF_INET;\r\n    inet_aton(\"127.0.0.1\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.2\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, FALSE));\r\n\r\n    //\r\n    // Test specifying the destination, gateway and interface index (Ipv6).\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_GETROUTE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    Request.msg.rtm_family = AF_INET6;\r\n    inet_pton(AF_INET6, \"::1\", &DestinationIpv6);\r\n    inet_pton(AF_INET6, \"13::\", &GatewayIpv6);\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv6, sizeof(GatewayIpv6)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckResult(SocketNetlinkRouteGetRouteBestRouteCheckResponse(Socket, FALSE));\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkRouteGetRouteBestRouteCheckResponse(int Socket, int CheckGateway)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the response to the NETLINK_ROUTE protocol's\r\n    RTM_GETROUTE message's get best route request.\r\n\r\nArguments:\r\n\r\n    Socket - Supplies the socket to read the response from.\r\n\r\n    CheckGateway - Supplies a boolean indicating whether to check for the\r\n        presence of the RTA_GATEWAY attribute.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct rtattr* Attribute;\r\n    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];\r\n    int AttributeSeenDst;\r\n    int AttributeSeenGateway;\r\n    int AttributeSeenOif;\r\n    int FoundDone;\r\n    struct nlmsghdr* Header;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    int RemainingLength;\r\n    int Result;\r\n    struct rtmsg* RtMsg;\r\n\r\n    AttributeSeenDst = 0;\r\n    AttributeSeenGateway = 0;\r\n    AttributeSeenOif = 0;\r\n    FoundDone = 0;\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    RtMsg = (struct rtmsg*)NLMSG_DATA(Header);\r\n    LxtLogInfo(\r\n        \"rtmsg: rtm_family %d rtm_dst_len %d rtm_src_len %d \"\r\n        \"rtm_tos %d rtm_table %d rtm_protocol %d \"\r\n        \"rtm_scope %d rtm_type %d rtm_flags %d\",\r\n        RtMsg->rtm_family,\r\n        RtMsg->rtm_dst_len,\r\n        RtMsg->rtm_src_len,\r\n        RtMsg->rtm_tos,\r\n        RtMsg->rtm_table,\r\n        RtMsg->rtm_protocol,\r\n        RtMsg->rtm_scope,\r\n        RtMsg->rtm_type,\r\n        RtMsg->rtm_flags);\r\n\r\n    Attribute = (struct rtattr*)((char*)RtMsg + sizeof(struct rtmsg));\r\n    RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    while (RTA_OK(Attribute, RemainingLength))\r\n    {\r\n        SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);\r\n\r\n        LxtLogInfo(\"RTATTR type: %2d len: %3d data: %s\", Attribute->rta_type, Attribute->rta_len, AttributeDump);\r\n\r\n        if (Attribute->rta_type == RTA_DST)\r\n        {\r\n            AttributeSeenDst += 1;\r\n        }\r\n        else if (Attribute->rta_type == RTA_GATEWAY)\r\n        {\r\n            AttributeSeenGateway += 1;\r\n        }\r\n        else if (Attribute->rta_type == RTA_OIF)\r\n        {\r\n            AttributeSeenOif += 1;\r\n        }\r\n\r\n        Attribute = RTA_NEXT(Attribute, RemainingLength);\r\n    }\r\n\r\n    LxtCheckTrue(AttributeSeenDst > 0);\r\n    if (CheckGateway != FALSE)\r\n    {\r\n        LxtCheckTrue(AttributeSeenGateway > 0);\r\n    }\r\n\r\n    LxtCheckTrue(AttributeSeenOif > 0);\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkRouteGetRouteDump(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the NETLINK_ROUTE protocol's RTM_GETROUTE message's\r\n    dump request.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct rtattr* Attribute;\r\n    char AttributeDump[ATTRIBUTE_DUMP_BUFFER_SIZE];\r\n    int AttributeSeenDst;\r\n    int AttributeSeenGateway;\r\n    int AttributeSeenOif;\r\n    int AttributeSeenPriority;\r\n    struct sockaddr_nl BindAddress;\r\n    struct nlmsgerr* Error;\r\n    int FoundDone;\r\n    struct nlmsghdr* Header;\r\n    int Index;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    int RemainingLength;\r\n    int Result;\r\n    struct rtmsg* RtMsg;\r\n    int SendResult;\r\n    int Socket;\r\n\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct rtmsg msg;\r\n    } Request;\r\n\r\n    AttributeSeenDst = 0;\r\n    AttributeSeenGateway = 0;\r\n    AttributeSeenOif = 0;\r\n    AttributeSeenPriority = 0;\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETROUTE request.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = sizeof(Request);\r\n    Request.nlh.nlmsg_type = RTM_GETROUTE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_UNSPEC;\r\n\r\n    //\r\n    // Test flags. Only passing the NLM_F_ROOT and/or NLM_F_MATCH flag(s)\r\n    // result in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Test flags. Passing 0 flags or invalid flags result in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = 0x40;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Test flags. NLM_F_REQUEST and at least one of NLM_F_ROOT/NLM_F_MATCH\r\n    // results in the correct response.\r\n    // For the response, verify that at least one interface is present (loopback),\r\n    // and also verify the presence of several attributes.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    FoundDone = 0;\r\n    for (;;)\r\n    {\r\n        LxtCheckErrno(ReceiveResult = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, NULL, 0));\r\n\r\n        Header = (struct nlmsghdr*)ReceiveBuffer;\r\n        while (NLMSG_OK(Header, ReceiveResult))\r\n        {\r\n            if (Header->nlmsg_type == NLMSG_DONE)\r\n            {\r\n                FoundDone = 1;\r\n                break;\r\n            }\r\n\r\n            RtMsg = (struct rtmsg*)NLMSG_DATA(Header);\r\n            LxtLogInfo(\r\n                \"rtmsg: rtm_family %d rtm_dst_len %d rtm_src_len %d \"\r\n                \"rtm_tos %d rtm_table %d rtm_protocol %d \"\r\n                \"rtm_scope %d rtm_type %d rtm_flags %d\",\r\n                RtMsg->rtm_family,\r\n                RtMsg->rtm_dst_len,\r\n                RtMsg->rtm_src_len,\r\n                RtMsg->rtm_tos,\r\n                RtMsg->rtm_table,\r\n                RtMsg->rtm_protocol,\r\n                RtMsg->rtm_scope,\r\n                RtMsg->rtm_type,\r\n                RtMsg->rtm_flags);\r\n\r\n            Attribute = (struct rtattr*)((char*)RtMsg + sizeof(struct rtmsg));\r\n\r\n            RemainingLength = Header->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));\r\n\r\n            while (RTA_OK(Attribute, RemainingLength))\r\n            {\r\n                SocketNetlinkRouteDumpAttributeData(AttributeDump, ATTRIBUTE_DUMP_BUFFER_SIZE, Attribute);\r\n\r\n                LxtLogInfo(\"RTATTR type: %2d len: %3d data: %s\", Attribute->rta_type, Attribute->rta_len, AttributeDump);\r\n\r\n                if (Attribute->rta_type == RTA_DST)\r\n                {\r\n                    AttributeSeenDst += 1;\r\n                }\r\n                else if (Attribute->rta_type == RTA_GATEWAY)\r\n                {\r\n                    AttributeSeenGateway += 1;\r\n                }\r\n                else if (Attribute->rta_type == RTA_OIF)\r\n                {\r\n                    AttributeSeenOif += 1;\r\n                }\r\n                else if (Attribute->rta_type == RTA_PRIORITY)\r\n                {\r\n                    AttributeSeenPriority += 1;\r\n                }\r\n\r\n                Attribute = RTA_NEXT(Attribute, RemainingLength);\r\n            }\r\n\r\n            Header = NLMSG_NEXT(Header, ReceiveResult);\r\n        }\r\n\r\n        if (FoundDone == 1)\r\n        {\r\n            break;\r\n        }\r\n    }\r\n\r\n    LxtCheckTrue(AttributeSeenDst > 0);\r\n    LxtCheckTrue(AttributeSeenGateway > 0);\r\n    LxtCheckTrue(AttributeSeenOif > 0);\r\n    LxtCheckTrue(AttributeSeenPriority > 0);\r\n\r\n    //\r\n    // Check that sending is still successful even if the\r\n    // receive buffer has overflown.\r\n    //\r\n\r\n    for (Index = 0; Index < 1000; Index++)\r\n    {\r\n        LxtCheckErrno(SendResult = send(Socket, &Request, sizeof(Request), 0));\r\n        LxtCheckEqual(SendResult, sizeof(Request), \"%d\");\r\n    }\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkGetLoopbackIndex(int* LoopbackIndex)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine obtains the interface index of the loopback adapter.\r\n\r\nArguments:\r\n\r\n    LoopbackIndex - Supplies a pointer to an integer that receives the\r\n        interface index of the loopback adapter.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct ifreq InterfaceRequest;\r\n    int Result;\r\n    int Socket;\r\n\r\n    LxtCheckErrno(Socket = socket(AF_INET, SOCK_STREAM, 0));\r\n    memset(&InterfaceRequest, 0, sizeof(InterfaceRequest));\r\n    strncpy(InterfaceRequest.ifr_name, SOCKET_LOOPBACK_IF_NAME, sizeof(InterfaceRequest.ifr_name) - 1);\r\n\r\n    LxtCheckErrnoZeroSuccess(ioctl(Socket, SIOCGIFINDEX, &InterfaceRequest));\r\n    *LoopbackIndex = InterfaceRequest.ifr_ifindex;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkCheckResponseError(void* ReceiveBuffer, int ReceiveResult, int Error)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine checks the Netlink error message response.\r\n\r\nArguments:\r\n\r\n    ReceiveBuffer - Supplies a pointer to the error message.\r\n\r\n    ReceiveResult - Supplies the total size of the error message.\r\n\r\n    Error - Supplies the error number to check for in the error message.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct nlmsgerr* ErrorMessage;\r\n    struct nlmsghdr* ReceiveHeader;\r\n    int Result;\r\n\r\n    ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(ReceiveHeader, ReceiveResult));\r\n    LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    ErrorMessage = (struct nlmsgerr*)NLMSG_DATA(ReceiveHeader);\r\n    LxtCheckEqual(ErrorMessage->error, Error, \"%d\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkRouteAddAttribute(struct nlmsghdr* Msghdr, int MessageSize, int AttributeType, void* AttributeData, int AttributeSize)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine adds a RT attribute to the Netlink message.\r\n\r\nArguments:\r\n\r\n    Msghdr - Supplies the message header.\r\n\r\n    MessageSize - Supplies the total possible size of the message.\r\n\r\n    AttributeType - Supplies the RT attribute type.\r\n\r\n    AttributeData - Supplies the RT attribute data.\r\n\r\n    AttributeSize - Supplies the RT attribute size.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Length;\r\n    struct rtattr* Attribute;\r\n\r\n    Length = RTA_LENGTH(AttributeSize);\r\n    if (NLMSG_ALIGN(Msghdr->nlmsg_len) + RTA_ALIGN(Length) > MessageSize)\r\n    {\r\n        LxtLogError(\"Adding RT attribute, message size overflowed: %d %d %d\", Msghdr->nlmsg_len, Length, MessageSize);\r\n\r\n        return -1;\r\n    }\r\n\r\n    Attribute = NLMSG_TAIL(Msghdr);\r\n    Attribute->rta_type = AttributeType;\r\n    Attribute->rta_len = Length;\r\n    memcpy(RTA_DATA(Attribute), AttributeData, AttributeSize);\r\n    Msghdr->nlmsg_len = NLMSG_ALIGN(Msghdr->nlmsg_len) + RTA_ALIGN(Length);\r\n    return 0;\r\n}\r\n\r\nvoid SocketNetlinkRouteAddAddressAttributes(struct nlmsghdr* Msghdr, int MessageSize, struct in_addr* AddressIpv4)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine appends the RTM_*ADDR message RT attributes to the message.\r\n\r\nArguments:\r\n\r\n    Msghdr - Supplies the message header.\r\n\r\n    MessageSize - Supplies the total possible size of the message.\r\n\r\n    AddressIpv4 - Supplies a pointer to the IPv4 address RT attribute.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    if (AddressIpv4 != NULL)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, IFA_ADDRESS, AddressIpv4, sizeof(*AddressIpv4)));\r\n\r\n        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, IFA_LOCAL, AddressIpv4, sizeof(*AddressIpv4)));\r\n    }\r\n\r\nErrorExit:\r\n    return;\r\n}\r\n\r\nvoid SocketNetlinkRouteAddRouteAttributes(struct nlmsghdr* Msghdr, int MessageSize, struct in_addr* DestinationIpv4, struct in_addr* GatewayIpv4, int InterfaceIndex)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine appends the RTM_*ROUTE message RT attributes to the message.\r\n\r\nArguments:\r\n\r\n    Msghdr - Supplies the message header.\r\n\r\n    MessageSize - Supplies the total possible size of the message.\r\n\r\n    DestinationIpv4 - Supplies a pointer to the destination IPv4 address RT attribute.\r\n\r\n    GatewayIpv4 - Supplies a pointer to the gateway IPv4 address RT attribute.\r\n\r\n    InterfaceIndex - Supplies the interface index RT attribute.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    if (DestinationIpv4 != NULL)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, RTA_DST, DestinationIpv4, sizeof(*DestinationIpv4)));\r\n    }\r\n\r\n    if (GatewayIpv4 != NULL)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, RTA_GATEWAY, GatewayIpv4, sizeof(*GatewayIpv4)));\r\n    }\r\n\r\n    if (InterfaceIndex > 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(Msghdr, MessageSize, RTA_OIF, &InterfaceIndex, sizeof(InterfaceIndex)));\r\n    }\r\n\r\nErrorExit:\r\n    return;\r\n}\r\n\r\nint SocketNetlinkRouteNewDelAddress(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the NETLINK_ROUTE protocol's\r\n    RTM_NEWADDR and RTM_DELADDR messages.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct in_addr AddressIpv4;\r\n    struct in6_addr AddressIpv6;\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    struct nlmsgerr* Error;\r\n    struct in_addr GatewayIpv4;\r\n    struct in6_addr GatewayIpv6;\r\n    int LoopbackIndex;\r\n    char ReceiveBuffer[5000];\r\n    struct nlmsghdr* ReceiveHeader;\r\n    int ReceiveResult;\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct ifaddrmsg msg;\r\n        char attr[200];\r\n    } Request;\r\n    int Result;\r\n    int Retries;\r\n    int Socket;\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETROUTE request.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWADDR;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.ifa_family = AF_UNSPEC;\r\n\r\n    //\r\n    // Get the interface index of the loopback adapter. All the tests\r\n    // below operate on the loopback adapter.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));\r\n    LxtCheckTrue(LoopbackIndex > 0);\r\n\r\n    //\r\n    // Test flags. Passing in (invalid) flags for RTM_GET* results in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Test invalid flags with NLM_F_ACK.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Test flags. Passing 0 flags or invalid flags result in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = 0x40;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Add an ip address 2.1.1.1/30.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWADDR;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.ifa_prefixlen = 30;\r\n    Request.msg.ifa_index = LoopbackIndex;\r\n    inet_aton(\"2.1.1.1\", &AddressIpv4);\r\n\r\n    //\r\n    // Test specifying an invalid address family.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    Request.msg.ifa_family = AF_UNSPEC;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EOPNOTSUPP));\r\n\r\n    //\r\n    // Test not specifying the ip address, which should fail.\r\n    //\r\n\r\n    Request.msg.ifa_family = AF_INET;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EINVAL));\r\n\r\n    //\r\n    // Basically any flag allows you to create.\r\n    // Windows can take up to 10 seconds before sending the WNF notification that lxcore\r\n    // uses to update its internal network interface and address cache. Before the cache\r\n    // is updated, this create operation will fail, so wait for 20 seconds for the\r\n    // operation to succeed.\r\n    //\r\n\r\n    SocketNetlinkRouteAddAddressAttributes(&Request.nlh, sizeof(Request), &AddressIpv4);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;\r\n    LxtCheckResult(SocketNetlinkSendAndWaitForExpectedError(Socket, &Request, sizeof(Request), 0));\r\n\r\n    //\r\n    // Test NLM_F_REPLACE, which should succeed.\r\n    // Windows can take up to 10 seconds before sending the WNF notification that lxcore\r\n    // uses to update its internal network interface and address cache. Before the cache\r\n    // is updated, this replace operation will fail, so wait for 20 seconds for the\r\n    // operation to succeed.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK;\r\n    LxtCheckResult(SocketNetlinkSendAndWaitForExpectedError(Socket, &Request, sizeof(Request), 0));\r\n\r\n    //\r\n    // Test NLM_F_EXCL, which should fail since it prevents existing addresses\r\n    // from being changed.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK;\r\n    LxtCheckResult(SocketNetlinkSendAndWaitForExpectedError(Socket, &Request, sizeof(Request), -EEXIST));\r\n\r\n    //\r\n    // Even if NLM_F_REPLACE is added to NLM_F_EXCL, it still fails.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));\r\n\r\n    //\r\n    // NLM_F_CREATE without NLM_F_REPLACE fails.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));\r\n\r\n    //\r\n    // NLM_F_CREATE with NLM_F_REPLACE succeeds.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Test giving an invalid interface index, which should fail.\r\n    //\r\n\r\n    Request.msg.ifa_index = 90000;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENODEV));\r\n\r\n    //\r\n    // Add an ip address 2.1.1.2/32.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWADDR;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.ifa_family = AF_INET;\r\n    Request.msg.ifa_prefixlen = 32;\r\n    Request.msg.ifa_index = LoopbackIndex;\r\n    inet_aton(\"2.1.1.2\", &AddressIpv4);\r\n    SocketNetlinkRouteAddAddressAttributes(&Request.nlh, sizeof(Request), &AddressIpv4);\r\n\r\n    //\r\n    // Only setting the NLM_F_REQUEST flag allows creating a new ip address.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Add an ip address 11::/31.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWADDR;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.ifa_family = AF_INET6;\r\n    Request.msg.ifa_prefixlen = 31;\r\n    Request.msg.ifa_index = LoopbackIndex;\r\n    inet_pton(AF_INET6, \"11::\", &AddressIpv6);\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFA_ADDRESS, &AddressIpv6, sizeof(AddressIpv6)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFA_LOCAL, &AddressIpv6, sizeof(AddressIpv6)));\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Delete 2.1.1.1/30.\r\n    //\r\n\r\n    usleep(1000 * 40);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELADDR;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.ifa_family = AF_INET;\r\n    Request.msg.ifa_prefixlen = 30;\r\n    Request.msg.ifa_index = LoopbackIndex;\r\n    inet_aton(\"2.1.1.1\", &AddressIpv4);\r\n    SocketNetlinkRouteAddAddressAttributes(&Request.nlh, sizeof(Request), &AddressIpv4);\r\n\r\n    //\r\n    // Passing 0 flags results in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Need at least NLM_F_REQUEST.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Try deleting again, which should fail.\r\n    //\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EADDRNOTAVAIL));\r\n\r\n    //\r\n    // Delete 2.1.1.2/32.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELADDR;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.ifa_family = AF_INET;\r\n    Request.msg.ifa_prefixlen = 32;\r\n    Request.msg.ifa_index = LoopbackIndex;\r\n    inet_aton(\"2.1.1.2\", &AddressIpv4);\r\n    SocketNetlinkRouteAddAddressAttributes(&Request.nlh, sizeof(Request), &AddressIpv4);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Test giving an invalid interface index, which should fail.\r\n    //\r\n\r\n    Request.msg.ifa_index = 90000;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENODEV));\r\n\r\n    //\r\n    // Delete ip address 11::/31.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELADDR;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.ifa_family = AF_INET6;\r\n    Request.msg.ifa_prefixlen = 31;\r\n    Request.msg.ifa_index = LoopbackIndex;\r\n    inet_pton(AF_INET6, \"11::\", &AddressIpv6);\r\n\r\n    //\r\n    // Test not specifying the ip address, which should fail.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EINVAL));\r\n\r\n    //\r\n    // Now, this should succeed.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFA_ADDRESS, &AddressIpv6, sizeof(AddressIpv6)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), IFA_LOCAL, &AddressIpv6, sizeof(AddressIpv6)));\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkRouteNewDelRoute(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the NETLINK_ROUTE protocol's\r\n    RTM_NEWROUTE and RTM_DELROUTE messages.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    struct in_addr DestinationIpv4;\r\n    struct in6_addr DestinationIpv6;\r\n    struct in_addr GatewayIpv4;\r\n    struct in6_addr GatewayIpv6;\r\n    int LoopbackIndex;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct rtmsg msg;\r\n        char attr[200];\r\n    } Request;\r\n    int Result;\r\n    int Socket;\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETROUTE request.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_UNSPEC;\r\n\r\n    //\r\n    // Get the interface index of the loopback adapter. All the tests\r\n    // below operate on the loopback adapter.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkGetLoopbackIndex(&LoopbackIndex));\r\n    LxtCheckTrue(LoopbackIndex > 0);\r\n\r\n    //\r\n    // Test flags. Passing in (invalid) flags for RTM_GET* results in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Test invalid flags with NLM_F_ACK.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Test flags. Passing 0 flags or invalid flags result in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.nlh.nlmsg_flags = 0x40;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Add a routing entry to lo with destination 1.1.1.1 and on-link gateway.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_LINK;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.1.1.1\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, -1);\r\n\r\n    //\r\n    // Test specifying an invalid address family.\r\n    //\r\n\r\n    Request.msg.rtm_family = AF_UNSPEC;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EOPNOTSUPP));\r\n\r\n    //\r\n    // Only send the request with the destination and no interface index.\r\n    // The request should fail with ENODEV, because no interface index was specified.\r\n    //\r\n\r\n    Request.msg.rtm_family = AF_INET;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENODEV));\r\n\r\n    //\r\n    // Send the request with only NLM_F_REQUEST. The request should fail with ENOENT,\r\n    // because destination 1.1.1.1 does not exist and NLM_F_CREATE was not specified.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENOENT));\r\n\r\n    //\r\n    // Now add the NLM_F_REPLACE flag, and the request should still fail.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENOENT));\r\n\r\n    //\r\n    // Now add the NLM_F_CREATE flag, and the request should succeed.\r\n    // Since the NLM_F_ACK flag was not specified, there should be no response,\r\n    // even though the operation succeeded.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Sending again gets an error message EEXIST.\r\n    //\r\n    // N.B. The sleep is to wait for the lxcore internal cache to be updated\r\n    //      with the latest routing info from NETIO.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;\r\n    LxtCheckResult(SocketNetlinkSendAndWaitForExpectedError(Socket, &Request, sizeof(Request), -EEXIST));\r\n\r\n    //\r\n    // Sending again with only NLM_F_REQUEST still gets an error message EEXIST.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));\r\n\r\n    //\r\n    // Sending again with NLM_F_REQUEST and NLM_F_EXCL still gets an error message EEXIST.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));\r\n\r\n    //\r\n    // Sending again after adding NLM_F_REPLACE still gets an error message EEXIST.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_REPLACE;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));\r\n\r\n    //\r\n    // Sending again with NLM_F_REPLACE is successful.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_APPEND | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Add a routing entry to lo with destination 1.1.1.2 and on-link gateway.\r\n    // Send the request with various flags. The request should succeed.\r\n    //\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_LINK;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.1.1.2\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Sending again gets an error message EEXIST.\r\n    //\r\n\r\n    usleep(1000 * 60);\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckEqual(ReceiveResult, Request.nlh.nlmsg_len + NLMSG_LENGTH(sizeof(int)), \"%d\");\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));\r\n\r\n    //\r\n    // Sending again with the following flags still gets an error message EEXIST.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_EXCL | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckEqual(ReceiveResult, Request.nlh.nlmsg_len + NLMSG_LENGTH(sizeof(int)), \"%d\");\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));\r\n\r\n    //\r\n    // Use NLM_F_REPLACE to change the gateway from on-link to 1.1.1.1.\r\n    // Note that including the NLM_F_EXCL flag fails the operation.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL;\r\n    inet_aton(\"1.1.1.1\", &GatewayIpv4);\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv4, sizeof(GatewayIpv4)));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckEqual(ReceiveResult, Request.nlh.nlmsg_len + NLMSG_LENGTH(sizeof(int)), \"%d\");\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -EEXIST));\r\n\r\n    //\r\n    // Now remove the NLM_F_EXCL flag, and the operation should succeed.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_ACK;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Add a routing entry to lo with destination 1.1.1.3 and gateway 1.1.1.1.\r\n    // Send the request with various flags. The request should succeed.\r\n    //\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.1.1.3\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.1\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Change the gateway from 1.1.1.1 to on-link.\r\n    // Note that invalid flags are silently dropped.\r\n    //\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | 0xF800;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_LINK;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.1.1.3\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Add two more routing entries: destination 1.1.2 and 1.3,\r\n    // with 1.1.1.1 as their gateway.\r\n    //\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_ACK;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.1.2\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.1\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.3\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.1\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Add three more routing entries: destination 1.1.1.0,\r\n    // with 1.1.1.1 as their gateway, and prefix lengths 30, 31 and 32.\r\n    //\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 30;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.1.1.0\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.1\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 31;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.1.1.0\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.1\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.1.1.0\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.1\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Test giving an invalid interface index, which should fail.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_aton(\"1.1.1.10\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.1\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, 90000);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ENETUNREACH));\r\n\r\n    //\r\n    // Add 2 Ipv6 entries.\r\n    //\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET6;\r\n    Request.msg.rtm_dst_len = 31;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_pton(AF_INET6, \"3ffa::\", &DestinationIpv6);\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    usleep(1000 * 60);\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_NEWROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET6;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_protocol = RTPROT_BOOT;\r\n    Request.msg.rtm_scope = RT_SCOPE_UNIVERSE;\r\n    Request.msg.rtm_type = RTN_UNICAST;\r\n    inet_pton(AF_INET6, \"3ffa::\", &DestinationIpv6);\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Now test RTM_DELROUTE!\r\n    // Now, we have the following routing entries for the loopback adapter:\r\n    // Destination    Gateway\r\n    // 1.1.1.0/30     1.1.1.1\r\n    // 1.1.1.0/31     1.1.1.1\r\n    // 1.1.1.0/32     1.1.1.1\r\n    // 1.1.1.1        on-link (zero)\r\n    // 1.1.1.2        1.1.1.1\r\n    // 1.1.1.3        on-link (zero)\r\n    // 1.1.2          1.1.1.1\r\n    // 1.3            1.1.1.1\r\n    // === IPv6 ===\r\n    // 3ffa::/31      on-link\r\n    // 3ffa::/32      on-link\r\n    //\r\n\r\n    //\r\n    // Try to delete the routing entry with destination 1.1.1.1 and\r\n    // gateway 1.2.3.4, which does not exist, returning ESRCH.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_aton(\"1.1.1.1\", &DestinationIpv4);\r\n    inet_aton(\"1.2.3.4\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));\r\n\r\n    //\r\n    // Passing 0 flags results in no response.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = 0;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Try to delete the routing entry with destination 1.1.1.1 and\r\n    // gateway 0.0.0.0, which should succeed.\r\n    // Note that as long as the NLM_F_REQUEST flag is present, it does\r\n    // not matter what the rest of the flags are.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_aton(\"1.1.1.1\", &DestinationIpv4);\r\n    inet_aton(\"0.0.0.0\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Try to delete the routing entry with destination 1.1.1.2 and\r\n    // gateway 1.2.3.4, which does not exist, returning ESRCH.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_aton(\"1.1.1.2\", &DestinationIpv4);\r\n    inet_aton(\"1.2.3.4\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));\r\n\r\n    //\r\n    // Try to delete the routing entry with destination 1.1.1.2 and\r\n    // gateway 0.0.0.0, which should succeed.\r\n    // Note that even though the gateway is 1.1.1.1, specifying\r\n    // 0.0.0.0 in the request specifies a wildcard gateway.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_aton(\"1.1.1.2\", &DestinationIpv4);\r\n    inet_aton(\"0.0.0.0\", &GatewayIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, &GatewayIpv4, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Test giving an invalid interface index, which should fail.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_aton(\"1.1.1.3\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, 90000);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));\r\n\r\n    //\r\n    // Try to delete the routing entry with destination 1.1.1.3 and\r\n    // no gateway specified, which should succeed.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_aton(\"1.1.1.3\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Try to delete the routing entry with destination 1.1.2 and\r\n    // no gateway specified, which should succeed.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_aton(\"1.1.2\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\n    //\r\n    // Try to delete the routing entry with destination 1.3 and\r\n    // gateway 1.1.1.1 specified, which should succeed.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_aton(\"1.3\", &DestinationIpv4);\r\n    inet_aton(\"1.1.1.1\", &GatewayIpv4);\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv4, sizeof(GatewayIpv4)));\r\n\r\n    //\r\n    // First try sending incomplete information (only sending the gateway\r\n    // and nothing else), which should fail.\r\n    //\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));\r\n\r\n    //\r\n    // Now try the actual delete.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv4, sizeof(DestinationIpv4)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Try to delete the routing entries with destination 1.1.1.0 and prefix\r\n    // lengths 29, 30, 31 and 32. Only 29 should fail and the rest should succeed.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET;\r\n    Request.msg.rtm_dst_len = 29;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_aton(\"1.1.1.0\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, LoopbackIndex);\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, -ESRCH));\r\n\r\n    Request.msg.rtm_dst_len = 30;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.msg.rtm_dst_len = 31;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    Request.msg.rtm_dst_len = 32;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Try to delete the routing entry with destination 3ffa:: and prefix length 31.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET6;\r\n    Request.msg.rtm_dst_len = 31;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_pton(AF_INET6, \"3ffa::\", &DestinationIpv6);\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrnoFailure(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), MSG_DONTWAIT), EAGAIN);\r\n\r\n    //\r\n    // Try to delete the routing entry with destination 3ffa:: and prefix length 32.\r\n    //\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_DELROUTE;\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | 0x4321;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n    Request.msg.rtm_family = AF_INET6;\r\n    Request.msg.rtm_dst_len = 32;\r\n    Request.msg.rtm_table = RT_TABLE_MAIN;\r\n    Request.msg.rtm_scope = RT_SCOPE_NOWHERE;\r\n    inet_pton(AF_INET6, \"3ffa::\", &DestinationIpv6);\r\n    inet_pton(AF_INET6, \"0::\", &GatewayIpv6);\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_DST, &DestinationIpv6, sizeof(DestinationIpv6)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_GATEWAY, &GatewayIpv6, sizeof(GatewayIpv6)));\r\n\r\n    LxtCheckErrnoZeroSuccess(SocketNetlinkRouteAddAttribute(&Request.nlh, sizeof(Request), RTA_OIF, &LoopbackIndex, sizeof(LoopbackIndex)));\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckResult(SocketNetlinkCheckResponseError(ReceiveBuffer, ReceiveResult, 0));\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkSendAndWaitForExpectedError(int Socket, void* Request, int RequestSize, int ExpectedError)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine repeatedly sends a request and receives the Netlink error\r\n    response until the Netlink error value matches the passed in expected value.\r\n\r\nArguments:\r\n\r\n    Socket - Supplies the socket to send and receive the response from.\r\n\r\n    Request -  Supplies the Netlink request to send.\r\n\r\n    RequestSize - Supplies the size of the Netlink request.\r\n\r\n    ExpectedError - Supplies the Netlink error to check for.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct nlmsgerr* Error;\r\n    char ReceiveBuffer[5000];\r\n    struct nlmsghdr* ReceiveHeader;\r\n    int ReceiveResult;\r\n    int Result;\r\n    int Retries;\r\n\r\n    Retries = 0;\r\n    while (Retries < 20)\r\n    {\r\n        Retries += 1;\r\n        LxtCheckErrno(sendto(Socket, Request, RequestSize, 0, NULL, 0));\r\n        LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n        ReceiveHeader = (struct nlmsghdr*)ReceiveBuffer;\r\n        LxtCheckTrue(NLMSG_OK(ReceiveHeader, ReceiveResult));\r\n        LxtCheckEqual(ReceiveHeader->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n        Error = (struct nlmsgerr*)NLMSG_DATA(ReceiveHeader);\r\n        if (Error->error == ExpectedError)\r\n        {\r\n            break;\r\n        }\r\n        else\r\n        {\r\n            LxtLogInfo(\"Error is: %d, waiting for it to become %d.\", Error->error, ExpectedError);\r\n        }\r\n\r\n        usleep(1000 * 1000);\r\n    }\r\n\r\n    LxtCheckEqual(Error->error, ExpectedError, \"%d\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkSetAndVerifySocketOptionTimeout(int Socket, int SocketOption, int Usec)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sets a timeout socket option and reads it back to verify.\r\n\r\nArguments:\r\n\r\n    Socket - Supplies the socket to set the socket option on.\r\n\r\n    SocketOption -  Supplies the timeout-related socket option to set.\r\n\r\n    Usec - Supplies the number of microseconds for the timeout.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t OptionLength;\r\n    int Result;\r\n    struct timeval Timeout;\r\n\r\n    OptionLength = sizeof(Timeout);\r\n    Timeout.tv_sec = 0;\r\n    Timeout.tv_usec = Usec;\r\n    LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SocketOption, &Timeout, OptionLength));\r\n\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(getsockopt(Socket, SOL_SOCKET, SocketOption, &Timeout, &OptionLength));\r\n\r\n    LxtCheckEqual(OptionLength, sizeof(Timeout), \"%d\");\r\n    LxtCheckEqual(Timeout.tv_sec, 0, \"%d\");\r\n    LxtCheckEqual(Timeout.tv_usec, Usec, \"%d\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SocketNetlinkSoPasscred(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests for the SO_PASSCRED socket option.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct sockaddr_nl BindAddress;\r\n    unsigned char ControlBuffer[CMSG_SPACE(sizeof(struct ucred))];\r\n    struct cmsghdr* ControlMessage;\r\n    struct in_addr DestinationIpv4;\r\n    struct iovec IoVector;\r\n    struct msghdr MessageHeader = {0};\r\n    int PassCredentials;\r\n    char ReceiveBuffer[5000];\r\n    int Result;\r\n    int Socket;\r\n\r\n    struct\r\n    {\r\n        struct nlmsghdr nlh;\r\n        struct rtmsg msg;\r\n        char attr[200];\r\n    } Request;\r\n\r\n    Socket = -1;\r\n\r\n    //\r\n    // Create and bind socket. Create a RTM_GETROUTE request.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    AddressLength = sizeof(BindAddress);\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, AddressLength));\r\n\r\n    //\r\n    // Enable SO_PASSCRED on the socket.\r\n    //\r\n\r\n    PassCredentials = 1;\r\n    LxtCheckErrno(setsockopt(Socket, SOL_SOCKET, SO_PASSCRED, &PassCredentials, sizeof(PassCredentials)));\r\n\r\n    memset(&Request, 0, sizeof(Request));\r\n    Request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\r\n    Request.nlh.nlmsg_type = RTM_GETROUTE;\r\n    Request.nlh.nlmsg_seq = 0x4567;\r\n\r\n    //\r\n    // NLM_F_REQUEST with no NLM_F_DUMP means \"get best route\" request.\r\n    //\r\n\r\n    Request.nlh.nlmsg_flags = NLM_F_REQUEST;\r\n\r\n    //\r\n    // Specify the destination (Ipv4).\r\n    //\r\n\r\n    inet_aton(\"1.1.1.1\", &DestinationIpv4);\r\n    SocketNetlinkRouteAddRouteAttributes(&Request.nlh, sizeof(Request), &DestinationIpv4, NULL, -1);\r\n\r\n    Request.msg.rtm_family = AF_INET;\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n\r\n    //\r\n    // Craft the message header to receive the data + ancillary data.\r\n    //\r\n\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    MessageHeader.msg_iov = &IoVector;\r\n    MessageHeader.msg_iovlen = 1;\r\n    MessageHeader.msg_control = ControlBuffer;\r\n    MessageHeader.msg_controllen = sizeof(ControlBuffer);\r\n    IoVector.iov_base = ReceiveBuffer;\r\n    IoVector.iov_len = sizeof(ReceiveBuffer);\r\n    memset(ControlBuffer, 0, sizeof(ControlBuffer));\r\n    LxtCheckErrno(Result = recvmsg(Socket, &MessageHeader, 0));\r\n    LxtCheckGreater(Result, 0, \"%d\");\r\n    LxtCheckEqual(MessageHeader.msg_controllen, sizeof(ControlBuffer), \"%d\");\r\n    LxtCheckEqual(MessageHeader.msg_flags, 0, \"%d\");\r\n\r\n    //\r\n    // Validate that SMC_CREDENTIALS control message is present and has valid\r\n    // values.\r\n    //\r\n\r\n    ControlMessage = SocketGetControlMessage(&MessageHeader, NULL, SOL_SOCKET, SCM_CREDENTIALS);\r\n\r\n    LxtCheckNotEqual(ControlMessage, NULL, \"%p\");\r\n    LxtCheckEqual(ControlMessage->cmsg_len, CMSG_LEN(sizeof(struct ucred)), \"%d\");\r\n\r\n    LxtCheckAncillaryCredentials(ControlMessage, 0, 0, 0);\r\n\r\n    //\r\n    // Pass NULL as the control buffer with invalid size.\r\n    //\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    MessageHeader.msg_iov = &IoVector;\r\n    MessageHeader.msg_iovlen = 1;\r\n    MessageHeader.msg_control = NULL;\r\n    MessageHeader.msg_controllen = sizeof(ControlBuffer);\r\n    IoVector.iov_base = ReceiveBuffer;\r\n    IoVector.iov_len = sizeof(ReceiveBuffer);\r\n    LxtCheckErrno(Result = recvmsg(Socket, &MessageHeader, 0));\r\n    LxtCheckGreater(Result, 0, \"%d\");\r\n    LxtCheckEqual(MessageHeader.msg_controllen, 0, \"%d\");\r\n\r\n    //\r\n    // Since the control buffer was not big enough (NULL) to hold the control\r\n    // message, proper (truncate) flags should be set.\r\n    //\r\n\r\n    LxtCheckEqual(MessageHeader.msg_flags, MSG_CTRUNC, \"%d\");\r\n\r\n    //\r\n    // Pass a control buffer smaller than the control message header.\r\n    //\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    memset(&MessageHeader, 0, sizeof(MessageHeader));\r\n    MessageHeader.msg_iov = &IoVector;\r\n    MessageHeader.msg_iovlen = 1;\r\n    MessageHeader.msg_control = NULL;\r\n    MessageHeader.msg_controllen = sizeof(struct cmsghdr) - 1;\r\n    IoVector.iov_base = ReceiveBuffer;\r\n    IoVector.iov_len = sizeof(ReceiveBuffer);\r\n    LxtCheckErrno(Result = recvmsg(Socket, &MessageHeader, 0));\r\n    LxtCheckGreater(Result, 0, \"%d\");\r\n    LxtCheckEqual(MessageHeader.msg_controllen, 0, \"%d\");\r\n\r\n    //\r\n    // Since the control buffer was not big enough (NULL) to hold the control\r\n    // message, proper (truncate) flags should be set.\r\n    //\r\n\r\n    LxtCheckEqual(MessageHeader.msg_flags, MSG_CTRUNC, \"%d\");\r\n\r\n    //\r\n    // Pass a NULL message header.\r\n    //\r\n\r\n    LxtCheckErrno(sendto(Socket, &Request, sizeof(Request), 0, NULL, 0));\r\n    memset(&ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    LxtCheckErrno(Result = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n    LxtCheckGreater(Result, 0, \"%d\");\r\n\r\n    //\r\n    // Check for general set/get of the SO_PASSCRED socket option.\r\n    //\r\n    // N.B After this routine, the state of the 'SO_PASSCRED' socket option in\r\n    //     the socket is not guaranteed.\r\n    //\r\n\r\n    LxtCheckErrno(SocketGetSetBooleanSocketOption(Socket, SOL_SOCKET, SO_PASSCRED, false));\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/overlayfs.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    OverlayFs.c\r\n\r\nAbstract:\r\n\r\n    This file is a overlayfs test.\r\n\r\n    N.B. This test depends on libmount, which is part of the libmount-dev\r\n         apt package.\r\n\r\n    N.B. In addition to this unit test, the official overlay test on github\r\n         should be run when changes are made.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/mount.h>\r\n#include <linux/capability.h>\r\n#include <libgen.h>\r\n#include <fcntl.h>\r\n#include <stdlib.h>\r\n#include <libmount/libmount.h>\r\n#include <sys/mman.h>\r\n#include <sys/time.h>\r\n#include <sys/prctl.h>\r\n#include <sys/xattr.h>\r\n#include \"lxtmount.h\"\r\n#include <unistd.h>\r\n#include <dirent.h>\r\n#include <fcntl.h>\r\n#include <string.h>\r\n#include <utime.h>\r\n#include <sys/cdefs.h>\r\n#include <linux/capability.h>\r\n\r\n#define LXT_NAME \"OverlayFs\"\r\n\r\n#define OVFS_TEST_PATH \"/data\"\r\n\r\n#define OVFS_TEST_MOUNT_PATH OVFS_TEST_PATH \"/\" OVFS_TEST_MERGED_DIR\r\n\r\n#define OVFS_TEST_MOUNT_NAME \"overlay\"\r\n\r\n#define OVFS_TEST_LOWER_DIR \"ovfs_test_lower\"\r\n#define OVFS_TEST_LOWER2_DIR \"ovfs_test_lower2\"\r\n#define OVFS_TEST_LOWER3_DIR \"ovfs_test_lower3\"\r\n#define OVFS_TEST_UPPER_DIR \"ovfs_test_upper\"\r\n#define OVFS_TEST_WORK_DIR \"ovfs_test_work\"\r\n#define OVFS_TEST_MERGED_DIR \"ovfs_test_merged\"\r\n\r\n#define OVFS_TEST_MOUNT_DEFAULT \"lowerdir=\" OVFS_TEST_LOWER_DIR \",upperdir=\" OVFS_TEST_UPPER_DIR \",workdir=\" OVFS_TEST_WORK_DIR\r\n\r\n#define OVFS_TEST_MOUNT_MULTI_LOWER \\\r\n    \"lowerdir=\" OVFS_TEST_LOWER_DIR \":\" OVFS_TEST_LOWER2_DIR \":\" OVFS_TEST_LOWER3_DIR \",upperdir=\" OVFS_TEST_UPPER_DIR \\\r\n    \",workdir=\" OVFS_TEST_WORK_DIR\r\n\r\n#define OVFS_TEST_MOUNT_FS_OPTS \"rw,\" OVFS_TEST_MOUNT_DEFAULT\r\n#define OVFS_TEST_MOUNT_COMBINED_OPTS \"rw,relatime,\" OVFS_TEST_MOUNT_DEFAULT\r\n\r\nconst char* g_OvFsTestDirs[] = {\r\n    OVFS_TEST_LOWER_DIR, OVFS_TEST_LOWER2_DIR, OVFS_TEST_LOWER3_DIR, OVFS_TEST_UPPER_DIR, OVFS_TEST_WORK_DIR, OVFS_TEST_MOUNT_PATH};\r\n\r\n//\r\n// N.B. This data must be kept in sync with OvFsTestDirsPopulate.\r\n//\r\n\r\nstruct\r\n{\r\n    char* Path;\r\n    char* Name;\r\n    mode_t Mode;\r\n    int Hydrates;\r\n} g_OvFsMergedContents[] = {\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir\", \"OnlyInLowerDir\", S_IFDIR | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLowerFile\", \"OnlyInLowerFile\", S_IFREG | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLowerSym\", \"OnlyInLowerSym\", S_IFLNK | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInUpperDir\", \"OnlyInUpperDir\", S_IFDIR | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInUpperFile\", \"OnlyInUpperFile\", S_IFREG | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInUpperSym\", \"OnlyInUpperSym\", S_IFLNK | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/InBothDir\", \"InBothDir\", S_IFDIR | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/InBothFile\", \"InBothFile\", S_IFREG | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/InBothSym\", \"InBothSym\", S_IFLNK | 0777, 0}};\r\n\r\nstruct\r\n{\r\n    char* Path;\r\n    char* Name;\r\n    mode_t Mode;\r\n    int Hydrates;\r\n} g_OvFsMergedMultiContents[] = {\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir\", \"OnlyInLowerDir\", S_IFDIR | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLowerFile\", \"OnlyInLowerFile\", S_IFREG | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLowerSym\", \"OnlyInLowerSym\", S_IFLNK | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLower2Dir\", \"OnlyInLower2Dir\", S_IFDIR | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLower2File\", \"OnlyInLower2File\", S_IFREG | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLower23File\", \"OnlyInLower23File\", S_IFREG | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLower3Dir\", \"OnlyInLower3Dir\", S_IFDIR | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInLower3File\", \"OnlyInLower3File\", S_IFREG | 0222, 1},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInUpperDir\", \"OnlyInUpperDir\", S_IFDIR | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInUpperFile\", \"OnlyInUpperFile\", S_IFREG | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/OnlyInUpperSym\", \"OnlyInUpperSym\", S_IFLNK | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/InBothDir\", \"InBothDir\", S_IFDIR | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/InBothFile\", \"InBothFile\", S_IFREG | 0777, 0},\r\n    {OVFS_TEST_MOUNT_PATH \"/InBothSym\", \"InBothSym\", S_IFLNK | 0777, 0}};\r\n\r\nLXT_VARIATION_HANDLER OvFsTestBasicMount;\r\nLXT_VARIATION_HANDLER OvFsTestFileObjectReadOps;\r\nLXT_VARIATION_HANDLER OvFsTestFileObjectWriteOpsUpper;\r\nLXT_VARIATION_HANDLER OvFsTestInodeOpaque;\r\nLXT_VARIATION_HANDLER OvFsTestInodeReadOps;\r\nLXT_VARIATION_HANDLER OvFsTestInodeRename;\r\nLXT_VARIATION_HANDLER OvFsTestInodeWhiteout;\r\nLXT_VARIATION_HANDLER OvFsTestInodeWriteOps;\r\nLXT_VARIATION_HANDLER OvFsTestInodeWriteOpsUpper;\r\nLXT_VARIATION_HANDLER OvFsTestInodeUnlink;\r\nLXT_VARIATION_HANDLER OvFsTestInodeXattr;\r\nLXT_VARIATION_HANDLER OvFsTestLowerWhiteout;\r\nLXT_VARIATION_HANDLER OvFsTestMultipleLower;\r\n\r\nint OvFsTestDirsPopulate(void);\r\n\r\nint OvFsTestDirsSetup(void);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"OverlayFs - basic mount\", OvFsTestBasicMount},\r\n    {\"OverlayFs - inode read ops\", OvFsTestInodeReadOps},\r\n    {\"OverlayFs - file object read ops\", OvFsTestFileObjectReadOps},\r\n    {\"OverlayFs - inode write ops upper\", OvFsTestInodeWriteOpsUpper},\r\n    {\"OverlayFs - file object write ops upper\", OvFsTestFileObjectWriteOpsUpper},\r\n    {\"OverlayFs - inode write ops\", OvFsTestInodeWriteOps},\r\n    {\"OverlayFs - inode unlink\", OvFsTestInodeUnlink},\r\n    {\"OverlayFs - whiteout\", OvFsTestInodeWhiteout},\r\n    {\"OverlayFs - opaque\", OvFsTestInodeOpaque},\r\n    {\"OverlayFs - rename\", OvFsTestInodeRename},\r\n    {\"OverlayFs - xattr\", OvFsTestInodeXattr},\r\n    {\"OverlayFs - multiple lower layers\", OvFsTestMultipleLower},\r\n    {\"OverlayFs - lower layer whiteouts\", OvFsTestLowerWhiteout}};\r\n\r\nstatic int g_TestPathMountId;\r\n\r\nconst int g_LxtUnstableInodes = 0;\r\n\r\nint OverlayFsTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    //\r\n    //  TODO_LX: Support other filesystems than volfs.\r\n    //\r\n\r\n    LxtCheckResult(g_TestPathMountId = MountGetMountId(OVFS_TEST_PATH));\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckErrno(chdir(OVFS_TEST_PATH));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n\r\n    char DeleteCmd[128];\r\n\r\n    for (int Index = 0; Index < LXT_COUNT_OF(g_OvFsTestDirs); ++Index)\r\n    {\r\n        sprintf(DeleteCmd, \"rm -rf %s\", g_OvFsTestDirs[Index]);\r\n        if (system(DeleteCmd) < 0)\r\n        {\r\n            LxtLogError(\"Failed to delete %s\", g_OvFsTestDirs[Index]);\r\n        }\r\n    }\r\n\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint OvFsTestBasicMount(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the mount and umount system calls for overlayfs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n    struct\r\n    {\r\n        char* Options;\r\n        int Errno;\r\n    } InvalidOpts[] = {\r\n        {NULL, EINVAL},\r\n        {\"\", EINVAL},\r\n        {\"lowerdir=doesNotExist\"\r\n         \",upperdir=\" OVFS_TEST_UPPER_DIR \",workdir=\" OVFS_TEST_WORK_DIR,\r\n         ENOENT},\r\n        {\"lowerdir=\" OVFS_TEST_LOWER_DIR \",lowerdir=\" OVFS_TEST_UPPER_DIR \",workdir=\" OVFS_TEST_WORK_DIR, EINVAL},\r\n        {\"lowerdir=\" OVFS_TEST_LOWER_DIR \",workdir=\" OVFS_TEST_WORK_DIR, EINVAL},\r\n        {\"lowerdir=\" OVFS_TEST_LOWER_DIR \",upperdir=\" OVFS_TEST_UPPER_DIR \",workdir=\" OVFS_TEST_UPPER_DIR, EINVAL},\r\n        {\"lowerdir=\" OVFS_TEST_LOWER_DIR \",upperdir=\" OVFS_TEST_UPPER_DIR \",workdir=\" OVFS_TEST_UPPER_DIR \"/\" OVFS_TEST_WORK_DIR, EINVAL},\r\n        {\"lowerdir=:\"\r\n         \",upperdir=\" OVFS_TEST_UPPER_DIR \",workdir=\" OVFS_TEST_WORK_DIR,\r\n         EINVAL},\r\n        {\"lowerdir=\" OVFS_TEST_LOWER_DIR \":\"\r\n         \",upperdir=\" OVFS_TEST_UPPER_DIR \",workdir=\" OVFS_TEST_WORK_DIR,\r\n         EINVAL},\r\n        {\"lowerdir=\" OVFS_TEST_LOWER_DIR \":\" OVFS_TEST_LOWER_DIR \":\" OVFS_TEST_LOWER_DIR \":\"\r\n         \",upperdir=\" OVFS_TEST_UPPER_DIR \",workdir=\" OVFS_TEST_WORK_DIR,\r\n         EINVAL},\r\n        {\"lowerdir=\" OVFS_TEST_LOWER_DIR \":\" OVFS_TEST_LOWER_DIR \":\"\r\n         \"doesNotExist\"\r\n         \",upperdir=\" OVFS_TEST_UPPER_DIR \",workdir=\" OVFS_TEST_WORK_DIR,\r\n         ENOENT}};\r\n    int MountId;\r\n    int Result;\r\n\r\n    //\r\n    // Set up the directories and ensure it's not a mount point yet.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    //\r\n    // Mount an overlayfs instance and check it was mounted.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckResult(\r\n        MountId = MountCheckIsMount(\r\n            OVFS_TEST_MOUNT_PATH, g_TestPathMountId, \"myovfsnew\", OVFS_TEST_MOUNT_NAME, \"/\", \"rw,relatime\", OVFS_TEST_MOUNT_FS_OPTS, OVFS_TEST_MOUNT_COMBINED_OPTS, 0));\r\n\r\n    //\r\n    // Mounting again should succeed.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckResult(\r\n        MountId = MountCheckIsMount(\r\n            OVFS_TEST_MOUNT_PATH, MountId, \"myovfsnew\", OVFS_TEST_MOUNT_NAME, \"/\", \"rw,relatime\", OVFS_TEST_MOUNT_FS_OPTS, OVFS_TEST_MOUNT_COMBINED_OPTS, 0));\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(\r\n        MountId = MountCheckIsMount(\r\n            OVFS_TEST_MOUNT_PATH, g_TestPathMountId, \"myovfsnew\", OVFS_TEST_MOUNT_NAME, \"/\", \"rw,relatime\", OVFS_TEST_MOUNT_FS_OPTS, OVFS_TEST_MOUNT_COMBINED_OPTS, 0));\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    //\r\n    // Check invalid mount parameters.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking invalid options...\");\r\n    mkdir(OVFS_TEST_UPPER_DIR \"/\" OVFS_TEST_WORK_DIR, 0777);\r\n    for (Index = 0; Index < LXT_COUNT_OF(InvalidOpts); ++Index)\r\n    {\r\n        LxtCheckErrnoFailure(\r\n            mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, InvalidOpts[Index].Options), InvalidOpts[Index].Errno);\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestDirsPopulate(void)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine populates the mount directories.\r\n\r\n    N.B. This data must be kept in sync with g_OvFsMergedContents.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char XattrName[64];\r\n    int Fd;\r\n    char* FileName;\r\n    int Index;\r\n    struct\r\n    {\r\n        char* Name;\r\n        int Mode;\r\n    } Paths[] = {\r\n        {OVFS_TEST_LOWER_DIR \"/OnlyInLowerDir\", S_IFDIR | 0666},\r\n        {OVFS_TEST_LOWER_DIR \"/InBothDir\", S_IFDIR | 0666},\r\n        {OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", S_IFREG | 0666},\r\n        {OVFS_TEST_LOWER_DIR \"/InBothFile\", S_IFREG | 0666},\r\n\r\n        {OVFS_TEST_LOWER2_DIR \"/OnlyInLower2Dir\", S_IFDIR | 0444},\r\n        {OVFS_TEST_LOWER2_DIR \"/InBothDir\", S_IFDIR | 0444},\r\n        {OVFS_TEST_LOWER2_DIR \"/OnlyInLower2File\", S_IFREG | 0444},\r\n        {OVFS_TEST_LOWER2_DIR \"/OnlyInLower23File\", S_IFREG | 0444},\r\n        {OVFS_TEST_LOWER2_DIR \"/InBothFile\", S_IFREG | 0444},\r\n\r\n        {OVFS_TEST_LOWER3_DIR \"/OnlyInLower3Dir\", S_IFDIR | 0111},\r\n        {OVFS_TEST_LOWER3_DIR \"/InBothDir\", S_IFDIR | 0111},\r\n        {OVFS_TEST_LOWER3_DIR \"/OnlyInLower3File\", S_IFREG | 0111},\r\n        {OVFS_TEST_LOWER3_DIR \"/OnlyInLower23File\", S_IFREG | 0111},\r\n        {OVFS_TEST_LOWER3_DIR \"/InBothFile\", S_IFREG | 0111},\r\n\r\n        {OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", S_IFDIR | 0777},\r\n        {OVFS_TEST_UPPER_DIR \"/InBothDir\", S_IFDIR | 0777},\r\n        {OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", S_IFREG | 0777},\r\n        {OVFS_TEST_UPPER_DIR \"/InBothFile\", S_IFREG | 0777}};\r\n    int Result;\r\n\r\n    Fd = -1;\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(Paths); ++Index)\r\n    {\r\n        FileName = strrchr(Paths[Index].Name, '/') + 1;\r\n        if (S_ISREG(Paths[Index].Mode))\r\n        {\r\n            LxtCheckErrno(Fd = creat(Paths[Index].Name, Paths[Index].Mode));\r\n            LxtCheckErrno(write(Fd, FileName, strlen(FileName) + 1));\r\n            LxtClose(Fd);\r\n            Fd = -1;\r\n        }\r\n        else\r\n        {\r\n            LxtCheckErrno(mkdir(Paths[Index].Name, Paths[Index].Mode));\r\n        }\r\n\r\n        LxtCheckErrno(lsetxattr(Paths[Index].Name, \"trusted.overlaytest\", FileName, strlen(FileName) + 1, XATTR_CREATE));\r\n\r\n        sprintf(XattrName, \"trusted.%s\", FileName);\r\n        LxtCheckErrno(lsetxattr(Paths[Index].Name, XattrName, FileName, strlen(FileName) + 1, XATTR_CREATE));\r\n    }\r\n\r\n    //\r\n    // N.B. xattrs cannot be set on symbolic links on all filesystems.\r\n    //\r\n\r\n    LxtCheckErrno(symlink(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", OVFS_TEST_LOWER_DIR \"/OnlyInLowerSym\"));\r\n\r\n    LxtCheckErrno(symlink(OVFS_TEST_LOWER_DIR \"/InBothFile\", OVFS_TEST_LOWER_DIR \"/InBothSym\"));\r\n\r\n    LxtCheckErrno(symlink(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", OVFS_TEST_UPPER_DIR \"/OnlyInUpperSym\"));\r\n\r\n    LxtCheckErrno(symlink(OVFS_TEST_UPPER_DIR \"/InBothFile\", OVFS_TEST_UPPER_DIR \"/InBothSym\"));\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint OvFsTestDirsSetup(void)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine prepares the mount directories.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char DeleteCmd[128];\r\n    int Index;\r\n    int Result;\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    sprintf(DeleteCmd, \"rm -rf %s\", OVFS_TEST_MOUNT_PATH);\r\n    system(DeleteCmd);\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MOUNT_PATH, 0777));\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsTestDirs); ++Index)\r\n    {\r\n        sprintf(DeleteCmd, \"rm -rf %s\", g_OvFsTestDirs[Index]);\r\n        system(DeleteCmd);\r\n        LxtCheckErrno(mkdir(g_OvFsTestDirs[Index], 0777));\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint OvFsTestFileObjectReadOps(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests file object operations that do not modify state.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    int BytesRead;\r\n    int Count;\r\n    struct dirent* Entry;\r\n    int EntryPosition;\r\n    int Fd;\r\n    int Found[LXT_COUNT_OF(g_OvFsMergedContents) + 2];\r\n    int FoundIndex;\r\n    int Index;\r\n    void* Mapping;\r\n    void* MapResult;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    struct stat StatMergedBuffer;\r\n\r\n    Fd = -1;\r\n    Mapping = NULL;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance and check inode operations that do not\r\n    // hydrate files.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    //\r\n    // Check the behavior for VFS file object operations that support file\r\n    // descriptors opened for read only.\r\n    //\r\n    // N.B. All other file operations will fail the request and are verified\r\n    //      in the VFS access test.\r\n    //\r\n\r\n    //\r\n    // Check the behavior for read directory on the root.\r\n    //\r\n\r\n    memset(Found, 0, sizeof(Found));\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH, O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n\r\n    while (BytesRead > 0)\r\n    {\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            if (strcmp(Entry->d_name, \".\") == 0)\r\n            {\r\n                FoundIndex = 0;\r\n            }\r\n            else if (strcmp(Entry->d_name, \"..\") == 0)\r\n            {\r\n                FoundIndex = 1;\r\n            }\r\n            else\r\n            {\r\n                for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n                {\r\n                    if (strcmp(g_OvFsMergedContents[Index].Name, Entry->d_name) == 0)\r\n                    {\r\n                        FoundIndex = Index + 2;\r\n                        break;\r\n                    }\r\n                }\r\n\r\n                if (Index == LXT_COUNT_OF(g_OvFsMergedContents))\r\n                {\r\n                    LxtLogError(\"Unexpected entry %s\", Entry->d_name);\r\n                    LxtCheckNotEqual(Index, LXT_COUNT_OF(g_OvFsMergedContents), \"%d\");\r\n                }\r\n            }\r\n\r\n            LxtCheckEqual(Found[FoundIndex], 0, \"%d\");\r\n            Found[FoundIndex] = 1;\r\n        }\r\n\r\n        Entry = (struct dirent*)Buffer;\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(Found); ++Index)\r\n    {\r\n        LxtCheckEqual(Found[FoundIndex], 1, \"%d\");\r\n    }\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check the behavior for read directory on sub directories.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if (S_ISDIR(g_OvFsMergedContents[Index].Mode) == FALSE)\r\n        {\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDONLY));\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n            Count = 0;\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            Count += 1;\r\n        }\r\n\r\n        LxtCheckEqual(Count, 2, \"%d\");\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for map.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if (S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE)\r\n        {\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDONLY));\r\n        LxtCheckMapErrno(Mapping = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, Fd, 0));\r\n        LxtCheckStringEqual(Mapping, g_OvFsMergedContents[Index].Name);\r\n        munmap(Mapping, PAGE_SIZE);\r\n        Mapping = MAP_FAILED;\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for ioctl.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if (S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE)\r\n        {\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDONLY));\r\n        LxtCheckErrno(ioctl(Fd, FIONREAD, &BytesRead));\r\n        LxtCheckEqual(BytesRead, strlen(g_OvFsMergedContents[Index].Name) + 1, \"%d\");\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for sync.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if ((S_ISDIR(g_OvFsMergedContents[Index].Mode) == FALSE) && (S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDONLY));\r\n        LxtCheckErrno(fsync(Fd));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for read file.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if (S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE)\r\n        {\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDONLY));\r\n        LxtCheckErrno(BytesRead = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n        Buffer[BytesRead] = 0;\r\n        LxtCheckStringEqual(Buffer, g_OvFsMergedContents[Index].Name);\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for seek.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if ((S_ISDIR(g_OvFsMergedContents[Index].Mode) == FALSE) && (S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtLogInfo(\"%s\", g_OvFsMergedContents[Index].Path);\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDONLY));\r\n        LxtCheckErrno(lseek(Fd, SEEK_SET, 1));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check that none of the operations hydrated files from the lower\r\n    // directory.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Mapping != MAP_FAILED)\r\n    {\r\n        munmap(Mapping, PAGE_SIZE);\r\n    }\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestFileObjectWriteOpsUpper(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests file object write operations that do not modify the\r\n    lower.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    char BufferExpected[100];\r\n    int BytesRead;\r\n    int BytesWritten;\r\n    int Count;\r\n    struct dirent* Entry;\r\n    int EntryPosition;\r\n    int Fd;\r\n    int Found[LXT_COUNT_OF(g_OvFsMergedContents) + 2];\r\n    int FoundIndex;\r\n    int Index;\r\n    void* Mapping;\r\n    void* MapResult;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    struct stat StatMergedBuffer;\r\n\r\n    Fd = -1;\r\n    Mapping = NULL;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance and check inode operations that modify the\r\n    // upper but do not hydrate files.\r\n    //\r\n    // N.B. The overlay fs mount does not need to be recreated after each\r\n    //      variation since only the upper is modified.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    //\r\n    // Check the behavior for map.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if ((S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE) || (g_OvFsMergedContents[Index].Hydrates != 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDWR));\r\n        LxtCheckMapErrno(Mapping = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, Fd, 0));\r\n        LxtCheckStringEqual(Mapping, g_OvFsMergedContents[Index].Name);\r\n        munmap(Mapping, PAGE_SIZE);\r\n        Mapping = MAP_FAILED;\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for truncate.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if ((S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE) || (g_OvFsMergedContents[Index].Hydrates != 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDWR));\r\n        LxtCheckErrno(ftruncate(Fd, 0));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for fallocate.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if ((S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE) || (g_OvFsMergedContents[Index].Hydrates != 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDWR));\r\n        LxtCheckErrno(fallocate(Fd, 0, 0, strlen(g_OvFsMergedContents[Index].Name) + 1));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for write file.\r\n    //\r\n\r\n    memset(BufferExpected, 0, sizeof(BufferExpected));\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if ((S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE) || (g_OvFsMergedContents[Index].Hydrates != 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDWR));\r\n        LxtCheckErrno(BytesRead = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n        Buffer[BytesRead] = 0;\r\n        LxtCheckEqual(BytesRead, strlen(g_OvFsMergedContents[Index].Name) + 1, \"%d\");\r\n        LxtCheckMemoryEqual(Buffer, BufferExpected, BytesRead);\r\n        LxtCheckErrno(lseek(Fd, SEEK_SET, 0));\r\n        LxtCheckErrno(BytesWritten = write(Fd, g_OvFsMergedContents[Index].Name, strlen(g_OvFsMergedContents[Index].Name) + 1));\r\n        LxtCheckEqual(BytesWritten, strlen(g_OvFsMergedContents[Index].Name) + 1, \"%d\");\r\n        LxtCheckErrno(lseek(Fd, SEEK_SET, 0));\r\n        LxtCheckErrno(BytesRead = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n        Buffer[BytesRead] = 0;\r\n        LxtCheckStringEqual(Buffer, g_OvFsMergedContents[Index].Name);\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check that none of the operations hydrated files from the lower\r\n    // directory.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Mapping != MAP_FAILED)\r\n    {\r\n        munmap(Mapping, PAGE_SIZE);\r\n    }\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestInodeOpaque(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests inode opaque operations.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    int BytesRead;\r\n    int Count;\r\n    struct dirent* Entry;\r\n    int EntryPosition;\r\n    int Fd;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Create a file in each directory.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerDir/OnlyInLowerDirFile\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_LOWER_DIR \"/InBothDir/InBothDirLowerFile\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir/OnlyInUpperDirFile\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_UPPER_DIR \"/InBothDir/InBothDirUpperFile\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Mount an overlayfs instance and check for the expected file state.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/InBothDir/InBothDirLowerFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/InBothDir/InBothDirUpperFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISLNK(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(lstat(OVFS_TEST_UPPER_DIR \"/InBothSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISLNK(StatBuffer.st_mode));\r\n\r\n    //\r\n    // Remove each directory and check that it is removed, first checking for\r\n    // the expected failure code.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rmdir(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\"), ENOTEMPTY);\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir/OnlyInLowerDirFile\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir/OnlyInLowerDirFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrnoFailure(rmdir(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir\"), ENOTEMPTY);\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir/OnlyInUpperDirFile\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir/OnlyInUpperDirFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrnoFailure(rmdir(OVFS_TEST_MERGED_DIR \"/InBothDir\"), ENOTEMPTY);\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/InBothDir/InBothDirUpperFile\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir/OnlyInUpperDirFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(rmdir(OVFS_TEST_MERGED_DIR \"/InBothDir\"), ENOTEMPTY);\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/InBothDir/InBothDirLowerFile\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir/OnlyInLowerDirFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/InBothDir\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    //\r\n    // Enumerate the top level and check that the three directories have been\r\n    // removed.\r\n    //\r\n\r\n    Count = 0;\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH, O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n\r\n    while (BytesRead > 0)\r\n    {\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            LxtLogInfo(\"%s\", Entry->d_name);\r\n            Count += 1;\r\n        }\r\n\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    LxtCheckEqual(Count - 2, LXT_COUNT_OF(g_OvFsMergedContents) - 3, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Create entries over the whiteouts and check for the expected state.\r\n    //\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n        Count = 0;\r\n    for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n    {\r\n\r\n        Entry = (struct dirent*)&Buffer[EntryPosition];\r\n        LxtLogInfo(\"%s\", Entry->d_name);\r\n        Count += 1;\r\n    }\r\n\r\n    LxtCheckEqual(Count, 2, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir/OnlyInLowerDirFile\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n        Count = 0;\r\n    for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n    {\r\n\r\n        Entry = (struct dirent*)&Buffer[EntryPosition];\r\n        Count += 1;\r\n    }\r\n\r\n    LxtCheckEqual(Count, 2, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir/OnlyInUpperDirFile\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(Fd = mkdir(OVFS_TEST_MERGED_DIR \"/InBothDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MERGED_DIR \"/InBothDir\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n        Count = 0;\r\n    for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n    {\r\n\r\n        Entry = (struct dirent*)&Buffer[EntryPosition];\r\n        Count += 1;\r\n    }\r\n\r\n    LxtCheckEqual(Count, 2, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MERGED_DIR \"/InBothDir/InBothDirLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MERGED_DIR \"/InBothDir/InBothDirUpperFile\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Enumerate the top level and check that the three directories have been\r\n    // replaced.\r\n    //\r\n\r\n    Count = 0;\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH, O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n\r\n    while (BytesRead > 0)\r\n    {\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            Count += 1;\r\n        }\r\n\r\n        Entry = (struct dirent*)Buffer;\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    LxtCheckEqual(Count - 2, LXT_COUNT_OF(g_OvFsMergedContents), \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Replace a directory with a file and back again to a directory.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/InBothDir\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MERGED_DIR \"/InBothDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_MERGED_DIR \"/InBothDir\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/InBothDir\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MERGED_DIR \"/InBothDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MERGED_DIR \"/InBothDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestInodeReadOps(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests inode operations that do not modify state.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    char Path[128];\r\n    ssize_t PathSize;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    struct stat StatMergedBuffer;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance and check inode operations that do not\r\n    // hydrate files.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    //\r\n    // Check the behavior for open, lookup, and fstat. The inode numbers in the\r\n    // merged folder should be unique for directories but match the files.\r\n    //\r\n\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir\", O_RDONLY, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/InBothDir\", O_RDONLY, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(stat(OVFS_TEST_LOWER_DIR \"/InBothDir\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerFile\", O_RDONLY, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/InBothFile\", O_RDONLY, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(stat(OVFS_TEST_LOWER_DIR \"/InBothFile\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInUpperDir\", O_RDONLY, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInUpperFile\", O_RDONLY, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Check the behavior for readlink.\r\n    //\r\n\r\n    LxtCheckErrno(PathSize = readlink(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerSym\", Path, sizeof(Path) - 1));\r\n\r\n    Path[PathSize] = 0;\r\n    LxtCheckStringEqual(Path, OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\");\r\n    LxtCheckErrno(PathSize = readlink(OVFS_TEST_MOUNT_PATH \"/InBothSym\", Path, sizeof(Path) - 1));\r\n\r\n    Path[PathSize] = 0;\r\n    LxtCheckStringEqual(Path, OVFS_TEST_UPPER_DIR \"/InBothFile\");\r\n    LxtCheckErrno(PathSize = readlink(OVFS_TEST_MOUNT_PATH \"/OnlyInUpperSym\", Path, sizeof(Path) - 1));\r\n\r\n    Path[PathSize] = 0;\r\n    LxtCheckStringEqual(Path, OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\");\r\n\r\n    //\r\n    //\r\n    // Check the behavior for stat. The inode numbers in the merged folder\r\n    // should be unique for directories but match the files.\r\n    //\r\n\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckResult(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir\", &StatMergedBuffer));\r\n        LxtCheckResult(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(stat(OVFS_TEST_MOUNT_PATH \"/InBothDir\", &StatMergedBuffer));\r\n        LxtCheckResult(stat(OVFS_TEST_LOWER_DIR \"/InBothDir\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerFile\", &StatMergedBuffer));\r\n        LxtCheckResult(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(stat(OVFS_TEST_MOUNT_PATH \"/InBothFile\", &StatMergedBuffer));\r\n        LxtCheckResult(stat(OVFS_TEST_LOWER_DIR \"/InBothFile\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInUpperDir\", &StatMergedBuffer));\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInUpperFile\", &StatMergedBuffer));\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Check that none of the operations hydrated files from the lower\r\n    // directory.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestInodeRename(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests inode operations that may modify state.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BufferSize;\r\n    char Buffer;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    struct stat StatMergedBuffer;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    //\r\n    // Rename each file and check for the expected state.\r\n    //\r\n\r\n    //\r\n    // When renaming a file from the lower, a whiteout and the renamed file are\r\n    // set in the upper.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(rename(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFile\", OVFS_TEST_MERGED_DIR \"/OnlyInLowerFileRenamed\"));\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFileRenamed\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFileRenamed\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISREG(StatMergedBuffer.st_mode));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    //\r\n    // When renaming a file from the upper, the file is simply renamed.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n    LxtCheckErrno(rename(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\", OVFS_TEST_MERGED_DIR \"/OnlyInUpperFileRenamed\"));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFileRenamed\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFileRenamed\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISREG(StatMergedBuffer.st_mode));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    //\r\n    // When renaming a file from both, a whiteout and the renamed file are\r\n    // set in the upper.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(rename(OVFS_TEST_MERGED_DIR \"/InBothFile\", OVFS_TEST_MERGED_DIR \"/InBothFileRenamed\"));\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFileRenamed\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/InBothFileRenamed\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISREG(StatMergedBuffer.st_mode));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Check the behavior for renaming a directory.\r\n    //\r\n\r\n    //\r\n    // When renaming a directory from the lower, the rename call should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(rename(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\", OVFS_TEST_MERGED_DIR \"/OnlyInLowerDirRenamed\"), EXDEV);\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // When renaming a directory from the upper, directory is simply renamed.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer));\r\n    LxtCheckErrno(rename(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir\", OVFS_TEST_MERGED_DIR \"/OnlyInUpperDirRenamed\"));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDirRenamed\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDirRenamed\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatMergedBuffer.st_mode));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    //\r\n    // When renaming a directory from both, the rename call should fail.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckErrnoFailure(rename(OVFS_TEST_MERGED_DIR \"/InBothDir\", OVFS_TEST_MERGED_DIR \"/InBothDirRenamed\"), EXDEV);\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/InBothDirRenamed\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // When renaming an opaque directory, the rename call should succeed.\r\n    //\r\n\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/InBothDir\"));\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MERGED_DIR \"/InBothDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(BufferSize = getxattr(OVFS_TEST_UPPER_DIR \"/InBothDir\", \"trusted.overlay.opaque\", &Buffer, sizeof(Buffer)));\r\n\r\n    LxtCheckEqual(Buffer, 'y', \"%c\");\r\n    LxtCheckEqual(BufferSize, 1, \"%d\");\r\n    LxtCheckErrno(rename(OVFS_TEST_MERGED_DIR \"/InBothDir\", OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\"));\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(BufferSize = getxattr(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", \"trusted.overlay.opaque\", &Buffer, sizeof(Buffer)));\r\n\r\n    LxtCheckEqual(Buffer, 'y', \"%c\");\r\n    LxtCheckEqual(BufferSize, 1, \"%d\");\r\n\r\n    //\r\n    // Unmount.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestInodeWhiteout(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests inode whiteout operations.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance and check for the expected file state.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISLNK(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(lstat(OVFS_TEST_UPPER_DIR \"/InBothSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISLNK(StatBuffer.st_mode));\r\n\r\n    //\r\n    // Unlink each entry and check for the expected behavior.\r\n    //\r\n\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFile\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInLowerSym\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInUpperSym\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperSym\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/InBothDir\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/InBothFile\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/InBothSym\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    //\r\n    // Create entries over the whiteouts and check for the expected state.\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFile\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_MERGED_DIR \"/OnlyInLowerSym\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_MERGED_DIR \"/OnlyInUpperSym\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MERGED_DIR \"/InBothDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MERGED_DIR \"/InBothFile\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_MERGED_DIR \"/InBothSym\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestInodeWriteOpsUpper(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests inode operations that do not modify state.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    int BytesRead;\r\n    int Count;\r\n    struct dirent* Entry;\r\n    int EntryPosition;\r\n    int Fd;\r\n    char* writeUpperCreate[] = {\"writeUpperDir\", \"writeUpperFile\", \"writeUpperSymlink\", \"writeUpperLink\"};\r\n    int Found[LXT_COUNT_OF(g_OvFsMergedContents) + 2 + LXT_COUNT_OF(writeUpperCreate)];\r\n    int FoundIndex;\r\n    int Index;\r\n    char Path[128];\r\n    ssize_t PathSize;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    struct stat StatMergedBuffer;\r\n    struct utimbuf Times;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance and check inode operations that do not\r\n    // hydrate files.\r\n    //\r\n    // N.B. The overlay fs mount does not need to be recreated after each\r\n    //      variation since only the upper is modified.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    //\r\n    // Check the behavior for open, chown, chmod, create cases, set times. The\r\n    // inode numbers in the merged folder should be unique for directories but\r\n    // match the files.\r\n    //\r\n\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/InBothFile\", O_RDWR, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(stat(OVFS_TEST_LOWER_DIR \"/InBothFile\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInUpperFile\", O_RDWR, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/InBothSym\", O_RDWR | O_PATH | O_NOFOLLOW, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(lstat(OVFS_TEST_LOWER_DIR \"/InBothSym\", &StatBuffer));\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n        LxtCheckResult(lstat(OVFS_TEST_UPPER_DIR \"/InBothSym\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n\r\n        LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInUpperSym\", O_RDWR | O_PATH | O_NOFOLLOW, 0));\r\n        LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckResult(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperSym\", &StatBuffer));\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Check the behavior for chown.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if (((S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE) && (S_ISDIR(g_OvFsMergedContents[Index].Mode) == FALSE)) ||\r\n            (g_OvFsMergedContents[Index].Hydrates != 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(chown(g_OvFsMergedContents[Index].Path, 111, 111));\r\n    }\r\n\r\n    //\r\n    // Check the behavior for chmod.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if (((S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE) && (S_ISDIR(g_OvFsMergedContents[Index].Mode) == FALSE)) ||\r\n            (g_OvFsMergedContents[Index].Hydrates != 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(chmod(g_OvFsMergedContents[Index].Path, 0777));\r\n    }\r\n\r\n    //\r\n    // Check the behavior for the 4 create cases.\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MOUNT_PATH \"/writeUpperDir\", 0777));\r\n    LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/writeUpperDir\", O_RDONLY, 0));\r\n    LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/writeUpperDir\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_LOWER_DIR \"/writeUpperDir\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_MOUNT_PATH \"/writeUpperFile\", 0777));\r\n    LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/writeUpperFile\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_LOWER_DIR \"/writeUpperFile\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(symlink(\"writeUpperSymlink\", OVFS_TEST_MOUNT_PATH \"/writeUpperSymlink\"));\r\n    LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/writeUpperSymlink\", O_RDONLY | O_PATH | O_NOFOLLOW, 0));\r\n    LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckResult(lstat(OVFS_TEST_UPPER_DIR \"/writeUpperSymlink\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrnoFailure(lstat(OVFS_TEST_LOWER_DIR \"/writeUpperSymlink\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(link(OVFS_TEST_MOUNT_PATH \"/writeUpperFile\", OVFS_TEST_MOUNT_PATH \"/writeUpperLink\"));\r\n    LxtCheckResult(Fd = open(OVFS_TEST_MOUNT_PATH \"/writeUpperLink\", O_RDONLY, 0));\r\n    LxtCheckResult(fstat(Fd, &StatMergedBuffer));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckResult(stat(OVFS_TEST_UPPER_DIR \"/writeUpperLink\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatBuffer.st_ino, StatMergedBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_LOWER_DIR \"/writeUpperLink\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Check the behavior for set times.\r\n    //\r\n\r\n    Times.actime = time(NULL);\r\n    Times.modtime = time(NULL);\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if (((S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE) && (S_ISDIR(g_OvFsMergedContents[Index].Mode) == FALSE)) ||\r\n            (g_OvFsMergedContents[Index].Hydrates != 0))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(utime(g_OvFsMergedContents[Index].Path, &Times));\r\n    }\r\n\r\n    //\r\n    // Check the behavior for read directory on the root after new files were\r\n    // added.\r\n    //\r\n\r\n    memset(Found, 0, sizeof(Found));\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH, O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n\r\n    while (BytesRead > 0)\r\n    {\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            if (strcmp(Entry->d_name, \".\") == 0)\r\n            {\r\n                FoundIndex = 0;\r\n            }\r\n            else if (strcmp(Entry->d_name, \"..\") == 0)\r\n            {\r\n                FoundIndex = 1;\r\n            }\r\n            else\r\n            {\r\n                for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n                {\r\n                    if (strcmp(g_OvFsMergedContents[Index].Name, Entry->d_name) == 0)\r\n                    {\r\n                        FoundIndex = Index + 2;\r\n                        break;\r\n                    }\r\n                }\r\n\r\n                if (Index == LXT_COUNT_OF(g_OvFsMergedContents))\r\n                {\r\n                    for (Index = 0; Index < LXT_COUNT_OF(writeUpperCreate); ++Index)\r\n                    {\r\n                        if (strcmp(writeUpperCreate[Index], Entry->d_name) == 0)\r\n                        {\r\n                            FoundIndex = Index + 2 + LXT_COUNT_OF(g_OvFsMergedContents);\r\n                            break;\r\n                        }\r\n                    }\r\n\r\n                    if (Index == LXT_COUNT_OF(writeUpperCreate))\r\n                    {\r\n                        LxtLogError(\"Unexpected entry %s\", Entry->d_name);\r\n                        LxtCheckNotEqual(Index, LXT_COUNT_OF(writeUpperCreate), \"%d\");\r\n                    }\r\n                }\r\n            }\r\n\r\n            LxtCheckEqual(Found[FoundIndex], 0, \"%d\");\r\n            Found[FoundIndex] = 1;\r\n        }\r\n\r\n        Entry = (struct dirent*)Buffer;\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(Found); ++Index)\r\n    {\r\n        LxtCheckEqual(Found[FoundIndex], 1, \"%d\");\r\n    }\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check the behavior for read directory on sub directories.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if (S_ISDIR(g_OvFsMergedContents[Index].Mode) == FALSE)\r\n        {\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedContents[Index].Path, O_RDONLY));\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n            Count = 0;\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            Count += 1;\r\n        }\r\n\r\n        LxtCheckEqual(Count, 2, \"%d\");\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check that none of the operations hydrated files from the lower\r\n    // directory.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestInodeWriteOps(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests inode operations that may modify state.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char BytesRead;\r\n    char Buffer[100];\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    int FdWrite;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    struct stat StatBufferWrite;\r\n\r\n    ChildPid = -1;\r\n    Fd = -1;\r\n    FdWrite = -1;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    //\r\n    // Open the same file for read and write. The read file will be from the\r\n    // lower layer, but opening the file for write will cause the file to be\r\n    // hydrated in the upper layer and the inode updated.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerFile\", O_RDONLY, 0));\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBufferWrite), ENOENT);\r\n    LxtCheckErrno(FdWrite = open(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerFile\", O_RDWR, 0));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBufferWrite));\r\n    LxtCheckErrno(fstat(FdWrite, &StatBufferWrite));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckNotEqual(StatBuffer.st_ino, StatBufferWrite.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(StatBuffer.st_mode, StatBufferWrite.st_mode, \"%d\");\r\n\r\n    //\r\n    // Check that the inode numbers are the same now that both files are open.\r\n    // Any open files referring to this inode will access the new inode\r\n    // metadata.\r\n    //\r\n\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatBuffer.st_ino, StatBufferWrite.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrno(fstat(FdWrite, &StatBufferWrite));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatBuffer.st_ino, StatBufferWrite.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(StatBuffer.st_mode, StatBufferWrite.st_mode, \"%d\");\r\n\r\n    //\r\n    // Check that chmod on one of the file descriptors impacts both. Any open\r\n    // files referring to this inode will access the new inode metadata.\r\n    //\r\n\r\n    LxtCheckNotEqual(StatBuffer.st_mode, S_IFREG | 0111, \"%d\");\r\n    LxtCheckErrno(fchmod(FdWrite, 0111));\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    LxtCheckErrno(fstat(FdWrite, &StatBufferWrite));\r\n    LxtLogInfo(\"%d, %d\", StatBuffer.st_mode, StatBufferWrite.st_mode);\r\n    LxtCheckEqual(StatBuffer.st_mode, S_IFREG | 0111, \"%d\");\r\n    LxtCheckEqual(StatBufferWrite.st_mode, S_IFREG | 0111, \"%d\");\r\n\r\n    //\r\n    // Check that writing only impacts the file object opened for write and not\r\n    // the one opened for read. Any open files objects will access the old file\r\n    // data.\r\n    //\r\n\r\n    LxtCheckErrno(write(FdWrite, \"OnlyInLowerFileModified\", sizeof(\"OnlyInLowerFileModified\")));\r\n    LxtCheckErrno(BytesRead = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n    Buffer[BytesRead] = 0;\r\n    LxtCheckStringEqual(Buffer, \"OnlyInLowerFile\") LxtCheckErrno(lseek(FdWrite, SEEK_SET, 0));\r\n    LxtCheckErrno(BytesRead = read(FdWrite, Buffer, sizeof(Buffer) - 1));\r\n    Buffer[BytesRead] = 0;\r\n    LxtCheckStringEqual(Buffer, \"OnlyInLowerFileModified\")\r\n\r\n        //\r\n        // Unmount and check it was unmounted.\r\n        //\r\n\r\n        if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    if (FdWrite != -1)\r\n    {\r\n        LxtClose(FdWrite);\r\n        FdWrite = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    //\r\n    // Check that chmod, chown, and utime hydrate files.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(chmod(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(chown(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerFile\", 1, 1));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckErrnoFailure(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(utimensat(-1, OVFS_TEST_MOUNT_PATH \"/OnlyInLowerSym\", NULL, AT_SYMLINK_NOFOLLOW));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer));\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    //\r\n    // Check that the 4 types of creation hydrate paths.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(symlink(\"CreatedSymlink\", OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir/CreatedSymlink\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckErrno(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir/CreatedSymlink\", &StatBuffer));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir/CreatedFile\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir/CreatedFile\", &StatBuffer));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir/CreatedDir\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir/CreatedDir\", &StatBuffer));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(link(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerFile\", OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir/CreatedLink\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir/CreatedLink\", &StatBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckErrnoFailure(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    //\r\n    // Check the undefined behavior for O_RDONLY with O_TRUNC\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerFile\", O_RDONLY | O_TRUNC, 0));\r\n    LxtCheckErrnoFailure(ftruncate(Fd, 0), EINVAL);\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckNotEqual(StatBuffer.st_size, 0, \"%d\");\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckEqual(StatBuffer.st_size, 0, \"%d\");\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInUpperFile\", O_RDONLY | O_TRUNC, 0));\r\n    LxtCheckErrnoFailure(ftruncate(Fd, 0), EINVAL);\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_LOWER_DIR \"/OnlyInUpperFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n    LxtCheckEqual(StatBuffer.st_size, 0, \"%d\");\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/InBothFile\", O_RDONLY | O_TRUNC, 0));\r\n    LxtCheckErrnoFailure(ftruncate(Fd, 0), EINVAL);\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckNotEqual(StatBuffer.st_size, 0, \"%d\");\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n    LxtCheckEqual(StatBuffer.st_size, 0, \"%d\");\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    //\r\n    // Repeat the above for a file outside of overlay.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckNotEqual(StatBuffer.st_size, 0, \"%d\");\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", O_RDONLY | O_TRUNC, 0));\r\n    LxtCheckErrnoFailure(ftruncate(Fd, 0), EINVAL);\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckEqual(StatBuffer.st_size, 0, \"%d\");\r\n\r\n    //\r\n    // Repeat the above for a file where write access is not granted.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckNotEqual(StatBuffer.st_size, 0, \"%d\");\r\n    LxtCheckErrno(chmod(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", 0444));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Drop privileges so the current process does not have VFS capabilities\r\n        // and is in another user\\group.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(2002));\r\n        LxtCheckErrno(setuid(2002));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Open the file with different truncate variations.\r\n        //\r\n\r\n        LxtCheckErrno(Fd = open(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", O_RDONLY, 0));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        LxtCheckErrnoFailure(Fd = open(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", O_RDWR, 0), EACCES);\r\n        LxtCheckErrnoFailure(Fd = open(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", O_RDONLY | O_TRUNC, 0), EACCES);\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    if (FdWrite != -1)\r\n    {\r\n        LxtClose(FdWrite);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestInodeUnlink(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests inode operations unlink.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int FdLower;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n\r\n    Fd = -1;\r\n    FdLower = -1;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance and check for the expected file state.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(lstat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISLNK(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(lstat(OVFS_TEST_UPPER_DIR \"/InBothSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISLNK(StatBuffer.st_mode));\r\n\r\n    //\r\n    // Unlink each entry and check for the expected behavior.\r\n    //\r\n\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFile\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInLowerSym\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/OnlyInUpperDir\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperDir\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInUpperSym\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperSym\", &StatBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/InBothDir\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/InBothFile\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/InBothSym\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothSym\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n\r\n    //\r\n    // Check that the lower file is detected during open and not on unlink.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\", O_RDWR));\r\n    LxtCheckErrno(FdLower = creat(OVFS_TEST_LOWER_DIR \"/OnlyInUpperFile\", 0777));\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Repeat the above, but create the file in the lower first.\r\n    //\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtClose(FdLower);\r\n    FdLower = -1;\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrno(FdLower = creat(OVFS_TEST_LOWER_DIR \"/OnlyInUpperFile\", 0777));\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\", O_RDWR));\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\"));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInUpperFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtClose(FdLower);\r\n    FdLower = -1;\r\n\r\n    //\r\n    // Check the behavior for unlink while a file is open.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrno(FdLower = open(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFile\", O_RDONLY));\r\n    LxtCheckErrno(fstat(FdLower, &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFile\", O_RDWR));\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFile\"));\r\n\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(fstat(FdLower, &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(fchmod(FdLower, 0777));\r\n    LxtCheckErrno(fchmod(Fd, 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtClose(FdLower);\r\n    FdLower = -1;\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\", O_RDONLY));\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(unlink(OVFS_TEST_MERGED_DIR \"/OnlyInUpperFile\"));\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    LxtCheckTrue(S_ISREG(StatBuffer.st_mode));\r\n    LxtCheckErrno(fchmod(Fd, 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check the behavior for rmdir while a directory is open.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrno(FdLower = open(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\", O_RDONLY));\r\n    LxtCheckErrno(fstat(FdLower, &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\"));\r\n    LxtCheckErrno(fstat(FdLower, &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrnoFailure(fchmod(FdLower, 0777), ENOTDIR);\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MERGED_DIR \"/InBothDir\", O_RDONLY));\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MERGED_DIR \"/InBothDir\"));\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(fchmod(Fd, 0777));\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    if (FdLower != -1)\r\n    {\r\n        LxtClose(FdLower);\r\n        FdLower = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    if (FdLower != -1)\r\n    {\r\n        LxtClose(FdLower);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestInodeXattr(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests inode operations that hydrate xattrs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Count;\r\n    struct\r\n    {\r\n        char* Path;\r\n        char* Name;\r\n    } HydratedData[] = {\r\n        {OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", \"OnlyInLowerDir\"}, {OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", \"OnlyInLowerFile\"}};\r\n\r\n    int Index;\r\n    char* ListCurrent;\r\n    char List[256];\r\n    ssize_t ListSize;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    char Value[64];\r\n    ssize_t ValueSize;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    //\r\n    // Check the xattr state in the merged directory.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedContents); ++Index)\r\n    {\r\n        if ((S_ISDIR(g_OvFsMergedContents[Index].Mode) == FALSE) && (S_ISREG(g_OvFsMergedContents[Index].Mode) == FALSE))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(ListSize = listxattr(g_OvFsMergedContents[Index].Path, List, sizeof(List)));\r\n\r\n        ListCurrent = List;\r\n        Count = 0;\r\n        for (;;)\r\n        {\r\n            LxtCheckErrno(ValueSize = getxattr(g_OvFsMergedContents[Index].Path, ListCurrent, Value, sizeof(Value)));\r\n\r\n            LxtCheckStringEqual(Value, g_OvFsMergedContents[Index].Name);\r\n            ListCurrent += strlen(ListCurrent) + 1;\r\n            Count += 1;\r\n            if (ListCurrent >= List + ListSize)\r\n            {\r\n                break;\r\n            }\r\n        }\r\n\r\n        LxtCheckEqual(Count, 2, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Hydrate files and check that the xattr state was copied.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckResult(chmod(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\", 0111));\r\n    LxtCheckResult(chmod(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFile\", 0111));\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(HydratedData); ++Index)\r\n    {\r\n        LxtCheckErrno(stat(HydratedData[Index].Path, &StatBuffer));\r\n        LxtCheckErrno(ListSize = listxattr(HydratedData[Index].Path, List, sizeof(List)));\r\n\r\n        ListCurrent = List;\r\n        Count = 0;\r\n        for (;;)\r\n        {\r\n            LxtCheckErrno(ValueSize = getxattr(HydratedData[Index].Path, ListCurrent, Value, sizeof(Value)));\r\n\r\n            LxtCheckStringEqual(Value, HydratedData[Index].Name);\r\n            ListCurrent += strlen(ListCurrent) + 1;\r\n            Count += 1;\r\n            if (ListCurrent >= List + ListSize)\r\n            {\r\n                break;\r\n            }\r\n        }\r\n\r\n        LxtCheckEqual(Count, 2, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    //\r\n    // Check the behavior for hydration of \"trusted.overlay.opaque\" where the\r\n    // value is dropped.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    Value[0] = 'y';\r\n    LxtCheckErrno(setxattr(OVFS_TEST_LOWER_DIR \"/OnlyInLowerDir\", \"trusted.overlay.opaque\", Value, 1, XATTR_CREATE));\r\n\r\n    LxtCheckErrno(setxattr(OVFS_TEST_LOWER_DIR \"/OnlyInLowerFile\", \"trusted.overlay.opaque\", Value, 1, XATTR_CREATE));\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckResult(chmod(OVFS_TEST_MERGED_DIR \"/OnlyInLowerDir\", 0111));\r\n    LxtCheckResult(chmod(OVFS_TEST_MERGED_DIR \"/OnlyInLowerFile\", 0111));\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(HydratedData); ++Index)\r\n    {\r\n        LxtCheckErrno(stat(HydratedData[Index].Path, &StatBuffer));\r\n        LxtCheckErrno(ListSize = listxattr(HydratedData[Index].Path, List, sizeof(List)));\r\n\r\n        ListCurrent = List;\r\n        Count = 0;\r\n        for (;;)\r\n        {\r\n            LxtCheckErrno(ValueSize = getxattr(HydratedData[Index].Path, ListCurrent, Value, sizeof(Value)));\r\n\r\n            LxtCheckStringEqual(Value, HydratedData[Index].Name);\r\n            ListCurrent += strlen(ListCurrent) + 1;\r\n            Count += 1;\r\n            if (ListCurrent >= List + ListSize)\r\n            {\r\n                break;\r\n            }\r\n        }\r\n\r\n        LxtCheckEqual(Count, 2, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Unmount and check that it was unmounted.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestLowerWhiteout(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests how whiteouts behavior when they are in the lower layer.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[256];\r\n    int BytesRead;\r\n    int Count;\r\n    struct dirent* Entry;\r\n    int EntryPosition;\r\n    int Fd;\r\n    int Found[LXT_COUNT_OF(g_OvFsMergedMultiContents) + 2];\r\n    int FoundIndex;\r\n    int Index;\r\n    void* Mapping;\r\n    void* MapResult;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    struct stat StatMergedBuffer;\r\n\r\n    Fd = -1;\r\n    Mapping = NULL;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Create some whiteout files in the lower directory.\r\n    //\r\n\r\n    LxtCheckResult(mknod(OVFS_TEST_LOWER_DIR \"/OnlyInLowerDir/whiteoutFile\", S_IFCHR | 0777, 0));\r\n    LxtCheckResult(mknod(OVFS_TEST_LOWER_DIR \"/InBothDir/whiteoutFile\", S_IFCHR | 0777, 0));\r\n\r\n    //\r\n    // Mount an overlayfs instance and check the behavior for the whiteouts\r\n    // created above.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_DEFAULT));\r\n\r\n    //\r\n    // Check that the whiteout cannot be opened but are reported through readdir\r\n    // when the folder is not merged with the upper.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerDir/whiteoutFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir/whiteoutFile\", &StatMergedBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n        Count = 0;\r\n    for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n    {\r\n\r\n        Entry = (struct dirent*)&Buffer[EntryPosition];\r\n        LxtLogInfo(\"%s\", Entry->d_name);\r\n        Count += 1;\r\n    }\r\n    LxtCheckEqual(Count, 3, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check that the whiteout cannot be opened and is not reported through\r\n    // readdir when the folder is merged with the upper.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER_DIR \"/InBothDir/whiteoutFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\", &StatMergedBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/InBothDir\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n        Count = 0;\r\n    for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n    {\r\n\r\n        Entry = (struct dirent*)&Buffer[EntryPosition];\r\n        LxtLogInfo(\"%s\", Entry->d_name);\r\n        Count += 1;\r\n    }\r\n    LxtCheckEqual(Count, 2, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check that the whiteout can be overwritten in the upper and that it\r\n    // is not replaced by a whiteout when removed.\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatMergedBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir/whiteoutFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\", &StatMergedBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/InBothDir/whiteoutFile\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Repeat the above with multiple lowers.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_MULTI_LOWER));\r\n\r\n    //\r\n    // Check that the whiteout cannot be opened but are reported through readdir\r\n    // when the folder is not merged with the upper.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER_DIR \"/OnlyInLowerDir/whiteoutFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir/whiteoutFile\", &StatMergedBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/OnlyInLowerDir\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n        Count = 0;\r\n    for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n    {\r\n\r\n        Entry = (struct dirent*)&Buffer[EntryPosition];\r\n        LxtLogInfo(\"%s\", Entry->d_name);\r\n        Count += 1;\r\n    }\r\n\r\n    LxtCheckEqual(Count, 3, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check that the whiteout cannot be opened and is not reported through\r\n    // readdir when the folder is merged with the upper.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER_DIR \"/InBothDir/whiteoutFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISCHR(StatBuffer.st_mode));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\", &StatMergedBuffer), ENOENT);\r\n\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/InBothDir\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n        Count = 0;\r\n    for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n    {\r\n\r\n        Entry = (struct dirent*)&Buffer[EntryPosition];\r\n        LxtLogInfo(\"%s\", Entry->d_name);\r\n        Count += 1;\r\n    }\r\n\r\n    LxtCheckEqual(Count, 2, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check that the whiteout can be overwritten in the upper and that it\r\n    // is not replaced by a whiteout when removed.\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatMergedBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/InBothDir/whiteoutFile\", &StatBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatBuffer.st_mode));\r\n    LxtCheckErrno(rmdir(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\"));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MOUNT_PATH \"/InBothDir/whiteoutFile\", &StatMergedBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/InBothDir/whiteoutFile\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Mapping != MAP_FAILED)\r\n    {\r\n        munmap(Mapping, PAGE_SIZE);\r\n    }\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n\r\nint OvFsTestMultipleLower(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests various operations with multiple lower layers.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    int BytesRead;\r\n    int Count;\r\n    struct dirent* Entry;\r\n    int EntryPosition;\r\n    int Fd;\r\n    int Found[LXT_COUNT_OF(g_OvFsMergedMultiContents) + 2];\r\n    int FoundIndex;\r\n    int Index;\r\n    void* Mapping;\r\n    void* MapResult;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    struct stat StatMergedBuffer;\r\n\r\n    Fd = -1;\r\n    Mapping = NULL;\r\n\r\n    //\r\n    // Set up the directories and populate some state.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n\r\n    //\r\n    // Mount an overlayfs instance and check inode operations that do not\r\n    // hydrate files.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_MULTI_LOWER));\r\n\r\n    //\r\n    // Check the behavior for VFS file object operations that support file\r\n    // descriptors opened for read only.\r\n    //\r\n    // N.B. All other file operations will fail the request and are verified\r\n    //      in the VFS access test.\r\n    //\r\n\r\n    //\r\n    // Check the behavior for read directory on the root.\r\n    //\r\n\r\n    memset(Found, 0, sizeof(Found));\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH, O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n\r\n    while (BytesRead > 0)\r\n    {\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            if (strcmp(Entry->d_name, \".\") == 0)\r\n            {\r\n                FoundIndex = 0;\r\n            }\r\n            else if (strcmp(Entry->d_name, \"..\") == 0)\r\n            {\r\n                FoundIndex = 1;\r\n            }\r\n            else\r\n            {\r\n                for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedMultiContents); ++Index)\r\n                {\r\n                    if (strcmp(g_OvFsMergedMultiContents[Index].Name, Entry->d_name) == 0)\r\n                    {\r\n                        FoundIndex = Index + 2;\r\n                        break;\r\n                    }\r\n                }\r\n\r\n                if (Index == LXT_COUNT_OF(g_OvFsMergedMultiContents))\r\n                {\r\n                    LxtLogError(\"Unexpected entry %s\", Entry->d_name);\r\n                    LxtCheckNotEqual(Index, LXT_COUNT_OF(g_OvFsMergedMultiContents), \"%d\");\r\n                }\r\n            }\r\n\r\n            LxtCheckEqual(Found[FoundIndex], 0, \"%d\");\r\n            Found[FoundIndex] = 1;\r\n        }\r\n\r\n        Entry = (struct dirent*)Buffer;\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(Found); ++Index)\r\n    {\r\n        LxtCheckEqual(Found[FoundIndex], 1, \"%d\");\r\n    }\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Check the behavior for read directory on sub directories.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedMultiContents); ++Index)\r\n    {\r\n        if (S_ISDIR(g_OvFsMergedMultiContents[Index].Mode) == FALSE)\r\n        {\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedMultiContents[Index].Path, O_RDONLY));\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)))\r\n\r\n            Count = 0;\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            Count += 1;\r\n        }\r\n\r\n        LxtCheckEqual(Count, 2, \"%d\");\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for read file.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedMultiContents); ++Index)\r\n    {\r\n        if (S_ISREG(g_OvFsMergedMultiContents[Index].Mode) == FALSE)\r\n        {\r\n            continue;\r\n        }\r\n\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedMultiContents[Index].Path, O_RDONLY));\r\n        LxtCheckErrno(BytesRead = read(Fd, Buffer, sizeof(Buffer) - 1));\r\n        Buffer[BytesRead] = 0;\r\n        LxtCheckStringEqual(Buffer, g_OvFsMergedMultiContents[Index].Name);\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior for seek.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_OvFsMergedMultiContents); ++Index)\r\n    {\r\n        if ((S_ISDIR(g_OvFsMergedMultiContents[Index].Mode) == FALSE) && (S_ISREG(g_OvFsMergedMultiContents[Index].Mode) == FALSE))\r\n        {\r\n\r\n            continue;\r\n        }\r\n\r\n        LxtLogInfo(\"%s\", g_OvFsMergedMultiContents[Index].Path);\r\n        LxtCheckErrno(Fd = open(g_OvFsMergedMultiContents[Index].Path, O_RDONLY));\r\n        LxtCheckErrno(lseek(Fd, SEEK_SET, 1));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    //\r\n    // Check that none of the operations hydrated files from the lower\r\n    // directory.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerDir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerFile\", &StatBuffer), ENOENT);\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLowerSym\", &StatBuffer), ENOENT);\r\n\r\n    //\r\n    // Check the search order for the multiple lower layers.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLower2File\", &StatMergedBuffer));\r\n    LxtCheckEqual(S_IFREG | 0444, StatMergedBuffer.st_mode, \"%d\");\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLower23File\", &StatMergedBuffer));\r\n    LxtCheckEqual(S_IFREG | 0444, StatMergedBuffer.st_mode, \"%d\");\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLower3File\", &StatMergedBuffer));\r\n    LxtCheckEqual(S_IFREG | 0111, StatMergedBuffer.st_mode, \"%d\");\r\n\r\n    //\r\n    // Hydrate some files from the lower layers.\r\n    //\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLower2File\", &StatMergedBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER2_DIR \"/OnlyInLower2File\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatMergedBuffer.st_ino, StatBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLower2File\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(chmod(OVFS_TEST_MOUNT_PATH \"/OnlyInLower2File\", 0777));\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLower2File\", &StatMergedBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLower2File\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatMergedBuffer.st_ino, StatBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLower3Dir\", &StatMergedBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER3_DIR \"/OnlyInLower3Dir\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckNotEqual(StatMergedBuffer.st_ino, StatBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLower3Dir\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(chown(OVFS_TEST_MOUNT_PATH \"/OnlyInLower3Dir\", 2001, 2001));\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLower3Dir\", &StatMergedBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLower3Dir\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckNotEqual(StatMergedBuffer.st_ino, StatBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLower23File\", &StatMergedBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_LOWER2_DIR \"/OnlyInLower23File\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatMergedBuffer.st_ino, StatBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLower23File\", &StatBuffer), ENOENT);\r\n    LxtCheckErrno(truncate(OVFS_TEST_MOUNT_PATH \"/OnlyInLower23File\", 0));\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/OnlyInLower23File\", &StatMergedBuffer));\r\n    LxtCheckErrno(stat(OVFS_TEST_UPPER_DIR \"/OnlyInLower23File\", &StatBuffer));\r\n    if (g_LxtUnstableInodes != 0)\r\n    {\r\n        LxtCheckEqual(StatMergedBuffer.st_ino, StatBuffer.st_ino, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    //\r\n    // Check the behavior for entries with the same name but differing types in\r\n    // the lower.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckErrno(mkdir(OVFS_TEST_LOWER_DIR \"/mixedType\", 0777));\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_LOWER_DIR \"/mixedType/onlyInLower1\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_LOWER2_DIR \"/mixedType\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(mkdir(OVFS_TEST_LOWER3_DIR \"/mixedType\", 0777));\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_LOWER3_DIR \"/mixedType/onlyInLower3\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Mount an overlayfs instance and check for the expected lookup and readdir\r\n    // behavior.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_MULTI_LOWER));\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/mixedType\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatMergedBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/mixedType/onlyInLower1\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISREG(StatMergedBuffer.st_mode));\r\n    LxtCheckErrnoFailure(stat(OVFS_TEST_MOUNT_PATH \"/mixedType/onlyInLower3\", &StatMergedBuffer), ENOENT);\r\n\r\n    Count = 0;\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/mixedType\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n\r\n    while (BytesRead > 0)\r\n    {\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            LxtLogInfo(\"%s\", Entry->d_name);\r\n            Count += 1;\r\n        }\r\n\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    LxtCheckEqual(Count, 3, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Repeat the above with the mismatch in the lowest layer.\r\n    //\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    //\r\n    // Check the behavior for entries with the same name but differing types in\r\n    // the lower.\r\n    //\r\n\r\n    LxtCheckResult(OvFsTestDirsSetup());\r\n    LxtCheckResult(OvFsTestDirsPopulate());\r\n    LxtCheckErrno(mkdir(OVFS_TEST_LOWER_DIR \"/mixedType\", 0777));\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_LOWER_DIR \"/mixedType/onlyInLower1\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(mkdir(OVFS_TEST_LOWER2_DIR \"/mixedType\", 0777));\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_LOWER2_DIR \"/mixedType/onlyInLower2\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = creat(OVFS_TEST_LOWER3_DIR \"/mixedType\", 0777));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Mount an overlayfs instance and check for the expected lookup and readdir\r\n    // behavior.\r\n    //\r\n\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckErrnoZeroSuccess(mount(\"myovfsnew\", OVFS_TEST_MOUNT_PATH, OVFS_TEST_MOUNT_NAME, 0, OVFS_TEST_MOUNT_MULTI_LOWER));\r\n\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/mixedType\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISDIR(StatMergedBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/mixedType/onlyInLower1\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISREG(StatMergedBuffer.st_mode));\r\n    LxtCheckErrno(stat(OVFS_TEST_MOUNT_PATH \"/mixedType/onlyInLower2\", &StatMergedBuffer));\r\n    LxtCheckTrue(S_ISREG(StatMergedBuffer.st_mode));\r\n\r\n    Count = 0;\r\n    LxtCheckErrno(Fd = open(OVFS_TEST_MOUNT_PATH \"/mixedType\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n\r\n    while (BytesRead > 0)\r\n    {\r\n        for (EntryPosition = 0; EntryPosition < BytesRead; EntryPosition += Entry->d_reclen)\r\n        {\r\n\r\n            Entry = (struct dirent*)&Buffer[EntryPosition];\r\n            LxtLogInfo(\"%s\", Entry->d_name);\r\n            Count += 1;\r\n        }\r\n\r\n        LxtCheckErrno(BytesRead = LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    LxtCheckEqual(Count, 4, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Unmount and check it was unmounted.\r\n    //\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(umount(OVFS_TEST_MOUNT_PATH));\r\n    LxtCheckResult(MountCheckIsNotMount(OVFS_TEST_MOUNT_PATH));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Mapping != MAP_FAILED)\r\n    {\r\n        munmap(Mapping, PAGE_SIZE);\r\n    }\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    umount(OVFS_TEST_MOUNT_PATH);\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/pipe.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    pipe.c\r\n\r\nAbstract:\r\n\r\n    This is the test for pipes.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <stdbool.h>\r\n#include <sys/mman.h>\r\n#include <sys/ioctl.h>\r\n#include <poll.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <sys/wait.h>\r\n#include <sys/epoll.h>\r\n#include <sys/eventfd.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/file.h>\r\n#include <sys/fsuid.h>\r\n#include <sys/cdefs.h>\r\n#include <sys/prctl.h>\r\n#include <linux/capability.h>\r\n#include <limits.h>\r\n\r\n#define LXT_NAME \"Pipe\"\r\n\r\n#define min(_a, _b) ((_a) < (_b) ? (_a) : (_b))\r\n\r\n#define _4KB (4 * 1024)\r\n\r\n#define _32KB (32 * 1024)\r\n\r\n#define _64KB (64 * 1024)\r\n\r\n#define _1MB (1024 * 1024)\r\n\r\n#define PIPE_DEFAULT_MAX_SIZE 1048576\r\n\r\n#define ROUND_TO_PAGES(Size) (((unsigned long int)(Size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))\r\n\r\n#define PIPE_BUFFER_LENGTH (_1MB / sizeof(unsigned int))\r\n\r\n#define PIPE_LOOPS (8)\r\n\r\n#define PIPE_FIFO \"/data/testfifo\"\r\n#define PIPE_FIFO_MESSAGE \"Hello World!\"\r\n\r\n//\r\n// Use /dev/fd, rather than /proc/self/fd directly, because that's what bash\r\n// uses for process substitution.\r\n//\r\n\r\n#define PIPE_FD_PATH_FORMAT \"/dev/fd/%d\"\r\n#define PIPE_CHILD_PATH_FORMAT \"%s/%s\"\r\n\r\nint PipeCheckFdPath(int Fd);\r\n\r\nLXT_VARIATION_HANDLER PipeEpoll;\r\n\r\nLXT_VARIATION_HANDLER PipeFifo;\r\n\r\nLXT_VARIATION_HANDLER PipeFifoNonBlock;\r\n\r\nLXT_VARIATION_HANDLER PipeFifoReadWrite;\r\n\r\nLXT_VARIATION_HANDLER PipeFifoReopen;\r\n\r\nLXT_VARIATION_HANDLER PipeReopen;\r\n\r\nLXT_VARIATION_HANDLER PipeStat;\r\n\r\nLXT_VARIATION_HANDLER PipeFileLocking;\r\n\r\nLXT_VARIATION_HANDLER PipeSecurity;\r\n\r\nLXT_VARIATION_HANDLER PipeFcntl;\r\n\r\nint PipeReader(unsigned int* Buffer, size_t BufferLength, int Pipe, bool Polling);\r\n\r\nint PipeReaderHangup(PLXT_ARGS Args);\r\n\r\nint PipeWriterHangup(PLXT_ARGS Args);\r\n\r\nint PipeTest(bool Polling, bool UsePipe2);\r\n\r\nint PipeVariation0(PLXT_ARGS Args);\r\n\r\nint PipeVariation1(PLXT_ARGS Args);\r\n\r\nint PipeVariationIoctl(PLXT_ARGS Args);\r\n\r\nint PipeWriter(unsigned int* Buffer, size_t BufferLength, int Pipe, bool Polling);\r\n\r\nint PipeZeroByteRead(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Pipe0\", PipeVariation0},\r\n    {\"Pipe1\", PipeVariation1},\r\n    {\"Pipe reader hangup\", PipeReaderHangup},\r\n    {\"Pipe writer hangup\", PipeWriterHangup},\r\n    {\"Pipe ioctls\", PipeVariationIoctl},\r\n    {\"Pipe - epoll\", PipeEpoll},\r\n    {\"Pipe - Fifo\", PipeFifo},\r\n    {\"Pipe - Fifo O_NONBLOCK\", PipeFifoNonBlock},\r\n    {\"Pipe - Fifo O_RDWR\", PipeFifoReadWrite},\r\n    {\"Pipe - Fifo re-open\", PipeFifoReopen},\r\n    {\"Pipe - fstat\", PipeStat},\r\n    {\"Pipe - File locking\", PipeFileLocking},\r\n    {\"Pipe - /proc/self/fd reopen\", PipeReopen},\r\n    {\"Pipe - Zero byte read\", PipeZeroByteRead},\r\n    {\"Pipe - security attributes\", PipeSecurity},\r\n    {\"Pipe - fcntl\", PipeFcntl}};\r\n\r\nint PipeTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint PipeCheckFdPath(int Fd)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine checks the path string for a pipe file descriptor.\r\n\r\n    N.B. This routine should not be used for fifos.\r\n\r\nArguments:\r\n\r\n    Fd - Supplies the pipe file descriptor.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Expected[100];\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    LxtCheckErrnoZeroSuccess(fstat(Fd, &Stat));\r\n    sprintf(Expected, \"pipe:[%lu]\", Stat.st_ino);\r\n    LxtCheckResult(LxtCheckFdPath(Fd, Expected));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint PipeEpoll(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests polling behavior for the read and write end of pipes.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buf[3];\r\n    int Count;\r\n    int Index;\r\n    int Pipes[2];\r\n    int PollFd;\r\n    int Result;\r\n    struct epoll_event Event;\r\n\r\n    PollFd = -1;\r\n    for (Index = 0; Index < LXT_COUNT_OF(Pipes); Index += 1)\r\n    {\r\n        Pipes[Index] = -1;\r\n    }\r\n\r\n    //\r\n    // Create a pipe and write to it so it's both read and write ready.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(pipe(Pipes));\r\n    LxtCheckErrno(write(Pipes[1], \"test\", 4));\r\n\r\n    //\r\n    // Make sure polling for write times out on the read descriptor, but\r\n    // polling for read works.\r\n    //\r\n\r\n    LxtCheckErrno(PollFd = epoll_create1(0));\r\n    Event.events = EPOLLOUT;\r\n    Event.data.fd = Pipes[0];\r\n    LxtCheckErrno(epoll_ctl(PollFd, EPOLL_CTL_ADD, Pipes[0], &Event));\r\n    LxtCheckErrnoZeroSuccess(epoll_wait(PollFd, &Event, 1, 0));\r\n    Event.events = EPOLLIN;\r\n    LxtCheckErrno(epoll_ctl(PollFd, EPOLL_CTL_MOD, Pipes[0], &Event));\r\n    memset(&Event, 0, sizeof(Event));\r\n    LxtCheckErrno(Count = epoll_wait(PollFd, &Event, 1, 0));\r\n    LxtCheckEqual(Count, 1, \"%d\");\r\n    LxtCheckEqual(Event.events, EPOLLIN, \"0x%x\");\r\n\r\n    //\r\n    // Make sure polling for read times out on the write descriptor, but\r\n    // polling for write works.\r\n    //\r\n\r\n    LxtCheckErrno(epoll_ctl(PollFd, EPOLL_CTL_DEL, Pipes[0], NULL));\r\n    Event.events = EPOLLIN;\r\n    Event.data.fd = Pipes[1];\r\n    LxtCheckErrno(epoll_ctl(PollFd, EPOLL_CTL_ADD, Pipes[1], &Event));\r\n    LxtCheckErrnoZeroSuccess(epoll_wait(PollFd, &Event, 1, 0));\r\n    Event.events = EPOLLOUT;\r\n    LxtCheckErrno(epoll_ctl(PollFd, EPOLL_CTL_MOD, Pipes[1], &Event));\r\n    memset(&Event, 0, sizeof(Event));\r\n    LxtCheckErrno(Count = epoll_wait(PollFd, &Event, 1, 0));\r\n    LxtCheckEqual(Count, 1, \"%d\");\r\n    LxtCheckEqual(Event.events, EPOLLOUT, \"0x%x\");\r\n\r\n    //\r\n    // Test edge triggered epoll events.\r\n    //\r\n\r\n    Event.events = EPOLLOUT | EPOLLET;\r\n    LxtCheckErrno(epoll_ctl(PollFd, EPOLL_CTL_MOD, Pipes[1], &Event));\r\n    Event.events = EPOLLIN | EPOLLET;\r\n    Event.data.fd = Pipes[0];\r\n    LxtCheckErrno(epoll_ctl(PollFd, EPOLL_CTL_ADD, Pipes[0], &Event));\r\n    memset(&Event, 0, sizeof(Event));\r\n    LxtCheckErrno(Count = epoll_wait(PollFd, &Event, 1, 0));\r\n    LxtCheckEqual(Count, 1, \"%d\");\r\n    LxtCheckEqual(Event.events, EPOLLOUT, \"0x%x\");\r\n    LxtCheckErrno(Count = epoll_wait(PollFd, &Event, 1, 0));\r\n    LxtCheckEqual(Count, 1, \"%d\");\r\n    LxtCheckEqual(Event.events, EPOLLIN, \"0x%x\");\r\n\r\n    //\r\n    // Both edge-triggered events should have fired, so no new events should\r\n    // be available.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(epoll_wait(PollFd, &Event, 1, 0));\r\n\r\n    //\r\n    // Perform read and write operations and recheck epoll status.\r\n    //\r\n\r\n    LxtCheckErrno(write(Pipes[1], \"more\", 4));\r\n    LxtCheckErrno(Count = epoll_wait(PollFd, &Event, 1, 0));\r\n    LxtCheckEqual(Count, 1, \"%d\");\r\n    if (Event.events == EPOLLOUT)\r\n    {\r\n\r\n        //\r\n        // TODO_LX: The WSL pipe implementation shares the epoll between both\r\n        //          endpoints, so the write endpoint is triggered here when it\r\n        //          is not expected.\r\n        //\r\n\r\n        LxtCheckErrno(Count = epoll_wait(PollFd, &Event, 1, 0));\r\n        LxtCheckEqual(Count, 1, \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(Event.events, EPOLLIN, \"0x%x\");\r\n    LxtCheckErrnoZeroSuccess(epoll_wait(PollFd, &Event, 1, 0));\r\n    LxtCheckErrno(read(Pipes[0], Buf, sizeof(Buf)));\r\n    LxtCheckErrnoZeroSuccess(epoll_wait(PollFd, &Event, 1, 0));\r\n\r\nErrorExit:\r\n    if (PollFd >= 0)\r\n    {\r\n        close(PollFd);\r\n    }\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(Pipes); Index += 1)\r\n    {\r\n        if (Pipes[Index] >= 0)\r\n        {\r\n            close(Pipes[Index]);\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeFifo(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests fifo files.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    unsigned int* Buffer;\r\n    ssize_t Bytes;\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    int Fd2;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    Fd = -1;\r\n    Fd2 = -1;\r\n    Buffer = NULL;\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n\r\n    //\r\n    // Create the fifo and make sure umask is applied.\r\n    //\r\n\r\n    umask(022);\r\n    LxtCheckErrnoZeroSuccess(mkfifo(PIPE_FIFO, 0666));\r\n    LxtCheckErrnoZeroSuccess(lstat(PIPE_FIFO, &Stat));\r\n    LxtCheckEqual(Stat.st_mode, S_IFIFO | 0644, \"0%o\");\r\n\r\n    //\r\n    // Check error when the file exists.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(mkfifo(PIPE_FIFO, 0666), EEXIST);\r\n\r\n    //\r\n    // Fork and connect.\r\n    //\r\n\r\n    Buffer = malloc(PIPE_BUFFER_LENGTH * sizeof(int));\r\n    LxtCheckNotEqual(Buffer, NULL, \"%p\");\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(Fd = open(PIPE_FIFO, O_RDONLY));\r\n        LxtCheckResult(LxtCheckFdPath(Fd, PIPE_FIFO));\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckResult(PipeReader(Buffer, PIPE_BUFFER_LENGTH, Fd, false));\r\n\r\n        //\r\n        // Connect a second writer.\r\n        //\r\n\r\n        LxtCheckErrno(Fd2 = open(PIPE_FIFO, O_WRONLY));\r\n        Buffer[0] = 42;\r\n        LxtCheckErrno(Bytes = write(Fd2, Buffer, sizeof(int)));\r\n        LxtCheckEqual(Bytes, sizeof(int), \"%ld\");\r\n        Buffer[0] = 0;\r\n        LxtCheckErrno(Bytes = read(Fd, Buffer, PIPE_BUFFER_LENGTH * sizeof(int)));\r\n        LxtCheckEqual(Bytes, sizeof(int), \"%ld\");\r\n        LxtCheckEqual(Buffer[0], 42, \"%u\");\r\n        close(Fd);\r\n        close(Fd2);\r\n        exit(0);\r\n    }\r\n\r\n    //\r\n    // Sleep to test blocking behavior on open for the child.\r\n    //\r\n\r\n    sleep(1);\r\n    LxtCheckErrno(Fd = open(PIPE_FIFO, O_WRONLY));\r\n    LxtCheckResult(LxtCheckFdPath(Fd, PIPE_FIFO));\r\n\r\n    //\r\n    // Use the synchronization point to ensure open unblocks before calling\r\n    // write.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtCheckResult(PipeWriter(Buffer, PIPE_BUFFER_LENGTH, Fd, false));\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    if (Fd2 >= 0)\r\n    {\r\n        close(Fd2);\r\n    }\r\n\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (Buffer != NULL)\r\n    {\r\n        free(Buffer);\r\n    }\r\n\r\n    unlink(PIPE_FIFO);\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY();\r\n    return Result;\r\n}\r\n\r\nint PipeFifoNonBlock(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests fifo files with O_NONBLOCK.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    unsigned int* Buffer;\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    Fd = -1;\r\n    Buffer = NULL;\r\n\r\n    //\r\n    // Create the fifo and make sure umask is applied.\r\n    //\r\n\r\n    umask(0);\r\n    LxtCheckErrnoZeroSuccess(mkfifo(PIPE_FIFO, 0666));\r\n    LxtCheckErrnoZeroSuccess(lstat(PIPE_FIFO, &Stat));\r\n    LxtCheckEqual(Stat.st_mode, S_IFIFO | 0666, \"0%o\");\r\n\r\n    //\r\n    // Try to connect with write when there's no reader, non-blocking.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(Fd = open(PIPE_FIFO, O_WRONLY | O_NONBLOCK), ENXIO);\r\n\r\n    //\r\n    // Fork and connect.\r\n    //\r\n\r\n    Buffer = malloc(PIPE_BUFFER_LENGTH * sizeof(int));\r\n    LxtCheckNotEqual(Buffer, NULL, \"%p\");\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(Fd = open(PIPE_FIFO, O_RDONLY | O_NONBLOCK));\r\n        LxtCheckResult(LxtCheckFdPath(Fd, PIPE_FIFO));\r\n        LxtCheckErrnoZeroSuccess(read(Fd, Buffer, PIPE_BUFFER_LENGTH)) LxtCheckResult(PipeReader(Buffer, PIPE_BUFFER_LENGTH, Fd, true));\r\n        close(Fd);\r\n        exit(0);\r\n    }\r\n\r\n    //\r\n    // O_NONBLOCK for write works once there is a reader.\r\n    //\r\n\r\n    sleep(1);\r\n    LxtCheckErrno(Fd = open(PIPE_FIFO, O_WRONLY | O_NONBLOCK));\r\n    LxtCheckResult(LxtCheckFdPath(Fd, PIPE_FIFO));\r\n    LxtCheckResult(PipeWriter(Buffer, PIPE_BUFFER_LENGTH, Fd, true));\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (Buffer != NULL)\r\n    {\r\n        free(Buffer);\r\n    }\r\n\r\n    unlink(PIPE_FIFO);\r\n    return Result;\r\n}\r\n\r\nint PipeFifoReadWrite(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests opening a fifo for read/write.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    ssize_t Bytes;\r\n    int Fd;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    Fd = -1;\r\n\r\n    //\r\n    // Create the fifo and make sure umask is applied.\r\n    //\r\n\r\n    umask(0);\r\n    LxtCheckErrnoZeroSuccess(mkfifo(PIPE_FIFO, 0666));\r\n    LxtCheckErrnoZeroSuccess(lstat(PIPE_FIFO, &Stat));\r\n    LxtCheckEqual(Stat.st_mode, S_IFIFO | 0666, \"0%o\");\r\n\r\n    //\r\n    // Open the fifo for read/write which should not block.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(PIPE_FIFO, O_RDWR));\r\n    LxtCheckResult(LxtCheckFdPath(Fd, PIPE_FIFO));\r\n    LxtCheckErrno(Bytes = write(Fd, PIPE_FIFO_MESSAGE, strlen(PIPE_FIFO_MESSAGE) + 1));\r\n\r\n    LxtCheckEqual(Bytes, strlen(PIPE_FIFO_MESSAGE) + 1, \"%ld\");\r\n    LxtCheckErrno(Bytes = read(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Bytes, strlen(PIPE_FIFO_MESSAGE) + 1, \"%ld\");\r\n    LxtCheckStringEqual(Buffer, PIPE_FIFO_MESSAGE);\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    unlink(PIPE_FIFO);\r\n    return Result;\r\n}\r\n\r\nint PipeFifoReopen(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests reopening a pipe after closing one end.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    ssize_t Bytes;\r\n    int ChildPid;\r\n    int Count;\r\n    int ReadFd;\r\n    int Result;\r\n    int WriteFd;\r\n    struct pollfd PollFd;\r\n\r\n    ReadFd = -1;\r\n    WriteFd = -1;\r\n\r\n    //\r\n    // Fork because this test changes signal state.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGPIPE, SA_SIGINFO));\r\n\r\n        //\r\n        // Create and open the fifo.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(mkfifo(PIPE_FIFO, 0666));\r\n        LxtCheckErrno(ReadFd = open(PIPE_FIFO, O_RDONLY | O_NONBLOCK));\r\n        LxtCheckErrno(WriteFd = open(PIPE_FIFO, O_WRONLY | O_NONBLOCK));\r\n        memset(&PollFd, 0, sizeof(PollFd));\r\n\r\n        //\r\n        // Check the initial state of the write end.\r\n        //\r\n\r\n        PollFd.fd = WriteFd;\r\n        PollFd.events = POLLIN | POLLOUT | POLLERR | POLLHUP;\r\n        LxtCheckErrno(Count = poll(&PollFd, 1, 1000));\r\n        LxtCheckEqual(Count, 1, \"%d\");\r\n        LxtCheckEqual(PollFd.revents, POLLOUT, \"0x%x\");\r\n\r\n        //\r\n        // Close the read end and check the write end returns error.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(close(ReadFd));\r\n        ReadFd = -1;\r\n        LxtCheckErrno(Count = poll(&PollFd, 1, 1000));\r\n        LxtCheckEqual(Count, 1, \"%d\");\r\n        LxtCheckEqual(PollFd.revents, POLLOUT | POLLERR, \"0x%x\");\r\n        LxtCheckErrnoFailure(write(WriteFd, PIPE_FIFO_MESSAGE, strlen(PIPE_FIFO_MESSAGE) + 1), EPIPE);\r\n\r\n        LxtCheckResult(LxtSignalCheckInfoReceived(SIGPIPE, SI_USER, getpid(), getuid()));\r\n\r\n        //\r\n        // Try to open an additional write end, which should fail because there\r\n        // is no reader.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(open(PIPE_FIFO, O_WRONLY | O_NONBLOCK), ENXIO);\r\n\r\n        //\r\n        // Open a new read end and check the write end is functional again.\r\n        //\r\n\r\n        LxtCheckErrno(ReadFd = open(PIPE_FIFO, O_RDONLY | O_NONBLOCK));\r\n        LxtCheckErrno(Count = poll(&PollFd, 1, 1000));\r\n        LxtCheckEqual(Count, 1, \"%d\");\r\n        LxtCheckEqual(PollFd.revents, POLLOUT, \"0x%x\");\r\n        LxtCheckErrno(Bytes = write(WriteFd, PIPE_FIFO_MESSAGE, strlen(PIPE_FIFO_MESSAGE) + 1));\r\n\r\n        LxtCheckEqual(Bytes, strlen(PIPE_FIFO_MESSAGE) + 1, \"%ld\");\r\n\r\n        //\r\n        // Check the poll state of the read end.\r\n        //\r\n\r\n        PollFd.fd = ReadFd;\r\n        LxtCheckErrno(Count = poll(&PollFd, 1, 1000));\r\n        LxtCheckEqual(Count, 1, \"%d\");\r\n        LxtCheckEqual(PollFd.revents, POLLIN, \"0x%x\");\r\n\r\n        //\r\n        // Close the write end and check the read end reports hangup.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(close(WriteFd));\r\n        WriteFd = -1;\r\n        LxtCheckErrno(Count = poll(&PollFd, 1, 1000));\r\n        LxtCheckEqual(Count, 1, \"%d\");\r\n        LxtCheckEqual(PollFd.revents, POLLIN | POLLHUP, \"0x%x\");\r\n\r\n        //\r\n        // Open a new write end and check the read end returns the old data.\r\n        //\r\n\r\n        LxtCheckErrno(WriteFd = open(PIPE_FIFO, O_WRONLY | O_NONBLOCK));\r\n        LxtCheckErrno(Count = poll(&PollFd, 1, 1000));\r\n        LxtCheckEqual(Count, 1, \"%d\");\r\n        LxtCheckEqual(PollFd.revents, POLLIN, \"0x%x\");\r\n        LxtCheckErrno(Bytes = read(ReadFd, Buffer, sizeof(Buffer)));\r\n        LxtCheckEqual(Bytes, strlen(PIPE_FIFO_MESSAGE) + 1, \"%ld\");\r\n        LxtCheckStringEqual(Buffer, PIPE_FIFO_MESSAGE);\r\n\r\n        //\r\n        // Read should fail now because there's no more data.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(read(ReadFd, Buffer, sizeof(Buffer)), EAGAIN);\r\n\r\n        //\r\n        // Close the write end and make sure the read end return EOF.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(close(WriteFd));\r\n        LxtCheckErrnoZeroSuccess(read(ReadFd, Buffer, sizeof(Buffer)));\r\n        LxtCheckErrnoZeroSuccess(close(ReadFd));\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    if (ReadFd >= 0)\r\n    {\r\n        close(ReadFd);\r\n    }\r\n\r\n    if (WriteFd >= 0)\r\n    {\r\n        close(WriteFd);\r\n    }\r\n\r\n    unlink(PIPE_FIFO);\r\n    return Result;\r\n}\r\n\r\nint PipeReopen(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests reopening a pipe through /proc/self/fd.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    int ChildPid;\r\n    int Fd;\r\n    struct stat FdStat;\r\n    char ChildPath[PATH_MAX];\r\n    char Path[PATH_MAX];\r\n    int Pipes[2];\r\n    struct stat PipeStat;\r\n    int Result;\r\n\r\n    Pipes[0] = -1;\r\n    Pipes[1] = -1;\r\n    Fd = -1;\r\n    ChildPid = -1;\r\n    LxtCheckErrnoZeroSuccess(pipe(Pipes));\r\n    LxtCheckErrno(write(Pipes[1], PIPE_FIFO_MESSAGE, sizeof(PIPE_FIFO_MESSAGE)));\r\n\r\n    //\r\n    // Attempt to reopen the read end.\r\n    //\r\n\r\n    sprintf(Path, PIPE_FD_PATH_FORMAT, Pipes[0]);\r\n    LxtCheckErrno(Fd = open(Path, O_RDONLY));\r\n    memset(Buffer, 0, sizeof(Buffer));\r\n    LxtCheckErrno(read(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckStringEqual(Buffer, PIPE_FIFO_MESSAGE);\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Reopen the write end through the read FD.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(Path, O_WRONLY));\r\n    LxtCheckErrno(write(Fd, PIPE_FIFO_MESSAGE, sizeof(PIPE_FIFO_MESSAGE)));\r\n    memset(Buffer, 0, sizeof(Buffer));\r\n    LxtCheckErrno(read(Pipes[0], Buffer, sizeof(Buffer)));\r\n    LxtCheckStringEqual(Buffer, PIPE_FIFO_MESSAGE);\r\n\r\n    //\r\n    // Check the result of stat.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fstat(Pipes[0], &PipeStat));\r\n    LxtCheckErrnoZeroSuccess(stat(Path, &FdStat));\r\n    LxtCheckMemoryEqual(&PipeStat, &FdStat, sizeof(struct stat));\r\n\r\n    //\r\n    // Failing variations.\r\n    //\r\n\r\n    sprintf(ChildPath, PIPE_CHILD_PATH_FORMAT, Path, \".\");\r\n    LxtCheckErrnoFailure(open(ChildPath, O_RDONLY), ENOTDIR);\r\n    sprintf(ChildPath, PIPE_CHILD_PATH_FORMAT, Path, \"..\");\r\n    LxtCheckErrnoFailure(open(ChildPath, O_RDONLY), ENOTDIR);\r\n    sprintf(ChildPath, PIPE_CHILD_PATH_FORMAT, Path, \"foo\");\r\n    LxtCheckErrnoFailure(open(ChildPath, O_RDONLY), ENOTDIR);\r\n    LxtCheckErrnoFailure(openat(Fd, \"foo\", O_RDONLY), ENOTDIR);\r\n\r\n    //\r\n    // Pipes have permissions set to 0600, so they can only be reopened by the\r\n    // owner.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        setuid(1000);\r\n        LxtCheckErrnoFailure(open(Path, O_RDONLY), EACCES);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    if (Pipes[0] >= 0)\r\n    {\r\n        close(Pipes[0]);\r\n    }\r\n\r\n    if (Pipes[1] >= 0)\r\n    {\r\n        close(Pipes[1]);\r\n    }\r\n\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeStat(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the results of fstat on a pipe.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Pipes[2];\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    Pipes[0] = -1;\r\n    Pipes[1] = -1;\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(setfsgid(1001));\r\n        LxtCheckErrno(setfsuid(1000));\r\n        LxtCheckErrnoZeroSuccess(pipe(Pipes));\r\n        LxtCheckErrnoZeroSuccess(fstat(Pipes[0], &Stat));\r\n        LxtCheckGreater(Stat.st_ino, 0, \"%lu\");\r\n        LxtCheckEqual(Stat.st_uid, 1000, \"%d\");\r\n        LxtCheckEqual(Stat.st_gid, 1001, \"%d\");\r\n        LxtCheckEqual(Stat.st_mode, S_IFIFO | 0600, \"%d\");\r\n        LxtCheckEqual(Stat.st_blksize, 4096, \"%ld\");\r\n        LxtCheckEqual(Stat.st_blocks, 0, \"%ld\");\r\n        LxtCheckEqual(Stat.st_size, 0, \"%ld\");\r\n        LxtCheckEqual(Stat.st_nlink, 1, \"%u\");\r\n        LxtCheckResult(PipeCheckFdPath(Pipes[0]));\r\n        LxtCheckResult(PipeCheckFdPath(Pipes[1]));\r\n        LxtCheckErrnoZeroSuccess(close(Pipes[0]));\r\n        LxtCheckErrnoZeroSuccess(close(Pipes[1]));\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    if (Pipes[0] >= 0)\r\n    {\r\n        close(Pipes[0]);\r\n    }\r\n\r\n    if (Pipes[1] >= 0)\r\n    {\r\n        close(Pipes[1]);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeTest(bool Polling, bool UsePipe2)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    unsigned int* Buffer;\r\n    bool Child;\r\n    pid_t Pid;\r\n    int Pipes[2];\r\n    int Result;\r\n    int ReturnStatus;\r\n\r\n    Child = false;\r\n    Pid = -1;\r\n    Buffer = mmap(NULL, PIPE_BUFFER_LENGTH * sizeof(unsigned int), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\r\n\r\n    if (Buffer == MAP_FAILED)\r\n    {\r\n        Result = -1;\r\n        LxtLogError(\"Could not map buffer! %d\", errno);\r\n        goto PipeTestEnd;\r\n    }\r\n\r\n    if (UsePipe2 == false)\r\n    {\r\n        Result = pipe(Pipes);\r\n        if (Result == -1)\r\n        {\r\n            LxtLogError(\"Could not create pipes with pipe! %d\", errno);\r\n            goto PipeTestEnd;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        Result = LxtPipe2(Pipes, 0);\r\n        if (Result == -1)\r\n        {\r\n            LxtLogError(\"Could not create pipes with pipe2! %d\", errno);\r\n            goto PipeTestEnd;\r\n        }\r\n    }\r\n\r\n    if (Polling != false)\r\n    {\r\n        Result = fcntl(Pipes[0], F_SETFL, O_NONBLOCK);\r\n        if (Result == -1)\r\n        {\r\n            LxtLogError(\"Could not set pipe to non-blocking! %d\", errno);\r\n            goto PipeTestEnd;\r\n        }\r\n\r\n        Result = fcntl(Pipes[1], F_SETFL, O_NONBLOCK);\r\n        if (Result == -1)\r\n        {\r\n            LxtLogError(\"Could not set pipe to non-blocking! %d\", errno);\r\n            goto PipeTestEnd;\r\n        }\r\n    }\r\n\r\n    Pid = fork();\r\n    if (Pid == -1)\r\n    {\r\n        LxtLogError(\"Could not fork! %d\", errno);\r\n        goto PipeTestEnd;\r\n    }\r\n\r\n    if (Pid > 0)\r\n    {\r\n        close(Pipes[0]);\r\n        Result = PipeWriter(Buffer, PIPE_BUFFER_LENGTH, Pipes[1], Polling);\r\n        if (Result == -1)\r\n        {\r\n            LxtLogError(\"PipeWriter failed! %d\", errno);\r\n            goto PipeTestEnd;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        Child = true;\r\n        close(Pipes[1]);\r\n        Result = PipeReader(Buffer, PIPE_BUFFER_LENGTH, Pipes[0], Polling);\r\n        if (Result == -1)\r\n        {\r\n            LxtLogError(\"PipeReader failed! %d\", errno);\r\n            goto PipeTestEnd;\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nPipeTestEnd:\r\n    if (Child != false)\r\n    {\r\n        _exit(0);\r\n    }\r\n    else if (Pid > 0)\r\n    {\r\n        Result = waitpid(Pid, &ReturnStatus, 0);\r\n        if (Result == -1)\r\n        {\r\n            LxtLogError(\"PipeTest reader child failed: %d\", errno);\r\n        }\r\n        else\r\n        {\r\n            LxtLogInfo(\"PipeTest read child has exited.\");\r\n        }\r\n    }\r\n\r\n    if (Buffer != NULL)\r\n    {\r\n        munmap(Buffer, PIPE_BUFFER_LENGTH * sizeof(unsigned int));\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeVariation0(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    return PipeTest(false, false);\r\n}\r\n\r\nint PipeWriter(unsigned int* Buffer, size_t BufferLength, int Pipe, bool Polling)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char* Current;\r\n    size_t Index;\r\n    size_t Loop;\r\n    int Result;\r\n    struct pollfd PollFd;\r\n    size_t Size;\r\n    size_t SizeRemaining;\r\n    unsigned int Value;\r\n    size_t WriteSize;\r\n    ssize_t Written;\r\n\r\n    PollFd.fd = Pipe;\r\n    PollFd.events = POLLOUT;\r\n    Value = 0;\r\n    Size = BufferLength * sizeof(unsigned int);\r\n\r\n    for (Loop = 0; Loop < PIPE_LOOPS; Loop += 1)\r\n    {\r\n        LxtLogInfo(\"Loop #%u...\", Loop);\r\n        for (Index = 0; Index < BufferLength; Index += 1)\r\n        {\r\n            Buffer[Index] = Value;\r\n            Value += 1;\r\n        }\r\n\r\n        if (Polling != false)\r\n        {\r\n            SizeRemaining = Size;\r\n            Current = (char*)Buffer;\r\n            while (SizeRemaining > 0)\r\n            {\r\n                Result = poll(&PollFd, 1, -1);\r\n                if (Result == -1)\r\n                {\r\n                    Result = errno;\r\n                    LxtLogError(\"Failed to poll for write! %d\", Result);\r\n                    goto WriterEnd;\r\n                }\r\n\r\n                if ((PollFd.revents & POLLOUT) == 0)\r\n                {\r\n                    LxtLogError(\"Error condition on write poll!\");\r\n                    Result = EINVAL;\r\n                    goto WriterEnd;\r\n                }\r\n\r\n                while (SizeRemaining > 0)\r\n                {\r\n                    WriteSize = min(SizeRemaining, _32KB);\r\n                    Written = write(Pipe, Current, WriteSize);\r\n                    if (Written == -1)\r\n                    {\r\n                        if (errno == EAGAIN)\r\n                        {\r\n                            LxtLogInfo(\"Write would have blocked\");\r\n                            break;\r\n                        }\r\n\r\n                        Result = errno;\r\n                        LxtLogError(\"Failed to write! %d\", Result);\r\n                        goto WriterEnd;\r\n                    }\r\n\r\n                    SizeRemaining -= Written;\r\n                    Current += Written;\r\n                }\r\n            }\r\n        }\r\n        else\r\n        {\r\n            Written = write(Pipe, Buffer, Size);\r\n            if (Written != (ssize_t)Size)\r\n            {\r\n                if (Written >= 0)\r\n                {\r\n                    LxtLogError(\"Wrote fewer bytes (%d) than expected (%d)!\", Written, Size);\r\n                    Result = EINVAL;\r\n                }\r\n                else\r\n                {\r\n                    Result = errno;\r\n                    LxtLogError(\"Failed to write! %d\", Result);\r\n                }\r\n\r\n                goto WriterEnd;\r\n            }\r\n        }\r\n    }\r\n\r\n    Result = 0;\r\n\r\nWriterEnd:\r\n    if (Result != 0)\r\n    {\r\n        errno = Result;\r\n        Result = -1;\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeReader(unsigned int* Buffer, size_t BufferLength, int Pipe, bool Polling)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int BytesAvailable;\r\n    char* Current;\r\n    size_t Index;\r\n    size_t Loop;\r\n    struct pollfd PollFd;\r\n    ssize_t Read;\r\n    int Result;\r\n    size_t Size;\r\n    size_t SizeRemaining;\r\n    unsigned int Value;\r\n\r\n    PollFd.fd = Pipe;\r\n    PollFd.events = POLLIN;\r\n    Value = 0;\r\n    Size = BufferLength * sizeof(unsigned int);\r\n    for (Loop = 0; Loop < PIPE_LOOPS; Loop += 1)\r\n    {\r\n        LxtLogInfo(\"Loop #%u...\", Loop);\r\n        Current = (char*)Buffer;\r\n        SizeRemaining = Size;\r\n        while (SizeRemaining > 0)\r\n        {\r\n            if (Polling != false)\r\n            {\r\n                Result = poll(&PollFd, 1, -1);\r\n                if (Result == -1)\r\n                {\r\n                    Result = errno;\r\n                    LxtLogError(\"Failed to poll for read! %d\", Result);\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                if ((PollFd.revents & POLLIN) == 0)\r\n                {\r\n                    LxtLogError(\"Error condition on read poll: 0x%X!\", PollFd.revents);\r\n                    Result = EINVAL;\r\n                    goto ErrorExit;\r\n                }\r\n            }\r\n\r\n            //\r\n            // Query the number of bytes available via the FIONREAD ioctl.\r\n            //\r\n\r\n            LxtCheckErrno(ioctl(Pipe, FIONREAD, &BytesAvailable));\r\n            Read = read(Pipe, Current, _4KB);\r\n            if (Read == -1)\r\n            {\r\n                Result = errno;\r\n                LxtLogError(\"Failed to read! %d\", Result);\r\n                goto ErrorExit;\r\n            }\r\n\r\n            if ((Read == 0) || (Read % sizeof(unsigned int) != 0))\r\n            {\r\n                LxtLogError(\"Read an invalid number of bytes (%d)!\", Read);\r\n                Result = EINVAL;\r\n                goto ErrorExit;\r\n            }\r\n\r\n            Current += Read;\r\n            SizeRemaining -= Read;\r\n        }\r\n\r\n        //\r\n        // Validate the buffer.\r\n        //\r\n\r\n        for (Index = 0; Index < BufferLength; Index += 1)\r\n        {\r\n            if (Buffer[Index] != Value)\r\n            {\r\n                LxtLogError(\"PipeReader buffer invalid - contains %u instead of expected %u!\", Buffer[Index], Value);\r\n                Result = EINVAL;\r\n                goto ErrorExit;\r\n            }\r\n\r\n            Value += 1;\r\n        }\r\n    }\r\n\r\n    LxtLogInfo(\"Reads finished\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Result != 0)\r\n    {\r\n        errno = Result;\r\n        Result = -1;\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeVariation1(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    return PipeTest(true, true);\r\n}\r\n\r\nint PipeReaderHangup(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the reader hangup epoll semantics.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesRead;\r\n    ssize_t BytesWritten;\r\n    ssize_t ExpectedResult;\r\n    char* Message = \"This is a test string for piping\";\r\n    int Pipes[2] = {-1, -1};\r\n    struct pollfd PollFd;\r\n    char ReadBuffer[100];\r\n    int Result;\r\n\r\n    //\r\n    // Create a pipe.\r\n    //\r\n\r\n    LxtCheckErrno(pipe(Pipes));\r\n\r\n    //\r\n    // Set the pipe FD's to non-blocking.\r\n    //\r\n\r\n    LxtLogInfo(\"Creating a pipe...\");\r\n    LxtCheckErrno(fcntl(Pipes[0], F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrno(fcntl(Pipes[1], F_SETFL, O_NONBLOCK));\r\n\r\n    //\r\n    // Write some data to the pipe.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing some data to the pipe.\");\r\n    ExpectedResult = strlen(Message);\r\n    LxtCheckErrno((BytesWritten = write(Pipes[1], Message, ExpectedResult)));\r\n    LxtCheckEqual(ExpectedResult, BytesWritten, \"%d\");\r\n\r\n    //\r\n    // Validate that the only epoll flag set on the reader end is EPOLLIN.\r\n    //\r\n\r\n    LxtLogInfo(\r\n        \"Validating that the EPOLLIN is set on the reader end of the \"\r\n        \"pipe.\");\r\n\r\n    PollFd.revents = 0;\r\n    PollFd.fd = Pipes[0];\r\n    PollFd.events = POLLIN | POLLOUT;\r\n    LxtCheckErrno(poll(&PollFd, 1, -1));\r\n    if (PollFd.revents != POLLIN)\r\n    {\r\n        LxtLogError(\r\n            \"Error condition on reader poll: 0x%X, Expected: \"\r\n            \"0x%X! (POLLIN)\",\r\n            PollFd.revents,\r\n            POLLIN);\r\n\r\n        Result = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read data from the other end of the pipe.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading the data from the reader end.\") ExpectedResult = strlen(Message);\r\n    LxtCheckErrno((BytesRead = read(Pipes[0], ReadBuffer, sizeof(ReadBuffer))));\r\n    LxtCheckEqual(ExpectedResult, BytesRead, \"%d\");\r\n\r\n    //\r\n    // Validate that the EPOLLIN is not set on the reader end now that the data\r\n    // has been read. This call will block, so specify a timeout value.\r\n    //\r\n\r\n    LxtLogInfo(\r\n        \"Validating that the EPOLLIN is *not* set on the reader end of \"\r\n        \"the pipe.\");\r\n\r\n    PollFd.revents = 0;\r\n    PollFd.fd = Pipes[0];\r\n    PollFd.events = POLLIN | POLLOUT;\r\n    LxtCheckErrno(poll(&PollFd, 1, 1));\r\n    if (PollFd.revents != 0)\r\n    {\r\n        LxtLogError(\"Error condition on reader poll: 0x%X, Expected: 0x%X!\", PollFd.revents, 0);\r\n\r\n        Result = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Hangup the reader end.\r\n    //\r\n\r\n    LxtLogInfo(\"Hanging the reader\");\r\n    close(Pipes[0]);\r\n    Pipes[0] = -1;\r\n\r\n    //\r\n    // Validate that when the reader hangs up, both EPOLLOUT and EPOLLERR are\r\n    // set.\r\n    //\r\n\r\n    LxtLogInfo(\r\n        \"Validating that the correct EPOLL flags are set on the writer \"\r\n        \"end of the pipe.\");\r\n\r\n    PollFd.revents = 0;\r\n    PollFd.fd = Pipes[1];\r\n    PollFd.events = POLLIN | POLLOUT;\r\n    LxtCheckErrno(poll(&PollFd, 1, -1));\r\n    if (PollFd.revents != (POLLOUT | POLLERR))\r\n    {\r\n        LxtLogError(\r\n            \"Error condition on writer poll: 0x%X, Expected: \"\r\n            \"0x%X! (POLLHUP)\",\r\n            PollFd.revents,\r\n            (POLLOUT | POLLERR));\r\n\r\n        Result = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Pipes[0] != -1)\r\n    {\r\n        close(Pipes[0]);\r\n    }\r\n\r\n    if (Pipes[1] != -1)\r\n    {\r\n        close(Pipes[1]);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeWriterHangup(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the writer hangup epoll semantics.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ssize_t BytesRead;\r\n    ssize_t BytesWritten;\r\n    ssize_t ExpectedResult;\r\n    char* Message = \"This is a test string for piping\";\r\n    int Pipes[2] = {-1, -1};\r\n    struct pollfd PollFd;\r\n    char ReadBuffer[100] = {0};\r\n    int Result;\r\n\r\n    //\r\n    // Create a pipe.\r\n    //\r\n\r\n    LxtCheckErrno(pipe(Pipes));\r\n\r\n    //\r\n    // Set the pipe FD's to non-blocking.\r\n    //\r\n\r\n    LxtLogInfo(\"Creating a pipe...\");\r\n    LxtCheckErrno(fcntl(Pipes[0], F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrno(fcntl(Pipes[1], F_SETFL, O_NONBLOCK));\r\n\r\n    //\r\n    // Write some data to the pipe.\r\n    //\r\n\r\n    LxtLogInfo(\"Writing some data to the pipe.\");\r\n    ExpectedResult = strlen(Message);\r\n    LxtCheckErrno((BytesWritten = write(Pipes[1], Message, ExpectedResult)));\r\n    LxtCheckEqual(ExpectedResult, BytesWritten, \"%d\");\r\n\r\n    //\r\n    // Hangup the writer end.\r\n    //\r\n\r\n    LxtLogInfo(\"Hanging the writer\");\r\n    close(Pipes[1]);\r\n    Pipes[1] = -1;\r\n\r\n    //\r\n    // Validate that both EPOLLIN and EPOLLHUP is set on the reader side.\r\n    //\r\n\r\n    LxtLogInfo(\r\n        \"Validating that the correct EPOLL flags are set on the reader \"\r\n        \"end of the pipe.\");\r\n\r\n    PollFd.revents = 0;\r\n    PollFd.fd = Pipes[0];\r\n    PollFd.events = POLLIN | POLLOUT;\r\n    LxtCheckErrno(poll(&PollFd, 1, -1));\r\n    if (PollFd.revents != (POLLHUP | POLLIN))\r\n    {\r\n        LxtLogError(\r\n            \"Error condition on reader poll: 0x%X. Expected: \"\r\n            \"0x%X (POLLHUP | POLLIN)\",\r\n            PollFd.revents,\r\n            (POLLHUP | POLLIN));\r\n\r\n        Result = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Drain the read side of the pipe.\r\n    //\r\n\r\n    LxtLogInfo(\"Reading all the data from the pipe.\") ExpectedResult = strlen(Message);\r\n    LxtCheckErrno((BytesRead = read(Pipes[0], ReadBuffer, sizeof(ReadBuffer))));\r\n    LxtCheckEqual(ExpectedResult, BytesRead, \"%d\");\r\n\r\n    LxtLogInfo(\r\n        \"Validating that only EPOLLHUP is set on the reader side now \"\r\n        \"that the pipe has been drained.\");\r\n\r\n    PollFd.revents = 0;\r\n    PollFd.fd = Pipes[0];\r\n    PollFd.events = POLLIN | POLLOUT;\r\n    LxtCheckErrno(poll(&PollFd, 1, -1));\r\n    if (PollFd.revents != POLLHUP)\r\n    {\r\n        LxtLogError(\r\n            \"Error condition on reader poll: 0x%X. Expected: \"\r\n            \"0x%X (POLLHUP)\",\r\n            PollFd.revents,\r\n            POLLHUP);\r\n\r\n        Result = EINVAL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Pipes[0] != -1)\r\n    {\r\n        close(Pipes[0]);\r\n    }\r\n\r\n    if (Pipes[1] != -1)\r\n    {\r\n        close(Pipes[1]);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeVariationIoctl(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the pipe ioctls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Pipes[2] = {-1, -1};\r\n    char Buffer[256] = {0};\r\n    int Result;\r\n\r\n    //\r\n    // Create a pipe and check the ioctls\r\n    //\r\n\r\n    LxtCheckErrno(pipe(Pipes));\r\n    LxtCheckErrnoFailure(ioctl(Pipes[0], TCGETS, Buffer), ENOTTY);\r\n\r\nErrorExit:\r\n    if (Pipes[0] != -1)\r\n    {\r\n        close(Pipes[0]);\r\n    }\r\n\r\n    if (Pipes[1] != -1)\r\n    {\r\n        close(Pipes[1]);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeFileLocking(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the pipe support for file locking.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Pipes[2] = {-1, -1};\r\n    int Result;\r\n\r\n    //\r\n    // Create a pipe and check the flock and lockf (fnctl+F_*LK).\r\n    //\r\n\r\n    LxtCheckErrno(pipe(Pipes));\r\n    LxtCheckErrno(flock(Pipes[0], LOCK_SH));\r\n    LxtCheckErrno(flock(Pipes[1], LOCK_SH));\r\n    LxtCheckErrno(flock(Pipes[0], LOCK_UN));\r\n    LxtCheckErrno(flock(Pipes[1], LOCK_UN));\r\n    LxtCheckErrno(flock(Pipes[0], LOCK_EX));\r\n    LxtCheckErrno(flock(Pipes[0], LOCK_UN));\r\n    LxtCheckErrno(flock(Pipes[1], LOCK_EX));\r\n    LxtCheckErrno(flock(Pipes[1], LOCK_UN));\r\n    LxtCheckErrno(lockf(Pipes[0], F_TEST, 0));\r\n    LxtCheckErrno(lockf(Pipes[1], F_TEST, 0));\r\n    LxtCheckErrno(lockf(Pipes[1], F_LOCK, 0));\r\n    LxtCheckErrno(lockf(Pipes[1], F_ULOCK, 0));\r\n\r\nErrorExit:\r\n    if (Pipes[0] != -1)\r\n    {\r\n        close(Pipes[0]);\r\n    }\r\n\r\n    if (Pipes[1] != -1)\r\n    {\r\n        close(Pipes[1]);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeZeroByteRead(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests zero byte read on pipes.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer;\r\n    int Pipes[2];\r\n    int Result;\r\n\r\n    Pipes[0] = -1;\r\n    Pipes[1] = -1;\r\n    LxtCheckErrnoZeroSuccess(pipe(Pipes));\r\n    LxtCheckErrno(read(Pipes[0], &Buffer, 0));\r\n\r\nErrorExit:\r\n    if (Pipes[0] >= 0)\r\n    {\r\n        close(Pipes[0]);\r\n    }\r\n\r\n    if (Pipes[1] >= 0)\r\n    {\r\n        close(Pipes[1]);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeSecurityHelper(int Pipe, int Uid, int Gid)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests security attributes on pipes.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    //\r\n    // Check the original values.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fstat(Pipe, &Stat));\r\n    LxtCheckEqual(Stat.st_uid, Uid, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, Gid, \"%d\");\r\n    LxtCheckEqual(Stat.st_mode, S_IFIFO | 0600, \"%d\");\r\n\r\n    //\r\n    // Check the values after chmod.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fchmod(Pipe, 0777));\r\n    LxtCheckErrnoZeroSuccess(fstat(Pipe, &Stat));\r\n    LxtCheckEqual(Stat.st_uid, Uid, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, Gid, \"%d\");\r\n    LxtCheckEqual(Stat.st_mode, S_IFIFO | 0777, \"%d\");\r\n\r\n    //\r\n    // Check the values after chown to the current user\\group.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fchown(Pipe, Uid, Gid));\r\n    LxtCheckErrnoZeroSuccess(fstat(Pipe, &Stat));\r\n    LxtCheckEqual(Stat.st_uid, Uid, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, Gid, \"%d\");\r\n    LxtCheckEqual(Stat.st_mode, S_IFIFO | 0777, \"%d\");\r\n\r\n    //\r\n    // Update the user\\group and check that it changes as root. As non-root\r\n    // the user doesn't have permissions to make the updates.\r\n    //\r\n\r\n    if (Uid == 0)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(fchown(Pipe, Uid + 1, Gid));\r\n        LxtCheckErrnoZeroSuccess(fstat(Pipe, &Stat));\r\n        LxtCheckEqual(Stat.st_uid, Uid + 1, \"%d\");\r\n        LxtCheckEqual(Stat.st_gid, Gid, \"%d\");\r\n        LxtCheckEqual(Stat.st_mode, S_IFIFO | 0777, \"%d\");\r\n\r\n        LxtCheckErrnoZeroSuccess(fchown(Pipe, Uid, Gid + 1));\r\n        LxtCheckErrnoZeroSuccess(fstat(Pipe, &Stat));\r\n        LxtCheckEqual(Stat.st_uid, Uid, \"%d\");\r\n        LxtCheckEqual(Stat.st_gid, Gid + 1, \"%d\");\r\n        LxtCheckEqual(Stat.st_mode, S_IFIFO | 0777, \"%d\");\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrnoFailure(fchown(Pipe, Uid + 1, Gid), EPERM);\r\n        LxtCheckErrnoFailure(fchown(Pipe, Uid, Gid + 1), EPERM);\r\n    }\r\n\r\n    //\r\n    // Check user\\group updates with -1.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(fchown(Pipe, Uid, -1));\r\n    LxtCheckErrnoZeroSuccess(fchown(Pipe, -1, Gid));\r\n    LxtCheckErrnoZeroSuccess(fchown(Pipe, -1, -1));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint PipeSecurity(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests security attributes on pipes.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Pipes[2];\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    Pipes[0] = -1;\r\n    Pipes[1] = -1;\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Check the security as root.\r\n        //\r\n\r\n        LxtCheckErrnoZeroSuccess(pipe(Pipes));\r\n        LxtCheckResult(PipeSecurityHelper(Pipes[0], 0, 0));\r\n        LxtClose(Pipes[0]);\r\n        Pipes[0] = -1;\r\n        LxtClose(Pipes[1]);\r\n        Pipes[1] = -1;\r\n\r\n        //\r\n        // Check the security as a different user\\group which drops\r\n        // capabilities.\r\n        //\r\n\r\n        LxtCheckErrno(setfsuid(1000));\r\n        LxtCheckErrno(setfsgid(1001));\r\n        LxtCheckErrnoZeroSuccess(pipe(Pipes));\r\n        LxtCheckResult(PipeSecurityHelper(Pipes[0], 1000, 1001));\r\n\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\nErrorExit:\r\n    if (Pipes[0] >= 0)\r\n    {\r\n        close(Pipes[0]);\r\n    }\r\n\r\n    if (Pipes[1] >= 0)\r\n    {\r\n        close(Pipes[1]);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PipeFcntlRoundUpToPower2(int Value)\r\n{\r\n\r\n    if (Value < 0)\r\n    {\r\n        Value = 0;\r\n    }\r\n    else\r\n    {\r\n        --Value;\r\n        Value |= Value >> 1;\r\n        Value |= Value >> 2;\r\n        Value |= Value >> 4;\r\n        Value |= Value >> 8;\r\n        Value |= Value >> 16;\r\n        Value += 1;\r\n    }\r\n\r\n    return Value;\r\n}\r\n\r\nint PipeFcntl(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the pipe fcntl commands.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[_4KB + 1];\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int EventFd;\r\n    int Index;\r\n    int Pipes[2];\r\n    int Result;\r\n    int Size;\r\n    int SizeSet;\r\n    int SizesToSet[] = {\r\n        0,\r\n        1,\r\n        _4KB - 1,\r\n        _4KB,\r\n        _64KB - 1,\r\n        _64KB,\r\n        _64KB + 1,\r\n        _64KB * 2,\r\n        _64KB * 2 + 1,\r\n        _64KB * 4 - 1,\r\n        _64KB * 4,\r\n        _64KB * 4 + 1,\r\n        PIPE_DEFAULT_MAX_SIZE - 1,\r\n        PIPE_DEFAULT_MAX_SIZE,\r\n        PIPE_DEFAULT_MAX_SIZE + 1,\r\n        _64KB};\r\n\r\n    ChildPid = -1;\r\n    EventFd = -1;\r\n    Pipes[0] = -1;\r\n    Pipes[1] = -1;\r\n    LxtCheckErrnoZeroSuccess(pipe(Pipes));\r\n    LxtCheckErrno(EventFd = eventfd(0, 0));\r\n\r\n    //\r\n    // Check the initial values for F_GETPIPE_SZ.\r\n    //\r\n\r\n    LxtCheckErrno(Size = fcntl(Pipes[0], F_GETPIPE_SZ));\r\n    LxtCheckEqual(Size, _64KB, \"%d\");\r\n    LxtCheckErrno(Size = fcntl(Pipes[1], F_GETPIPE_SZ));\r\n    LxtCheckEqual(Size, _64KB, \"%d\");\r\n\r\n    //\r\n    // Update the size and check for the expected values.\r\n    //\r\n    // From the man pages, \"In the current implementation, the allocation is the\r\n    // next higher power-of-two page-size multiple of the requested size\"\r\n    //\r\n\r\n    srand(time(NULL));\r\n    for (Index = 0; Index < LXT_COUNT_OF(SizesToSet); ++Index)\r\n    {\r\n\r\n        LxtCheckErrno(SizeSet = fcntl(Pipes[rand() % 2], F_SETPIPE_SZ, SizesToSet[Index]));\r\n        LxtLogInfo(\"Setting %d -> %d\", SizesToSet[Index], SizeSet);\r\n\r\n        Size = SizesToSet[Index];\r\n        if (Size < PAGE_SIZE)\r\n        {\r\n            Size = PAGE_SIZE;\r\n        }\r\n        else\r\n        {\r\n            Size = PipeFcntlRoundUpToPower2(Size);\r\n        }\r\n\r\n        LxtCheckEqual(SizeSet, Size, \"%d\");\r\n        LxtCheckErrno(Size = fcntl(Pipes[0], F_GETPIPE_SZ));\r\n        LxtCheckEqual(Size, SizeSet, \"%d\");\r\n        LxtCheckErrno(Size = fcntl(Pipes[1], F_GETPIPE_SZ));\r\n        LxtCheckEqual(Size, SizeSet, \"%d\");\r\n    }\r\n\r\n    //\r\n    // Try to shrink the data below the maximum amount.\r\n    //\r\n\r\n    LxtCheckErrno(write(Pipes[1], Buffer, sizeof(Buffer)));\r\n    LxtCheckErrnoFailure(fcntl(Pipes[1], F_SETPIPE_SZ, sizeof(Buffer) - 1), EBUSY);\r\n\r\n    //\r\n    // Try to increase the buffer from an unprivileged thread.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Drop the CAP_SYS_RESOURCE capability.\r\n        //\r\n\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapGet(&CapHeader, CapData)) LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted &= ~CAP_TO_MASK(CAP_SYS_RESOURCE);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(fcntl(Pipes[1], F_SETPIPE_SZ, PIPE_DEFAULT_MAX_SIZE + 1), EPERM);\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n\r\n    //\r\n    // Check the maximum value permissions check.\r\n    //\r\n\r\n    //\r\n    // Negative variations.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(fcntl(Pipes[1], F_SETPIPE_SZ, -1), EINVAL);\r\n    LxtCheckErrnoFailure(Size = fcntl(EventFd, F_GETPIPE_SZ), EBADF);\r\n    LxtCheckErrnoFailure(Size = fcntl(EventFd, F_SETPIPE_SZ, _64KB), EBADF);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    LxtClose(EventFd);\r\n    LxtClose(Pipes[0]);\r\n    LxtClose(Pipes[1]);\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/poll.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Template.c\r\n\r\nAbstract:\r\n\r\n    This file is the poll.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <poll.h>\r\n#include <signal.h>\r\n#include <sys/stat.h>\r\n#include <sys/time.h>\r\n#include <sys/types.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n\r\n#define LXT_NAME \"Poll\"\r\n\r\nint PollVariation0(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {{\"Poll0\", PollVariation0}};\r\n\r\nint PollTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint CountFilledDescriptors(struct pollfd* PollDescriptors, int Count)\r\n{\r\n    int NumberFilled;\r\n    int Index;\r\n\r\n    NumberFilled = 0;\r\n\r\n    for (Index = 0; Index < Count; Index += 1)\r\n    {\r\n\r\n        if (PollDescriptors[Index].revents != 0)\r\n        {\r\n            NumberFilled += 1;\r\n        }\r\n    }\r\n\r\n    return NumberFilled;\r\n}\r\n\r\nint PollVariation0(PLXT_ARGS Args)\r\n{\r\n    int Result;\r\n    int FileDescriptor1;\r\n    int FileDescriptor2;\r\n    struct pollfd PollDescriptors[3];\r\n    int NumberFilled;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileDescriptor1 = -1;\r\n    FileDescriptor2 = -1;\r\n\r\n    //\r\n    // Open a file that will be used for select.\r\n    //\r\n\r\n    FileDescriptor1 = open(\"/data/test/poll_test.bin\", O_RDWR | O_CREAT, S_IRWXU);\r\n\r\n    if (FileDescriptor1 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    FileDescriptor2 = open(\"/data/test/poll_test.bin\", O_RDWR | O_CREAT, S_IRWXU);\r\n\r\n    if (FileDescriptor2 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Could not create test file! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Fill the poll descriptors.\r\n    //\r\n\r\n    PollDescriptors[0].fd = FileDescriptor1;\r\n    PollDescriptors[0].events = POLLIN;\r\n    PollDescriptors[0].revents = -1;\r\n\r\n    PollDescriptors[1].fd = FileDescriptor2;\r\n    PollDescriptors[1].events = POLLRDHUP;\r\n    PollDescriptors[1].revents = -1;\r\n\r\n    PollDescriptors[2].fd = 100;\r\n    PollDescriptors[2].events = POLLOUT;\r\n    PollDescriptors[2].revents = -1;\r\n\r\n    Result = poll(PollDescriptors, 3, 60001);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Waiting on poll failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (Result != 2)\r\n    {\r\n        LxtLogError(\"Waiting on poll returned more than 2 events! %d\", Result);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    NumberFilled = CountFilledDescriptors(PollDescriptors, 3);\r\n\r\n    if (NumberFilled != Result)\r\n    {\r\n        LxtLogError(\"Poll returned %d events but filled %d!\", Result, NumberFilled);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    if (PollDescriptors[2].revents != POLLNVAL)\r\n    {\r\n        LxtLogError(\"Poll descriptor 3 was filled incorrectly!\");\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    NumberFilled = CountFilledDescriptors(PollDescriptors, 3);\r\n\r\n    if (NumberFilled != Result)\r\n    {\r\n        LxtLogError(\"Poll returned %d events but filled %d!\", Result, NumberFilled);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Poll with zero descriptors and it should time out.\r\n    //\r\n\r\n    LxtLogInfo(\"Wait for 1s for poll to timeout...\");\r\n\r\n    Result = poll(PollDescriptors, 0, 1000);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Waiting on poll failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (Result != 0)\r\n    {\r\n        LxtLogError(\"Waiting on poll returned data but should have timed out! %d\", Result);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Poll for read/write but get notified of error.\r\n    //\r\n\r\n    read(FileDescriptor2, NULL, 111);\r\n\r\n    PollDescriptors[1].fd = FileDescriptor2;\r\n    PollDescriptors[1].events = POLLIN | POLLOUT;\r\n    PollDescriptors[1].revents = -1;\r\n\r\n    PollDescriptors[2].fd = -FileDescriptor2;\r\n    PollDescriptors[2].events = POLLIN | POLLOUT;\r\n    PollDescriptors[2].revents = -1;\r\n\r\n    Result = poll(PollDescriptors, 3, -2);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Waiting on poll failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (Result != 2)\r\n    {\r\n        LxtLogError(\"Waiting on poll returned more than 2 events! %d\", Result);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    NumberFilled = CountFilledDescriptors(PollDescriptors, 3);\r\n\r\n    if (NumberFilled != Result)\r\n    {\r\n        LxtLogError(\"Poll returned %d events but filled %d!\", Result, NumberFilled);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Poll for nothing but still got notified of error.\r\n    //\r\n\r\n    PollDescriptors[1].fd = FileDescriptor2;\r\n    PollDescriptors[1].events = 0;\r\n    PollDescriptors[1].revents = -1;\r\n\r\n    PollDescriptors[2].fd = -FileDescriptor2;\r\n    PollDescriptors[2].events = POLLIN | POLLOUT;\r\n    PollDescriptors[2].revents = -1;\r\n\r\n    Result = poll(PollDescriptors, 3, -2);\r\n\r\n    if (Result == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"Waiting on poll failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (Result != 1)\r\n    {\r\n        LxtLogError(\"Waiting on poll returned more than 1 events! %d\", Result);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    NumberFilled = CountFilledDescriptors(PollDescriptors, 3);\r\n\r\n    if (NumberFilled != Result)\r\n    {\r\n        LxtLogError(\"Poll returned %d events but filled %d!\", Result, NumberFilled);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    NumberFilled = CountFilledDescriptors(PollDescriptors, 1);\r\n\r\n    if (NumberFilled != Result)\r\n    {\r\n        LxtLogError(\"Poll returned %d events but filled %d!\", Result, NumberFilled);\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\ncleanup:\r\n\r\n    if (FileDescriptor1 != -1)\r\n    {\r\n        close(FileDescriptor1);\r\n    }\r\n\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/random.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    random.c\r\n\r\nAbstract:\r\n\r\n    This file is a test for the getrandom system call and the /dev/random and\r\n    /dev/urandom devices.\r\n\r\n--*/\r\n\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <fcntl.h>\r\n#include <sys/socket.h>\r\n#include <sys/un.h>\r\n#include <netinet/in.h>\r\n#include <netdb.h>\r\n#include <time.h>\r\n#include <linux/random.h>\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n\r\n#define DEV_RANDOM \"/dev/random\"\r\n#define DEV_RANDOM_MAX_BYTES (512)\r\n\r\n#define DEV_URANDOM \"/dev/urandom\"\r\n#define DEV_URANDOM_MAX_BYTES (0x1FFFFFF)\r\n\r\n#define PROC_SYS_KERNEL_RANDOM \"/proc/sys/kernel/random\"\r\n#define PROC_SYS_KERNEL_RANDOM_BOOTID PROC_SYS_KERNEL_RANDOM \"/boot_id\"\r\n#define PROC_SYS_KERNEL_RANDOM_ENTROPY_AVAIL PROC_SYS_KERNEL_RANDOM \"/entropy_avail\"\r\n#define PROC_SYS_KERNEL_RANDOM_POOLSIZE PROC_SYS_KERNEL_RANDOM \"/poolsize\"\r\n#define PROC_SYS_KERNEL_RANDOM_UUID PROC_SYS_KERNEL_RANDOM \"/uuid\"\r\n#define PROC_SYS_KERNEL_RANDOM_BYTES 37\r\n\r\n#define LXT_NAME \"random\"\r\n\r\nint GetrandomSyscall(PLXT_ARGS Args);\r\n\r\nint DevRandomDevice(PLXT_ARGS Args);\r\n\r\nint DevUrandomDevice(PLXT_ARGS Args);\r\n\r\nint ProcfsRandom(PLXT_ARGS Args);\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"getrandom syscall\", GetrandomSyscall},\r\n    {\"/dev/random device\", DevRandomDevice},\r\n    {\"/dev/urandom device\", DevUrandomDevice},\r\n    {\"/proc/sys/kernel/random\", ProcfsRandom}};\r\n\r\nint RandomTestEntry(int Argc, char* Argv[])\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return 0;\r\n}\r\n\r\nint GetrandomSyscall(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    char* Buffer;\r\n    size_t BufferSize;\r\n    size_t Result;\r\n\r\n    BufferSize = DEV_URANDOM_MAX_BYTES + 1;\r\n    Buffer = malloc(BufferSize);\r\n    if (Buffer == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"malloc failed\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Valid parameter variations.\r\n    //\r\n\r\n    LxtCheckResult(LxtGetrandom(NULL, 0, 0));\r\n    LxtCheckEqual(Result, 0, \"%Iu\");\r\n    LxtCheckResult(LxtGetrandom(NULL, 0, GRND_RANDOM));\r\n    LxtCheckResult(LxtGetrandom(NULL, 0, GRND_NONBLOCK));\r\n    LxtCheckResult(LxtGetrandom(NULL, 0, (GRND_RANDOM | GRND_NONBLOCK)));\r\n    LxtCheckResult(LxtGetrandom(Buffer, BufferSize, 0));\r\n    if (Result > DEV_URANDOM_MAX_BYTES)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"BytesRead %Iu greater than expected %Iu\", Result, DEV_URANDOM_MAX_BYTES);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(LxtGetrandom(Buffer, BufferSize, GRND_RANDOM));\r\n    if (Result > DEV_RANDOM_MAX_BYTES)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"BytesRead %Iu greater than expected %Iu\", Result, DEV_RANDOM_MAX_BYTES);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtGetrandom(NULL, 0, ((GRND_RANDOM | GRND_NONBLOCK) + 1)), EINVAL);\r\n    LxtCheckErrnoFailure(LxtGetrandom(NULL, 0, -1), EINVAL);\r\n    LxtCheckErrnoFailure(LxtGetrandom(NULL, 1, 0), EFAULT);\r\n    LxtCheckErrnoFailure(LxtGetrandom(-1, 1, 0), EFAULT);\r\n\r\nErrorExit:\r\n    if (Buffer != NULL)\r\n    {\r\n        free(Buffer);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint DevRandomDevice(PLXT_ARGS Args)\r\n{\r\n\r\n    char Buffer[DEV_RANDOM_MAX_BYTES + 1];\r\n    int BytesRead;\r\n    int Fd;\r\n    int Result;\r\n\r\n    Fd = -1;\r\n\r\n    LxtCheckResult(Fd = open(DEV_RANDOM, O_RDONLY));\r\n    LxtCheckResult(BytesRead = read(Fd, Buffer, LXT_COUNT_OF(Buffer)));\r\n\r\n    if (BytesRead > DEV_RANDOM_MAX_BYTES)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"BytesRead %d greater than expected %d\", BytesRead, DEV_RANDOM_MAX_BYTES);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint DevUrandomDevice(PLXT_ARGS Args)\r\n{\r\n\r\n    char* Buffer;\r\n    size_t BufferSize;\r\n    int BytesRead;\r\n    int Fd;\r\n    int Result;\r\n\r\n    BufferSize = DEV_URANDOM_MAX_BYTES + 1;\r\n    Fd = -1;\r\n    Buffer = malloc(BufferSize);\r\n    if (Buffer == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"malloc failed\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(Fd = open(DEV_URANDOM, O_RDONLY));\r\n    LxtCheckResult(BytesRead = read(Fd, Buffer, BufferSize));\r\n    if (BytesRead > DEV_URANDOM_MAX_BYTES)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"BytesRead %d greater than expected %d\", BytesRead, DEV_URANDOM_MAX_BYTES);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Buffer != NULL)\r\n    {\r\n        free(Buffer);\r\n    }\r\n\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint ProcfsRandom(PLXT_ARGS Args)\r\n\r\n{\r\n    char Buffer[PROC_SYS_KERNEL_RANDOM_BYTES];\r\n    int BytesRead;\r\n    int Fd;\r\n    int Result;\r\n\r\n    //\r\n    // Test /proc/sys/kernel/random/uuid.\r\n    //\r\n\r\n    LxtCheckResult(Fd = open(PROC_SYS_KERNEL_RANDOM_UUID, O_RDONLY));\r\n    LxtCheckResult(BytesRead = read(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(PROC_SYS_KERNEL_RANDOM_BYTES, BytesRead, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Test /proc/sys/kernel/random/boot_id.\r\n    //\r\n\r\n    LxtCheckResult(Fd = open(PROC_SYS_KERNEL_RANDOM_BOOTID, O_RDONLY));\r\n    LxtCheckResult(BytesRead = read(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(PROC_SYS_KERNEL_RANDOM_BYTES, BytesRead, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Test /proc/sys/kernel/random/entropy_avail.\r\n    //\r\n\r\n    LxtCheckResult(Fd = open(PROC_SYS_KERNEL_RANDOM_ENTROPY_AVAIL, O_RDONLY));\r\n    LxtCheckResult(BytesRead = read(Fd, Buffer, (sizeof(Buffer) - 1)));\r\n    LxtCheckEqual(5, BytesRead, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    //\r\n    // Test /proc/sys/kernel/random/poolsize.\r\n    //\r\n\r\n    LxtCheckResult(Fd = open(PROC_SYS_KERNEL_RANDOM_POOLSIZE, O_RDONLY));\r\n    LxtCheckResult(BytesRead = read(Fd, Buffer, (sizeof(Buffer) - 1)));\r\n    LxtCheckEqual(5, BytesRead, \"%d\");\r\n    Buffer[BytesRead] = '\\0';\r\n    LxtCheckStringEqual(Buffer, \"4096\\n\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/resourcelimits.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    resourcelimit.c\r\n\r\nAbstract:\r\n\r\n    This file contains unit test for set and get resource limits.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/prctl.h>\r\n#include <sys/types.h>\r\n#include <sys/resource.h>\r\n#include <sys/stat.h>\r\n#include <sys/wait.h>\r\n#include <fcntl.h>\r\n#include <unistd.h>\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n\r\n#include <sys/capability.h>\r\n\r\n#else\r\n\r\n#include <sys/cdefs.h>\r\n#include <linux/capability.h>\r\n\r\n#define _LINUX_CAPABILITY_VERSION_3 0x20080522\r\n\r\n#ifndef O_PATH\r\n#define O_PATH 010000000\r\n#endif\r\n\r\n#endif\r\n\r\n#define LXT_NAME \"resourcelimits\"\r\n#define LXT_RESOURCE_LIMIT_TEST_FILE \"rlimit_testfile\"\r\n#define LXT_RESOURCE_LIMIT_UID 1024\r\n#define LXT_RESOURCE_LIMIT_GID 1024\r\n#define LXT_NOFILE (10)\r\n#define LXT_NR_OPEN (1024 * 1024)\r\n\r\nint ResourceLimitTest(PLXT_ARGS Args);\r\n\r\nint ResourceLimitNoFile(PLXT_ARGS Args);\r\n\r\nint PrlimitTest(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Resource Limit Test\", ResourceLimitTest}, {\"RLIMIT_NOFILE\", ResourceLimitNoFile}, {\"prlimit64 test\", PrlimitTest}};\r\n\r\nint ResourceLimitsTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint ResourceLimitTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n    struct rlimit ResourceLimit;\r\n    int Result;\r\n    int Status;\r\n\r\n    //\r\n    // Get all resource limits.\r\n    //\r\n\r\n    LxtLogInfo(\"Getting all resource limits:\");\r\n    for (Index = 0; Index < 16; Index += 1)\r\n    {\r\n        LxtCheckErrno(getrlimit(Index, &ResourceLimit));\r\n        LxtLogInfo(\"Resource# %d: current %llu, max %llu\", Index, ResourceLimit.rlim_cur, ResourceLimit.rlim_max)\r\n    }\r\n\r\n    //\r\n    // Set/Get invalid resources.\r\n    //\r\n    LxtCheckErrnoFailure(setrlimit(17, &ResourceLimit), EINVAL);\r\n    LxtCheckErrnoFailure(getrlimit(17, &ResourceLimit), EINVAL);\r\n    LxtCheckErrnoFailure(setrlimit(-1, &ResourceLimit), EINVAL);\r\n    LxtCheckErrnoFailure(getrlimit(-1, &ResourceLimit), EINVAL);\r\n    LxtCheckErrnoFailure(setrlimit(16, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(setrlimit(16, (struct rlimit*)-1), EFAULT);\r\n    LxtCheckErrnoFailure(getrlimit(16, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(getrlimit(16, (struct rlimit*)-1), EINVAL);\r\n    LxtCheckErrnoFailure(setrlimit(LXT_NR_OPEN, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(setrlimit(LXT_NR_OPEN, (struct rlimit*)-1), EFAULT);\r\n    LxtCheckErrnoFailure(getrlimit(LXT_NR_OPEN, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(getrlimit(LXT_NR_OPEN, (struct rlimit*)-1), EINVAL);\r\n\r\n    //\r\n    // Set invalid resource limits.\r\n    //\r\n\r\n    ResourceLimit.rlim_cur = 2;\r\n    ResourceLimit.rlim_max = 1;\r\n    LxtLogInfo(\"Setting resource limit with soft limit being greater than hard limit\");\r\n    LxtCheckErrnoFailure(setrlimit(RLIMIT_NPROC, &ResourceLimit), EINVAL);\r\n\r\n    //\r\n    // Set NoFile limit past the WSL max, to NR_OPEN, and past NR_OPEN\r\n    //\r\n\r\n    ResourceLimit.rlim_cur = 2049;\r\n    ResourceLimit.rlim_max = 2050;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n\r\n    ResourceLimit.rlim_cur = LXT_NR_OPEN - 1;\r\n    ResourceLimit.rlim_max = LXT_NR_OPEN;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n\r\n    ResourceLimit.rlim_cur = LXT_NR_OPEN;\r\n    ResourceLimit.rlim_max = LXT_NR_OPEN;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n\r\n    ResourceLimit.rlim_cur = LXT_NR_OPEN + 1;\r\n    ResourceLimit.rlim_max = LXT_NR_OPEN + 1;\r\n    LxtCheckErrnoFailure(setrlimit(RLIMIT_NOFILE, &ResourceLimit), EPERM);\r\n\r\n    //\r\n    // Test RLIMIT_NPROC.\r\n    //\r\n\r\n    ResourceLimit.rlim_cur = 7823;\r\n    ResourceLimit.rlim_max = 7824;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NPROC, &ResourceLimit));\r\n\r\n    ResourceLimit.rlim_cur = 0x7ffffffffffffffe;\r\n    ResourceLimit.rlim_max = 0x7fffffffffffffff;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NPROC, &ResourceLimit));\r\n    LxtCheckErrno(getrlimit(RLIMIT_NPROC, &ResourceLimit));\r\n    LxtCheckEqual(ResourceLimit.rlim_cur, 0x7ffffffffffffffe, \"%Iu\");\r\n    LxtCheckEqual(ResourceLimit.rlim_max, 0x7fffffffffffffff, \"%Iu\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint ResourceLimitNoFile(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int FileDescriptorCount;\r\n    int* FileDescriptors;\r\n    int Index;\r\n    int InitialFileDescriptorCount;\r\n    struct rlimit InitialResourceLimit;\r\n    struct rlimit ResourceLimit;\r\n    int Result;\r\n    struct stat Stat;\r\n\r\n    ChildPid = -1;\r\n    FileDescriptors = NULL;\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &InitialResourceLimit));\r\n    LxtLogInfo(\"Initial rlim_cur %Iu rlim_max %Iu\", InitialResourceLimit.rlim_cur, InitialResourceLimit.rlim_max);\r\n\r\n    ResourceLimit = InitialResourceLimit;\r\n\r\n    //\r\n    // Lower the current file descriptor limit.\r\n    //\r\n\r\n    ResourceLimit.rlim_cur = LXT_NOFILE;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtLogInfo(\"rlim_cur %Iu rlim_max %Iu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    //\r\n    // Determine how many file descriptors are already open.\r\n    //\r\n\r\n    InitialFileDescriptorCount = 0;\r\n    for (Index = 0; Index < ResourceLimit.rlim_cur; Index += 1)\r\n    {\r\n        if (fstat(Index, &Stat) == 0)\r\n        {\r\n\r\n            //\r\n            // Ensure that the file descriptor number matches the index. This\r\n            // is strictly to make the validation later in this test more\r\n            // straightforward.\r\n            //\r\n\r\n            LxtCheckEqual(Index, InitialFileDescriptorCount, \"%d\");\r\n            InitialFileDescriptorCount += 1;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Determine how many file descriptors are already open and allocate an\r\n    // array large enough to open the maximum number of file descriptors.\r\n    //\r\n\r\n    LxtLogInfo(\"%d currently open file descriptors\", InitialFileDescriptorCount);\r\n    FileDescriptorCount = ResourceLimit.rlim_cur - InitialFileDescriptorCount;\r\n    FileDescriptors = malloc(sizeof(int) * FileDescriptorCount);\r\n    if (FileDescriptors == NULL)\r\n    {\r\n        LxtLogError(\"alloc failed\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(FileDescriptors, -1, sizeof(FileDescriptors));\r\n    LxtCheckErrno(FileDescriptors[0] = creat(LXT_RESOURCE_LIMIT_TEST_FILE, 0655));\r\n\r\n    //\r\n    // Ensure that a file descriptor with a value greater than the current\r\n    // rlimit cannot be created.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(dup2(FileDescriptors[0], ResourceLimit.rlim_cur), EBADF);\r\n    LxtCheckErrnoFailure(dup2(FileDescriptors[0], (ResourceLimit.rlim_cur + 1)), EBADF);\r\n\r\n    //\r\n    // Open enough file descriptors to completely fill the table.\r\n    //\r\n\r\n    for (Index = 1; Index < FileDescriptorCount; Index += 1)\r\n    {\r\n        LxtCheckErrno(FileDescriptors[Index] = open(LXT_RESOURCE_LIMIT_TEST_FILE, O_RDONLY));\r\n    }\r\n\r\n    LxtLogInfo(\"Opened %d file descriptors\", Index);\r\n\r\n    //\r\n    // Ensure that opening one more file descriptor fails.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(open(LXT_RESOURCE_LIMIT_TEST_FILE, O_RDONLY), EMFILE);\r\n\r\n    //\r\n    // Lower the limit to the initial file descriptor count and close all but\r\n    // the highest numbered file descriptor.\r\n    //\r\n\r\n    ResourceLimit.rlim_cur = InitialFileDescriptorCount;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtLogInfo(\"rlim_cur %Iu rlim_max %Iu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    for (Index = 0; Index < (FileDescriptorCount - 1); Index += 1)\r\n    {\r\n        LxtClose(FileDescriptors[Index]);\r\n        FileDescriptors[Index] = -1;\r\n    }\r\n\r\n    //\r\n    // Try to open file descriptors up to the max value.\r\n    //\r\n\r\n    for (Index = 0; Index < (FileDescriptorCount - 1); Index += 1)\r\n    {\r\n        LxtCheckErrnoFailure(FileDescriptors[Index] = open(LXT_RESOURCE_LIMIT_TEST_FILE, O_RDONLY), EMFILE);\r\n    }\r\n\r\n    //\r\n    // Ensure that a child process inherits the same file descriptor limits.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        for (Index = 0; Index < (FileDescriptorCount - 1); Index += 1)\r\n        {\r\n            LxtCheckErrnoFailure(FileDescriptors[Index] = open(LXT_RESOURCE_LIMIT_TEST_FILE, O_RDONLY), EMFILE);\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Increment the rlimit and open a single file descriptor.\r\n    //\r\n\r\n    ResourceLimit.rlim_cur += 1;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtLogInfo(\"rlim_cur %Iu rlim_max %Iu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    LxtCheckErrno(FileDescriptors[0] = open(LXT_RESOURCE_LIMIT_TEST_FILE, O_RDONLY));\r\n    LxtCheckEqual(InitialFileDescriptorCount, FileDescriptors[0], \"%d\");\r\n\r\n    //\r\n    // Reset the rlimit to the original value.\r\n    //\r\n\r\n    ResourceLimit.rlim_cur = LXT_NOFILE;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtLogInfo(\"rlim_cur %Iu rlim_max %Iu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    //\r\n    // Attempt to reopen the rest of the file descriptors now that the limit has\r\n    // been increased.\r\n    //\r\n\r\n    for (Index = 1; Index < (FileDescriptorCount - 1); Index += 1)\r\n    {\r\n        LxtCheckErrno(FileDescriptors[Index] = open(LXT_RESOURCE_LIMIT_TEST_FILE, O_RDONLY));\r\n        LxtCheckEqual((Index + InitialFileDescriptorCount), FileDescriptors[Index], \"%d\");\r\n    }\r\n\r\n    //\r\n    // Attempt to open one more file descriptor (should fail).\r\n    //\r\n\r\n    LxtCheckErrnoFailure(open(LXT_RESOURCE_LIMIT_TEST_FILE, O_RDONLY), EMFILE);\r\n\r\n    //\r\n    // Set the current resource limit to the max.\r\n    //\r\n\r\n    ResourceLimit.rlim_cur = ResourceLimit.rlim_max;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtLogInfo(\"rlim_cur %Iu rlim_max %Iu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    //\r\n    // Make the file descriptor limit very large.\r\n    //\r\n\r\n    ResourceLimit.rlim_max = 0x100000;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtLogInfo(\"rlim_cur %Iu rlim_max %Iu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    ResourceLimit.rlim_cur = ResourceLimit.rlim_max;\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtLogInfo(\"rlim_cur %Iu rlim_max %Iu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    //\r\n    // Attempt to set the file descriptor limit larger than the maximum allowed.\r\n    //\r\n\r\n    ResourceLimit.rlim_cur = ResourceLimit.rlim_max;\r\n    ResourceLimit.rlim_max += 1;\r\n    LxtCheckErrnoFailure(setrlimit(RLIMIT_NOFILE, &ResourceLimit), EPERM);\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtLogInfo(\"rlim_cur %Iu rlim_max %Iu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    //\r\n    // Restore the original rlimit.\r\n    //\r\n\r\n    LxtCheckErrno(setrlimit(RLIMIT_NOFILE, &InitialResourceLimit));\r\n    LxtCheckErrno(getrlimit(RLIMIT_NOFILE, &ResourceLimit));\r\n    LxtLogInfo(\"Restored rlim_cur %Iu rlim_max %Iu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (FileDescriptors != NULL)\r\n    {\r\n        for (Index = 0; Index < FileDescriptorCount; Index += 1)\r\n        {\r\n            if (FileDescriptors[Index] >= 0)\r\n            {\r\n                LxtClose(FileDescriptors[Index]);\r\n            }\r\n        }\r\n\r\n        free(FileDescriptors);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n    else\r\n    {\r\n        unlink(LXT_RESOURCE_LIMIT_TEST_FILE);\r\n    }\r\n}\r\n\r\nint PrlimitTest(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int CurrentPid;\r\n    int Index;\r\n    struct rlimit NewLimit;\r\n    struct rlimit OldLimit;\r\n    int ParentPid;\r\n    int Result;\r\n    int Status;\r\n\r\n    ChildPid = -1;\r\n    ParentPid = getpid();\r\n\r\n    //\r\n    // Get and set all resource limits.\r\n    //\r\n\r\n    for (Index = 0; Index < 16; Index += 1)\r\n    {\r\n        LxtCheckErrno(LxtPrlimit64(0, Index, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(0, Index, &NewLimit, &OldLimit));\r\n        LxtCheckEqual(OldLimit.rlim_max, NewLimit.rlim_max, \"%Iu\");\r\n        LxtCheckEqual(OldLimit.rlim_cur, NewLimit.rlim_cur, \"%Iu\");\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, Index, &NewLimit, &OldLimit));\r\n    }\r\n\r\n    //\r\n    // Pid != 0 variations.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        CurrentPid = getpid();\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // UID variations.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(LxtSetresuid(-1, -1, LXT_RESOURCE_LIMIT_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n\r\n        LxtCheckErrno(LxtSetresuid(-1, LXT_RESOURCE_LIMIT_UID, -1));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n\r\n        LxtCheckErrno(LxtSetresuid(LXT_RESOURCE_LIMIT_UID, -1, -1));\r\n        LxtCheckErrnoFailure(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit), EPERM);\r\n        LxtCheckErrnoFailure(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit), EPERM);\r\n\r\n        CurrentPid = getpid();\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Change the UID and verify that querying the parent's resource limits\r\n        // succeeds with the CAP_SYS_RESOURCE capability.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_SYS_RESOURCE)].permitted |= CAP_TO_MASK(CAP_SYS_RESOURCE);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(LxtSetresuid(LXT_RESOURCE_LIMIT_UID, LXT_RESOURCE_LIMIT_UID, LXT_RESOURCE_LIMIT_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n\r\n        //\r\n        // Drop the CAP_SYS_RESOURCE capability and verify querying the parents\r\n        // resource limits fails.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit), EPERM);\r\n        LxtCheckErrnoFailure(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit), EPERM);\r\n\r\n        CurrentPid = getpid();\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // GID variations.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(LxtSetresgid(-1, -1, LXT_RESOURCE_LIMIT_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n\r\n        LxtCheckErrno(LxtSetresgid(-1, LXT_RESOURCE_LIMIT_GID, -1));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n\r\n        LxtCheckErrno(LxtSetresgid(LXT_RESOURCE_LIMIT_GID, -1, -1));\r\n        LxtCheckErrnoFailure(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit), EPERM);\r\n        LxtCheckErrnoFailure(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit), EPERM);\r\n\r\n        CurrentPid = getpid();\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Change the GID and verify that querying the parent's resource limits\r\n        // succeeds with the CAP_SYS_RESOURCE capability.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SYS_RESOURCE)].permitted |= CAP_TO_MASK(CAP_SYS_RESOURCE);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(LxtSetresgid(LXT_RESOURCE_LIMIT_GID, LXT_RESOURCE_LIMIT_GID, LXT_RESOURCE_LIMIT_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n\r\n        //\r\n        // Drop the CAP_SYS_RESOURCE capability and verify querying the parents\r\n        // resource limits fails.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, NULL, &NewLimit), EPERM);\r\n        LxtCheckErrnoFailure(LxtPrlimit64(ParentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit), EPERM);\r\n\r\n        CurrentPid = getpid();\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, NULL, &NewLimit));\r\n        LxtCheckErrno(LxtPrlimit64(CurrentPid, RLIMIT_NOFILE, &NewLimit, &OldLimit));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // The new limit should still be set even if old limit buffer is invalid.\r\n    //\r\n\r\n    LxtCheckErrno(LxtPrlimit64(0, RLIMIT_NOFILE, NULL, &OldLimit));\r\n    NewLimit = OldLimit;\r\n    NewLimit.rlim_cur -= 1;\r\n    LxtCheckErrnoFailure(LxtPrlimit64(0, RLIMIT_NOFILE, &NewLimit, -1), EFAULT);\r\n    LxtCheckErrno(LxtPrlimit64(0, RLIMIT_NOFILE, NULL, &NewLimit));\r\n    LxtCheckNotEqual(OldLimit.rlim_cur, NewLimit.rlim_cur, \"%Iu\");\r\n\r\n    //\r\n    // Verify that if the new limit is invalid, the old limit is not returned.\r\n    //\r\n\r\n    LxtCheckErrno(LxtPrlimit64(0, RLIMIT_NOFILE, NULL, &OldLimit));\r\n    NewLimit = OldLimit;\r\n    NewLimit.rlim_cur = NewLimit.rlim_max + 1;\r\n    memset(&OldLimit, 0, sizeof(OldLimit));\r\n    LxtCheckErrnoFailure(LxtPrlimit64(0, RLIMIT_NOFILE, &NewLimit, &OldLimit), EINVAL);\r\n    LxtCheckEqual(OldLimit.rlim_max, 0, \"%Iu\");\r\n    LxtCheckEqual(OldLimit.rlim_cur, 0, \"%Iu\");\r\n    LxtCheckErrnoFailure(LxtPrlimit64(0, RLIMIT_NOFILE, -1, &OldLimit), EFAULT);\r\n    LxtCheckEqual(OldLimit.rlim_max, 0, \"%Iu\");\r\n    LxtCheckEqual(OldLimit.rlim_cur, 0, \"%Iu\");\r\n\r\n    //\r\n    // Negative variations.\r\n    //\r\n\r\n    LxtCheckErrno(LxtPrlimit64(0, RLIMIT_NPROC, NULL, NULL));\r\n    LxtCheckErrnoFailure(LxtPrlimit64(0, 16, NULL, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, 16, NULL, NULL), ESRCH);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, RLIMIT_NPROC, NULL, NULL), ESRCH);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, RLIMIT_NPROC, -1, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, RLIMIT_NPROC, NULL, -1), ESRCH);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, RLIMIT_NPROC, -1, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(0, 16, NULL, &OldLimit), EINVAL);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(0, RLIMIT_NPROC, -1, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(0, RLIMIT_NPROC, NULL, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(0, RLIMIT_NPROC, -1, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(0, 16, -1, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, 16, NULL, &OldLimit), ESRCH);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, RLIMIT_NPROC, -1, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, RLIMIT_NPROC, NULL, -1), ESRCH);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, RLIMIT_NPROC, -1, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtPrlimit64(-1, 16, -1, -1), EFAULT);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/sched.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    sched.c\r\n\r\nAbstract:\r\n\r\n    This file is the scheduler test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sched.h>\r\n#include <stdio.h>\r\n#include <sys/syscall.h>\r\n\r\n#define LXT_NAME \"sched\"\r\n\r\nint GetDefaultScheduler(PLXT_ARGS Args);\r\n\r\nint SetScheduler(PLXT_ARGS Args);\r\n\r\nint SetSchedulerChild(PLXT_ARGS Args);\r\n\r\nint SetGetAffinity(PLXT_ARGS Args);\r\n\r\nint SetGetAffinityNp(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Get Scheduler Default\", GetDefaultScheduler},\r\n    {\"Set Scheduler\", SetScheduler},\r\n    {\"Set-Get Affinity\", SetGetAffinity},\r\n    {\"Set-Get Affinity np\", SetGetAffinityNp}};\r\n\r\nint SchedTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint GetDefaultScheduler(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int Policy;\r\n\r\n    Policy = sched_getscheduler(0);\r\n    LxtLogInfo(\"Policy received %d\", Policy);\r\n    if (Policy == SCHED_OTHER)\r\n    {\r\n        Result = LXT_RESULT_SUCCESS;\r\n    }\r\n    else\r\n    {\r\n        LxtLogError(\"Bad policy. Expected(%d) != Returned(%d)\", SCHED_OTHER, Policy);\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SetScheduler(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct sched_param Param;\r\n    int Policy;\r\n\r\n    Policy = sched_getscheduler(0);\r\n    LxtLogInfo(\"Policy received %d\", Policy);\r\n    if (Policy < 0)\r\n    {\r\n        LxtLogError(\"Bad policy. errno %d = %s\", errno, strerror(errno));\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Set a different policy\r\n    //\r\n\r\n    if (Policy == SCHED_OTHER)\r\n    {\r\n        Policy = SCHED_FIFO;\r\n    }\r\n    else\r\n    {\r\n        Policy = SCHED_OTHER;\r\n    }\r\n\r\n    LxtLogInfo(\"Setting policy %d\", Policy);\r\n    Param.sched_priority = 17;\r\n    Result = sched_setscheduler(0, Policy, &Param);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"Set scheduler failed errno %d = %s\", errno, strerror(errno));\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = sched_getscheduler(0);\r\n    LxtLogInfo(\"Policy received %d\", Result);\r\n    if (Policy != Result)\r\n    {\r\n        LxtLogError(\"Bad policy. Expected(%d) != Returned(%d)\", Policy, Result);\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SetSchedulerChild(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int Policy;\r\n    int Pid;\r\n\r\n    //\r\n    // The child should inherit this scheduler\r\n    //\r\n\r\n    sched_setscheduler(0, SCHED_OTHER, NULL);\r\n\r\n    Pid = fork();\r\n    if (Pid != 0)\r\n    {\r\n        sleep(1);\r\n\r\n        Result = sched_setscheduler(Pid, SCHED_FIFO, NULL);\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"Set scheduler failed errno %d = %s\", errno, strerror(errno));\r\n\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        sleep(2);\r\n    }\r\n    else\r\n    {\r\n        Policy = sched_getscheduler(0);\r\n        LxtLogInfo(\"Child - Policy gotten %d\", Policy);\r\n        if (Policy != SCHED_OTHER)\r\n        {\r\n            LxtLogError(\"Bad policy. Expected(%d) != Returned(%d)\", SCHED_OTHER, Policy);\r\n\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n        sleep(2);\r\n\r\n        Policy = sched_getscheduler(0);\r\n        printf(\"Child - Policy gotten %d\", Policy);\r\n        if (Policy != SCHED_FIFO)\r\n        {\r\n            LxtLogError(\"Bad policy. Expected(%d) != Returned(%d)\", SCHED_FIFO, Policy);\r\n\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SetGetAffinity(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int Size = -1;\r\n    cpu_set_t Set;\r\n    cpu_set_t Desired;\r\n    int Sizes[] = {-8, 8, 16, 24, 32, 40, 64, 128, 256};\r\n    int SizeExpected;\r\n    int Index;\r\n\r\n    CPU_ZERO(&Set);\r\n\r\n    LxtLogInfo(\"sizeof(cpu_set_t) = %Iu\", sizeof(cpu_set_t));\r\n    LxtCheckErrno(Size = LxtSched_GetAffinity(0, sizeof(Set), &Set));\r\n    LxtCheckEqual(Size, 64, \"%d\");\r\n    LxtLogInfo(\"Affinity before: %08x\", *(uint32_t*)&Set);\r\n    CPU_ZERO(&Desired);\r\n    CPU_SET(0, &Desired);\r\n    LxtCheckErrno(LxtSched_SetAffinity(0, 1, &Desired));\r\n    LxtCheckErrno(Size = LxtSched_GetAffinity(0, sizeof(Set), &Set));\r\n    LxtCheckEqual(Size, 64, \"%d\");\r\n    LxtCheckErrno(LxtSched_SetAffinity(0, 3, &Desired));\r\n    LxtCheckErrno(Size = LxtSched_GetAffinity(0, sizeof(Set), &Set));\r\n    LxtCheckEqual(Size, 64, \"%d\");\r\n    LxtCheckErrno(LxtSched_SetAffinity(0, sizeof(Desired), &Desired));\r\n    LxtCheckErrno(Size = LxtSched_GetAffinity(0, sizeof(Set), &Set));\r\n    LxtCheckEqual(Size, 64, \"%d\");\r\n    LxtLogInfo(\"Affinity after: %08x\", *(uint32_t*)&Set);\r\n    if (!CPU_EQUAL(&Set, &Desired))\r\n    {\r\n        LxtLogError(\"sched_setaffinity failed to set the affinity. \");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Test with various buffer sizes.\r\n    //\r\n\r\n    for (Index = 0; Index < (int)LXT_COUNT_OF(Sizes); Index++)\r\n    {\r\n        LxtLogInfo(\"Testing size %d\", Sizes[Index]);\r\n        LxtCheckErrno(Size = LxtSched_GetAffinity(0, Sizes[Index], &Set));\r\n        SizeExpected = Sizes[Index];\r\n        if ((SizeExpected > 64) || (SizeExpected < 0))\r\n        {\r\n            SizeExpected = 64;\r\n        }\r\n\r\n        LxtCheckEqual(Size, SizeExpected, \"%d\");\r\n        if (!CPU_EQUAL(&Set, &Desired))\r\n        {\r\n            LxtLogError(\"sched_setaffinity failed to set the affinity. \");\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    LxtCheckErrno(Size = LxtSched_GetAffinity(getpid(), sizeof(Set), &Set));\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 0, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 1, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 2, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 7, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 9, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 10, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 31, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 33, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 63, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, 65, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, -1, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, -63, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, -1, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, -1, -1), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(-1, -1, &Set), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, sizeof(Set), NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(0, sizeof(Set), -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(-1, sizeof(Set), &Set), ESRCH);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(-1, sizeof(Set), NULL), ESRCH);\r\n    LxtCheckErrnoFailure(LxtSched_GetAffinity(-1, sizeof(Set), -1), ESRCH);\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SetGetAffinityNp(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    cpu_set_t Set;\r\n    CPU_ZERO(&Set);\r\n    CPU_SET(0, &Set);\r\n    LxtCheckErrno(LxtSched_SetAffinity(0, sizeof(Set), &Set));\r\n    LxtCheckErrno(LxtSched_GetAffinity(0, sizeof(Set), &Set));\r\n\r\n    //\r\n    // N.B Affinity cannot be validated because its not guaranteed for it to\r\n    //     take effect.\r\n    //\r\n\r\n    LxtLogInfo(\"Current Affinity: %08x\", *(uint32_t*)&Set);\r\n    CPU_ZERO(&Set);\r\n    CPU_SET(1, &Set);\r\n    LxtCheckErrno(LxtSched_SetAffinity(0, sizeof(Set), &Set));\r\n    LxtCheckErrno(LxtSched_GetAffinity(0, sizeof(Set), &Set));\r\n    LxtLogInfo(\"Current Affinity: %08x\", *(uint32_t*)&Set);\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/select.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    select.c\r\n\r\nAbstract:\r\n\r\n    This file is the select system call test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/select.h>\r\n#include <sys/stat.h>\r\n#include <sys/time.h>\r\n#include <sys/resource.h>\r\n#include <sys/types.h>\r\n#include <sys/mman.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n#include <stdlib.h>\r\n\r\n#if defined(__aarch64__)\r\n\r\n//\r\n// ARM64 glibc converts select to pselect.\r\n//\r\n\r\n#define __ARCH_WANT_SYSCALL_DEPRECATED\r\n#include <linux/unistd.h>\r\n#define SYS_select __NR_select\r\n#endif\r\n\r\n#define LxtSelect(_nfds, _readfds, _writefds, _exceptfds, _timeout) \\\r\n    (syscall(SYS_select, _nfds, _readfds, _writefds, _exceptfds, _timeout))\r\n\r\n#define LXT_NAME \"Select\"\r\n#define LXT_SELECT_TEST_FILE \"/data/test/select_test.bin\"\r\n#define LXT_SHORT_TIMEOUT 1\r\n\r\nLXT_VARIATION_HANDLER SelectFdBufferSize;\r\nLXT_VARIATION_HANDLER SelectMaxNfds;\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {{\"FD buffer sizes\", SelectFdBufferSize}, {\"Max nfds\", SelectMaxNfds}};\r\n\r\nint SelectTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint SelectFdBufferSize(PLXT_ARGS Args)\r\n{\r\n\r\n    unsigned char* Address;\r\n    int Fds[sizeof(unsigned long) * 8 * 2];\r\n    int Index;\r\n    void* MapResult;\r\n    int Result;\r\n    fd_set* ReadSet;\r\n    unsigned char* ReadSetBuffer;\r\n    int ReadSetCount;\r\n    struct timeval Timeout;\r\n\r\n    Address = NULL;\r\n    memset(Fds, -1, sizeof(Fds));\r\n    Result = LXT_RESULT_FAILURE;\r\n\r\n    //\r\n    // Open files that will be used for select.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(Fds); ++Index)\r\n    {\r\n        LxtCheckErrno(Fds[Index] = open(LXT_SELECT_TEST_FILE, O_RDWR | O_CREAT, S_IRWXU));\r\n    }\r\n\r\n    //\r\n    // Create a read\\write page followed by a no access page. The readset buffer\r\n    // will be adjusted so it is just before the no access page.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address = mmap(NULL, PAGE_SIZE * 2, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANONYMOUS), -1, 0));\r\n    LxtCheckErrno(mprotect(Address + PAGE_SIZE, PAGE_SIZE, PROT_NONE));\r\n\r\n    //\r\n    // A ReadSetCount of 0 should not touch the buffer.\r\n    //\r\n\r\n    ReadSetCount = 0;\r\n    ReadSetBuffer = (Address + PAGE_SIZE) - sizeof(unsigned char);\r\n    memset(ReadSetBuffer, -1, sizeof(unsigned char));\r\n    ReadSet = (fd_set*)ReadSetBuffer;\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(*ReadSetBuffer, (unsigned char)-1, \"%c\");\r\n\r\n    //\r\n    // Test select with different sized buffers that have all of the bits set\r\n    // and a ReadSetCount of 1. The expectation is that the write will fail if\r\n    // the buffer is less than an unsigned long. If larger, the values will be\r\n    // zeroed out to an unsigned long but not more.\r\n    //\r\n\r\n    ReadSetCount = 1;\r\n    ReadSetBuffer = (Address + PAGE_SIZE) - sizeof(unsigned char);\r\n    memset(ReadSetBuffer, -1, sizeof(unsigned char));\r\n    ReadSet = (fd_set*)ReadSetBuffer;\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrnoFailure(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout), EFAULT);\r\n    LxtCheckEqual(*ReadSetBuffer, (unsigned char)-1, \"%c\");\r\n\r\n    if (sizeof(unsigned int) != sizeof(unsigned long))\r\n    {\r\n        ReadSetBuffer = (Address + PAGE_SIZE) - sizeof(unsigned int);\r\n        memset(ReadSetBuffer, -1, sizeof(unsigned int));\r\n        ReadSet = (fd_set*)ReadSetBuffer;\r\n        memset(&Timeout, 0, sizeof(Timeout));\r\n        LxtCheckErrnoFailure(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout), EFAULT);\r\n        LxtCheckEqual(*(unsigned int*)ReadSetBuffer, (unsigned int)-1, \"%d\");\r\n    }\r\n\r\n    ReadSetBuffer = (Address + PAGE_SIZE) - sizeof(unsigned long);\r\n    memset(ReadSetBuffer, -1, sizeof(unsigned long));\r\n    ReadSet = (fd_set*)ReadSetBuffer;\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(*((unsigned long*)ReadSetBuffer), 0, \"%ld\");\r\n\r\n    ReadSetBuffer = (Address + PAGE_SIZE) - (sizeof(unsigned long) * 2);\r\n    memset(ReadSetBuffer, -1, sizeof(unsigned long) * 2);\r\n    ReadSet = (fd_set*)ReadSetBuffer;\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));\r\n    LxtCheckEqual(*((unsigned long*)ReadSetBuffer), 0, \"%ld\");\r\n    LxtCheckEqual(*(((unsigned long*)ReadSetBuffer + 1)), -1, \"%ld\");\r\n\r\n    //\r\n    // Test with a ReadSetCount of exactly the number of bits in an unsigned\r\n    // long.\r\n    //\r\n\r\n    ReadSetCount = sizeof(unsigned long) * 8;\r\n    ReadSetBuffer = (Address + PAGE_SIZE) - (sizeof(unsigned long) * 2);\r\n    memset(ReadSetBuffer, -1, sizeof(unsigned long) * 2);\r\n    ReadSet = (fd_set*)ReadSetBuffer;\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));\r\n    LxtCheckNotEqual(*((unsigned long*)ReadSetBuffer), -1, \"%ld\");\r\n    LxtCheckEqual(*(((unsigned long*)ReadSetBuffer + 1)), -1, \"%ld\");\r\n\r\n    //\r\n    // And again with one larger.\r\n    //\r\n\r\n    ReadSetCount = sizeof(unsigned long) * 8 + 1;\r\n    ReadSetBuffer = (Address + PAGE_SIZE) - (sizeof(unsigned long) * 2);\r\n    memset(ReadSetBuffer, -1, sizeof(unsigned long) * 2);\r\n    ReadSet = (fd_set*)ReadSetBuffer;\r\n    memset(&Timeout, 0, sizeof(Timeout));\r\n    LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));\r\n    LxtCheckNotEqual(*((unsigned long*)ReadSetBuffer), -1, \"%ld\");\r\n    LxtCheckEqual(*(((unsigned long*)ReadSetBuffer + 1)), 1, \"%ld\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    for (Index = 0; Index < LXT_COUNT_OF(Fds); ++Index)\r\n    {\r\n        if (Fds[Index] != -1)\r\n        {\r\n            LxtClose(Fds[Index]);\r\n        }\r\n    }\r\n\r\n    if (Address != NULL)\r\n    {\r\n        munmap(Address, PAGE_SIZE * 2);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SelectMaxNfds(PLXT_ARGS Args)\r\n{\r\n\r\n    int Result;\r\n    int Fd;\r\n    int NumFd;\r\n    fd_set ReadSet;\r\n    struct timeval Timeout;\r\n    struct rlimit MaxFds;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    Fd = -1;\r\n    Timeout.tv_sec = 5;\r\n    Timeout.tv_usec = 0;\r\n\r\n    //\r\n    // Open a file that will be used for select.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(LXT_SELECT_TEST_FILE, (O_RDWR | O_CREAT), S_IRWXU));\r\n\r\n    //\r\n    // Create the select set and set the FD in it.\r\n    //\r\n\r\n    FD_ZERO(&ReadSet);\r\n    FD_SET(Fd, &ReadSet);\r\n\r\n    //\r\n    // -ve values for 'nfds' should return EINVAL.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(select(-1, &ReadSet, NULL, NULL, &Timeout), EINVAL);\r\n\r\n    LxtLogInfo(\"Waiting on select to succeed..\");\r\n\r\n    //\r\n    // Set 'nfds' to > FD_SETSIZE. Kernel seems to ignore anything above the\r\n    // current ulimit(RLIMIT_NOFILE).\r\n    //\r\n    // N.B The value chosen for 'nfds' is > nr_open (which is the upper limit\r\n    //     for RLIMIT_NOFILE and by default set to 1048576). As per the man\r\n    //     page, EINVAL is returned if nfds exceeds the RLIMIT_NOFILE resource\r\n    //     limit, but that doesn't seem to be case.\r\n    //\r\n\r\n    LxtCheckErrno(NumFd = select(1048576 + 100, &ReadSet, NULL, NULL, &Timeout));\r\n\r\n    LxtCheckEqual(NumFd, 1, \"%d\");\r\n    if (FD_ISSET(Fd, &ReadSet) == 0)\r\n    {\r\n        LxtLogError(\r\n            \"Select was satisfied but file descriptor is not set \"\r\n            \"for read!\");\r\n\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Provide a bad file descriptor to select. As per the man page kernel\r\n    // ignores any FD > maximum FD currently opened by the process. But,\r\n    // in testing it seems like it does perform that check.\r\n    //\r\n    // N.B Below assumes that 200 > #FD's opened by the process.\r\n    //\r\n\r\n    FD_ZERO(&ReadSet);\r\n    FD_SET(200, &ReadSet);\r\n\r\n    //\r\n    //  Kernel ignores anything above FD_SETSIZE.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(select(201, &ReadSet, NULL, NULL, &Timeout), EBADF);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    return Result;\r\n}"
  },
  {
    "path": "test/linux/unit_tests/sem.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    sem.c\r\n\r\nAbstract:\r\n\r\n    This file is a test for the system V semaphore family of system calls.\r\n\r\n--*/\r\n\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/eventfd.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/xattr.h>\r\n#include <sys/mman.h>\r\n#include <fcntl.h>\r\n#include <sys/socket.h>\r\n#include <sys/un.h>\r\n#include <sys/ipc.h>\r\n#include <sys/shm.h>\r\n#include <sys/sem.h>\r\n#include <sys/prctl.h>\r\n#include <sys/wait.h>\r\n#include <grp.h>\r\n#include <netinet/in.h>\r\n#include <netdb.h>\r\n#include <time.h>\r\n#include <linux/random.h>\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n\r\n#include <sys/capability.h>\r\n\r\n#else\r\n\r\n#include <sys/cdefs.h>\r\n#include <linux/capability.h>\r\n\r\n#define _LINUX_CAPABILITY_VERSION_3 0x20080522\r\n\r\n#ifndef O_PATH\r\n#define O_PATH 010000000\r\n#endif\r\n\r\n#endif\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n\r\n#define LXT_NAME \"sem\"\r\n\r\n#define SEM_ACCESS_UID 1004\r\n#define SEM_ACCESS_GID 1004\r\n#define SEM_COUNT (10)\r\n\r\n//\r\n// Globals.\r\n//\r\n\r\nbool g_VerboseSem = true;\r\n\r\nint SemCtlSyscall(PLXT_ARGS Args);\r\n\r\nint SemGetSyscall(PLXT_ARGS Args);\r\n\r\nint SemOpFlags(PLXT_ARGS Args);\r\n\r\nint SemOpSyscall(PLXT_ARGS Args);\r\n\r\nvoid SemPrintInfo(struct semid_ds* Stat);\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"semget syscall\", SemGetSyscall}, {\"semctl syscall\", SemCtlSyscall}, {\"semop syscall\", SemOpSyscall}, {\"semop flags\", SemOpFlags}};\r\n\r\nint SemTestEntry(int Argc, char* Argv[])\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return 0;\r\n}\r\n\r\nint SemCtlSyscall(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    gid_t Gid;\r\n    int Id;\r\n    int Index;\r\n    struct semid_ds OldStat;\r\n    int Result;\r\n    struct seminfo SemInfo;\r\n    struct semid_ds Stat;\r\n    uid_t Uid;\r\n    unsigned short Values[SEM_COUNT];\r\n\r\n    ChildPid = -1;\r\n    Uid = getuid();\r\n    Gid = getgid();\r\n    LxtCheckErrno(Id = LxtSemGet(IPC_PRIVATE, SEM_COUNT, (IPC_CREAT | IPC_EXCL)));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SEM_STAT, &Stat));\r\n    LxtCheckEqual(SEM_COUNT, Stat.sem_nsems, \"%Iu\");\r\n    LxtCheckEqual(Uid, Stat.sem_perm.uid, \"%d\");\r\n    LxtCheckEqual(Gid, Stat.sem_perm.gid, \"%d\");\r\n    LxtCheckEqual(Uid, Stat.sem_perm.cuid, \"%d\");\r\n    LxtCheckEqual(Gid, Stat.sem_perm.cgid, \"%d\");\r\n    LxtCheckNotEqual(0, Stat.sem_ctime, \"%Iu\");\r\n    LxtCheckEqual(0, Stat.sem_otime, \"%Iu\");\r\n\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_STAT, &Stat));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_SET, &Stat));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_INFO, &SemInfo));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_INFO, &SemInfo));\r\n    LxtCheckErrno(LxtSemCtl(0, 0, IPC_INFO, &SemInfo));\r\n    LxtCheckErrno(LxtSemCtl(1, 0, IPC_INFO, &SemInfo));\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETPID, NULL), \"%d\");\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, GETPID, NULL), EINVAL);\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, GETVAL, NULL), EINVAL);\r\n    memset(&Values, 0, sizeof(Values));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, GETALL, &Values));\r\n    for (Index = 0; Index < SEM_COUNT; Index += 1)\r\n    {\r\n        LxtCheckEqual(Values[Index], LxtSemCtl(Id, Index, GETVAL, NULL), \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETNCNT, NULL), \"%d\");\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETZCNT, NULL), \"%d\");\r\n\r\n    //\r\n    // Check GETPID and GETVAL again after doing a setval on a single semaphore.\r\n    //\r\n\r\n    Values[0] = 1;\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SETVAL, Values[0]));\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, SETVAL, Values[0]), EINVAL);\r\n    LxtCheckEqual(getpid(), LxtSemCtl(Id, 0, GETPID, NULL), \"%d\");\r\n    LxtCheckEqual(Values[0], LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, GETALL, &Values));\r\n    for (Index = 0; Index < SEM_COUNT; Index += 1)\r\n    {\r\n        LxtCheckEqual(Values[Index], LxtSemCtl(Id, Index, GETVAL, NULL), \"%d\");\r\n    }\r\n\r\n    //\r\n    // Verify the pid and value of the other semaphores has not changed.\r\n    //\r\n\r\n    for (Index = 1; Index < SEM_COUNT; Index += 1)\r\n    {\r\n        LxtCheckEqual(0, LxtSemCtl(Id, Index, GETPID, NULL), \"%d\");\r\n        LxtCheckEqual(0, LxtSemCtl(Id, Index, GETVAL, NULL), \"%d\");\r\n    }\r\n\r\n    //\r\n    // SETALL command.\r\n    //\r\n\r\n    for (Index = 0; Index < SEM_COUNT; Index += 1)\r\n    {\r\n        Values[0] = Index;\r\n    }\r\n\r\n    //\r\n    // Ensure that each semaphore's value has been updated. Interestingly the\r\n    // last pid value is not updated by the SETALL command.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SETALL, &Values));\r\n    for (Index = 0; Index < SEM_COUNT; Index += 1)\r\n    {\r\n        if (Index == 0)\r\n        {\r\n            LxtCheckEqual(getpid(), LxtSemCtl(Id, Index, GETPID, NULL), \"%d\");\r\n        }\r\n        else\r\n        {\r\n            LxtCheckEqual(0, LxtSemCtl(Id, Index, GETPID, NULL), \"%d\");\r\n        }\r\n\r\n        LxtCheckEqual(Values[Index], LxtSemCtl(Id, Index, GETVAL, NULL), \"%d\");\r\n    }\r\n\r\n    memset(Values, 0, sizeof(Values));\r\n    Values[1] = -1;\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, SETALL, &Values), ERANGE);\r\n\r\n    //\r\n    // Create a child without the CAP_IPC_OWNER capability.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapGet(&CapHeader, CapData)) LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted &= ~CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Verify commands that requires the IPC_OWNER capability now fail.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(LxtSemCtl(Id, 0, SEM_STAT, &Stat), EACCES);\r\n        LxtCheckErrnoFailure(LxtSemCtl(Id, 0, IPC_STAT, &Stat), EACCES);\r\n\r\n        //\r\n        // Change the UID and verify commands fail.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SEM_ACCESS_UID));\r\n        LxtCheckErrnoFailure(LxtSemCtl(Id, 0, IPC_SET, &Stat), EPERM);\r\n        LxtCheckErrnoFailure(LxtSemCtl(Id, 0, IPC_RMID, NULL), EPERM);\r\n\r\n        LxtCheckErrnoFailure(LxtSemCtl(Id, 0, IPC_STAT, &Stat), EACCES);\r\n        LxtCheckErrnoFailure(LxtSemCtl(Id, 0, SEM_STAT, &Stat), EACCES);\r\n\r\n        LxtCheckErrno(LxtSemCtl(Id, 0, IPC_INFO, &SemInfo));\r\n        LxtCheckErrno(LxtSemCtl(0, 0, IPC_INFO, &SemInfo));\r\n        Result = LXT_RESULT_SUCCESS;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    //\r\n    // Ensure IPC_SET cannot set invalid mode bits (they are silently ignored).\r\n    //\r\n\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_STAT, &Stat));\r\n    Stat.sem_perm.mode = -1;\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_SET, &Stat));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.sem_perm.mode, 0777, \"%o\");\r\n\r\n    //\r\n    // Ensure the uid and gid cannot be set to -1.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_STAT, &OldStat));\r\n    Stat = OldStat;\r\n    Stat.sem_perm.uid = -1;\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, IPC_SET, &Stat), EINVAL);\r\n    Stat = OldStat;\r\n    Stat.sem_perm.gid = -1;\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, IPC_SET, &Stat), EINVAL);\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.sem_perm.uid, OldStat.sem_perm.uid, \"%d\");\r\n    LxtCheckEqual(Stat.sem_perm.gid, OldStat.sem_perm.gid, \"%d\");\r\n\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, -1, GETPID, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, GETPID, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, -1, GETVAL, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, GETVAL, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, -1, SETVAL, 0), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, SETVAL, 0), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, -1, GETNCNT, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, GETNCNT, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, -1, GETZCNT, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, GETZCNT, NULL), EINVAL);\r\n\r\n    LxtCheckErrnoFailure(LxtSemCtl(-1, 0, SEM_STAT, &Stat), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(-1, 0, IPC_STAT, &Stat), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(-1, 0, IPC_SET, &Stat), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, IPC_INFO, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, IPC_INFO, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemCtl(0, 0, IPC_INFO, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemCtl(0, 0, IPC_INFO, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemCtl(-1, 0, GETPID, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, GETPID, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(-1, 0, GETVAL, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, SEM_COUNT, GETVAL, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, GETALL, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, GETALL, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemCtl(-1, 0, GETNCNT, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(-1, 0, GETZCNT, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, SETALL, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, SETALL, -1), EFAULT);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtSemCtl(Id, 0, IPC_RMID, NULL);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SemGetSyscall(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int Id;\r\n    key_t Key;\r\n    int Mode;\r\n    size_t Result;\r\n    struct semid_ds Stat;\r\n    time_t Time;\r\n\r\n    ChildPid = -1;\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a key, verify that creating the key with the IPC_EXCL flag fails.\r\n    //\r\n\r\n    Mode = 0000;\r\n    LxtLogInfo(\"Mode %o\", Mode);\r\n    LxtCheckErrno(LxtGetrandom(&Key, sizeof(Key), 0));\r\n    LxtLogInfo(\"Key = %u\", Key);\r\n    LxtCheckErrno(Id = LxtSemGet(Key, SEM_COUNT, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtLogInfo(\"Id = %d\", Id);\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_STAT, &Stat));\r\n    SemPrintInfo(&Stat);\r\n    LxtCheckEqual(Key, Stat.sem_perm.__key, \"%Iu\");\r\n    LxtCheckEqual(SEM_COUNT, Stat.sem_nsems, \"%Iu\");\r\n    LxtCheckEqual(0, Stat.sem_otime, \"%Iu\");\r\n    LxtCheckNotEqual(0, Stat.sem_ctime, \"%Iu\");\r\n    LxtCheckEqual(Mode, Stat.sem_perm.mode, \"%o\");\r\n    LxtCheckEqual(getuid(), Stat.sem_perm.cuid, \"%d\");\r\n    LxtCheckEqual(getuid(), Stat.sem_perm.uid, \"%d\");\r\n    LxtCheckEqual(getgid(), Stat.sem_perm.cgid, \"%d\");\r\n    LxtCheckEqual(getgid(), Stat.sem_perm.gid, \"%d\");\r\n\r\n    //\r\n    // semget with IPC_CREAT or IPC_EXCL when the region already exists.\r\n    //\r\n\r\n    LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, IPC_CREAT), \"%Iu\");\r\n    LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, IPC_EXCL), \"%Iu\");\r\n    LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, 0), \"%Iu\");\r\n\r\n    //\r\n    // semget with count = 0 should succeed.\r\n    //\r\n\r\n    LxtCheckEqual(Id, LxtSemGet(Key, 0, 0), \"%Iu\");\r\n\r\n    //\r\n    // Create a child with a different uid and gid that does not have the\r\n    // IPC_OWNER capability.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(SEM_ACCESS_GID));\r\n        LxtCheckErrno(setuid(SEM_ACCESS_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // These should succeed because the child still has the IPC_OWNER cap.\r\n        //\r\n\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, IPC_CREAT), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, IPC_EXCL), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, 0777), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, 0666), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, 0600), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, 0060), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, 0006), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, 0), \"%Iu\");\r\n\r\n        //\r\n        // Drop all group membership and the CAP_IPC_OWNER capability and\r\n        // attempt to call semget with unmatching mode bits.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtSemGet(Key, SEM_COUNT, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtSemGet(Key, SEM_COUNT, 0666), EACCES);\r\n        LxtCheckErrnoFailure(LxtSemGet(Key, SEM_COUNT, 0600), EACCES);\r\n        LxtCheckErrnoFailure(LxtSemGet(Key, SEM_COUNT, 0060), EACCES);\r\n        LxtCheckErrnoFailure(LxtSemGet(Key, SEM_COUNT, 0006), EACCES);\r\n\r\n        //\r\n        // Use the same permission as before, these should succeed.\r\n        //\r\n\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, IPC_CREAT), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, IPC_EXCL), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, 0), \"%Iu\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    //\r\n    // semget with IPC_CREAT | IPC_EXCL when the region already exists, should\r\n    // succeed with only IPC_EXCL.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtSemGet(Key, SEM_COUNT, (IPC_CREAT | IPC_EXCL)), EEXIST);\r\n\r\n    //\r\n    // semget with a known key and a size that does not match.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtSemGet(Key, (SEM_COUNT * 2), 0), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemGet(Key, SEM_COUNT + 1, 0), EINVAL);\r\n\r\n    //\r\n    // N.B. There appears to be no error checking for invalid flags, only the\r\n    //      presence of valid flags.\r\n    //\r\n    // -1 includes the IPC_EXCL flag so this should return EEXIST.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtSemGet(Key, SEM_COUNT, -1), EEXIST);\r\n    LxtCheckEqual(Id, LxtSemGet(Key, SEM_COUNT, (-1 & ~IPC_EXCL)), \"%Iu\");\r\n\r\n    //\r\n    // Delete the region and create a new one with a size of one byte.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_RMID, &Stat));\r\n    LxtCheckErrnoFailure(LxtSemCtl(Id, 0, IPC_RMID, NULL), EINVAL);\r\n    Id = -1;\r\n    LxtCheckErrno(Id = LxtSemGet(IPC_PRIVATE, 1, 0));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_STAT, &Stat));\r\n    LxtCheckEqual(1, Stat.sem_nsems, \"%Iu\");\r\n\r\n    //\r\n    // Delete the region and create a new region with a size of zero bytes\r\n    // (should fail).\r\n    //\r\n\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_RMID, &Stat));\r\n    Id = -1;\r\n    LxtCheckErrnoFailure(Id = LxtSemGet(IPC_PRIVATE, 0, 0), EINVAL);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtSemCtl(Id, 0, IPC_RMID, NULL);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SemCloneChild(void* Param)\r\n{\r\n\r\n    int Id;\r\n    int Result;\r\n\r\n    Id = *((int*)Param);\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 1, GETVAL, NULL), \"%d\");\r\n    LxtCheckErrno(unshare(CLONE_SYSVSEM));\r\n\r\n    //\r\n    // Verify the values did not change.\r\n    //\r\n\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 1, GETVAL, NULL), \"%d\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    exit(Result);\r\n}\r\n\r\nint SemCloneThread(void* Param)\r\n{\r\n\r\n    long long Data;\r\n    int Event;\r\n    int Result;\r\n\r\n    Event = *((int*)Param);\r\n    LxtCheckErrno(read(Event, &Data, sizeof(Data)));\r\n\r\n    //\r\n    // Just exit the thread, not the thread group, on success.\r\n    //\r\n\r\n    syscall(SYS_exit, 0);\r\n\r\nErrorExit:\r\n    exit(Result);\r\n}\r\n\r\nint SemOpFlags(PLXT_ARGS Args)\r\n{\r\n    int ChildPid;\r\n    LXT_CLONE_ARGS CloneArgs;\r\n    long long EventData;\r\n    int Flags;\r\n    int Id;\r\n    struct sembuf Operations[SEM_COUNT];\r\n    size_t Result;\r\n    char* SharedStack;\r\n    int SharedEvent;\r\n    pid_t SharedTid;\r\n    struct semid_ds Stat;\r\n    int StackSize;\r\n    int Status;\r\n    char* UnsharedStack;\r\n    int UnsharedEvent;\r\n    pid_t UnsharedTid;\r\n    unsigned short Values[SEM_COUNT];\r\n\r\n    ChildPid = -1;\r\n    EventData = 1;\r\n    Id = -1;\r\n    SharedEvent = -1;\r\n    SharedStack = NULL;\r\n    UnsharedEvent = -1;\r\n    UnsharedStack = NULL;\r\n    memset(Operations, 0, sizeof(Operations));\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n\r\n    //\r\n    // Create a semaphore set.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtSemGet(IPC_PRIVATE, SEM_COUNT, (IPC_CREAT | IPC_EXCL)));\r\n\r\n    //\r\n    // Test the nowait flag.\r\n    //\r\n\r\n    Operations[0].sem_num = 0;\r\n    Operations[0].sem_op = -1;\r\n    Operations[0].sem_flg = IPC_NOWAIT;\r\n    LxtCheckErrnoFailure(LxtSemOp(Id, Operations, 1), EAGAIN);\r\n\r\n    //\r\n    // Increment the first semaphore.\r\n    //\r\n\r\n    Operations[0].sem_num = 0;\r\n    Operations[0].sem_op = 1;\r\n    Operations[0].sem_flg = 0;\r\n    LxtCheckErrno(LxtSemOp(Id, Operations, 1));\r\n\r\n    //\r\n    // Create a child.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Decrement the first semaphore and increment the second semaphore,\r\n        // both with the undo flag set.\r\n        //\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = -1;\r\n        Operations[0].sem_flg = SEM_UNDO;\r\n        Operations[1].sem_num = 1;\r\n        Operations[1].sem_op = 1;\r\n        Operations[1].sem_flg = SEM_UNDO;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 2));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Ensure the child's operations were undone.\r\n    //\r\n\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 1, GETVAL, NULL), \"%d\");\r\n\r\n    //\r\n    // Ensure the wait can still be satisfied.\r\n    //\r\n\r\n    Operations[0].sem_num = 0;\r\n    Operations[0].sem_op = -1;\r\n    Operations[0].sem_flg = 0;\r\n    LxtCheckErrno(LxtSemOp(Id, Operations, 1));\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n\r\n    //\r\n    // Create a child.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Set the first semaphore to the max with the undo flag specified and\r\n        // lower the count without the undo flag specified.\r\n        //\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = 0x7fff;\r\n        Operations[0].sem_flg = SEM_UNDO;\r\n        Operations[1].sem_num = 0;\r\n        Operations[1].sem_op = -0x7fff;\r\n        Operations[1].sem_flg = 0;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 2));\r\n        LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = 1;\r\n        Operations[0].sem_flg = 0;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 1));\r\n        LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for child to perform first operation.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Wait for child to perform second operation.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Wait for the child to exit and ensure the count does not drop below\r\n    // zero.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n\r\n    //\r\n    // Create a child.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Set the first semaphore to the max without the undo flag specified and\r\n        // lower the count with the undo flag specified.\r\n        //\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = 0x7fff;\r\n        Operations[0].sem_flg = 0;\r\n        Operations[1].sem_num = 0;\r\n        Operations[1].sem_op = -0x7fff;\r\n        Operations[1].sem_flg = SEM_UNDO;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 2));\r\n        LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = 1;\r\n        Operations[0].sem_flg = 0;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 1));\r\n        LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for child to perform first operation.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Wait for child to perform second operation.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Wait for the child to exit and ensure the count does not exceed the max\r\n    // semaphore value.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckEqual(0x7fff, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SETVAL, 0));\r\n\r\n    //\r\n    // Validate semctl SETVAL clears undo adjustments.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Set the first semaphore to the max with the undo flag specified and\r\n        // lower the count without the undo flag specified.\r\n        //\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = 0x7fff;\r\n        Operations[0].sem_flg = 0;\r\n        Operations[1].sem_num = 0;\r\n        Operations[1].sem_op = -0x7fff;\r\n        Operations[1].sem_flg = SEM_UNDO;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 2));\r\n        LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = 1;\r\n        Operations[0].sem_flg = 0;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 1));\r\n        LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for child to perform first operation.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Wait for child to perform second operation and set the semaphore value\r\n    // to zero. This should remove the pending semaphore adjustment.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SETVAL, 0));\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Wait for the child to exit and ensure the adjustment was not applied.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n\r\n    //\r\n    // Create a child, verify when the child unshares the semaphore adjustments\r\n    // are cleared.\r\n    //\r\n\r\n    memset(Values, 0, sizeof(Values));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SETALL, &Values));\r\n    LxtCheckErrno(LxtSemCtl(Id, 1, SETVAL, 1));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Increment one semaphore and decrement another both with the undo\r\n        // flag set.\r\n        //\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = 1;\r\n        Operations[0].sem_flg = SEM_UNDO;\r\n        Operations[1].sem_num = 1;\r\n        Operations[1].sem_op = -1;\r\n        Operations[1].sem_flg = SEM_UNDO;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 2));\r\n        LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n        LxtCheckEqual(0, LxtSemCtl(Id, 1, GETVAL, NULL), \"%d\");\r\n        LxtCheckErrno(unshare(CLONE_SYSVSEM));\r\n\r\n        //\r\n        // Ensure the state was undone.\r\n        //\r\n\r\n        LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n        LxtCheckEqual(1, LxtSemCtl(Id, 1, GETVAL, NULL), \"%d\");\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for child to unshare.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Ensure the child's operations were undone.\r\n    //\r\n\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 1, GETVAL, NULL), \"%d\");\r\n    LXT_SYNCHRONIZATION_POINT();\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Reset semaphore state.\r\n    //\r\n\r\n    memset(Values, 0, sizeof(Values));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SETALL, &Values));\r\n    LxtCheckErrno(LxtSemCtl(Id, 1, SETVAL, 1));\r\n    Operations[0].sem_num = 0;\r\n    Operations[0].sem_op = 1;\r\n    Operations[0].sem_flg = SEM_UNDO;\r\n    Operations[1].sem_num = 1;\r\n    Operations[1].sem_op = -1;\r\n    Operations[1].sem_flg = SEM_UNDO;\r\n    LxtCheckErrno(LxtSemOp(Id, Operations, 2));\r\n\r\n    //\r\n    // Clone a child to share the same SystemV semaphore adjustment structure.\r\n    //\r\n\r\n    LxtCheckResult(LxtClone(SemCloneChild, &Id, CLONE_SYSVSEM | SIGCHLD, &CloneArgs));\r\n\r\n    //\r\n    // Wait for child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(CloneArgs.CloneId, 0));\r\n\r\n    //\r\n    // Values should not have changed yet.\r\n    //\r\n\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 1, GETVAL, NULL), \"%d\");\r\n\r\n    //\r\n    // Create two threads, one sharing the semaphore adjustment structure\r\n    // and one not.\r\n    //\r\n\r\n    Flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;\r\n\r\n    StackSize = 1024 * 1024;\r\n\r\n    LxtCheckErrno(SharedEvent = eventfd(0, EFD_SEMAPHORE));\r\n    SharedStack = malloc(StackSize);\r\n    LxtCheckResult(clone(SemCloneThread, SharedStack + StackSize, Flags | CLONE_SYSVSEM, &SharedEvent, &SharedTid, NULL, &SharedTid));\r\n\r\n    LxtCheckErrno(UnsharedEvent = eventfd(0, EFD_SEMAPHORE));\r\n    UnsharedStack = malloc(StackSize);\r\n    LxtCheckResult(clone(SemCloneThread, UnsharedStack + StackSize, Flags, &UnsharedEvent, &UnsharedTid, NULL, &UnsharedTid));\r\n\r\n    //\r\n    // Unshare; since there is still a thread sharing, adjustments should\r\n    // not occur.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_SYSVSEM));\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 1, GETVAL, NULL), \"%d\");\r\n\r\n    //\r\n    // Signal the sharing thread and wait for it to exit; adjustments should\r\n    // occur shortly thereafter.\r\n    //\r\n\r\n    LxtCheckErrno(write(SharedEvent, &EventData, sizeof(EventData)));\r\n    LxtCheckErrno(LxtJoinThread(&SharedTid));\r\n    usleep(100000);\r\n    LxtCheckEqual(0, LxtSemCtl(Id, 0, GETVAL, NULL), \"%d\");\r\n    LxtCheckEqual(1, LxtSemCtl(Id, 1, GETVAL, NULL), \"%d\");\r\n\r\n    //\r\n    // Signal the unshared thread to clean things up.\r\n    //\r\n\r\n    LxtCheckErrno(write(UnsharedEvent, &EventData, sizeof(EventData)));\r\n    LxtCheckErrno(LxtJoinThread(&UnsharedTid));\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtSemCtl(Id, 0, IPC_RMID, NULL);\r\n    }\r\n\r\n    if (SharedEvent != -1)\r\n    {\r\n        close(SharedEvent);\r\n    }\r\n\r\n    if (UnsharedEvent != -1)\r\n    {\r\n        close(SharedEvent);\r\n    }\r\n\r\n    free(SharedStack);\r\n    free(UnsharedStack);\r\n    return Result;\r\n}\r\n\r\nint SemOpSyscall(PLXT_ARGS Args)\r\n\r\n{\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int Id;\r\n    int Index;\r\n    int Mode;\r\n    struct sembuf Operations[SEM_COUNT];\r\n    size_t Result;\r\n    struct semid_ds Stat;\r\n    int Status;\r\n    time_t Time;\r\n    struct timespec Timeout;\r\n    unsigned short Values[SEM_COUNT];\r\n\r\n    ChildPid = -1;\r\n    Id = -1;\r\n    memset(Operations, 0, sizeof(Operations));\r\n    memset(Values, 0, sizeof(Values));\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n\r\n    //\r\n    // Create a semaphore with zero mode bits.\r\n    //\r\n\r\n    Mode = 0000;\r\n    LxtLogInfo(\"Mode %o\", Mode);\r\n    LxtCheckErrno(Id = LxtSemGet(IPC_PRIVATE, SEM_COUNT, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtLogInfo(\"Id = %d\", Id);\r\n\r\n    //\r\n    // Create a child with a different uid and gid that does not have the\r\n    // IPC_OWNER capability.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(SEM_ACCESS_GID));\r\n        LxtCheckErrno(setuid(SEM_ACCESS_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // These should succeed because the child still has the IPC_OWNER cap.\r\n        //\r\n\r\n        memset(Operations, 0, sizeof(Operations));\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, SEM_COUNT));\r\n\r\n        //\r\n        // Drop all group membership and the CAP_IPC_OWNER capability and\r\n        // attempt to call semget with unmatching mode bits.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Attempt to issue operations, these should fail.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, Operations, SEM_COUNT), EACCES);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Create a new readable semaphore.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_RMID, NULL));\r\n    Mode = 0004;\r\n    LxtLogInfo(\"Mode %o\", Mode);\r\n    LxtCheckErrno(Id = LxtSemGet(IPC_PRIVATE, SEM_COUNT, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtLogInfo(\"Id = %d\", Id);\r\n\r\n    //\r\n    // Create a child with a different uid and gid that does not have the\r\n    // IPC_OWNER capability.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(SEM_ACCESS_GID));\r\n        LxtCheckErrno(setuid(SEM_ACCESS_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // These should succeed because the child still has the IPC_OWNER cap.\r\n        //\r\n\r\n        memset(Operations, 0, sizeof(Operations));\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, SEM_COUNT));\r\n\r\n        //\r\n        // Drop all group membership and the CAP_IPC_OWNER capability and\r\n        // attempt to call semget with unmatching mode bits.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Attempt to issue a \"wait for zero\" operation, this should succeed\r\n        // and return immediately because the value is zero.\r\n        //\r\n\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, SEM_COUNT));\r\n\r\n        //\r\n        // Attempt to increment the semaphore, this should fail.\r\n        //\r\n\r\n        Operations[1].sem_num = 0;\r\n        Operations[1].sem_op = 1;\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, &Operations[1], 1), EACCES);\r\n\r\n        //\r\n        // Attempt to decrement the semaphore, this should fail.\r\n        //\r\n\r\n        Operations[2].sem_num = 0;\r\n        Operations[2].sem_op = -1;\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, &Operations[2], 1), EACCES);\r\n\r\n        //\r\n        // Attempt the increment and wait operations after a wait for zero that\r\n        // succeeds.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, Operations, 3), EACCES);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Create a new writable semaphore.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, IPC_RMID, NULL));\r\n    Mode = 0002;\r\n    LxtLogInfo(\"Mode %o\", Mode);\r\n    LxtCheckErrno(Id = LxtSemGet(IPC_PRIVATE, SEM_COUNT, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtLogInfo(\"Id = %d\", Id);\r\n\r\n    //\r\n    // Create a child with a different uid and gid that does not have the\r\n    // IPC_OWNER capability.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(SEM_ACCESS_GID));\r\n        LxtCheckErrno(setuid(SEM_ACCESS_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // These should succeed because the child still has the IPC_OWNER cap.\r\n        //\r\n\r\n        memset(Operations, 0, sizeof(Operations));\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, SEM_COUNT));\r\n\r\n        //\r\n        // Drop all group membership and the CAP_IPC_OWNER capability and\r\n        // attempt to call semget with unmatching mode bits.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Attempt to issue a \"wait for zero\" operation, this should fail.\r\n        //\r\n\r\n        memset(Operations, 0, sizeof(Operations));\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, Operations, SEM_COUNT), EACCES);\r\n\r\n        //\r\n        // Attempt to increment the semaphore, this should succeed.\r\n        //\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = 1;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 1));\r\n\r\n        //\r\n        // Attempt to decrement the semaphore, this should succeed.\r\n        //\r\n\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = -1;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 1));\r\n\r\n        //\r\n        // Fill the operations buffer with a combination of valid operations\r\n        // and operations that the caller does not have permission to do. The\r\n        // parent will verify the semaphore values are adjusted correctly.\r\n        //\r\n\r\n        memset(Operations, 0, sizeof(Operations));\r\n        Operations[0].sem_num = 0;\r\n        Operations[0].sem_op = 1;\r\n\r\n        Operations[1].sem_num = 1;\r\n        Operations[1].sem_op = 1;\r\n\r\n        Operations[2].sem_num = 2;\r\n        Operations[2].sem_op = 0;\r\n\r\n        Operations[3].sem_num = 3;\r\n        Operations[3].sem_op = 1;\r\n\r\n        Operations[4].sem_num = 2;\r\n        Operations[4].sem_op = 0;\r\n\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 3));\r\n        LxtCheckErrno(LxtSemOp(Id, &Operations[1], 2));\r\n        LxtCheckErrno(LxtSemOp(Id, &Operations[1], 3));\r\n        LxtCheckErrno(LxtSemOp(Id, &Operations[2], 2));\r\n        LxtCheckErrno(LxtSemOp(Id, &Operations[2], 3));\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, &Operations[2], 1), EACCES);\r\n        LXT_SYNCHRONIZATION_POINT(); // (1)\r\n\r\n        //\r\n        // Wait for parent to query.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT(); // (2)\r\n\r\n        //\r\n        // Test how overflow is handled. It looks like there is a per-semaphore\r\n        // rolling count that is checked before any operations are performed.\r\n        //\r\n\r\n        memset(Operations, 0, sizeof(Operations));\r\n        Operations[0].sem_op = 32767;\r\n        Operations[1].sem_op = 1;\r\n        LxtCheckErrno(LxtSemOp(Id, Operations, 1));\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, &Operations[1], 1), ERANGE);\r\n        LXT_SYNCHRONIZATION_POINT(); // (3)\r\n\r\n        //\r\n        // Wait for parent to query.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT(); // (4)\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, Operations, 2), ERANGE);\r\n        LXT_SYNCHRONIZATION_POINT(); // (5)\r\n\r\n        LXT_SYNCHRONIZATION_POINT(); // (6)\r\n        memset(Operations, 0, sizeof(Operations));\r\n        Operations[0].sem_op = 32767;\r\n        Operations[1].sem_op = -1;\r\n        Operations[2].sem_op = 2;\r\n        Operations[3].sem_op = -1;\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, Operations, 4), ERANGE);\r\n        LXT_SYNCHRONIZATION_POINT(); // (7)\r\n\r\n        memset(Operations, 0, sizeof(Operations));\r\n        Operations[0].sem_op = -1;\r\n        Operations[1].sem_op = 32767;\r\n        Operations[2].sem_op = 1;\r\n        LXT_SYNCHRONIZATION_POINT(); // (8)\r\n        LxtLogInfo(\"child semop\");\r\n        LxtCheckErrnoFailure(LxtSemOp(Id, Operations, 4), ERANGE);\r\n        LxtLogInfo(\"child return\");\r\n        LXT_SYNCHRONIZATION_POINT(); // (9)\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to do the first semop and query the values.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT(); // (1)\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, GETALL, &Values));\r\n    LxtCheckEqual(1, Values[0], \"%u\");\r\n    LxtCheckEqual(3, Values[1], \"%u\");\r\n    LxtCheckEqual(3, Values[3], \"%u\");\r\n    memset(Values, 0, sizeof(Values));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SETALL, &Values));\r\n    LXT_SYNCHRONIZATION_POINT(); // (2)\r\n\r\n    LXT_SYNCHRONIZATION_POINT(); // (3)\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, GETALL, &Values));\r\n    LxtCheckEqual(32767, Values[0], \"%u\");\r\n    memset(Values, 0, sizeof(Values));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SETALL, &Values));\r\n    LXT_SYNCHRONIZATION_POINT(); // (4)\r\n\r\n    LXT_SYNCHRONIZATION_POINT(); // (5)\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, GETALL, &Values));\r\n    LxtCheckEqual(0, Values[0], \"%u\");\r\n\r\n    LXT_SYNCHRONIZATION_POINT(); // (6)\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, GETALL, &Values));\r\n    LxtCheckEqual(0, Values[0], \"%u\");\r\n    memset(Values, 0, sizeof(Values));\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, SETALL, &Values));\r\n    LXT_SYNCHRONIZATION_POINT(); // (7)\r\n\r\n    LXT_SYNCHRONIZATION_POINT(); // (8)\r\n    Operations[0].sem_num = 0;\r\n    Operations[0].sem_op = 1;\r\n    sleep(1);\r\n    LxtCheckErrno(LxtSemOp(Id, Operations, 1));\r\n    LXT_SYNCHRONIZATION_POINT(); // (9)\r\n    LxtCheckErrno(LxtSemCtl(Id, 0, GETALL, &Values));\r\n    LxtCheckEqual(1, Values[0], \"%u\");\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtSemOp(Id, NULL, 0), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemOp(Id, NULL, 501), E2BIG);\r\n    LxtCheckErrnoFailure(LxtSemOp(Id, NULL, 1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemOp(Id, -1, 1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemOp(-1, NULL, 0), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemOp(-1, NULL, 1), EINVAL);\r\n\r\n    LxtCheckErrnoFailure(LxtSemTimedOp(Id, NULL, 0, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemTimedOp(Id, NULL, 501, NULL), E2BIG);\r\n    LxtCheckErrnoFailure(LxtSemTimedOp(Id, NULL, 1, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemTimedOp(Id, -1, 1, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemTimedOp(Id, Operations, 1, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtSemTimedOp(-1, NULL, 0, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtSemTimedOp(-1, NULL, 1, -1), EINVAL);\r\n    Timeout.tv_sec = 0;\r\n    Timeout.tv_nsec = 999999999 + 1;\r\n    LxtCheckErrnoFailure(LxtSemTimedOp(Id, Operations, 1, &Timeout), EINVAL);\r\n    Timeout.tv_sec = -1;\r\n    Timeout.tv_nsec = 0;\r\n    LxtCheckErrnoFailure(LxtSemTimedOp(Id, Operations, 1, &Timeout), EINVAL);\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtSemCtl(Id, 0, IPC_RMID, NULL);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid SemPrintInfo(struct semid_ds* Stat)\r\n\r\n{\r\n\r\n    if (g_VerboseSem == false)\r\n    {\r\n        return;\r\n    }\r\n\r\n    LxtLogInfo(\"sem_perm.__key %u\", Stat->sem_perm.__key);\r\n    LxtLogInfo(\"sem_perm.uid %u\", Stat->sem_perm.uid);\r\n    LxtLogInfo(\"sem_perm.gid %u\", Stat->sem_perm.gid);\r\n    LxtLogInfo(\"sem_perm.cuid %u\", Stat->sem_perm.cuid);\r\n    LxtLogInfo(\"sem_perm.cgid %u\", Stat->sem_perm.cgid);\r\n    LxtLogInfo(\"sem_perm.mode %o\", Stat->sem_perm.mode);\r\n    LxtLogInfo(\"sem_perm.__seq %d\", Stat->sem_perm.__seq);\r\n    LxtLogInfo(\"sem_otime %Iu\", Stat->sem_otime);\r\n    LxtLogInfo(\"sem_ctime %Iu\", Stat->sem_ctime);\r\n    LxtLogInfo(\"sem_nsems %Iu\", Stat->sem_nsems);\r\n    return;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/shm.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    shm.c\r\n\r\nAbstract:\r\n\r\n    This file is a test for the system V shared memory family of system calls.\r\n\r\n--*/\r\n\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/xattr.h>\r\n#include <sys/mman.h>\r\n#include <fcntl.h>\r\n#include <sys/socket.h>\r\n#include <sys/un.h>\r\n#include <sys/ipc.h>\r\n#include <sys/shm.h>\r\n#include <sys/prctl.h>\r\n#include <sys/wait.h>\r\n#include <grp.h>\r\n#include <netinet/in.h>\r\n#include <netdb.h>\r\n#include <time.h>\r\n#include <linux/random.h>\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n\r\n#include <sys/capability.h>\r\n\r\n#else\r\n\r\n#include <sys/cdefs.h>\r\n#include <linux/capability.h>\r\n\r\n#define _LINUX_CAPABILITY_VERSION_3 0x20080522\r\n\r\n#ifndef O_PATH\r\n#define O_PATH 010000000\r\n#endif\r\n\r\n#endif\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n\r\n#define LXT_NAME \"shm\"\r\n\r\n#define SHM_ACCESS_UID 1004\r\n#define SHM_ACCESS_GID 1004\r\n\r\n//\r\n// Globals.\r\n//\r\n\r\nbool g_RunningOnNative = false;\r\nbool g_VerboseShm = false;\r\n\r\nint ShmAtAccess(PLXT_ARGS Args);\r\n\r\nint ShmAtDtSyscall(PLXT_ARGS Args);\r\n\r\nint ShmCtlSyscall(PLXT_ARGS Args);\r\n\r\nint ShmGetAccess(PLXT_ARGS Args);\r\n\r\nint ShmGetSyscall(PLXT_ARGS Args);\r\n\r\nint ShmPidNamespace(PLXT_ARGS Args);\r\n\r\nvoid ShmPrintInfo(struct shmid_ds* Stat);\r\n\r\nvoid ShmPrintInfoAttach(struct shmid_ds* Stat);\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"shmget syscall\", ShmGetSyscall},\r\n    {\"shmget access\", ShmGetAccess},\r\n    {\"shmctl syscall\", ShmCtlSyscall},\r\n    {\"shmat / shmdt syscalls\", ShmAtDtSyscall},\r\n    {\"shmat access\", ShmAtAccess},\r\n    {\"shm pid namespace\", ShmPidNamespace}};\r\n\r\nint ShmTestEntry(int Argc, char* Argv[])\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return 0;\r\n}\r\n\r\nint ShmAtAccess(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    unsigned char* Address;\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int Id;\r\n    void* MapResult;\r\n    int Result;\r\n\r\n    Address = NULL;\r\n    ChildPid = -1;\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a shared memory region that should be unmappable by a process\r\n    // without the CAP_IPC_OWNER capability.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and attempt to map again (should fail).\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        Address = LxtShmAt(Id, NULL, 0);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a read only memory region and verify that it is only mappable as\r\n    // read only by the owner.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0400));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and attempt to with the readonly\r\n        // flag.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Attempt to map as read / write (should fail).\r\n        //\r\n\r\n        Address = LxtShmAt(Id, NULL, 0);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Attempt to map as execute (should fail).\r\n        //\r\n\r\n        Address = LxtShmAt(Id, NULL, SHM_EXEC);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Id = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a group read only memory region and verify that it is only\r\n    // mappable by members of the same group.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0040));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and attempt to with the readonly\r\n        // flag.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Attempt to map as read / write (should fail).\r\n        //\r\n\r\n        Address = LxtShmAt(Id, NULL, 0);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Attempt to map as execute (should fail).\r\n        //\r\n\r\n        Address = LxtShmAt(Id, NULL, SHM_EXEC);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create another read only memory region and verify that it is\r\n    // mappable.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0004));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Remove all group membership, drop the CAP_IPC_OWNER capability, and\r\n        // attempt to with the readonly flag.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Attempt to map as read / write (should fail).\r\n        //\r\n\r\n        Address = LxtShmAt(Id, NULL, 0);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Attempt to map as execute (should fail).\r\n        //\r\n\r\n        Address = LxtShmAt(Id, NULL, SHM_EXEC);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a shared memory region that is write only This should be\r\n    // unmappable by processes without the CAP_IPC_OWNER capability.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0222));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and attempt to map again (should fail).\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        Address = LxtShmAt(Id, NULL, SHM_RDONLY);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Address = LxtShmAt(Id, NULL, 0);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Attempt to map as execute (should fail).\r\n        //\r\n\r\n        Address = LxtShmAt(Id, NULL, SHM_EXEC);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a shared memory region that can only be read or written by the\r\n    // owner.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0700));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and attempt to map (should fail).\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Change the UID and verify the mapping fails.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        Address = LxtShmAt(Id, NULL, SHM_RDONLY);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Address = LxtShmAt(Id, NULL, 0);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a shared memory region that is only mappable by other.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0007));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Verify the region is mappable with CAP_IPC_OWNER.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and attempt to map again this\r\n        // should fail because the caller is still has a matching UID.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        Address = LxtShmAt(Id, NULL, SHM_RDONLY);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Change the UID and attempt to map, this should still fail because\r\n        // the caller has group ownership.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        Address = LxtShmAt(Id, NULL, SHM_RDONLY);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Change the caller GID and attempt to map, this should still fail\r\n        // because the caller has a supplementary group membership.\r\n        //\r\n\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        Address = LxtShmAt(Id, NULL, SHM_RDONLY);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Drop supplementary group membership, finally this should succeed.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_EXEC));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY | SHM_EXEC));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Create a shared memory region that is only mappable as read / execute.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0555));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and try to map read / write and\r\n        // read / write / execute (should fail).\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        Address = LxtShmAt(Id, NULL, 0);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Address = LxtShmAt(Id, NULL, SHM_EXEC);\r\n        if (Address != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpectedly able to shmat\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Map the region as readonly, read / execute.\r\n        //\r\n\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n        LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY | SHM_EXEC));\r\n        LxtCheckErrno(LxtShmDt(Address));\r\n        Address = NULL;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\nErrorExit:\r\n    if (Address != NULL)\r\n    {\r\n        LxtShmDt(Address);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    //\r\n    // N.B. The identifier should not be removed by any child processes.\r\n    //\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtShmCtl(Id, IPC_RMID, NULL);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint ShmAtDtSyscall(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    unsigned char* Address;\r\n    unsigned char* Address2;\r\n    int ChildPid;\r\n    int Id;\r\n    key_t Key;\r\n    void* MapResult;\r\n    struct shmid_ds ParentStat;\r\n    unsigned char* RemappedMemory;\r\n    size_t Result;\r\n    struct shmid_ds Stat;\r\n\r\n    Address = NULL;\r\n    Address2 = NULL;\r\n    ChildPid = -1;\r\n    Id = -1;\r\n\r\n    //\r\n    // (1) Create a shared memory region.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE * 3, 0));\r\n    LxtLogInfo(\"Id = %d\", Id);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &ParentStat));\r\n    LxtCheckEqual(PAGE_SIZE * 3, ParentStat.shm_segsz, \"%Iu\");\r\n    LxtCheckEqual(0, ParentStat.shm_atime, \"%Iu\");\r\n    LxtCheckEqual(0, ParentStat.shm_dtime, \"%Iu\");\r\n    LxtCheckNotEqual(0, ParentStat.shm_ctime, \"%Iu\");\r\n    LxtCheckEqual(ParentStat.shm_nattch, 0, \"%Iu\");\r\n\r\n    //\r\n    // Map the shared memory region.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n    LxtLogInfo(\"Address = %p\", Address);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &ParentStat));\r\n    ShmPrintInfoAttach(&ParentStat);\r\n    LxtCheckNotEqual(0, ParentStat.shm_atime, \"%Iu\");\r\n    LxtCheckEqual(0, ParentStat.shm_dtime, \"%Iu\");\r\n    LxtCheckEqual(ParentStat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckEqual(getpid(), ParentStat.shm_lpid, \"%Iu\");\r\n\r\n    //\r\n    // Sleep for 2 seconds then fork and verify that attach statics are\r\n    // updated correctly. The attach count and attach time should be updated\r\n    // but the last attach pid should not change.\r\n    //\r\n\r\n    sleep(2);\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n        LxtCheckEqual(Stat.shm_nattch, 2, \"%Iu\");\r\n        LxtCheckEqual(ParentStat.shm_lpid, Stat.shm_lpid, \"%Iu\");\r\n        LxtCheckNotEqual(ParentStat.shm_atime, Stat.shm_atime, \"%Iu\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckNotEqual(0, Stat.shm_dtime, \"%Iu\");\r\n\r\n    //\r\n    // Attempt to map the region in an area that already is mapped.\r\n    //\r\n\r\n    Address2 = LxtShmAt(Id, Address, 0);\r\n    if (Address2 != MAP_FAILED)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"shmat on a used region should fail without SHM_REMAP flag %p %d\", Address2, errno);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Address2 = LxtShmAt(Id, Address + PAGE_SIZE, 0);\r\n    if (Address2 != MAP_FAILED)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"shmat on a used region should fail without SHM_REMAP flag %p %d\", Address2, errno);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Address2 = LxtShmAt(Id, Address + (PAGE_SIZE * 2), 0);\r\n    if (Address2 != MAP_FAILED)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"shmat on a used region should fail without SHM_REMAP flag %p %d\", Address2, errno);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (g_RunningOnNative == false)\r\n    {\r\n        LxtLogInfo(\"WARNING: these variations are expected to fail on native Ubuntu\");\r\n        Address2 = LxtShmAt(Id, Address, SHM_REMAP);\r\n        if (Address2 != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"shmat with SHM_REMAP replacing entire region\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Address2 = LxtShmAt(Id, Address + PAGE_SIZE, SHM_REMAP);\r\n        if (Address2 != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"shmat with SHM_REMAP replacing last two pages.\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Unmap the first page in the range.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(munmap(Address, PAGE_SIZE), EINVAL);\r\n\r\n        //\r\n        // Unmap the middle page of the three-page range.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(munmap(Address + PAGE_SIZE, PAGE_SIZE), EINVAL);\r\n\r\n        //\r\n        // Unmap the last page in the range.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(munmap(Address + (2 * PAGE_SIZE), PAGE_SIZE), EINVAL);\r\n\r\n        //\r\n        // Use the remap system call to resize the region.\r\n        //\r\n\r\n        RemappedMemory = LxtMremap(Address, PAGE_SIZE * 3, PAGE_SIZE * 4, MREMAP_MAYMOVE, NULL);\r\n\r\n        if (RemappedMemory != MAP_FAILED)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"mremap moving the region.\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Use the SHM_REMAP flag to replace the entire region.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address2 = LxtShmAt(Id, Address, SHM_REMAP));\r\n    LxtCheckEqual(Address, Address2, \"%p\");\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckErrno(LxtShmDt(Address));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 0, \"%Iu\");\r\n    LxtCheckErrnoFailure(LxtShmDt(Address), EINVAL);\r\n\r\n    //\r\n    // Use the SHM_REMAP flag to replace the last two pages of the original\r\n    // region.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, Address, 0));\r\n    LxtCheckMapErrno(Address2 = LxtShmAt(Id, Address + PAGE_SIZE, SHM_REMAP));\r\n    LxtCheckEqual(Address + PAGE_SIZE, Address2, \"%p\");\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 2, \"%Iu\");\r\n    LxtCheckErrno(LxtShmDt(Address));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckErrno(LxtShmDt(Address2));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 0, \"%Iu\");\r\n    LxtCheckErrnoFailure(LxtShmDt(Address), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmDt(Address2), EINVAL);\r\n    Address = NULL;\r\n    Address2 = NULL;\r\n\r\n    //\r\n    // Unmap the middle page of the three-page range to split the region,\r\n    // this should increment the attach count.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n    LxtCheckErrno(munmap(Address + PAGE_SIZE, PAGE_SIZE));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 2, \"%Iu\");\r\n    LxtCheckNotEqual(0, Stat.shm_dtime, \"%Iu\");\r\n\r\n    //\r\n    // Unmap the last page in the range.\r\n    //\r\n\r\n    LxtCheckErrno(munmap(Address + (2 * PAGE_SIZE), PAGE_SIZE));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n\r\n    //\r\n    // Use detach to clear the range.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmDt(Address));\r\n    Address = NULL;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 0, \"%Iu\");\r\n\r\n    //\r\n    // (2) Map the region again. Unmap the middle page of the three-page range.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n    LxtLogInfo(\"Address = %p\", Address);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckErrno(munmap(Address + PAGE_SIZE, PAGE_SIZE));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 2, \"%Iu\");\r\n\r\n    //\r\n    // Use shmdt to remove both remaining mapped regions, this should unmap\r\n    // both attached regions.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmDt(Address));\r\n    Address = NULL;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 0, \"%Iu\");\r\n\r\n    //\r\n    // (3) Use the remap system call to resize the region.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n    LxtLogInfo(\"Address = %p\", Address);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckMapErrno(RemappedMemory = LxtMremap(Address, PAGE_SIZE * 3, PAGE_SIZE * 4, MREMAP_MAYMOVE, NULL));\r\n\r\n    LxtLogInfo(\"RemappedMemory = %p\", RemappedMemory);\r\n\r\n    //\r\n    // If the address changed, attempt to remap the old address.\r\n    //\r\n\r\n    if (Address != RemappedMemory)\r\n    {\r\n        LxtCheckErrnoFailure(LxtShmDt(Address), EINVAL);\r\n        Address = RemappedMemory;\r\n    }\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n\r\n    //\r\n    // Unmap the middle two pages in the range.\r\n    //\r\n\r\n    LxtCheckErrno(munmap(Address + PAGE_SIZE, (2 * PAGE_SIZE)));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 2, \"%Iu\");\r\n\r\n    //\r\n    // Unmap the first page in the range.\r\n    //\r\n\r\n    LxtCheckErrno(munmap(Address, PAGE_SIZE));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n\r\n    //\r\n    // Use shmdt to remove the remaining region (the last page in the range).\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmDt(Address));\r\n    Address = NULL;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 0, \"%Iu\");\r\n\r\n    //\r\n    // (4) Map the region again. Use the mremap system call to shrink the\r\n    // region and validate that the global shared memory region remains the\r\n    // same size.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n    LxtLogInfo(\"Address = %p\", Address);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckEqual(Stat.shm_segsz, (PAGE_SIZE * 3), \"%Iu\");\r\n    LxtCheckMapErrno(RemappedMemory = LxtMremap(Address, PAGE_SIZE * 3, PAGE_SIZE, 0, NULL));\r\n\r\n    LxtLogInfo(\"RemappedMemory = %p\", RemappedMemory);\r\n    LxtCheckEqual(Address, RemappedMemory, \"%p\");\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckEqual(Stat.shm_segsz, PAGE_SIZE * 3, \"%Iu\");\r\n\r\n    //\r\n    // Use shmdt to remove the region.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmDt(Address));\r\n    Address = NULL;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 0, \"%Iu\");\r\n\r\n    //\r\n    // (5) Map the region twice.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n    LxtLogInfo(\"Address = %p\", Address);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n\r\n    LxtCheckMapErrno(Address2 = LxtShmAt(Id, NULL, 0));\r\n    LxtLogInfo(\"Address2 = %p\", Address2);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 2, \"%Iu\");\r\n\r\n    //\r\n    // Ensure the shared memory region were mapped to different locations and\r\n    // detach both.\r\n    //\r\n\r\n    LxtCheckNotEqual(Address, Address2, \"%p\");\r\n    LxtCheckErrno(LxtShmDt(Address2));\r\n    Address2 = NULL;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckErrno(LxtShmDt(Address));\r\n    Address = NULL;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 0, \"%Iu\");\r\n\r\n    //\r\n    // (6) Map the region, delete the region, and validate that the region is\r\n    // still able to be mapped.\r\n    //\r\n\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n\r\n    //\r\n    // Delete the region again (should succeed).\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    Address[0] = 'a';\r\n    LxtCheckMapErrno(Address2 = LxtShmAt(Id, NULL, 0));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    Address2[0] = 'a';\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 2, \"%Iu\");\r\n\r\n    //\r\n    // Detach both mapped regions.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmDt(Address));\r\n    Address = NULL;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfoAttach(&Stat);\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n    LxtCheckErrno(LxtShmDt(Address2));\r\n    Address2 = NULL;\r\n\r\n    //\r\n    // The region should be deleted at this point to the shmctl should fail.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EINVAL);\r\n    Id = -1;\r\n\r\n    //\r\n    // (7) Delete the shared memory region and attempt to attach it afterwards.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE * 3, 0));\r\n    LxtLogInfo(\"Id = %d\", Id);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 0, \"%Iu\");\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Address = LxtShmAt(Id, NULL, 0);\r\n    if ((Address != MAP_FAILED) && (errno != EINVAL))\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"unexpectedly able to attach deleted memory region %p, %d\", Address, errno);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Attempt to stat the deleted region.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EINVAL);\r\n    Id = -1;\r\n\r\n    //\r\n    // (8) Use mremap to move the last page to a new location.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE * 3, 0));\r\n    LxtLogInfo(\"Id = %d\", Id);\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n    LxtLogInfo(\"Address = %p\", Address);\r\n    LxtCheckMapErrno(Address2 = LxtMremap(Address + (2 * PAGE_SIZE), PAGE_SIZE, PAGE_SIZE * 4, MREMAP_MAYMOVE, NULL));\r\n\r\n    LxtLogInfo(\"Address2 = %p\", Address2);\r\n    LxtCheckNotEqual(Address + (2 * PAGE_SIZE), Address2, \"%p\");\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 2, \"%Iu\");\r\n\r\n    //\r\n    // Detach the original address and validate the second region remains.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmDt(Address));\r\n    Address = NULL;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n\r\n    //\r\n    // Ensure that shmdt does not work for the new address.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtShmDt(Address2), EINVAL);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 1, \"%Iu\");\r\n\r\n    //\r\n    // Call shmdt on what would have been the start of the new region.\r\n    //\r\n    // N.B. This functions like a new mapping of the memory where the first two\r\n    //      pages have been unmapped.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmDt(Address2 - (2 * PAGE_SIZE)));\r\n    Address2 = NULL;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_nattch, 0, \"%Iu\");\r\n\r\nErrorExit:\r\n    if (Address != NULL)\r\n    {\r\n        LxtShmDt(Address);\r\n    }\r\n\r\n    if (Address2 != NULL)\r\n    {\r\n        LxtShmDt(Address2);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtShmCtl(Id, IPC_RMID, NULL);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint ShmGetAccess(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int Id;\r\n    key_t Key;\r\n    int Mode;\r\n    int Result;\r\n\r\n    ChildPid = -1;\r\n    Id = -1;\r\n\r\n    LxtCheckErrno(LxtGetrandom(&Key, sizeof(Key), 0));\r\n    LxtLogInfo(\"Key = %u\", Key);\r\n\r\n    //\r\n    // Create a shared memory region with a mode of all zeros.\r\n    //\r\n\r\n    Mode = 0000;\r\n    LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First attempt with the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the UID.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the GID.\r\n        //\r\n\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Drop supplementary group membership.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a shared memory region with a user read / write / execute mode.\r\n    //\r\n\r\n    Mode = 0700;\r\n    LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First attempt with the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0700), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0070), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0007), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0124), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the UID.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the GID.\r\n        //\r\n\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Drop supplementary group membership.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a shared memory region with a group read / write / execute mode.\r\n    //\r\n\r\n    Mode = 0070;\r\n    LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First attempt with the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the UID (group still matches so this should succeed).\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0700), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0070), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0007), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0124), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the GID (callers still has supplementary group membership so\r\n        // this should succeed).\r\n        //\r\n\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0700), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0070), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0007), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0124), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Drop supplementary group membership.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a shared memory region with a other read / write / execute mode.\r\n    //\r\n\r\n    Mode = 0007;\r\n    LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First attempt with the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the UID.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the GID (callers still has supplementary group membership so\r\n        // this should succeed).\r\n        //\r\n\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Drop supplementary group membership (this should succeed).\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0700), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0070), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0007), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0124), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a shared memory region with a other read / write mode.\r\n    //\r\n\r\n    Mode = 0006;\r\n    LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First attempt with the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0006), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0004), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0002), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0001), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the UID.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0006), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0004), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0002), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0001), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Change the GID (callers still has supplementary group membership so\r\n        // this should succeed).\r\n        //\r\n\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0006), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0004), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0002), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0001), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n\r\n        //\r\n        // Drop supplementary group membership (this should succeed).\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES);\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0666), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0600), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0060), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0006), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0024), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0424), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0024), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), \"%Iu\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n    Id = -1;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    //\r\n    // N.B. The identifier should not be removed by any child processes.\r\n    //\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtShmCtl(Id, IPC_RMID, NULL);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint ShmGetSyscall(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int Id;\r\n    key_t Key;\r\n    int Mode;\r\n    size_t Result;\r\n    struct shmid_ds Stat;\r\n    time_t Time;\r\n\r\n    ChildPid = -1;\r\n    Id = -1;\r\n\r\n    //\r\n    // Create a key, verify that creating the key with the IPC_EXCL flag fails.\r\n    //\r\n\r\n    Mode = 0000;\r\n    LxtLogInfo(\"Mode %o\", Mode);\r\n    LxtCheckErrno(LxtGetrandom(&Key, sizeof(Key), 0));\r\n    LxtLogInfo(\"Key = %u\", Key);\r\n    LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode)));\r\n    LxtLogInfo(\"Id = %d\", Id);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    ShmPrintInfo(&Stat);\r\n    LxtCheckEqual(Key, Stat.shm_perm.__key, \"%Iu\");\r\n    LxtCheckEqual(PAGE_SIZE, Stat.shm_segsz, \"%Iu\");\r\n    LxtCheckEqual(getpid(), Stat.shm_cpid, \"%Iu\");\r\n    LxtCheckEqual(0, Stat.shm_lpid, \"%Iu\");\r\n    LxtCheckEqual(0, Stat.shm_atime, \"%Iu\");\r\n    LxtCheckEqual(0, Stat.shm_dtime, \"%Iu\");\r\n    LxtCheckNotEqual(0, Stat.shm_ctime, \"%Iu\");\r\n    LxtCheckEqual(Mode, Stat.shm_perm.mode, \"%o\");\r\n    LxtCheckEqual(getuid(), Stat.shm_perm.cuid, \"%d\");\r\n    LxtCheckEqual(getuid(), Stat.shm_perm.uid, \"%d\");\r\n    LxtCheckEqual(getgid(), Stat.shm_perm.cgid, \"%d\");\r\n    LxtCheckEqual(getgid(), Stat.shm_perm.gid, \"%d\");\r\n\r\n    //\r\n    // shmget with IPC_CREAT or IPC_EXCL when the region already exists.\r\n    //\r\n\r\n    LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_CREAT), \"%Iu\");\r\n    LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_EXCL), \"%Iu\");\r\n    LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0), \"%Iu\");\r\n\r\n    //\r\n    // Create a child with a different uid and gid that does not have the\r\n    // IPC_OWNER capability.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // These should succeed because the child still has the IPC_OWNER cap.\r\n        //\r\n\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_CREAT), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_EXCL), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0666), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0600), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0060), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0006), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0), \"%Iu\");\r\n\r\n        //\r\n        // Drop all group membership and the CAP_IPC_OWNER capability and\r\n        // attempt to call shmget with unmatching mode bits.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0666), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0600), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0060), EACCES);\r\n        LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0006), EACCES);\r\n\r\n        //\r\n        // Use the same permission as before, these should succeed.\r\n        //\r\n\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_CREAT), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_EXCL), \"%Iu\");\r\n        LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0), \"%Iu\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // shmget with size = 0 should succeed.\r\n    //\r\n\r\n    LxtCheckEqual(Id, LxtShmGet(Key, 0, 0), \"%Iu\");\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    //\r\n    // shmget with IPC_CREAT | IPC_EXCL when the region already exists, should\r\n    // succeed with only IPC_EXCL.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL)), EEXIST);\r\n\r\n    //\r\n    // shmget with a known key and a size that does not match.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtShmGet(Key, (PAGE_SIZE * 2), 0), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE + 1, 0), EINVAL);\r\n\r\n    //\r\n    // N.B. There appears to be no error checking for invalid flags, only the\r\n    //      presence of valid flags.\r\n    //\r\n    // -1 includes the IPC_EXCL flag so this should return EEXIST.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, -1), EEXIST);\r\n    LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, (-1 & ~IPC_EXCL)), \"%Iu\");\r\n\r\n    //\r\n    // Delete the region and create a new one with a size of one byte.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, &Stat));\r\n    Id = -1;\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, 1, 0));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(1, Stat.shm_segsz, \"%Iu\");\r\n\r\n    //\r\n    // Delete the region and create a new region with a size of zero bytes\r\n    // (should fail).\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, &Stat));\r\n    Id = -1;\r\n    LxtCheckErrnoFailure(Id = LxtShmGet(IPC_PRIVATE, 0, 0), EINVAL);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtShmCtl(Id, IPC_RMID, &Stat);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint ShmCtlSyscall(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int Id;\r\n    struct shminfo IpcInfo;\r\n    key_t Key;\r\n    struct shmid_ds OldStat;\r\n    int RandomId;\r\n    size_t Result;\r\n    struct shm_info ShmInfo;\r\n    struct shmid_ds Stat = {0};\r\n\r\n    Id = -1;\r\n\r\n    //\r\n    // Test permissions for the IPC_STAT.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First attempt with the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and verify that the region cannot\r\n        // be queried.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EACCES);\r\n\r\n        //\r\n        // Create a no access shared memory region and verify that it cannot be\r\n        // queried without the CAP_IPC_OWNER (even by its owner).\r\n        //\r\n\r\n        LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0));\r\n        LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EACCES);\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n\r\n        //\r\n        // Create a write only shared memory region and verify that it cannot be\r\n        // queried without the CAP_IPC_OWNER (even by its owner).\r\n        //\r\n\r\n        LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0200));\r\n        LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EACCES);\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n\r\n        //\r\n        // Create a read only shared memory region and verify that it can be\r\n        // queried.\r\n        //\r\n\r\n        LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0400));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Test permissions for IPC_SET.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First attempt with the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and verify that IPC_SET can still\r\n        // be called by the owner.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Change the GID.\r\n        //\r\n\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Drop supplementary group membership.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Change the UID (this should fail).\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, NULL), EFAULT);\r\n        LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, &Stat), EPERM);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Test permissions for IPC_SET.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First attempt with the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability and verify that IPC_SET can still\r\n        // be called by the creator and owner.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Change the owner UID.\r\n        //\r\n\r\n        Stat.shm_perm.uid = SHM_ACCESS_UID;\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Change the GID.\r\n        //\r\n\r\n        LxtCheckErrno(setgid(SHM_ACCESS_GID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Drop supplementary group membership.\r\n        //\r\n\r\n        LxtCheckErrno(Result = setgroups(0, NULL));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Change the UID to match (this should succeed).\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // IPC_STAT should still fail.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EACCES);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Reset the region's UID.\r\n    //\r\n\r\n    Stat.shm_perm.uid = getuid();\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n    //\r\n    // Test permissions for SHM_LOCK / SHM_UNLOCK.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Drop the CAP_IPC_LOCK capability.\r\n        //\r\n\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapGet(&CapHeader, CapData)) LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        CapData[CAP_TO_INDEX(CAP_IPC_LOCK)].permitted &= ~CAP_TO_MASK(CAP_IPC_LOCK);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Change the UID and verify SHM_LOCK and SHM_UNLOCK fail.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmCtl(Id, SHM_LOCK, NULL), EPERM);\r\n        LxtCheckErrnoFailure(LxtShmCtl(Id, SHM_UNLOCK, NULL), EPERM);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Test permissions for IPC_RMID.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Change the UID and verify IPC_RMID fails.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_RMID, NULL), EPERM);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Verify IPC_RMID can be called by the memory region's owner.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Drop the CAP_IPC_OWNER capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n        CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n        //\r\n        // Change the owner UID.\r\n        //\r\n\r\n        Stat.shm_perm.uid = SHM_ACCESS_UID;\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n\r\n        //\r\n        // Change the caller's UID to match.\r\n        //\r\n\r\n        LxtCheckErrno(setuid(SHM_ACCESS_UID));\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Create a new shared memory region since the previous was just deleted.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0));\r\n\r\n    //\r\n    // Verify IPC_INFO.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_INFO, &IpcInfo));\r\n    LxtCheckErrno(LxtShmCtl(0, IPC_INFO, &IpcInfo));\r\n    LxtLogInfo(\"shminfo.shmmax %Iu\", IpcInfo.shmmax);\r\n    LxtLogInfo(\"shminfo.shmmin %Iu\", IpcInfo.shmmin);\r\n    LxtLogInfo(\"shminfo.shmmni %Iu\", IpcInfo.shmmni);\r\n    LxtLogInfo(\"shminfo.shmseg %Iu\", IpcInfo.shmseg);\r\n    LxtLogInfo(\"shminfo.shmall %Iu\", IpcInfo.shmall);\r\n    LxtCheckEqual(IpcInfo.shmmin, 1, \"%Iu\");\r\n\r\n    //\r\n    // Verify SHM_INFO.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, SHM_INFO, &ShmInfo));\r\n    LxtCheckErrno(LxtShmCtl(0, SHM_INFO, &ShmInfo));\r\n    LxtLogInfo(\"shm_info.used_ids %Iu\", ShmInfo.used_ids);\r\n    LxtLogInfo(\"shm_info.shm_tot %Iu\", ShmInfo.shm_tot);\r\n    LxtLogInfo(\"shm_info.shm_rss %Iu\", ShmInfo.shm_rss);\r\n    LxtLogInfo(\"shm_info.shm_swp %Iu\", ShmInfo.shm_swp);\r\n    LxtLogInfo(\"shm_info.swap_attempts %Iu\", ShmInfo.swap_attempts);\r\n    LxtLogInfo(\"shm_info.swap_successes %Iu\", ShmInfo.swap_successes);\r\n    LxtCheckNotEqual(ShmInfo.used_ids, 0, \"%Iu\");\r\n\r\n    //\r\n    // Verify SHM_LOCK and SHM_UNLOCK. The locked state is boolean (there is\r\n    // no count for locked / unlocked).\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, SHM_LOCK, NULL));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(SHM_LOCKED, Stat.shm_perm.mode & SHM_LOCKED, \"%o\");\r\n    LxtCheckErrno(LxtShmCtl(Id, SHM_LOCK, NULL));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(SHM_LOCKED, Stat.shm_perm.mode & SHM_LOCKED, \"%o\");\r\n    LxtCheckErrno(LxtShmCtl(Id, SHM_UNLOCK, NULL));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(0, Stat.shm_perm.mode & SHM_LOCKED, \"%o\");\r\n    LxtCheckErrno(LxtShmCtl(Id, SHM_UNLOCK, NULL));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(0, Stat.shm_perm.mode & SHM_LOCKED, \"%o\");\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    //\r\n    // Ensure IPC_SET cannot set invalid mode bits (they are silently ignored).\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    Stat.shm_perm.mode = -1;\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_perm.mode, 0777, \"%o\");\r\n\r\n    //\r\n    // Ensure the uid and gid cannot be set to -1.\r\n    //\r\n\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &OldStat));\r\n    Stat = OldStat;\r\n    Stat.shm_perm.uid = -1;\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, &Stat), EINVAL);\r\n    Stat = OldStat;\r\n    Stat.shm_perm.gid = -1;\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, &Stat), EINVAL);\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n    LxtCheckEqual(Stat.shm_perm.uid, OldStat.shm_perm.uid, \"%d\");\r\n    LxtCheckEqual(Stat.shm_perm.gid, OldStat.shm_perm.gid, \"%d\");\r\n\r\n    LxtCheckErrnoFailure(LxtShmCtl(-1, IPC_STAT, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtShmCtl(-1, IPC_SET, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtShmCtl(-1, IPC_INFO, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_INFO, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_INFO, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtShmCtl(-1, SHM_INFO, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, SHM_INFO, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtShmCtl(Id, SHM_INFO, -1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtShmCtl(-1, SHM_LOCK, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmCtl(-1, SHM_UNLOCK, NULL), EINVAL);\r\n\r\n    //\r\n    // Generate an ID that does not refer to a valid memory region and attempt\r\n    // operations on the nonexistent region.\r\n    //\r\n\r\n    do\r\n    {\r\n        LxtCheckErrno(LxtGetrandom(&RandomId, sizeof(RandomId), 0));\r\n        Result = LxtShmCtl(RandomId, IPC_STAT, &Stat);\r\n    } while ((Result == 0) && (errno != EINVAL));\r\n\r\n    LxtCheckErrnoFailure(LxtShmCtl(RandomId, IPC_RMID, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmCtl(RandomId, IPC_STAT, &Stat), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmCtl(RandomId, IPC_SET, &Stat), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmCtl(RandomId, SHM_LOCK, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtShmCtl(RandomId, SHM_UNLOCK, NULL), EINVAL);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtShmCtl(Id, IPC_RMID, NULL);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint ShmPidNamespaceWork(void)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the behavior of System V shared memory across IPC\r\n    namespaces. A child threadgroup is forked into a new IPC namespace,\r\n\r\n\r\n    and the parent and\r\n    child communicate across a unix socket connection. Each side queries the\r\n    credentials of the other side via SO_PEERCRED and ancillary messages and\r\n    validates that the appropriate credentials are returned.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    void* Address;\r\n    void* Address2;\r\n    pid_t ChildPid = 0;\r\n    int Id;\r\n    void* MapResult;\r\n    pid_t ParentPid;\r\n    struct shmid_ds ParentStat;\r\n    int Result;\r\n    struct shmid_ds Stat;\r\n    int Status;\r\n\r\n    Address = NULL;\r\n    Address2 = NULL;\r\n    Id = -1;\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n\r\n    //\r\n    // Create and map a shared memory region.\r\n    //\r\n\r\n    LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0));\r\n    LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0));\r\n    LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &ParentStat));\r\n\r\n    //\r\n    // Unshare the PID namespace used for children.\r\n    //\r\n\r\n    LxtLogInfo(\"Unsharing CLONE_NEWPID\");\r\n    LxtCheckErrno(unshare(CLONE_NEWPID));\r\n\r\n    //\r\n    // Fork a child that will exist in a new IPC namespace.\r\n    //\r\n\r\n    ParentPid = getpid();\r\n    LxtLogInfo(\"ParentPid %d\", ParentPid);\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        LxtLogInfo(\"Child's view of ChildPid %d\", getpid());\r\n\r\n        //\r\n        // Attach the shared segment.\r\n        //\r\n\r\n        LxtCheckMapErrno(Address2 = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n        LxtCheckEqual(getpid(), Stat.shm_lpid, \"%d\");\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Wait for the parent to query credentials.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n        LxtCheckEqual(ParentPid, Stat.shm_lpid, \"%d\");\r\n    }\r\n    else\r\n    {\r\n\r\n        LxtLogInfo(\"Parent's view of ChildPid %d\", ChildPid);\r\n\r\n        //\r\n        // Wait for the child to attach.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n\r\n        //\r\n        // Query the last attach pid (should NOT match ChildPid) and create\r\n        // a new mapping.\r\n        //\r\n\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n        LxtCheckNotEqual(ChildPid, Stat.shm_lpid, \"%d\");\r\n        LxtCheckMapErrno(Address2 = LxtShmAt(Id, NULL, 0));\r\n        LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat));\r\n        LxtCheckEqual(getpid(), Stat.shm_lpid, \"%d\");\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    if (Address != NULL)\r\n    {\r\n        LxtShmDt(Address);\r\n    }\r\n\r\n    if (Address2 != NULL)\r\n    {\r\n        LxtShmDt(Address2);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (Id != -1)\r\n    {\r\n        LxtShmCtl(Id, IPC_RMID, NULL);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint ShmPidNamespace(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the behavior of System V shared memory across IPC\r\n    namespaces.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Result;\r\n\r\n    //\r\n    // Fork into a new parent so that the existing threadgroup does not have its\r\n    // IPC namespaces altered for later tests.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(ShmPidNamespaceWork());\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid ShmPrintInfo(struct shmid_ds* Stat)\r\n\r\n{\r\n\r\n    if (g_VerboseShm == false)\r\n    {\r\n        return;\r\n    }\r\n\r\n    LxtLogInfo(\"shm_perm.__key %u\", Stat->shm_perm.__key);\r\n    LxtLogInfo(\"shm_perm.uid %u\", Stat->shm_perm.uid);\r\n    LxtLogInfo(\"shm_perm.gid %u\", Stat->shm_perm.gid);\r\n    LxtLogInfo(\"shm_perm.cuid %u\", Stat->shm_perm.cuid);\r\n    LxtLogInfo(\"shm_perm.cgid %u\", Stat->shm_perm.cgid);\r\n    LxtLogInfo(\"shm_perm.mode %o\", Stat->shm_perm.mode);\r\n    LxtLogInfo(\"shm_perm.__seq %d\", Stat->shm_perm.__seq);\r\n    LxtLogInfo(\"shm_segsz %Iu\", Stat->shm_segsz);\r\n    LxtLogInfo(\"shm_atime %Iu\", Stat->shm_atime);\r\n    LxtLogInfo(\"shm_dtime %Iu\", Stat->shm_dtime);\r\n    LxtLogInfo(\"shm_ctime %Iu\", Stat->shm_ctime);\r\n    LxtLogInfo(\"shm_cpid %Iu\", Stat->shm_cpid);\r\n    LxtLogInfo(\"shm_lpid %Iu\", Stat->shm_lpid);\r\n    LxtLogInfo(\"shm_nattch %Iu\", Stat->shm_nattch);\r\n    return;\r\n}\r\n\r\nvoid ShmPrintInfoAttach(struct shmid_ds* Stat)\r\n\r\n{\r\n\r\n    if (g_VerboseShm == false)\r\n    {\r\n        return;\r\n    }\r\n\r\n    LxtLogInfo(\"shm_segsz %Iu\", Stat->shm_segsz);\r\n    LxtLogInfo(\"shm_atime %Iu\", Stat->shm_atime);\r\n    LxtLogInfo(\"shm_dtime %Iu\", Stat->shm_dtime);\r\n    LxtLogInfo(\"shm_ctime %Iu\", Stat->shm_ctime);\r\n    LxtLogInfo(\"shm_cpid %Iu\", Stat->shm_cpid);\r\n    LxtLogInfo(\"shm_lpid %Iu\", Stat->shm_lpid);\r\n    LxtLogInfo(\"shm_nattch %Iu\", Stat->shm_nattch);\r\n    return;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/socket.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    socket.c\r\n\r\nAbstract:\r\n\r\n    Linux socket client / server test.\r\n\r\n--*/\r\n\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/types.h>\r\n#include <sys/socket.h>\r\n#include <sys/un.h>\r\n#include <netinet/in.h>\r\n#include <netdb.h>\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include \"common.h\"\r\n\r\n#define LXT_NAME_CLIENT \"SocketClient\"\r\n#define LXT_NAME_SERVER \"SocketServer\"\r\n\r\n#define LXT_AF_UNIX_SOCKET_PATH \"af_unix_socket\"\r\n\r\n#define FD_STDOUT 1\r\n//\r\n// Function declarations.\r\n//\r\n\r\nlong long GetTickCount(void);\r\n\r\nint SocketClientDgram(PLXT_ARGS Args);\r\n\r\nint SocketClientSend(int NumConnectAndSends, int Family, int Type, int Protocol);\r\n\r\nint SocketClientSendMultiple(PLXT_ARGS Args);\r\n\r\nint SocketClientSendMultipleIpv6(PLXT_ARGS Args);\r\n\r\nint SocketClientSendWithFlags(PLXT_ARGS Args);\r\n\r\nint SocketClientUnix(PLXT_ARGS Args);\r\n\r\nint SocketCreateAcceptedSockets(int Family, int Type, int Protocol);\r\n\r\nint SocketCreateBoundSocket(int Family, int Type, int Protocol);\r\n\r\nint SocketCreateConnectSocket(int Family, int Type, int Protocol);\r\n\r\nint SocketGetSockName(PLXT_ARGS Args);\r\n\r\nint SocketParseCommandLine(int Argc, char* Argv[], LXT_ARGS* Args);\r\n\r\nint SocketServerAccept(int NumAccepts, int Family, int Type, int Protocol);\r\n\r\nint SocketServerAcceptMultiple(PLXT_ARGS Args);\r\n\r\nint SocketServerAcceptMultipleIpv6(PLXT_ARGS Args);\r\n\r\nint SocketServerAcceptWithFlags(PLXT_ARGS Args);\r\n\r\nint SocketServerDgram(PLXT_ARGS Args);\r\n\r\nint SocketServerUnix(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtClientVariations[] = {\r\n    {\"Socket Client - send multiple\", SocketClientSendMultiple},\r\n    {\"Socket Client - AF_UNIX\", SocketClientUnix},\r\n    {\"Socket Client - send multiple Ipv6\", SocketClientSendMultipleIpv6},\r\n    {\"Socket Client - send (MSG_WAITALL)\", SocketClientSendWithFlags},\r\n    {\"Socket Client - SOCK_DGRAM\", SocketClientDgram},\r\n\r\n    //\r\n    // Variations that do not require a server.\r\n    //\r\n\r\n    {\"Socket - getsockname\", SocketGetSockName},\r\n};\r\n\r\n//\r\n// N.B. Keep the number of variations up to date with\r\n// LXT_SOCKET_NUM_SERVER_VARIATIONS in socket/common.h\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtServerVariations[] = {\r\n    {\"Socket Server - accept multiple\", SocketServerAcceptMultiple},\r\n    {\"Socket Server - AF_UNIX\", SocketServerUnix},\r\n    {\"Socket Server - accept multiple Ipv6\", SocketServerAcceptMultipleIpv6},\r\n    {\"Socket Server - accept (MSG_WAITALL)\", SocketServerAcceptWithFlags},\r\n    {\"Socket Server - SOCK_DGRAM\", SocketServerDgram}};\r\n\r\n//\r\n// Function definitions.\r\n//\r\n\r\nlong long GetTickCount(void)\r\n{\r\n\r\n    struct timespec Now;\r\n\r\n    if (clock_gettime(CLOCK_MONOTONIC, &Now))\r\n    {\r\n        return 0;\r\n    }\r\n\r\n    return Now.tv_sec * 1000 + Now.tv_nsec / 1000000;\r\n}\r\n\r\nint main(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(SocketParseCommandLine(Argc, Argv, &Args))\r\n\r\n        ErrorExit : LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint SocketClientDgram(PLXT_ARGS Args)\r\n{\r\n\r\n    char ReceiveBuffer[LXT_SOCKET_DEFAULT_BUFFER_LENGTH] = {0};\r\n    int Result = LXT_RESULT_FAILURE;\r\n    char* SendBuffer;\r\n    struct sockaddr* ServerAddress = {0};\r\n    struct sockaddr_in ServerAddressIpv4 = {0};\r\n    struct sockaddr_in6 ServerAddressIpv6 = {0};\r\n    struct sockaddr_un ServerAddressUnix = {0};\r\n    int ServerAddressLength;\r\n    int Size;\r\n    int Socket = 0;\r\n\r\n    sleep(2);\r\n\r\n    Socket = socket(AF_INET6, SOCK_DGRAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket(AF_INET6, SOCK_DGRAM, 0) - %s\", strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    ServerAddressIpv6.sin6_family = AF_INET6;\r\n    ServerAddressIpv6.sin6_addr = in6addr_loopback;\r\n    ServerAddressIpv6.sin6_port = htons(LXT_SOCKET_DEFAULT_PORT_IPV6);\r\n    ServerAddress = (struct sockaddr*)&ServerAddressIpv6;\r\n    ServerAddressLength = sizeof(ServerAddressIpv6);\r\n    SendBuffer = LXT_SOCKET_DEFAULT_SEND_STRING;\r\n\r\n    Size = sendto(Socket, SendBuffer, strlen(SendBuffer), 0, ServerAddress, ServerAddressLength);\r\n\r\n    if (Size < 0)\r\n    {\r\n        LxtLogError(\"sendto - %s\", strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Size = recvfrom(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0, ServerAddress, &ServerAddressLength);\r\n\r\n    if (Size < 0)\r\n    {\r\n        LxtLogError(\"recvfrom - %s\", strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Received from server: %s\", ReceiveBuffer);\r\n\r\n    if (memcmp(SendBuffer, ReceiveBuffer, Size) != 0)\r\n    {\r\n        LxtLogError(\"Message received back from server %s did not match expected %s\", ReceiveBuffer, SendBuffer);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketClientSend(int NumConnectAndSends, int Family, int Type, int Protocol)\r\n\r\n{\r\n\r\n    int Index;\r\n    char ReceiveBuffer[LXT_SOCKET_DEFAULT_BUFFER_LENGTH] = {0};\r\n    int Result = LXT_RESULT_FAILURE;\r\n    char* SendBuffer;\r\n    int Size;\r\n    int Socket = 0;\r\n\r\n    //\r\n    // Sleep to allow the server process to be listening.\r\n    //\r\n\r\n    sleep(2);\r\n    SendBuffer = LXT_SOCKET_DEFAULT_SEND_STRING;\r\n    for (Index = 0; Index < NumConnectAndSends; Index++)\r\n    {\r\n        Socket = SocketCreateConnectSocket(Family, Type, Protocol);\r\n        if (Socket <= 0)\r\n        {\r\n            LxtLogError(\"SocketCreateConnectSocket failed\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Size = send(Socket, SendBuffer, strlen(SendBuffer), 0);\r\n        if (Size < 0)\r\n        {\r\n            LxtLogError(\"send(%d, SendBuffer, strlen(SendBuffer), 0) - %s\", Socket, strerror(errno));\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        memset(ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n        Size = read(Socket, ReceiveBuffer, Size);\r\n        if (Size < 0)\r\n        {\r\n            LxtLogError(\"read(%d, ReceiveBuffer, sizeof(ReceiveBuffer) - %s\", Socket, strerror(errno));\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtLogInfo(\"Received from server: %s\", ReceiveBuffer);\r\n\r\n        if (memcmp(SendBuffer, ReceiveBuffer, Size) != 0)\r\n        {\r\n            LxtLogError(\"Message received back from server %s did not match expected %s\", ReceiveBuffer, SendBuffer);\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n\r\n        Socket = 0;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketClientSendMultiple(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    return SocketClientSend(LXT_SOCKET_SERVER_MAX_BACKLOG_NUM, AF_INET, SOCK_STREAM, 0);\r\n}\r\n\r\nint SocketClientSendMultipleIpv6(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    return SocketClientSend(LXT_SOCKET_SERVER_MAX_BACKLOG_NUM, AF_INET6, SOCK_STREAM, 0);\r\n}\r\n\r\nint SocketClientSendWithFlags(PLXT_ARGS Args)\r\n{\r\n    int FullMessageSize;\r\n    char* ReceiveBuffer;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    char* SendBuffer;\r\n    int Size;\r\n    int Socket = 0;\r\n\r\n    //\r\n    // Sleep to allow the server process to be listening.\r\n    //\r\n\r\n    sleep(2);\r\n    SendBuffer = LXT_SOCKET_DEFAULT_SEND_STRING;\r\n    FullMessageSize = 2 * strlen(SendBuffer);\r\n    ReceiveBuffer = malloc(FullMessageSize);\r\n    if (ReceiveBuffer == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Socket = SocketCreateConnectSocket(AF_INET, SOCK_STREAM, 0);\r\n    if (Socket <= 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Size = send(Socket, SendBuffer, strlen(SendBuffer), 0);\r\n    if (Size < 0)\r\n    {\r\n        LxtLogError(\"send(%d, SendBuffer, strlen(SendBuffer), 0) - %s\", Socket, strerror(errno));\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Sleep long enough that the second send won't be concatenated by WSK to\r\n    // test MSW_WAITALL code path.\r\n    //\r\n\r\n    sleep(1);\r\n    Size = send(Socket, SendBuffer, strlen(SendBuffer), 0);\r\n    if (Size < 0)\r\n    {\r\n        LxtLogError(\"send(%d, SendBuffer, strlen(SendBuffer), 0) - %s\", Socket, strerror(errno));\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(ReceiveBuffer, 0, FullMessageSize);\r\n    Size = recv(Socket, ReceiveBuffer, FullMessageSize, MSG_WAITALL);\r\n    if (Size < 0)\r\n    {\r\n        LxtLogError(\"read(%d, ReceiveBuffer, %d, MSG_WAITALL - %s\", Socket, FullMessageSize, strerror(errno));\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Received from server: %s\", ReceiveBuffer);\r\n\r\n    if (memcmp(SendBuffer, ReceiveBuffer, FullMessageSize / 2) != 0)\r\n    {\r\n        LxtLogError(\"Message received back from server %s did not match expected %s\", ReceiveBuffer, SendBuffer);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (memcmp(SendBuffer, ReceiveBuffer + FullMessageSize / 2, sizeof(SendBuffer)) != 0)\r\n    {\r\n\r\n        LxtLogError(\"Message received back from server %s did not match expected %s\", ReceiveBuffer, SendBuffer);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    if (ReceiveBuffer != NULL)\r\n    {\r\n        free(ReceiveBuffer);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketClientUnix(PLXT_ARGS Args)\r\n{\r\n\r\n    return SocketClientSend(1, AF_UNIX, SOCK_SEQPACKET, 0);\r\n}\r\n\r\nint SocketCreateAcceptedSocket(int Socket, int Family)\r\n{\r\n\r\n    int AcceptedSocket = 0;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    struct sockaddr* Address;\r\n    struct sockaddr_in AddressIpv4 = {0};\r\n    struct sockaddr_in6 AddressIpv6 = {0};\r\n    struct sockaddr_un AddressUnix = {0};\r\n    socklen_t AddressLength;\r\n\r\n    switch (Family)\r\n    {\r\n    case AF_INET:\r\n        Address = (struct sockaddr*)&AddressIpv4;\r\n        AddressLength = sizeof(AddressIpv4);\r\n        break;\r\n\r\n    case AF_INET6:\r\n        Address = (struct sockaddr*)&AddressIpv6;\r\n        AddressLength = sizeof(AddressIpv6);\r\n        break;\r\n\r\n    case AF_UNIX:\r\n        Address = (struct sockaddr*)&AddressUnix;\r\n        AddressLength = sizeof(AddressUnix);\r\n        break;\r\n\r\n    default:\r\n        LxtLogError(\"Unsupported Family %d\", Family);\r\n        AcceptedSocket = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    AcceptedSocket = accept(Socket, Address, &AddressLength);\r\n\r\n    if (AcceptedSocket < 0)\r\n    {\r\n        LxtLogError(\"accept(%d, Address, &AddressLength) - %s\", Socket, strerror(errno));\r\n\r\n        close(AcceptedSocket);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = AcceptedSocket;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SocketCreateBoundSocket(int Family, int Type, int Protocol)\r\n{\r\n    int OptVal;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    struct sockaddr* ServerAddress;\r\n    int ServerAddressSize;\r\n    struct sockaddr_in ServerAddressIpv4 = {0};\r\n    struct sockaddr_in6 ServerAddressIpv6 = {0};\r\n    struct sockaddr_un ServerAddressUnix = {0};\r\n    int Size;\r\n    int Socket = 0;\r\n\r\n    Socket = socket(Family, Type, Protocol);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket(%d, %d, %d) - %s\", Family, Type, Protocol, strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // TODO: when setsockopt is implemented uncomment the below.\r\n    //\r\n    // OptVal = 1;\r\n    // if (setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, &OptVal, sizeof(OptVal)) < 0) {\r\n    //    LxtLogError(\"setsockopt(%d, SOL_SOCKET, SO_REUSEADDR, &OptVal, sizeof(OptVal)) - %s\",\r\n    //                 Socket,\r\n    //                 strerror(errno));\r\n    //    goto ErrorExit;\r\n    //}\r\n\r\n    switch (Family)\r\n    {\r\n    case AF_INET:\r\n        ServerAddressIpv4.sin_family = AF_INET;\r\n        ServerAddressIpv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r\n        ServerAddressIpv4.sin_port = htons(LXT_SOCKET_DEFAULT_PORT);\r\n        ServerAddress = (struct sockaddr*)&ServerAddressIpv4;\r\n        ServerAddressSize = sizeof(ServerAddressIpv4);\r\n        break;\r\n\r\n    case AF_INET6:\r\n        ServerAddressIpv6.sin6_family = AF_INET6;\r\n        ServerAddressIpv6.sin6_addr = in6addr_loopback;\r\n        ServerAddressIpv6.sin6_port = htons(LXT_SOCKET_DEFAULT_PORT_IPV6);\r\n        ServerAddress = (struct sockaddr*)&ServerAddressIpv6;\r\n        ServerAddressSize = sizeof(ServerAddressIpv6);\r\n        break;\r\n\r\n    case AF_UNIX:\r\n        ServerAddressUnix.sun_family = AF_UNIX;\r\n        strcpy(ServerAddressUnix.sun_path, LXT_AF_UNIX_SOCKET_PATH);\r\n        ServerAddress = (struct sockaddr*)&ServerAddressUnix;\r\n        ServerAddressSize = sizeof(ServerAddressUnix);\r\n        // unlink(LXT_AF_UNIX_SOCKET_PATH); // TODO: when unlink is implemented uncomment this.\r\n        break;\r\n\r\n    default:\r\n        LxtLogError(\"Unsupported Family %d\", Family);\r\n        close(Socket);\r\n        Socket = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (bind(Socket, ServerAddress, ServerAddressSize) < 0)\r\n    {\r\n        LxtLogError(\"bind(%d, ServerAddress, ServerAddressSize) - %s\", Socket, strerror(errno));\r\n\r\n        close(Socket);\r\n        Socket = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = Socket;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SocketCreateConnectSocket(int Family, int Type, int Protocol)\r\n{\r\n\r\n    int Result = LXT_RESULT_FAILURE;\r\n    struct sockaddr* ServerAddress = NULL;\r\n    struct sockaddr_in ServerAddressIpv4 = {0};\r\n    struct sockaddr_in6 ServerAddressIpv6 = {0};\r\n    int ServerAddressSize;\r\n    struct sockaddr_un ServerAddressUnix = {0};\r\n    int Size;\r\n    int Socket = 0;\r\n\r\n    Socket = socket(Family, Type, Protocol);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket(%d, %d, %d) - %s\", Family, Type, Protocol, strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    switch (Family)\r\n    {\r\n    case AF_INET:\r\n        ServerAddressIpv4.sin_family = AF_INET;\r\n        ServerAddressIpv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r\n        ServerAddressIpv4.sin_port = htons(LXT_SOCKET_DEFAULT_PORT);\r\n        ServerAddress = (struct sockaddr*)&ServerAddressIpv4;\r\n        ServerAddressSize = sizeof(ServerAddressIpv4);\r\n        break;\r\n\r\n    case AF_INET6:\r\n        ServerAddressIpv6.sin6_family = AF_INET6;\r\n        ServerAddressIpv6.sin6_addr = in6addr_loopback;\r\n        ServerAddressIpv6.sin6_port = htons(LXT_SOCKET_DEFAULT_PORT_IPV6);\r\n        ServerAddress = (struct sockaddr*)&ServerAddressIpv6;\r\n        ServerAddressSize = sizeof(ServerAddressIpv6);\r\n        break;\r\n\r\n    case AF_UNIX:\r\n        ServerAddressUnix.sun_family = AF_UNIX;\r\n        strcpy(ServerAddressUnix.sun_path, LXT_AF_UNIX_SOCKET_PATH);\r\n        ServerAddress = (struct sockaddr*)&ServerAddressUnix;\r\n        ServerAddressSize = sizeof(ServerAddressUnix);\r\n        break;\r\n\r\n    default:\r\n        LxtLogError(\"LxtSocketClientSend Unsupported Family %d\", Family);\r\n        close(Socket);\r\n        Socket = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (connect(Socket, ServerAddress, ServerAddressSize) < 0)\r\n    {\r\n        LxtLogError(\"connect failed - %s\", strerror(errno));\r\n        close(Socket);\r\n        Socket = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = Socket;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SocketGetSockName(PLXT_ARGS Args)\r\n{\r\n    union\r\n    {\r\n        struct in_addr Addr;\r\n        struct in6_addr AddrIpv6;\r\n    } Addr;\r\n    struct sockaddr_storage Address = {0};\r\n    int AddressFamilies[] = {AF_INET, AF_INET6};\r\n    int AddressFamily;\r\n    int AddressSize;\r\n    int Port;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    int Socket = 0;\r\n    int Index;\r\n\r\n    for (Index = 0; Index < (int)LXT_COUNT_OF(AddressFamilies); Index++)\r\n    {\r\n        Socket = socket(AddressFamilies[Index], SOCK_STREAM, 0);\r\n        if (Socket < 0)\r\n        {\r\n            LxtLogError(\"socket(%d, SOCK_STREAM, 0) - %s\", AddressFamilies[Index], strerror(errno));\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        switch (AddressFamilies[Index])\r\n        {\r\n        case AF_INET:\r\n            ((struct sockaddr_in*)&Address)->sin_family = AF_INET;\r\n            ((struct sockaddr_in*)&Address)->sin_port = htons(0);\r\n            ((struct sockaddr_in*)&Address)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r\n            AddressSize = sizeof(struct sockaddr_in);\r\n            break;\r\n\r\n        case AF_INET6:\r\n            ((struct sockaddr_in6*)&Address)->sin6_family = AF_INET6;\r\n            ((struct sockaddr_in6*)&Address)->sin6_port = htons(0);\r\n            ((struct sockaddr_in6*)&Address)->sin6_addr = in6addr_loopback;\r\n            AddressSize = sizeof(struct sockaddr_in6);\r\n            break;\r\n\r\n        default:\r\n            LxtLogError(\"Unsupported Family %d\", AddressFamilies[Index]);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (bind(Socket, (struct sockaddr*)&Address, AddressSize) < 0)\r\n        {\r\n            LxtLogError(\"bind(%d, (struct sockaddr*)&Address, AddressSize) - %s\", Socket, strerror(errno));\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (getsockname(Socket, (struct sockaddr*)&Address, &AddressSize) < 0)\r\n        {\r\n            LxtLogError(\"getsockname(%d, &Address, &AddressSize) - %s\", Socket, strerror(errno));\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        memset(&Addr, 0, sizeof(Addr));\r\n        switch (AddressFamilies[Index])\r\n        {\r\n        case AF_INET:\r\n            AddressFamily = ((struct sockaddr_in*)&Address)->sin_family;\r\n            Port = ((struct sockaddr_in*)&Address)->sin_port;\r\n            memcpy(\r\n                (struct sockaddr_in*)&Addr,\r\n                &((struct sockaddr_in*)&Address)->sin_addr.s_addr,\r\n                sizeof(((struct sockaddr_in*)&Address)->sin_addr.s_addr));\r\n\r\n            break;\r\n\r\n        case AF_INET6:\r\n            AddressFamily = ((struct sockaddr_in6*)&Address)->sin6_family;\r\n            Port = ((struct sockaddr_in6*)&Address)->sin6_port;\r\n            memcpy(\r\n                (struct sockaddr_in6*)&Addr, &((struct sockaddr_in6*)&Address)->sin6_addr, sizeof(((struct sockaddr_in6*)&Address)->sin6_addr));\r\n\r\n            break;\r\n\r\n        default:\r\n            LxtLogError(\"Unsupported AddressFamily %d\", AddressFamilies[Index]);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (AddressFamily != AddressFamilies[Index])\r\n        {\r\n            LxtLogError(\"Socket %d is bound, address family %d should be %d\", Socket, AddressFamily, AddressFamilies[Index], strerror(errno));\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (Port == 0)\r\n        {\r\n            LxtLogError(\"Socket %d is bound, port should be non-null\", Socket, strerror(errno));\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Create the underlaying socket and query again, port should be the same.\r\n        //\r\n\r\n        if (listen(Socket, 32) < 0)\r\n        {\r\n            LxtLogError(\"listen(%d, 32) - %s\", Socket, strerror(errno));\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (getsockname(Socket, (struct sockaddr*)&Address, &AddressSize) < 0)\r\n        {\r\n            LxtLogError(\"getsockname(%d, &Address, &AddressSize) - %s\", Socket, strerror(errno));\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        switch (AddressFamilies[Index])\r\n        {\r\n        case AF_INET:\r\n            if (AddressFamily != ((struct sockaddr_in*)&Address)->sin_family)\r\n            {\r\n                LxtLogError(\"Socket %d is bound, address family %d should be %d\", Socket, ((struct sockaddr_in*)&Address)->sin_family, AddressFamily, strerror(errno));\r\n\r\n                goto ErrorExit;\r\n            }\r\n\r\n            if (Port != ((struct sockaddr_in*)&Address)->sin_port)\r\n            {\r\n                LxtLogError(\"Socket %d should be bound to port %d\", Socket, Port, strerror(errno));\r\n\r\n                goto ErrorExit;\r\n            }\r\n\r\n            if (memcmp(&Addr, &((struct sockaddr_in*)&Address)->sin_addr.s_addr, sizeof(((struct sockaddr_in*)&Address)->sin_addr.s_addr)) != 0)\r\n            {\r\n\r\n                LxtLogError(\"Socket %d addr should be localhost\", Socket, strerror(errno));\r\n\r\n                goto ErrorExit;\r\n            }\r\n\r\n            break;\r\n\r\n        case AF_INET6:\r\n            if (AddressFamily != ((struct sockaddr_in6*)&Address)->sin6_family)\r\n            {\r\n                LxtLogError(\r\n                    \"Socket %d is bound, address family %d should be %d\",\r\n                    Socket,\r\n                    ((struct sockaddr_in6*)&Address)->sin6_family,\r\n                    AddressFamily,\r\n                    strerror(errno));\r\n\r\n                goto ErrorExit;\r\n            }\r\n\r\n            if (Port != ((struct sockaddr_in6*)&Address)->sin6_port)\r\n            {\r\n                LxtLogError(\"Socket %d should be bound to port %d\", Socket, Port, strerror(errno));\r\n\r\n                goto ErrorExit;\r\n            }\r\n\r\n            if (memcmp(&Addr, &((struct sockaddr_in6*)&Address)->sin6_addr, sizeof(((struct sockaddr_in6*)&Address)->sin6_addr)) != 0)\r\n            {\r\n\r\n                LxtLogError(\"Socket %d addr should be localhost\", Socket, strerror(errno));\r\n\r\n                goto ErrorExit;\r\n            }\r\n            break;\r\n\r\n        default:\r\n            LxtLogError(\"Unsupported Family %d\", AddressFamilies[Index]);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n\r\n        Socket = 0;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketParseCommandLine(int Argc, char* Argv[], LXT_ARGS* Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ArgvIndex;\r\n    int Result;\r\n    int ValidArguments;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    ValidArguments = 0;\r\n\r\n    if (Argc < 2)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (ArgvIndex = 1; ArgvIndex < Argc; ++ArgvIndex)\r\n    {\r\n        if (Argv[ArgvIndex][0] != '-')\r\n        {\r\n            printf(\"Unexpected character %s\\n\", Argv[ArgvIndex]);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        switch (Argv[ArgvIndex][1])\r\n        {\r\n        case 'c':\r\n\r\n            //\r\n            // Run client variations.\r\n            //\r\n\r\n            ValidArguments = 1;\r\n            LxtCheckResult(LxtInitialize(Argc, Argv, Args, LXT_NAME_CLIENT));\r\n            LxtCheckResult(LxtRunVariations(Args, g_LxtClientVariations, LXT_COUNT_OF(g_LxtClientVariations)));\r\n            break;\r\n\r\n        case 's':\r\n\r\n            //\r\n            // Run server variations.\r\n            //\r\n\r\n            ValidArguments = 1;\r\n            LxtCheckResult(LxtInitialize(Argc, Argv, Args, LXT_NAME_SERVER));\r\n            LxtCheckResult(LxtRunVariations(Args, g_LxtServerVariations, LXT_COUNT_OF(g_LxtServerVariations)));\r\n            break;\r\n\r\n        case 'v':\r\n\r\n            //\r\n            // This was already taken care of by LxtInitialize.\r\n            //\r\n\r\n            ++ArgvIndex;\r\n\r\n            break;\r\n\r\n        default:\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    if (ValidArguments == 0)\r\n    {\r\n        printf(\"\\nuse: socket <One of the below arguments>\\n\");\r\n        printf(\"\\t-c : Run all client variations (server must already be running)\\n\");\r\n        printf(\"\\t-s : Run all server variations\\n\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketServerDgram(PLXT_ARGS Args)\r\n{\r\n\r\n    char Buffer[LXT_SOCKET_DEFAULT_BUFFER_LENGTH] = {0};\r\n    struct sockaddr* FromAddress = {0};\r\n    struct sockaddr_in FromAddressIpv4 = {0};\r\n    struct sockaddr_in6 FromAddressIpv6 = {0};\r\n    struct sockaddr_un FromAddressUnix = {0};\r\n    int FromAddressLength;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    int Size;\r\n    int Socket = 0;\r\n\r\n    Socket = SocketCreateBoundSocket(AF_INET6, SOCK_DGRAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    FromAddress = (struct sockaddr*)&FromAddressIpv6;\r\n    FromAddressLength = sizeof(FromAddressIpv6);\r\n\r\n    Size = recvfrom(Socket, Buffer, sizeof(Buffer), 0, FromAddress, &FromAddressLength);\r\n\r\n    if (Size < 0)\r\n    {\r\n        LxtLogError(\"recvfrom - %s\", strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Received : %s\", Buffer);\r\n\r\n    Size = sendto(Socket, Buffer, Size, 0, FromAddress, FromAddressLength);\r\n\r\n    if (Size < 0)\r\n    {\r\n        LxtLogError(\"sendto - %s\", strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketServerAccept(int NumAccepts, int Family, int Type, int Protocol)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[LXT_SOCKET_DEFAULT_BUFFER_LENGTH] = {0};\r\n    int ClientSockets[LXT_SOCKET_SERVER_MAX_BACKLOG_NUM] = {0};\r\n    int Index;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    int Size;\r\n    int Socket = 0;\r\n\r\n    Socket = SocketCreateBoundSocket(Family, Type, Protocol);\r\n    if (Socket < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (listen(Socket, NumAccepts) < 0)\r\n    {\r\n        LxtLogError(\"listen(%d, %d) - %s\", Socket, NumAccepts, strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (Index = 0; Index < NumAccepts; Index++)\r\n    {\r\n        ClientSockets[Index] = SocketCreateAcceptedSocket(Socket, Family);\r\n        if (ClientSockets[Index] < 0)\r\n        {\r\n            goto ErrorExit;\r\n        }\r\n\r\n        memset(Buffer, 0, sizeof(Buffer));\r\n        Size = read(ClientSockets[Index], Buffer, sizeof(Buffer));\r\n\r\n        //\r\n        // TODO: might need to handle the case where the socket gets shut down\r\n        // without receiving any data as in the connect / close variation.\r\n        //\r\n\r\n        if (Size < 0)\r\n        {\r\n            LxtLogError(\"read(%d, Buffer, sizeof(Buffer)) - %s\", ClientSockets[Index], strerror(errno));\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtLogInfo(\"Received: %s\", Buffer);\r\n\r\n        if (write(ClientSockets[Index], Buffer, Size) < 0)\r\n        {\r\n            LxtLogError(\"write(%d, Buffer, Size) - %s\", ClientSockets[Index], strerror(errno));\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    for (Index = 0; Index < NumAccepts; Index++)\r\n    {\r\n        if (ClientSockets[Index] > 0)\r\n        {\r\n            if (close(ClientSockets[Index]) < 0)\r\n            {\r\n                LxtLogError(\"close(%d) - %s\", ClientSockets[Index], strerror(errno));\r\n            }\r\n        }\r\n    }\r\n\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketServerAcceptMultiple(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    return SocketServerAccept(LXT_SOCKET_SERVER_MAX_BACKLOG_NUM, AF_INET, SOCK_STREAM, 0);\r\n}\r\n\r\nint SocketServerAcceptMultipleIpv6(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    return SocketServerAccept(LXT_SOCKET_SERVER_MAX_BACKLOG_NUM, AF_INET6, SOCK_STREAM, 0);\r\n}\r\n\r\nint SocketServerAcceptWithFlags(PLXT_ARGS Args)\r\n{\r\n    int AcceptedSocket = 0;\r\n    int Index;\r\n    char* ReceiveBuffer;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    int Size;\r\n    int FullMessageSize;\r\n    int Socket = 0;\r\n\r\n    ReceiveBuffer = LXT_SOCKET_DEFAULT_SEND_STRING;\r\n    FullMessageSize = 2 * strlen(ReceiveBuffer);\r\n    ReceiveBuffer = malloc(FullMessageSize);\r\n    if (ReceiveBuffer == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Socket = SocketCreateBoundSocket(AF_INET, SOCK_STREAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (listen(Socket, 32) < 0)\r\n    {\r\n        LxtLogError(\"listen(%d, 32) - %s\", Socket, strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    AcceptedSocket = SocketCreateAcceptedSocket(Socket, AF_INET);\r\n    memset(ReceiveBuffer, 0, sizeof(ReceiveBuffer));\r\n    Size = recv(AcceptedSocket, ReceiveBuffer, FullMessageSize, MSG_WAITALL);\r\n\r\n    //\r\n    // TODO: might need to handle the case where the socket gets shut down\r\n    // without receiving any data as in the connect / close variation.\r\n    //\r\n\r\n    if (Size < 0)\r\n    {\r\n        LxtLogError(\"recv(%d, Buffer, %d, MSG_WAITALL) - %s\", AcceptedSocket, FullMessageSize, strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"Received: %s\", ReceiveBuffer);\r\n\r\n    if (write(AcceptedSocket, ReceiveBuffer, Size) < 0)\r\n    {\r\n        LxtLogError(\"write(%d, Buffer, Size) - %s\", AcceptedSocket, strerror(errno));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (AcceptedSocket > 0)\r\n    {\r\n        if (close(AcceptedSocket) < 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", AcceptedSocket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    if (ReceiveBuffer != NULL)\r\n    {\r\n        free(ReceiveBuffer);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SocketServerUnix(PLXT_ARGS Args)\r\n{\r\n\r\n    return SocketServerAccept(1, AF_UNIX, SOCK_SEQPACKET, 0);\r\n}"
  },
  {
    "path": "test/linux/unit_tests/socket_nonblock.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    socket_nonblock.c\r\n\r\nAbstract:\r\n\r\n    This file is a simple test for nonblocking sockets.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/epoll.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n#include <sys/types.h>\r\n#include <sys/socket.h>\r\n#include <sys/un.h>\r\n#include <netinet/in.h>\r\n#include <netdb.h>\r\n#include \"common.h\"\r\n\r\n#include <sys/wait.h>\r\n\r\n#define LXT_NAME \"socket_nonblocking\"\r\n\r\nint SocketAsyncTest(PLXT_ARGS Args);\r\n\r\n#define SOCKET_NAME \"PartyInTheUsa\"\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Socket_Async_Simple\", SocketAsyncTest},\r\n};\r\n\r\nint SocketNonblockTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\n#ifndef EPOLLONESHOT\r\n#define EPOLLONESHOT (1 << 30)\r\n#endif\r\n\r\nint NonblockEpollCreateClientSocket(int IsNonBlocking)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n    struct sockaddr_in ServerAddress = {0};\r\n    int Socket;\r\n\r\n    //\r\n    // Create a socket.\r\n    //\r\n\r\n    Socket = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket(AF_INET, SOCK_STREAM, 0) - %s\", strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Connect to the server.\r\n    //\r\n\r\n    ServerAddress.sin_family = AF_INET;\r\n    ServerAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r\n    ServerAddress.sin_port = htons(LXT_SOCKET_DEFAULT_PORT);\r\n\r\n    Result = connect(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));\r\n\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"connect(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Set the socket as non blocking.\r\n    //\r\n\r\n    if (IsNonBlocking != 0)\r\n    {\r\n        Result = fcntl(Socket, F_SETFL, O_NONBLOCK);\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"fcntl(%d) - %d\", Socket, errno);\r\n            Result = -1;\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    Result = Socket;\r\n    Socket = 0;\r\n\r\ncleanup:\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n            Result = LXT_RESULT_FAILURE;\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint NonblockEpollCreateClientUnixSocket(int IsNonBlocking)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    int Result;\r\n    struct sockaddr_un ServerAddress = {0};\r\n    int Socket;\r\n\r\n    //\r\n    // Create a socket.\r\n    //\r\n\r\n    Socket = socket(AF_UNIX, SOCK_STREAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket(AF_UNIX, SOCK_STREAM, 0) - %s\", strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Connect to the server.\r\n    //\r\n\r\n    ServerAddress.sun_family = AF_UNIX;\r\n    strcpy(ServerAddress.sun_path, SOCKET_NAME);\r\n\r\n    Result = connect(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));\r\n\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"connect(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Set the socket as non blocking.\r\n    //\r\n\r\n    if (IsNonBlocking != 0)\r\n    {\r\n        Result = fcntl(Socket, F_SETFL, O_NONBLOCK);\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"fcntl(%d) - %d\", Socket, errno);\r\n            Result = -1;\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    Result = Socket;\r\n    Socket = 0;\r\n\r\ncleanup:\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n            Result = LXT_RESULT_FAILURE;\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint NonblockEpollCreateListenSocket(int IsNonBlocking)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_in ServerAddress = {0};\r\n    int Result;\r\n    int Socket;\r\n\r\n    //\r\n    // Create a socket.\r\n    //\r\n\r\n    Socket = socket(AF_INET, SOCK_STREAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket - %s\", strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Bind the socket to an ipv4 socket.\r\n    //\r\n\r\n    ServerAddress.sin_family = AF_INET;\r\n    ServerAddress.sin_addr.s_addr = INADDR_ANY;\r\n    ServerAddress.sin_port = htons(LXT_SOCKET_DEFAULT_PORT);\r\n\r\n    Result = bind(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));\r\n\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"bind(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Mark the socket as a listen socket.\r\n    //\r\n\r\n    Result = listen(Socket, LXT_SOCKET_SERVER_MAX_BACKLOG_NUM);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"listen(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Set the socket as non blocking.\r\n    //\r\n\r\n    if (IsNonBlocking != 0)\r\n    {\r\n        Result = fcntl(Socket, F_SETFL, O_NONBLOCK);\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"fcntl(%d) - %d\", Socket, errno);\r\n            Result = -1;\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    Result = Socket;\r\n    Socket = 0;\r\n\r\ncleanup:\r\n\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint NonblockEpollCreateListenUnixSocket(int IsNonBlocking)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_un ServerAddress = {0};\r\n    int Result;\r\n    int Socket;\r\n\r\n    //\r\n    // Create a socket.\r\n    //\r\n\r\n    Socket = socket(AF_UNIX, SOCK_STREAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        LxtLogError(\"socket - %s\", strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Bind the socket to an ipv4 socket.\r\n    //\r\n\r\n    ServerAddress.sun_family = AF_UNIX;\r\n    strcpy(ServerAddress.sun_path, SOCKET_NAME);\r\n\r\n    Result = bind(Socket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress));\r\n\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"bind(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Mark the socket as a listen socket.\r\n    //\r\n\r\n    Result = listen(Socket, LXT_SOCKET_SERVER_MAX_BACKLOG_NUM);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"listen(%d) - %s\", Socket, strerror(errno));\r\n        Result = -1;\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Set the socket as non blocking.\r\n    //\r\n\r\n    if (IsNonBlocking != 0)\r\n    {\r\n        Result = fcntl(Socket, F_SETFL, O_NONBLOCK);\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"fcntl(%d) - %d\", Socket, errno);\r\n            Result = -1;\r\n            goto cleanup;\r\n        }\r\n    }\r\n\r\n    Result = Socket;\r\n    Socket = 0;\r\n\r\ncleanup:\r\n\r\n    if (Socket > 0)\r\n    {\r\n        if (close(Socket) != 0)\r\n        {\r\n            LxtLogError(\"close(%d) - %s\", Socket, strerror(errno));\r\n        }\r\n    }\r\n\r\n    return Result;\r\n}\r\nint NonblockEpollHandleClientAccept(int Socket)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_in ClientAddress = {0};\r\n    socklen_t ClientLength;\r\n    int Error;\r\n    int Result;\r\n    int RetryCount;\r\n\r\n    ClientLength = sizeof(ClientAddress);\r\n    Result = -1;\r\n    RetryCount = 0;\r\n\r\n    while (1)\r\n    {\r\n        Result = accept(Socket, (struct sockaddr*)&ClientAddress, &ClientLength);\r\n        if (Result < 0)\r\n        {\r\n            Error = errno;\r\n            LxtLogInfo(\"[Server] accept(%d) returned %d (error %d)\", Socket, Result, Error);\r\n\r\n            if (Error == EAGAIN)\r\n            {\r\n                LxtLogInfo(\"[Server] nonblocking accept said try again, sleeping...\");\r\n                usleep(1 * 1000 * 1000);\r\n\r\n                RetryCount += 1;\r\n                if (RetryCount > 10)\r\n                {\r\n                    LxtLogInfo(\"[Server] too many retries, exiting...\");\r\n                    Result = -1;\r\n                    goto cleanup;\r\n                }\r\n\r\n                continue;\r\n            }\r\n        }\r\n\r\n        break;\r\n    }\r\n\r\ncleanup:\r\n\r\n    return Result;\r\n}\r\n\r\nconst char* NonblockDataToWrite[] = {\r\n    \"<This is the first message> \",\r\n    \"<This is another message> \",\r\n    \"<Dumbledore is dead> \",\r\n    \"<Harry Potter must not go back to Hogwarts> \",\r\n    \"<There must always be a stark in Winterfell>\",\r\n};\r\n\r\nconst int NonblockWriteItemCount = sizeof(NonblockDataToWrite) / sizeof(NonblockDataToWrite[0]);\r\n\r\nint SocketAsyncTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[256];\r\n    int Result;\r\n    int EpollFileDescriptor;\r\n    int FileDescriptor1;\r\n    int FileDescriptor2;\r\n    struct epoll_event EpollControlEvent;\r\n    struct epoll_event EpollWaitEvent[2];\r\n    int ChildPid;\r\n    int Index;\r\n    int ChildStatus;\r\n    int RetryCount;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    FileDescriptor1 = -1;\r\n    FileDescriptor2 = -1;\r\n    EpollFileDescriptor = -1;\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Create the server socket.\r\n    //\r\n\r\n    LxtLogInfo(\"[Setup] About to create server socket...\");\r\n\r\n    FileDescriptor1 = NonblockEpollCreateListenSocket(1);\r\n    if (FileDescriptor1 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"[Setup] Could not create socket! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    //\r\n    // Fork to create a server and a client.\r\n    //\r\n\r\n    LxtLogInfo(\"[Setup] About to fork...\");\r\n\r\n    ChildPid = fork();\r\n\r\n    if (ChildPid == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"[Setup] Fork failed! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        LxtLogInfo(\"[Client] Waiting 5 seconds to let server block...\");\r\n\r\n        usleep(5 * 1000 * 1000);\r\n\r\n        LxtLogInfo(\"[Client] Connecting to server...\");\r\n\r\n        FileDescriptor2 = NonblockEpollCreateClientSocket(1);\r\n        if (FileDescriptor2 == -1)\r\n        {\r\n            Result = errno;\r\n            LxtLogError(\"[Client] Could not connect to server! %d\", Result);\r\n            goto cleanup;\r\n        }\r\n\r\n        LxtLogInfo(\"[Client] Connected to server, fd = %d\", FileDescriptor2);\r\n\r\n        //\r\n        // Wait for data to be available in a loop.\r\n        //\r\n\r\n        RetryCount = 0;\r\n        while (1)\r\n        {\r\n\r\n            LxtLogInfo(\"[Client] Trying to read data ...\");\r\n\r\n            memset(Buffer, 0, sizeof(Buffer));\r\n\r\n            Result = read(FileDescriptor2, Buffer, sizeof(Buffer));\r\n            if (Result < 0)\r\n            {\r\n                Result = errno;\r\n                if (Result = EAGAIN)\r\n                {\r\n                    LxtLogInfo(\"[Client] No data available, will try again...\");\r\n                    usleep(1 * 1000 * 1000);\r\n\r\n                    RetryCount += 1;\r\n                    if (RetryCount > 10)\r\n                    {\r\n                        LxtLogInfo(\"[Client] Too many retries, exiting...\");\r\n                        Result = -1;\r\n\r\n                        goto cleanup;\r\n                    }\r\n\r\n                    continue;\r\n                }\r\n\r\n                Result = -1;\r\n                goto cleanup;\r\n            }\r\n\r\n            LxtLogInfo(\"[Client] read %d bytes: %s ...\", Result, Buffer);\r\n\r\n            if (Result == 0)\r\n            {\r\n                LxtLogInfo(\"[Client] exiting after reading 0 bytes ...\");\r\n                goto cleanup;\r\n            }\r\n\r\n            //\r\n            // Reset the retry count after a successful read.\r\n            //\r\n\r\n            RetryCount = 0;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Accept an incoming connection.\r\n    //\r\n\r\n    FileDescriptor2 = NonblockEpollHandleClientAccept(FileDescriptor1);\r\n    if (FileDescriptor2 == -1)\r\n    {\r\n        Result = errno;\r\n        LxtLogError(\"[Server] Could not accept! %d\", Result);\r\n        goto cleanup;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Writing to socket %d times!\", NonblockWriteItemCount);\r\n\r\n    for (Index = 0; Index < NonblockWriteItemCount; Index += 1)\r\n    {\r\n\r\n        Result = write(FileDescriptor2, NonblockDataToWrite[Index], strlen(NonblockDataToWrite[Index]));\r\n\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"[Server] Write %d failed %d\", Index, Result);\r\n\r\n            goto cleanup;\r\n        }\r\n\r\n        LxtLogInfo(\r\n            \"[Server] Write (%d, %s, %d) -> %d!\",\r\n            FileDescriptor2,\r\n            NonblockDataToWrite[Index],\r\n            strlen(NonblockDataToWrite[Index]) + (Index == NonblockWriteItemCount - 1),\r\n            Result);\r\n\r\n        usleep(1 * 1000 * 1000);\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Closing client fd = %d\", FileDescriptor2);\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n        FileDescriptor2 = -1;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Waiting for child to exit\");\r\n\r\n    ChildStatus = 0;\r\n    wait(&ChildStatus);\r\n\r\n    LxtLogInfo(\"[Server] Child WIFEXITED=%d WEXITSTATUS=%d\", WIFEXITED(ChildStatus), WEXITSTATUS(ChildStatus));\r\n\r\n    //\r\n    // Determine if the test passed or failed.\r\n    //\r\n\r\n    if ((Result < 0) || (WIFEXITED(ChildStatus) == 0) || (WEXITSTATUS(ChildStatus) != 0))\r\n    {\r\n\r\n        LxtLogInfo(\"[Server] Test failed!\");\r\n        Result = -1;\r\n    }\r\n\r\n    LxtLogInfo(\"[Server] Done\");\r\n\r\ncleanup:\r\n\r\n    if (FileDescriptor1 != -1)\r\n    {\r\n        close(FileDescriptor1);\r\n    }\r\n\r\n    if (EpollFileDescriptor != -1)\r\n    {\r\n        close(EpollFileDescriptor);\r\n    }\r\n\r\n    if (FileDescriptor2 != -1)\r\n    {\r\n        close(FileDescriptor2);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtLogInfo(\"[Child] Exit with %d!\", Result);\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/splice.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    splice.c\r\n\r\nAbstract:\r\n\r\n    This file contains tests for the splice syscall.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <unistd.h>\r\n#include <sys/syscall.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/wait.h>\r\n#include <fcntl.h>\r\n\r\n#define LXT_NAME \"Splice\"\r\n\r\n#define SPLICE_SYSCALL(_Fdin, _Offin, _Fdout, _Offout, _Size, _Flags) \\\r\n    syscall(SYS_splice, _Fdin, _Offin, _Fdout, _Offout, _Size, _Flags)\r\n\r\n#define TEE_SYSCALL(_Fdin, _Fdout, _Size, _Flags) syscall(SYS_tee, _Fdin, _Fdout, _Size, _Flags)\r\n\r\n#ifndef SPLICE_F_NONBLOCK\r\n#define SPLICE_F_NONBLOCK 0x02\r\n#endif\r\n\r\n#define SPLICE_READ_PIPE_INDEX 0\r\n#define SPLICE_WRITE_PIPE_INDEX 1\r\n\r\n//\r\n// The following defines a standardized file path and content for a file that\r\n// can be used for splicing.\r\n//\r\n\r\n#define SPLICE_STD_FILE \"/data/test/splice_test_std_file_1.txt\"\r\n#define SPLICE_STD_FILE_CONTENT \"123456789Test\"\r\n#define SPLICE_STD_FILE_SIZE (unsigned long)(strlen(SPLICE_STD_FILE_CONTENT))\r\n\r\nint SpliceOpenStandardFile(void);\r\n\r\nint SpliceVariationBasicTests(PLXT_ARGS Args);\r\n\r\nint SpliceVariationBlocking(PLXT_ARGS Args);\r\n\r\nint SpliceVariationInvalidParameters(PLXT_ARGS Args);\r\n\r\nint TeeVariationBasicTests(PLXT_ARGS Args);\r\n\r\nint TeeVariationInvalidParameters(PLXT_ARGS Args);\r\n\r\n//\r\n// Globals.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Splice - Invalid Parameter Test \", SpliceVariationInvalidParameters},\r\n    {\"Splice - Blocking Tests\", SpliceVariationBlocking},\r\n    {\"Splice - Basic Usage Tests\", SpliceVariationBasicTests},\r\n    {\"Tee - Invalid Parameter Test \", TeeVariationInvalidParameters},\r\n    {\"Tee - Basic Usage Test\", TeeVariationBasicTests}};\r\n\r\nint SpliceTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine is the main entry point for the procfs tests.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint SpliceOpenStandardFile(void)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine returns the file descriptor for the standard file which\r\n    include uniform data for splicing.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    On success, returns open file descriptor; otherwise, -1.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    off_t Offset;\r\n    int Result;\r\n\r\n    LxtCheckErrno(Fd = open(SPLICE_STD_FILE, (O_CREAT | O_RDWR), (S_IRUSR | S_IWUSR)));\r\n\r\n    LxtCheckErrno(write(Fd, SPLICE_STD_FILE_CONTENT, SPLICE_STD_FILE_SIZE));\r\n\r\n    //\r\n    // Set the offset back to zero.\r\n    //\r\n\r\n    LxtCheckErrno(Offset = lseek(Fd, 0, SEEK_SET));\r\n\r\n    //\r\n    // Set the result.\r\n    //\r\n\r\n    Result = Fd;\r\n    Fd = 0;\r\n\r\nErrorExit:\r\n    if (Fd > 0)\r\n    {\r\n        close(Fd);\r\n        unlink(SPLICE_STD_FILE);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SpliceVariationBasicTests(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine runs basic usage tests for splice, including splicing between\r\n    two pipes and a pipe and a regular file, and tests the results for\r\n    accurate splice sizes, content, and file offsets.\r\n\r\nArguments:\r\n\r\n    Args - Supplies a pointer to variation arguments.\r\n\r\nReturn Value:\r\n\r\n    On success, returns open file descriptor; otherwise, -1.\r\n\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[4];\r\n    LXT_PIPE DestinationPipe = {-1, -1};\r\n    loff_t InitialOffset;\r\n    loff_t Offset;\r\n    char* PipeData;\r\n    ssize_t PipeDataSize;\r\n    int RegularFd;\r\n    int Result;\r\n    ssize_t SpliceSize;\r\n    LXT_PIPE SourcePipe = {-1, -1};\r\n\r\n    RegularFd = -1;\r\n    PipeData = \"1234\";\r\n    PipeDataSize = strlen(PipeData);\r\n    LxtCheckResult(LxtCreatePipe(&DestinationPipe));\r\n    LxtCheckResult(LxtCreatePipe(&SourcePipe));\r\n    LxtCheckResult(RegularFd = SpliceOpenStandardFile());\r\n\r\n    //\r\n    // Set up read-end of pipes as non-blocking for empty tests.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(SourcePipe.Read, F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrno(fcntl(DestinationPipe.Read, F_SETFL, O_NONBLOCK));\r\n\r\n    //\r\n    // Perform basic tests between pipes.\r\n    //\r\n\r\n    LxtLogInfo(\"Basic Usage - Splicing between two pipes\");\r\n    LxtCheckErrno(write(SourcePipe.Write, PipeData, PipeDataSize));\r\n    LxtCheckErrno(SpliceSize = SPLICE_SYSCALL(SourcePipe.Read, NULL, DestinationPipe.Write, NULL, PipeDataSize, SPLICE_F_NONBLOCK));\r\n\r\n    LxtCheckEqual(SpliceSize, PipeDataSize, \"%d\");\r\n\r\n    //\r\n    // Check that the read pipe is now empty by attempting to read a single\r\n    // byte.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(read(SourcePipe.Read, Buffer, 1), EAGAIN);\r\n    LxtCheckErrno(SpliceSize = read(DestinationPipe.Read, Buffer, PipeDataSize));\r\n\r\n    LxtCheckEqual(SpliceSize, PipeDataSize, \"%d\");\r\n\r\n    //\r\n    // LX_TODO: Enable the following additional basic tests once splicing is\r\n    // available for VolFs file types.\r\n    //\r\n\r\n    /*\r\n    //\r\n    // Perform test from a regular file to a pipe.\r\n    //\r\n\r\n    InitialOffset = 4;\r\n    Offset = InitialOffset;\r\n    LxtLogInfo(\"Basic Usage - Splicing from a regular file to a pipe\");\r\n    LxtCheckErrno(SpliceSize = SPLICE_SYSCALL(RegularFd,\r\n                                              &Offset,\r\n                                              DestinationPipe.Write,\r\n                                              NULL,\r\n                                              4,\r\n                                              SPLICE_F_NONBLOCK));\r\n\r\n    LxtCheckEqual(SpliceSize, 4, \"%d\");\r\n    LxtCheckErrno(SpliceSize = read(DestinationPipe.Read, Buffer, 4));\r\n    LxtCheckEqual(SpliceSize, 4, \"%d\");\r\n    LxtCheckEqual(Offset - InitialOffset, 4, \"%d\");\r\n    LxtCheckEqual(Buffer[0],\r\n                  SPLICE_STD_FILE_CONTENT[InitialOffset],\r\n                  \"%c\");\r\n\r\n    //\r\n    // Ensure that offset remains unchanged on the file.\r\n    //\r\n\r\n    LxtCheckErrno(SpliceSize = read(RegularFd, Buffer, 4));\r\n    LxtCheckEqual(SpliceSize, 4, \"%d\");\r\n    LxtCheckEqual(Buffer[0],\r\n                  SPLICE_STD_FILE_CONTENT[0],\r\n                  \"%c\");\r\n\r\n    //\r\n    // Reset the file's offset for the next test.\r\n    //\r\n\r\n    LxtCheckErrno(lseek(RegularFd, 0, SEEK_SET));\r\n\r\n    //\r\n    // Perform test from a pipe to a regular file.\r\n    //\r\n\r\n    InitialOffset = 1;\r\n    Offset = InitialOffset;\r\n    LxtLogInfo(\"Basic Usage - Splicing from a pipe to a regular file\");\r\n    LxtCheckErrno(write(SourcePipe.Write, PipeData, PipeDataSize));\r\n    LxtCheckErrno(SpliceSize = SPLICE_SYSCALL(SourcePipe.Read,\r\n                                              NULL,\r\n                                              RegularFd,\r\n                                              &Offset,\r\n                                              PipeDataSize,\r\n                                              SPLICE_F_NONBLOCK));\r\n\r\n    LxtCheckEqual(SpliceSize, PipeDataSize, \"%d\");\r\n    LxtCheckEqual(Offset - InitialOffset, PipeDataSize, \"%d\");\r\n\r\n    //\r\n    // Ensure that the regular file's internal offset is unchanged and that the\r\n    // the pipe data was properly splice into the file with the offset.\r\n    //\r\n\r\n    LxtCheckErrno(SpliceSize = read(RegularFd, Buffer, 2));\r\n    LxtCheckEqual(SpliceSize, 2, \"%d\");\r\n    LxtCheckEqual(Buffer[0],\r\n                  SPLICE_STD_FILE_CONTENT[0],\r\n                  \"%c\");\r\n\r\n    LxtCheckEqual(Buffer[1], PipeData[0], \"%c\");\r\n    */\r\n\r\nErrorExit:\r\n    LxtClosePipe(&DestinationPipe);\r\n    LxtClosePipe(&SourcePipe);\r\n    if (RegularFd > 0)\r\n    {\r\n        close(RegularFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SpliceVariationBlocking(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the splice syscall with the splice-specific non-blocking\r\n    flag with pipes that have opposite internal blocking settings and checks for\r\n    proper behavior.\r\n\r\nArguments:\r\n\r\n    Args - Supplies a pointer to variation arguments.\r\n\r\nReturn Value:\r\n\r\n    On success, returns open file descriptor; otherwise, -1.\r\n\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    LXT_PIPE DestinationPipe = {-1, -1};\r\n    int Result;\r\n    LXT_PIPE SourcePipe = {-1, -1};\r\n    int WaitPidResult;\r\n    int WaitPidStatus;\r\n\r\n    LxtCheckResult(LxtCreatePipe(&DestinationPipe));\r\n    LxtCheckResult(LxtCreatePipe(&SourcePipe));\r\n\r\n    //\r\n    // Verify that a pipe which is automatically set to have blocking I/O\r\n    // semantics, does not block when the splice call is supplied with the\r\n    // splice-specific non-blocking flag.\r\n    //\r\n\r\n    LxtLogInfo(\"Blocking - Non-blocking splice with blocking pipes\");\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // By default, the created pipes are blocking. The splice non-blocking\r\n        // flag should override this behavior.\r\n        //\r\n\r\n        Result = SPLICE_SYSCALL(SourcePipe.Read, NULL, DestinationPipe.Write, NULL, 1, SPLICE_F_NONBLOCK);\r\n\r\n        if (Result >= 0)\r\n        {\r\n            LxtLogError(\"Non-blocking splice syscall succeeded\");\r\n            _exit(1);\r\n        }\r\n\r\n        if (errno != EAGAIN)\r\n        {\r\n            LxtLogError(\"Non-blocking splice syscall returned with error %s\", strerror(errno));\r\n\r\n            _exit(1);\r\n        }\r\n\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckResult(WaitPidStatus = LxtWaitPidPollOptions(ChildPid, 0, 0, 2));\r\n\r\n    //\r\n    // Verify that a pipe that is set to non-blocking will block when the\r\n    // splice-specific non-blocking flag is not passed to splice.\r\n    //\r\n\r\n    LxtLogInfo(\"Blocking - Blocking splice with non-blocking pipe\");\r\n    LxtCheckErrno(fcntl(SourcePipe.Read, F_SETFL, O_NONBLOCK));\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Result = SPLICE_SYSCALL(SourcePipe.Read, NULL, DestinationPipe.Write, NULL, 1, 0);\r\n\r\n        _exit(0);\r\n    }\r\n\r\n    sleep(2);\r\n    LxtCheckErrno(WaitPidResult = waitpid(ChildPid, &WaitPidStatus, WNOHANG));\r\n\r\n    //\r\n    // If the child is not alive, it did not block as expected.\r\n    //\r\n\r\n    LxtCheckEqual(WaitPidResult, 0, \"%d\");\r\n    kill(ChildPid, SIGKILL);\r\n    Result = 0;\r\nErrorExit:\r\n    LxtClosePipe(&DestinationPipe);\r\n    LxtClosePipe(&SourcePipe);\r\n    return Result;\r\n}\r\n\r\nint SpliceVariationInvalidParameters(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine tests the splice syscall errors are properly set when invalid\r\n    parameters are passed to it.\r\n\r\nArguments:\r\n\r\n    Args - Supplies a pointer to variation arguments.\r\n\r\nReturn Value:\r\n\r\n    On success, returns open file descriptor; otherwise, -1.\r\n\r\n--*/\r\n{\r\n\r\n    LXT_PIPE DestinationPipe = {-1, -1};\r\n    loff_t ReadOffset;\r\n    int Result;\r\n    LXT_PIPE SourcePipe = {-1, -1};\r\n    int StandardFd;\r\n\r\n    StandardFd = 0;\r\n    LxtCheckErrno(LxtCreatePipe(&SourcePipe));\r\n    LxtCheckErrno(LxtCreatePipe(&DestinationPipe));\r\n    LxtCheckResult(StandardFd = SpliceOpenStandardFile());\r\n\r\n    //\r\n    // Put some random data into the source pipe and set the offset.\r\n    //\r\n\r\n    LxtCheckErrno(write(SourcePipe.Write, \"1234\", 4));\r\n    ReadOffset = 2;\r\n\r\n    //\r\n    // Check that a call with invalid parameters, but a splice size of zero will\r\n    // succeed.\r\n    //\r\n\r\n    LxtLogInfo(\"Invalid Params - Passing invalid parameters with splice size of zero\");\r\n\r\n    LxtCheckErrno(SPLICE_SYSCALL(SourcePipe.Read, &ReadOffset, DestinationPipe.Write, NULL, 0, 0));\r\n\r\n    LxtCheckErrno(SPLICE_SYSCALL(StandardFd, NULL, StandardFd, NULL, 0, 0));\r\n    LxtCheckErrno(SPLICE_SYSCALL(-1, NULL, -1, NULL, 0, 0));\r\n\r\n    //\r\n    // Check that invalid flags do not cause any errors.\r\n    //\r\n\r\n    LxtCheckErrno(SPLICE_SYSCALL(SourcePipe.Read, NULL, DestinationPipe.Write, NULL, 4, 0xF0));\r\n\r\n    //\r\n    // Check the error result when a pipe is given a non-null offset.\r\n    //\r\n\r\n    LxtLogInfo(\"Invalid Params - Passing non-null offset with pipe fd\");\r\n    LxtCheckErrnoFailure(SPLICE_SYSCALL(SourcePipe.Read, &ReadOffset, DestinationPipe.Write, NULL, 1, 0), ESPIPE);\r\n\r\n    LxtCheckErrnoFailure(SPLICE_SYSCALL(SourcePipe.Read, NULL, DestinationPipe.Write, &ReadOffset, 1, 0), ESPIPE);\r\n\r\n    //\r\n    // Ensure that the correct error is returned when splice takes two\r\n    // parameters that are not pipes.\r\n    //\r\n\r\n    LxtLogInfo(\"Invalid Params - Passing two non-pipe fd's to splice\");\r\n    LxtCheckErrnoFailure(SPLICE_SYSCALL(StandardFd, NULL, StandardFd, NULL, 1, 0), EINVAL);\r\n\r\n    //\r\n    // Validate errors returned wrong read\\write pipe\r\n    //\r\n\r\n    //\r\n    // LX_TODO: Here and in the tee syscall variation, the atomic property of\r\n    // tee/splice must be implemented before these tests are enabled.\r\n    //\r\n\r\n    /*\r\n        LxtLogInfo(\"Invalid Params - Invalid splice read\\write pipe 1\");\r\n        LxtCheckErrnoFailure(SPLICE_SYSCALL(SourcePipe.Read,\r\n                                            NULL,\r\n                                            SourcePipe.Read,\r\n                                            NULL,\r\n                                            200,\r\n                                            0),\r\n                             EBADF);\r\n\r\n        LxtLogInfo(\"Invalid Params - Invalid splice read\\write pipe 2\");\r\n        LxtCheckErrnoFailure(SPLICE_SYSCALL(SourcePipe.Write,\r\n                                            NULL,\r\n                                            SourcePipe.Read,\r\n                                            NULL,\r\n                                            200,\r\n                                            0),\r\n                             EBADF);\r\n\r\n        LxtLogInfo(\"Invalid Params - Invalid splice read\\write pipe 3\");\r\n        LxtCheckErrnoFailure(SPLICE_SYSCALL(SourcePipe.Read,\r\n                                            NULL,\r\n                                            DestinationPipe.Read,\r\n                                            NULL,\r\n                                            200,\r\n                                            0),\r\n                             EBADF);\r\n    */\r\n\r\nErrorExit:\r\n    LxtClosePipe(&SourcePipe);\r\n    LxtClosePipe(&DestinationPipe);\r\n    if (StandardFd > 0)\r\n    {\r\n        close(StandardFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TeeVariationBasicTests(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[16];\r\n    LXT_PIPE DestinationPipe = {-1, -1};\r\n    char* PipeData;\r\n    ssize_t PipeDataSize;\r\n    ssize_t ReadSize;\r\n    int Result;\r\n    ssize_t SpliceSize;\r\n    LXT_PIPE SourcePipe = {-1, -1};\r\n\r\n    PipeData = \"1234\";\r\n    PipeDataSize = strlen(PipeData);\r\n    LxtCheckResult(LxtCreatePipe(&DestinationPipe));\r\n    LxtCheckResult(LxtCreatePipe(&SourcePipe));\r\n\r\n    //\r\n    // Set up read-end of pipes as non-blocking for empty tests.\r\n    //\r\n\r\n    LxtCheckErrno(fcntl(SourcePipe.Read, F_SETFL, O_NONBLOCK));\r\n    LxtCheckErrno(fcntl(DestinationPipe.Read, F_SETFL, O_NONBLOCK));\r\n\r\n    //\r\n    // Perform basic tests between pipes.\r\n    //\r\n\r\n    LxtLogInfo(\"Basic Usage - Tee\");\r\n    LxtCheckErrno(write(SourcePipe.Write, PipeData, PipeDataSize));\r\n    LxtCheckErrno(SpliceSize = TEE_SYSCALL(SourcePipe.Read, DestinationPipe.Write, PipeDataSize, SPLICE_F_NONBLOCK));\r\n\r\n    LxtCheckEqual(SpliceSize, PipeDataSize, \"%d\");\r\n\r\n    //\r\n    // Check that the read pipe still has the same data as before.\r\n    //\r\n\r\n    LxtCheckErrno(ReadSize = read(SourcePipe.Read, Buffer, PipeDataSize));\r\n    LxtCheckEqual(ReadSize, PipeDataSize, \"%d\");\r\n    Buffer[ReadSize] = '\\0';\r\n    LxtCheckStringEqual(Buffer, PipeData);\r\n\r\n    //\r\n    // Check that the destination pipe has the correct data after the tee.\r\n    //\r\n\r\n    LxtCheckErrno(ReadSize = read(DestinationPipe.Read, Buffer, PipeDataSize));\r\n    LxtCheckEqual(ReadSize, PipeDataSize, \"%d\");\r\n    Buffer[ReadSize] = '\\0';\r\n    LxtCheckStringEqual(Buffer, PipeData);\r\n\r\nErrorExit:\r\n    LxtClosePipe(&SourcePipe);\r\n    LxtClosePipe(&DestinationPipe);\r\n    return Result;\r\n}\r\n\r\nint TeeVariationInvalidParameters(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_PIPE DestinationPipe = {-1, -1};\r\n    int Result;\r\n    LXT_PIPE SourcePipe = {-1, -1};\r\n    int StandardFd;\r\n\r\n    StandardFd = 0;\r\n    LxtCheckErrno(LxtCreatePipe(&SourcePipe));\r\n    LxtCheckErrno(LxtCreatePipe(&DestinationPipe));\r\n    LxtCheckResult(StandardFd = SpliceOpenStandardFile());\r\n\r\n    //\r\n    // Put some random data into the source pipe and set the offset.\r\n    //\r\n\r\n    LxtCheckErrno(write(SourcePipe.Write, \"1234\", 4));\r\n\r\n    //\r\n    // Check that a call with invalid parameters, but a size of zero will\r\n    // succeed.\r\n    //\r\n\r\n    LxtLogInfo(\"Invalid Params - Passing invalid parameters to tee with size of zero\");\r\n\r\n    LxtCheckErrno(TEE_SYSCALL(StandardFd, DestinationPipe.Write, 0, 0));\r\n    LxtCheckErrno(TEE_SYSCALL(SourcePipe.Read, StandardFd, 0, 0));\r\n    LxtCheckErrno(TEE_SYSCALL(StandardFd, StandardFd, 0, 0));\r\n\r\n    //\r\n    // Check that invalid flags do not cause any errors.\r\n    //\r\n\r\n    LxtCheckErrno(TEE_SYSCALL(SourcePipe.Read, DestinationPipe.Write, 4, 0xF0));\r\n    //\r\n    // Validate the errors returned with invalid parameters and non-zero splice\r\n    // sizes.\r\n    //\r\n\r\n    LxtLogInfo(\"Invalid Params - Passing a non-pipe to a tee syscall\");\r\n    LxtCheckErrnoFailure(TEE_SYSCALL(StandardFd, DestinationPipe.Write, 1, 0), EINVAL);\r\n\r\n    LxtCheckErrnoFailure(TEE_SYSCALL(SourcePipe.Read, StandardFd, 1, 0), EINVAL);\r\n\r\n    //\r\n    // Validate errors returned sending to the wrong read\\write pipe\r\n    //\r\n\r\nErrorExit:\r\n    LxtClosePipe(&SourcePipe);\r\n    LxtClosePipe(&DestinationPipe);\r\n    if (StandardFd > 0)\r\n    {\r\n        close(StandardFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/sysfs.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    sysfs.c\r\n\r\nAbstract:\r\n\r\n    This file contains tests for the sysfs file system.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <dirent.h>\r\n#include <fcntl.h>\r\n\r\n#define LXT_NAME \"SysFs\"\r\n\r\n#define SYSFS_MNT \"/sys\"\r\n\r\nint SysFsClassNet(PLXT_ARGS Args);\r\n\r\nint SysFsDevicesSystemCpu(PLXT_ARGS Args);\r\n\r\nint SysFsDevicesVirtualNet(PLXT_ARGS Args);\r\n\r\nint SysFsKernelDebug(PLXT_ARGS Args);\r\n\r\nint SysFsRoot(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"SysFs - /sys root\", SysFsRoot},\r\n    {\"SysFs - /sys/class/net\", SysFsClassNet},\r\n    {\"SysFs - /sys/devices/virtual/net\", SysFsDevicesVirtualNet},\r\n    {\"SysFs - /sys/devices/system/cpu\", SysFsDevicesSystemCpu},\r\n    {\"SysFs - /sys/kernel/debug\", SysFsKernelDebug},\r\n};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsRootChildren[] = {\r\n    {\"block\", DT_DIR},\r\n    {\"bus\", DT_DIR},\r\n    {\"class\", DT_DIR},\r\n    {\"dev\", DT_DIR},\r\n    {\"devices\", DT_DIR},\r\n    {\"firmware\", DT_DIR},\r\n    {\"fs\", DT_DIR},\r\n    {\"kernel\", DT_DIR},\r\n    {\"module\", DT_DIR},\r\n    {\"power\", DT_DIR}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsClassNetChildren[] = {{\"lo\", DT_LNK}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsDevicesVirtualNetChildren[] = {{\"lo\", DT_DIR}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsDevicesVirtualNetDeviceChildren[] = {\r\n    {\"address\", DT_REG}, {\"ifindex\", DT_REG}, {\"flags\", DT_REG}, {\"mtu\", DT_REG}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsDevicesSystemCpuChildren[] = {{\"cpu0\", DT_DIR}, {\"present\", DT_REG}, {\"possible\", DT_REG}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsDevicesSystemCpuDeviceChildren[] = {{\"topology\", DT_DIR}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsDevicesSystemCpuDeviceCpuFreqChildren[] = {{\"cpuinfo_max_freq\", DT_REG}, {\"scaling_max_freq\", DT_REG}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsDevicesSystemCpuDeviceTopologyChildren[] = {\r\n    {\"core_id\", DT_REG},\r\n    {\"core_siblings\", DT_REG},\r\n    {\"core_siblings_list\", DT_REG},\r\n    {\"physical_package_id\", DT_REG},\r\n    {\"thread_siblings\", DT_REG},\r\n    {\"thread_siblings_list\", DT_REG}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsKernelDebugChildren[] = {{\"tracing\", DT_DIR}, {\"wakeup_sources\", DT_REG}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsKernelDebugTracingChildren[] = {{\"trace_marker\", DT_REG}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsKernelIp4Children[] = {\r\n    {\"tcp_rmem_min\", DT_REG}, {\"tcp_rmem_def\", DT_REG}, {\"tcp_rmem_max\", DT_REG}, {\"tcp_wmem_min\", DT_REG}, {\"tcp_wmem_def\", DT_REG}, {\"tcp_wmem_max\", DT_REG}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsModuleLowmemorykillerChildren[] = {{\"parameters\", DT_DIR}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsModuleLowmemorykillerParametersChildren[] = {{\"adj\", DT_REG}, {\"minfree\", DT_REG}};\r\n\r\nstatic const LXT_CHILD_INFO g_SysFsPowerChildren[] = {{\"autosleep\", DT_REG}};\r\n\r\nint SysfsTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine is the main entry point for the procfs tests.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint SysFsClassNet(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the sysfs network class directory (/sys/class/net).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    //\r\n    // This test may fail on real Linux because the contents are not\r\n    // guaranteed to be the same on every system.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(SYSFS_MNT \"/class/net\", g_SysFsClassNetChildren, LXT_COUNT_OF(g_SysFsClassNetChildren)));\r\n\r\n    LxtCheckResult(LxtCheckLinkTarget(SYSFS_MNT \"/class/net/lo\", \"../../devices/virtual/net/lo\"));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SysFsDevicesSystemCpu(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the cpu device directory (/sys/devices/system/cpu).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    char ChildPath[256];\r\n    int CpuInfoProcessorCount;\r\n    ssize_t BytesRead;\r\n    int Fd;\r\n    char* Line;\r\n    int ProcessorIndex;\r\n    int ProcessorCount;\r\n    int Result;\r\n    size_t Size;\r\n    FILE* Stream;\r\n\r\n    //\r\n    // First check always present contents.\r\n    //\r\n\r\n    Fd = 0;\r\n    Stream = NULL;\r\n    Line = NULL;\r\n    LxtCheckResult(LxtCheckDirectoryContents(\r\n        SYSFS_MNT \"/devices/system/cpu\", g_SysFsDevicesSystemCpuChildren, LXT_COUNT_OF(g_SysFsDevicesSystemCpuChildren)));\r\n\r\n    //\r\n    // Determine the number of CPUs.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(SYSFS_MNT \"/devices/system/cpu/present\", O_RDONLY));\r\n    LxtCheckErrno(BytesRead = read(Fd, Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Buffer[0], '0', \"%c\");\r\n    ProcessorCount = 1;\r\n    if (Buffer[1] != '\\n')\r\n    {\r\n        LxtCheckEqual(Buffer[1], '-', \"%c\");\r\n        ProcessorCount = strtol(&Buffer[2], NULL, 10);\r\n        LxtCheckGreater(ProcessorCount, 0, \"%d\");\r\n        ProcessorCount += 1;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n\r\n    //\r\n    // Check CPU directory for each CPU.\r\n    //\r\n\r\n    for (ProcessorIndex = 0; ProcessorIndex < ProcessorCount; ProcessorIndex += 1)\r\n    {\r\n\r\n        snprintf(ChildPath, sizeof(ChildPath), SYSFS_MNT \"/devices/system/cpu/cpu%d\", ProcessorIndex);\r\n\r\n        LxtCheckResult(LxtCheckDirectoryContents(\r\n            ChildPath, g_SysFsDevicesSystemCpuDeviceChildren, LXT_COUNT_OF(g_SysFsDevicesSystemCpuDeviceChildren)));\r\n\r\n        snprintf(ChildPath, sizeof(ChildPath), SYSFS_MNT \"/devices/system/cpu/cpu%d/topology\", ProcessorIndex);\r\n\r\n        LxtCheckResult(LxtCheckDirectoryContents(\r\n            ChildPath, g_SysFsDevicesSystemCpuDeviceTopologyChildren, LXT_COUNT_OF(g_SysFsDevicesSystemCpuDeviceTopologyChildren)));\r\n    }\r\n\r\n    //\r\n    // No more directories should exist.\r\n    //\r\n\r\n    snprintf(ChildPath, sizeof(ChildPath), SYSFS_MNT \"/devices/system/cpu/cpu%d\", ProcessorCount);\r\n\r\n    LxtCheckErrnoFailure(open(ChildPath, O_RDONLY | O_DIRECTORY), ENOENT);\r\n\r\n    //\r\n    // Check if the number of CPUs matches /proc/cpuinfo.\r\n    //\r\n\r\n    Stream = fopen(\"/proc/cpuinfo\", \"r\");\r\n    if (Stream == NULL)\r\n    {\r\n        LxtLogError(\"fopen failed, errno: %d\", errno);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Size = 0;\r\n    CpuInfoProcessorCount = 0;\r\n    while (getline(&Line, &Size, Stream) != -1)\r\n    {\r\n        if (strstr(Line, \"processor\") == Line)\r\n        {\r\n            CpuInfoProcessorCount += 1;\r\n        }\r\n    }\r\n\r\n    LxtCheckEqual(ProcessorCount, CpuInfoProcessorCount, \"%d\");\r\n\r\nErrorExit:\r\n    if (Line != NULL)\r\n    {\r\n        free(Line);\r\n    }\r\n\r\n    if (Stream != NULL)\r\n    {\r\n        fclose(Stream);\r\n    }\r\n\r\n    if (Fd > 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SysFsDevicesVirtualNet(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the sysfs network device directory\r\n    (/sys/devices/virtual/net).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    //\r\n    // This test may fail on real Linux because the contents are not\r\n    // guaranteed to be the same on every system.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(\r\n        SYSFS_MNT \"/devices/virtual/net\", g_SysFsDevicesVirtualNetChildren, LXT_COUNT_OF(g_SysFsDevicesVirtualNetChildren)));\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(\r\n        SYSFS_MNT \"/devices/virtual/net/lo\", g_SysFsDevicesVirtualNetDeviceChildren, LXT_COUNT_OF(g_SysFsDevicesVirtualNetDeviceChildren)));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SysFsKernelDebug(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the debug directory (/sys/kernel/debug).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(SYSFS_MNT \"/kernel/debug\", g_SysFsKernelDebugChildren, LXT_COUNT_OF(g_SysFsKernelDebugChildren)));\r\n\r\n    LxtCheckResult(LxtCheckDirectoryContents(\r\n        SYSFS_MNT \"/kernel/debug/tracing\", g_SysFsKernelDebugTracingChildren, LXT_COUNT_OF(g_SysFsKernelDebugTracingChildren)));\r\n\r\n    LxtCheckResult(LxtCheckWrite(SYSFS_MNT \"/kernel/debug/tracing/trace_marker\", \"bogus\"));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint SysFsRoot(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the sysfs root directory (/sys).\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtCheckStat(SYSFS_MNT, 1, DT_DIR));\r\n    LxtCheckResult(LxtCheckDirectoryContents(SYSFS_MNT, g_SysFsRootChildren, LXT_COUNT_OF(g_SysFsRootChildren)));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/sysinfo.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Sysinfo.c\r\n\r\nAbstract:\r\n\r\n    This file is a sysinfo test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include \"linux/kernel.h\"\r\n\r\n#define LXT_NAME \"Sysinfo\"\r\n\r\nextern int sysinfo(struct sysinfo* info);\r\n\r\nint SysinfoVariationPrint(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {{\"SysinfoVariationPrint\", SysinfoVariationPrint}};\r\n\r\nint SysInfoTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtInitialize(Argc, Argv, &Args, LXT_NAME);\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint SysinfoVariationPrint(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    struct sysinfo SysInfo;\r\n\r\n    LxtCheckErrnoFailure(sysinfo(NULL), EFAULT);\r\n    LxtCheckErrno(sysinfo(&SysInfo));\r\n    LxtLogInfo(\r\n        \"SysInfo.uptime: %d\\n\"\r\n        \"SysInfo.loads[0]: %d\\n\"\r\n        \"SysInfo.loads[1]: %d\\n\"\r\n        \"SysInfo.loads[2]: %d\\n\"\r\n        \"SysInfo.totalram: %d\\n\"\r\n        \"SysInfo.freeram: %d\\n\"\r\n        \"SysInfo.sharedram: %d\\n\"\r\n        \"SysInfo.bufferram: %d\\n\"\r\n        \"SysInfo.totalswap: %d\\n\"\r\n        \"SysInfo.freeswap: %d\\n\"\r\n        \"SysInfo.procs: %d\\n\"\r\n        \"SysInfo.pad: %d\\n\"\r\n        \"SysInfo.totalhigh: %d\\n\"\r\n        \"SysInfo.freehigh: %d\\n\"\r\n        \"SysInfo.mem_unit: %d\\n\",\r\n        SysInfo.uptime,\r\n        SysInfo.loads[0],\r\n        SysInfo.loads[1],\r\n        SysInfo.loads[2],\r\n        SysInfo.totalram,\r\n        SysInfo.freeram,\r\n        SysInfo.sharedram,\r\n        SysInfo.bufferram,\r\n        SysInfo.totalswap,\r\n        SysInfo.freeswap,\r\n        SysInfo.procs,\r\n        SysInfo.pad,\r\n        SysInfo.totalhigh,\r\n        SysInfo.freehigh,\r\n        SysInfo.mem_unit);\r\n\r\n    LxtCheckGreater(SysInfo.uptime, 0, \"%d\");\r\n    LxtCheckEqual(SysInfo.loads[0], 33984, \"%d\");\r\n    LxtCheckEqual(SysInfo.loads[1], 37856, \"%d\");\r\n    LxtCheckEqual(SysInfo.loads[2], 38400, \"%d\");\r\n    LxtCheckGreater(SysInfo.totalram, 0, \"%d\");\r\n    LxtCheckGreater(SysInfo.freeram, 0, \"%d\");\r\n    LxtCheckEqual(SysInfo.sharedram, 0, \"%d\");\r\n    LxtCheckEqual(SysInfo.bufferram, 0, \"%d\");\r\n    LxtCheckGreater(SysInfo.totalswap, 0, \"%d\");\r\n    LxtCheckGreater(SysInfo.freeswap, 0, \"%d\");\r\n    LxtCheckGreater(SysInfo.procs, 1, \"%d\"); // TODO: change back to 2, right now there is only a single process running.\r\n    LxtCheckEqual(SysInfo.pad, 0, \"%d\");\r\n    LxtCheckEqual(SysInfo.totalhigh, 139208 * 1024, \"%d\");\r\n    LxtCheckEqual(SysInfo.freehigh, 272 * 1024, \"%d\");\r\n    LxtCheckEqual(SysInfo.mem_unit, 1, \"%d\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/timer.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    timer.c\r\n\r\nAbstract:\r\n\r\n    This file is a timer test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <signal.h>\r\n#include <time.h>\r\n#include <sys/resource.h>\r\n#include <sys/types.h>\r\n#include <sys/wait.h>\r\n#include <sys/time.h>\r\n#include <limits.h>\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n\r\n#define LXT_NAME \"timer\"\r\n\r\n#define LXT_SHORT_TIMER 1\r\n#define LXT_SHORT_TIMER_WAIT_PID 5\r\n#define LXT_SHORT_TIMER_US 250000\r\n#define LXT_LONG_TIMER 10\r\n\r\n#define LXT_INVALID_TIMER_ID ((timer_t) - 1)\r\n\r\nint AlarmSyscall(PLXT_ARGS Args);\r\n\r\nint ITimerInvalidParam(PLXT_ARGS Args);\r\n\r\nint ITimerPerThreadGroup(PLXT_ARGS Args);\r\n\r\nint ITimerSignal(PLXT_ARGS Args);\r\n\r\nint ITimerPeriodicSignal(PLXT_ARGS Args);\r\n\r\nint NanosleepInvalidParam(PLXT_ARGS Args);\r\n\r\nint TimerCreateSyscall(PLXT_ARGS Args);\r\n\r\nint TimerCreateInvalidParam(PLXT_ARGS Args);\r\n\r\nint ClockGetTimeAlignment(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"nanosleep invalid param\", NanosleepInvalidParam},\r\n    {\"ITimerPerThreadGroup\", ITimerPerThreadGroup},\r\n    {\"ITimerSignal\", ITimerSignal},\r\n    {\"AlarmSyscall\", AlarmSyscall},\r\n    {\"ITimerPeriodicSignal\", ITimerPeriodicSignal},\r\n    {\"ITimer invalid param\", ITimerInvalidParam},\r\n    {\"timer_create\", TimerCreateSyscall},\r\n    {\"timer_create invalid param\", TimerCreateInvalidParam},\r\n    {\"clock_gettime alignment\", ClockGetTimeAlignment}};\r\n\r\nstatic const struct itimerval g_ZeroTimer;\r\n\r\n//\r\n// Global variables\r\n//\r\n\r\nstatic struct timespec g_SignalTime;\r\nstatic int g_SignalCount;\r\n\r\nint TimerTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY();\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint ITimerInvalidParam(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct itimerval NewTimer;\r\n    int Result;\r\n\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    NewTimer.it_value.tv_sec = -1;\r\n    LxtCheckErrnoFailure(setitimer(ITIMER_REAL, &NewTimer, NULL), EINVAL);\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    NewTimer.it_value.tv_usec = 999999 + 1;\r\n    LxtCheckErrnoFailure(setitimer(ITIMER_REAL, &NewTimer, NULL), EINVAL);\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    NewTimer.it_interval.tv_sec = -1;\r\n    LxtCheckErrnoFailure(setitimer(ITIMER_REAL, &NewTimer, NULL), EINVAL);\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    NewTimer.it_interval.tv_usec = 999999 + 1;\r\n    LxtCheckErrnoFailure(setitimer(ITIMER_REAL, &NewTimer, NULL), EINVAL);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid* ITimerPerThreadGroupWorker(void* ptr)\r\n\r\n{\r\n\r\n    struct itimerval NewTimer;\r\n    struct itimerval OldTimer;\r\n    int Result;\r\n\r\n    //\r\n    // Check that the timer is per threadgroup and the result is the remaining\r\n    // time.\r\n    //\r\n\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    memset(&OldTimer, 1, sizeof(OldTimer));\r\n    sleep(1);\r\n    LxtCheckResult(setitimer(ITIMER_REAL, &NewTimer, &OldTimer));\r\n    if (OldTimer.it_value.tv_sec >= LXT_LONG_TIMER)\r\n    {\r\n        Result = 1;\r\n        LxtLogError(\"Unexpected OldTimer %d\", OldTimer.it_value.tv_sec);\r\n        goto ErrorExit;\r\n    }\r\n\r\nErrorExit:\r\n    return 0;\r\n}\r\n\r\nint ITimerPerThreadGroup(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Result;\r\n    struct itimerval NewTimer;\r\n    struct itimerval OldTimer;\r\n    pthread_t Thread = {0};\r\n\r\n    //\r\n    // Check that the timer is per threadgroup and not preserved across fork.\r\n    //\r\n\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    NewTimer.it_value.tv_sec = LXT_LONG_TIMER;\r\n    memset(&OldTimer, 1, sizeof(OldTimer));\r\n    LxtCheckResult(setitimer(ITIMER_REAL, &NewTimer, &OldTimer));\r\n    LxtCheckResult(LxtCompareMemory((void*)&g_ZeroTimer, (void*)&OldTimer, sizeof(g_ZeroTimer), \"Zero\", \"Initial\"));\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&OldTimer, 1, sizeof(OldTimer));\r\n        LxtCheckResult(setitimer(ITIMER_REAL, &NewTimer, &OldTimer));\r\n        LxtCheckResult(LxtCompareMemory((void*)&g_ZeroTimer, (void*)&OldTimer, sizeof(g_ZeroTimer), \"Zero\", \"Initial child\"));\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(pthread_create(&Thread, NULL, ITimerPerThreadGroupWorker, NULL));\r\n    pthread_join(Thread, NULL);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid ITimerSignalHandler(int Signal)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    if (Signal == SIGALRM)\r\n    {\r\n        LxtClockGetTime(CLOCK_REALTIME, &g_SignalTime);\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint ITimerSignal(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct sigaction Action;\r\n    int ChildPid;\r\n    int ExpectedWaitStatus;\r\n    int Result;\r\n    struct timespec StartTime;\r\n    struct itimerval NewTimer;\r\n\r\n    //\r\n    // Check the different dispositions of the SIGALRM signal and cancelling the\r\n    // timer.\r\n    //\r\n\r\n    //\r\n    // Default disposition should terminate\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&NewTimer, 0, sizeof(NewTimer));\r\n        NewTimer.it_value.tv_sec = LXT_SHORT_TIMER;\r\n        LxtCheckResult(setitimer(ITIMER_REAL, &NewTimer, NULL));\r\n        sleep(LXT_SHORT_TIMER * 2);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, SIGALRM, 0, LXT_SHORT_TIMER_WAIT_PID));\r\n\r\n    //\r\n    // Default disposition should not terminate if canceled.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&NewTimer, 0, sizeof(NewTimer));\r\n        NewTimer.it_value.tv_sec = LXT_SHORT_TIMER;\r\n        LxtCheckResult(setitimer(ITIMER_REAL, &NewTimer, NULL));\r\n        memset(&NewTimer, 0, sizeof(NewTimer));\r\n        LxtCheckResult(setitimer(ITIMER_REAL, &NewTimer, NULL));\r\n        sleep(LXT_SHORT_TIMER * 2);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, LXT_SHORT_TIMER_WAIT_PID));\r\n\r\n    //\r\n    // Ignored should not terminate.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&Action, 0, sizeof(Action));\r\n        Action.sa_handler = SIG_IGN;\r\n        LxtCheckErrnoZeroSuccess(sigaction(SIGALRM, &Action, NULL));\r\n        memset(&NewTimer, 0, sizeof(NewTimer));\r\n        NewTimer.it_value.tv_sec = LXT_SHORT_TIMER;\r\n        LxtCheckResult(setitimer(ITIMER_REAL, &NewTimer, NULL));\r\n        sleep(LXT_SHORT_TIMER * 2);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, LXT_SHORT_TIMER_WAIT_PID));\r\n\r\n    //\r\n    // Check that the signal handler is invoked within a reasonable time\r\n    // interval.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&Action, 0, sizeof(Action));\r\n        Action.sa_handler = ITimerSignalHandler;\r\n        LxtCheckErrnoZeroSuccess(sigaction(SIGALRM, &Action, NULL));\r\n        memset(&g_SignalTime, 0, sizeof(g_SignalTime));\r\n        memset(&NewTimer, 0, sizeof(NewTimer));\r\n        NewTimer.it_value.tv_sec = LXT_SHORT_TIMER;\r\n        LxtClockGetTime(CLOCK_REALTIME, &StartTime);\r\n        LxtCheckResult(setitimer(ITIMER_REAL, &NewTimer, NULL));\r\n        sleep(LXT_SHORT_TIMER * 2);\r\n        if (g_SignalTime.tv_sec - StartTime.tv_sec != 1)\r\n        {\r\n            LxtLogError(\"Unexpected seconds elapsed %d\", g_SignalTime.tv_sec - StartTime.tv_sec);\r\n\r\n            _exit(1);\r\n        }\r\n\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, LXT_SHORT_TIMER_WAIT_PID));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint AlarmSyscall(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct sigaction Action;\r\n    int ChildPid;\r\n    int ExpectedWaitStatus;\r\n    int Result;\r\n    struct timespec StartTime;\r\n\r\n    //\r\n    // Check the different dispositions of the SIGALRM signal and cancelling the\r\n    // timer.\r\n    //\r\n\r\n    //\r\n    // Default disposition should terminate\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(alarm(LXT_SHORT_TIMER));\r\n        sleep(LXT_SHORT_TIMER * 2);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, SIGALRM, 0, LXT_SHORT_TIMER_WAIT_PID));\r\n\r\n    //\r\n    // Default disposition should not terminate if canceled.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckResult(alarm(LXT_SHORT_TIMER));\r\n        LxtCheckResult(Result = alarm(0));\r\n        if (Result > LXT_SHORT_TIMER)\r\n        {\r\n            LxtLogError(\"Unexpected value for previously armed timer %u\", Result);\r\n            _exit(1);\r\n        }\r\n\r\n        sleep(LXT_SHORT_TIMER * 2);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, LXT_SHORT_TIMER_WAIT_PID));\r\n\r\n    //\r\n    // Ignored should not terminate.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&Action, 0, sizeof(Action));\r\n        Action.sa_handler = SIG_IGN;\r\n        LxtCheckErrnoZeroSuccess(sigaction(SIGALRM, &Action, NULL));\r\n        LxtCheckResult(alarm(LXT_SHORT_TIMER));\r\n        sleep(LXT_SHORT_TIMER * 2);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, LXT_SHORT_TIMER_WAIT_PID));\r\n\r\n    //\r\n    // Check that the signal handler is invoked within a reasonable time\r\n    // interval.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&Action, 0, sizeof(Action));\r\n        Action.sa_handler = ITimerSignalHandler;\r\n        LxtCheckErrnoZeroSuccess(sigaction(SIGALRM, &Action, NULL));\r\n        memset(&g_SignalTime, 0, sizeof(g_SignalTime));\r\n        LxtClockGetTime(CLOCK_REALTIME, &StartTime);\r\n        LxtCheckResult(alarm(LXT_SHORT_TIMER));\r\n        sleep(LXT_SHORT_TIMER * 2);\r\n        if (g_SignalTime.tv_sec - StartTime.tv_sec != 1)\r\n        {\r\n            LxtLogError(\"Unexpected seconds elapsed %d\", g_SignalTime.tv_sec - StartTime.tv_sec);\r\n\r\n            _exit(1);\r\n        }\r\n\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, LXT_SHORT_TIMER_WAIT_PID));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid ITimerPeriodicSignalHandler(int Signal)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n    if (Signal == SIGALRM)\r\n    {\r\n        ++g_SignalCount;\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint ITimerPeriodicSignal(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct sigaction Action;\r\n    int ChildPid;\r\n    int ExpectedWaitStatus;\r\n    int Result;\r\n    struct itimerval NewTimer;\r\n\r\n    //\r\n    // Check that the signal handler is invoked within a reasonable time\r\n    // interval.\r\n    //\r\n\r\n    LxtCheckResult(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        memset(&Action, 0, sizeof(Action));\r\n        Action.sa_handler = ITimerPeriodicSignalHandler;\r\n        LxtCheckErrnoZeroSuccess(sigaction(SIGALRM, &Action, NULL));\r\n        memset(&g_SignalCount, 0, sizeof(g_SignalCount));\r\n        memset(&NewTimer, 0, sizeof(NewTimer));\r\n        NewTimer.it_value.tv_sec = LXT_SHORT_TIMER;\r\n        NewTimer.it_interval.tv_usec = LXT_SHORT_TIMER_US;\r\n        LxtCheckResult(setitimer(ITIMER_REAL, &NewTimer, NULL));\r\n        while (sleep(LXT_SHORT_TIMER * 2) != LXT_SHORT_TIMER * 2)\r\n        {\r\n            LxtLogInfo(\"Periodic timer detected: %d\", g_SignalCount);\r\n            if (g_SignalCount >= 3)\r\n            {\r\n                break;\r\n            }\r\n        }\r\n\r\n        LxtLogInfo(\"Periodic timer count: %d\", g_SignalCount);\r\n        _exit(LXT_RESULT_SUCCESS);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, LXT_SHORT_TIMER_WAIT_PID));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint NanosleepInvalidParam(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct timespec SleepDuration;\r\n    int Result;\r\n\r\n    SleepDuration.tv_sec = 0;\r\n    SleepDuration.tv_nsec = 999999999;\r\n    LxtCheckErrno(nanosleep(&SleepDuration, NULL));\r\n\r\n    //\r\n    // N.B. The clock_nanosleep system call returns error codes on failure\r\n    //      instead of setting errno.\r\n    //\r\n\r\n    LxtCheckEqual(0, clock_nanosleep(CLOCK_MONOTONIC, 0, &SleepDuration, NULL), \"%d\");\r\n\r\n    SleepDuration.tv_nsec += 1;\r\n    LxtCheckErrnoFailure(nanosleep(&SleepDuration, NULL), EINVAL);\r\n\r\n    //\r\n    // N.B. The clock_nanosleep system call returns error codes on failure\r\n    //      instead of setting errno.\r\n    //\r\n\r\n    LxtCheckEqual(EINVAL, clock_nanosleep(CLOCK_MONOTONIC, 0, &SleepDuration, NULL), \"%d\");\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid TimerCreateHandler(int Signal, siginfo_t* SignalInfo, void* SignalContext)\r\n{\r\n\r\n    int Overrun;\r\n    int Result;\r\n    timer_t TimerId;\r\n\r\n    LxtLogInfo(\"Caught signal %d\", Signal);\r\n\r\n    TimerId = *((timer_t*)SignalInfo->si_value.sival_ptr);\r\n    LxtLogInfo(\"SignalInfo->si_value.sival_ptr = %p\", SignalInfo->si_value.sival_ptr);\r\n    LxtLogInfo(\"*SignalInfo->si_value.sival_ptr = %d\", (long)TimerId);\r\n    LxtLogInfo(\"SignalInfo->si_overrun count = %d\", SignalInfo->si_overrun);\r\n    LxtCheckResult(Overrun = LxtTimer_GetOverrun(TimerId));\r\n    LxtLogInfo(\"timer_getoverrun count = %d\", Overrun);\r\n    LxtCheckEqual(Overrun, SignalInfo->si_overrun, \"%d\");\r\n    LxtLogInfo(\"SignalInfo->si_timerid = %d\", SignalInfo->_sifields._timer);\r\n\r\nErrorExit:\r\n    signal(Signal, SIG_IGN);\r\n    return;\r\n}\r\n\r\nvoid* TimerCreateSyscallThread(void* Parameter)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine is the timer create thread callback.\r\n\r\nArguments:\r\n\r\n    Parameter - Supplies the parameter.\r\n\r\nReturn Value:\r\n\r\n    0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    timer_t TimerId;\r\n    int Result;\r\n    struct itimerspec TimerSpec;\r\n\r\n    //\r\n    // Query the timer and validate that it is not set.\r\n    //\r\n\r\n    TimerId = (timer_t)Parameter;\r\n    LxtCheckResult(LxtTimer_GetTime(TimerId, &TimerSpec));\r\n    LxtCheckEqual(TimerSpec.it_value.tv_sec, 0, \"%lx\");\r\n    LxtCheckEqual(TimerSpec.it_value.tv_nsec, 0, \"%lx\");\r\n    LxtCheckEqual(TimerSpec.it_interval.tv_sec, 0, \"%lx\");\r\n    LxtCheckEqual(TimerSpec.it_interval.tv_nsec, 0, \"%lx\");\r\n\r\nErrorExit:\r\n    return (void*)(long)Result;\r\n}\r\n\r\nint TimerCreateSyscall(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct itimerspec OldTimerSpec;\r\n    int Overrun;\r\n    int Result;\r\n    struct sigaction SigAction;\r\n    int Signal;\r\n    int SignalToTest;\r\n    struct sigevent SigEvent;\r\n    siginfo_t SignalInfo;\r\n    sigset_t SigMask;\r\n    pthread_t Thread;\r\n    void* ThreadReturn;\r\n    timer_t TimerId;\r\n    struct itimerspec TimerSpec;\r\n\r\n    SignalToTest = SIGRTMIN;\r\n    TimerId = (timer_t)-1;\r\n\r\n    //\r\n    // Create signal handler and temporarily block signal delivery.\r\n    //\r\n\r\n    SigAction.sa_flags = SA_SIGINFO;\r\n    SigAction.sa_sigaction = TimerCreateHandler;\r\n    sigemptyset(&SigAction.sa_mask);\r\n    LxtCheckResult(sigaction(SignalToTest, &SigAction, NULL));\r\n\r\n    sigemptyset(&SigMask);\r\n    sigaddset(&SigMask, SignalToTest);\r\n    LxtCheckResult(sigprocmask(SIG_SETMASK, &SigMask, NULL));\r\n\r\n    //\r\n    // Create the timer.\r\n    //\r\n\r\n    SigEvent.sigev_value.sival_ptr = &TimerId;\r\n    SigEvent.sigev_signo = SignalToTest;\r\n    SigEvent.sigev_notify = SIGEV_SIGNAL;\r\n    LxtCheckResult(LxtTimer_Create(CLOCK_REALTIME, &SigEvent, &TimerId));\r\n    LxtLogInfo(\"create_timer TimerId = %d\", TimerId);\r\n\r\n    //\r\n    // Set the timer - verify that the initial timer state is all zeros.\r\n    //\r\n\r\n    memset(&TimerSpec, 0, sizeof(TimerSpec));\r\n    TimerSpec.it_value.tv_sec = 1;\r\n    LxtCheckResult(LxtTimer_SetTime(TimerId, 0, &TimerSpec, &OldTimerSpec));\r\n    LxtCheckEqual(OldTimerSpec.it_value.tv_sec, 0, \"%lx\");\r\n    LxtCheckEqual(OldTimerSpec.it_value.tv_nsec, 0, \"%lx\");\r\n    LxtCheckEqual(OldTimerSpec.it_interval.tv_sec, 0, \"%lx\");\r\n    LxtCheckEqual(OldTimerSpec.it_interval.tv_nsec, 0, \"%lx\");\r\n\r\n    //\r\n    // Query the time of the timer that was just created.\r\n    //\r\n\r\n    LxtCheckResult(LxtTimer_GetTime(TimerId, &OldTimerSpec));\r\n    LxtLogInfo(\"OldTimerSpec.it_value.tv_sec = 0x%lx\", OldTimerSpec.it_value.tv_sec);\r\n    LxtLogInfo(\"OldTimerSpec.it_value.tv_nsec = 0x%lx\", OldTimerSpec.it_value.tv_nsec);\r\n    LxtLogInfo(\"OldTimerSpec.it_interval.tv_sec = 0x%lx\", OldTimerSpec.it_interval.tv_sec);\r\n    LxtLogInfo(\"OldTimerSpec.it_interval.tv_nsec = 0x%lx\", OldTimerSpec.it_interval.tv_nsec);\r\n    if ((OldTimerSpec.it_value.tv_sec > 1) || ((OldTimerSpec.it_value.tv_sec < 1) && (OldTimerSpec.it_value.tv_nsec == 0)))\r\n    {\r\n\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"timer_gettime returned tv_sec %lx tv_nsec %lx\", OldTimerSpec.it_value.tv_sec, OldTimerSpec.it_value.tv_nsec);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckEqual(OldTimerSpec.it_interval.tv_sec, 0, \"%lx\");\r\n    LxtCheckEqual(OldTimerSpec.it_interval.tv_nsec, 0, \"%lx\");\r\n\r\n    //\r\n    // Unblock signal delivery.\r\n    //\r\n\r\n    LxtCheckResult(sigprocmask(SIG_UNBLOCK, &SigMask, NULL));\r\n\r\n    //\r\n    // Wait for the signal to be delivered.\r\n    //\r\n\r\n    LxtCheckErrno(Signal = sigtimedwait(&SigMask, &SignalInfo, NULL));\r\n    LxtCheckEqual(Signal, SignalToTest, \"%d\");\r\n    LxtCheckEqual(SignalInfo.si_signo, SignalToTest, \"%d\");\r\n    LxtCheckEqual(SignalInfo.si_code, SI_TIMER, \"%d\");\r\n    LxtLogInfo(\"SignalInfo->si_value.sival_ptr = %p\", SignalInfo.si_value.sival_ptr);\r\n    LxtLogInfo(\"SignalInfo->si_value.sival_int = %d\", SignalInfo.si_value.sival_int);\r\n    LxtLogInfo(\"SignalInfo->si_overrun = %d\", SignalInfo.si_overrun);\r\n    LxtCheckResult(Overrun = LxtTimer_GetOverrun(TimerId));\r\n    LxtLogInfo(\"timer_getoverrun count = %d\", Overrun);\r\n    LxtCheckEqual(Overrun, SignalInfo.si_overrun, \"%d\");\r\n    LxtLogInfo(\"SignalInfo->si_timerid = %d\", SignalInfo._sifields._timer);\r\n    LxtCheckErrnoZeroSuccess(sigpending(&SigMask));\r\n\r\n    //\r\n    // Create a pthread and ensure that it can see the timer.\r\n    //\r\n\r\n    LxtCheckResultError(pthread_create(&Thread, NULL, TimerCreateSyscallThread, (void*)TimerId));\r\n\r\n    pthread_join(Thread, &ThreadReturn);\r\n    LxtCheckEqual((long)ThreadReturn, 0L, \"%ld\");\r\n\r\n    //\r\n    // Delete the timer, delete twice to verify the second deletion fails.\r\n    //\r\n\r\n    LxtCheckResult(LxtTimer_Delete(TimerId));\r\n    LxtCheckErrnoFailure(LxtTimer_Delete(TimerId), EINVAL);\r\n    TimerId = LXT_INVALID_TIMER_ID;\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (TimerId != LXT_INVALID_TIMER_ID)\r\n    {\r\n        LxtTimer_Delete(TimerId);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TimerCreateInvalidParam(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Index;\r\n    struct itimerspec OldTimerSpec;\r\n    struct rlimit ResourceLimit = {0};\r\n    int Result;\r\n    struct sigevent SigEvent;\r\n    sigset_t SigMask;\r\n    int Status;\r\n    timer_t TimerId;\r\n    timer_t* TimerIdArray;\r\n    struct itimerspec TimerSpec;\r\n\r\n    ChildPid = -1;\r\n    TimerId = LXT_INVALID_TIMER_ID;\r\n    TimerIdArray = NULL;\r\n\r\n    //\r\n    // timer_create invalid user buffers.\r\n    //\r\n\r\n    SigEvent.sigev_value.sival_ptr = &TimerId;\r\n    SigEvent.sigev_notify = SIGEV_SIGNAL;\r\n    SigEvent.sigev_signo = SIGRTMIN;\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, -1, &TimerId), EFAULT);\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, &SigEvent, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, &SigEvent, -1), EFAULT);\r\n\r\n    //\r\n    // timer_create invalid clock id's.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_MONOTONIC_RAW, &SigEvent, &TimerId), ENOTSUP);\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME_COARSE, &SigEvent, &TimerId), ENOTSUP);\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_MONOTONIC_COARSE, &SigEvent, &TimerId), ENOTSUP);\r\n    LxtCheckErrnoFailure(LxtTimer_Create(-1, &SigEvent, &TimerId), EINVAL);\r\n\r\n    //\r\n    // timer_create invalid sigevent structures.\r\n    //\r\n\r\n    //\r\n    // Invalid notify methods.\r\n    //\r\n\r\n    SigEvent.sigev_notify = 5;\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, &SigEvent, &TimerId), EINVAL);\r\n    SigEvent.sigev_notify = -1;\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, &SigEvent, &TimerId), EINVAL);\r\n\r\n    //\r\n    // Invalid signal numbers.\r\n    //\r\n\r\n    SigEvent.sigev_notify = SIGEV_SIGNAL;\r\n    SigEvent.sigev_signo = 0;\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, &SigEvent, &TimerId), EINVAL);\r\n    SigEvent.sigev_signo = 65;\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, &SigEvent, &TimerId), EINVAL);\r\n    SigEvent.sigev_signo = -1;\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, &SigEvent, &TimerId), EINVAL);\r\n\r\n    //\r\n    // N.B. timer_create with a NULL SigEvent argument succeeds.\r\n    //\r\n\r\n    TimerId = LXT_INVALID_TIMER_ID;\r\n    LxtCheckResult(LxtTimer_Create(CLOCK_REALTIME, NULL, &TimerId));\r\n    LxtLogInfo(\"create_timer TimerId = %d\", TimerId);\r\n\r\n    //\r\n    // timer_settime invalid user buffers.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtTimer_SetTime(TimerId, 0, NULL, &OldTimerSpec), EINVAL);\r\n    LxtCheckErrnoFailure(LxtTimer_SetTime(TimerId, 0, -1, &OldTimerSpec), EFAULT);\r\n\r\n    //\r\n    // N.B. timer_settime with NULL old value succeeds.\r\n    //\r\n\r\n    TimerSpec.it_value.tv_sec = 1;\r\n    TimerSpec.it_value.tv_nsec = 0;\r\n    TimerSpec.it_interval.tv_sec = 1;\r\n    TimerSpec.it_interval.tv_nsec = 1;\r\n    LxtCheckResult(LxtTimer_SetTime(TimerId, 0, &TimerSpec, NULL));\r\n\r\n    //\r\n    // N.B. Even though the timer_settime call fails with an invalid buffer for\r\n    //      old value, the timer is still modified.\r\n    //\r\n\r\n    TimerSpec.it_interval.tv_sec = 4;\r\n    TimerSpec.it_interval.tv_nsec = 4;\r\n    LxtCheckErrnoFailure(LxtTimer_SetTime(TimerId, 0, &TimerSpec, -1), EFAULT);\r\n    OldTimerSpec = TimerSpec;\r\n    TimerSpec.it_interval.tv_sec = 5;\r\n    TimerSpec.it_interval.tv_nsec = 5;\r\n    LxtCheckResult(LxtTimer_SetTime(TimerId, 0, &TimerSpec, &OldTimerSpec));\r\n    LxtCheckEqual(OldTimerSpec.it_interval.tv_sec, 4, \"%lx\");\r\n    LxtCheckEqual(OldTimerSpec.it_interval.tv_nsec, 4, \"%lx\");\r\n\r\n    //\r\n    // Invalid timerspec values.\r\n    //\r\n\r\n    memset(&TimerSpec, 0, sizeof(TimerSpec));\r\n    TimerSpec.it_value.tv_sec = -1;\r\n    LxtCheckErrnoFailure(LxtTimer_SetTime(TimerId, 0, &TimerSpec, NULL), EINVAL);\r\n    memset(&TimerSpec, 0, sizeof(TimerSpec));\r\n    TimerSpec.it_value.tv_nsec = 999999999 + 1;\r\n    LxtCheckErrnoFailure(LxtTimer_SetTime(TimerId, 0, &TimerSpec, NULL), EINVAL);\r\n    memset(&TimerSpec, 0, sizeof(TimerSpec));\r\n    TimerSpec.it_interval.tv_sec = -1;\r\n    LxtCheckErrnoFailure(LxtTimer_SetTime(TimerId, 0, &TimerSpec, NULL), EINVAL);\r\n    memset(&TimerSpec, 0, sizeof(TimerSpec));\r\n    TimerSpec.it_interval.tv_nsec = 999999999 + 1;\r\n    LxtCheckErrnoFailure(LxtTimer_SetTime(TimerId, 0, &TimerSpec, NULL), EINVAL);\r\n\r\n    //\r\n    // timer_getoverrun and timer_gettime invalid param.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtTimer_GetOverrun(-1), EINVAL);\r\n    LxtCheckErrnoFailure(LxtTimer_GetTime(-1, &TimerSpec), EINVAL);\r\n    LxtCheckErrnoFailure(LxtTimer_GetTime(TimerId, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtTimer_GetTime(TimerId, -1), EFAULT);\r\n    LxtCheckResult(LxtTimer_Delete(TimerId));\r\n    TimerId = LXT_INVALID_TIMER_ID;\r\n\r\n    //\r\n    // Query how many timers can be created, ensure that we are able to create\r\n    // exactly that many timers.\r\n    //\r\n\r\n    LxtCheckResult(getrlimit(RLIMIT_SIGPENDING, &ResourceLimit));\r\n    LxtLogInfo(\"getrlimit(RLIMIT_SIGPENDING) Current %lu, Max %lu\", ResourceLimit.rlim_cur, ResourceLimit.rlim_max);\r\n\r\n    TimerIdArray = malloc(ResourceLimit.rlim_cur * sizeof(timer_t));\r\n    if (TimerIdArray == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(TimerIdArray, 0xff, (ResourceLimit.rlim_cur * sizeof(timer_t)));\r\n    for (Index = 0; Index < ResourceLimit.rlim_cur; Index += 1)\r\n    {\r\n        LxtCheckResult(LxtTimer_Create(CLOCK_REALTIME, NULL, &TimerIdArray[Index]));\r\n    }\r\n\r\n    //\r\n    // Create one more timer, this should fail. Delete one and create another.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, NULL, &TimerId), EAGAIN);\r\n    LxtCheckResult(LxtTimer_Delete(TimerIdArray[0]));\r\n    TimerIdArray[0] = LXT_INVALID_TIMER_ID;\r\n    LxtCheckResult(LxtTimer_Create(CLOCK_REALTIME, NULL, &TimerId));\r\n    LxtCheckResult(LxtTimer_Delete(TimerId));\r\n    TimerId = LXT_INVALID_TIMER_ID;\r\n\r\n    //\r\n    // Set the timer limit to zero and attempt to create a new timer.\r\n    //\r\n\r\n    LXT_SYNCHRONIZATION_POINT_START();\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        ResourceLimit.rlim_cur = 0;\r\n        LxtCheckResult(setrlimit(RLIMIT_SIGPENDING, &ResourceLimit));\r\n        LxtCheckErrnoFailure(LxtTimer_Create(CLOCK_REALTIME, NULL, &TimerId), EAGAIN);\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT();\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (TimerId != LXT_INVALID_TIMER_ID)\r\n    {\r\n        LxtTimer_Delete(TimerId);\r\n    }\r\n\r\n    if (TimerIdArray != NULL)\r\n    {\r\n        for (Index = 0; Index < ResourceLimit.rlim_max; Index += 1)\r\n        {\r\n            if (TimerIdArray[Index] != LXT_INVALID_TIMER_ID)\r\n            {\r\n                LxtTimer_Delete(TimerIdArray[Index]);\r\n            }\r\n        }\r\n\r\n        free(TimerIdArray);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}\r\n\r\ntypedef struct _LXSS_BYTE_ALIGNED_TIMESPEC\r\n{\r\n    char Padding;\r\n    char Buffer[10];\r\n} LXSS_BYTE_ALIGNED_TIMESPEC;\r\n\r\nint ClockGetTimeAlignment(PLXT_ARGS Args)\r\n\r\n{\r\n    int Result;\r\n    LXSS_BYTE_ALIGNED_TIMESPEC Timespec;\r\n\r\n    LxtLogInfo(\"calling clock_gettime with user buffer %p\", &Timespec.Buffer);\r\n    LxtCheckErrnoZeroSuccess(clock_gettime(CLOCK_MONOTONIC, (struct timespec*)&Timespec.Buffer));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/timerfd.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    timerfd.c\r\n\r\nAbstract:\r\n\r\n    This file is a timerfd test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n// #include <sys/timerfd.h>\r\n#include <sys/epoll.h>\r\n#include <sys/time.h>\r\n#include <unistd.h>\r\n#include <sys/stat.h>\r\n#include <sys/types.h>\r\n#include <fcntl.h>\r\n\r\n#define LXT_NAME \"timerfd\"\r\n#define LXT_EVENT_ARRAY_SIZE (10)\r\n#define LXT_BASIC_TEST_LOOP_COUNT (3)\r\n\r\nint TimerFdBasic(PLXT_ARGS Args);\r\n\r\nint TimerFdEpoll(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {{\"Timerfd Basic\", TimerFdBasic}, {\"Timerfd Epoll\", TimerFdEpoll}};\r\n\r\n//\r\n// Global variables\r\n//\r\n\r\nstatic struct timespec g_SignalTime;\r\nstatic int g_SignalCount;\r\n\r\nint TimerFdTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint TimerFdBasic(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct epoll_event Event;\r\n    struct epoll_event Events[LXT_EVENT_ARRAY_SIZE];\r\n    int EpollFd;\r\n    struct stat FileStat;\r\n    int Flags;\r\n    int Index;\r\n    int LoopIndex;\r\n    struct itimerspec NewTimer;\r\n    struct itimerspec OldTimer;\r\n    int ReadyFdCount;\r\n    int Result;\r\n    struct timespec SleepDuration;\r\n    uint64_t TimerExpirationCount;\r\n    int TimerFd;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    EpollFd = -1;\r\n    TimerFd = -1;\r\n\r\n    //\r\n    // Create invalid timers.\r\n    //\r\n\r\n    TimerFd = timerfd_create(-1, O_NONBLOCK);\r\n    if (TimerFd >= 0)\r\n    {\r\n        LxtLogError(\"timerfd_create was supposed to fail because of invalid parameters but did not fail.\");\r\n        Result = TimerFd;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    TimerFd = timerfd_create(CLOCK_MONOTONIC, -1);\r\n    if (TimerFd >= 0)\r\n    {\r\n        LxtLogError(\"timerfd_create was supposed to fail because of invalid parameters but did not fail.\");\r\n        Result = TimerFd;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    TimerFd = timerfd_create(-1, -1);\r\n    if (TimerFd >= 0)\r\n    {\r\n        LxtLogError(\"timerfd_create was supposed to fail because of invalid parameters but did not fail.\");\r\n        Result = TimerFd;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Create a timer fd.\r\n    //\r\n\r\n    TimerFd = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK);\r\n    if (TimerFd < 0)\r\n    {\r\n        LxtLogError(\"timerfd_create failed\");\r\n        Result = TimerFd;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Create epoll fd to monitor the file for read events.\r\n    //\r\n\r\n    EpollFd = epoll_create(LXT_EVENT_ARRAY_SIZE);\r\n    if (EpollFd < 0)\r\n    {\r\n        LxtLogError(\"epoll create failed\");\r\n        Result = EpollFd;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Event.events = EPOLLIN;\r\n    Event.data.fd = TimerFd;\r\n    Result = epoll_ctl(EpollFd, EPOLL_CTL_ADD, TimerFd, &Event);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"epoll_ctl failed.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Set timer fd to expire at one second interval.\r\n    //\r\n\r\n    Flags = 0;\r\n    NewTimer.it_interval.tv_sec = 1;\r\n    NewTimer.it_interval.tv_nsec = 0;\r\n    NewTimer.it_value.tv_sec = 1;\r\n    NewTimer.it_value.tv_nsec = 0;\r\n\r\n    //\r\n    // Set timer with invalid parameters.\r\n    //\r\n\r\n    Result = timerfd_settime(-1, Flags, &NewTimer, &OldTimer);\r\n    if (Result >= 0)\r\n    {\r\n        LxtLogError(\"timerfd_settime was supposed to fail because of invalid parameters but did not fail.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Set timer with valid parameters.\r\n    //\r\n\r\n    Result = timerfd_settime(TimerFd, Flags, &NewTimer, &OldTimer);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"timerfd_settime failed\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Read with invalid buffer size.\r\n    //\r\n\r\n    Result = read(TimerFd, &TimerExpirationCount, sizeof(TimerExpirationCount) - 1);\r\n    if (Result >= 0)\r\n    {\r\n        LxtLogError(\"read was supposed to fail because of invalid parameters but did not fail.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Loop three times. In each iteration increase the wait time by one seconds.\r\n    // this should increase the expiration count by one in each iteration.\r\n    //\r\n\r\n    for (LoopIndex = 1; LoopIndex <= LXT_BASIC_TEST_LOOP_COUNT; LoopIndex += 1)\r\n    {\r\n\r\n        //\r\n        // Sleep for a factor of a second.\r\n        //\r\n\r\n        SleepDuration.tv_sec = LoopIndex;\r\n        SleepDuration.tv_nsec = 0;\r\n        nanosleep(&SleepDuration, NULL);\r\n        ReadyFdCount = epoll_wait(EpollFd, Events, LXT_EVENT_ARRAY_SIZE, 0);\r\n        if (ReadyFdCount < 0)\r\n        {\r\n            LxtLogError(\"epoll_wait failed.\");\r\n            Result = ReadyFdCount;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        for (Index = 0; Index < ReadyFdCount; Index += 1)\r\n        {\r\n\r\n            //\r\n            // Look out for TimerFd.\r\n            //\r\n\r\n            if (Events[Index].data.fd == TimerFd)\r\n            {\r\n                TimerExpirationCount = 0;\r\n                if (read(TimerFd, &TimerExpirationCount, sizeof(TimerExpirationCount)) != -1)\r\n                {\r\n                    LxtLogInfo(\"Number of times the timer expired in %d seconds is : %lld\", SleepDuration.tv_sec, (long long)TimerExpirationCount);\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    //\r\n    // Read even before the timer has expired. Set a 10 second expiration window\r\n    // and do a read after that. The read should fail with EAGAIN.\r\n    //\r\n\r\n    Flags = 0;\r\n    NewTimer.it_interval.tv_sec = 10;\r\n    NewTimer.it_interval.tv_nsec = 0;\r\n    Result = timerfd_settime(TimerFd, Flags, &NewTimer, &OldTimer);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"timerfd_settime failed\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = read(TimerFd, &TimerExpirationCount, sizeof(TimerExpirationCount));\r\n    if (Result != -1)\r\n    {\r\n        LxtLogError(\"Read was supposed to fail with eagain but it did not %d\", Result);\r\n    }\r\n\r\n    //\r\n    // Call get time with invalid parameters.\r\n    //\r\n\r\n    Result = timerfd_gettime(-1, &OldTimer);\r\n    if (Result >= 0)\r\n    {\r\n        LxtLogError(\"timerfd_gettime was supposed to fail because of invalid parameters but did not fail.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = timerfd_gettime(TimerFd, NULL);\r\n    if (Result >= 0)\r\n    {\r\n        LxtLogError(\"timerfd_gettime was supposed to fail because of invalid parameters but did not fail.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = timerfd_gettime(-1, NULL);\r\n    if (Result >= 0)\r\n    {\r\n        LxtLogError(\"timerfd_gettime was supposed to fail because of invalid parameters but did not fail.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    NewTimer.it_value.tv_sec = 60;\r\n    NewTimer.it_interval.tv_sec = 60;\r\n    Result = timerfd_settime(TimerFd, Flags, &NewTimer, &OldTimer);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"timerfd_settime failed\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = timerfd_gettime(TimerFd, &OldTimer);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"timerfd_gettime failed.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\r\n        \"Current timer settings: Interval (seconds: %d, nanoseconds: %lu),\"\r\n        \"time till next expiration (seconds %d, nanoseconds %ld, %d)\",\r\n        OldTimer.it_interval.tv_sec,\r\n        OldTimer.it_interval.tv_nsec,\r\n        OldTimer.it_value.tv_sec,\r\n        OldTimer.it_value.tv_nsec,\r\n        OldTimer.it_value.tv_nsec);\r\n\r\n    //\r\n    // Stat on the TimerFd.\r\n    //\r\n\r\n    Result = fstat(TimerFd, &FileStat);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"stat on timerfd failed.\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtLogInfo(\"File Size: %d bytes\", FileStat.st_size);\r\n    LxtLogInfo(\"Number of Links: %d\", FileStat.st_nlink);\r\n    LxtLogInfo(\"File inode: %d\", FileStat.st_ino);\r\n    LxtLogInfo(\"Symbolic link: %c \", (S_ISLNK(FileStat.st_mode)) ? 'Y' : 'N');\r\n    LxtLogInfo(\"Mode: %d\", FileStat.st_mode);\r\n    LxtLogInfo(\"Mode: %lu\", FileStat.st_mode);\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    NewTimer.it_value.tv_sec = -1;\r\n    LxtCheckErrnoFailure(timerfd_settime(TimerFd, 0, &NewTimer, NULL), EINVAL);\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    NewTimer.it_value.tv_nsec = 999999999 + 1;\r\n    LxtCheckErrnoFailure(timerfd_settime(TimerFd, 0, &NewTimer, NULL), EINVAL);\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    NewTimer.it_interval.tv_sec = -1;\r\n    LxtCheckErrnoFailure(timerfd_settime(TimerFd, 0, &NewTimer, NULL), EINVAL);\r\n    memset(&NewTimer, 0, sizeof(NewTimer));\r\n    NewTimer.it_interval.tv_nsec = 999999999 + 1;\r\n    LxtCheckErrnoFailure(timerfd_settime(TimerFd, 0, &NewTimer, NULL), EINVAL);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (EpollFd > 0)\r\n    {\r\n        LxtClose(EpollFd);\r\n    }\r\n\r\n    if (TimerFd > 0)\r\n    {\r\n        LxtClose(TimerFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TimerFdEpoll(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine validates the various epoll states of timer FD.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct epoll_event Event;\r\n    struct epoll_event Events[LXT_EVENT_ARRAY_SIZE];\r\n    int EpollFd;\r\n    struct itimerspec NewTimer;\r\n    struct itimerspec OldTimer;\r\n    int ReadyFdCount;\r\n    int Result;\r\n    struct timespec SleepDuration;\r\n    uint64_t TimerExpirationCount;\r\n    int TimerFd;\r\n\r\n    //\r\n    // Initialize locals.\r\n    //\r\n\r\n    EpollFd = -1;\r\n    TimerFd = -1;\r\n\r\n    //\r\n    // Create a timer fd.\r\n    //\r\n\r\n    LxtCheckErrno((TimerFd = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK)));\r\n\r\n    //\r\n    // Create epoll fd to monitor the file for read events.\r\n    //\r\n\r\n    LxtCheckErrno((EpollFd = epoll_create(LXT_EVENT_ARRAY_SIZE)));\r\n\r\n    //\r\n    // EPOLLIN is the only interesting event for timer FD.\r\n    //\r\n\r\n    Event.events = EPOLLIN;\r\n    Event.data.fd = TimerFd;\r\n    LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, TimerFd, &Event));\r\n\r\n    //\r\n    // Soon after creation, there shouldn't be any epoll set on the timer FD.\r\n    //\r\n\r\n    LxtCheckErrno((ReadyFdCount = epoll_wait(EpollFd, &Event, 1, 10)));\r\n    LxtCheckEqual(ReadyFdCount, 0, \"%d\");\r\n\r\n    //\r\n    // Set the timer to fire soon (1ms).\r\n    //\r\n\r\n    NewTimer.it_interval.tv_sec = 0;\r\n    NewTimer.it_interval.tv_nsec = 0;\r\n    NewTimer.it_value.tv_sec = 0;\r\n    NewTimer.it_value.tv_nsec = 1000;\r\n\r\n    //\r\n    // Set the timer.\r\n    //\r\n\r\n    LxtCheckErrno(timerfd_settime(TimerFd, 0, &NewTimer, &OldTimer));\r\n\r\n    //\r\n    // Wait for EPOLL for a timeout > timer. `epoll_wait` takes time in ms.\r\n    //\r\n\r\n    LxtCheckErrno((ReadyFdCount = epoll_wait(EpollFd, &Event, 1, 1000)));\r\n    LxtCheckEqual(ReadyFdCount, 1, \"%d\");\r\n\r\n    //\r\n    // Setting the timer to 0 should reset the timer and the epoll.\r\n    //\r\n\r\n    NewTimer.it_interval.tv_sec = 0;\r\n    NewTimer.it_interval.tv_nsec = 0;\r\n    NewTimer.it_value.tv_sec = 0;\r\n    NewTimer.it_value.tv_nsec = 0;\r\n    LxtCheckErrno(timerfd_settime(TimerFd, 0, &NewTimer, &OldTimer));\r\n    LxtCheckErrno((ReadyFdCount = epoll_wait(EpollFd, &Event, 1, 10)));\r\n    LxtCheckEqual(ReadyFdCount, 0, \"%d\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (EpollFd > 0)\r\n    {\r\n        close(EpollFd);\r\n    }\r\n\r\n    if (TimerFd > 0)\r\n    {\r\n        close(TimerFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/tty.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    tty.c\r\n\r\nAbstract:\r\n\r\n    This file contains unit tests for the /dev/tty0\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <unistd.h>\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <errno.h>\r\n#include <fcntl.h>\r\n#include <poll.h>\r\n#include <linux/kd.h>\r\n#include <linux/vt.h>\r\n#include <termios.h>\r\n#include <sys/ioctl.h>\r\n\r\n#define LXT_NAME \"tty\"\r\n\r\n#define LXT_MANUAL_OUTPUT \"ttyOutput.txt\"\r\n\r\n#define LXT_NON_DEFAULT_MODE (S_IFCHR | 0111)\r\n\r\n#define LXT_NON_DEFAULT_ID 255\r\n\r\nLXT_VARIATION_HANDLER TestDevTty0Ioctl;\r\n\r\nLXT_VARIATION_HANDLER TestDevTtyIoctl;\r\n\r\nLXT_VARIATION_HANDLER TestDevTtyStat;\r\n\r\nLXT_VARIATION_HANDLER TestDevTtyOpen;\r\n\r\nLXT_VARIATION_HANDLER TestDevTtySecurity;\r\n\r\n//\r\n// TODO: Enable TestDevTty0Ioctl\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    // {\"Test the implementation of the ioctl(/dev/tty0)\", TestDevTty0Ioctl},\r\n    {\"tty stat\", TestDevTtyStat},\r\n    {\"tty open\", TestDevTtyOpen},\r\n    {\"tty security\", TestDevTtySecurity},\r\n    {\"tty ioctl\", TestDevTtyIoctl}};\r\n\r\nint TtyTestEntry(int argc, char* argv[])\r\n{\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    //\r\n    // Started in a unit test mode.\r\n    //\r\n\r\n    LxtCheckResult(LxtInitialize(argc, argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint TestDevTty0Ioctl(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int fd;\r\n    int err;\r\n    char path[] = \"/dev/tty0\";\r\n    struct vt_stat vt_stat;\r\n    int vt_index;\r\n    struct termios termios;\r\n    int i;\r\n\r\n    err = -1;\r\n    fd = -1;\r\n\r\n    //\r\n    // Open the target.\r\n    //\r\n\r\n    fd = open(path, O_RDWR);\r\n    if (-1 == fd)\r\n    {\r\n        err = errno;\r\n        LxtLogError(\"open('%s') failed, %d\", path, err);\r\n        goto exit;\r\n    }\r\n\r\n    //\r\n    // Test the ioctl(KDSETMODE)\r\n    //\r\n\r\n    err = ioctl(fd, KDSETMODE, KD_TEXT);\r\n    if (-1 == err)\r\n    {\r\n        err = errno;\r\n        LxtLogError(\"ioctl('%s', KDSETMODE, KD_TEXT) failed, %d\", path, err);\r\n        goto exit;\r\n    }\r\n\r\n    LxtLogInfo(\"ioctl('%s', KDSETMODE) -> %d \", path, err);\r\n\r\n    //\r\n    // Test the ioctl(VT_GETSTATE)\r\n    //\r\n\r\n    err = ioctl(fd, VT_GETSTATE, &vt_stat);\r\n    if (-1 == err)\r\n    {\r\n        err = errno;\r\n        LxtLogError(\"ioctl('%s', VT_GETSTATE) failed, %d\", path, err);\r\n        goto exit;\r\n    }\r\n\r\n    LxtLogInfo(\"ioctl('%s', VT_GETSTATE) -> %d \", path, err);\r\n    LxtLogInfo(\"    vt_stat.v_active = %d\", vt_stat.v_active);\r\n    LxtLogInfo(\"    vt_stat.v_signal = %d\", vt_stat.v_signal);\r\n    LxtLogInfo(\"    vt_stat.v_state  = %d\", vt_stat.v_state);\r\n\r\n    //\r\n    // Test the activation of the VT#7\r\n    //\r\n\r\n    vt_index = 7;\r\n\r\n    err = ioctl(fd, VT_ACTIVATE, vt_index);\r\n    if (-1 == err)\r\n    {\r\n        err = errno;\r\n        LxtLogError(\"ioctl('%s', VT_ACTIVATE, %d) failed, %d\", path, vt_index, err);\r\n        goto exit;\r\n    }\r\n\r\n    LxtLogInfo(\"ioctl('%s', VT_ACTIVATE, %d) -> %d \", path, vt_index, err);\r\n\r\n    err = ioctl(fd, VT_WAITACTIVE, vt_index);\r\n    if (-1 == err)\r\n    {\r\n        err = errno;\r\n        LxtLogError(\"ioctl('%s', VT_WAITACTIVE, %d) failed, %d\", path, vt_index, err);\r\n        goto exit;\r\n    }\r\n\r\n    LxtLogInfo(\"ioctl('%s', VT_WAITACTIVE, %d) -> %d \", path, vt_index, err);\r\n\r\n    //\r\n    // Get/set port settings.\r\n    //\r\n\r\n#if !defined(__amd64__)\r\n\r\n    err = ioctl(fd, TCGETS, &termios);\r\n    if (-1 == err)\r\n    {\r\n        err = errno;\r\n        LxtLogError(\"ioctl('%s', TCGETS) failed, %d\", path, err);\r\n        goto exit;\r\n    }\r\n\r\n    LxtLogInfo(\"ioctl('%s', TCGETS) -> %d \", path, err);\r\n    LxtLogInfo(\"    termios.c_iflag  = %d\", termios.c_iflag);\r\n    LxtLogInfo(\"    termios.c_oflag  = %d\", termios.c_oflag);\r\n    LxtLogInfo(\"    termios.c_cflag  = %d\", termios.c_cflag);\r\n    LxtLogInfo(\"    termios.c_lflag  = %d\", termios.c_lflag);\r\n    LxtLogInfo(\"    termios.c_line  = %d\", termios.c_line);\r\n    for (i = 0; i < NCCS; i++)\r\n    {\r\n        LxtLogInfo(\"    termios.c_cc[%d] = %d\", i, termios.c_cc[i]);\r\n    }\r\n\r\n#endif\r\n\r\n    //\r\n    // Test the ioctl(VT_GETSTATE) after all preparation completed.\r\n    //\r\n\r\n    err = ioctl(fd, VT_GETSTATE, &vt_stat);\r\n    if (-1 == err)\r\n    {\r\n        err = errno;\r\n        LxtLogError(\"ioctl('%s', VT_GETSTATE) failed, %d\", path, err);\r\n        goto exit;\r\n    }\r\n\r\n    LxtLogInfo(\"ioctl('%s', VT_GETSTATE) -> %d \", path, err);\r\n    LxtLogInfo(\"    vt_stat.v_active = %d\", vt_stat.v_active);\r\n    LxtLogInfo(\"    vt_stat.v_signal = %d\", vt_stat.v_signal);\r\n    LxtLogInfo(\"    vt_stat.v_state  = %d\", vt_stat.v_state);\r\n\r\n    //\r\n    // Done.\r\n    // Close the test device handle.\r\n    //\r\n\r\n    close(fd);\r\n    fd = -1;\r\n\r\n    err = 0;\r\n\r\nexit:\r\n    if (-1 != fd)\r\n    {\r\n        close(fd);\r\n    }\r\n\r\n    return err;\r\n}\r\n\r\nint TestDevTtyIoctl(PLXT_ARGS Args)\r\n\r\n{\r\n    int Mode = 0;\r\n    int Result;\r\n\r\n    LxtCheckErrno(ioctl(0, KDGKBTYPE, &Mode));\r\n    LxtCheckEqual(Mode, KB_101, \"%d\");\r\n    LxtCheckErrno(ioctl(0, KDGKBMODE, &Mode));\r\n    LxtCheckEqual(Mode, K_UNICODE, \"%d\");\r\n    LxtCheckErrno(ioctl(0, KDSKBMODE, Mode));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TestDevTtyStat(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    int Index;\r\n    int Result;\r\n    struct stat StatFd;\r\n    struct stat StatFile;\r\n    char TtyName[32];\r\n\r\n    //\r\n    // stat the file through the fd and tty name result.\r\n    //\r\n\r\n    for (Index = 0; Index < 3; ++Index)\r\n    {\r\n        LxtCheckErrno(ttyname_r(Index, TtyName, sizeof(TtyName)));\r\n        LxtLogInfo(\"Name %d: %s\", Index, TtyName);\r\n        LxtCheckErrno(fstat(Index, &StatFd));\r\n        LxtCheckErrno(stat(TtyName, &StatFile));\r\n        LxtCheckEqual(StatFd.st_dev, StatFile.st_dev, \"%d\");\r\n        LxtCheckNotEqual(StatFd.st_dev, 0, \"%d\");\r\n        LxtCheckEqual(StatFd.st_ino, StatFile.st_ino, \"%d\");\r\n        LxtCheckNotEqual(StatFd.st_ino, 0, \"%d\");\r\n        LxtCheckEqual(StatFd.st_mode, StatFile.st_mode, \"%d\");\r\n        LxtCheckNotEqual(StatFd.st_mode, 0, \"%d\");\r\n        LxtCheckEqual(StatFd.st_nlink, StatFile.st_nlink, \"%d\");\r\n        LxtCheckEqual(StatFd.st_nlink, 1, \"%d\");\r\n        LxtCheckEqual(StatFd.st_uid, StatFile.st_uid, \"%d\");\r\n        LxtCheckEqual(StatFd.st_uid, 0, \"%d\");\r\n        LxtCheckEqual(StatFd.st_gid, StatFile.st_gid, \"%d\");\r\n        LxtCheckEqual(StatFd.st_gid, 5, \"%d\");\r\n        LxtCheckEqual(StatFd.st_rdev, StatFile.st_rdev, \"%d\");\r\n        LxtCheckNotEqual(StatFd.st_rdev, 0, \"%d\");\r\n        LxtCheckEqual(StatFd.st_size, StatFile.st_size, \"%d\");\r\n        LxtCheckEqual(StatFd.st_size, 0, \"%d\");\r\n        LxtCheckEqual(StatFd.st_blocks, StatFile.st_blocks, \"%d\");\r\n        LxtCheckEqual(StatFd.st_blocks, 0, \"%d\");\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TestDevTtyOpen(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    ssize_t BytesRead;\r\n    int Index;\r\n    char LinkName[32];\r\n    char LinkTarget[32];\r\n    int Result;\r\n    struct stat StatFd;\r\n    struct stat StatFile;\r\n    int TtyFd;\r\n    char TtyName[32];\r\n    char TtyNameFd[32];\r\n\r\n    TtyFd = -1;\r\n\r\n    //\r\n    // Check that the current tty can be opened by name and is linked\r\n    // appropriately in procfs.\r\n    //\r\n\r\n    for (Index = 0; Index < 3; ++Index)\r\n    {\r\n        LxtCheckErrno(ttyname_r(Index, TtyName, sizeof(TtyName)));\r\n        LxtCheckErrno(TtyFd = open(TtyName, O_RDWR));\r\n        LxtCheckErrno(ttyname_r(TtyFd, TtyNameFd, sizeof(TtyNameFd)));\r\n        LxtCheckStringEqual(TtyName, TtyNameFd);\r\n\r\n        snprintf(LinkName, sizeof(LinkName), \"/proc/self/fd/%d\", Index);\r\n        LxtCheckErrno(BytesRead = readlink(LinkName, LinkTarget, sizeof(LinkTarget) - 1));\r\n        LinkTarget[BytesRead] = 0;\r\n        LxtCheckStringEqual(TtyName, LinkTarget);\r\n\r\n        snprintf(LinkName, sizeof(LinkName), \"/proc/self/fd/%d\", TtyFd);\r\n        LxtCheckErrno(BytesRead = readlink(LinkName, LinkTarget, sizeof(LinkTarget) - 1));\r\n        LinkTarget[BytesRead] = 0;\r\n        LxtCheckStringEqual(TtyName, LinkTarget);\r\n\r\n        LxtCheckErrno(fstat(TtyFd, &StatFd));\r\n        LxtCheckErrno(fstat(Index, &StatFile));\r\n        LxtCheckEqual(StatFd.st_ino, StatFile.st_ino, \"%d\");\r\n        LxtCheckEqual(StatFd.st_rdev, StatFile.st_rdev, \"%d\");\r\n        LxtClose(TtyFd);\r\n        TtyFd = -1;\r\n    }\r\n\r\n    //\r\n    // Check that /dev/tty0 fails to open, this behavior differs from native\r\n    // Linux.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(TtyFd = open(\"/dev/tty0\", O_RDWR), EIO);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (TtyFd != -1)\r\n    {\r\n        LxtClose(TtyFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TestDevTtySecurity(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    bool ResetSecurity;\r\n    int Result;\r\n    struct stat Stat;\r\n    struct stat StatOriginal;\r\n    char TtyName[32];\r\n\r\n    ResetSecurity = false;\r\n    LxtCheckErrno(fstat(0, &StatOriginal));\r\n    ResetSecurity = true;\r\n    LxtCheckErrno(ttyname_r(0, TtyName, sizeof(TtyName)));\r\n\r\n    //\r\n    // Check that the uid, gid, and mode can be changed on the name or fd and\r\n    // are reflected into the stat on the fd and name\r\n    //\r\n\r\n    LxtCheckErrno(chmod(TtyName, LXT_NON_DEFAULT_MODE));\r\n    LxtCheckErrno(chown(TtyName, LXT_NON_DEFAULT_ID, LXT_NON_DEFAULT_ID));\r\n    LxtCheckErrno(fstat(0, &Stat));\r\n    LxtCheckEqual(Stat.st_mode, LXT_NON_DEFAULT_MODE, \"%d\");\r\n    LxtCheckEqual(Stat.st_uid, LXT_NON_DEFAULT_ID, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, LXT_NON_DEFAULT_ID, \"%d\");\r\n\r\n    LxtCheckErrno(fchmod(0, StatOriginal.st_mode));\r\n    LxtCheckErrno(fchown(0, StatOriginal.st_uid, StatOriginal.st_gid));\r\n    ResetSecurity = false;\r\n    LxtCheckErrno(stat(TtyName, &Stat));\r\n    LxtCheckEqual(Stat.st_mode, StatOriginal.st_mode, \"%d\");\r\n    LxtCheckEqual(Stat.st_uid, StatOriginal.st_uid, \"%d\");\r\n    LxtCheckEqual(Stat.st_gid, StatOriginal.st_gid, \"%d\");\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (ResetSecurity != false)\r\n    {\r\n        fchmod(0, StatOriginal.st_mode);\r\n        fchown(0, StatOriginal.st_uid, StatOriginal.st_gid);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/ttys.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Template.c\r\n\r\nAbstract:\r\n\r\n    This file is a ttys test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/ioctl.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/sysmacros.h>\r\n#include <fcntl.h>\r\n#include <termios.h>\r\n#include <stdarg.h>\r\n#include <stdio.h>\r\n#include <poll.h>\r\n\r\n#define LXT_NAME \"Ttys\"\r\n\r\n#define LXT_TTYS_LARGE_BUFFER_SIZE (1 * 1024)\r\n#define LXT_TTYS_DEFAULT \"/dev/ttyS1\"\r\n#define LXT_TTYS_DEFAULT_MINOR (LXT_TTYS_DEV_OFFSET + 1)\r\n#define LXT_TTYS_DEFAULT2 \"/dev/ttyS2\"\r\n#define LXT_TTYS_DEFAULT2_MINOR (LXT_TTYS_DEV_OFFSET + 2)\r\n#define LXT_TTYS_FORMAT \"/dev/ttyS%d\"\r\n#define LXT_TTYS_MAX 192\r\n#define LXT_TTYS_DEV_MODE (S_IFCHR | 0660)\r\n#define LXT_TTYS_DEV_MAJOR 4\r\n#define LXT_TTYS_DEV_OFFSET 64\r\n\r\nLXT_VARIATION_HANDLER TtysBasicOps;\r\nLXT_VARIATION_HANDLER TtysTermiosBaudParity;\r\nLXT_VARIATION_HANDLER TtysWrite;\r\nLXT_VARIATION_HANDLER TtysWindowSize;\r\nLXT_VARIATION_HANDLER TtysTermiosFlowControl;\r\nLXT_VARIATION_HANDLER TtysWriteRead;\r\nLXT_VARIATION_HANDLER TtysModemIoctls;\r\n\r\n//\r\n// Global constants\r\n//\r\n// TtysWriteRead requires two connected serial ports for testing.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Ttys basic operations\", TtysBasicOps},\r\n    {\"Ttys termios - baud rate and parity\", TtysTermiosBaudParity},\r\n    {\"Ttys write\", TtysWrite},\r\n    {\"Ttys window size\", TtysWindowSize},\r\n    /* {\"Ttys write read\", TtysWriteRead}, */\r\n    {\"Ttys termios - flow control\", TtysTermiosFlowControl},\r\n    {\"Ttys modem ioctls\", TtysModemIoctls}};\r\n\r\nint TtysTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint TtysBasicOps(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[64];\r\n    dev_t Device;\r\n    int Fd;\r\n    char Path[32];\r\n    int Index;\r\n    int Result;\r\n\r\n    for (Index = 0; Index < LXT_TTYS_MAX; ++Index)\r\n    {\r\n        snprintf(Path, sizeof(Path), LXT_TTYS_FORMAT, Index);\r\n        unlink(Path);\r\n        Device = makedev(LXT_TTYS_DEV_MAJOR, Index + LXT_TTYS_DEV_OFFSET);\r\n        LxtCheckErrno(mknod(Path, LXT_TTYS_DEV_MODE, Device));\r\n        Fd = open(Path, O_RDWR | O_NONBLOCK, 0);\r\n        if (Fd != -1)\r\n        {\r\n            read(Fd, Buffer, sizeof(Buffer));\r\n            write(Fd, Buffer, sizeof(Buffer));\r\n            fsync(Fd);\r\n            tcflush(Fd, TCIOFLUSH);\r\n            LxtClose(Fd);\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TtysTermiosBaudParity(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int BaudRate;\r\n    int BaudRates[] = {B1200, B9600, B9600};\r\n    int Cflag;\r\n    dev_t Device;\r\n    int Iflag;\r\n    int Index;\r\n    int Fd;\r\n    int Result;\r\n    struct termios Termios = {0};\r\n    struct termios TermiosNew = {0};\r\n\r\n    Fd = -1;\r\n    Result = LXT_RESULT_FAILURE;\r\n\r\n    //\r\n    // Check the default termios values.\r\n    //\r\n    // N.B. Ignore termios fields that may differ.\r\n    //\r\n\r\n    Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);\r\n    mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    if (Fd == -1)\r\n    {\r\n        if (errno == EIO)\r\n        {\r\n            LxtLogInfo(\"Skipping test %d\", errno);\r\n            Result = LXT_RESULT_SUCCESS;\r\n        }\r\n        else\r\n        {\r\n            LxtLogError(\"Unexpected error %d\", errno);\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(tcgetattr(Fd, &Termios));\r\n    LxtCheckEqual(Termios.c_oflag, 05, \"%d\");\r\n    LxtCheckEqual(Termios.c_lflag, 0105063, \"%d\");\r\n    LxtCheckEqual(Termios.c_line, 0, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[0], 3, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[1], 28, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[2], 127, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[3], 21, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[5], 0, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[6], 1, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[7], 0, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[10], 26, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[11], 0, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[12], 18, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[13], 15, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[14], 23, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[15], 22, \"%d\");\r\n    LxtCheckEqual(Termios.c_cc[16], 0, \"%d\");\r\n\r\n    //\r\n    // Set and check the baud rate.\r\n    //\r\n\r\n    BaudRate = cfgetispeed(&Termios);\r\n    for (Index = 0; Index < LXT_COUNT_OF(BaudRates); ++Index)\r\n    {\r\n        cfsetspeed(&Termios, BaudRates[Index]);\r\n        LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n        LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n        LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n        Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n        LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n        LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    }\r\n\r\n    //\r\n    // Reset to the original.\r\n    //\r\n\r\n    cfsetspeed(&Termios, BaudRate);\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Set and check the parity enable and type bits\r\n    //\r\n    // N.B. NT does not support setting input and output parity independently,\r\n    //      but Linux does.\r\n    //\r\n\r\n    Cflag = Termios.c_cflag;\r\n    Iflag = Termios.c_iflag;\r\n\r\n    //\r\n    // No parity, 8 bits\r\n    //\r\n\r\n    Termios.c_iflag &= ~INPCK;\r\n    Termios.c_cflag &= ~PARENB;\r\n    Termios.c_cflag &= ~CSTOPB;\r\n    Termios.c_cflag &= ~CMSPAR;\r\n    Termios.c_cflag &= ~CSIZE;\r\n    Termios.c_cflag |= CS8;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Even parity, 7 bits\r\n    //\r\n\r\n    Termios.c_iflag |= INPCK;\r\n    Termios.c_cflag |= PARENB;\r\n    Termios.c_cflag &= ~PARODD;\r\n    Termios.c_cflag &= ~CMSPAR;\r\n    Termios.c_cflag &= ~CSTOPB;\r\n    Termios.c_cflag &= ~CSIZE;\r\n    Termios.c_cflag |= CS7;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Odd parity, 7 bits\r\n    //\r\n\r\n    Termios.c_iflag |= INPCK;\r\n    Termios.c_cflag |= PARENB;\r\n    Termios.c_cflag |= PARODD;\r\n    Termios.c_cflag &= ~CMSPAR;\r\n    Termios.c_cflag &= ~CSTOPB;\r\n    Termios.c_cflag &= ~CSIZE;\r\n    Termios.c_cflag |= CS7;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Even parity, 5 bits\r\n    //\r\n\r\n    Termios.c_iflag |= INPCK;\r\n    Termios.c_cflag |= PARENB;\r\n    Termios.c_cflag &= ~PARODD;\r\n    Termios.c_cflag &= ~CMSPAR;\r\n    Termios.c_cflag &= ~CSTOPB;\r\n    Termios.c_cflag &= ~CSIZE;\r\n    Termios.c_cflag |= CS5;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Odd parity, 5 bits, with stop bit\r\n    //\r\n\r\n    Termios.c_iflag |= INPCK;\r\n    Termios.c_cflag |= PARENB;\r\n    Termios.c_cflag |= PARODD;\r\n    Termios.c_cflag &= ~CMSPAR;\r\n    Termios.c_cflag |= CSTOPB;\r\n    Termios.c_cflag &= ~CSIZE;\r\n    Termios.c_cflag |= CS5;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Space parity, 7 bits\r\n    //\r\n\r\n    Termios.c_iflag |= INPCK;\r\n    Termios.c_cflag |= PARENB;\r\n    Termios.c_cflag &= ~PARODD;\r\n    Termios.c_cflag |= CMSPAR;\r\n    Termios.c_cflag &= ~CSTOPB;\r\n    Termios.c_cflag &= ~CSIZE;\r\n    Termios.c_cflag |= CS7;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Mark parity, 7 bits\r\n    //\r\n\r\n    Termios.c_iflag |= INPCK;\r\n    Termios.c_cflag |= PARENB;\r\n    Termios.c_cflag |= PARODD;\r\n    Termios.c_cflag |= CMSPAR;\r\n    Termios.c_cflag &= ~CSTOPB;\r\n    Termios.c_cflag &= ~CSIZE;\r\n    Termios.c_cflag |= CS7;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Reset to the original\r\n    //\r\n\r\n    Termios.c_cflag = Cflag;\r\n    Termios.c_iflag = Iflag;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TtysWrite(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[64];\r\n    int BytesWritten;\r\n    dev_t Device;\r\n    int Fd;\r\n    char Path[32];\r\n    struct pollfd PollFd;\r\n    int Index;\r\n    int Result;\r\n\r\n    //\r\n    // Test non-blocking write paths.\r\n    //\r\n    // N.B. Blocking can hang if there is no reader.\r\n    //\r\n\r\n    Fd = -1;\r\n    Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);\r\n    mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    if (Fd == -1)\r\n    {\r\n        if (errno == EIO)\r\n        {\r\n            LxtLogInfo(\"Skipping test %d\", errno);\r\n            Result = LXT_RESULT_SUCCESS;\r\n        }\r\n        else\r\n        {\r\n            LxtLogError(\"Unexpected error %d\", errno);\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(&PollFd, 0, sizeof(PollFd));\r\n    PollFd.fd = Fd;\r\n    PollFd.events = POLLIN | POLLOUT | POLLHUP;\r\n    LxtCheckErrno(poll(&PollFd, 1, 0));\r\n    LxtCheckEqual(PollFd.revents & POLLOUT, POLLOUT, \"%d\");\r\n\r\n    errno = 0;\r\n    BytesWritten = write(Fd, Buffer, sizeof(Buffer));\r\n    if ((BytesWritten != sizeof(Buffer)) && (errno != EAGAIN))\r\n    {\r\n        LxtLogError(\"Unexpected BytesWritten %d, %d\", BytesWritten, errno);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TtysWindowSize(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    dev_t Device;\r\n    int Fd;\r\n    char Path[32];\r\n    int Result;\r\n    struct winsize WindowSize;\r\n    struct winsize WindowSizeNew;\r\n\r\n    //\r\n    // Test setting and getting the window size of a serial device.\r\n    //\r\n\r\n    Fd = -1;\r\n    Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);\r\n    mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);\r\n    if (Fd == -1)\r\n    {\r\n        if (errno == EIO)\r\n        {\r\n            LxtLogInfo(\"Skipping test %d\", errno);\r\n            Result = LXT_RESULT_SUCCESS;\r\n        }\r\n        else\r\n        {\r\n            LxtLogError(\"Unexpected error %d\", errno);\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(ioctl(Fd, TIOCGWINSZ, &WindowSize));\r\n    LxtLogInfo(\"%d, %d\", WindowSize.ws_row, WindowSize.ws_col);\r\n    WindowSizeNew = WindowSize;\r\n    WindowSizeNew.ws_row += 1;\r\n    WindowSizeNew.ws_col += 1;\r\n    LxtCheckErrno(ioctl(Fd, TIOCSWINSZ, &WindowSizeNew));\r\n    LxtCheckErrno(ioctl(Fd, TIOCGWINSZ, &WindowSize));\r\n    LxtCheckMemoryEqual(&WindowSize, &WindowSizeNew, sizeof(WindowSize));\r\n    memset(&WindowSizeNew, 0, sizeof(WindowSizeNew));\r\n    LxtCheckErrno(ioctl(Fd, TIOCSWINSZ, &WindowSizeNew));\r\n    LxtCheckErrno(ioctl(Fd, TIOCGWINSZ, &WindowSize));\r\n    LxtCheckMemoryEqual(&WindowSize, &WindowSizeNew, sizeof(WindowSize));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TtysTermiosFlowControl(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Cflag;\r\n    dev_t Device;\r\n    int Iflag;\r\n    int Index;\r\n    int Fd;\r\n    int Result;\r\n    struct termios Termios = {0};\r\n    struct termios TermiosNew = {0};\r\n    char VStart;\r\n    char VStop;\r\n\r\n    Fd = -1;\r\n    Result = LXT_RESULT_FAILURE;\r\n\r\n    //\r\n    // Check the default termios values.\r\n    //\r\n    // N.B. Ignore the termios settings that may differ..\r\n    //\r\n\r\n    Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);\r\n    mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    if (Fd == -1)\r\n    {\r\n        if (errno == EIO)\r\n        {\r\n            LxtLogInfo(\"Skipping test %d\", errno);\r\n            Result = LXT_RESULT_SUCCESS;\r\n        }\r\n        else\r\n        {\r\n            LxtLogError(\"Unexpected error %d\", errno);\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Set and check the parity enable and type bits\r\n    //\r\n    // N.B. NT does not support setting input and output parity independently,\r\n    //      but Linux does.\r\n    //\r\n\r\n    LxtCheckErrno(tcgetattr(Fd, &Termios));\r\n    Cflag = Termios.c_cflag;\r\n    Iflag = Termios.c_iflag;\r\n    VStart = Termios.c_cc[VSTART];\r\n    VStop = Termios.c_cc[VSTOP];\r\n\r\n    //\r\n    // ixon and ixoff set\r\n    //\r\n\r\n    Termios.c_iflag |= IXON;\r\n    Termios.c_iflag |= IXOFF;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // ixon and ixoff not set\r\n    //\r\n\r\n    Termios.c_iflag &= ~IXON;\r\n    Termios.c_iflag &= ~IXOFF;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // crtscts set\r\n    //\r\n\r\n    Termios.c_cflag |= CRTSCTS;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // crtscts not set\r\n    //\r\n\r\n    Termios.c_cflag &= ~CRTSCTS;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // clocal not set\r\n    //\r\n\r\n    LxtLogInfo(\"Clearing clocal\");\r\n    Termios.c_cflag &= ~CLOCAL;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // clocal set\r\n    //\r\n\r\n    Termios.c_cflag |= CLOCAL;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Flip vstart and vstop\r\n    //\r\n\r\n    LxtLogInfo(\"Updating vstart and vstop\");\r\n    Termios.c_cc[VSTART] = VStop;\r\n    Termios.c_cc[VSTOP] = VStart;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    //\r\n    // Reset to the original\r\n    //\r\n\r\n    LxtLogInfo(\"Resetting...\");\r\n    Termios.c_cflag = Cflag;\r\n    Termios.c_iflag = Iflag;\r\n    Termios.c_cc[VSTART] = VStart;\r\n    Termios.c_cc[VSTOP] = VStop;\r\n    LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosNew));\r\n    LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TtysWriteReadTransfer(PLXT_ARGS Args, int FdRead, int FdWrite)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int BytesRead;\r\n    int BytesWritten;\r\n    int BytesTotal;\r\n    pid_t ChildPid;\r\n    int Index;\r\n    char* RecvBuffer;\r\n    int Result;\r\n    char* SendBuffer;\r\n\r\n    ChildPid = -1;\r\n    RecvBuffer = NULL;\r\n    SendBuffer = NULL;\r\n\r\n    //\r\n    // Allocate a buffer with known data to transfer and a buffer to receive\r\n    // the data.\r\n    //\r\n\r\n    SendBuffer = LxtAlloc(LXT_TTYS_LARGE_BUFFER_SIZE);\r\n    if (SendBuffer == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (Index = 0; Index < LXT_TTYS_LARGE_BUFFER_SIZE; ++Index)\r\n    {\r\n        SendBuffer[Index] = (char)Index;\r\n    }\r\n\r\n    RecvBuffer = LxtAlloc(LXT_TTYS_LARGE_BUFFER_SIZE);\r\n    if (RecvBuffer == NULL)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(RecvBuffer, 0, LXT_TTYS_LARGE_BUFFER_SIZE);\r\n\r\n    //\r\n    // Send and recv the data.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        for (BytesTotal = 0; BytesTotal < LXT_TTYS_LARGE_BUFFER_SIZE; BytesTotal += BytesWritten)\r\n        {\r\n            BytesWritten = write(FdWrite, &SendBuffer[BytesTotal], LXT_TTYS_LARGE_BUFFER_SIZE - BytesTotal);\r\n\r\n            if (BytesWritten == -1)\r\n            {\r\n                if ((errno == EAGAIN) || (errno == EINTR))\r\n                {\r\n                    BytesWritten = 0;\r\n                    continue;\r\n                }\r\n\r\n                Result = LXT_RESULT_FAILURE;\r\n                LxtLogError(\"Write failed with %d\", errno);\r\n                goto ErrorExit;\r\n            }\r\n        }\r\n\r\n        _exit(0);\r\n    }\r\n\r\n    for (BytesTotal = 0; BytesTotal < LXT_TTYS_LARGE_BUFFER_SIZE; BytesTotal += BytesRead)\r\n    {\r\n        BytesRead = read(FdRead, &RecvBuffer[BytesTotal], LXT_TTYS_LARGE_BUFFER_SIZE - BytesTotal);\r\n\r\n        if (BytesRead == -1)\r\n        {\r\n            if ((errno == EAGAIN) || (errno == EINTR))\r\n            {\r\n                BytesRead = 0;\r\n                continue;\r\n            }\r\n\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Read failed with %d\", errno);\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n    for (Index = 0; Index < LXT_TTYS_LARGE_BUFFER_SIZE; ++Index)\r\n    {\r\n        if (RecvBuffer[Index] != (char)Index)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Mismatch at index %d: %d != %d\", Index, (int)(RecvBuffer[Index]), (int)((char)Index));\r\n\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    if (RecvBuffer != NULL)\r\n    {\r\n        LxtFree(RecvBuffer);\r\n    }\r\n\r\n    if (SendBuffer != NULL)\r\n    {\r\n        LxtFree(SendBuffer);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TtysWriteReadTermios(PLXT_ARGS Args, int FdRead, int FdWrite)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct termios FdReadTermios;\r\n    int Fds[2];\r\n    struct termios FdWriteTermios;\r\n    int Index;\r\n    struct termios Termios;\r\n    int Result;\r\n\r\n    //\r\n    // Set the termios structure for transferring raw data.\r\n    //\r\n\r\n    Fds[0] = FdRead;\r\n    Fds[1] = FdWrite;\r\n    for (Index = 0; Index < LXT_COUNT_OF(Fds); ++Index)\r\n    {\r\n        LxtCheckErrno(tcgetattr(Fds[Index], &Termios));\r\n        Termios.c_iflag = 0;\r\n        Termios.c_oflag = 0;\r\n        Termios.c_cflag = B115200 | CREAD | CS8;\r\n        Termios.c_lflag = 0;\r\n        LxtCheckErrno(tcsetattr(Fds[Index], TCSANOW, &Termios));\r\n    }\r\n\r\n    memset(&FdReadTermios, 0, sizeof(FdReadTermios));\r\n    LxtCheckErrno(tcgetattr(FdRead, &FdReadTermios));\r\n    memset(&FdWriteTermios, 0, sizeof(FdWriteTermios));\r\n    LxtCheckErrno(tcgetattr(FdWrite, &FdWriteTermios));\r\n    LxtCheckMemoryEqual(&FdReadTermios, &FdWriteTermios, sizeof(FdWriteTermios));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TtysWriteRead(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int BytesWritten;\r\n    dev_t Device;\r\n    int FdRead;\r\n    int FdWrite;\r\n    char Path[32];\r\n    int Result;\r\n\r\n    FdRead = -1;\r\n    FdWrite = -1;\r\n\r\n    Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);\r\n    mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);\r\n    LxtCheckErrno(FdRead = open(LXT_TTYS_DEFAULT, O_RDWR, 0));\r\n    Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT2_MINOR);\r\n    mknod(LXT_TTYS_DEFAULT2, LXT_TTYS_DEV_MODE, Device);\r\n    LxtCheckErrno(FdWrite = open(LXT_TTYS_DEFAULT2, O_RDWR, 0));\r\n    LxtCheckResult(TtysWriteReadTermios(Args, FdRead, FdWrite));\r\n\r\n    //\r\n    // Transfer data with blocking IO\r\n    //\r\n\r\n    LxtCheckResult(TtysWriteReadTransfer(Args, FdRead, FdWrite));\r\n\r\n    //\r\n    // Transfer again with non-blocking IO.\r\n    //\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (FdRead != -1)\r\n    {\r\n        LxtClose(FdRead);\r\n    }\r\n\r\n    if (FdWrite != -1)\r\n    {\r\n        LxtClose(FdWrite);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TtysModemIoctls(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    dev_t Device;\r\n    int Fd;\r\n    int ModemSettings;\r\n    int ModemSettingsOrig;\r\n    int Result;\r\n    struct termios Termios;\r\n    struct termios TermiosOrig;\r\n\r\n    Fd = -1;\r\n    ModemSettingsOrig = -1;\r\n    Result = LXT_RESULT_FAILURE;\r\n\r\n    //\r\n    // Check the default modem settings\r\n    //\r\n\r\n    Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);\r\n    mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);\r\n    Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);\r\n    if (Fd == -1)\r\n    {\r\n        if (errno == EIO)\r\n        {\r\n            LxtLogInfo(\"Skipping test %d\", errno);\r\n            Result = LXT_RESULT_SUCCESS;\r\n        }\r\n        else\r\n        {\r\n            LxtLogError(\"Unexpected error %d\", errno);\r\n        }\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettingsOrig));\r\n    LxtLogInfo(\"ModemSettingsOrig: %d\", ModemSettingsOrig);\r\n    ModemSettings = ModemSettingsOrig;\r\n    LxtCheckResult(ioctl(Fd, TIOCMSET, &ModemSettings));\r\n    LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));\r\n    LxtCheckEqual(ModemSettings, ModemSettingsOrig, \"%d\");\r\n\r\n    //\r\n    // Check that invalid settings are ignored\r\n    //\r\n\r\n    ModemSettings = -1;\r\n    LxtCheckResult(ioctl(Fd, TIOCMSET, &ModemSettings));\r\n    LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));\r\n    LxtCheckNotEqual(ModemSettings, ModemSettingsOrig, \"%d\");\r\n    ModemSettings = -1;\r\n    LxtCheckResult(ioctl(Fd, TIOCMBIC, &ModemSettings));\r\n    LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));\r\n    LxtCheckEqual(ModemSettings, 0, \"%d\");\r\n    ModemSettings = -1;\r\n    LxtCheckResult(ioctl(Fd, TIOCMBIS, &ModemSettings));\r\n    LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));\r\n    LxtCheckNotEqual(ModemSettings, ModemSettingsOrig, \"%d\");\r\n\r\n    //\r\n    // Recheck the settings after closing and reopening.\r\n    //\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0));\r\n    LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));\r\n    LxtCheckNotEqual(ModemSettings, ModemSettingsOrig, \"%d\");\r\n\r\n    //\r\n    // Check DTR\r\n    //\r\n    // N.B. Some serial drivers start up with DTR on native Linux.\r\n    //\r\n\r\n    ModemSettings = ModemSettingsOrig & ~TIOCM_DTR;\r\n    LxtCheckResult(ioctl(Fd, TIOCMSET, &ModemSettings));\r\n    LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));\r\n    LxtCheckEqual(ModemSettings, ModemSettingsOrig & ~TIOCM_DTR, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0));\r\n\r\n    ModemSettings = TIOCM_DTR;\r\n    LxtCheckResult(ioctl(Fd, TIOCMBIS, &ModemSettings));\r\n    LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));\r\n    LxtCheckEqual(ModemSettings, ModemSettingsOrig | TIOCM_DTR, \"%d\");\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0));\r\n    LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));\r\n    LxtCheckEqual(ModemSettings, ModemSettingsOrig | TIOCM_DTR, \"%d\");\r\n\r\n    //\r\n    // Check that changing RTS doesn't impact termios\r\n    //\r\n\r\n    LxtCheckErrno(tcgetattr(Fd, &TermiosOrig));\r\n    ModemSettings = TIOCM_RTS;\r\n    LxtCheckResult(ioctl(Fd, TIOCMBIS, &ModemSettings));\r\n    LxtCheckErrno(tcgetattr(Fd, &Termios));\r\n    LxtCheckMemoryEqual(&TermiosOrig, &Termios, sizeof(Termios));\r\n    ModemSettings = TIOCM_RTS;\r\n    LxtCheckResult(ioctl(Fd, TIOCMBIC, &ModemSettings));\r\n    LxtCheckErrno(tcgetattr(Fd, &Termios));\r\n    LxtCheckMemoryEqual(&TermiosOrig, &Termios, sizeof(Termios));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        if (ModemSettingsOrig != -1)\r\n        {\r\n            LxtCheckResult(ioctl(Fd, TIOCMSET, &ModemSettingsOrig));\r\n        }\r\n\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/unittests.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    unittests.c\r\n\r\nAbstract:\r\n\r\n    This file contains the entrypoint for the unittest executable.\r\n\r\n--*/\r\n\r\n#include \"unittests.h\"\r\n#include <stdio.h>\r\n#include <string.h>\r\n\r\nstatic const LXT_TEST LxtTests[] = {\r\n    {\"auxv\", false, AuxvTestEntry},\r\n    {\"binfmt\", false, BinFmtTestEntry},\r\n    {\"brk\", false, BrkTestEntry},\r\n    {\"cgroup\", false, CgroupTestEntry},\r\n    {\"dev_pt\", false, DevPtTestEntry},\r\n    {\"dev_pt_2\", false, DevPtTwoTestEntry},\r\n    {\"drvfs\", false, DrvfsTestEntry},\r\n    {\"dup\", false, DupTestEntry},\r\n    {\"epoll\", false, EpollTestEntry},\r\n    {\"eventfd\", false, EventfdTestEntry},\r\n    {\"execve\", true, .Handler = {.TestHandlerEnvp = ExecveTestEntry}},\r\n    {\"flock\", false, FlockTestEntry},\r\n    {\"fork\", false, ForkTestEntry},\r\n    {\"fscommon\", false, FsCommonTestEntry},\r\n    {\"fstab\", false, FstabTestEntry},\r\n    {\"get_set_id\", false, GetSetIdTestEntry},\r\n    {\"get_addr_info\", false, GetSetIdTestEntry},\r\n    {\"get_time\", false, GetTimeTestEntry},\r\n    {\"inotify\", false, InotifyTestEntry},\r\n    {\"interop\", false, InteropTestEntry},\r\n    {\"ioprio\", false, IoprioTestEntry},\r\n    {\"keymgmt\", false, KeymgmtTestEntry},\r\n    {\"madvise\", false, MadviseTestEntry},\r\n    {\"mprotect\", false, MprotectTestEntry},\r\n    {\"mremap\", false, MremapTestEntry},\r\n    {\"namespace\", false, NamespaceTestEntry},\r\n    {\"netlink\", false, NetlinkTestEntry},\r\n    {\"overlayfs\", false, OverlayFsTestEntry},\r\n    {\"pipe\", false, PipeTestEntry},\r\n    {\"poll\", false, PollTestEntry},\r\n    {\"random\", false, RandomTestEntry},\r\n    {\"resourcelimits\", false, ResourceLimitsTestEntry},\r\n    {\"sched\", false, SchedTestEntry},\r\n#if !defined(__aarch64__)\r\n    {\"select\", false, SelectTestEntry},\r\n#endif\r\n    {\"sem\", false, SemTestEntry},\r\n    {\"shm\", false, ShmTestEntry},\r\n    {\"socket_nonblock\", false, SocketNonblockTestEntry},\r\n    {\"splice\", false, SpliceTestEntry},\r\n    {\"sysfs\", false, SysfsTestEntry},\r\n    {\"sysinfo\", false, SysInfoTestEntry},\r\n    {\"timer\", false, TimerTestEntry},\r\n    {\"timerfd\", false, TimerFdTestEntry},\r\n    {\"tty\", false, TtyTestEntry},\r\n    {\"ttys\", false, TtysTestEntry},\r\n    {\"user\", false, UserTestEntry},\r\n    {\"utimensat\", false, UtimensatTestEntry},\r\n    {\"vfsaccess\", false, VfsAccessTestEntry},\r\n    {\"vnet\", false, VnetTestEntry},\r\n    {\"waitpid\", false, WaitPidTestEntry},\r\n    {\"wslpath\", false, WslPathTestEntry},\r\n    {\"xattr\", false, XattrTestEntry}};\r\n\r\nint main(int Argc, char* Argv[], char** Envp)\r\n{\r\n\r\n    unsigned int Itr = 0;\r\n    int Result = 1;\r\n\r\n    // parse arguments as:\r\n    // [0 - binary name] [1 - test name] [2.. - test name params]\r\n    if (Argc < 2)\r\n    {\r\n        // throw error\r\n        LxtLogError(\r\n            \"Error: too few arguments\\n \\\r\n            example usage: ./unittests [testname] [testparam1] [testparam..]\\n\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    // decrement Argc when passing it to the test\r\n    // tests view themselves as the binaries being called\r\n    // main(Argc = 3, Argv = [\"unittests\", false, \"exampletest\", false, \"param1\"]) -->\r\n    // ex. ExampleTestEntry(Argc = 2, Argv = [\"exampletest\", false, \"param1\"])\r\n    Argc--;\r\n\r\n    for (Itr = 0; Itr < LXT_COUNT_OF(LxtTests); Itr++)\r\n    {\r\n        if (strcmp(Argv[1], LxtTests[Itr].Name) == 0)\r\n        {\r\n            Result = (LxtTests[Itr].Envp) ? (LxtTests[Itr].Handler.TestHandlerEnvp(Argc, Argv + 1, Envp))\r\n                                          : (LxtTests[Itr].Handler.TestHandler(Argc, Argv + 1));\r\n\r\n            if (LXT_SUCCESS(Result))\r\n            {\r\n                LxtLogPassed(\"%s\", LxtTests[Itr].Name);\r\n            }\r\n            else\r\n            {\r\n                LxtLogError(\"%s\", LxtTests[Itr].Name);\r\n            }\r\n\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    LxtLogError(\"Test name [%s] could not be recognized\", Argv[1]);\r\n\r\nErrorExit:\r\n    return Result;\r\n}"
  },
  {
    "path": "test/linux/unit_tests/unittests.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    unittests.h\r\n\r\nAbstract:\r\n\r\n    This file contains definitions for the unittest executable.\r\n\r\n--*/\r\n\r\n#pragma once\r\n\r\n#include \"lxtutil.h\"\r\n\r\n// Define test names\r\n\r\n#define AUXV_TESTNAME \"auxv\"\r\n#define BINFMT_TESTNAME \"binfmt\"\r\n#define BRK_TESTNAME \"brk\"\r\n#define CGROUP_TESTNAME \"cgroup\"\r\n#define DEV_PT_TESTNAME \"dev_pt\"\r\n#define DEV_PT_TWO_TESTNAME \"dev_pt_2\"\r\n#define DRVFS_TESTNAME \"drvfs\"\r\n#define DUP_TESTNAME \"dup\"\r\n#define EPOLL_TESTNAME \"epoll\"\r\n#define EVENTFD_TESTNAME \"eventfd\"\r\n#define EXECVE_TESTNAME \"execve\"\r\n#define FLOCK_TESTNAME \"flock\"\r\n#define FORK_TESTNAME \"fork\"\r\n#define FSCOMMON_TESTNAME \"fscommon\"\r\n#define FSTAB_TESTNAME \"fstab\"\r\n#define GET_SET_ID_TESTNAME \"get_set_id\"\r\n#define GETADDRINFO_TESTNAME \"get_addr_info\"\r\n#define GETTIME_TESTNAME \"get_time\"\r\n#define INOTIFY_TESTNAME \"inotify\"\r\n#define INTEROP_TESTNAME \"interop\"\r\n#define IOPRIO_TESTNAME \"ioprio\"\r\n#define KEYMGMT_TESTNAME \"keymgmt\"\r\n#define MADVISE_TESTNAME \"madvise\"\r\n#define MOUNT_TESTNAME \"mount\"\r\n#define MPROTECT_TESTNAME \"mprotect\"\r\n#define MREMAP_TESTNAME \"mremap\"\r\n#define NAMESPACE_TESTNAME \"namespace\"\r\n#define NETLINK_TESTNAME \"netlink\"\r\n#define OVERLAYFS_TESTNAME \"overlayfs\"\r\n#define PIPE_TESTNAME \"pipe\"\r\n#define POLL_TESTNAME \"poll\"\r\n#define PTRACE_TESTNAME \"ptrace\"\r\n#define RANDOM_TESTNAME \"random\"\r\n#define RESOURCELIMITS_TESTNAME \"resource_limits\"\r\n#define SCHED_TESTNAME \"sched\"\r\n#define SELECT_TESTNAME \"select\"\r\n#define SEM_TESTNAME \"sem\"\r\n#define SHM_TESTNAME \"shm\"\r\n#define SOCKET_NONBLOCK_TESTNAME \"socket_nonblock\"\r\n#define SPLICE_TESTNAME \"splice\"\r\n#define SYSFS_TESTNAME \"sysfs\"\r\n#define SYS_INFO_TESTNAME \"sysinfo\"\r\n#define TIMER_TESTNAME \"timer\"\r\n#define TIMERFD_TESTNAME \"timerfd\"\r\n#define TTY_TESTNAME \"tty\"\r\n#define TTYS_TESTNAME \"ttys\"\r\n#define USER_TESTNAME \"user\"\r\n#define UTIMENSAT_TESTNAME \"utimensat\"\r\n#define VFSACCESS_TESTNAME \"vfsaccess\"\r\n#define VNET_TESTNAME \"vnet\"\r\n#define WAITPID_TESTNAME \"waitpid\"\r\n#define WSLPATH_TESTNAME \"wslpath\"\r\n#define XATTR_TESTNAME \"xattr\"\r\n\r\n// define path to unit test binary\r\n#define WSL_UNIT_TEST_BINARY \"/data/test/wsl_unit_tests\"\r\n\r\n// #define FUNCTIONS_H_INCLUDED\r\n// #ifndef FUNCTIONS_H_INCLUDED\r\n\r\n// Define each test's entry point\r\n\r\nint AuxvTestEntry(int Argc, char* Argv[]);\r\n\r\nint BinFmtTestEntry(int Argc, char* Argv[]);\r\n\r\nint BrkTestEntry(int Argc, char* Argv[]);\r\n\r\nint CgroupTestEntry(int Argc, char* Argv[]);\r\n\r\nint DevPtTestEntry(int Argc, char* Argv[]);\r\n\r\nint DevPtTwoTestEntry(int Argc, char* Argv[]);\r\n\r\nint DrvfsTestEntry(int Argc, char* Argv[]);\r\n\r\nint DupTestEntry(int Argc, char* Argv[]);\r\n\r\nint EpollTestEntry(int Argc, char* Argv[]);\r\n\r\nint EventfdTestEntry(int Argc, char* Argv[]);\r\n\r\nint ExecveTestEntry(int Argc, char* Argv[], char** Envp);\r\n\r\nint FlockTestEntry(int Argc, char* Argv[]);\r\n\r\nint ForkTestEntry(int Argc, char* Argv[]);\r\n\r\nint FsCommonTestEntry(int Argc, char* Argv[]);\r\n\r\nint FstabTestEntry(int Argc, char* Argv[]);\r\n\r\nint FutexTestEntry(int Argc, char* Argv[]);\r\n\r\nint GetSetIdTestEntry(int Argc, char* Argv[]);\r\n\r\nint GetAddrInfoTestEntry(int Argc, char* Argv[]);\r\n\r\nint GetTimeTestEntry(int Argc, char* Argv[]);\r\n\r\nint InotifyTestEntry(int Argc, char* Argv[]);\r\n\r\nint InteropTestEntry(int Argc, char* Argv[]);\r\n\r\nint IoprioTestEntry(int Argc, char* Argv[]);\r\n\r\nint KeymgmtTestEntry(int Argc, char* Argv[]);\r\n\r\nint MadviseTestEntry(int Argc, char* Argv[]);\r\n\r\nint MprotectTestEntry(int Argc, char* Argv[]);\r\n\r\nint MremapTestEntry(int Argc, char* Argv[]);\r\n\r\nint NamespaceTestEntry(int Argc, char* Argv[]);\r\n\r\nint NetlinkTestEntry(int Argc, char* Argv[]);\r\n\r\nint OverlayFsTestEntry(int Argc, char* Argv[]);\r\n\r\nint PipeTestEntry(int Argc, char* Argv[]);\r\n\r\nint PollTestEntry(int Argc, char* Argv[]);\r\n\r\nint RandomTestEntry(int Argc, char* Argv[]);\r\n\r\nint ResourceLimitsTestEntry(int Argc, char* Argv[]);\r\n\r\nint SchedTestEntry(int Argc, char* Argv[]);\r\n\r\nint SelectTestEntry(int Argc, char* Argv[]);\r\n\r\nint SemTestEntry(int Argc, char* Argv[]);\r\n\r\nint ShmTestEntry(int Argc, char* Argv[]);\r\n\r\nint SocketNonblockTestEntry(int Argc, char* Argv[]);\r\n\r\nint SpliceTestEntry(int Argc, char* Argv[]);\r\n\r\nint SysfsTestEntry(int Argc, char* Argv[]);\r\n\r\nint SysInfoTestEntry(int Argc, char* Argv[]);\r\n\r\nint TimerTestEntry(int Argc, char* Argv[]);\r\n\r\nint TimerFdTestEntry(int Argc, char* Argv[]);\r\n\r\nint TtyTestEntry(int Argc, char* Argv[]);\r\n\r\nint TtysTestEntry(int Argc, char* Argv[]);\r\n\r\nint UserTestEntry(int Argc, char* Argv[]);\r\n\r\nint UtimensatTestEntry(int Argc, char* Argv[]);\r\n\r\nint VfsAccessTestEntry(int Argc, char* Argv[]);\r\n\r\nint VnetTestEntry(int Argc, char* Argv[]);\r\n\r\nint WaitPidTestEntry(int Argc, char* Argv[]);\r\n\r\nint WslPathTestEntry(int Argc, char* Argv[]);\r\n\r\nint XattrTestEntry(int Argc, char* Argv[]);\r\n\r\n// #endif\r\n\r\n// for parsing which test to use\r\n\r\ntypedef int LXT_TEST_HANDLER(int Argc, char* Argv[]);\r\n\r\ntypedef int LXT_TEST_HANDLER_ENVP(int Argc, char* Argv[], char** Envp);\r\n\r\ntypedef union _TEST_HANDLER_UNION\r\n{\r\n    LXT_TEST_HANDLER* TestHandler;\r\n    LXT_TEST_HANDLER_ENVP* TestHandlerEnvp;\r\n} LXT_TEST_HANDLER_UNION;\r\n\r\ntypedef struct _LXT_TEST\r\n{\r\n    const char* Name;\r\n    const bool Envp;\r\n    LXT_TEST_HANDLER_UNION Handler;\r\n} LXT_TEST;\r\n"
  },
  {
    "path": "test/linux/unit_tests/user.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    user.c\r\n\r\nAbstract:\r\n\r\n    This file is the source for the user management.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdlib.h>\r\n#include <unistd.h>\r\n#include <stdio.h>\r\n#include <pwd.h>\r\n\r\n#define LXT_NAME \"user\"\r\n\r\nint ValidateUserTest(char* Username, uid_t Uid, uid_t Gid);\r\n\r\nint UserTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    uid_t Gid;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    uid_t Uid;\r\n    char* Username;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n\r\n    if (Argc < 4)\r\n    {\r\n        LxtLogError(\"User test requires three arguments: username, uid, gid\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Username = Argv[1];\r\n    Uid = atoi(Argv[2]);\r\n    Gid = atoi(Argv[3]);\r\n    LxtCheckResult(ValidateUserTest(Username, Uid, Gid));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint ValidateUserTest(char* Username, uid_t Uid, uid_t Gid)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct passwd* PasswordEntry;\r\n    uid_t RealGid;\r\n    uid_t RealUid;\r\n    int Result = LXT_RESULT_FAILURE;\r\n\r\n    RealUid = getuid();\r\n    if (Uid != RealUid)\r\n    {\r\n        LxtLogError(\"Uid %u does not match RealUid %u\", Uid, RealUid);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    RealGid = getgid();\r\n    if (Gid != RealGid)\r\n    {\r\n        LxtLogError(\"Gid %u does not match RealGid %u\", Gid, RealGid);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Compare passed-in values to the values stored in the password entry file.\r\n    //\r\n\r\n    PasswordEntry = getpwnam(Username);\r\n    if (PasswordEntry == NULL)\r\n    {\r\n        LxtLogError(\"getpwnam %s failed\", Username);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (Uid != PasswordEntry->pw_uid)\r\n    {\r\n        LxtLogError(\"Uid %u does not match PasswordEntry->pw_uid %u\", Uid, PasswordEntry->pw_uid);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (Gid != PasswordEntry->pw_gid)\r\n    {\r\n        LxtLogError(\"Gid %u does not match PasswordEntry->pw_gid %u\", Gid, PasswordEntry->pw_gid);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (strstr(PasswordEntry->pw_dir, Username) == NULL)\r\n    {\r\n        LxtLogError(\"Home path %s does not contain Username %s\", PasswordEntry->pw_dir, Username);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n    LxtLogPassed(\"Username %s, Uid %u, Gid %u successfully validated!\", Username, Uid, Gid);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/utimensat.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    utimensat.c\r\n\r\nAbstract:\r\n\r\n    This file contains test routines for the utimensat and utimes system\r\n    calls.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/stat.h>\r\n#include <fcntl.h>\r\n#include <sys/types.h>\r\n#include <sys/time.h>\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n#include <sys/capability.h>\r\n#endif\r\n\r\n#include <unistd.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <errno.h>\r\n#include <libmount/libmount.h>\r\n#include \"lxtfs.h\"\r\n#include \"lxtmount.h\"\r\n\r\n#define LXT_NAME \"Utimensat\"\r\n\r\nLXT_VARIATION_HANDLER TestBasicFunctions;\r\nLXT_VARIATION_HANDLER TestUtimes;\r\nLXT_VARIATION_HANDLER TestInvalid;\r\nLXT_VARIATION_HANDLER TestPermissions;\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nconst char ChildFileName[] = \"testfile\";\r\nconst char ChildFileFullPath[] = \"/data/test_utimensat/testfile\";\r\nconst char DirPath[] = \"/data/test_utimensat\";\r\nconst char LinkFileName[] = \"testlink\";\r\nconst char LinkFullPath[] = \"/data/test_utimensat/testlink\";\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Test basic functionality\", TestBasicFunctions},\r\n    {\"Test utimes\", TestUtimes},\r\n    {\"Test invalid parameters\", TestInvalid},\r\n    {\"Test permissions\", TestPermissions},\r\n};\r\n\r\ntypedef struct timespec TIMESPEC, *PTIMESPEC;\r\n\r\nenum\r\n{\r\n    PermissionsRoot,\r\n    PermissionsOmit,\r\n    PermissionsNow,\r\n    PermissionsSet,\r\n    PermissionsNowOmit,\r\n    PermissionsOmitNow,\r\n    PermissionsBeyondMax\r\n};\r\n\r\ntypedef struct _PERMISSIONS_TEST_CASE\r\n{\r\n    TIMESPEC SetTime[2];\r\n} PERMISSIONS_TEST_CASE, *PPERMISSIONS_TEST_CASE;\r\n\r\nPERMISSIONS_TEST_CASE PermissionsTest[] = {\r\n    {{{1234567, 98765432}, {4444444, 5555555}}},\r\n    {{{1234567, UTIME_OMIT}, {4444444, UTIME_OMIT}}},\r\n    {{{1234567, UTIME_NOW}, {4444444, UTIME_NOW}}},\r\n    {{{1234567, 11111111}, {4444444, 22222222}}},\r\n    {{{1234567, UTIME_NOW}, {4444444, UTIME_OMIT}}},\r\n    {{{1234567, UTIME_OMIT}, {4444444, UTIME_NOW}}}};\r\n\r\ntypedef struct _PERMISSIONS_FILE\r\n{\r\n    const char* Filename;\r\n    uid_t Owner;\r\n    mode_t Mode;\r\n    int Changeable[PermissionsBeyondMax];\r\n} PERMISSIONS_FILE, *PPERMISSIONS_FILE;\r\n\r\nenum\r\n{\r\n    OwnerMatchingThread = 1000,\r\n    OwnerNotMatchingThread\r\n};\r\n\r\nPERMISSIONS_FILE PermissionsFiles[] = {\r\n    {\"OwnedAndWritable\", OwnerMatchingThread, 0666, {0, 0, 0, 0, 0, 0}},\r\n    {\"OwnedReadonly\", OwnerMatchingThread, 0444, {0, 0, 0, 0, 0, 0}},\r\n    {\"OwnedWriteonly\", OwnerMatchingThread, 0222, {0, 0, 0, 0, 0, 0}},\r\n    {\"UnownedAndWritable\", OwnerNotMatchingThread, 0666, {0, 0, 0, EPERM, EPERM, EPERM}},\r\n    {\"UnownedAndReadonly\", OwnerNotMatchingThread, 0444, {0, 0, EACCES, EPERM, EPERM, EPERM}},\r\n    {\"UnownedAndWriteonly\", OwnerNotMatchingThread, 0222, {0, 0, 0, EPERM, EPERM, EPERM}}};\r\n\r\nint TestBasicFunctions(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine executes basic test functions, including setting timestamps\r\n    to a range of values including UTIME_NOW or UTIME_OMIT, on a range of\r\n    different ways to specify the target file, including relative, absolute,\r\n    descriptor, via symbolic link, on a symbolic link, and validating that\r\n    the expected outcome occurs.\r\n\r\nArguments:\r\n\r\n    Args - Supplies a pointer to test arguments.\r\n\r\nReturn Value:\r\n\r\n    0 if all variations complete successfully, -1 if they do not.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Flags;\r\n    int Result;\r\n\r\n    //\r\n    // If running on wslfs, timestamps use NT precision.\r\n    //\r\n\r\n    Flags = 0;\r\n    if (g_LxtFsInfo.Flags.DrvFsBehavior != 0)\r\n    {\r\n        LxtLogInfo(\"Using NT precision timestamps.\");\r\n        Flags = FS_UTIME_NT_PRECISION;\r\n    }\r\n\r\n    Result = LxtFsUtimeBasicCommon(DirPath, Flags);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint TestUtimes(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine executes test functions specific to the utimes syscall.\r\n\r\nArguments:\r\n\r\n    Args - Supplies a pointer to test arguments.\r\n\r\nReturn Value:\r\n\r\n    0 if all variations complete successfully, -1 if they do not.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    struct timeval SetTimeVal[2];\r\n\r\n    LxtCheckErrno(Fd = creat(ChildFileName, 0777));\r\n    memset(SetTimeVal, 0, sizeof(SetTimeVal));\r\n    LxtCheckResult(utimes(ChildFileName, SetTimeVal));\r\n    LxtCheckResult(utimes(ChildFileName, NULL));\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    memset(SetTimeVal, 0, sizeof(SetTimeVal));\r\n    SetTimeVal[1].tv_usec = 999999 + 1;\r\n    LxtCheckErrnoFailure(utimes(ChildFileName, SetTimeVal), EINVAL);\r\n    memset(SetTimeVal, 0, sizeof(SetTimeVal));\r\n    SetTimeVal[1].tv_usec = 999999 + 1;\r\n    LxtCheckErrnoFailure(utimes(ChildFileName, SetTimeVal), EINVAL);\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TestInvalid(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine executes test functions which are expected to fail, and\r\n    validates that they fail with the correct error.  This includes invalid\r\n    descriptors, flags, nonexistent files, or invalid timestamps.\r\n\r\nArguments:\r\n\r\n    Args - Supplies a pointer to test arguments.\r\n\r\nReturn Value:\r\n\r\n    0 if all variations complete successfully, -1 if they do not.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Result;\r\n    TIMESPEC SetTime[2];\r\n    struct timeval SetTimeVal[2];\r\n\r\n    Fd = -1;\r\n    memset(SetTime, 0, sizeof(SetTime));\r\n    memset(SetTimeVal, 0, sizeof(SetTimeVal));\r\n\r\n    LxtCheckErrnoFailure(utimensat(12345, ChildFileName, SetTime, 0), EBADF);\r\n\r\n    LxtCheckErrnoFailure(utimensat(0, ChildFileFullPath, SetTime, 0x70000000), EINVAL);\r\n\r\n    LxtCheckErrnoFailure(utimensat(AT_FDCWD, \"bogus\", SetTime, 0), ENOENT);\r\n\r\n    LxtCheckResult(Fd = open(ChildFileFullPath, O_RDWR));\r\n\r\n    LxtCheckErrnoFailure(utimensat(Fd, \"bogus\", SetTime, 0), ENOTDIR);\r\n#pragma GCC diagnostic push\r\n#pragma GCC diagnostic ignored \"-Wnonnull\"\r\n\r\n    LxtCheckErrnoFailure(utimensat(Fd, NULL, SetTime, AT_SYMLINK_NOFOLLOW), EINVAL);\r\n\r\n#pragma GCC diagnostic pop\r\n\r\n    LxtClose(Fd);\r\n    Fd = -1;\r\n\r\n    SetTime[0].tv_nsec = 1000000000;\r\n    LxtCheckErrnoFailure(utimensat(0, ChildFileFullPath, SetTime, 0), EINVAL);\r\n\r\n    SetTimeVal[0].tv_usec = 1000000;\r\n    LxtCheckErrnoFailure(utimes(ChildFileFullPath, SetTimeVal), EINVAL);\r\n\r\n    SetTimeVal[0].tv_usec = UTIME_NOW;\r\n    LxtCheckErrnoFailure(utimes(ChildFileFullPath, SetTimeVal), EINVAL);\r\n\r\n    SetTimeVal[0].tv_usec = UTIME_OMIT;\r\n    LxtCheckErrnoFailure(utimes(ChildFileFullPath, SetTimeVal), EINVAL);\r\n\r\n    SetTimeVal[0].tv_usec = 0;\r\n    LxtCheckErrnoFailure(utimes(\"bogus\", SetTimeVal), ENOENT);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint TestPermissions(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine executes test functions that should succeed or fail\r\n    depending on inputs and user states, and validates that they fail with\r\n    the expected code.  In particular, validates that callers without\r\n    privilege or file ownership cannot set file timestamps, and those\r\n    without privilege, file ownership or write access cannot set timestamps\r\n    to current.\r\n\r\nArguments:\r\n\r\n    Args - Supplies a pointer to test arguments.\r\n\r\nReturn Value:\r\n\r\n    0 if all variations complete successfully, -1 if they do not.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int Fd;\r\n    unsigned int FileNumber;\r\n    int Result;\r\n    unsigned int TestCase;\r\n\r\n    Result = chdir(DirPath);\r\n    if (Result < 0)\r\n    {\r\n        LxtLogError(\"Could not change directory: %d\", Result);\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)\r\n    {\r\n        Fd = open(PermissionsFiles[FileNumber].Filename, O_CREAT | O_RDWR, PermissionsFiles[FileNumber].Mode);\r\n        if ((Fd < 0) && (errno != EEXIST))\r\n        {\r\n            LxtLogError(\"Could not create file: %d\", Result);\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Result = fchown(Fd, PermissionsFiles[FileNumber].Owner, PermissionsFiles[FileNumber].Owner);\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"Could not change owner: %d\", Result);\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Result = fchmod(Fd, PermissionsFiles[FileNumber].Mode);\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"Could not change mode: %d\", Result);\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        close(Fd);\r\n        Fd = 0;\r\n    }\r\n\r\n    //\r\n    // While still root, check that we can perform all of the things it should\r\n    // be able to do.\r\n    //\r\n\r\n    TestCase = PermissionsRoot;\r\n\r\n    for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)\r\n    {\r\n        Result = utimensat(AT_FDCWD, PermissionsFiles[FileNumber].Filename, PermissionsTest[TestCase].SetTime, 0);\r\n        if (PermissionsFiles[FileNumber].Changeable[TestCase] == 0)\r\n        {\r\n            if (Result < 0)\r\n            {\r\n                LxtLogError(\"Could not change time as expected, file %s case %i, Result %d\", PermissionsFiles[FileNumber].Filename, TestCase, Result);\r\n                Result = -1;\r\n                goto ErrorExit;\r\n            }\r\n        }\r\n        else\r\n        {\r\n            if ((Result == 0) || (errno != PermissionsFiles[FileNumber].Changeable[TestCase]))\r\n            {\r\n\r\n                LxtLogError(\r\n                    \"Could change time, or failed with the wrong code, file %s case %i, Result %d, errno %i, expected %i\",\r\n                    PermissionsFiles[FileNumber].Filename,\r\n                    TestCase,\r\n                    Result,\r\n                    errno,\r\n                    PermissionsFiles[FileNumber].Changeable[TestCase]);\r\n                Result = -1;\r\n                goto ErrorExit;\r\n            }\r\n        }\r\n    }\r\n\r\n    //\r\n    // Set to user that should owns some of the files, and lacks privilege\r\n    // to access others\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Result = setuid(OwnerMatchingThread);\r\n        if (Result < 0)\r\n        {\r\n            LxtLogError(\"Could not setuid: %d\", Result);\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Advance to the next test (the first one was performed above), and continue\r\n        //\r\n\r\n        for (TestCase += 1; TestCase < PermissionsBeyondMax; TestCase += 1)\r\n        {\r\n            for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)\r\n            {\r\n                Result = utimensat(AT_FDCWD, PermissionsFiles[FileNumber].Filename, PermissionsTest[TestCase].SetTime, 0);\r\n                if (PermissionsFiles[FileNumber].Changeable[TestCase] == 0)\r\n                {\r\n                    if (Result < 0)\r\n                    {\r\n                        LxtLogError(\"Could not change time as expected, file %s case %i, Result %d\", PermissionsFiles[FileNumber].Filename, TestCase, Result);\r\n                        Result = -1;\r\n                        goto ErrorExit;\r\n                    }\r\n                }\r\n                else\r\n                {\r\n                    if ((Result == 0) || (errno != PermissionsFiles[FileNumber].Changeable[TestCase]))\r\n                    {\r\n\r\n                        LxtLogError(\r\n                            \"Could change time, or failed with the wrong code, file %s case %i, Result %d, errno %i, expected %i\",\r\n                            PermissionsFiles[FileNumber].Filename,\r\n                            TestCase,\r\n                            Result,\r\n                            errno,\r\n                            PermissionsFiles[FileNumber].Changeable[TestCase]);\r\n                        Result = -1;\r\n                        goto ErrorExit;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n\r\n        exit(0);\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    for (FileNumber = 0; FileNumber < LXT_COUNT_OF(PermissionsFiles); FileNumber += 1)\r\n    {\r\n        unlink(PermissionsFiles[FileNumber].Filename);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint UtimensatTestEntry(int Argc, char* Argv[])\r\n{\r\n    LXT_ARGS Args;\r\n    int Fd;\r\n    int Result;\r\n\r\n    Fd = -1;\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtFsUtimeCreateTestFiles(DirPath, 0));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    if (Fd >= 0)\r\n    {\r\n        close(Fd);\r\n    }\r\n\r\n    LxtFsUtimeCleanupTestFiles(DirPath);\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/vfsaccess.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    vfsaccess.c\r\n\r\nAbstract:\r\n\r\n    This file is a vfs access permissions test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdlib.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/xattr.h>\r\n#include <utime.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n#include <sys/syscall.h>\r\n#include <sys/mman.h>\r\n#include <sys/prctl.h>\r\n#include <sys/time.h>\r\n#include <sys/types.h>\r\n#include <unistd.h>\r\n#include <pwd.h>\r\n#include <sys/wait.h>\r\n#include <limits.h>\r\n#include \"lxtfs.h\"\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n\r\n#include <sys/capability.h>\r\n\r\n#else\r\n\r\n#include <sys/cdefs.h>\r\n#include <linux/capability.h>\r\n\r\n#define _LINUX_CAPABILITY_VERSION_3 0x20080522\r\n\r\n#ifndef O_PATH\r\n#define O_PATH 010000000\r\n#endif\r\n\r\n#endif\r\n\r\n#include <sys/vfs.h>\r\n#include <linux/prctl.h>\r\n#include <stdio.h>\r\n\r\n#define LXT_NAME \"vfsaccess\"\r\n#define LXT_NAME_DRVFS \"vfsaccess_drvfs\"\r\n\r\n#define VFS_FILE_OBJECT_COUNT LXT_COUNT_OF(g_VfsFileObjectFlags)\r\n\r\n#define VFS_FILE_CONTENTS \"vfsaccesstestfilecontents\"\r\n\r\n#define VFS_ACCESS_UID 1012\r\n\r\n#define VFS_ACCESS_PARENT_DIR \"/data/test/vfsaccesstest\"\r\n\r\n#define VFS_ACCESS_CHMOD_DIR VFS_ACCESS_PARENT_DIR \"/vfsaccessdir_chmod\"\r\n\r\n#define VFS_ACCESS_OPATH_DIR VFS_ACCESS_PARENT_DIR \"/vfsaccessopathdir\"\r\n\r\n#define VFS_ACCESS_OPATH_FILE VFS_ACCESS_PARENT_DIR \"/vfsaccessopath\"\r\n\r\n#define VFS_ACCESS_OPATH_FILE_LINK VFS_ACCESS_PARENT_DIR \"/vfsaccessopathlink\"\r\n\r\n#define VFS_ACCESS_STICKY_BIT_DIR VFS_ACCESS_PARENT_DIR \"/vfsaccessdir_stickybit\"\r\n\r\n#define VFS_ACCESS_GROUP_USER_ID_DIR VFS_ACCESS_PARENT_DIR \"/vfsaccessdir_groupuserid\"\r\n\r\n#define VFS_ACCESS_UTIME_FILE VFS_ACCESS_PARENT_DIR \"/vfsaccesutime\"\r\n\r\n#define VFS_ACCESS_FSUID_FILE VFS_ACCESS_PARENT_DIR \"/setfsuid_testfile\"\r\n\r\n#define VFS_ACCESS_FIFO VFS_ACCESS_PARENT_DIR \"/vfsaccess_fifo\"\r\n\r\n#define O_NOACCESS (O_WRONLY | O_RDWR)\r\n\r\n#define VFS_ACCESS_EXECVE_TEST_RESULT (123)\r\n\r\ntypedef struct _VFS_ACCESS_FILE\r\n{\r\n    char* Name;\r\n    mode_t Mode;\r\n} VFS_ACCESS_FILE, *PVFS_ACCESS_FILE;\r\n\r\ntypedef struct _VFS_ACCESS_FILE_OBJECT\r\n{\r\n    int Fd;\r\n    int Flags;\r\n} VFS_ACCESS_FILE_OBJECT, *PVFS_ACCESS_FILE_OBJECT;\r\n\r\nstruct reuid_t\r\n{\r\n    uid_t r;\r\n    uid_t e;\r\n    uid_t s;\r\n};\r\n\r\nint VfsAccessCheckResult(int ResultActual, int ResultExpected, int ErrnoActual, int ErrnoExpected, char* Message, int VariationIndex);\r\n\r\nvoid VfsAccessFileObjectCleanup(void);\r\n\r\nint VfsAccessFileObjectCreateFiles(void);\r\n\r\nint VfsAccessFileObjectCreateSymlinks(void);\r\n\r\nint VfsAccessFileObjectOpenFiles(VFS_ACCESS_FILE_OBJECT Files[]);\r\n\r\nint VfsAccessFileObjectOpenSymlinks(VFS_ACCESS_FILE_OBJECT Files[]);\r\n\r\nint VfsAccessFileObjectChecks(PLXT_ARGS Args);\r\n\r\nint VfsAccessFileObjectSymlinksChecks(PLXT_ARGS Args);\r\n\r\nint VfsAccessRemapReference(PLXT_ARGS Args);\r\n\r\nint VfsAccessChmod(PLXT_ARGS Args);\r\n\r\nint VfsAccessChmodCap(PLXT_ARGS Args);\r\n\r\nLXT_VARIATION_HANDLER VfsAccessFifo;\r\n\r\nint VfsAccessOPath(PLXT_ARGS Args);\r\n\r\nint VfsAccessStickyBit(PLXT_ARGS Args);\r\n\r\nint VfsAccessSetUserGroupId(PLXT_ARGS Args);\r\n\r\nint VfsAccessSetUserGroupIdExecveChild(void);\r\n\r\nint VfsAccessInodeChecks(PLXT_ARGS Args);\r\n\r\nint VfsAccessParseArgs(int Argc, char* Argv[], LXT_ARGS* Args);\r\n\r\nint VfsAccessUTimeCap(PLXT_ARGS Args);\r\n\r\nint VfsAccessSetFsUid(PLXT_ARGS Args);\r\n\r\nint VfsAccessSetUid(PLXT_ARGS Args);\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\n#define VFS_ACCESS_FILE_OBJECT_FILE 0\r\n#define VFS_ACCESS_REMAP_FILE 1\r\n\r\nstatic const VFS_ACCESS_FILE g_VfsFiles[] = {\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessfile\", S_IRWXU | S_IRWXG | S_IRWXO},\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessfile_remap\", S_IRWXU | S_IRWXG | S_IRWXO}};\r\n\r\nstatic const char* g_VfsSymlinks[] = {\r\n    VFS_ACCESS_PARENT_DIR \"/sym_vfsaccessfile\", VFS_ACCESS_PARENT_DIR \"/sym_vfsaccessfile_remap\"};\r\n\r\nstatic const int g_VfsFileObjectFlags[] = {O_RDONLY, O_WRONLY, O_RDWR, O_NOACCESS, O_RDONLY | O_PATH, O_RDWR | O_APPEND};\r\n\r\nstatic const VFS_ACCESS_FILE g_VfsInodeEntries[] = {\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessfile_r\", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH},\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessfile_w\", S_IFREG | S_IWUSR | S_IWGRP | S_IWOTH},\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessfile_x\", S_IFREG | S_IXUSR | S_IXGRP | S_IXOTH},\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessfile_rw\", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH},\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessdir_r\", S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH},\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessdir_w\", S_IFDIR | S_IWUSR | S_IWGRP | S_IWOTH},\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessdir_x\", S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH},\r\n    {VFS_ACCESS_PARENT_DIR \"/vfsaccessdir_wx\", S_IFDIR | S_IWUSR | S_IWGRP | S_IWOTH | S_IXUSR | S_IXGRP | S_IXOTH}};\r\n\r\n#define VFS_ACCESS_INODE_ENTRY_FILE \"vfsaccessfile\"\r\n\r\nint g_VfsSetFsUidCaps[] = {\r\n    CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID, CAP_LINUX_IMMUTABLE, CAP_MAC_OVERRIDE, CAP_MKNOD};\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"VfsAccess file object checks\", VfsAccessFileObjectChecks},\r\n    {\"VfsAccess symlinks checks\", VfsAccessFileObjectSymlinksChecks},\r\n    {\"VfsAccess remap reference\", VfsAccessRemapReference},\r\n    {\"VfsAccess chmod\", VfsAccessChmod},\r\n    {\"VfsAccess chmod cap\", VfsAccessChmodCap},\r\n    {\"VfsAccess O_PATH\", VfsAccessOPath},\r\n    {\"VfsAccess sticky bit\", VfsAccessStickyBit},\r\n    {\"VfsAccess set-user-ID set-group-ID\", VfsAccessSetUserGroupId},\r\n    {\"VfsAccess inode checks\", VfsAccessInodeChecks},\r\n    {\"VfsAccess utime cap\", VfsAccessUTimeCap},\r\n    {\"VfsAccess setfsuid\", VfsAccessSetFsUid},\r\n    //{\"VfsAccess Fifo\", VfsAccessFifo},\r\n    {\"VfsAccess set*uid\", VfsAccessSetUid}};\r\n\r\nbool g_UseDrvFs = false;\r\n\r\nint VfsAccessTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    if ((Argc == 2) && (strcmp(Argv[1], \"execvetest\") == 0))\r\n    {\r\n        return VFS_ACCESS_EXECVE_TEST_RESULT;\r\n    }\r\n\r\n    LxtCheckResult(VfsAccessParseArgs(Argc, Argv, &Args));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nvoid VfsAccessFileObjectCleanup(void)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine cleans up test files.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_VfsSymlinks); Index += 1)\r\n    {\r\n        unlink(g_VfsSymlinks[Index]);\r\n    }\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_VfsFiles); ++Index)\r\n    {\r\n        unlink(g_VfsFiles[Index].Name);\r\n    }\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_VfsInodeEntries); ++Index)\r\n    {\r\n        if (S_ISREG(g_VfsInodeEntries[Index].Mode))\r\n        {\r\n            unlink(g_VfsInodeEntries[Index].Name);\r\n        }\r\n        else\r\n        {\r\n            rmdir(g_VfsInodeEntries[Index].Name);\r\n        }\r\n    }\r\n\r\n    return;\r\n}\r\n\r\nint VfsAccessFileObjectCreateFiles(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    unsigned int BytesWritten;\r\n    unsigned int Index;\r\n    int Fd;\r\n    int Result;\r\n\r\n    Fd = -1;\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_VfsFiles); ++Index)\r\n    {\r\n        unlink(g_VfsFiles[Index].Name);\r\n        LxtCheckErrno(Fd = open(g_VfsFiles[Index].Name, O_RDWR | O_CREAT, g_VfsFiles[Index].Mode));\r\n        LxtCheckErrno(write(Fd, VFS_FILE_CONTENTS, sizeof(VFS_FILE_CONTENTS)));\r\n        if (Index == VFS_ACCESS_REMAP_FILE)\r\n        {\r\n            BytesWritten = sizeof(VFS_FILE_CONTENTS);\r\n            while (BytesWritten < 2 * PAGE_SIZE)\r\n            {\r\n                LxtCheckErrno(write(Fd, VFS_FILE_CONTENTS, sizeof(VFS_FILE_CONTENTS)));\r\n                BytesWritten += sizeof(VFS_FILE_CONTENTS);\r\n            }\r\n        }\r\n\r\n        LxtClose(Fd);\r\n        Fd = -1;\r\n    }\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_VfsInodeEntries); ++Index)\r\n    {\r\n        if (S_ISREG(g_VfsInodeEntries[Index].Mode))\r\n        {\r\n            unlink(g_VfsInodeEntries[Index].Name);\r\n            LxtCheckErrno(Fd = open(g_VfsInodeEntries[Index].Name, O_RDWR | O_CREAT, g_VfsInodeEntries[Index].Mode));\r\n\r\n            LxtCheckErrno(write(Fd, VFS_FILE_CONTENTS, sizeof(VFS_FILE_CONTENTS)));\r\n            LxtClose(Fd);\r\n            Fd = -1;\r\n        }\r\n        else\r\n        {\r\n            rmdir(g_VfsInodeEntries[Index].Name);\r\n            LxtCheckErrno(mkdir(g_VfsInodeEntries[Index].Name, g_VfsInodeEntries[Index].Mode));\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VfsAccessFileObjectCreateSymlinks(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    unsigned int Index;\r\n    int IntermediateResult;\r\n    int Result;\r\n\r\n    LxtCheckEqual(LXT_COUNT_OF(g_VfsFiles), LXT_COUNT_OF(g_VfsSymlinks), \"%d\");\r\n\r\n    //\r\n    // Create symlinks for the files.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_VfsFiles); Index += 1)\r\n    {\r\n        IntermediateResult = symlink(g_VfsFiles[Index].Name, g_VfsSymlinks[Index]);\r\n\r\n        //\r\n        // The symlink call may fail if the symlink already exists. This is ok\r\n        // in order to run the unit test on the same machine multiple times.\r\n        //\r\n\r\n        if (IntermediateResult < 0)\r\n        {\r\n            LxtCheckErrnoFailure(IntermediateResult, EEXIST);\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint VfsAccessCheckResult(int ResultActual, int ResultExpected, int ErrnoActual, int ErrnoExpected, char* Message, int VariationIndex)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    if (ResultActual != ResultExpected)\r\n    {\r\n        if (ResultActual >= 0)\r\n        {\r\n            ErrnoActual = 0;\r\n        }\r\n\r\n        LxtLogError(\"Unexpected %s (%d) result actual %d (%s) != expected %d\", Message, VariationIndex, ResultActual, strerror(ErrnoActual), ResultExpected);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if ((ResultActual == -1) && (ErrnoActual != ErrnoExpected))\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Unexpected %s (%d) errno actual %s != expected %s\", Message, VariationIndex, strerror(ErrnoActual), strerror(ErrnoExpected));\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint VfsAccessFifo(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests access permissions on fifos.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Result;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkfifo(VFS_ACCESS_FIFO, 0600));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        setgid(1000);\r\n        setuid(1000);\r\n        LxtCheckErrnoFailure(open(VFS_ACCESS_FIFO, O_RDONLY | O_NONBLOCK), EACCES);\r\n\r\n        LxtCheckErrnoFailure(open(VFS_ACCESS_FIFO, O_WRONLY | O_NONBLOCK), EACCES);\r\n\r\n        exit(0);\r\n    }\r\n\r\n    LxtWaitPidPoll(ChildPid, 0);\r\n\r\nErrorExit:\r\n    unlink(VFS_ACCESS_FIFO);\r\n    return Result;\r\n}\r\n\r\nint VfsAccessFileObjectChecks(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int AccessMode;\r\n    char Buffer;\r\n    int ErrnoExpected;\r\n    VFS_ACCESS_FILE_OBJECT Files[VFS_FILE_OBJECT_COUNT];\r\n    unsigned int Index;\r\n    void* Map;\r\n    int Result;\r\n    int ResultActual;\r\n    int ResultExpected;\r\n    bool VirtiofsNoDax;\r\n\r\n    LxtLogInfo(\"Fs type %d with dax = %d\\n\", g_LxtFsInfo.FsType, g_LxtFsInfo.Flags.Dax);\r\n    VirtiofsNoDax = g_LxtFsInfo.FsType == LxtFsTypeVirtioFs && g_LxtFsInfo.Flags.Dax == 0;\r\n    memset(Files, -1, sizeof(Files));\r\n    LxtCheckResult(VfsAccessFileObjectOpenFiles(Files));\r\n    for (Index = 0; Index < VFS_FILE_OBJECT_COUNT; ++Index)\r\n    {\r\n\r\n        //\r\n        // Validate read with a valid buffer.\r\n        //\r\n\r\n        ResultExpected = -1;\r\n        if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n        {\r\n\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        ResultActual = LxtRead(Files[Index].Fd, &Buffer, 1);\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, \"read\", Index));\r\n\r\n        //\r\n        // Validate read with a invalid buffer and a size of 0. The size of 0\r\n        // should cause the buffer to not be checked.\r\n        //\r\n\r\n        ResultExpected = -1;\r\n        if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n        {\r\n\r\n            ResultExpected = 0;\r\n        }\r\n\r\n        ResultActual = LxtRead(Files[Index].Fd, (void*)0x1, 0);\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, \"read\", Index));\r\n\r\n        //\r\n        // Validate write with a valid buffer.\r\n        //\r\n\r\n        ResultExpected = -1;\r\n        if ((Files[Index].Flags == O_WRONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n        {\r\n\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        ResultActual = LxtWrite(Files[Index].Fd, &Buffer, 1);\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, \"write\", Index));\r\n\r\n        //\r\n        // Validate write with a invalid buffer and a size of 0. The size of 0\r\n        // should cause the buffer to not be checked.\r\n        //\r\n\r\n        ResultExpected = -1;\r\n        if ((Files[Index].Flags == O_WRONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n        {\r\n\r\n            ResultExpected = 0;\r\n        }\r\n\r\n        ResultActual = LxtWrite(Files[Index].Fd, (void*)0x1, 0);\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, \"write\", Index));\r\n\r\n        //\r\n        // Validate map read shared and upgrading to write access.\r\n        //\r\n        // N.B. The Linux 9p client does not allow mapping shared if the file is\r\n        //      opened for write.\r\n        //\r\n        // N.B. The virtiofs device relies on fuse mapping which only supports\r\n        //      shared in the presence of DAX.\r\n        //\r\n\r\n        ErrnoExpected = EACCES;\r\n        if (Files[Index].Flags == O_PATH)\r\n        {\r\n            ErrnoExpected = EBADF;\r\n        }\r\n        else if (VirtiofsNoDax && (Files[Index].Flags == O_RDONLY || (Files[Index].Flags & O_ACCMODE) == O_RDWR))\r\n        {\r\n            ErrnoExpected = ENODEV;\r\n        }\r\n\r\n        ResultExpected = -1;\r\n        if (!VirtiofsNoDax && (((Files[Index].Flags & O_ACCMODE) == O_RDONLY) || ((Files[Index].Flags & O_ACCMODE) == O_RDWR)) &&\r\n            ((Files[Index].Flags & O_PATH) == 0))\r\n        {\r\n\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        Map = mmap(NULL, sizeof(Buffer), PROT_READ, MAP_SHARED, Files[Index].Fd, 0);\r\n        ResultActual = -1;\r\n        if (Map != MAP_FAILED)\r\n        {\r\n            ResultActual = 1;\r\n        }\r\n\r\n        LxtLogInfo(\"%d, %d, %d\", Index, Files[Index].Flags, ResultExpected);\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"mmap read shared\", Index));\r\n\r\n        if (Map != MAP_FAILED)\r\n        {\r\n            ResultExpected = -1;\r\n            if ((Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n            {\r\n\r\n                ResultExpected = 0;\r\n            }\r\n\r\n            ResultActual = mprotect(Map, sizeof(Buffer), PROT_WRITE);\r\n            LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"mmap read shared mprotect\", Index));\r\n\r\n            LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));\r\n        }\r\n\r\n        //\r\n        // Validate map read private and upgrading to write access.\r\n        //\r\n        // N.B. The Linux 9p client does not allow mapping shared if the file is\r\n        //      opened for write.\r\n        //\r\n\r\n        ResultExpected = -1;\r\n        if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n        {\r\n\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        Map = mmap(NULL, sizeof(Buffer), PROT_READ, MAP_PRIVATE, Files[Index].Fd, 0);\r\n        ResultActual = -1;\r\n        if (Map != MAP_FAILED)\r\n        {\r\n            ResultActual = 1;\r\n        }\r\n\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"mmap read private\", Index));\r\n\r\n        if (Map != MAP_FAILED)\r\n        {\r\n            ResultExpected = -1;\r\n            if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n            {\r\n\r\n                ResultExpected = 0;\r\n            }\r\n\r\n            ResultActual = mprotect(Map, sizeof(Buffer), PROT_WRITE);\r\n            VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"mmap read private mprotect\", Index);\r\n\r\n            LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));\r\n        }\r\n\r\n        //\r\n        // Validate map write shared and private\r\n        //\r\n        // N.B. The virtiofs device relies on fuse mapping which only supports\r\n        //      shared in the presence of DAX.\r\n        //\r\n\r\n        ErrnoExpected = EACCES;\r\n        if (Files[Index].Flags == O_PATH)\r\n        {\r\n            ErrnoExpected = EBADF;\r\n        }\r\n        else if (VirtiofsNoDax && (Files[Index].Flags & O_ACCMODE) == O_RDWR)\r\n        {\r\n            ErrnoExpected = ENODEV;\r\n        }\r\n\r\n        ResultExpected = -1;\r\n        if (!VirtiofsNoDax && (Files[Index].Flags & O_ACCMODE) == O_RDWR)\r\n        {\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        Map = mmap(NULL, sizeof(Buffer), PROT_WRITE, MAP_SHARED, Files[Index].Fd, 0);\r\n        ResultActual = -1;\r\n        if (Map != MAP_FAILED)\r\n        {\r\n            ResultActual = 1;\r\n            LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));\r\n        }\r\n\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"mmap write shared\", Index));\r\n\r\n        ResultExpected = -1;\r\n        if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n        {\r\n\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        Map = mmap(NULL, sizeof(Buffer), PROT_WRITE, MAP_PRIVATE, Files[Index].Fd, 0);\r\n        ResultActual = -1;\r\n        if (Map != MAP_FAILED)\r\n        {\r\n            ResultActual = 1;\r\n            LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));\r\n        }\r\n\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"mmap write private\", Index));\r\n\r\n        LxtClose(Files[Index].Fd);\r\n        Files[Index].Fd = -1;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint VfsAccessFileObjectSymlinksChecks(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer;\r\n    int ErrnoExpected;\r\n    VFS_ACCESS_FILE_OBJECT Files[VFS_FILE_OBJECT_COUNT];\r\n    unsigned int Index;\r\n    void* Map;\r\n    int Result;\r\n    int ResultActual;\r\n    int ResultExpected;\r\n    bool VirtiofsNoDax;\r\n\r\n    LxtLogInfo(\"Fs type %d with dax = %d\\n\", g_LxtFsInfo.FsType, g_LxtFsInfo.Flags.Dax);\r\n    VirtiofsNoDax = g_LxtFsInfo.FsType == LxtFsTypeVirtioFs && g_LxtFsInfo.Flags.Dax == 0;\r\n\r\n    memset(Files, -1, sizeof(Files));\r\n    LxtCheckResult(VfsAccessFileObjectOpenSymlinks(Files));\r\n    for (Index = 0; Index < VFS_FILE_OBJECT_COUNT; ++Index)\r\n    {\r\n\r\n        //\r\n        // Validate read\r\n        //\r\n\r\n        ResultExpected = -1;\r\n        if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n        {\r\n\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        ResultActual = LxtRead(Files[Index].Fd, &Buffer, 1);\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, \"read\", Index));\r\n\r\n        //\r\n        // Validate write\r\n        //\r\n\r\n        ResultExpected = -1;\r\n        if ((Files[Index].Flags == O_WRONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))\r\n        {\r\n\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        ResultActual = LxtWrite(Files[Index].Fd, &Buffer, 1);\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, \"write\", Index));\r\n\r\n        //\r\n        // Validate map read\r\n        //\r\n\r\n        ErrnoExpected = EACCES;\r\n        if (Files[Index].Flags == O_PATH)\r\n        {\r\n            ErrnoExpected = EBADF;\r\n        }\r\n        else if (VirtiofsNoDax && (Files[Index].Flags == O_RDONLY || (Files[Index].Flags & O_ACCMODE) == O_RDWR))\r\n        {\r\n            ErrnoExpected = ENODEV;\r\n        }\r\n\r\n        ResultExpected = -1;\r\n        if (!VirtiofsNoDax && (((Files[Index].Flags & O_ACCMODE) == O_RDONLY) || ((Files[Index].Flags & O_ACCMODE) == O_RDWR)) &&\r\n            ((Files[Index].Flags & O_PATH) == 0))\r\n        {\r\n\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        Map = mmap(NULL, sizeof(Buffer), PROT_READ, MAP_SHARED, Files[Index].Fd, 0);\r\n        ResultActual = -1;\r\n        if (Map != MAP_FAILED)\r\n        {\r\n            ResultActual = 1;\r\n            LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));\r\n        }\r\n\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"mmap read\", Index));\r\n\r\n        //\r\n        // Validate map write\r\n        //\r\n\r\n        ErrnoExpected = EACCES;\r\n        if (Files[Index].Flags == O_PATH)\r\n        {\r\n            ErrnoExpected = EBADF;\r\n        }\r\n        else if (VirtiofsNoDax && (Files[Index].Flags & O_ACCMODE) == O_RDWR)\r\n        {\r\n            ErrnoExpected = ENODEV;\r\n        }\r\n\r\n        ResultExpected = -1;\r\n        if (!VirtiofsNoDax && (Files[Index].Flags & O_ACCMODE) == O_RDWR)\r\n        {\r\n            ResultExpected = 1;\r\n        }\r\n\r\n        Map = mmap(NULL, sizeof(Buffer), PROT_WRITE, MAP_SHARED, Files[Index].Fd, 0);\r\n        ResultActual = -1;\r\n        if (Map != MAP_FAILED)\r\n        {\r\n            ResultActual = 1;\r\n            LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));\r\n        }\r\n\r\n        LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"mmap write\", Index));\r\n\r\n        LxtClose(Files[Index].Fd);\r\n        Files[Index].Fd = -1;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint VfsAccessFileObjectOpenFiles(VFS_ACCESS_FILE_OBJECT Files[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    unsigned int Index;\r\n    int Result;\r\n\r\n    for (Index = 0; Index < VFS_FILE_OBJECT_COUNT; ++Index)\r\n    {\r\n        Files[Index].Flags = g_VfsFileObjectFlags[Index];\r\n        LxtCheckErrno(Files[Index].Fd = open(g_VfsFiles[VFS_ACCESS_FILE_OBJECT_FILE].Name, Files[Index].Flags, 0));\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint VfsAccessFileObjectOpenSymlinks(VFS_ACCESS_FILE_OBJECT Files[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    unsigned int Index;\r\n    int Result;\r\n\r\n    for (Index = 0; Index < VFS_FILE_OBJECT_COUNT; Index += 1)\r\n    {\r\n        Files[Index].Flags = g_VfsFileObjectFlags[Index];\r\n        LxtCheckErrno(Files[Index].Fd = open(g_VfsSymlinks[VFS_ACCESS_FILE_OBJECT_FILE], Files[Index].Flags, 0));\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint VfsAccessRemapReference(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    char Buffer;\r\n    int FdReadOnly = -1;\r\n    int FdReadWrite = -1;\r\n    int MapFlags;\r\n    void* MapResult;\r\n    void* MapReadOnly = NULL;\r\n    void* MapReadWrite = NULL;\r\n    void* RemappedMemory = NULL;\r\n    int Result;\r\n    bool VirtiofsNoDax;\r\n\r\n    VirtiofsNoDax = g_LxtFsInfo.FsType == LxtFsTypeVirtioFs && g_LxtFsInfo.Flags.Dax == 0;\r\n    MapFlags = VirtiofsNoDax ? MAP_PRIVATE : MAP_SHARED;\r\n\r\n    //\r\n    // Open and map a file whose only reference is read only and open second\r\n    // file descriptor and mapping read write.\r\n    //\r\n\r\n    LxtCheckErrno(FdReadOnly = open(g_VfsFiles[VFS_ACCESS_REMAP_FILE].Name, O_RDONLY, 0));\r\n    LxtCheckMapErrno(MapReadOnly = mmap(NULL, sizeof(Buffer), PROT_READ, MapFlags, FdReadOnly, 0));\r\n    LxtCheckErrno(FdReadWrite = open(g_VfsFiles[VFS_ACCESS_REMAP_FILE].Name, O_RDWR, 0));\r\n    LxtCheckMapErrno(MapReadWrite = mmap(NULL, sizeof(Buffer), PROT_READ | PROT_WRITE, MapFlags, FdReadWrite, 0));\r\n    LxtCheckMapErrno(RemappedMemory = mremap(MapReadWrite, sizeof(Buffer), PAGE_SIZE * 2, MREMAP_MAYMOVE));\r\n\r\nErrorExit:\r\n    if (FdReadOnly != -1)\r\n    {\r\n        if (MapReadOnly != NULL)\r\n        {\r\n            LxtMunmap(MapReadOnly, sizeof(Buffer));\r\n        }\r\n\r\n        LxtClose(FdReadOnly);\r\n    }\r\n\r\n    if (FdReadWrite != -1)\r\n    {\r\n        if (RemappedMemory != NULL)\r\n        {\r\n            LxtMunmap(RemappedMemory, PAGE_SIZE * 2);\r\n        }\r\n        else if (MapReadWrite != NULL)\r\n        {\r\n            LxtMunmap(MapReadWrite, sizeof(Buffer));\r\n        }\r\n\r\n        LxtClose(FdReadWrite);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VfsAccessChmod(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int DirFd;\r\n    int Result;\r\n    struct stat StatBuf;\r\n\r\n    DirFd = -1;\r\n\r\n    //\r\n    // Set bits with chmod and then fchmod.\r\n    //\r\n\r\n    rmdir(VFS_ACCESS_CHMOD_DIR);\r\n    LxtCheckErrno(mkdir(VFS_ACCESS_CHMOD_DIR, S_IRWXU));\r\n    LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));\r\n    if (StatBuf.st_mode != (S_IRWXU | S_IFDIR))\r\n    {\r\n        LxtLogError(\"Unexpected mode %d != S_IRWXU | S_IFDIR\", StatBuf.st_mode);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXG));\r\n    LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));\r\n    if (StatBuf.st_mode != (S_IRWXG | S_IFDIR))\r\n    {\r\n        LxtLogError(\"Unexpected mode %d != S_IRWXG | S_IFDIR\", StatBuf.st_mode);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXO));\r\n    LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));\r\n    if (StatBuf.st_mode != (S_IRWXO | S_IFDIR))\r\n    {\r\n        LxtLogError(\"Unexpected mode %d != S_IRWXO | S_IFDIR\", StatBuf.st_mode);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXU));\r\n    LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));\r\n    if (StatBuf.st_mode != (S_IRWXU | S_IFDIR))\r\n    {\r\n        LxtLogError(\"Unexpected mode %d != S_IRWXU | S_IFDIR\", StatBuf.st_mode);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(chmod(VFS_ACCESS_CHMOD_DIR, 0xffff));\r\n    LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));\r\n    if (StatBuf.st_mode != (S_ISVTX | S_ISGID | S_ISUID | S_IRWXU | S_IRWXG | S_IRWXO | S_IFDIR))\r\n    {\r\n        LxtLogError(\"Unexpected mode %d != All bits\", StatBuf.st_mode);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(DirFd = open(VFS_ACCESS_CHMOD_DIR, O_DIRECTORY | O_RDONLY, 0));\r\n    LxtCheckErrno(fchmod(DirFd, S_IRWXG));\r\n    LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));\r\n    if (StatBuf.st_mode != (S_IRWXG | S_IFDIR))\r\n    {\r\n        LxtLogError(\"Unexpected mode %d != S_IRWXG | S_IFDIR\", StatBuf.st_mode);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(fchmod(DirFd, S_IRWXO));\r\n    LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));\r\n    if (StatBuf.st_mode != (S_IRWXO | S_IFDIR))\r\n    {\r\n        LxtLogError(\"Unexpected mode %d != S_IRWXO | S_IFDIR\", StatBuf.st_mode);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(fchmod(DirFd, S_IRWXU));\r\n    LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));\r\n    if (StatBuf.st_mode != (S_IRWXU | S_IFDIR))\r\n    {\r\n        LxtLogError(\"Unexpected mode %d != S_IRWXU | S_IFDIR\", StatBuf.st_mode);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(fchmod(DirFd, 0xffff));\r\n    LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));\r\n    if (StatBuf.st_mode != (S_ISVTX | S_ISGID | S_ISUID | S_IRWXU | S_IRWXG | S_IRWXO | S_IFDIR))\r\n    {\r\n        LxtLogError(\"Unexpected mode %d != All bits\", StatBuf.st_mode);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (DirFd != -1)\r\n    {\r\n        LxtClose(DirFd);\r\n    }\r\n\r\n    rmdir(VFS_ACCESS_CHMOD_DIR);\r\n    return Result;\r\n}\r\n\r\nvoid VfsAccessChmodCapChild(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int Result;\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n    CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);\r\n    CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);\r\n    CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n    CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n    CapData[0].effective = CapData[0].permitted;\r\n    CapData[1].effective = CapData[1].permitted;\r\n\r\n    //\r\n    // Drop privileges so the current process does not have CAP_FOWNER.\r\n    //\r\n\r\n    LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n    LxtCheckErrno(setgid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(setuid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n    //\r\n    // TODO: Update the below when checks are enforced\r\n    //\r\n\r\n    //\r\n    // Try to chmod the directory to the current value.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXU), EPERM);\r\n\r\n    //\r\n    // Try to chmod on the directory without CAP_FOWNER to 0751.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IXOTH), EPERM);\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    _exit(Result);\r\n}\r\n\r\nint VfsAccessChmodCap(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Result;\r\n\r\n    rmdir(VFS_ACCESS_CHMOD_DIR);\r\n    LxtCheckErrno(mkdir(VFS_ACCESS_CHMOD_DIR, S_IRWXU));\r\n    ChildPid = fork();\r\n    if (ChildPid == 0)\r\n    {\r\n        VfsAccessChmodCapChild();\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    rmdir(VFS_ACCESS_CHMOD_DIR);\r\n    return Result;\r\n}\r\n\r\nvoid VfsAccessOPathChild(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[100];\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int Fd;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    struct statfs StatFsBuffer;\r\n    struct timespec Times[2] = {{0, UTIME_NOW}, {0, UTIME_NOW}};\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n    CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n    CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n    CapData[0].effective = CapData[0].permitted;\r\n    CapData[1].effective = CapData[1].permitted;\r\n    Fd = -1;\r\n\r\n    //\r\n    // Drop privileges so the current process does not have VFS related\r\n    // capabilities.\r\n    //\r\n\r\n    LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n    LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n    //\r\n    // Open the file with O_PATH and check the behavior for the syscalls of\r\n    // interest.\r\n    //\r\n\r\n    LxtCheckErrno(Fd = open(VFS_ACCESS_OPATH_FILE, O_PATH, 0));\r\n\r\n    //\r\n    // Check syscalls that take a file descriptor should fail because O_PATH was\r\n    // specified.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(fchmod(Fd, 0), EBADF);\r\n    LxtCheckErrnoFailure(fchown(Fd, 0, 0), EBADF);\r\n    LxtCheckErrnoFailure(fsync(Fd), EBADF);\r\n    LxtCheckErrnoFailure(LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)), EBADF);\r\n    LxtCheckErrnoFailure(futimens(Fd, Times), EBADF);\r\n    LxtCheckErrnoFailure(flistxattr(Fd, Buffer, sizeof(Buffer)), EBADF);\r\n\r\n    //\r\n    // Check syscalls that take a file descriptor should succeed even through\r\n    // O_PATH was specified.\r\n    //\r\n\r\n    LxtCheckErrno(fstat(Fd, &StatBuffer));\r\n    LxtCheckErrno(fstatfs(Fd, &StatFsBuffer));\r\n\r\n    //\r\n    // Check syscalls that should succeed on a directory with O_PATH specified.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(close(Fd));\r\n    LxtCheckErrno(Fd = open(VFS_ACCESS_OPATH_DIR, O_PATH | O_DIRECTORY));\r\n    LxtCheckErrnoZeroSuccess(fchdir(Fd));\r\n\r\n    //\r\n    // Chdir should still fail if execute permissions are removed.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(chmod(VFS_ACCESS_OPATH_DIR, 0));\r\n    LxtCheckErrnoFailure(fchdir(Fd), EACCES);\r\n\r\n    //\r\n    // Check syscalls that take a path should succeed because they do not\r\n    // require access to the file, but instead just the path.\r\n    //\r\n\r\n    LxtCheckErrno(chmod(VFS_ACCESS_OPATH_FILE, 0));\r\n    LxtCheckErrno(chown(VFS_ACCESS_OPATH_FILE, 0, 0));\r\n    LxtCheckErrno(stat(VFS_ACCESS_OPATH_FILE, &StatBuffer));\r\n    LxtCheckErrno(statfs(VFS_ACCESS_OPATH_FILE, &StatFsBuffer));\r\n    LxtCheckErrno(readlink(VFS_ACCESS_OPATH_FILE_LINK, Buffer, sizeof(Buffer)));\r\n\r\n    //\r\n    // Xattr is not supported on drvfs currently.\r\n    //\r\n\r\n    if (g_UseDrvFs == false)\r\n    {\r\n        LxtCheckErrno(listxattr(VFS_ACCESS_OPATH_FILE_LINK, Buffer, sizeof(Buffer)));\r\n        LxtCheckErrno(llistxattr(VFS_ACCESS_OPATH_FILE_LINK, Buffer, sizeof(Buffer)));\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    _exit(Result);\r\n}\r\n\r\nint VfsAccessOPath(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Fd;\r\n    int Result;\r\n\r\n    unlink(VFS_ACCESS_OPATH_FILE);\r\n    unlink(VFS_ACCESS_OPATH_FILE_LINK);\r\n    LxtCheckErrno(mkdir(VFS_ACCESS_OPATH_DIR, 0111));\r\n    LxtCheckErrno(Fd = open(VFS_ACCESS_OPATH_FILE, O_CREAT, 0));\r\n    LxtCheckErrno(symlink(VFS_ACCESS_OPATH_FILE, VFS_ACCESS_OPATH_FILE_LINK));\r\n    ChildPid = fork();\r\n    if (ChildPid == 0)\r\n    {\r\n        VfsAccessOPathChild();\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    unlink(VFS_ACCESS_OPATH_FILE);\r\n    unlink(VFS_ACCESS_OPATH_FILE_LINK);\r\n    rmdir(VFS_ACCESS_OPATH_DIR);\r\n    return Result;\r\n}\r\n\r\nvoid VfsAccessRenameCapChild(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int File;\r\n    int Result;\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n    CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);\r\n    CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);\r\n    CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n    CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n    CapData[0].effective = CapData[0].permitted;\r\n    CapData[1].effective = CapData[1].permitted;\r\n\r\n    //\r\n    // Drop privileges so the current process does not have CAP_FOWNER.\r\n    //\r\n\r\n    LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n    LxtCheckErrno(setgid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(setuid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n    //\r\n    // Create a file and directory for the current user.\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(VFS_ACCESS_STICKY_BIT_DIR \"/userdir1\", S_IRWXU));\r\n    LxtCheckErrno(File = creat(VFS_ACCESS_STICKY_BIT_DIR \"/userfile1\", S_IRWXU));\r\n    close(File);\r\n\r\n    //\r\n    // Try to rename the file and directory to an existing entry without\r\n    // CAP_FOWNER.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(rename(VFS_ACCESS_STICKY_BIT_DIR \"/userfile1\", VFS_ACCESS_STICKY_BIT_DIR \"/file1\"), EPERM);\r\n\r\n    LxtCheckErrnoFailure(rename(VFS_ACCESS_STICKY_BIT_DIR \"/userdir1\", VFS_ACCESS_STICKY_BIT_DIR \"/dir1\"), EPERM);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n\r\n    rmdir(VFS_ACCESS_STICKY_BIT_DIR \"/userdir1\");\r\n    remove(VFS_ACCESS_STICKY_BIT_DIR \"/userfile1\");\r\n    _exit(Result);\r\n}\r\n\r\nvoid VfsAccessRmdirCapChild(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int Result;\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n    CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);\r\n    CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);\r\n    CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);\r\n    CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);\r\n    CapData[0].effective = CapData[0].permitted;\r\n    CapData[1].effective = CapData[1].permitted;\r\n\r\n    //\r\n    // Drop privileges so the current process does not have CAP_FOWNER.\r\n    //\r\n\r\n    LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n    LxtCheckErrno(setgid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(setuid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n    //\r\n    // Try to remove the file and directory without CAP_FOWNER.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(remove(VFS_ACCESS_STICKY_BIT_DIR \"/file1\"), EPERM);\r\n    LxtCheckErrnoFailure(remove(VFS_ACCESS_STICKY_BIT_DIR \"/dir1\"), EPERM);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    _exit(Result);\r\n}\r\n\r\nint VfsAccessStickyBit(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int ChildStatus;\r\n    int File = 0;\r\n    int Result;\r\n\r\n    //\r\n    // Create a directory with the sticky bit set and a file inside.\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(VFS_ACCESS_STICKY_BIT_DIR, S_IRWXU | S_ISVTX));\r\n    LxtCheckErrno(mkdir(VFS_ACCESS_STICKY_BIT_DIR \"/dir1\", S_IRWXU));\r\n    LxtCheckErrno(File = creat(VFS_ACCESS_STICKY_BIT_DIR \"/file1\", S_IRWXU));\r\n    ChildPid = fork();\r\n    if (ChildPid == 0)\r\n    {\r\n        VfsAccessRmdirCapChild();\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    ChildPid = fork();\r\n    if (ChildPid == 0)\r\n    {\r\n        VfsAccessRenameCapChild();\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (File > 0)\r\n    {\r\n        close(File);\r\n    }\r\n\r\n    rmdir(VFS_ACCESS_STICKY_BIT_DIR \"/dir1\");\r\n    remove(VFS_ACCESS_STICKY_BIT_DIR \"/file1\");\r\n    rmdir(VFS_ACCESS_STICKY_BIT_DIR);\r\n    return Result;\r\n}\r\n\r\nint VfsAccessSetUserGroupIdExecveChild(void)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine runs the child process for VfsAccessSetUserGroupId.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    uid_t EffectiveGroup;\r\n    uid_t FilesystemGroup;\r\n    uid_t RealGroup;\r\n    uid_t SavedGroup;\r\n    int Result = LXT_RESULT_FAILURE;\r\n    uid_t EffectiveUser;\r\n    uid_t FilesystemUser;\r\n    uid_t RealUser;\r\n    uid_t SavedUser;\r\n\r\n    LxtLogInfo(\"Child executable starting\");\r\n\r\n    //\r\n    // Get the user and group id and verify they match the expected.\r\n    //\r\n\r\n    LxtCheckResult(getresuid(&RealUser, &EffectiveUser, &SavedUser));\r\n    LxtCheckEqual(EffectiveUser, VFS_ACCESS_UID, \"%u\");\r\n    LxtCheckEqual(SavedUser, VFS_ACCESS_UID, \"%u\");\r\n\r\n    FilesystemUser = LxtSetfsuid(-1);\r\n    LxtCheckEqual(FilesystemUser, VFS_ACCESS_UID, \"%u\");\r\n\r\n    LxtCheckResult(getresgid(&RealGroup, &EffectiveGroup, &SavedGroup));\r\n    LxtCheckEqual(EffectiveGroup, VFS_ACCESS_UID, \"%u\");\r\n    LxtCheckEqual(SavedGroup, VFS_ACCESS_UID, \"%u\");\r\n\r\n    FilesystemGroup = LxtSetfsgid(-1);\r\n    LxtCheckEqual(FilesystemGroup, VFS_ACCESS_UID, \"%u\");\r\n\r\n    LxtLogInfo(\"Child executable finished\");\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid VfsAccessSetUserGroupIdFSetIdChild(int Fd1)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    char* Data = \"Test data\";\r\n    int Fd2 = -1;\r\n    char Path[PATH_MAX];\r\n    int Result;\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n\r\n    //\r\n    // Drop privileges so the current process does not have VFS capabilities\r\n    // and is in the other user\\group.\r\n    //\r\n\r\n    LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n    LxtCheckErrno(setgid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(setuid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n    // The plan 9 server cannot know about the uid change after open, so reopen\r\n    // the file with the new security context.\r\n    if (g_LxtFsInfo.FsType == LxtFsTypePlan9)\r\n    {\r\n        snprintf(Path, sizeof(Path), \"/proc/self/fd/%d\", Fd1);\r\n        LxtCheckResult(Fd2 = open(Path, O_WRONLY));\r\n        LxtCheckErrno(write(Fd2, Data, 1));\r\n        LxtCheckClose(Fd2);\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno(write(Fd1, Data, 1));\r\n    }\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd2 >= 0)\r\n    {\r\n        close(Fd2);\r\n    }\r\n\r\n    _exit(Result);\r\n}\r\n\r\nvoid VfsAccessSetUserGroupIdChmodChild(char* FilePath, uid_t Uid, gid_t Gid)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct stat Buffer;\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int Result;\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n    CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);\r\n    // CapData[CAP_TO_INDEX(CAP_FOWNER)].permitted |= CAP_TO_MASK(CAP_FOWNER);\r\n    CapData[0].effective = CapData[0].permitted;\r\n    CapData[1].effective = CapData[1].permitted;\r\n    LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n    LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n    LxtLogInfo(\"chown(%s, %d, %d)\", FilePath, Uid, Gid);\r\n    LxtCheckErrnoFailure(chown(FilePath, Uid, Gid), EPERM);\r\n    LxtCheckErrno(stat(FilePath, &Buffer));\r\n    LxtCheckEqual((Buffer.st_mode & (S_ISUID | S_ISGID)), (S_ISUID | S_ISGID), \"%o\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    _exit(Result);\r\n}\r\n\r\nint VfsAccessSetUserGroupId(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char* Argv[4];\r\n    struct stat Buffer;\r\n    int ChildPid;\r\n    char* Envp[1];\r\n    mode_t ExpectedMode;\r\n    int Fd1 = 0;\r\n    int Fd2 = 0;\r\n    int Mode;\r\n    int Result;\r\n\r\n    rmdir(VFS_ACCESS_GROUP_USER_ID_DIR);\r\n\r\n    //\r\n    // Create a directory with the set-group-ID bit set.\r\n    //\r\n\r\n    LxtCheckErrno(mkdir(VFS_ACCESS_GROUP_USER_ID_DIR, S_IRWXU));\r\n    LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR, &Buffer));\r\n    LxtLogInfo(\"VFS_ACCESS_GROUP_USER_ID_DIR mode after mkdir %o\", Buffer.st_mode);\r\n\r\n    //\r\n    // Change the owner of the directory and set the set-group-ID bit.\r\n    //\r\n\r\n    LxtCheckErrno(chown(VFS_ACCESS_GROUP_USER_ID_DIR, VFS_ACCESS_UID, VFS_ACCESS_UID));\r\n    LxtCheckErrno(chmod(VFS_ACCESS_GROUP_USER_ID_DIR, S_IRWXU | S_ISGID));\r\n    LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR, &Buffer));\r\n    LxtLogInfo(\"VFS_ACCESS_GROUP_USER_ID_DIR mode after chmod %o\", Buffer.st_mode);\r\n\r\n    //\r\n    // Create some files and child directories.\r\n    //\r\n\r\n    LxtCheckErrno(Fd1 = creat(VFS_ACCESS_GROUP_USER_ID_DIR \"/file1\", 0777 | S_IRWXU | S_ISGID | S_ISUID));\r\n    LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR \"/file1\", &Buffer));\r\n    LxtLogInfo(\"VFS_ACCESS_GROUP_USER_ID_DIR /file1 mode after mkdir %o\", Buffer.st_mode);\r\n\r\n    LxtCheckErrno(mkdir(VFS_ACCESS_GROUP_USER_ID_DIR \"/dir1\", S_IRWXU));\r\n    LxtCheckErrno(Fd2 = creat(VFS_ACCESS_GROUP_USER_ID_DIR \"/dir1/file2\", S_IRWXU));\r\n    LxtCheckErrno(mkdir(VFS_ACCESS_GROUP_USER_ID_DIR \"/dir1/dir2\", S_IRWXU));\r\n\r\n    //\r\n    // Validate the files and directories have the correct uid and gid.\r\n    //\r\n\r\n    LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR \"/file1\", &Buffer));\r\n    if (Buffer.st_gid != VFS_ACCESS_UID)\r\n    {\r\n        LxtLogError(\"/file1 gid %u does not match expected %u\", Buffer.st_gid, VFS_ACCESS_UID);\r\n    }\r\n\r\n    LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR \"/dir1\", &Buffer));\r\n    if (Buffer.st_gid != VFS_ACCESS_UID)\r\n    {\r\n        LxtLogError(\"/dir gid %u does not match expected %u\", Buffer.st_gid, VFS_ACCESS_UID);\r\n    }\r\n\r\n    LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR \"/dir1/file2\", &Buffer));\r\n    if (Buffer.st_gid != VFS_ACCESS_UID)\r\n    {\r\n        LxtLogError(\"/dir1/file2 gid %u does not match expected %u\", Buffer.st_gid, VFS_ACCESS_UID);\r\n    }\r\n\r\n    LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR \"/dir1/dir2\", &Buffer));\r\n    if (Buffer.st_gid != VFS_ACCESS_UID)\r\n    {\r\n        LxtLogError(\"/dir1/dir2 gid %u does not match expected %u\", Buffer.st_gid, VFS_ACCESS_UID);\r\n    }\r\n\r\n    //\r\n    // Validate the execute behavior of the set user id and group id bits.\r\n    // Make a copy of the current binary to use for the test.\r\n    //\r\n\r\n    // change Args->Argv[0] so that it points to the new single test binary design\r\n    Args->Argv[0] = WSL_UNIT_TEST_BINARY;\r\n\r\n    LxtCheckResult(LxtCopyFile(Args->Argv[0], VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\"));\r\n\r\n    LxtCheckErrno(chown(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", VFS_ACCESS_UID, VFS_ACCESS_UID));\r\n\r\n    LxtCheckErrno(stat(Args->Argv[0], &Buffer));\r\n    LxtCheckErrno(chmod(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", (Buffer.st_mode | S_ISUID | S_ISGID)));\r\n\r\n    ExpectedMode = Buffer.st_mode;\r\n\r\n    //\r\n    // Start the child process.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        Argv[0] = VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\";\r\n        Argv[1] = \"vfsaccess\";\r\n        Argv[2] = \"-c\";\r\n        Argv[3] = Envp[0] = NULL;\r\n        LxtCheckErrno(stat(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", &Buffer));\r\n        LxtLogInfo(\"child %o %u %u\", Buffer.st_mode, Buffer.st_uid, Buffer.st_gid);\r\n        execve(Argv[0], Argv, Envp);\r\n        LxtLogError(\"Execve failed, errno: %d (%s)\", errno, strerror(errno));\r\n        _exit(LXT_RESULT_FAILURE);\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Set the uid and gid again to make sure the set-user-id and set-group-id\r\n    // bits are stripped from the mode.\r\n    //\r\n\r\n    LxtCheckErrno(chown(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", -1, -1));\r\n    LxtCheckErrno(stat(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", &Buffer));\r\n    LxtCheckEqual(Buffer.st_mode, ExpectedMode, \"0%o\");\r\n\r\n    //\r\n    // Re-set the set-user-id and set-group-id bits.\r\n    //\r\n\r\n    LxtCheckErrno(chmod(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", (Buffer.st_mode | S_ISUID | S_ISGID)));\r\n\r\n    // VirtioFs does not currently handle capability flags. There is a new KILLPRIV2 FUSE flag that may address this in the future.\r\n    if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        //\r\n        // Fork and drop privileges so the current process does not have CAP_FOWNER\r\n        // which is required for changing the owner of a file with the set-user-id\r\n        // or set-group-id bits set.\r\n        //\r\n\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            VfsAccessSetUserGroupIdChmodChild(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", VFS_ACCESS_UID, VFS_ACCESS_UID);\r\n        }\r\n\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            VfsAccessSetUserGroupIdChmodChild(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", VFS_ACCESS_UID, -1);\r\n        }\r\n\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            VfsAccessSetUserGroupIdChmodChild(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", -1, VFS_ACCESS_UID);\r\n        }\r\n\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            VfsAccessSetUserGroupIdChmodChild(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\", -1, -1);\r\n        }\r\n\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    }\r\n\r\n    //\r\n    // Validate the behavior of CAP_FSETID for files.\r\n    //\r\n\r\n    LxtLogInfo(\"Checking CAP_FSETID for files\");\r\n    LxtCheckErrno(fstat(Fd1, &Buffer));\r\n    Mode = Buffer.st_mode;\r\n    if ((Mode & (S_ISGID | S_ISUID)) != (S_ISGID | S_ISUID))\r\n    {\r\n        LxtLogError(\"Unexpected mode\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(write(Fd1, &Buffer, sizeof(Buffer)));\r\n    LxtCheckErrno(fstat(Fd1, &Buffer));\r\n    LxtCheckEqual(Buffer.st_mode, Mode, \"%o\");\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        VfsAccessSetUserGroupIdFSetIdChild(Fd1);\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit and validate that the set id bits were\r\n    // silently removed.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    LxtCheckErrno(fstat(Fd1, &Buffer));\r\n    LxtCheckEqual(Buffer.st_mode, Mode & ~(S_ISGID | S_ISUID), \"%o\");\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd1 > 0)\r\n    {\r\n        LxtClose(Fd1);\r\n    }\r\n\r\n    if (Fd2 > 0)\r\n    {\r\n        LxtClose(Fd2);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    //\r\n    // Clean-up created files and directories.\r\n    //\r\n\r\n    unlink(VFS_ACCESS_PARENT_DIR \"/wsl_unit_tests\");\r\n    remove(VFS_ACCESS_GROUP_USER_ID_DIR \"/dir1/file2\");\r\n    rmdir(VFS_ACCESS_GROUP_USER_ID_DIR \"/dir1/dir2\");\r\n    remove(VFS_ACCESS_GROUP_USER_ID_DIR \"/file1\");\r\n    rmdir(VFS_ACCESS_GROUP_USER_ID_DIR \"/dir1\");\r\n    rmdir(VFS_ACCESS_GROUP_USER_ID_DIR);\r\n    return Result;\r\n}\r\n\r\nvoid VfsAccessInodeChecksChild(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    char* CommandLine[] = {NULL, NULL};\r\n    int ErrnoExpected;\r\n    char FileName[100];\r\n    unsigned int Index;\r\n    int Result;\r\n    int ResultActual;\r\n    int ResultExpected;\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n\r\n    //\r\n    // Drop privileges so the current process does not have VFS capabilities\r\n    // and is in the other user\\group.\r\n    //\r\n\r\n    LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n    LxtCheckErrno(setgid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(setuid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n    //\r\n    // For each file, check that read, write and execute is enforced. Similarly\r\n    // for directories check that list, create\\delete, and search is enforced.\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_VfsInodeEntries); ++Index)\r\n    {\r\n        if (S_ISREG(g_VfsInodeEntries[Index].Mode))\r\n        {\r\n\r\n            //\r\n            // Check read access.\r\n            //\r\n\r\n            ResultExpected = -1;\r\n            if ((g_VfsInodeEntries[Index].Mode & S_IROTH) != 0)\r\n            {\r\n                ResultExpected = 0;\r\n            }\r\n\r\n            ResultActual = open(g_VfsInodeEntries[Index].Name, O_RDONLY, 0);\r\n            if (ResultActual != -1)\r\n            {\r\n                LxtClose(ResultActual);\r\n                ResultActual = 0;\r\n            }\r\n\r\n            LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, \"file open O_RDONLY\", Index));\r\n\r\n            //\r\n            // Check write access.\r\n            //\r\n\r\n            ResultExpected = -1;\r\n            if ((g_VfsInodeEntries[Index].Mode & S_IWOTH) != 0)\r\n            {\r\n                ResultExpected = 0;\r\n            }\r\n\r\n            ResultActual = open(g_VfsInodeEntries[Index].Name, O_WRONLY, 0);\r\n            if (ResultActual != -1)\r\n            {\r\n                LxtClose(ResultActual);\r\n                ResultActual = 0;\r\n            }\r\n\r\n            LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, \"file open O_WRONLY\", Index));\r\n\r\n            //\r\n            // Check read\\write access.\r\n            //\r\n\r\n            ResultExpected = -1;\r\n            if (((g_VfsInodeEntries[Index].Mode & S_IROTH) != 0) && ((g_VfsInodeEntries[Index].Mode & S_IWOTH) != 0))\r\n            {\r\n\r\n                ResultExpected = 0;\r\n            }\r\n\r\n            ResultActual = open(g_VfsInodeEntries[Index].Name, O_RDWR, 0);\r\n            if (ResultActual != -1)\r\n            {\r\n                LxtClose(ResultActual);\r\n                ResultActual = 0;\r\n            }\r\n\r\n            LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, \"file open O_RDWR\", Index));\r\n\r\n            //\r\n            // Check no access (open time check for read\\write).\r\n            //\r\n\r\n            if (((g_VfsInodeEntries[Index].Mode & S_IROTH) != 0) && ((g_VfsInodeEntries[Index].Mode & S_IWOTH) != 0))\r\n            {\r\n\r\n                ResultExpected = 0;\r\n            }\r\n\r\n            ResultActual = open(g_VfsInodeEntries[Index].Name, O_NOACCESS, 0);\r\n            if (ResultActual != -1)\r\n            {\r\n                LxtClose(ResultActual);\r\n                ResultActual = 0;\r\n            }\r\n\r\n            LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, \"file open O_NOACCESS\", Index));\r\n\r\n            //\r\n            // Check execute access.\r\n            //\r\n\r\n            ErrnoExpected = EACCES;\r\n            ResultExpected = -1;\r\n            if ((g_VfsInodeEntries[Index].Mode & S_IXOTH) != 0)\r\n            {\r\n                ErrnoExpected = ENOEXEC;\r\n            }\r\n\r\n            CommandLine[0] = g_VfsInodeEntries[Index].Name;\r\n            ResultActual = execv(CommandLine[0], CommandLine);\r\n            LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"execv\", Index));\r\n        }\r\n        else\r\n        {\r\n\r\n            //\r\n            // Check read access.\r\n            //\r\n\r\n            ResultExpected = -1;\r\n            if ((g_VfsInodeEntries[Index].Mode & S_IROTH) != 0)\r\n            {\r\n                ResultExpected = 0;\r\n            }\r\n\r\n            ResultActual = open(g_VfsInodeEntries[Index].Name, O_RDONLY, 0);\r\n            if (ResultActual != -1)\r\n            {\r\n                LxtClose(ResultActual);\r\n                ResultActual = 0;\r\n            }\r\n\r\n            LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, \"directory open O_RDONLY\", Index));\r\n\r\n            //\r\n            // Check create\\delete (write) access. Execute access is also\r\n            // required to create and delete.\r\n            //\r\n\r\n            sprintf(FileName, \"%s/%s\", g_VfsInodeEntries[Index].Name, VFS_ACCESS_INODE_ENTRY_FILE);\r\n\r\n            ResultExpected = -1;\r\n            if (((g_VfsInodeEntries[Index].Mode & S_IWOTH) != 0) && ((g_VfsInodeEntries[Index].Mode & S_IXOTH) != 0))\r\n            {\r\n\r\n                ResultExpected = 0;\r\n            }\r\n\r\n            ResultActual = open(FileName, O_CREAT | O_RDONLY, S_IRUSR);\r\n            if (ResultActual != -1)\r\n            {\r\n                LxtClose(ResultActual);\r\n                ResultActual = 0;\r\n            }\r\n\r\n            LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, \"directory create file\", Index));\r\n\r\n            if (ResultActual == 0)\r\n            {\r\n                ResultActual = unlink(FileName);\r\n                LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, \"directory delete file\", Index));\r\n            }\r\n\r\n            //\r\n            // Check search (execute) access.\r\n            //\r\n\r\n            ErrnoExpected = EACCES;\r\n            ResultExpected = -1;\r\n            if ((g_VfsInodeEntries[Index].Mode & S_IXOTH) != 0)\r\n            {\r\n                ErrnoExpected = ENOENT;\r\n            }\r\n\r\n            ResultActual = open(FileName, O_RDONLY, 0);\r\n            if (ResultActual != -1)\r\n            {\r\n                LxtClose(ResultActual);\r\n                ResultActual = 0;\r\n            }\r\n\r\n            LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, \"directory search file\", Index));\r\n        }\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    _exit(Result);\r\n}\r\n\r\nint VfsAccessInodeChecks(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Result;\r\n\r\n    ChildPid = fork();\r\n    if (ChildPid == 0)\r\n    {\r\n        VfsAccessInodeChecksChild();\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint VfsAccessParseArgs(int Argc, char* Argv[], LXT_ARGS* Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine parses command line arguments for the vfsaccess tests.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of arguments.\r\n\r\n    Argv - Supplies an array of arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ArgvIndex;\r\n    bool Cleanup;\r\n    const char* Name;\r\n    int Result;\r\n    int ValidArguments;\r\n\r\n    Result = LXT_RESULT_FAILURE;\r\n    ValidArguments = 0;\r\n    g_UseDrvFs = false;\r\n    Name = LXT_NAME;\r\n    Cleanup = true;\r\n    if (Argc < 1)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    umask(0);\r\n    for (ArgvIndex = 1; ArgvIndex < Argc; ++ArgvIndex)\r\n    {\r\n        if (strcmp(Argv[ArgvIndex], \"drvfs\") == 0)\r\n        {\r\n            g_UseDrvFs = true;\r\n            Name = LXT_NAME_DRVFS;\r\n            continue;\r\n        }\r\n\r\n        if (Argv[ArgvIndex][0] != '-')\r\n        {\r\n            printf(\"Unexpected character %s\", Argv[ArgvIndex]);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        switch (Argv[ArgvIndex][1])\r\n        {\r\n        case 'c':\r\n\r\n            //\r\n            // Run the setusergroupid execve test child\r\n            //\r\n\r\n            ValidArguments = 1;\r\n            Cleanup = false;\r\n            Result = VfsAccessSetUserGroupIdExecveChild();\r\n            goto ErrorExit;\r\n\r\n        case 'v':\r\n        case 'l':\r\n\r\n            //\r\n            // This was already taken care of by LxtInitialize.\r\n            //\r\n\r\n            ++ArgvIndex;\r\n\r\n            break;\r\n\r\n        case 'h':\r\n        case 'a':\r\n            break;\r\n\r\n        default:\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    //\r\n    // If -c was not specified, just run the tests\r\n    //\r\n\r\n    ValidArguments = 1;\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, Args, Name));\r\n    LxtCheckResult(LxtFsTestSetup(Args, VFS_ACCESS_PARENT_DIR, \"/vfsaccesstest\", g_UseDrvFs));\r\n\r\n    if (Args->HelpRequested == false)\r\n    {\r\n        LxtLogInfo(\"Creating files.\");\r\n        LxtCheckResult(VfsAccessFileObjectCreateFiles());\r\n        LxtCheckResult(VfsAccessFileObjectCreateSymlinks());\r\n    }\r\n\r\n    //\r\n    // Tests must be run forked since some of the tests change the uid and\r\n    // don't change it back, which breaks umount during cleanup.\r\n    //\r\n\r\n    LxtCheckResult(LxtRunVariationsForked(Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    if (ValidArguments == 0)\r\n    {\r\n        printf(\"\\nuse: %s <One of the below arguments>\\n\", Argv[0]);\r\n        printf(\"\\t-c : Run %s execve test child (don't use directly)\\n\", Argv[0]);\r\n    }\r\n\r\n    if (Cleanup != false)\r\n    {\r\n        VfsAccessFileObjectCleanup();\r\n        LxtFsTestCleanup(VFS_ACCESS_PARENT_DIR, \"/vfsaccesstest\", g_UseDrvFs);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid VfsAccessUTimeCapChild(void)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int Fd;\r\n    int Result;\r\n    struct timeval Times[2];\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n    CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);\r\n    CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);\r\n    CapData[0].effective = CapData[0].permitted;\r\n    CapData[1].effective = CapData[1].permitted;\r\n\r\n    //\r\n    // Drop privileges so the current process does not have CAP_FOWNER.\r\n    //\r\n\r\n    LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n    LxtCheckErrno(setgid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(setuid(VFS_ACCESS_UID));\r\n    LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n\r\n    //\r\n    // Create a file with a different user.\r\n    //\r\n\r\n    unlink(VFS_ACCESS_UTIME_FILE);\r\n    LxtCheckErrno(Fd = open(VFS_ACCESS_UTIME_FILE, O_CREAT, 0));\r\n    LxtClose(Fd);\r\n    LxtCheckErrno(chown(VFS_ACCESS_UTIME_FILE, VFS_ACCESS_UID + 1, VFS_ACCESS_UID + 1));\r\n\r\n    //\r\n    // Try to change the time on the file to 0.\r\n    //\r\n\r\n    memset(Times, 0, sizeof(Times));\r\n    LxtCheckErrnoFailure(utimes(VFS_ACCESS_UTIME_FILE, Times), EPERM);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    unlink(VFS_ACCESS_UTIME_FILE);\r\n    _exit(Result);\r\n}\r\n\r\nint VfsAccessUTimeCap(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int Result;\r\n\r\n    ChildPid = fork();\r\n    if (ChildPid == 0)\r\n    {\r\n        VfsAccessUTimeCapChild();\r\n    }\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint VfsAccessSetFsUid(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    unsigned long long Effective;\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    struct __user_cap_data_struct ExpectedCapData[2];\r\n    int Fd;\r\n    int Index;\r\n    struct passwd* Password;\r\n    int Result;\r\n\r\n    memset(&CapData, 0, sizeof(CapData));\r\n    memset(&CapHeader, 0, sizeof(CapHeader));\r\n    CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n\r\n    Fd = -1;\r\n    Result = LXT_RESULT_FAILURE;\r\n\r\n    //\r\n    // Get the password entry for the 'nobody' user.\r\n    //\r\n\r\n    Password = getpwnam(\"nobody\");\r\n    if (Password == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Create a file to be used for access checks.\r\n    //\r\n\r\n    Fd = open(VFS_ACCESS_FSUID_FILE, O_CREAT | O_RDWR, 0644);\r\n    if (Fd < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Get the original capabilities.\r\n    //\r\n\r\n    LxtCheckErrno(LxtCapGet(&CapHeader, ExpectedCapData));\r\n    Effective = (((unsigned long long)ExpectedCapData[0].effective) << 32) | ExpectedCapData[1].effective;\r\n    LxtLogInfo(\"Before setfsuid(nobody) %016llX\", Effective);\r\n\r\n    //\r\n    // Set the fsuid and ensure that the correct capabilities are dropped when\r\n    // switching from root.\r\n    //\r\n\r\n    if (LxtSetfsuid(Password->pw_uid) < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(LxtCapGet(&CapHeader, CapData));\r\n    Effective = (((unsigned long long)CapData[0].effective) << 32) | CapData[1].effective;\r\n    LxtLogInfo(\"After setfsuid(nobody) %016llX\", Effective);\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_VfsSetFsUidCaps); Index++)\r\n    {\r\n        ExpectedCapData[CAP_TO_INDEX(g_VfsSetFsUidCaps[Index])].effective &= ~CAP_TO_MASK(g_VfsSetFsUidCaps[Index]);\r\n    }\r\n\r\n    if ((CapData[0].effective != ExpectedCapData[0].effective) || (CapData[1].effective != ExpectedCapData[1].effective))\r\n    {\r\n\r\n        LxtLogError(\"Capabilities do not match expected\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Verify that opening the file fails since we no longer have the correct fsuid or capabilities.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(open(VFS_ACCESS_FSUID_FILE, O_RDWR), EACCES);\r\n\r\n    //\r\n    // Set the fsuid back to root and verify that the capabilities were correctly restored.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSetfsuid(0));\r\n\r\n    LxtCheckErrno(LxtCapGet(&CapHeader, CapData));\r\n    Effective = (((unsigned long long)CapData[0].effective) << 32) | CapData[1].effective;\r\n    LxtLogInfo(\"After setfsuid(root) %016llX\", Effective);\r\n    for (Index = 0; Index < LXT_COUNT_OF(g_VfsSetFsUidCaps); Index++)\r\n    {\r\n        ExpectedCapData[CAP_TO_INDEX(g_VfsSetFsUidCaps[Index])].effective |= CAP_TO_MASK(g_VfsSetFsUidCaps[Index]);\r\n    }\r\n\r\n    if ((CapData[0].effective != ExpectedCapData[0].effective) || (CapData[1].effective != ExpectedCapData[1].effective))\r\n    {\r\n\r\n        LxtLogError(\"Capabilities do not match expected\");\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != -1)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    unlink(VFS_ACCESS_FSUID_FILE);\r\n    return Result;\r\n}\r\n\r\nvoid getreuid(struct reuid_t* Set)\r\n{\r\n    getresuid(&Set->r, &Set->e, &Set->s);\r\n    // LxtLogInfo(\"getresuid(%d,%d,%d)\",Set->r,Set->e,Set->s);\r\n}\r\n\r\npid_t fork_wait()\r\n{\r\n    pid_t pid;\r\n    int status = 0;\r\n    if ((pid = fork()) == 0)\r\n    {\r\n        return pid;\r\n    }\r\n    else\r\n    {\r\n        waitpid(pid, &status, 0);\r\n    }\r\n    return pid;\r\n}\r\n\r\nint VfsAccessSetUid(PLXT_ARGS Args)\r\n\r\n{\r\n\r\n    struct reuid_t Original;\r\n    struct reuid_t Set;\r\n    struct passwd* Nobody;\r\n    int Result;\r\n\r\n    getreuid(&Original);\r\n    LxtLogInfo(\"Current UID: %d\", Original.r);\r\n    LxtLogInfo(\"Current EUID: %d\", Original.e);\r\n    LxtLogInfo(\"Current SUID: %d\", Original.s);\r\n\r\n    //\r\n    // Try setting without changing\r\n    //\r\n\r\n    setreuid(-1, -1);\r\n    getreuid(&Set);\r\n    LxtCheckEqual(Set.r, Original.r, \"%d\");\r\n    LxtCheckEqual(Set.e, Original.e, \"%d\");\r\n    LxtCheckEqual(Set.s, Original.s, \"%d\");\r\n\r\n    //\r\n    // More tests possible when run as root.\r\n    //\r\n\r\n    if (Original.r == 0 || Original.e == 0)\r\n    {\r\n        Nobody = getpwnam(\"nobody\");\r\n        if (Nobody == NULL)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Couldn't get details for user 'nobody'\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtLogInfo(\"Attempting setreuid(%d, -1)\", Nobody->pw_uid);\r\n        LxtCheckResult(setreuid(Nobody->pw_uid, -1));\r\n        getreuid(&Set);\r\n        if (Set.r != Nobody->pw_uid || Set.e != 0 || Set.s != 0)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n        }\r\n\r\n        //\r\n        // reset state to 0, 0, 0\r\n        //\r\n\r\n        LxtLogInfo(\"setuid(0)\");\r\n        LxtCheckResult(setuid(0));\r\n        getreuid(&Set);\r\n        if (Set.r != 0 || Set.e != 0 || Set.s != 0)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            return -1; // Fatal, Nobody tests rely on this succeeding\r\n        }\r\n\r\n        //\r\n        // This test checks that setuid only touches the ruid and suid values.\r\n        //\r\n\r\n        LxtLogInfo(\"setresuid(-1, %d, %d)\", Nobody->pw_uid, Nobody->pw_uid);\r\n        LxtCheckResult(setresuid(-1, Nobody->pw_uid, Nobody->pw_uid));\r\n        getreuid(&Set);\r\n        if (Set.r != 0 || Set.e != Nobody->pw_uid || Set.s != Nobody->pw_uid)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n        }\r\n\r\n        //\r\n        // Set state to 0, 0, 65534\r\n        //\r\n\r\n        LxtLogInfo(\"Attempting setuid(0)\");\r\n        LxtCheckResult(setuid(0));\r\n        getreuid(&Set);\r\n        if (Set.r != 0 || Set.e != 0 || Set.s != 65534)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n        }\r\n\r\n        //\r\n        // This test checks the first transitive property of setreuid wherein\r\n        // setting the effective uid also sets the suid\r\n        //\r\n        // Reset state to 0, 0, 0\r\n        //\r\n\r\n        LxtLogInfo(\"setresuid(0, 0, 0)\");\r\n        LxtCheckResult(setresuid(0, 0, 0));\r\n        getreuid(&Set);\r\n        if (Set.r != 0 || Set.e != 0 || Set.s != 0)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            return -1; // Fatal, Other tests rely on this succeeding\r\n        }\r\n        LxtLogInfo(\"setreuid(-1, %d)\", Nobody->pw_uid);\r\n        LxtCheckResult(setreuid(-1, Nobody->pw_uid));\r\n        getreuid(&Set);\r\n        if (Set.r != 0 || Set.e != Nobody->pw_uid || Set.s != Nobody->pw_uid)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n        }\r\n\r\n        //\r\n        // Set state to 0, 0, 65534\r\n        //\r\n\r\n        LxtLogInfo(\"Attempting setuid(0)\");\r\n        LxtCheckResult(setuid(0));\r\n        getreuid(&Set);\r\n        if (Set.r != 0 || Set.e != 0 || Set.s != 65534)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n        }\r\n\r\n        //\r\n        // This test checks the second transitive property of setreuid\r\n        // wherein setting the ruid, but not the euid will Set the suid\r\n        // to be the euid\r\n        //\r\n\r\n        LxtLogInfo(\"setresuid(%d, 0, VFS_ACCESS_UID)\", Nobody->pw_uid);\r\n        LxtCheckResult(setresuid(Nobody->pw_uid, 0, VFS_ACCESS_UID));\r\n        getreuid(&Set);\r\n        if (Set.r != Nobody->pw_uid || Set.e != 0 || Set.s != VFS_ACCESS_UID)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n        }\r\n        LxtLogInfo(\"Attempting setreuid(0, -1)\");\r\n        LxtCheckResult(setreuid(0, -1));\r\n        getreuid(&Set);\r\n        if (Set.r != 0 || Set.e != 0 || Set.s != 0)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n        }\r\n\r\n        //\r\n        // This test checks that unprivileged processes can Set the euid\r\n        // to the ruid or suid. Need to fork and wait as privileges are\r\n        // irreversibly dropped by this syscall\r\n        //\r\n\r\n        if (fork_wait() == 0)\r\n        {\r\n            LxtLogInfo(\"setresuid(%d, VFS_ACCESS_UID, 0)\", Nobody->pw_uid);\r\n            LxtCheckResult(setresuid(Nobody->pw_uid, VFS_ACCESS_UID, 0));\r\n            getreuid(&Set);\r\n            if (Set.r != Nobody->pw_uid || Set.e != VFS_ACCESS_UID || Set.s != 0)\r\n            {\r\n                LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            }\r\n            LxtLogInfo(\"Attempting setreuid(-1, %d)\", Nobody->pw_uid);\r\n            LxtCheckResult(setreuid(-1, Nobody->pw_uid));\r\n            getreuid(&Set);\r\n            if (Set.r != Nobody->pw_uid || Set.e != Nobody->pw_uid || Set.s != 0)\r\n            {\r\n                LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            }\r\n            exit(0);\r\n        }\r\n\r\n        //\r\n        // This test checks that unprivileged processes can Set the euid\r\n        // to the ruid or suid.\r\n        //\r\n\r\n        LxtLogInfo(\"setresuid(%d, %d, 0)\", Nobody->pw_uid, Nobody->pw_uid);\r\n        LxtCheckResult(setresuid(Nobody->pw_uid, Nobody->pw_uid, 0));\r\n        getreuid(&Set);\r\n        if (Set.r != Nobody->pw_uid || Set.e != Nobody->pw_uid || Set.s != 0)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n        }\r\n        LxtLogInfo(\"Attempting setreuid(-1, 0)\");\r\n        LxtCheckResult(setreuid(-1, 0));\r\n        getreuid(&Set);\r\n        if (Set.r != Nobody->pw_uid || Set.e != 0 || Set.s != 0)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n        }\r\n\r\n        //\r\n        // Reset state to 0, 0, 0\r\n        //\r\n\r\n        LxtLogInfo(\"setresuid(0, 0, 0)\");\r\n        LxtCheckResult(setresuid(0, 0, 0));\r\n        getreuid(&Set);\r\n        if (Set.r != 0 || Set.e != 0 || Set.s != 0)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            return -1; // Fatal, Other tests rely on this succeeding\r\n        }\r\n\r\n        //\r\n        // This test validates that unprivileged users can only Set the ruid\r\n        // to the ruid or the euid\r\n        //\r\n\r\n        if (fork_wait() == 0)\r\n        {\r\n            LxtLogInfo(\"setresuid(%d, VFS_ACCESS_UID, 0)\", Nobody->pw_uid);\r\n            LxtCheckResult(setresuid(Nobody->pw_uid, VFS_ACCESS_UID, 0));\r\n            getreuid(&Set);\r\n            if (Set.r != Nobody->pw_uid || Set.e != VFS_ACCESS_UID || Set.s != 0)\r\n            {\r\n                LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            }\r\n            LxtLogInfo(\"Attempting setreuid(VFS_ACCESS_UID, -1)\");\r\n            LxtCheckResult(setreuid(VFS_ACCESS_UID, -1));\r\n            getreuid(&Set);\r\n            if (Set.r != VFS_ACCESS_UID || Set.e != VFS_ACCESS_UID || Set.s != VFS_ACCESS_UID)\r\n            {\r\n                LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            }\r\n            exit(0);\r\n        }\r\n        if (fork_wait() == 0)\r\n        {\r\n            LxtLogInfo(\"setresuid(%d, VFS_ACCESS_UID, 0)\", Nobody->pw_uid);\r\n            LxtCheckResult(setresuid(Nobody->pw_uid, VFS_ACCESS_UID, 0));\r\n            getreuid(&Set);\r\n            if (Set.r != Nobody->pw_uid || Set.e != VFS_ACCESS_UID || Set.s != 0)\r\n            {\r\n                LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            }\r\n            LxtLogInfo(\"Attempting setreuid(0, -1)\");\r\n            LxtCheckErrnoFailure(setreuid(0, -1), EPERM);\r\n            getreuid(&Set);\r\n            if (Set.r != Nobody->pw_uid || Set.e != VFS_ACCESS_UID || Set.s != 0)\r\n            {\r\n                LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            }\r\n            exit(0);\r\n        }\r\n\r\n        //\r\n        // Drop all permissions permanently\r\n        //\r\n\r\n        LxtLogInfo(\"Dropping all permissions\");\r\n        LxtLogInfo(\"setresuid(%d, %d, %d)\", Nobody->pw_uid, Nobody->pw_uid, Nobody->pw_uid);\r\n        LxtCheckResult(setresuid(Nobody->pw_uid, Nobody->pw_uid, Nobody->pw_uid));\r\n        getreuid(&Set);\r\n        if (Set.r != Nobody->pw_uid || Set.e != Nobody->pw_uid)\r\n        {\r\n            LxtLogError(\"uid=%d, euid=%d, suid=%d\", Set.r, Set.e, Set.s);\r\n            return -1;\r\n        }\r\n    }\r\n\r\n    //\r\n    // Try to gain root uid\r\n    //\r\n\r\n    LxtLogInfo(\"Attempting setreuid(0, -1)\");\r\n    LxtCheckErrnoFailure(setreuid(0, -1), EPERM);\r\n    getreuid(&Set);\r\n    if (Set.r == 0 || Set.e == 0)\r\n    {\r\n        LxtLogError(\"Gained root permissions!\");\r\n    }\r\n    LxtLogInfo(\"Attempting setreuid(-1, 0)\");\r\n    LxtCheckErrnoFailure(setreuid(-1, 0), EPERM);\r\n    getreuid(&Set);\r\n    if (Set.r == 0 || Set.e == 0)\r\n    {\r\n        LxtLogError(\"Gained root permissions!\");\r\n    }\r\n    LxtLogInfo(\"Attempting setreuid(0, 0)\");\r\n    LxtCheckErrnoFailure(setreuid(0, 0), EPERM);\r\n    getreuid(&Set);\r\n    if (Set.r == 0 || Set.e == 0)\r\n    {\r\n        LxtLogError(\"Gained root permissions!\");\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/vnet.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    vnet.c\r\n\r\nAbstract:\r\n\r\n    This file is a test of virtual network devices.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <fcntl.h>\r\n#include <unistd.h>\r\n#include <sys/ioctl.h>\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <sys/wait.h>\r\n#include <sys/socket.h>\r\n#include <fcntl.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <stddef.h>\r\n#include <bits/local_lim.h>\r\n#include <net/if.h>\r\n#include <net/if_arp.h>\r\n#include <arpa/inet.h>\r\n#include <netinet/in.h>\r\n#include <netinet/ip.h>\r\n#include <netinet/udp.h>\r\n#include <netinet/tcp.h>\r\n#include <linux/icmp.h>\r\n#include <netinet/icmp6.h>\r\n#include <net/if.h>\r\n#include <linux/net_namespace.h>\r\n#include <linux/netlink.h>\r\n#include <linux/rtnetlink.h>\r\n#include <linux/sockios.h>\r\n#include <linux/veth.h>\r\n\r\n//\r\n// Globals.\r\n//\r\n\r\n#define LXT_NAME \"VirtualNetwork\"\r\n#define LXT_REQUEST_SEQUENCE 0x4567\r\n#define LXT_IP_ADDRESS1 \"172.12.13.113\"\r\n#define LXT_IP_ADDRESS2 \"172.12.13.114\"\r\n\r\n//\r\n// Functions.\r\n//\r\n\r\nint BindSocketForNetlink(int Socket);\r\n\r\nint CreateVirtualBridgeViaIoctl(const char* Name);\r\n\r\nint CreateVirtualBridgeViaNetlink(const char* Name);\r\n\r\nvoid CreateVirtualDeviceInfo(const char* Type, const char* Name, const char* DeviceData, int DeviceDataSize, char* Buffer, int* BufferSize);\r\n\r\nint CreateVirtualDeviceViaNetlink(int Flags, const char* DeviceData, int DeviceDataSize, struct nlmsghdr** Response);\r\n\r\nint CreateVirtualEthernetPairViaNetlink(const char* Name, const char* PeerName);\r\n\r\nint DeleteVirtualDeviceViaNetlink(const char* Name);\r\n\r\nint DeleteVirtualBridgeViaIoctl(const char* Name);\r\n\r\nint GetNetworkInterfaceIndex(const char* Name);\r\n\r\nint SetIpAddress(const char* InterfaceName, const struct in_addr* AddressIpv4, char PrefixLength);\r\n\r\nint SetRoute(const char* InterfaceName, const struct in_addr* AddressIpv4, int PrefixLength);\r\n\r\nint SetVirtualDeviceAttributeViaNetlink(const char* Name, int AttributeType, int AttributeValue, int* Response);\r\n\r\nint SetVirtualDeviceFlagViaNetlink(const char* Name, int Flag, bool IsFlagEnabled, int* Response);\r\n\r\nint VerifyRouteLinkDoesNotExist(\r\n    const char* InterfaceName,\r\n    /* optional */ int* SocketToUse);\r\n\r\nint VerifyRouteLinkExists(\r\n    const char* InterfaceName,\r\n    /* optional */ int* SocketToUse);\r\n\r\n//\r\n// Test cases.\r\n//\r\n\r\nLXT_VARIATION_HANDLER EmptyDeviceErrorFromNetlink;\r\nLXT_VARIATION_HANDLER PhysicalDeviceNamespace1;\r\nLXT_VARIATION_HANDLER SanityPermissionsCheck;\r\nLXT_VARIATION_HANDLER VirtualBridgeAutoName;\r\nLXT_VARIATION_HANDLER VirtualBridgeFromIoctl1;\r\nLXT_VARIATION_HANDLER VirtualBridgeNamespace1;\r\nLXT_VARIATION_HANDLER VirtualBridgeFromNetlink1;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairFromNetlink1;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairFromNetlink2;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairNamespace1;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairNamespace2;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairNamespace3;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairNamespace4;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairNamespace5;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairConfigure;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairData;\r\nLXT_VARIATION_HANDLER VirtualEthernetPairNamespaceData;\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"Permissions Check\", SanityPermissionsCheck},\r\n    {\"No Device Error Check\", EmptyDeviceErrorFromNetlink},\r\n    {\"Virtual Bridge IOCTL\", VirtualBridgeFromIoctl1},\r\n    {\"Virtual Bridge Netlink\", VirtualBridgeFromNetlink1},\r\n    {\"Virtual Bridge auto name generation\", VirtualBridgeAutoName},\r\n    {\"Virtual Ethernet Pair\", VirtualEthernetPairFromNetlink1},\r\n    {\"Virtual Ethernet Pair (part 2)\", VirtualEthernetPairFromNetlink2},\r\n    {\"Virtual Bridge basic namespace check\", VirtualBridgeNamespace1},\r\n    {\"Virtual Ethernet Pair basic namespace check\", VirtualEthernetPairNamespace1},\r\n    {\"Virtual Ethernet Pair namespace check (part 2)\", VirtualEthernetPairNamespace2},\r\n    {\"Virtual Ethernet Pair namespace check (part 3)\", VirtualEthernetPairNamespace3},\r\n    {\"Virtual Ethernet Pair namespace link verification\", VirtualEthernetPairNamespace4},\r\n    {\"Virtual Ethernet Pair namespace socket check\", VirtualEthernetPairNamespace5},\r\n    {\"Virtual Ethernet Pair simple configuration\", VirtualEthernetPairConfigure},\r\n\r\n    //\r\n    // Requires firewall manipulation to allow packet traversal\r\n    //\r\n\r\n    //{\"Virtual Ethernet Pair data check\", VirtualEthernetPairData},\r\n    //{\"Virtual Ethernet Pair namespace data check\", VirtualEthernetPairNamespaceData},\r\n\r\n    //\r\n    // Moving physical adapters between namespaces is not currently supported\r\n    //\r\n\r\n    //{\"Physical device basic namespace check\", PhysicalDeviceNamespace1},\r\n};\r\n\r\nint VnetTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine main entry point for the virtual network device test.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int OriginalNetworkNamespaceFd;\r\n    int Result;\r\n\r\n    OriginalNetworkNamespaceFd = -1;\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n\r\n    //\r\n    // Since new devices are going to be created/destroyed, move to a new\r\n    // network namespace to try to prevent polluting the root namespace in case\r\n    // of errors.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Run tests.\r\n    //\r\n\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n\r\n    //\r\n    // Try to restore to original network namespace.\r\n    //\r\n\r\n    if (OriginalNetworkNamespaceFd > 0)\r\n    {\r\n        setns(OriginalNetworkNamespaceFd, CLONE_NEWNET);\r\n        close(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint BindSocketForNetlink(int Socket)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine is a helper function to bind a socket for NETLINK use.\r\n\r\nArguments:\r\n\r\n    Socket - Supplies the socket to bind.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_nl BindAddress;\r\n    int Result;\r\n\r\n    memset(&BindAddress, 0, sizeof(BindAddress));\r\n    BindAddress.nl_family = AF_NETLINK;\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&BindAddress, sizeof(BindAddress)));\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint CreateVirtualBridgeViaIoctl(const char* Name)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates a virtual bridge device via the SIOCBRADDBR ioctl.\r\n\r\nArguments:\r\n\r\n    Name - Supplies the name to assign the new bridge.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int Sock;\r\n\r\n    Sock = socket(AF_UNIX, SOCK_STREAM, 0);\r\n    if (Sock < 0)\r\n    {\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = ioctl(Sock, SIOCBRADDBR, Name);\r\n\r\nErrorExit:\r\n    if (Sock > 0)\r\n    {\r\n        close(Sock);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nvoid CreateVirtualDeviceInfo(const char* Type, const char* Name, const char* DeviceData, int DeviceDataSize, char* Buffer, int* BufferSize)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine generates the ifinfomsg and attributes representing a virtual\r\n    device.\r\n\r\nArguments:\r\n\r\n    Type - Supplies the optional name of the virtual device type.\r\n\r\n    Name - Supplies the name to assign the new device.\r\n\r\n    DeviceData - Supplies an optional buffer of device-type specific data.\r\n\r\n    DeviceDataSize - Supplies the size in bytes of DeviceData.\r\n\r\n    Buffer - Supplies the buffer to store the message. If this is null\r\n        BufferSize will return the number of bytes required.\r\n\r\n    BufferSize - Supplies a pointer to the size of the buffer.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct rtattr* Attribute;\r\n    int BufferOffset;\r\n    struct rtattr* LinkAttribute;\r\n    int LinkAttributeMessageOffset;\r\n    int LocalBufferSize;\r\n    int MessageSize;\r\n    int StringLength;\r\n\r\n    BufferOffset = 0;\r\n    if (Buffer == NULL)\r\n    {\r\n        LocalBufferSize = 0;\r\n    }\r\n    else\r\n    {\r\n        LocalBufferSize = *BufferSize;\r\n    }\r\n\r\n    MessageSize = RTA_ALIGN(sizeof(struct ifinfomsg));\r\n    if (LocalBufferSize >= MessageSize)\r\n    {\r\n        memset(&Buffer[BufferOffset], 0, (MessageSize - BufferOffset));\r\n        BufferOffset = MessageSize;\r\n    }\r\n\r\n    StringLength = strlen(Name);\r\n    MessageSize += RTA_SPACE(StringLength + 1);\r\n    if (LocalBufferSize >= MessageSize)\r\n    {\r\n        Attribute = (struct rtattr*)&Buffer[BufferOffset];\r\n        Attribute->rta_len = RTA_LENGTH(StringLength + 1);\r\n        Attribute->rta_type = IFLA_IFNAME;\r\n        memcpy(RTA_DATA(Attribute), Name, StringLength + 1);\r\n        BufferOffset = MessageSize;\r\n    }\r\n\r\n    if ((Type != NULL) || ((DeviceData != NULL) && (DeviceDataSize > 0)))\r\n    {\r\n        MessageSize += RTA_SPACE(0);\r\n        if (LocalBufferSize >= MessageSize)\r\n        {\r\n            LinkAttribute = (struct rtattr*)&Buffer[BufferOffset];\r\n            LinkAttribute->rta_type = IFLA_LINKINFO;\r\n\r\n            //\r\n            // The length of the link attribute contains other nested\r\n            // attributes, so remember it here and set it later.\r\n            //\r\n\r\n            LinkAttributeMessageOffset = BufferOffset;\r\n            BufferOffset = MessageSize;\r\n        }\r\n        else\r\n        {\r\n            LinkAttribute = NULL;\r\n        }\r\n\r\n        StringLength = strlen(Type);\r\n        MessageSize += RTA_SPACE(StringLength);\r\n        if (LocalBufferSize >= MessageSize)\r\n        {\r\n            Attribute = (struct rtattr*)&Buffer[BufferOffset];\r\n            Attribute->rta_len = RTA_LENGTH(StringLength);\r\n            Attribute->rta_type = IFLA_INFO_KIND;\r\n            memcpy(RTA_DATA(Attribute), Type, StringLength);\r\n            BufferOffset = MessageSize;\r\n        }\r\n\r\n        if ((DeviceData != NULL) && (DeviceDataSize > 0))\r\n        {\r\n            MessageSize += RTA_SPACE(DeviceDataSize);\r\n            if (LocalBufferSize >= MessageSize)\r\n            {\r\n                Attribute = (struct rtattr*)&Buffer[BufferOffset];\r\n                Attribute->rta_len = RTA_LENGTH(DeviceDataSize);\r\n                Attribute->rta_type = IFLA_INFO_DATA;\r\n                memcpy(RTA_DATA(Attribute), DeviceData, DeviceDataSize);\r\n                BufferOffset = MessageSize;\r\n            }\r\n        }\r\n\r\n        if (LinkAttribute != NULL)\r\n        {\r\n            LinkAttribute->rta_len = MessageSize - LinkAttributeMessageOffset;\r\n        }\r\n    }\r\n\r\n    *BufferSize = MessageSize;\r\n    return;\r\n}\r\n\r\nint CreateVirtualBridgeViaNetlink(const char* Name)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates a new virtual bridge using the netlink RTM_NEWLINK\r\n    message. On success the caller should free the Response buffer.\r\n\r\nArguments:\r\n\r\n    Name - Supplies the name to give the new virtual bridge.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char* DeviceData;\r\n    int DeviceDataSize;\r\n    struct nlmsgerr* ErrorMessage;\r\n    struct nlmsghdr* Response;\r\n    int Result;\r\n\r\n    Response = NULL;\r\n\r\n    //\r\n    // Determine the size of the device data part of the message.\r\n    //\r\n\r\n    CreateVirtualDeviceInfo(\"bridge\", Name, NULL, 0, NULL, &DeviceDataSize);\r\n\r\n    //\r\n    // Allocate a buffer to hold the device data.\r\n    //\r\n\r\n    DeviceData = LxtAlloc(DeviceDataSize);\r\n    if (DeviceData == NULL)\r\n    {\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Fill in the device data buffer.\r\n    //\r\n\r\n    CreateVirtualDeviceInfo(\"bridge\", Name, NULL, 0, DeviceData, &DeviceDataSize);\r\n\r\n    //\r\n    // Attempt to create the device.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualDeviceViaNetlink((NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | NLM_F_REQUEST), DeviceData, DeviceDataSize, &Response));\r\n\r\n    //\r\n    // Verify success.\r\n    //\r\n\r\n    ErrorMessage = NLMSG_DATA(Response);\r\n    LxtCheckEqual(ErrorMessage->error, 0, \"%d\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Response != NULL)\r\n    {\r\n        LxtFree(Response);\r\n    }\r\n\r\n    if (DeviceData != NULL)\r\n    {\r\n        LxtFree(DeviceData);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint CreateVirtualDeviceViaNetlink(int Flags, const char* DeviceData, int DeviceDataSize, struct nlmsghdr** Response)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates a new virtual device using the netlink RTM_NEWLINK\r\n    message. On success the caller should free the Response buffer.\r\n\r\nArguments:\r\n\r\n    Flags - Supplies the message flags.\r\n\r\n    DeviceData - Supplies the optional device data.\r\n\r\n    DeviceDataSize - Supplies the size in bytes of the device data.\r\n\r\n    Response - Supplies a pointer to be filled in with newly allocated memory\r\n        holding the response. It is the caller's responsibility to free this\r\n        memory.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct nlmsgerr* ErrorMessage;\r\n    struct nlmsghdr* NextResponseMessage;\r\n    struct nlmsghdr* RequestMessage;\r\n    int RequestMessageSize;\r\n    struct nlmsghdr* ResponseMessage;\r\n    int ResponseMessageSize;\r\n    int ReceiveResult;\r\n    int Result;\r\n    int Socket;\r\n\r\n    //\r\n    // Allocate space for the request and the response.\r\n    //\r\n\r\n    RequestMessageSize = NLMSG_SPACE(DeviceDataSize);\r\n    RequestMessage = LxtAlloc(RequestMessageSize);\r\n    if (RequestMessage == NULL)\r\n    {\r\n        Result = ENOMEM;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    ResponseMessageSize = RequestMessageSize + sizeof(*ErrorMessage);\r\n    ResponseMessage = LxtAlloc(ResponseMessageSize);\r\n    if (RequestMessage == NULL)\r\n    {\r\n        Result = ENOMEM;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Create and bind socket.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    LxtCheckErrno(BindSocketForNetlink(Socket));\r\n\r\n    //\r\n    // Create a RTM_NEWLINK request.\r\n    //\r\n\r\n    memset(RequestMessage, 0, sizeof(*RequestMessage));\r\n    RequestMessage->nlmsg_len = RequestMessageSize;\r\n    RequestMessage->nlmsg_type = RTM_NEWLINK;\r\n    RequestMessage->nlmsg_seq = LXT_REQUEST_SEQUENCE;\r\n    RequestMessage->nlmsg_flags = Flags;\r\n    memcpy(NLMSG_DATA(RequestMessage), DeviceData, DeviceDataSize);\r\n    LxtCheckErrno(sendto(Socket, RequestMessage, RequestMessageSize, 0, NULL, 0));\r\n\r\n    //\r\n    // Get the response.\r\n    //\r\n\r\n    memset(ResponseMessage, 0, ResponseMessageSize);\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, ResponseMessage, ResponseMessageSize, 0, NULL, 0));\r\n\r\n    LxtCheckTrue(NLMSG_OK(ResponseMessage, ReceiveResult));\r\n    LxtCheckEqual(ResponseMessage->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckGreater((ResponseMessage->nlmsg_len + 1), NLMSG_PAYLOAD(ResponseMessage, 0), \"%d\");\r\n\r\n    ErrorMessage = NLMSG_DATA(ResponseMessage);\r\n    if (ErrorMessage->error < 0)\r\n    {\r\n\r\n        //\r\n        // On error, the entire message should be returned.\r\n        //\r\n\r\n        LxtCheckEqual(ResponseMessage->nlmsg_len, ResponseMessageSize, \"%d\");\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // On success, just the header of the original message is expected to\r\n        // be returned, which is already included in the error message size.\r\n        //\r\n\r\n        LxtCheckEqual(ResponseMessage->nlmsg_len, NLMSG_SPACE(sizeof(*ErrorMessage)), \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(ResponseMessage->nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ResponseMessage->nlmsg_seq, LXT_REQUEST_SEQUENCE, \"%d\");\r\n    LxtCheckEqual(ResponseMessage->nlmsg_pid, getpid(), \"%d\");\r\n    NextResponseMessage = NLMSG_NEXT(ResponseMessage, ReceiveResult);\r\n    LxtCheckTrue(NLMSG_OK(NextResponseMessage, ReceiveResult) == FALSE);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    if (RequestMessage != NULL)\r\n    {\r\n        LxtFree(RequestMessage);\r\n    }\r\n\r\n    if (Result != 0)\r\n    {\r\n        if (ResponseMessage != NULL)\r\n        {\r\n            LxtFree(ResponseMessage);\r\n        }\r\n\r\n        errno = Result;\r\n        return -1;\r\n    }\r\n    else\r\n    {\r\n        *Response = ResponseMessage;\r\n        return 0;\r\n    }\r\n}\r\n\r\nint DeleteVirtualBridgeViaIoctl(const char* Name)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine deletes a virtual bridge device via the SIOCBRDELBR ioctl.\r\n\r\nArguments:\r\n\r\n    Name - Supplies the name to assign the new bridge.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int Sock;\r\n\r\n    Sock = socket(AF_UNIX, SOCK_STREAM, 0);\r\n    if (Sock < 0)\r\n    {\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = ioctl(Sock, SIOCBRDELBR, Name);\r\n\r\nErrorExit:\r\n    if (Sock > 0)\r\n    {\r\n        close(Sock);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint DeleteVirtualDeviceViaNetlink(const char* Name)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine removes a virtual device using the netlink RTM_DELLINK\r\n    message.\r\n\r\nArguments:\r\n\r\n    Name - Supplies the name of the device to delete.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    int InterfaceIndex;\r\n    struct nlmsghdr* NextResponseMessage;\r\n    int ReceiveResult;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\r\n    } RequestMessage;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct nlmsgerr Error __attribute__((aligned(NLMSG_ALIGNTO)));\r\n    } ResponseMessage;\r\n    int Result;\r\n    int Socket;\r\n\r\n    LxtCheckErrno(InterfaceIndex = GetNetworkInterfaceIndex(Name));\r\n\r\n    //\r\n    // Create and bind NETLINK socket.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    LxtCheckErrno(BindSocketForNetlink(Socket));\r\n\r\n    //\r\n    // Create a RTM_DELLINK request.\r\n    //\r\n\r\n    memset(&RequestMessage, 0, sizeof(RequestMessage));\r\n    RequestMessage.Header.nlmsg_len = sizeof(RequestMessage);\r\n    RequestMessage.Header.nlmsg_type = RTM_DELLINK;\r\n    RequestMessage.Header.nlmsg_seq = LXT_REQUEST_SEQUENCE;\r\n    RequestMessage.Header.nlmsg_flags = NLM_F_ACK | NLM_F_REQUEST;\r\n    RequestMessage.Message.ifi_index = InterfaceIndex;\r\n    LxtCheckErrno(sendto(Socket, &RequestMessage, sizeof(RequestMessage), 0, NULL, 0));\r\n\r\n    //\r\n    // Get the response.\r\n    //\r\n\r\n    memset(&ResponseMessage, 0, sizeof(ResponseMessage));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, &ResponseMessage, sizeof(ResponseMessage), 0, NULL, 0));\r\n\r\n    LxtCheckTrue(NLMSG_OK(&ResponseMessage.Header, ReceiveResult));\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_len, sizeof(ResponseMessage), \"%d\");\r\n\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_seq, LXT_REQUEST_SEQUENCE, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_pid, getpid(), \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Error.error, 0, \"%d\");\r\n    NextResponseMessage = NLMSG_NEXT(&ResponseMessage.Header, ReceiveResult);\r\n    LxtCheckTrue(NLMSG_OK(NextResponseMessage, ReceiveResult) == FALSE);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint CreateVirtualEthernetPairViaNetlink(const char* Name, const char* PeerName)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates a new virtual bridge using the netlink RTM_NEWLINK\r\n    message. On success the caller should free the Response buffer.\r\n\r\nArguments:\r\n\r\n    Name - Supplies the name to give the new virtual ethernet.\r\n\r\n    PeerName - Supplies the name to give the other end of the new virtual\r\n        ethernet pair.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct rtattr* Attribute;\r\n    char* DeviceData;\r\n    int DeviceDataSize;\r\n    struct nlmsgerr* ErrorMessage;\r\n    char* PeerDeviceData;\r\n    int PeerDeviceDataSize;\r\n    struct nlmsghdr* Response;\r\n    int Result;\r\n\r\n    Response = NULL;\r\n\r\n    //\r\n    // Determine the size of the peer device data part of the message.\r\n    //\r\n\r\n    CreateVirtualDeviceInfo(NULL, PeerName, NULL, 0, NULL, &PeerDeviceDataSize);\r\n\r\n    //\r\n    // Allocate a buffer to hold the peer device data.\r\n    //\r\n\r\n    PeerDeviceData = LxtAlloc(RTA_SPACE(PeerDeviceDataSize));\r\n    if (PeerDeviceData == NULL)\r\n    {\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Fill in the peer device data buffer.\r\n    //\r\n\r\n    Attribute = (struct rtattr*)PeerDeviceData;\r\n    Attribute->rta_len = RTA_LENGTH(PeerDeviceDataSize);\r\n    Attribute->rta_type = VETH_INFO_PEER;\r\n    CreateVirtualDeviceInfo(NULL, PeerName, NULL, 0, RTA_DATA(Attribute), &PeerDeviceDataSize);\r\n\r\n    //\r\n    // Determine the size of the device data part of the message.\r\n    //\r\n\r\n    CreateVirtualDeviceInfo(\"veth\", Name, PeerDeviceData, Attribute->rta_len, NULL, &DeviceDataSize);\r\n\r\n    //\r\n    // Allocate a buffer to hold the peer device data.\r\n    //\r\n\r\n    DeviceData = LxtAlloc(DeviceDataSize);\r\n    if (DeviceData == NULL)\r\n    {\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Fill in the peer device data buffer.\r\n    //\r\n\r\n    CreateVirtualDeviceInfo(\"veth\", Name, PeerDeviceData, Attribute->rta_len, DeviceData, &DeviceDataSize);\r\n\r\n    //\r\n    // Attempt to create the device.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualDeviceViaNetlink((NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | NLM_F_REQUEST), DeviceData, DeviceDataSize, &Response));\r\n\r\n    //\r\n    // Verify success.\r\n    //\r\n\r\n    ErrorMessage = NLMSG_DATA(Response);\r\n    LxtCheckEqual(ErrorMessage->error, 0, \"%d\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Response != NULL)\r\n    {\r\n        LxtFree(Response);\r\n    }\r\n\r\n    if (DeviceData != NULL)\r\n    {\r\n        LxtFree(DeviceData);\r\n    }\r\n\r\n    if (PeerDeviceData != NULL)\r\n    {\r\n        LxtFree(PeerDeviceData);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint GetNetworkInterfaceIndex(const char* Name)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine returns the network interface index of a device.\r\n\r\nArguments:\r\n\r\n    Name - Supplies the name of the device\r\n\r\nReturn Value:\r\n\r\n    Returns the index on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct ifreq InterfaceRequest;\r\n    int Result;\r\n    int Socket;\r\n\r\n    Socket = socket(AF_UNIX, SOCK_DGRAM, 0);\r\n    if (Socket < 0)\r\n    {\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    memset(&InterfaceRequest, 0, sizeof(InterfaceRequest));\r\n    strcpy(InterfaceRequest.ifr_name, Name);\r\n    Result = ioctl(Socket, SIOCGIFINDEX, &InterfaceRequest);\r\n    LxtClose(Socket);\r\n    if (Result < 0)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = InterfaceRequest.ifr_ifindex;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint EmptyDeviceErrorFromNetlink(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine sends a create message with no device described. This pattern\r\n    is used by tools like 'ip' to determine if the system supports virtual\r\n    network device creation.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct ifinfomsg DeviceData;\r\n    struct nlmsgerr* ErrorMessage;\r\n    struct nlmsghdr* Response;\r\n    int Result;\r\n\r\n    Response = NULL;\r\n    memset(&DeviceData, 0, sizeof(DeviceData));\r\n    LxtCheckResult(CreateVirtualDeviceViaNetlink((NLM_F_REQUEST | NLM_F_ACK), (const char*)&DeviceData, sizeof(DeviceData), &Response));\r\n\r\n    ErrorMessage = NLMSG_DATA(Response);\r\n    LxtCheckEqual(ErrorMessage->error, -ENODEV, \"%d\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Response != NULL)\r\n    {\r\n        LxtFree(Response);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint PhysicalDeviceNamespace1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine does basic network namespace sanity testing with a physical\r\n    device (assumes existence of eth0):\r\n        1) creates a new namespace\r\n        2) attempt to move eth0 to new namespace\r\n        3) close new namespace\r\n        4) verify device was returned to the root namespace\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int NewNetworkNamespaceFd;\r\n    int OriginalNetworkNamespaceFd;\r\n    int RootNetworkNamespaceFd;\r\n    int Response;\r\n    int Result;\r\n\r\n    NewNetworkNamespaceFd = 0;\r\n    OriginalNetworkNamespaceFd = 0;\r\n\r\n    //\r\n    // Open file descriptor of default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Open file descriptor of the root network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(RootNetworkNamespaceFd = open(\"/proc/1/ns/net\", 0));\r\n\r\n    //\r\n    // Switch to a new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Open file descriptor of the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(NewNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Switch to root network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(RootNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\n    //\r\n    // Try to move eth0 to the new network namespace.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceAttributeViaNetlink(\"eth0\", IFLA_NET_NS_FD, NewNetworkNamespaceFd, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n\r\n    //\r\n    // Verify that the device is gone.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(GetNetworkInterfaceIndex(\"eth0\"), ENODEV);\r\n\r\n    //\r\n    // Close the new network namespace. This should restore any physical\r\n    // devices to the root network namespace.\r\n    //\r\n\r\n    LxtCheckClose(NewNetworkNamespaceFd);\r\n\r\n    //\r\n    // Pause for a bit to allow background processing to occur.\r\n    //\r\n\r\n    sleep(1);\r\n\r\n    //\r\n    // Verify that the device is back.\r\n    //\r\n\r\n    LxtCheckErrno(GetNetworkInterfaceIndex(\"eth0\"));\r\n\r\n    //\r\n    // Restore back to default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\nErrorExit:\r\n    if (NewNetworkNamespaceFd > 0)\r\n    {\r\n        close(NewNetworkNamespaceFd);\r\n    }\r\n\r\n    if (RootNetworkNamespaceFd > 0)\r\n    {\r\n        close(RootNetworkNamespaceFd);\r\n    }\r\n\r\n    if (OriginalNetworkNamespaceFd > 0)\r\n    {\r\n        close(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SanityPermissionsCheck(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine does a simple operation to check that the current user has\r\n    appropriate permissions. This should be run as the first test to give a\r\n    quick error to the user that typically the entire test needs to be run\r\n    with sudo.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct ifinfomsg DeviceData;\r\n    struct nlmsgerr* ErrorMessage;\r\n    struct nlmsghdr* Response;\r\n    int Result;\r\n\r\n    Response = NULL;\r\n    memset(&DeviceData, 0, sizeof(DeviceData));\r\n    LxtCheckResult(CreateVirtualDeviceViaNetlink((NLM_F_REQUEST | NLM_F_ACK), (const char*)&DeviceData, sizeof(DeviceData), &Response));\r\n\r\n    ErrorMessage = NLMSG_DATA(Response);\r\n    if (ErrorMessage->error == -1)\r\n    {\r\n        LxtLogError(\"Make sure test is run with sudo!\");\r\n        Result = -1;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Response != NULL)\r\n    {\r\n        LxtFree(Response);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SetIpAddress(const char* InterfaceName, const struct in_addr* AddressIpv4, char PrefixLength)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine adds an IP address to a network interface.\r\n\r\nArguments:\r\n\r\n    InterfaceName - Supplies the interface name.\r\n\r\n    AddressIpv4 - Supplies the IP address.\r\n\r\n    PrefixLength - Supplies the prefix length of the address.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct nlmsgerr* ErrorMessage;\r\n    int InterfaceIndex;\r\n    int ReceiveResult;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct ifaddrmsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\r\n        struct\r\n        {\r\n            struct rtattr RtHeader __attribute__((aligned(RTA_ALIGNTO)));\r\n            struct in_addr AddressIpv4 __attribute__((aligned(RTA_ALIGNTO)));\r\n        } AddressAttribute;\r\n        struct\r\n        {\r\n            struct rtattr RtHeader __attribute__((aligned(RTA_ALIGNTO)));\r\n            struct in_addr AddressIpv4 __attribute__((aligned(RTA_ALIGNTO)));\r\n        } LocalAddressAttribute;\r\n    } RequestMessage;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct nlmsgerr Error __attribute__((aligned(NLMSG_ALIGNTO)));\r\n    } ResponseMessage;\r\n    int Result;\r\n    int Socket;\r\n\r\n    LxtCheckErrno(InterfaceIndex = GetNetworkInterfaceIndex(InterfaceName));\r\n\r\n    //\r\n    // Create and bind NETLINK socket.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    LxtCheckErrno(BindSocketForNetlink(Socket));\r\n\r\n    //\r\n    // Create a RTM_NEWLINK request.\r\n    //\r\n\r\n    memset(&RequestMessage, 0, sizeof(RequestMessage));\r\n    RequestMessage.Header.nlmsg_len = sizeof(RequestMessage);\r\n    RequestMessage.Header.nlmsg_type = RTM_NEWADDR;\r\n    RequestMessage.Header.nlmsg_seq = LXT_REQUEST_SEQUENCE;\r\n    RequestMessage.Header.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | NLM_F_REQUEST;\r\n\r\n    RequestMessage.Message.ifa_family = AF_INET;\r\n    RequestMessage.Message.ifa_prefixlen = PrefixLength;\r\n    RequestMessage.Message.ifa_index = InterfaceIndex;\r\n    RequestMessage.AddressAttribute.RtHeader.rta_len = RTA_LENGTH(sizeof(*AddressIpv4));\r\n\r\n    RequestMessage.AddressAttribute.RtHeader.rta_type = IFA_ADDRESS;\r\n    memcpy(&RequestMessage.AddressAttribute.AddressIpv4, AddressIpv4, sizeof(*AddressIpv4));\r\n\r\n    RequestMessage.LocalAddressAttribute.RtHeader.rta_len = RTA_LENGTH(sizeof(*AddressIpv4));\r\n\r\n    RequestMessage.LocalAddressAttribute.RtHeader.rta_type = IFA_LOCAL;\r\n    memcpy(&RequestMessage.LocalAddressAttribute.AddressIpv4, AddressIpv4, sizeof(*AddressIpv4));\r\n\r\n    LxtCheckErrno(sendto(Socket, &RequestMessage, sizeof(RequestMessage), 0, NULL, 0));\r\n\r\n    //\r\n    // Get the response.\r\n    //\r\n\r\n    memset(&ResponseMessage, 0, sizeof(ResponseMessage));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, &ResponseMessage, sizeof(ResponseMessage), 0, NULL, 0));\r\n\r\n    LxtCheckEqual(ReceiveResult, sizeof(ResponseMessage), \"%d\");\r\n    if (ResponseMessage.Header.nlmsg_len != ReceiveResult)\r\n    {\r\n        LxtCheckGreater(ResponseMessage.Header.nlmsg_len, ReceiveResult, \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_seq, LXT_REQUEST_SEQUENCE, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_pid, getpid(), \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Error.error, 0, \"%d\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SetRoute(const char* InterfaceName, const struct in_addr* AddressIpv4, int PrefixLength)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine adds a route to a network interface.\r\n\r\nArguments:\r\n\r\n    InterfaceName - Supplies the interface name.\r\n\r\n    AddressIpv4 - Supplies the IP address.\r\n\r\n    PrefixLength - Supplies the prefix length of AddressIpv4.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct nlmsgerr* ErrorMessage;\r\n    int InterfaceIndex;\r\n    int ReceiveResult;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct rtmsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\r\n        struct\r\n        {\r\n            struct rtattr RtHeader __attribute__((aligned(RTA_ALIGNTO)));\r\n            struct in_addr AddressIpv4 __attribute__((aligned(RTA_ALIGNTO)));\r\n        } DestAttribute;\r\n        struct\r\n        {\r\n            struct rtattr RtHeader __attribute__((aligned(RTA_ALIGNTO)));\r\n            int InterfaceIndex __attribute__((aligned(RTA_ALIGNTO)));\r\n        } IndexAttribute;\r\n    } RequestMessage;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct nlmsgerr Error __attribute__((aligned(NLMSG_ALIGNTO)));\r\n    } ResponseMessage;\r\n    int Result;\r\n    int Socket;\r\n\r\n    LxtCheckErrno(InterfaceIndex = GetNetworkInterfaceIndex(InterfaceName));\r\n\r\n    //\r\n    // Create and bind NETLINK socket.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    LxtCheckErrno(BindSocketForNetlink(Socket));\r\n\r\n    //\r\n    // Create a RTM_NEWROUTE request.\r\n    //\r\n\r\n    memset(&RequestMessage, 0, sizeof(RequestMessage));\r\n    RequestMessage.Header.nlmsg_len = sizeof(RequestMessage);\r\n    RequestMessage.Header.nlmsg_type = RTM_NEWROUTE;\r\n    RequestMessage.Header.nlmsg_seq = LXT_REQUEST_SEQUENCE;\r\n    RequestMessage.Header.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK | NLM_F_REQUEST;\r\n\r\n    RequestMessage.Message.rtm_family = AF_INET;\r\n    RequestMessage.Message.rtm_dst_len = PrefixLength;\r\n    RequestMessage.Message.rtm_table = RT_TABLE_MAIN;\r\n    RequestMessage.Message.rtm_protocol = RTPROT_BOOT;\r\n    RequestMessage.Message.rtm_scope = RT_SCOPE_LINK;\r\n    RequestMessage.Message.rtm_type = RTA_DST;\r\n\r\n    RequestMessage.DestAttribute.RtHeader.rta_len = RTA_LENGTH(sizeof(*AddressIpv4));\r\n\r\n    RequestMessage.DestAttribute.RtHeader.rta_type = RTA_DST;\r\n    memcpy(&RequestMessage.DestAttribute.AddressIpv4, AddressIpv4, sizeof(*AddressIpv4));\r\n\r\n    RequestMessage.IndexAttribute.RtHeader.rta_len = RTA_LENGTH(sizeof(int));\r\n\r\n    RequestMessage.IndexAttribute.RtHeader.rta_type = RTA_OIF;\r\n    RequestMessage.IndexAttribute.InterfaceIndex = InterfaceIndex;\r\n\r\n    LxtCheckErrno(sendto(Socket, &RequestMessage, sizeof(RequestMessage), 0, NULL, 0));\r\n\r\n    //\r\n    // Get the response.\r\n    //\r\n\r\n    memset(&ResponseMessage, 0, sizeof(ResponseMessage));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, &ResponseMessage, sizeof(ResponseMessage), 0, NULL, 0));\r\n\r\n    LxtCheckEqual(ReceiveResult, sizeof(ResponseMessage), \"%d\");\r\n    if (ResponseMessage.Header.nlmsg_len != ReceiveResult)\r\n    {\r\n        LxtCheckGreater(ResponseMessage.Header.nlmsg_len, ReceiveResult, \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_seq, LXT_REQUEST_SEQUENCE, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_pid, getpid(), \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Error.error, 0, \"%d\");\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SetVirtualDeviceAttributeViaNetlink(const char* Name, int AttributeType, int AttributeValue, int* Response)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine attempts to move a virtual device to the network namespace\r\n    referenced by the NamespaceFd.\r\n\r\nArguments:\r\n\r\n    Name - Supplies the name of the virtual device.\r\n\r\n    AttributeType - Supplies the attribute type.\r\n\r\n    AttributeValue - Supplies the attribute value.\r\n\r\n    NetworkNamespaceFd - Supplies the file descriptor of a network namespace.\r\n\r\n    Response - Supplies a pointer to receive the response errno.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct nlmsgerr* ErrorMessage;\r\n    int InterfaceIndex;\r\n    int ReceiveResult;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\r\n        struct\r\n        {\r\n            struct rtattr RtHeader __attribute__((aligned(RTA_ALIGNTO)));\r\n            int RtValue __attribute__((aligned(RTA_ALIGNTO)));\r\n        } Attribute;\r\n    } RequestMessage;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct nlmsgerr Error __attribute__((aligned(NLMSG_ALIGNTO)));\r\n    } ResponseMessage;\r\n    int Result;\r\n    int Socket;\r\n\r\n    LxtCheckErrno(InterfaceIndex = GetNetworkInterfaceIndex(Name));\r\n\r\n    //\r\n    // Create and bind NETLINK socket.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    LxtCheckErrno(BindSocketForNetlink(Socket));\r\n\r\n    //\r\n    // Create a RTM_NEWLINK update request.\r\n    //\r\n\r\n    memset(&RequestMessage, 0, sizeof(RequestMessage));\r\n    RequestMessage.Header.nlmsg_len = sizeof(RequestMessage);\r\n    RequestMessage.Header.nlmsg_type = RTM_NEWLINK;\r\n    RequestMessage.Header.nlmsg_seq = LXT_REQUEST_SEQUENCE;\r\n    RequestMessage.Header.nlmsg_flags = NLM_F_ACK | NLM_F_REQUEST;\r\n    RequestMessage.Message.ifi_index = InterfaceIndex;\r\n    RequestMessage.Attribute.RtHeader.rta_len = RTA_LENGTH(sizeof(int));\r\n    RequestMessage.Attribute.RtHeader.rta_type = AttributeType;\r\n    RequestMessage.Attribute.RtValue = AttributeValue;\r\n    LxtCheckErrno(sendto(Socket, &RequestMessage, sizeof(RequestMessage), 0, NULL, 0));\r\n\r\n    //\r\n    // Get the response.\r\n    //\r\n\r\n    memset(&ResponseMessage, 0, sizeof(ResponseMessage));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, &ResponseMessage, sizeof(ResponseMessage), 0, NULL, 0));\r\n\r\n    LxtCheckEqual(ReceiveResult, sizeof(ResponseMessage), \"%d\");\r\n    if (ResponseMessage.Header.nlmsg_len != ReceiveResult)\r\n    {\r\n        LxtCheckGreater(ResponseMessage.Header.nlmsg_len, ReceiveResult, \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_seq, LXT_REQUEST_SEQUENCE, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_pid, getpid(), \"%d\");\r\n    *Response = ResponseMessage.Error.error;\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint SetVirtualDeviceFlagViaNetlink(const char* Name, int Flag, bool IsFlagEnabled, int* Response)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine toggles network interface flags (e.g. up/down).\r\n\r\nArguments:\r\n\r\n    Name - Supplies the name of the virtual device.\r\n\r\n    Flag - Supplies the flag to toggle.\r\n\r\n    IsFlagEnabled - Supplies the state to which the flag should be set.\r\n\r\n    Response - Supplies a pointer to receive the response errno.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    struct nlmsgerr* ErrorMessage;\r\n    int InterfaceIndex;\r\n    int ReceiveResult;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\r\n    } RequestMessage;\r\n    struct\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct nlmsgerr Error __attribute__((aligned(NLMSG_ALIGNTO)));\r\n    } ResponseMessage;\r\n    int Result;\r\n    int Socket;\r\n\r\n    LxtCheckErrno(InterfaceIndex = GetNetworkInterfaceIndex(Name));\r\n\r\n    //\r\n    // Create and bind NETLINK socket.\r\n    //\r\n\r\n    LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n    LxtCheckErrno(BindSocketForNetlink(Socket));\r\n\r\n    //\r\n    // Create a RTM_NEWLINK update request.\r\n    //\r\n\r\n    memset(&RequestMessage, 0, sizeof(RequestMessage));\r\n    RequestMessage.Header.nlmsg_len = sizeof(RequestMessage);\r\n    RequestMessage.Header.nlmsg_type = RTM_NEWLINK;\r\n    RequestMessage.Header.nlmsg_seq = LXT_REQUEST_SEQUENCE;\r\n    RequestMessage.Header.nlmsg_flags = NLM_F_ACK | NLM_F_REQUEST;\r\n    RequestMessage.Message.ifi_index = InterfaceIndex;\r\n    RequestMessage.Message.ifi_change = Flag;\r\n    if (IsFlagEnabled)\r\n    {\r\n        RequestMessage.Message.ifi_flags = Flag;\r\n    }\r\n\r\n    LxtCheckErrno(sendto(Socket, &RequestMessage, sizeof(RequestMessage), 0, NULL, 0));\r\n\r\n    //\r\n    // Get the response.\r\n    //\r\n\r\n    memset(&ResponseMessage, 0, sizeof(ResponseMessage));\r\n    LxtCheckErrno(ReceiveResult = recvfrom(Socket, &ResponseMessage, sizeof(ResponseMessage), 0, NULL, 0));\r\n\r\n    LxtCheckEqual(ReceiveResult, sizeof(ResponseMessage), \"%d\");\r\n    if (ResponseMessage.Header.nlmsg_len != ReceiveResult)\r\n    {\r\n        LxtCheckGreater(ResponseMessage.Header.nlmsg_len, ReceiveResult, \"%d\");\r\n    }\r\n\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_flags, 0, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_seq, LXT_REQUEST_SEQUENCE, \"%d\");\r\n    LxtCheckEqual(ResponseMessage.Header.nlmsg_pid, getpid(), \"%d\");\r\n    *Response = ResponseMessage.Error.error;\r\n    Result = 0;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VerifyRouteLinkDoesNotExist(\r\n    const char* InterfaceName,\r\n    /* optional */ int* SocketToUse)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine attempts to retrieve the link information for a network\r\n    interface, expecting that interface not to exist.\r\n\r\nArguments:\r\n\r\n    InterfaceName - Supplies the name of the network interface.\r\n\r\n    SocketToUse - Supplies an optional socket bound to NETLINK to use for the\r\n        query.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    bool CloseSocket;\r\n    struct nlmsgerr* ErrorMessage;\r\n    struct nlmsghdr* Header;\r\n    int InterfaceIndex;\r\n    size_t InterfaceNameLength;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    int Result;\r\n    int Socket;\r\n\r\n    struct _RequestMessage\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\r\n        struct\r\n        {\r\n            struct rtattr RtHeader __attribute__((aligned(RTA_ALIGNTO)));\r\n            char InterfaceName[32] __attribute__((aligned(RTA_ALIGNTO)));\r\n        } Attribute;\r\n    } RequestMessage;\r\n\r\n    if (SocketToUse != NULL)\r\n    {\r\n        CloseSocket = false;\r\n        Socket = *SocketToUse;\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Create and bind socket. Create a RTM_GETLINK request.\r\n        //\r\n\r\n        LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n        CloseSocket = true;\r\n        LxtCheckErrno(BindSocketForNetlink(Socket));\r\n    }\r\n\r\n    memset(&RequestMessage, 0, sizeof(RequestMessage));\r\n    RequestMessage.Header.nlmsg_type = RTM_GETLINK;\r\n    RequestMessage.Header.nlmsg_seq = LXT_REQUEST_SEQUENCE;\r\n    ;\r\n    RequestMessage.Message.ifi_family = AF_NETLINK;\r\n    RequestMessage.Header.nlmsg_flags = NLM_F_REQUEST;\r\n    InterfaceNameLength = strlen(InterfaceName) + 1;\r\n    LxtCheckGreaterOrEqual(sizeof(RequestMessage.Attribute.InterfaceName), InterfaceNameLength, \"%lu\");\r\n\r\n    RequestMessage.Attribute.RtHeader.rta_len = RTA_LENGTH(InterfaceNameLength);\r\n    RequestMessage.Attribute.RtHeader.rta_type = IFLA_IFNAME;\r\n    memcpy(RequestMessage.Attribute.InterfaceName, InterfaceName, InterfaceNameLength);\r\n\r\n    RequestMessage.Header.nlmsg_len = offsetof(struct _RequestMessage, Attribute.InterfaceName) + InterfaceNameLength;\r\n\r\n    LxtCheckErrno(sendto(Socket, &RequestMessage, RequestMessage.Header.nlmsg_len, 0, NULL, 0));\r\n\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, NLMSG_ERROR, \"%d\");\r\n    LxtCheckGreater((Header->nlmsg_len + 1), NLMSG_PAYLOAD(Header, 0), \"%d\");\r\n\r\n    ErrorMessage = NLMSG_DATA(Header);\r\n    LxtCheckEqual(ErrorMessage->error, -ENODEV, \"%d\");\r\n\r\nErrorExit:\r\n    if (CloseSocket)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VerifyRouteLinkExists(\r\n    const char* InterfaceName,\r\n    /* optional */ int* SocketToUse)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine retrieves the link information for a network interface.\r\n\r\nArguments:\r\n\r\n    InterfaceName - Supplies the name of the network interface.\r\n\r\n    SocketToUse - Supplies an optional socket bound to NETLINK to use for the\r\n        query.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    socklen_t AddressLength;\r\n    bool CloseSocket;\r\n    struct nlmsghdr* Header;\r\n    struct ifinfomsg* InterfaceInfo;\r\n    int InterfaceIndex;\r\n    size_t InterfaceNameLength;\r\n    char ReceiveBuffer[5000];\r\n    int ReceiveResult;\r\n    int Result;\r\n    int Socket;\r\n\r\n    struct _RequestMessage\r\n    {\r\n        struct nlmsghdr Header;\r\n        struct ifinfomsg Message __attribute__((aligned(NLMSG_ALIGNTO)));\r\n        struct\r\n        {\r\n            struct rtattr RtHeader __attribute__((aligned(RTA_ALIGNTO)));\r\n            char InterfaceName[32] __attribute__((aligned(RTA_ALIGNTO)));\r\n        } Attribute;\r\n    } RequestMessage;\r\n\r\n    if (SocketToUse != NULL)\r\n    {\r\n        CloseSocket = false;\r\n        Socket = *SocketToUse;\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Create and bind socket. Create a RTM_GETLINK request.\r\n        //\r\n\r\n        LxtCheckErrno(Socket = socket(AF_NETLINK, SOCK_RAW, 0));\r\n        CloseSocket = true;\r\n        LxtCheckErrno(BindSocketForNetlink(Socket));\r\n    }\r\n\r\n    memset(&RequestMessage, 0, sizeof(RequestMessage));\r\n    RequestMessage.Header.nlmsg_type = RTM_GETLINK;\r\n    RequestMessage.Header.nlmsg_seq = LXT_REQUEST_SEQUENCE;\r\n    ;\r\n    RequestMessage.Message.ifi_family = AF_NETLINK;\r\n    RequestMessage.Header.nlmsg_flags = NLM_F_REQUEST;\r\n    InterfaceNameLength = strlen(InterfaceName) + 1;\r\n    LxtCheckGreaterOrEqual(sizeof(RequestMessage.Attribute.InterfaceName), InterfaceNameLength, \"%lu\");\r\n\r\n    RequestMessage.Attribute.RtHeader.rta_len = RTA_LENGTH(InterfaceNameLength);\r\n    RequestMessage.Attribute.RtHeader.rta_type = IFLA_IFNAME;\r\n    memcpy(RequestMessage.Attribute.InterfaceName, InterfaceName, InterfaceNameLength);\r\n\r\n    RequestMessage.Header.nlmsg_len = offsetof(struct _RequestMessage, Attribute.InterfaceName) + InterfaceNameLength;\r\n\r\n    LxtCheckErrno(sendto(Socket, &RequestMessage, RequestMessage.Header.nlmsg_len, 0, NULL, 0));\r\n\r\n    LxtCheckErrno(ReceiveResult = recv(Socket, ReceiveBuffer, sizeof(ReceiveBuffer), 0));\r\n\r\n    Header = (struct nlmsghdr*)ReceiveBuffer;\r\n    LxtCheckTrue(NLMSG_OK(Header, ReceiveResult));\r\n    LxtCheckEqual(Header->nlmsg_type, RTM_NEWLINK, \"%d\");\r\n    LxtCheckGreaterOrEqual((Header->nlmsg_len + 1), NLMSG_PAYLOAD(Header, sizeof(*InterfaceInfo)), \"%d\");\r\n\r\n    InterfaceInfo = NLMSG_DATA(Header);\r\n    if (SocketToUse == NULL)\r\n    {\r\n        LxtCheckErrno(InterfaceIndex = GetNetworkInterfaceIndex(InterfaceName));\r\n        LxtCheckEqual(InterfaceIndex, InterfaceInfo->ifi_index, \"%d\");\r\n    }\r\n\r\nErrorExit:\r\n    if (CloseSocket)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualBridgeAutoName(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates and deletes new bridges, allowing the system to\r\n    assign them default \"bridgexx\" names.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDeviceOne;\r\n    bool DeleteDeviceTwo;\r\n    int Result;\r\n\r\n    DeleteDeviceOne = false;\r\n    DeleteDeviceTwo = false;\r\n\r\n    LxtCheckResult(CreateVirtualBridgeViaNetlink(\"\"));\r\n    DeleteDeviceOne = true;\r\n    LxtCheckErrno(GetNetworkInterfaceIndex(\"bridge0\"));\r\n    LxtCheckResult(CreateVirtualBridgeViaNetlink(\"\"));\r\n    DeleteDeviceTwo = true;\r\n    LxtCheckErrno(GetNetworkInterfaceIndex(\"bridge1\"));\r\n\r\nErrorExit:\r\n    if (DeleteDeviceTwo)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"bridge1\");\r\n    }\r\n\r\n    if (DeleteDeviceOne)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"bridge0\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualBridgeFromIoctl1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates and deletes a new bridge using UNIX socket ioctls.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(CreateVirtualBridgeViaIoctl(\"testbridge\"));\r\n    LxtCheckErrno(GetNetworkInterfaceIndex(\"testbridge\"));\r\n    LxtCheckResult(DeleteVirtualBridgeViaIoctl(\"testbridge\"));\r\n    LxtCheckErrnoFailure(GetNetworkInterfaceIndex(\"testbridge\"), ENODEV);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint VirtualBridgeFromNetlink1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates and deletes a new bridge using netlink messages.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDevice;\r\n    int Result;\r\n\r\n    DeleteDevice = false;\r\n\r\n    LxtCheckResult(CreateVirtualBridgeViaNetlink(\"testbridge\"));\r\n    DeleteDevice = true;\r\n    LxtCheckErrno(GetNetworkInterfaceIndex(\"testbridge\"));\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"testbridge\"));\r\n    DeleteDevice = false;\r\n    LxtCheckErrnoFailure(GetNetworkInterfaceIndex(\"testbridge\"), ENODEV);\r\n\r\nErrorExit:\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"testbridge\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualBridgeNamespace1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine does basic network namespace sanity testing with a bridge:\r\n        1) creates a new virtual bridge device\r\n        1) creates a new namespace\r\n        3) try to move the bridge into the other namespace - should fail.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDevice;\r\n    int NewNetworkNamespaceFd;\r\n    int OriginalNetworkNamespaceFd;\r\n    int Response;\r\n    int Result;\r\n\r\n    DeleteDevice = false;\r\n    NewNetworkNamespaceFd = 0;\r\n    OriginalNetworkNamespaceFd = 0;\r\n\r\n    //\r\n    // Open file descriptor of default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Create a new virtual bridge.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualBridgeViaNetlink(\"testbridge\"));\r\n    DeleteDevice = true;\r\n\r\n    //\r\n    // Switch to a new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Open file descriptor of the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(NewNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Switch back to original network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\n    //\r\n    // Try to move the bridge into the new namespace. This should fail as\r\n    // bridges are not allowed to move between namespaces.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceAttributeViaNetlink(\"testbridge\", IFLA_NET_NS_FD, NewNetworkNamespaceFd, &Response));\r\n\r\n    LxtCheckEqual(Response, -EINVAL, \"%d\");\r\n\r\n    //\r\n    // Delete the device.\r\n    //\r\n\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"testbridge\"));\r\n    DeleteDevice = false;\r\n\r\nErrorExit:\r\n    if (NewNetworkNamespaceFd > 0)\r\n    {\r\n        close(NewNetworkNamespaceFd);\r\n    }\r\n\r\n    if (OriginalNetworkNamespaceFd > 0)\r\n    {\r\n        close(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"testbridge\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairFromNetlink1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates and deletes a new virtual ethernet pair using netlink\r\n    messages. The delete is performed on the primary device.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDevice;\r\n    int Result;\r\n\r\n    DeleteDevice = false;\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth_tst0\", \"veth_tst1\"));\r\n    LxtCheckErrno(GetNetworkInterfaceIndex(\"veth_tst0\"));\r\n    LxtCheckErrno(GetNetworkInterfaceIndex(\"veth_tst1\"));\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"veth_tst0\"));\r\n    LxtCheckErrnoFailure(GetNetworkInterfaceIndex(\"veth_tst0\"), ENODEV);\r\n    LxtCheckErrnoFailure(GetNetworkInterfaceIndex(\"veth_tst1\"), ENODEV);\r\n\r\nErrorExit:\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth_tst0\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairFromNetlink2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates and deletes a new virtual ethernet pair using netlink\r\n    messages. The delete is performed on the peer device.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDevice;\r\n    int Result;\r\n\r\n    DeleteDevice = false;\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth_tst0\", \"veth_tst1\"));\r\n    DeleteDevice = true;\r\n    LxtCheckErrno(GetNetworkInterfaceIndex(\"veth_tst0\"));\r\n    LxtCheckErrno(GetNetworkInterfaceIndex(\"veth_tst1\"));\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"veth_tst1\"));\r\n    DeleteDevice = false;\r\n    LxtCheckErrnoFailure(GetNetworkInterfaceIndex(\"veth_tst0\"), ENODEV);\r\n    LxtCheckErrnoFailure(GetNetworkInterfaceIndex(\"veth_tst1\"), ENODEV);\r\n\r\nErrorExit:\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth_tst1\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairNamespace1(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine does basic network namespace sanity testing with a virtual\r\n    ethernet pair:\r\n        1) creates a new virtual ethernet pair\r\n        2) creates a new namespace\r\n        3) try to move one end of the pair into the other namespace\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDevice;\r\n    int NewNetworkNamespaceFd;\r\n    int OriginalNetworkNamespaceFd;\r\n    int Response;\r\n    int Result;\r\n\r\n    DeleteDevice = false;\r\n    NewNetworkNamespaceFd = 0;\r\n    OriginalNetworkNamespaceFd = 0;\r\n\r\n    //\r\n    // Open file descriptor of default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Create a new virtual ethernet pair.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth0\", \"veth1\"));\r\n    DeleteDevice = true;\r\n\r\n    //\r\n    // Switch to a new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Open file descriptor of the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(NewNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Switch back to original network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\n    //\r\n    // Try to move one half of the pair into the new namespace.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceAttributeViaNetlink(\"veth1\", IFLA_NET_NS_FD, NewNetworkNamespaceFd, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n\r\n    //\r\n    // Delete the device.\r\n    //\r\n\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"veth0\"));\r\n    DeleteDevice = false;\r\n\r\nErrorExit:\r\n    if (NewNetworkNamespaceFd > 0)\r\n    {\r\n        close(NewNetworkNamespaceFd);\r\n    }\r\n\r\n    if (OriginalNetworkNamespaceFd > 0)\r\n    {\r\n        (void)setns(OriginalNetworkNamespaceFd, CLONE_NEWNET);\r\n        close(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth0\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairNamespace2(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine does basic network namespace sanity testing with a virtual\r\n    ethernet pair:\r\n        1) create a virtual ethernet pair.\r\n        2) create a new namespace\r\n        3) move one end of the pair into the new namespace.\r\n        4) create another virtual ethernet pair using the same name as that of\r\n           the moved end.\r\n        5) try to move the same-named end again.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteFirstDevice;\r\n    bool DeleteSecondDevice;\r\n    int NewNetworkNamespaceFd;\r\n    int OriginalNetworkNamespaceFd;\r\n    int Response;\r\n    int Result;\r\n\r\n    DeleteFirstDevice = false;\r\n    DeleteSecondDevice = false;\r\n    NewNetworkNamespaceFd = 0;\r\n    OriginalNetworkNamespaceFd = 0;\r\n\r\n    //\r\n    // Open file descriptor of default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Create a new virtual ethernet pair.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth0\", \"veth1\"));\r\n    DeleteFirstDevice = true;\r\n\r\n    //\r\n    // Switch to a new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Open file descriptor of the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(NewNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Switch back to original network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\n    //\r\n    // Try to move one half of the pair into the new namespace.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceAttributeViaNetlink(\"veth1\", IFLA_NET_NS_FD, NewNetworkNamespaceFd, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n\r\n    //\r\n    // Create a new virtual ethernet pair, re-using the moved name.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth2\", \"veth1\"));\r\n    DeleteSecondDevice = true;\r\n\r\n    //\r\n    // Try to move it again, expecting failure.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n    LxtCheckResult(SetVirtualDeviceAttributeViaNetlink(\"veth1\", IFLA_NET_NS_FD, NewNetworkNamespaceFd, &Response));\r\n\r\n    LxtCheckEqual(Response, -EEXIST, \"%d\");\r\n\r\n    //\r\n    // Cleanup.\r\n    //\r\n\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"veth0\"));\r\n    DeleteFirstDevice = false;\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"veth2\"));\r\n    DeleteSecondDevice = false;\r\n\r\nErrorExit:\r\n    if (NewNetworkNamespaceFd > 0)\r\n    {\r\n        close(NewNetworkNamespaceFd);\r\n    }\r\n\r\n    if (OriginalNetworkNamespaceFd > 0)\r\n    {\r\n        (void)setns(OriginalNetworkNamespaceFd, CLONE_NEWNET);\r\n        close(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    if (DeleteFirstDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth0\");\r\n    }\r\n\r\n    if (DeleteSecondDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth2\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairNamespace3(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine does basic network namespace sanity testing with a virtual\r\n    ethernet pair:\r\n        1) creates a new virtual ethernet pair\r\n        2) creates a new namespace\r\n        3) move one end of the pair into the other namespace\r\n        4) close the new namespace\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDevice;\r\n    int NewNetworkNamespaceFd;\r\n    int OriginalNetworkNamespaceFd;\r\n    int Response;\r\n    int Result;\r\n\r\n    DeleteDevice = false;\r\n    NewNetworkNamespaceFd = 0;\r\n    OriginalNetworkNamespaceFd = 0;\r\n\r\n    //\r\n    // Open file descriptor of default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Create a new virtual ethernet pair.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth0\", \"veth1\"));\r\n    DeleteDevice = true;\r\n\r\n    //\r\n    // Switch to a new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Open file descriptor of the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(NewNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Switch back to original network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\n    //\r\n    // Try to move one half of the pair into the new namespace.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceAttributeViaNetlink(\"veth1\", IFLA_NET_NS_FD, NewNetworkNamespaceFd, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n\r\n    //\r\n    // Close the new namespace.\r\n    //\r\n\r\n    LxtCheckClose(NewNetworkNamespaceFd);\r\n\r\n    //\r\n    // Both endpoints should have been deleted when the namespace was closed.\r\n    // Delay a bit to let the state settle.\r\n    //\r\n\r\n    sleep(1);\r\n    LxtCheckErrnoFailure(GetNetworkInterfaceIndex(\"veth0\"), ENODEV);\r\n    DeleteDevice = false;\r\n\r\nErrorExit:\r\n    if (NewNetworkNamespaceFd > 0)\r\n    {\r\n        close(NewNetworkNamespaceFd);\r\n    }\r\n\r\n    if (OriginalNetworkNamespaceFd > 0)\r\n    {\r\n        (void)setns(OriginalNetworkNamespaceFd, CLONE_NEWNET);\r\n        close(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth0\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairNamespace4(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine does simple validation on network interface link information\r\n    when creating virtual ethernet adapters and moving them between namespaces.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDevice;\r\n    int NewNetworkNamespaceFd;\r\n    int OriginalNetworkNamespaceFd;\r\n    int Response;\r\n    int Result;\r\n\r\n    DeleteDevice = false;\r\n    NewNetworkNamespaceFd = 0;\r\n    OriginalNetworkNamespaceFd = 0;\r\n\r\n    //\r\n    // Open file descriptor of default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Create a new virtual ethernet pair.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth0\", \"veth1\"));\r\n    DeleteDevice = true;\r\n\r\n    //\r\n    // Switch to a new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Open file descriptor of the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(NewNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Switch back to original network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\n    //\r\n    // Check the route link entries.\r\n    //\r\n\r\n    LxtCheckResult(VerifyRouteLinkExists(\"veth0\", NULL));\r\n    LxtCheckResult(VerifyRouteLinkExists(\"veth1\", NULL));\r\n\r\n    //\r\n    // Try to move one half of the pair into the new namespace.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceAttributeViaNetlink(\"veth1\", IFLA_NET_NS_FD, NewNetworkNamespaceFd, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n\r\n    //\r\n    // Check the route link entries.\r\n    //\r\n\r\n    LxtCheckErrno(setns(NewNetworkNamespaceFd, CLONE_NEWNET));\r\n    LxtCheckResult(VerifyRouteLinkExists(\"veth1\", NULL));\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n    LxtCheckResult(VerifyRouteLinkExists(\"veth0\", NULL));\r\n\r\n    //\r\n    // Close the new namespace, and wait a second for state to settle.\r\n    //\r\n\r\n    LxtCheckClose(NewNetworkNamespaceFd);\r\n    sleep(1);\r\n\r\n    //\r\n    // The namespace deletion should have removed the devices.\r\n    //\r\n\r\n    LxtCheckResult(VerifyRouteLinkDoesNotExist(\"veth0\", NULL));\r\n    DeleteDevice = false;\r\n    LxtCheckResult(VerifyRouteLinkDoesNotExist(\"veth1\", NULL));\r\n\r\nErrorExit:\r\n    if (NewNetworkNamespaceFd > 0)\r\n    {\r\n        close(NewNetworkNamespaceFd);\r\n    }\r\n\r\n    if (OriginalNetworkNamespaceFd > 0)\r\n    {\r\n        (void)setns(OriginalNetworkNamespaceFd, CLONE_NEWNET);\r\n        close(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth0\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairNamespace5(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine does simple validation of socket creation being tied to the\r\n    network namespace.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDevice;\r\n    int NewNetworkNamespaceFd;\r\n    int OriginalNetworkNamespaceFd;\r\n    int Response;\r\n    int Result;\r\n    int SocketNewNs;\r\n\r\n    DeleteDevice = false;\r\n    NewNetworkNamespaceFd = 0;\r\n    OriginalNetworkNamespaceFd = 0;\r\n\r\n    //\r\n    // Open file descriptor of default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Create a new virtual ethernet pair.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth0\", \"veth1\"));\r\n    DeleteDevice = true;\r\n\r\n    //\r\n    // Switch to a new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Create a socket while in the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(SocketNewNs = socket(AF_NETLINK, SOCK_RAW, 0));\r\n\r\n    //\r\n    // Open file descriptor of the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(NewNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Switch back to original network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\n    //\r\n    // Bind the socket created in the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(BindSocketForNetlink(SocketNewNs));\r\n\r\n    //\r\n    // Check the route link entries.\r\n    //\r\n\r\n    LxtCheckResult(VerifyRouteLinkExists(\"veth0\", NULL));\r\n    LxtCheckResult(VerifyRouteLinkExists(\"veth1\", NULL));\r\n\r\n    //\r\n    // Check the non-existence of the entries with the other socket.\r\n    //\r\n\r\n    LxtCheckResult(VerifyRouteLinkDoesNotExist(\"veth0\", &SocketNewNs));\r\n    LxtCheckResult(VerifyRouteLinkDoesNotExist(\"veth1\", &SocketNewNs));\r\n\r\n    //\r\n    // Try to move one half of the pair into the new namespace.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceAttributeViaNetlink(\"veth1\", IFLA_NET_NS_FD, NewNetworkNamespaceFd, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n\r\n    //\r\n    // Check the route link entries.\r\n    //\r\n\r\n    LxtCheckResult(VerifyRouteLinkExists(\"veth0\", NULL));\r\n    LxtCheckResult(VerifyRouteLinkDoesNotExist(\"veth0\", &SocketNewNs));\r\n    LxtCheckResult(VerifyRouteLinkDoesNotExist(\"veth1\", NULL));\r\n    LxtCheckResult(VerifyRouteLinkExists(\"veth1\", &SocketNewNs));\r\n\r\n    //\r\n    // Close the new namespace and the related socket, and wait a second for\r\n    // state to settle.\r\n    //\r\n\r\n    LxtCheckClose(SocketNewNs);\r\n    SocketNewNs = 0;\r\n    LxtCheckClose(NewNetworkNamespaceFd);\r\n    sleep(1);\r\n\r\n    //\r\n    // The namespace deletion should have removed the devices.\r\n    //\r\n\r\n    LxtCheckResult(VerifyRouteLinkDoesNotExist(\"veth0\", NULL));\r\n    DeleteDevice = false;\r\n    LxtCheckResult(VerifyRouteLinkDoesNotExist(\"veth1\", NULL));\r\n\r\nErrorExit:\r\n    if (SocketNewNs > 0)\r\n    {\r\n        close(SocketNewNs);\r\n    }\r\n\r\n    if (NewNetworkNamespaceFd > 0)\r\n    {\r\n        close(NewNetworkNamespaceFd);\r\n    }\r\n\r\n    if (OriginalNetworkNamespaceFd > 0)\r\n    {\r\n        (void)setns(OriginalNetworkNamespaceFd, CLONE_NEWNET);\r\n        close(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth0\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairConfigure(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine creates a virtual ethernet pair and attempts to bring them up\r\n    and assign them static IP addresses.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    bool DeleteDevice;\r\n    struct in_addr AddressIpv4;\r\n    int Response;\r\n    int Result;\r\n\r\n    DeleteDevice = false;\r\n\r\n    //\r\n    // Create a new virtual ethernet pair.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth0\", \"veth1\"));\r\n    DeleteDevice = true;\r\n\r\n    //\r\n    // Bring the interfaces up and assign static IP addresses.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceFlagViaNetlink(\"veth0\", IFF_UP, true, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n    inet_aton(LXT_IP_ADDRESS1, &AddressIpv4);\r\n    LxtCheckResult(SetIpAddress(\"veth0\", &AddressIpv4, 32));\r\n\r\n    LxtCheckResult(SetVirtualDeviceFlagViaNetlink(\"veth1\", IFF_UP, true, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n    inet_aton(LXT_IP_ADDRESS2, &AddressIpv4);\r\n    LxtCheckResult(SetIpAddress(\"veth1\", &AddressIpv4, 32));\r\n\r\n    //\r\n    // Try to delete the device.\r\n    //\r\n\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"veth0\"));\r\n    DeleteDevice = false;\r\n\r\nErrorExit:\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth0\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairData(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_in AddressIpv4 = {};\r\n    socklen_t AddressLength;\r\n    ssize_t BytesReceived;\r\n    ssize_t BytesSent;\r\n    bool DeleteDevice;\r\n    struct sockaddr_in FromAddressIpv4;\r\n    ;\r\n    char RecvBuffer[10] = {0};\r\n    int Response;\r\n    int Result;\r\n    char SendBuffer[10] = {\"123456789\"};\r\n    int Socket;\r\n    int Socket2;\r\n\r\n    DeleteDevice = false;\r\n    Socket = -1;\r\n    Socket2 = -1;\r\n\r\n    //\r\n    // Make sure the loopback adapter is up.\r\n    //\r\n\r\n    (void)SetVirtualDeviceFlagViaNetlink(\"lo\", IFF_UP, true, &Response);\r\n\r\n    //\r\n    // Create a new virtual ethernet pair.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth0\", \"veth1\"));\r\n    DeleteDevice = true;\r\n\r\n    //\r\n    // Bring the interfaces up and assign static IP addresses.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceFlagViaNetlink(\"veth0\", IFF_UP, true, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n    AddressIpv4.sin_family = AF_INET;\r\n    inet_aton(LXT_IP_ADDRESS1, &AddressIpv4.sin_addr);\r\n    LxtCheckResult(SetIpAddress(\"veth0\", &AddressIpv4.sin_addr, 32));\r\n    LxtCheckErrno((Socket = socket(AF_INET, SOCK_DGRAM, 0)));\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&AddressIpv4, sizeof(AddressIpv4)));\r\n\r\n    LxtCheckResult(SetVirtualDeviceFlagViaNetlink(\"veth1\", IFF_UP, true, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n    LxtCheckResult(SetRoute(\"veth1\", &AddressIpv4.sin_addr, 32));\r\n    inet_aton(LXT_IP_ADDRESS2, &AddressIpv4.sin_addr);\r\n    LxtCheckResult(SetIpAddress(\"veth1\", &AddressIpv4.sin_addr, 32));\r\n    LxtCheckErrno((Socket2 = socket(AF_INET, SOCK_DGRAM, 0)));\r\n    LxtCheckErrno(bind(Socket2, (struct sockaddr*)&AddressIpv4, sizeof(AddressIpv4)));\r\n\r\n    //\r\n    // Send a packet between the devices.\r\n    //\r\n\r\n    AddressLength = sizeof(AddressIpv4);\r\n    LxtCheckErrno(getsockname(Socket2, (struct sockaddr*)&AddressIpv4, &AddressLength));\r\n\r\n    LxtCheckEqual(AddressLength, sizeof(AddressIpv4), \"%lu\");\r\n    LxtCheckResult(SetRoute(\"veth0\", &AddressIpv4.sin_addr, 32));\r\n    LxtLogInfo(\"AddressIpv4.sin_family = %d\", AddressIpv4.sin_family);\r\n    LxtLogInfo(\"AddressIpv4.sin_port = %d\", AddressIpv4.sin_port);\r\n    LxtLogInfo(\"AddressIpv4.sin_addr = %08x\", AddressIpv4.sin_addr.s_addr);\r\n    LxtCheckErrno((BytesSent = sendto(Socket, SendBuffer, sizeof(SendBuffer), 0, (struct sockaddr*)&AddressIpv4, sizeof(AddressIpv4))));\r\n\r\n    LxtCheckEqual(BytesSent, sizeof(SendBuffer), \"%lu\");\r\n    AddressLength = sizeof(FromAddressIpv4);\r\n    LxtCheckErrno((BytesReceived = recvfrom(Socket2, RecvBuffer, sizeof(RecvBuffer), 0, (struct sockaddr*)&FromAddressIpv4, &AddressLength)));\r\n\r\n    LxtCheckEqual(BytesReceived, sizeof(SendBuffer), \"%lu\");\r\n    LxtCheckMemoryEqual(RecvBuffer, SendBuffer, BytesReceived);\r\n    LxtCheckEqual(AddressLength, sizeof(FromAddressIpv4), \"%lu\");\r\n\r\n    //\r\n    // Try to delete the device.\r\n    //\r\n\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"veth0\"));\r\n    DeleteDevice = false;\r\n\r\nErrorExit:\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    if (Socket2 > 0)\r\n    {\r\n        close(Socket2);\r\n    }\r\n\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth0\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint VirtualEthernetPairNamespaceData(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    struct sockaddr_in AddressIpv4 = {};\r\n    socklen_t AddressLength;\r\n    ssize_t BytesReceived;\r\n    ssize_t BytesSent;\r\n    bool DeleteDevice;\r\n    struct sockaddr_in FromAddressIpv4;\r\n    ;\r\n    int NewNetworkNamespaceFd;\r\n    int OriginalNetworkNamespaceFd;\r\n    char RecvBuffer[10] = {0};\r\n    int Response;\r\n    int Result;\r\n    char SendBuffer[10] = {\"123456789\"};\r\n    int Socket;\r\n    int Socket2;\r\n\r\n    DeleteDevice = false;\r\n    NewNetworkNamespaceFd = 0;\r\n    OriginalNetworkNamespaceFd = 0;\r\n    Socket = -1;\r\n    Socket2 = -1;\r\n\r\n    //\r\n    // Open file descriptor of default network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(OriginalNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Switch to a new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(unshare(CLONE_NEWNET));\r\n\r\n    //\r\n    // Open file descriptor of the new network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(NewNetworkNamespaceFd = open(\"/proc/self/ns/net\", 0));\r\n\r\n    //\r\n    // Switch back to original network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\n    //\r\n    // Create a new virtual ethernet pair.\r\n    //\r\n\r\n    LxtCheckResult(CreateVirtualEthernetPairViaNetlink(\"veth0\", \"veth1\"));\r\n    DeleteDevice = true;\r\n\r\n    //\r\n    // Try to move one half of the pair into the new namespace.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceAttributeViaNetlink(\"veth1\", IFLA_NET_NS_FD, NewNetworkNamespaceFd, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n\r\n    //\r\n    // Bring the interfaces up and assign static IP addresses.\r\n    //\r\n\r\n    LxtCheckResult(SetVirtualDeviceFlagViaNetlink(\"veth0\", IFF_UP, true, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n    AddressIpv4.sin_family = AF_INET;\r\n    inet_aton(LXT_IP_ADDRESS1, &AddressIpv4.sin_addr);\r\n    LxtCheckResult(SetIpAddress(\"veth0\", &AddressIpv4.sin_addr, 32));\r\n    LxtCheckErrno((Socket = socket(AF_INET, SOCK_DGRAM, 0)));\r\n    LxtCheckErrno(bind(Socket, (struct sockaddr*)&AddressIpv4, sizeof(AddressIpv4)));\r\n\r\n    //\r\n    // Switch to the new network namespace to configure the other endpoint.\r\n    //\r\n\r\n    LxtCheckErrno(setns(NewNetworkNamespaceFd, CLONE_NEWNET));\r\n    LxtCheckResult(SetVirtualDeviceFlagViaNetlink(\"veth1\", IFF_UP, true, &Response));\r\n\r\n    LxtCheckEqual(Response, 0, \"%d\");\r\n    LxtCheckResult(SetRoute(\"veth1\", &AddressIpv4.sin_addr, 32));\r\n    inet_aton(LXT_IP_ADDRESS2, &AddressIpv4.sin_addr);\r\n    LxtCheckResult(SetIpAddress(\"veth1\", &AddressIpv4.sin_addr, 32));\r\n\r\n    //\r\n    // Open UDP sockets to try to send and receive on the IP addresses assigned\r\n    // above.\r\n    //\r\n\r\n    LxtCheckErrno((Socket2 = socket(AF_INET, SOCK_DGRAM, 0)));\r\n    LxtCheckErrno(bind(Socket2, (struct sockaddr*)&AddressIpv4, sizeof(AddressIpv4)));\r\n\r\n    AddressLength = sizeof(AddressIpv4);\r\n    LxtCheckErrno(getsockname(Socket2, (struct sockaddr*)&AddressIpv4, &AddressLength));\r\n\r\n    LxtCheckEqual(AddressLength, sizeof(AddressIpv4), \"%lu\");\r\n\r\n    //\r\n    // Switch back to original network namespace.\r\n    //\r\n\r\n    LxtCheckErrno(setns(OriginalNetworkNamespaceFd, CLONE_NEWNET));\r\n\r\n    //\r\n    // Send a packet between the devices.\r\n    //\r\n\r\n    LxtCheckResult(SetRoute(\"veth0\", &AddressIpv4.sin_addr, 32));\r\n    LxtLogInfo(\"AddressIpv4.sin_family = %d\", AddressIpv4.sin_family);\r\n    LxtLogInfo(\"AddressIpv4.sin_port = %d\", AddressIpv4.sin_port);\r\n    LxtLogInfo(\"AddressIpv4.sin_addr = %08x\", AddressIpv4.sin_addr.s_addr);\r\n    LxtCheckErrno((BytesSent = sendto(Socket, SendBuffer, sizeof(SendBuffer), 0, (struct sockaddr*)&AddressIpv4, sizeof(AddressIpv4))));\r\n\r\n    LxtCheckEqual(BytesSent, sizeof(SendBuffer), \"%lu\");\r\n    AddressLength = sizeof(FromAddressIpv4);\r\n    LxtCheckErrno((BytesReceived = recvfrom(Socket2, RecvBuffer, sizeof(RecvBuffer), 0, (struct sockaddr*)&FromAddressIpv4, &AddressLength)));\r\n\r\n    LxtCheckEqual(BytesReceived, sizeof(SendBuffer), \"%lu\");\r\n    LxtCheckMemoryEqual(RecvBuffer, SendBuffer, BytesReceived);\r\n    LxtCheckEqual(AddressLength, sizeof(FromAddressIpv4), \"%lu\");\r\n\r\n    //\r\n    // Try to delete the device.\r\n    //\r\n\r\n    LxtCheckResult(DeleteVirtualDeviceViaNetlink(\"veth0\"));\r\n    DeleteDevice = false;\r\n\r\nErrorExit:\r\n    if (NewNetworkNamespaceFd > 0)\r\n    {\r\n        close(NewNetworkNamespaceFd);\r\n    }\r\n\r\n    if (OriginalNetworkNamespaceFd > 0)\r\n    {\r\n        (void)setns(OriginalNetworkNamespaceFd, CLONE_NEWNET);\r\n        close(OriginalNetworkNamespaceFd);\r\n    }\r\n\r\n    if (Socket > 0)\r\n    {\r\n        close(Socket);\r\n    }\r\n\r\n    if (Socket2 > 0)\r\n    {\r\n        close(Socket2);\r\n    }\r\n\r\n    if (DeleteDevice)\r\n    {\r\n        (void)DeleteVirtualDeviceViaNetlink(\"veth0\");\r\n    }\r\n\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/waitpid.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    WaitPid.c\r\n\r\nAbstract:\r\n\r\n    This file is a WaitPid test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#include <fcntl.h>\r\n#include <sys/wait.h>\r\n#include <sys/resource.h>\r\n#include <sys/syscall.h>\r\n#include <sys/prctl.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include \"lxtutil.h\"\r\n\r\n#define LXT_NAME \"WaitPid\"\r\n\r\n#define WAITPID_DEFAULT_WAIT_TIMEOUT_US 100000\r\n#define WAITPID_DEFAULT_WAIT_COUNT 20\r\n#define WAITPID_THREADGROUP_LEADER_UID 1044\r\n#define WAITPID_PTHREAD_UID 1055\r\n\r\nbool g_VmMode = false;\r\n\r\nint GetPPidPoll(pid_t ExpectedPid);\r\n\r\nLXT_VARIATION_HANDLER WaitPidVariationExitStatusBlock;\r\n\r\nint WaitPidVariationExitStatusHelper(PLXT_ARGS Args, int Blocking);\r\n\r\nLXT_VARIATION_HANDLER WaitPidVariationExitStatusPoll;\r\nLXT_VARIATION_HANDLER WaitPidVariationInitPid;\r\nLXT_VARIATION_HANDLER WaitPidVariationParentChild;\r\nLXT_VARIATION_HANDLER WaitPidVariationProcessGroup;\r\nLXT_VARIATION_HANDLER WaitPidVariationInvalidParameter;\r\nLXT_VARIATION_HANDLER WaitPidVariationWaitId;\r\nLXT_VARIATION_HANDLER WaitPidVariationCloneParent;\r\nLXT_VARIATION_HANDLER WaitPidVariationZombie;\r\nLXT_VARIATION_HANDLER WaitPidVariationZombieStress;\r\n\r\n//\r\n// Global constants\r\n//\r\n// FIXME: Enable parent\\child test when clone gs issue is resolved.\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"WaitPidVariation - Exit status poll\", WaitPidVariationExitStatusPoll},\r\n    {\"WaitPidVariation - Exit status block\", WaitPidVariationExitStatusBlock},\r\n    {\"WaitPidVariation - Init pid\", WaitPidVariationInitPid},\r\n    {\"WaitPidVariation - Process groups\", WaitPidVariationProcessGroup},\r\n    {\"WaitPidVariation - Invalid parameter\", WaitPidVariationInvalidParameter},\r\n    {\"WaitPidVariation - waitid\", WaitPidVariationWaitId},\r\n    {\"WaitPidVariation - CLONE_PARENT\", WaitPidVariationCloneParent},\r\n    {\"WaitPidVariation - zombie support\", WaitPidVariationZombie},\r\n    {\"WaitPidVariation - zombie stress\", WaitPidVariationZombieStress}};\r\n\r\nint WaitPidTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    if (LxtWslVersion() == 2)\r\n    {\r\n        g_VmMode = true;\r\n    }\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LXT_SYNCHRONIZATION_POINT_INIT();\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY();\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint GetPPidPoll(pid_t ExpectedPPid)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    pid_t CurrentPPid;\r\n    int Result;\r\n    int WaitCount;\r\n\r\n    //\r\n    // Wait for the current ppid to reach the expected ppid.\r\n    //\r\n\r\n    for (WaitCount = 0; WaitCount < WAITPID_DEFAULT_WAIT_COUNT; ++WaitCount)\r\n    {\r\n        CurrentPPid = getppid();\r\n        if (CurrentPPid == ExpectedPPid)\r\n        {\r\n            break;\r\n        }\r\n\r\n        usleep(WAITPID_DEFAULT_WAIT_TIMEOUT_US);\r\n    }\r\n\r\n    if (WaitCount == WAITPID_DEFAULT_WAIT_COUNT)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Unexpected pid, %d != %d\", CurrentPPid, ExpectedPPid);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint LxtWaitPidHelper(pid_t ChildPid, int ExpectedWaitStatus, int Blocking)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int SecondWaitPidStatus;\r\n    int Result;\r\n    int WaitCount;\r\n    int WaitPidResult;\r\n    int WaitPidStatus;\r\n\r\n    if (Blocking == 0)\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(ChildPid, ExpectedWaitStatus));\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrno((WaitPidResult = waitpid(ChildPid, &WaitPidStatus, 0)));\r\n        if ((WaitPidStatus & 0xFFFF0000) != 0)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpected high short status: %d - %d\", WaitPidStatus, ExpectedWaitStatus);\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (WaitPidStatus != ExpectedWaitStatus)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"Unexpected status: %d != %d\", WaitPidStatus, ExpectedWaitStatus);\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (WIFEXITED(WaitPidStatus) != 0)\r\n        {\r\n            LxtCheckErrnoFailure(waitpid(ChildPid, &SecondWaitPidStatus, WNOHANG), ECHILD);\r\n        }\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint WaitPidVariationInitPid(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    int ExitCodeIndex;\r\n    unsigned char ExitCode;\r\n    int ExpectedPid;\r\n    int ExpectedWaitStatus;\r\n    pid_t ParentParentPid;\r\n    LXT_PIPE Pipe = {-1, -1};\r\n    int Result;\r\n    pid_t WaitPidResult;\r\n    int WaitPidStatus;\r\n    int WaitPipe;\r\n\r\n    //\r\n    // Determine who the subreaper of the test is. On WSL 1 it will be init, on\r\n    // WSL 2 it will be the relay process (parent of this process).\r\n    //\r\n\r\n    ExpectedPid = LXT_INIT_PID;\r\n    if (g_VmMode != false)\r\n    {\r\n        ExpectedPid = getppid();\r\n    }\r\n\r\n    LxtCheckResult(LxtCreatePipe(&Pipe));\r\n\r\n    //\r\n    // Check when a parent dies, the child has a parent pid of the subreaper.\r\n    //\r\n\r\n    ExitCode = 0;\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            LxtCheckResult(GetPPidPoll(ExpectedPid));\r\n            LxtCheckErrno(write(Pipe.Write, &WaitPipe, sizeof(WaitPipe)));\r\n        }\r\n\r\n        _exit(ExitCode);\r\n    }\r\n    else\r\n    {\r\n        LxtCheckResult(LxtWaitPidPoll(-1, ExitCode));\r\n        LxtCheckErrno(read(Pipe.Read, &WaitPipe, sizeof(WaitPipe)));\r\n    }\r\n\r\n    //\r\n    // The init process should never be a child.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(waitpid(LXT_INIT_PID, &WaitPidStatus, WNOHANG), ECHILD);\r\n\r\nErrorExit:\r\n    LxtClosePipe(&Pipe);\r\n\r\n    return Result;\r\n}\r\n\r\nint WaitPidVariationExitStatusBlock(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    return WaitPidVariationExitStatusHelper(Args, 1);\r\n}\r\n\r\nint WaitPidVariationExitStatusHelper(PLXT_ARGS Args, int Blocking)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int ExitCodeIndex;\r\n    unsigned char ExitCodes[] = {0, 1, 128, 255};\r\n    pid_t ChildPid[LXT_COUNT_OF(ExitCodes)];\r\n    int ExpectedWaitStatus;\r\n    pid_t ParentParentPid;\r\n    pid_t ParentPid;\r\n    int Result;\r\n\r\n    //\r\n    // Check that the correct _exit status is returned to a parent process and\r\n    // that it can only be checked once serially.\r\n    //\r\n\r\n    ParentParentPid = getppid();\r\n    ParentPid = getpid();\r\n    for (ExitCodeIndex = 0; ExitCodeIndex < LXT_COUNT_OF(ExitCodes); ++ExitCodeIndex)\r\n    {\r\n        LxtCheckErrno(ChildPid[ExitCodeIndex] = fork());\r\n        if (ChildPid[ExitCodeIndex] == 0)\r\n        {\r\n            ChildPid[ExitCodeIndex] = getpid();\r\n            if (getppid() != ParentPid)\r\n            {\r\n                Result = LXT_RESULT_FAILURE;\r\n                LxtLogError(\"Unexpected parent pid in child - %d != %d\", ChildPid[ExitCodeIndex], ParentPid);\r\n\r\n                goto ErrorExit;\r\n            }\r\n\r\n            _exit(ExitCodes[ExitCodeIndex]);\r\n        }\r\n        else\r\n        {\r\n            ExpectedWaitStatus = ExitCodes[ExitCodeIndex] << 8;\r\n            LxtCheckResult(LxtWaitPidHelper(ChildPid[ExitCodeIndex], ExpectedWaitStatus, Blocking));\r\n        }\r\n    }\r\n\r\n    //\r\n    // Recheck the results after launching the children in parallel.\r\n    //\r\n\r\n    LxtLogInfo(\"Running forks in parallel...\");\r\n    for (ExitCodeIndex = 0; ExitCodeIndex < LXT_COUNT_OF(ExitCodes); ++ExitCodeIndex)\r\n    {\r\n        LxtCheckErrno(ChildPid[ExitCodeIndex] = fork());\r\n        if (ChildPid[ExitCodeIndex] == 0)\r\n        {\r\n            ChildPid[ExitCodeIndex] = getpid();\r\n            if (getppid() != ParentPid)\r\n            {\r\n                Result = LXT_RESULT_FAILURE;\r\n                LxtLogError(\"Unexpected parent pid in child - %d != %d\", ChildPid[ExitCodeIndex], ParentPid);\r\n\r\n                goto ErrorExit;\r\n            }\r\n\r\n            _exit(ExitCodes[ExitCodeIndex]);\r\n        }\r\n    }\r\n\r\n    while (ExitCodeIndex--)\r\n    {\r\n        ExpectedWaitStatus = ExitCodes[ExitCodeIndex] << 8;\r\n        LxtCheckResult(LxtWaitPidHelper(ChildPid[ExitCodeIndex], ExpectedWaitStatus, Blocking));\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint WaitPidVariationExitStatusPoll(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    return WaitPidVariationExitStatusHelper(Args, 0);\r\n}\r\n\r\ntypedef struct _WAIT_PID_PARENT_DATA\r\n{\r\n    int Generation;\r\n    pid_t ParentPid;\r\n    pid_t ParentTid;\r\n    LXT_PIPE Pipes[4];\r\n    LXT_CLONE_ARGS CloneArgs[2];\r\n    pid_t ForkPid;\r\n} WAIT_PID_PARENT_DATA, *PWAIT_PID_PARENT_DATA;\r\n\r\nvoid WaitPidPrintData(PWAIT_PID_PARENT_DATA CurrentData, char* Message)\r\n\r\n{\r\n\r\n    int Index;\r\n    pid_t WaitResult;\r\n    int WaitStatus;\r\n\r\n    //\r\n    // TODO: Enable _CLONE\r\n    //\r\n\r\n    LxtLogInfo(\"***** %s\", Message);\r\n    WaitStatus = -1;\r\n    for (Index = 0; Index < LXT_COUNT_OF(CurrentData->CloneArgs); ++Index)\r\n    {\r\n        LxtLogInfo(\"Before waitpid\", Message);\r\n        WaitResult = waitpid(CurrentData->CloneArgs[Index].CloneId, &WaitStatus, WNOHANG);\r\n        LxtLogInfo(\"%s - Clone %d WNOHANG - %d, %d\", Message, CurrentData->CloneArgs[Index].CloneId, WaitResult, WaitStatus);\r\n        //      WaitResult = waitpid(CurrentData->CloneArgs[Index].CloneId, &WaitStatus, WNOHANG | __WCLONE);\r\n        //      LxtLogInfo(\"%s - Clone %d WNOHANG | __WCLONE - %d, %d\", Message, CurrentData->CloneArgs[Index].CloneId, WaitResult, WaitStatus);\r\n    }\r\n\r\n    WaitResult = waitpid(CurrentData->ForkPid, &WaitStatus, WNOHANG);\r\n    LxtLogInfo(\"%s - Fork %d WNOHANG - %d, %d\", Message, CurrentData->ForkPid, WaitResult, WaitStatus);\r\n    //    WaitResult = waitpid(CurrentData->ForkPid, &WaitStatus, WNOHANG | __WCLONE);\r\n    //    LxtLogInfo(\"%s - Fork %d WNOHANG | __WCLONE - %d, %d\", Message, CurrentData->ForkPid, WaitResult, WaitStatus);\r\n    LxtLogInfo(\"***** %s\", Message);\r\n\r\n    return;\r\n}\r\n\r\nint WaitPidVariationParentChildClone(void* Parameter)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int CloneIndex;\r\n    PWAIT_PID_PARENT_DATA CurrentData;\r\n    pid_t CurrentPid;\r\n    pid_t CurrentTid;\r\n    int Index;\r\n    int ParentIndex;\r\n    int Result;\r\n    int WaitPipe;\r\n\r\n    CurrentData = Parameter;\r\n    CurrentTid = gettid();\r\n    for (Index = 0; Index < LXT_COUNT_OF(CurrentData->CloneArgs); ++Index)\r\n    {\r\n        if (CurrentData->CloneArgs[Index].CloneId == CurrentTid)\r\n        {\r\n            CloneIndex = Index;\r\n            break;\r\n        }\r\n    }\r\n\r\n    if (Index == LXT_COUNT_OF(CurrentData->CloneArgs))\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Unable to find clone tid %d\", CurrentTid);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrno(read(CurrentData->Pipes[CloneIndex].Read, &WaitPipe, sizeof(WaitPipe)));\r\n    CurrentPid = getpid();\r\n    if (CurrentPid != CurrentData->ParentPid)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Unexpected pid for clone %d: %d != %d\", CloneIndex, CurrentPid, CurrentData->ParentPid);\r\n\r\n        goto ErrorExit;\r\n    }\r\n\r\n    WaitPidPrintData(CurrentData, \"Clone\");\r\n    ParentIndex = LXT_COUNT_OF(CurrentData->CloneArgs) + 1;\r\n    LxtCheckErrno(write(CurrentData->Pipes[ParentIndex].Write, &WaitPipe, sizeof(WaitPipe)));\r\n    LxtCheckErrno(read(CurrentData->Pipes[CloneIndex].Read, &WaitPipe, sizeof(WaitPipe)));\r\n\r\nErrorExit:\r\n    LxtLogInfo(\"Clone %d exit\", CloneIndex);\r\n    return Result;\r\n}\r\n\r\nint WaitPidVariationParentChildFork(PWAIT_PID_PARENT_DATA ParentData)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    WAIT_PID_PARENT_DATA CurrentData;\r\n    int ForkIndex;\r\n    pid_t ParentPid;\r\n    int ParentIndex;\r\n    int Result;\r\n    int WaitPipe;\r\n\r\n    ForkIndex = LXT_COUNT_OF(ParentData->CloneArgs);\r\n    LxtCheckErrno(read(ParentData->Pipes[ForkIndex].Read, &WaitPipe, sizeof(WaitPipe)));\r\n    CurrentData.ParentPid = getpid();\r\n    CurrentData.ParentTid = gettid();\r\n    if (CurrentData.ParentPid != CurrentData.ParentTid)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Current fork thread is not thread group leader %d != %d\", CurrentData.ParentPid, CurrentData.ParentTid);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    ParentPid = getppid();\r\n    if (ParentPid != ParentData->ParentPid)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Unexpected ppid %d != %d\", ParentPid, ParentData->ParentPid);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    if (CurrentData.ParentPid == ParentData->ParentPid)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Unexpected pid %d == %d\", CurrentData.ParentPid, ParentData->ParentPid);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    ParentData->ForkPid = getpid();\r\n    WaitPidPrintData(ParentData, \"Fork\");\r\n    ParentIndex = LXT_COUNT_OF(ParentData->CloneArgs) + 1;\r\n    LxtCheckErrno(write(ParentData->Pipes[ParentIndex].Write, &WaitPipe, sizeof(WaitPipe)));\r\n    LxtCheckErrno(read(ParentData->Pipes[ForkIndex].Read, &WaitPipe, sizeof(WaitPipe)));\r\n\r\nErrorExit:\r\n    _exit(Result);\r\n}\r\n\r\nint WaitPidVariationCreateParentData(PWAIT_PID_PARENT_DATA CurrentData)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Index;\r\n    int ParentIndex;\r\n    int Result;\r\n    int WaitPipe;\r\n    pid_t WaitResult;\r\n    int WaitStatus;\r\n\r\n    CurrentData->Generation = CurrentData->Generation + 1;\r\n    CurrentData->ParentPid = getpid();\r\n    CurrentData->ParentTid = gettid();\r\n    if (CurrentData->ParentPid != CurrentData->ParentTid)\r\n    {\r\n        Result = LXT_RESULT_FAILURE;\r\n        LxtLogError(\"Current thread is not thread group leader %d != %d\", CurrentData->ParentPid, CurrentData->ParentTid);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes); ++Index)\r\n    {\r\n        LxtCheckResult(LxtCreatePipe(&CurrentData->Pipes[Index]));\r\n    }\r\n\r\n    //\r\n    // TODO: Enable _CLONE\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(CurrentData->CloneArgs); ++Index)\r\n    {\r\n        LxtCheckResult(LxtClone(WaitPidVariationParentChildClone, CurrentData, LXT_CLONE_FLAGS_DEFAULT, &CurrentData->CloneArgs[Index]));\r\n    }\r\n\r\n    LxtCheckErrno(CurrentData->ForkPid = fork());\r\n    if (CurrentData->ForkPid == 0)\r\n    {\r\n        WaitPidVariationParentChildFork(CurrentData);\r\n    }\r\n\r\n    WaitPidPrintData(CurrentData, \"Parent\");\r\n    ParentIndex = LXT_COUNT_OF(CurrentData->CloneArgs) + 1;\r\n    for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes) - 1; ++Index)\r\n    {\r\n        LxtCheckErrno(write(CurrentData->Pipes[Index].Write, &WaitPipe, sizeof(WaitPipe)));\r\n        LxtCheckErrno(read(CurrentData->Pipes[ParentIndex].Read, &WaitPipe, sizeof(WaitPipe)));\r\n    }\r\n\r\n    //\r\n    // Release the waiters\r\n    //\r\n\r\n    for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes) - 1; ++Index)\r\n    {\r\n        LxtCheckErrno(write(CurrentData->Pipes[Index].Write, &WaitPipe, sizeof(WaitPipe)));\r\n    }\r\n\r\nErrorExit:\r\n    for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes); ++Index)\r\n    {\r\n        LxtClosePipe(&CurrentData->Pipes[Index]);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint WaitPidVariationParentChild(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    WAIT_PID_PARENT_DATA CurrentData;\r\n    int Result;\r\n\r\n    memset(&CurrentData, 0, sizeof(CurrentData));\r\n    LxtCheckResult(WaitPidVariationCreateParentData(&CurrentData));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint WaitPidVariationProcessGroup(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests waiting on all children in the same process group.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    const int OtherGroupChildCount = 2;\r\n    pid_t OtherGroupChild[OtherGroupChildCount];\r\n    int Result;\r\n    pid_t SameGroupChild;\r\n    int Status;\r\n    int WaitResult;\r\n\r\n    LxtCheckResult(LxtSignalBlock(SIGUSR1));\r\n    for (int Child = 0; Child < OtherGroupChildCount; Child++)\r\n    {\r\n        LxtCheckErrno(OtherGroupChild[Child] = fork());\r\n        if (OtherGroupChild[Child] == 0)\r\n        {\r\n\r\n            //\r\n            // Change process group, then signal the parent.\r\n            //\r\n\r\n            LxtCheckErrnoZeroSuccess(setpgid(0, 0));\r\n            LxtCheckErrnoZeroSuccess(kill(getppid(), SIGUSR1));\r\n            _exit(0);\r\n        }\r\n\r\n        //\r\n        // Wait to make sure the child changed its process group.\r\n        //\r\n\r\n        LxtCheckResult(LxtSignalWaitBlocked(SIGUSR1, OtherGroupChild[Child], 2));\r\n    }\r\n\r\n    LxtCheckErrno(SameGroupChild = fork());\r\n    if (SameGroupChild == 0)\r\n    {\r\n        _exit(0);\r\n    }\r\n\r\n    //\r\n    // Wait for the same process group, which should return the status of the\r\n    // second child.\r\n    //\r\n\r\n    LxtCheckResult(WaitResult = LxtWaitPidPoll(0, 0));\r\n    LxtCheckEqual(WaitResult, SameGroupChild, \"%d\");\r\n\r\n    //\r\n    // Wait again, which should fail because there are no more children in this\r\n    // process group.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(waitpid(0, &Status, WNOHANG), ECHILD);\r\n\r\n    //\r\n    // Wait on the specific process group of one of the remaining children.\r\n    //\r\n\r\n    LxtCheckResult(WaitResult = LxtWaitPidPoll(-OtherGroupChild[0], 0));\r\n    LxtCheckEqual(WaitResult, OtherGroupChild[0], \"%d\");\r\n\r\n    //\r\n    // Wait on all children, which should return the child in the other process\r\n    // group.\r\n    //\r\n\r\n    LxtCheckResult(WaitResult = LxtWaitPidPoll(-1, 0));\r\n    LxtCheckEqual(WaitResult, OtherGroupChild[1], \"%d\");\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint WaitPidVariationInvalidParameter(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests invalid parameter handling for the waitpid system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    int Status;\r\n\r\n    LxtCheckErrnoFailure(waitpid(0, &Status, WEXITED), EINVAL);\r\n    LxtCheckErrnoFailure(waitpid(0, &Status, WNOWAIT), EINVAL);\r\n    LxtCheckErrnoFailure(waitpid(0, NULL, WNOHANG), ECHILD);\r\n    LxtCheckErrnoFailure(waitpid(0, (void*)-1, WNOHANG), ECHILD);\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid* WaitPidVariationWaitIdThread(void* Parameter)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine is the thread handler for the waitid test.\r\n\r\nArguments:\r\n\r\n    Parameter - Supplies the thread parameter.\r\n\r\nReturn Value:\r\n\r\n    Returns the thread id on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n    LxtLogInfo(\"WaitPid child tid %d\", gettid());\r\n    LxtCheckErrno(LxtSetUid(WAITPID_PTHREAD_UID));\r\n\r\n    //\r\n    // Enter a very long sleep, this will be interrupted when the threadgroup\r\n    // leader dies.\r\n    //\r\n\r\n    sleep(-1);\r\n    Result = 0;\r\n\r\nErrorExit:\r\n\r\n#pragma GCC diagnostic push\r\n#pragma GCC diagnostic ignored \"-Wint-to-pointer-cast\"\r\n\r\n    return (void*)Result;\r\n\r\n#pragma GCC diagnostic pop\r\n}\r\n\r\nint WaitPidVariationWaitId(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests the waitid system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ChildPid;\r\n    int ExpectedStatus;\r\n    int Result;\r\n    siginfo_t SigInfo;\r\n    pthread_t Thread;\r\n    struct rusage Usage;\r\n\r\n    ExpectedStatus = 44;\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // Create a child thread, set the uid of the threadgroup leader,\r\n        // and exit.\r\n        //\r\n\r\n        LxtCheckResultError(pthread_create(&Thread, NULL, WaitPidVariationWaitIdThread, NULL));\r\n\r\n        LxtLogInfo(\"Waitid parent tid %d\", gettid());\r\n\r\n        //\r\n        // Briefly sleep to allow the pthread to run.\r\n        //\r\n\r\n        sleep(1);\r\n        LxtCheckErrno(LxtSetUid(WAITPID_THREADGROUP_LEADER_UID));\r\n        _exit(ExpectedStatus);\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_ALL, 0, &SigInfo, WEXITED, &Usage));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_code, CLD_EXITED, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_status, ExpectedStatus, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_pid, ChildPid, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_uid, WAITPID_THREADGROUP_LEADER_UID, \"%d\");\r\n\r\n    //\r\n    // Wait for a specific child.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(ExpectedStatus);\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, WEXITED, &Usage));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_code, CLD_EXITED, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_status, ExpectedStatus, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_pid, ChildPid, \"%d\");\r\n\r\n    //\r\n    // Wait with WNOHANG specified.\r\n    //\r\n    // N.B. Parent must sleep to allow the child to exit before waiting.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(ExpectedStatus);\r\n    }\r\n\r\n    sleep(1);\r\n    LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, (WEXITED | WNOHANG), &Usage));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_code, CLD_EXITED, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_status, ExpectedStatus, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_pid, ChildPid, \"%d\");\r\n\r\n    //\r\n    // Wait with a null siginfo structure.\r\n    //\r\n    // N.B. The man page states that the pid of the child should be returned but\r\n    //      this is not the case. The wait should still be consumed so the\r\n    //      second wait should fail with ECHILD.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL));\r\n    LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL), ECHILD);\r\n\r\n    //\r\n    // Wait with the WNOWAIT option supplied which means the wait is not\r\n    // consumed. Wait again to consume the wait and verify it was not consumed\r\n    // by the first call.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(ExpectedStatus);\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, (WEXITED | WNOWAIT), &Usage));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_code, CLD_EXITED, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_status, ExpectedStatus, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_pid, ChildPid, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, WEXITED, &Usage));\r\n    LxtCheckEqual(Result, 0, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_code, CLD_EXITED, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_status, ExpectedStatus, \"%d\");\r\n    LxtCheckEqual(SigInfo.si_pid, ChildPid, \"%d\");\r\n\r\n    //\r\n    // Call getrusage with the supported values.\r\n    //\r\n\r\n    LxtCheckErrno(getrusage(RUSAGE_SELF, &Usage));\r\n    LxtCheckErrno(getrusage(RUSAGE_THREAD, &Usage));\r\n    LxtCheckErrno(getrusage(RUSAGE_CHILDREN, &Usage));\r\n\r\n    //\r\n    // Invalid parameter variations.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(0);\r\n    }\r\n\r\n    LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WNOHANG, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtWaitId(-1, 0, NULL, WEXITED, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtWaitId(P_PGID + 5, 0, NULL, WEXITED, NULL), EINVAL);\r\n    LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, 0x10, NULL), EINVAL);\r\n\r\n    //\r\n    // N.B. Providing an invalid pointer to a siginfo structure returns efault\r\n    //      but consumes the wait.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, (void*)-1, WEXITED, NULL), EFAULT);\r\n    LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL), ECHILD);\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(0);\r\n    }\r\n\r\n    //\r\n    // N.B. Providing an invalid pointer to a rusage structure returns efault\r\n    //      but consumes the wait.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, (void*)-1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL), ECHILD);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint WaitPidVariationCloneParentChild(void)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine is the child process for WaitPidVariationCloneParent.\r\n\r\nArguments:\r\n\r\n    None..\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t GrandChildParent;\r\n    pid_t ChildParent;\r\n    int Result;\r\n    int WaitPidStatus;\r\n\r\n    Result = 0;\r\n\r\n    //\r\n    // Create a child process with the CLONE_PARENT flag.\r\n    //\r\n    // The new process should not be reported as a child.\r\n    //\r\n\r\n    ChildParent = getppid();\r\n    LxtLogInfo(\"ChildParent %d\", ChildParent);\r\n    LxtCheckResult(ChildPid = LxtCloneSyscall(CLONE_PARENT | SIGCHLD, NULL, NULL, NULL, NULL));\r\n    if (ChildPid == 0)\r\n    {\r\n        GrandChildParent = getppid();\r\n        LxtCheckEqual(ChildParent, GrandChildParent, \"%d\");\r\n        LxtLogInfo(\"Grand child %d exiting\", LxtGetTid());\r\n    }\r\n    else\r\n    {\r\n        LxtCheckErrnoFailure(waitpid(ChildPid, &WaitPidStatus, 0), ECHILD);\r\n        LxtLogInfo(\"Child %d exiting\", LxtGetTid());\r\n    }\r\n\r\nErrorExit:\r\n    _exit(Result);\r\n}\r\n\r\nint WaitPidVariationCloneParent(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests invalid parameter handling for the waitpid system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t GrandChildPid;\r\n    int Result;\r\n    int Status;\r\n\r\n    ChildPid = -1;\r\n\r\n    //\r\n    // Create a child process, that in turn creates a grandchild process\r\n    // with CLONE_PARENT.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        WaitPidVariationCloneParentChild();\r\n    }\r\n\r\n    //\r\n    // Wait for the child and the grandchild.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    LxtCheckResult(GrandChildPid = LxtWaitPidPoll(0, 0));\r\n    LxtLogInfo(\"Waited on grandchild %d\", GrandChildPid);\r\n\r\n    //\r\n    // Check that the same scenario works when parent dies before\r\n    // CLONE_PARENT.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            sleep(1);\r\n            WaitPidVariationCloneParentChild();\r\n        }\r\n\r\n        _exit(0);\r\n    }\r\n\r\n    //\r\n    // Wait for the child and give the grandchild time to finish.\r\n    //\r\n\r\n    LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));\r\n    LxtLogInfo(\"Waited on child %d\", ChildPid);\r\n    sleep(2);\r\n\r\nErrorExit:\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\ntypedef struct _WAITPID_THREAD_PARAMETERS\r\n{\r\n    pid_t ChildPid;\r\n    pid_t GrandChildPid1;\r\n    pid_t GrandChildPid2;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(ChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);\r\n    LXT_PIPE Pipe;\r\n    int ChildLevel;\r\n    int Variation;\r\n} WAITPID_THREAD_PARAMETERS, *PWAITPID_THREAD_PARAMETERS;\r\n\r\nint ThreadZombieThread(void* Context)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine is the thread proc used by WaitPidVariationThreadZombie.\r\n\r\nArguments:\r\n\r\n    Context - Supplies the thread context.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int ChildLevel;\r\n    pid_t ChildPid;\r\n    int Flags;\r\n    pid_t GrandChildPid1;\r\n    pid_t GrandChildPid2;\r\n    LXT_CLONE_ARGS CloneArgs;\r\n    WAITPID_THREAD_PARAMETERS LocalParameter;\r\n    PWAITPID_THREAD_PARAMETERS Parameter;\r\n    int Result;\r\n    int Status;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(ChildPid);\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);\r\n\r\n    memcpy(&LocalParameter, Context, sizeof(LocalParameter));\r\n    ChildPid = 0;\r\n    ChildLevel = LocalParameter.ChildLevel;\r\n    if (ChildLevel != 0)\r\n    {\r\n        free(Context);\r\n    }\r\n\r\n    LxtSyncChildPidParent = LocalParameter.LxtSyncChildPidParent;\r\n    LxtSyncChildPidChild = LocalParameter.LxtSyncChildPidChild;\r\n    GrandChildPid1 = -1;\r\n    GrandChildPid2 = -1;\r\n    LxtSyncGrandChildPid2Parent = LocalParameter.LxtSyncGrandChildPid2Parent;\r\n    LxtSyncGrandChildPid2Child = LocalParameter.LxtSyncGrandChildPid2Child;\r\n    Parameter = NULL;\r\n\r\n    LxtSignalInitializeThread();\r\n\r\n    if (ChildLevel == 0)\r\n    {\r\n        LxtLogInfo(\"Child %d starting, variation = %d...\", getpid(), LocalParameter.Variation);\r\n\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGCHLD, SA_SIGINFO));\r\n        LxtCheckErrno(setsid());\r\n        switch (LocalParameter.Variation)\r\n        {\r\n        case 0:\r\n            Flags = SIGCHLD;\r\n            break;\r\n\r\n        case 1:\r\n            Flags = CLONE_FS | CLONE_FILES | SIGCHLD;\r\n            break;\r\n\r\n        case 2:\r\n            Flags = CLONE_FS | CLONE_FILES;\r\n            break;\r\n\r\n        case 3:\r\n            Flags = CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_FS | CLONE_FILES;\r\n            break;\r\n        };\r\n\r\n        Parameter = malloc(sizeof(*Parameter));\r\n        if (Parameter != NULL)\r\n        {\r\n            memcpy(Parameter, &LocalParameter, sizeof(*Parameter));\r\n        }\r\n\r\n        LxtCheckNotEqual(Parameter, NULL, \"%p\");\r\n        Parameter->ChildLevel = 1;\r\n\r\n        //\r\n        // The clone stack is leaked but the current process will exit shortly.\r\n        //\r\n\r\n        LxtCheckErrno(LxtClone(ThreadZombieThread, Parameter, Flags, &CloneArgs));\r\n\r\n        Parameter = NULL;\r\n        GrandChildPid1 = CloneArgs.CloneId;\r\n        LocalParameter.GrandChildPid1 = GrandChildPid1;\r\n        Parameter = malloc(sizeof(*Parameter));\r\n        if (Parameter != NULL)\r\n        {\r\n            memcpy(Parameter, &LocalParameter, sizeof(*Parameter));\r\n        }\r\n\r\n        LxtCheckNotEqual(Parameter, NULL, \"%p\");\r\n        Parameter->ChildLevel = 2;\r\n\r\n        //\r\n        // The clone stack is leaked but the current process will exit shortly.\r\n        //\r\n\r\n        LxtCheckErrno(LxtClone(ThreadZombieThread, Parameter, Flags, &CloneArgs));\r\n\r\n        Parameter = NULL;\r\n        LocalParameter.GrandChildPid2 = CloneArgs.CloneId;\r\n\r\n        LxtSignalWait();\r\n        if (LocalParameter.Variation < 2)\r\n        {\r\n            LxtCheckResult(LxtSignalCheckSigChldReceived(CLD_EXITED, GrandChildPid1, getuid(), 0));\r\n        }\r\n        else if (LocalParameter.Variation != 3)\r\n        {\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT_CHILD();\r\n        LxtCheckErrno(write(LocalParameter.Pipe.Write, &LocalParameter, sizeof(LocalParameter)));\r\n\r\n        LXT_SYNCHRONIZATION_POINT_CHILD();\r\n\r\n        //\r\n        // Exiting with one zombie child, and one child waiting for an exit\r\n        // signal.\r\n        //\r\n\r\n        LxtLogInfo(\"Exiting pid = %d\", getpid());\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n    else if (ChildLevel == 1)\r\n    {\r\n\r\n        //\r\n        // First child created via this thread, grandchild of the original.\r\n        //\r\n\r\n        GrandChildPid1 = 0;\r\n        LxtLogInfo(\"Grandchild %d starting...\", getpid());\r\n        LxtLogInfo(\"Exiting pid = %d\", getpid());\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n    else\r\n    {\r\n\r\n        //\r\n        // Second child created via this thread, second grandchild of the\r\n        // original.\r\n        //\r\n\r\n        GrandChildPid2 = 0;\r\n        LxtLogInfo(\"Grandchild %d starting...\", getpid());\r\n        LxtCheckResult(LxtSignalInitialize());\r\n        LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));\r\n        LXT_SYNCHRONIZATION_POINT_CHILD_FOR(GrandChildPid2);\r\n        LxtCheckResult(LxtSignalCheckNoSignal());\r\n        LxtLogInfo(\"Exiting pid = %d\", getpid());\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\nErrorExit:\r\n    if (Result != 0)\r\n    {\r\n\r\n        //\r\n        // Intentionally orphaning the thread unless there was an error.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid2, FALSE);\r\n    }\r\n\r\n    if (ChildLevel == 0)\r\n    {\r\n        if (LocalParameter.Variation != 3)\r\n        {\r\n            LXT_SYNCHRONIZATION_POINT_END();\r\n        }\r\n        else\r\n        {\r\n            LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD();\r\n        }\r\n    }\r\n\r\n    if (Parameter != NULL)\r\n    {\r\n        free(Parameter);\r\n    }\r\n\r\n    syscall(SYS_exit, Result);\r\n}\r\n\r\nint WaitPidVariationZombie(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests zombie handling with waitpid system call.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[50];\r\n    LXT_CLONE_ARGS ChildClone;\r\n    int ChildDir;\r\n    pid_t ChildPid;\r\n    FILE* ChildStatusFile;\r\n    pthread_t ChildThread;\r\n    int GrandChildDir;\r\n    pid_t GrandChildPid1;\r\n    pid_t GrandChildPid2;\r\n    FILE* GrandChildStatusFile;\r\n    int Index;\r\n    bool IsFork;\r\n    char Path[32];\r\n    LXT_PIPE Pipe = {-1, -1};\r\n    void* PointerResult;\r\n    int Result;\r\n    struct stat StatBuffer;\r\n    int Status;\r\n    char StatusDescription[25];\r\n    char* StatusFileEntry;\r\n    size_t StatusFileEntryLength;\r\n    char StatusToken;\r\n    WAITPID_THREAD_PARAMETERS ThreadParam;\r\n    LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);\r\n\r\n    ChildDir = -1;\r\n    ChildPid = -1;\r\n    ChildStatusFile = NULL;\r\n    ChildThread = 0;\r\n    GrandChildDir = -1;\r\n    GrandChildPid1 = -1;\r\n    GrandChildPid2 = -1;\r\n    GrandChildStatusFile = NULL;\r\n    IsFork = true;\r\n    StatusFileEntry = NULL;\r\n    LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid2);\r\n\r\n    //\r\n    // TODO: test needs to be debugged for WSL2.\r\n    //\r\n\r\n    if (g_VmMode != false)\r\n    {\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckResult(LxtSignalInitialize());\r\n    LxtCheckResult(LxtSignalSetupHandler(SIGCHLD, SA_SIGINFO));\r\n    LxtCheckResult(LxtSignalIgnore(SIGPIPE));\r\n\r\n    memset(&ThreadParam, 0, sizeof(ThreadParam));\r\n    ThreadParam.LxtSyncChildPidParent = LxtSyncChildPidParent;\r\n    ThreadParam.LxtSyncChildPidChild = LxtSyncChildPidChild;\r\n    ThreadParam.LxtSyncGrandChildPid2Parent = LxtSyncGrandChildPid2Parent;\r\n    ThreadParam.LxtSyncGrandChildPid2Child = LxtSyncGrandChildPid2Child;\r\n    for (Index = 0; Index < 4; Index += 1)\r\n    {\r\n        ChildPid = -1;\r\n        GrandChildPid1 = -1;\r\n        GrandChildPid2 = -1;\r\n        LXT_SYNCHRONIZATION_POINT_START();\r\n        LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid2);\r\n        ThreadParam.ChildPid = -1;\r\n        ThreadParam.GrandChildPid2 = -1;\r\n        ThreadParam.GrandChildPid2 = -1;\r\n        LxtCheckResult(LxtCreatePipe(&Pipe));\r\n        memcpy(&ThreadParam.Pipe, &Pipe, sizeof(Pipe));\r\n        ThreadParam.Variation = Index;\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            return ThreadZombieThread(&ThreadParam);\r\n        }\r\n\r\n        ThreadParam.ChildPid = ChildPid;\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtCheckErrno(read(Pipe.Read, &ThreadParam, sizeof(ThreadParam)));\r\n        GrandChildPid1 = ThreadParam.GrandChildPid1;\r\n        GrandChildPid2 = ThreadParam.GrandChildPid2;\r\n\r\n        //\r\n        // Check basic info of child after one of its children has entered\r\n        // zombie state.\r\n        //\r\n\r\n        LxtCheckErrno(sprintf(Path, \"/proc/%d\", ChildPid));\r\n        LxtCheckErrno(ChildDir = open(Path, O_RDONLY));\r\n        LxtCheckErrno(fstat(ChildDir, &StatBuffer));\r\n        LxtCheckErrno(sprintf(Path, \"/proc/%d/status\", ChildPid));\r\n        LxtCheckNullErrno(ChildStatusFile = fopen(Path, \"r\"));\r\n        while ((Result = getline(&StatusFileEntry, &StatusFileEntryLength, ChildStatusFile)) >= 0)\r\n        {\r\n\r\n            if (strncmp(StatusFileEntry, \"State:\\t\", 7) == 0)\r\n            {\r\n                break;\r\n            }\r\n\r\n            free(StatusFileEntry);\r\n            StatusFileEntry = NULL;\r\n        }\r\n\r\n        LxtCheckErrno(Result);\r\n        Result = sscanf(StatusFileEntry, \"State:  %c (%s)\", &StatusToken, StatusDescription);\r\n\r\n        LxtCheckEqual(Result, 2, \"%d\");\r\n        if (StatusToken == 'R')\r\n        {\r\n            LxtCheckStringEqual(StatusDescription, \"running)\");\r\n        }\r\n        else if (StatusToken == 'S')\r\n        {\r\n            LxtCheckStringEqual(StatusDescription, \"sleeping)\");\r\n        }\r\n        else\r\n        {\r\n            LxtLogError(\"Unexpected status: %c (%s)\", StatusToken, StatusDescription);\r\n            Result = -1;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtCheckErrno(getpgid(ChildPid));\r\n        LxtCheckErrno(kill(ChildPid, SIGWINCH));\r\n        LxtCheckErrno(sprintf(Path, \"/proc/%d/ns/mnt\", ChildPid));\r\n        LxtCheckErrno(readlink(Path, Buffer, sizeof(Buffer)));\r\n        LxtCheckErrno(sprintf(Path, \"/proc/%d/fd/0\", ChildPid));\r\n        LxtCheckErrno(stat(Path, &StatBuffer));\r\n\r\n        //\r\n        // Check basic info of first zombie.\r\n        //\r\n\r\n        LxtCheckErrno(sprintf(Path, \"/proc/%d\", GrandChildPid1));\r\n        if (Index != 3)\r\n        {\r\n            LxtCheckErrno(getpgid(GrandChildPid1));\r\n            LxtCheckErrno(kill(GrandChildPid1, SIGWINCH));\r\n            LxtCheckErrno(GrandChildDir = open(Path, O_RDONLY));\r\n            LxtCheckErrno(fstat(GrandChildDir, &StatBuffer));\r\n            LxtCheckErrno(sprintf(Path, \"/proc/%d/fd/0\", GrandChildPid1));\r\n\r\n            //\r\n            // TODO_LX: Zombied procfs \"fd\" entry should be accessible but\r\n            //          empty.\r\n            //\r\n            // LxtCheckErrnoFailure(stat(Path, &StatBuffer), ENOENT);\r\n            //\r\n\r\n            LxtCheckErrno(sprintf(Path, \"/proc/%d/status\", GrandChildPid1));\r\n            LxtCheckNullErrno(GrandChildStatusFile = fopen(Path, \"r\"));\r\n            free(StatusFileEntry);\r\n            StatusFileEntry = NULL;\r\n            while ((Result = getline(&StatusFileEntry, &StatusFileEntryLength, GrandChildStatusFile)) >= 0)\r\n            {\r\n\r\n                if (strncmp(StatusFileEntry, \"State:\\t\", 7) == 0)\r\n                {\r\n                    break;\r\n                }\r\n\r\n                free(StatusFileEntry);\r\n                StatusFileEntry = NULL;\r\n            }\r\n\r\n            LxtCheckErrno(Result);\r\n            Result = sscanf(StatusFileEntry, \"State:  %c (%s)\", &StatusToken, StatusDescription);\r\n\r\n            LxtCheckEqual(Result, 2, \"%d\");\r\n            LxtCheckEqual(StatusToken, 'Z', \"%c\");\r\n            LxtCheckStringEqual(StatusDescription, \"zombie)\");\r\n\r\n            LxtCheckErrno(sprintf(Path, \"/proc/%d/fd/0\", GrandChildPid1));\r\n\r\n            //\r\n            // TODO_LX: Zombied procfs \"fd\" entry should be accessible but\r\n            //          empty.\r\n            //\r\n            // LxtCheckErrnoFailure(open(Path, O_RDONLY), ENOENT);\r\n            //\r\n        }\r\n        else\r\n        {\r\n            LxtCheckErrnoFailure(GrandChildDir = open(Path, O_RDONLY), ENOENT);\r\n        }\r\n\r\n        //\r\n        // Close the read end of the pipe.\r\n        //\r\n\r\n        close(Pipe.Read);\r\n        Pipe.Read = -1;\r\n        LxtCheckErrno(write(Pipe.Write, &Result, sizeof(Result)));\r\n\r\n        //\r\n        // Allow the child to exit.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT();\r\n        LxtSignalWait();\r\n        if (Index != 3)\r\n        {\r\n            LxtCheckResult(LxtSignalCheckSigChldReceived(CLD_EXITED, ChildPid, getuid(), 0));\r\n\r\n            LxtSignalResetReceived();\r\n        }\r\n        else\r\n        {\r\n\r\n            //\r\n            // The threadgroup leader should have exited but there is still a\r\n            // thread running so the signal will not yet be sent.\r\n            //\r\n\r\n            LxtCheckResult(LxtSignalCheckNoSignal());\r\n        }\r\n\r\n        //\r\n        // Check basic info of running grandchild after child exit.\r\n        //\r\n\r\n        if (Index != 3)\r\n        {\r\n\r\n            //\r\n            // TODO_LX: These calls fail due to lack of proper thread support.\r\n            //\r\n\r\n            LxtCheckErrno(getpgid(GrandChildPid2));\r\n            LxtCheckErrno(kill(GrandChildPid2, SIGWINCH));\r\n        }\r\n\r\n        LxtCheckErrno(sprintf(Path, \"/proc/%d/ns/mnt\", GrandChildPid2));\r\n        LxtCheckErrno(readlink(Path, Buffer, sizeof(Buffer)));\r\n        LxtCheckErrno(sprintf(Path, \"/proc/%d/fd/0\", GrandChildPid2));\r\n        LxtCheckErrno(stat(Path, &StatBuffer));\r\n\r\n        //\r\n        // Signal last grandchild to exit.\r\n        //\r\n\r\n        LXT_SYNCHRONIZATION_POINT_PARENT_FOR(GrandChildPid2);\r\n        LxtSignalWait();\r\n        if (Index == 3)\r\n        {\r\n            LxtCheckResult(LxtSignalCheckSigChldReceived(CLD_EXITED, ChildPid, getuid(), 0));\r\n\r\n            LxtSignalResetReceived();\r\n        }\r\n\r\n        //\r\n        // Check the child information after it is a zombie.\r\n        //\r\n\r\n        LxtCheckErrno(fstat(ChildDir, &StatBuffer));\r\n        rewind(ChildStatusFile);\r\n        free(StatusFileEntry);\r\n        StatusFileEntry = NULL;\r\n        while ((Result = getline(&StatusFileEntry, &StatusFileEntryLength, ChildStatusFile)) >= 0)\r\n        {\r\n\r\n            if (strncmp(StatusFileEntry, \"State:\\t\", 7) == 0)\r\n            {\r\n                break;\r\n            }\r\n\r\n            free(StatusFileEntry);\r\n            StatusFileEntry = NULL;\r\n        }\r\n\r\n        LxtCheckErrno(Result);\r\n        Result = sscanf(StatusFileEntry, \"State:  %c (%s)\", &StatusToken, StatusDescription);\r\n\r\n        LxtCheckEqual(Result, 2, \"%d\");\r\n        LxtCheckEqual(StatusToken, 'Z', \"%c\");\r\n        LxtCheckStringEqual(StatusDescription, \"zombie)\");\r\n        LxtCheckErrno(getpgid(ChildPid));\r\n        LxtCheckErrno(kill(ChildPid, SIGWINCH));\r\n        LxtCheckErrno(sprintf(Path, \"/proc/%d/ns/mnt\", ChildPid));\r\n        LxtCheckErrnoFailure(readlink(Path, Buffer, sizeof(Buffer)), ENOENT);\r\n        sleep(2);\r\n        LxtCheckErrnoFailure(write(Pipe.Write, &Result, sizeof(Result)), EPIPE);\r\n\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n        ChildPid = -1;\r\n        LxtCheckErrnoFailure(waitpid(-1, &Status, WNOHANG), ECHILD);\r\n        LxtCheckEqual(Result, 0, \"%d\");\r\n        LxtClosePipe(&Pipe);\r\n    }\r\n\r\nErrorExit:\r\n    free(StatusFileEntry);\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    if (GrandChildPid2 > 0)\r\n    {\r\n        LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD_FOR(GrandChildPid2);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_DESTROY_FOR(GrandChildPid2);\r\n    if (GrandChildStatusFile != NULL)\r\n    {\r\n        fclose(GrandChildStatusFile);\r\n    }\r\n\r\n    if (GrandChildDir >= 0)\r\n    {\r\n        close(GrandChildDir);\r\n    }\r\n\r\n    if (ChildStatusFile != NULL)\r\n    {\r\n        fclose(ChildStatusFile);\r\n    }\r\n\r\n    if (ChildDir >= 0)\r\n    {\r\n        close(ChildDir);\r\n    }\r\n\r\n    //\r\n    // Intentionally leaking pipes from children / grandchildren to test\r\n    // file descriptor cleanup on exit in the fork case.\r\n    //\r\n\r\n    LxtClosePipe(&Pipe);\r\n\r\n    LxtSignalDefault(SIGPIPE);\r\n    LxtSignalDefault(SIGCHLD);\r\n    return Result;\r\n}\r\n\r\nint WaitPidVariationZombieStress(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine stress tests zombie handling.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    pid_t ChildPid;\r\n    pid_t GrandChildPid;\r\n    pid_t GreatGrandChildPid;\r\n    int Iterations;\r\n    int Result;\r\n    int Status;\r\n\r\n    GrandChildPid = -1;\r\n    GreatGrandChildPid = -1;\r\n    for (Iterations = 0; Iterations < 100; Iterations += 1)\r\n    {\r\n        ChildPid = -1;\r\n        LxtCheckErrno(ChildPid = fork());\r\n        if (ChildPid == 0)\r\n        {\r\n            LxtCheckErrno(setsid());\r\n            LxtCheckErrno(prctl(PR_SET_CHILD_SUBREAPER, 1));\r\n            for (Iterations = 0; Iterations < 3; Iterations += 1)\r\n            {\r\n                LxtCheckErrno(GrandChildPid = fork());\r\n                if (GrandChildPid == 0)\r\n                {\r\n                    ChildPid = -1;\r\n                    LxtCheckErrno(GreatGrandChildPid = fork());\r\n                    if (GreatGrandChildPid == 0)\r\n                    {\r\n                        usleep(random() % 100);\r\n                        goto ErrorExit;\r\n                    }\r\n\r\n                    usleep(random() % 100);\r\n                    (void)waitpid(GreatGrandChildPid, &Status, WNOHANG);\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                (void)waitpid(GrandChildPid, &Status, WNOHANG);\r\n            }\r\n\r\n            usleep(random() % 100);\r\n            (void)waitpid(GrandChildPid, &Status, WNOHANG);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LXT_SYNCHRONIZATION_POINT_END();\r\n        ChildPid = -1;\r\n    }\r\n\r\nErrorExit:\r\n    if ((GreatGrandChildPid == 0) || (GrandChildPid == 0))\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    LXT_SYNCHRONIZATION_POINT_END();\r\n    return Result;\r\n}"
  },
  {
    "path": "test/linux/unit_tests/wslpath.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    wslpath.c\r\n\r\nAbstract:\r\n\r\n    This file contains tests for the wslpath binary.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include <stdlib.h>\r\n#include <unistd.h>\r\n#include <sys/mount.h>\r\n\r\n#define LXT_NAME \"wslpath\"\r\n\r\n#define WSLPATH_ESCAPE_NAME \"wslpath_foo\\\\:bar\"\r\n#define WSLPATH_ESCAPE_NAME_ESCAPED \"wslpath_foo\\uf05c\\uf03abar\"\r\n#define WSLPATH_ESCAPE_DIR \"/mnt/c/\" WSLPATH_ESCAPE_NAME\r\n#define WSLPATH_ESCAPE_DIR_ESCAPED \"/mnt/c/\" WSLPATH_ESCAPE_NAME_ESCAPED\r\n#define WSLPATH_ESCAPE_DIR_WIN \"C:\\\\\" WSLPATH_ESCAPE_NAME_ESCAPED\r\n#define WSLPATH_SYMLINK_TEST_DIR \"/mnt/c/symlink_test_dir\"\r\n#define WSLPATH_SYMLINK_TEST_TARGET WSLPATH_SYMLINK_TEST_DIR \"/target\"\r\n#define WSLPATH_SYMLINK_TEST_LINK WSLPATH_SYMLINK_TEST_DIR \"/link\"\r\n#define WSLPATH_SYMLINK_TEST_DIR_WIN \"C:\\\\symlink_test_dir\"\r\n#define WSLPATH_SYMLINK_TEST_TARGET_WIN WSLPATH_SYMLINK_TEST_DIR_WIN \"\\\\target\"\r\n#define WSLPATH_SYMLINK_TEST_LINK_WIN WSLPATH_SYMLINK_TEST_DIR_WIN \"\\\\link\"\r\n#define WSLPATH_DISTRO_PREFIX \"\\\\\\\\wsl.localhost\\\\\" LXSS_DISTRO_NAME_TEST\r\n#define WSLPATH_DISTRO_COMPAT_PREFIX \"\\\\\\\\wsl$\\\\\" LXSS_DISTRO_NAME_TEST\r\n#define WSLPATH_ESCAPE_LX_DIR \"/data/\" WSLPATH_ESCAPE_NAME\r\n#define WSLPATH_ESCAPE_LX_DIR_WIN WSLPATH_DISTRO_PREFIX \"\\\\data\\\\\" WSLPATH_ESCAPE_NAME_ESCAPED\r\n#define WSLPATH_MOUNT_POINT \"/data/wslpath_mount\"\r\n\r\nLXT_VARIATION_HANDLER WslPathTestDrvFsEscaped;\r\n\r\nLXT_VARIATION_HANDLER WslPathTestDrvFsToWinPath;\r\n\r\nLXT_VARIATION_HANDLER WslPathTestDrvFsSymlink;\r\n\r\nLXT_VARIATION_HANDLER WslPathTestDrvFsFromWinPath;\r\n\r\nLXT_VARIATION_HANDLER WslPathTestInvalidMountInfo;\r\n\r\nLXT_VARIATION_HANDLER WslPathTestLxEscaped;\r\n\r\nLXT_VARIATION_HANDLER WslPathTestLxFromWinPath;\r\n\r\nLXT_VARIATION_HANDLER WslPathTestLxToWinPath;\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"WslPath - Windows to DrvFs\", WslPathTestDrvFsFromWinPath},\r\n    {\"WslPath - DrvFs to Windows\", WslPathTestDrvFsToWinPath},\r\n    {\"WslPath - DrvFs escaped characters\", WslPathTestDrvFsEscaped},\r\n    {\"WslPath - DrvFs symlinks\", WslPathTestDrvFsSymlink},\r\n    {\"WslPath - \\\\\\\\wsl.localhost to Linux\", WslPathTestLxFromWinPath},\r\n    {\"WslPath - Linux to \\\\\\\\wsl.localhost\", WslPathTestLxToWinPath},\r\n    {\"WslPath - \\\\\\\\wsl.localhost escaped characters\", WslPathTestLxEscaped},\r\n    {\"WslPath - Invalid mountinfo line\", WslPathTestInvalidMountInfo},\r\n};\r\n\r\nint WslPathTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    This routine main entry point for the wslpath tests.\r\n\r\nArguments:\r\n\r\n    Argc - Supplies the number of command line arguments.\r\n\r\n    Argv - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint WslPathTestDrvFsEscaped(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests wslpath on DrvFs paths with escaped characters.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(mkdir(WSLPATH_ESCAPE_DIR, 0777));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_ESCAPE_DIR, WSLPATH_ESCAPE_DIR_WIN, false));\r\n\r\n    //\r\n    // Translating win to drvfs does not unescape, since the escaped characters\r\n    // work on drvfs.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_ESCAPE_DIR_WIN, WSLPATH_ESCAPE_DIR_ESCAPED, true));\r\n\r\nErrorExit:\r\n    rmdir(WSLPATH_ESCAPE_DIR);\r\n    return Result;\r\n}\r\n\r\nint WslPathTestDrvFsFromWinPath(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests wslpath on Windows paths.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"C:\\\\\", \"/mnt/c/\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"C:\\\\Foo\", \"/mnt/c/Foo\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"C:\\\\Foo\\\\\", \"/mnt/c/Foo/\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"C:\\\\Foo\\\\bar\", \"/mnt/c/Foo/bar\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"C:/Foo/bar\", \"/mnt/c/Foo/bar\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"foo\", \"foo\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"foo\\\\\", \"foo/\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"C:\\\\Program Files\\\\Git\", \"/mnt/c/Program Files/Git\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"C:\\\\Program Files\\\\PowerShell\\\\7\", \"/mnt/c/Program Files/PowerShell/7\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"C:\\\\Program Files (x86)\\\\Common Files\", \"/mnt/c/Program Files (x86)/Common Files\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\r\n        \"C:\\\\Users\\\\Test User\\\\AppData\\\\Local\\\\Programs\\\\Microsoft VS Code\\\\bin\",\r\n        \"/mnt/c/Users/Test User/AppData/Local/Programs/Microsoft VS Code/bin\",\r\n        true));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint WslPathTestDrvFsSymlink(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests wslpath on DrvFs paths with symlinks.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(WSLPATH_SYMLINK_TEST_DIR, 0777));\r\n    LxtCheckErrnoZeroSuccess(mkdir(WSLPATH_SYMLINK_TEST_TARGET, 0777));\r\n    LxtCheckErrnoZeroSuccess(symlink(WSLPATH_SYMLINK_TEST_TARGET, WSLPATH_SYMLINK_TEST_LINK));\r\n\r\n    //\r\n    // Drvfs to Windows follows links.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_SYMLINK_TEST_LINK, WSLPATH_SYMLINK_TEST_TARGET_WIN, false));\r\n\r\n    //\r\n    // Windows to DrvFs is text based so does not.\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_SYMLINK_TEST_LINK_WIN, WSLPATH_SYMLINK_TEST_LINK, true));\r\n\r\nErrorExit:\r\n    unlink(WSLPATH_SYMLINK_TEST_LINK);\r\n    rmdir(WSLPATH_SYMLINK_TEST_TARGET);\r\n    rmdir(WSLPATH_SYMLINK_TEST_DIR);\r\n\r\n    return Result;\r\n}\r\n\r\nint WslPathTestDrvFsToWinPath(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests wslpath on DrvFs paths.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/mnt/c\", \"C:\\\\\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/mnt/c/\", \"C:\\\\\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/mnt/c/Users\", \"C:\\\\Users\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/mnt/c/Users/\", \"C:\\\\Users\\\\\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/mnt/c/DOESNOTEXIST/\", \"C:\\\\DOESNOTEXIST\\\\\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/mnt/c/Program Files/Git\", \"C:\\\\Program Files\\\\Git\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/mnt/c/Program Files/PowerShell/7\", \"C:\\\\Program Files\\\\PowerShell\\\\7\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/mnt/c/Program Files (x86)/Common Files\", \"C:\\\\Program Files (x86)\\\\Common Files\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\r\n        \"/mnt/c/Users/Test User/AppData/Local/Programs/Microsoft VS Code/bin\",\r\n        \"C:\\\\Users\\\\Test User\\\\AppData\\\\Local\\\\Programs\\\\Microsoft VS Code\\\\bin\",\r\n        false));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint WslPathTestInvalidMountInfo(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests wslpath's handling of ill-formed mountinfo lines.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    //\r\n    // WSL1 does not allow using an empty string as the mount source. This is\r\n    // technically a minor bug in WSL1, but it also means this test is not\r\n    // relevant, so skip it.\r\n    //\r\n\r\n    if (LxtWslVersion() == 1)\r\n    {\r\n        LxtLogInfo(\"Test skipped on WSL1.\");\r\n        Result = 0;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(mkdir(WSLPATH_MOUNT_POINT, 0777));\r\n\r\n    //\r\n    // Using an empty string as the mount source will cause the mountinfo file\r\n    // to have a blank field, which neither libmount nor mountutil can parse.\r\n    // This should however not break wslpath.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(mount(\"\", WSLPATH_MOUNT_POINT, \"tmpfs\", 0, NULL));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/mnt/c\", \"C:\\\\\", false));\r\n\r\nErrorExit:\r\n    umount(WSLPATH_MOUNT_POINT);\r\n    rmdir(WSLPATH_MOUNT_POINT);\r\n    return Result;\r\n}\r\n\r\nint WslPathTestLxEscaped(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests wslpath on internal Linux paths with escaped characters.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckErrno(mkdir(WSLPATH_ESCAPE_LX_DIR, 0777));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_ESCAPE_LX_DIR, WSLPATH_ESCAPE_LX_DIR_WIN, false));\r\n\r\n    //\r\n    // Translating \\\\wsl.localhost to Linux does unescape (unlike drvfs).\r\n    //\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_ESCAPE_LX_DIR_WIN, WSLPATH_ESCAPE_LX_DIR, true));\r\n\r\nErrorExit:\r\n    rmdir(WSLPATH_ESCAPE_LX_DIR);\r\n    return Result;\r\n}\r\n\r\nint WslPathTestLxFromWinPath(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests wslpath on \\\\wsl.localhost paths.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_DISTRO_PREFIX, \"/\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_DISTRO_PREFIX \"\\\\\", \"/\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_DISTRO_PREFIX \"\\\\root\", \"/root\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_DISTRO_PREFIX \"\\\\proc\\\\stat\", \"/proc/stat\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_DISTRO_PREFIX \"/proc/stat\", \"/proc/stat\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_DISTRO_COMPAT_PREFIX \"\\\\proc\\\\stat\", \"/proc/stat\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(WSLPATH_DISTRO_COMPAT_PREFIX, \"/\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"\\\\\\\\?\\\\C:\\\\Users\", \"/mnt/c/Users\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"\\\\\\\\?\\\\C:\\\\Users\\\\\", \"/mnt/c/Users/\", true));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\".\", \".\", true));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nint WslPathTestLxToWinPath(PLXT_ARGS Args)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine tests wslpath on internal Linux paths.\r\n\r\nArguments:\r\n\r\n    Args - Supplies the command line arguments.\r\n\r\nReturn Value:\r\n\r\n    Returns 0 on success, -1 on failure.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    int Result;\r\n\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/\", WSLPATH_DISTRO_PREFIX \"\\\\\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/root\", WSLPATH_DISTRO_PREFIX \"\\\\root\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/proc/stat\", WSLPATH_DISTRO_PREFIX \"\\\\proc\\\\stat\", false));\r\n    LxtCheckResult(LxtCheckWslPathTranslation(\"/proc/1/\", WSLPATH_DISTRO_PREFIX \"\\\\proc\\\\1\\\\\", false));\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n"
  },
  {
    "path": "test/linux/unit_tests/xattr.c",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Xattr.c\r\n\r\nAbstract:\r\n\r\n    This file is a xattr test.\r\n\r\n--*/\r\n\r\n#include \"lxtcommon.h\"\r\n#include \"unittests.h\"\r\n#include \"lxtutil.h\"\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <sys/types.h>\r\n#include <sys/wait.h>\r\n#include <sys/ptrace.h>\r\n#include <sys/syscall.h>\r\n#include <sys/uio.h>\r\n#include <sched.h>\r\n#include <signal.h>\r\n#include <time.h>\r\n#include <limits.h>\r\n#include <sys/mman.h>\r\n#include <sys/prctl.h>\r\n#include <sys/user.h>\r\n#include <stddef.h>\r\n#include <fcntl.h>\r\n#include \"lxtfs.h\"\r\n#include <sys/xattr.h>\r\n#include <libmount/libmount.h>\r\n#include \"lxtmount.h\"\r\n\r\n#if !defined(__amd64__) && !defined(__aarch64__)\r\n\r\n#include <sys/capability.h>\r\n\r\n#else\r\n\r\n#include <sys/cdefs.h>\r\n#include <linux/capability.h>\r\n\r\n#define _LINUX_CAPABILITY_VERSION_3 0x20080522\r\n\r\n#endif\r\n\r\n#define LXT_NAME \"xattr\"\r\n#define LXT_NAME_DRVFS \"xattr_drvfs\"\r\n\r\n#define LXT_XATTR_MODE 0777\r\n#define LXT_XATTR_UID 1004\r\n#define LXT_XATTR_GID 1004\r\n#define LXT_XATTR_TEST_PARENT \"/data/xattrtest\"\r\n#define LXT_XATTR_ACCESS_FILE_PATH LXT_XATTR_TEST_PARENT \"/xattrAccessFile\"\r\n#define LXT_XATTR_FILE_PATH LXT_XATTR_TEST_PARENT \"/xattrFile\"\r\n#define LXT_XATTR_DIR_PATH LXT_XATTR_TEST_PARENT \"/xattrDir\"\r\n#define LXT_XATTR_LINK_PATH LXT_XATTR_TEST_PARENT \"/xattrLink\"\r\n#define LXT_XATTR_FIFO_PATH LXT_XATTR_TEST_PARENT \"/xattrFifo\"\r\n#define LXT_XATTR_SIZE_MAX (4040)\r\n#define LXT_XATTR_CASE_SENSITIVE_LENGTH (sizeof(LXT_XATTR_CASE_SENSITIVE) - 1)\r\n#define LXT_XATTR_TEST_VALUE \"test\"\r\n#define LXT_XATTR_TEST_LENGTH (sizeof(LXT_XATTR_TEST_VALUE) - 1)\r\n\r\n#define LXT_XATTR_PATH_COUNT (LXT_COUNT_OF(g_XattrPaths))\r\n\r\nint XattrTestCreatePaths(int* Fds);\r\n\r\nvoid XattrTestDeletePaths(int* Fds);\r\n\r\nLXT_VARIATION_HANDLER XattrAccessTest;\r\nLXT_VARIATION_HANDLER XattrListTest;\r\nLXT_VARIATION_HANDLER XattrGetTest;\r\nLXT_VARIATION_HANDLER XattrRemoveTest;\r\nLXT_VARIATION_HANDLER XattrSetTest;\r\n\r\n//\r\n// Global constants\r\n//\r\n\r\nstatic const LXT_VARIATION g_LxtVariations[] = {\r\n    {\"xattr list\", XattrListTest}, {\"xattr get\", XattrGetTest}, {\"xattr set\", XattrSetTest}, {\"xattr remove\", XattrRemoveTest}, {\"xattr access\", XattrAccessTest}};\r\n\r\nstatic const char* g_XattrPaths[] = {\r\n    LXT_XATTR_FILE_PATH, LXT_XATTR_DIR_PATH, LXT_XATTR_LINK_PATH, LXT_XATTR_FIFO_PATH, \"/dev/null\", \"/proc/cpuinfo\"};\r\n\r\nssize_t GetXattr(const char* Path, const char* Name, void* Value, size_t Size)\r\n{\r\n    size_t Result = LxtGetxattr(Path, Name, Value, Size);\r\n    if (Result < 0)\r\n    {\r\n        int SavedErrno = errno;\r\n        ssize_t SavedResult = Result;\r\n        Result = LxtGetxattr(Path, Name, NULL, 0);\r\n        LxtLogInfo(\"getxattr(%s, %s, NULL, 0) = %Iu\", Path, Name, Result);\r\n        errno = SavedErrno;\r\n        Result = SavedResult;\r\n    }\r\n\r\n    return Result;\r\n}\r\n\r\nint XattrTestEntry(int Argc, char* Argv[])\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    LXT_ARGS Args;\r\n    int Index;\r\n    const char* Name;\r\n    int Result;\r\n    bool UseDrvFs;\r\n\r\n    Name = LXT_NAME;\r\n    UseDrvFs = false;\r\n    for (Index = 1; Index < Argc; Index += 1)\r\n    {\r\n        if (strcmp(Argv[Index], \"drvfs\") == 0)\r\n        {\r\n            UseDrvFs = true;\r\n            Name = LXT_NAME_DRVFS;\r\n            break;\r\n        }\r\n    }\r\n\r\n    LxtCheckResult(LxtInitialize(Argc, Argv, &Args, Name));\r\n    LxtCheckResult(LxtFsTestSetup(&Args, LXT_XATTR_TEST_PARENT, \"/xattrtest\", UseDrvFs));\r\n\r\n    LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));\r\n\r\nErrorExit:\r\n    LxtFsTestCleanup(LXT_XATTR_TEST_PARENT, \"/xattrtest\", UseDrvFs);\r\n    LxtUninitialize();\r\n    return !LXT_SUCCESS(Result);\r\n}\r\n\r\nint XattrTestCreatePaths(int* Fds)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    int Fd;\r\n    int Index;\r\n    int Result;\r\n\r\n    memset(Fds, -1, sizeof(*Fds) * LXT_XATTR_PATH_COUNT);\r\n    XattrTestDeletePaths(NULL);\r\n    LxtCheckErrno(Fd = creat(LXT_XATTR_FILE_PATH, LXT_XATTR_MODE));\r\n    LxtClose(Fd);\r\n    LxtCheckErrno(mkdir(LXT_XATTR_DIR_PATH, LXT_XATTR_MODE));\r\n    LxtCheckErrno(symlink(LXT_XATTR_FILE_PATH, LXT_XATTR_LINK_PATH));\r\n    LxtCheckErrno(mkfifo(LXT_XATTR_FIFO_PATH, LXT_XATTR_MODE));\r\n    for (Index = 0; Index < LXT_XATTR_PATH_COUNT; ++Index)\r\n    {\r\n        Fds[Index] = open(g_XattrPaths[Index], O_RDONLY | O_NONBLOCK);\r\n    }\r\n\r\nErrorExit:\r\n    return Result;\r\n}\r\n\r\nvoid XattrTestDeletePaths(int* Fds)\r\n\r\n{\r\n\r\n    int Index;\r\n\r\n    if (Fds != NULL)\r\n    {\r\n        for (Index = 0; Index < LXT_XATTR_PATH_COUNT; ++Index)\r\n        {\r\n            if (Fds[Index] != -1)\r\n            {\r\n                LxtClose(Fds[Index]);\r\n                Fds[Index] = -1;\r\n            }\r\n        }\r\n    }\r\n\r\n    unlink(LXT_XATTR_FILE_PATH);\r\n    rmdir(LXT_XATTR_DIR_PATH);\r\n    unlink(LXT_XATTR_LINK_PATH);\r\n    unlink(LXT_XATTR_FIFO_PATH);\r\n    return;\r\n}\r\n\r\nint XattrListTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[1024];\r\n    ssize_t ExpectedSize;\r\n    int Fds[LXT_XATTR_PATH_COUNT];\r\n    int Index;\r\n    ssize_t Result;\r\n    ssize_t Size;\r\n\r\n    LxtCheckErrno(XattrTestCreatePaths(Fds));\r\n    for (Index = 0; Index < (LXT_XATTR_PATH_COUNT - 1); ++Index)\r\n    {\r\n\r\n        LxtLogInfo(\"%s\", g_XattrPaths[Index]);\r\n\r\n        //\r\n        // Check that the xattr syscalls return 0 for all entries to indicate no\r\n        // attributes are present.\r\n        //\r\n        // N.B. DrvFs (not in WslFs mode) will return the case sensitivity\r\n        //      attribute for directories only.\r\n        //\r\n\r\n        if ((Index == 1) && ((g_LxtFsInfo.FsType == LxtFsTypeDrvFs) || (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)))\r\n        {\r\n            ExpectedSize = LXT_XATTR_CASE_SENSITIVE_LENGTH + 1;\r\n        }\r\n        else\r\n        {\r\n            ExpectedSize = 0;\r\n        }\r\n\r\n        LxtCheckErrno(Size = LxtListxattr(g_XattrPaths[Index], Buffer, sizeof(Buffer)));\r\n        LxtCheckEqual(Size, ExpectedSize, \"%lld\");\r\n        LxtCheckErrno(Size = LxtLlistxattr(g_XattrPaths[Index], Buffer, sizeof(Buffer)));\r\n        LxtCheckEqual(Size, ExpectedSize, \"%lld\");\r\n        LxtCheckErrno(Size = LxtFlistxattr(Fds[Index], Buffer, sizeof(Buffer)));\r\n        LxtCheckEqual(Size, ExpectedSize, \"%lld\");\r\n\r\n        //\r\n        // Check that the buffer and size are not validated if there are no\r\n        // attributes.\r\n        //\r\n\r\n        if (ExpectedSize == 0)\r\n        {\r\n            LxtCheckErrnoZeroSuccess(LxtListxattr(g_XattrPaths[Index], 0x1, sizeof(Buffer)));\r\n            LxtCheckErrnoZeroSuccess(LxtLlistxattr(g_XattrPaths[Index], 0x1, sizeof(Buffer)));\r\n            LxtCheckErrnoZeroSuccess(LxtFlistxattr(Fds[Index], 0x1, sizeof(Buffer)));\r\n            LxtCheckErrnoZeroSuccess(LxtListxattr(g_XattrPaths[Index], Buffer, -1));\r\n            LxtCheckErrnoZeroSuccess(LxtLlistxattr(g_XattrPaths[Index], Buffer, -1));\r\n            LxtCheckErrnoZeroSuccess(LxtFlistxattr(Fds[Index], Buffer, -1));\r\n        }\r\n    }\r\n\r\n    //\r\n    // Check for invalid parameters.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtListxattr(0x1, Buffer, sizeof(Buffer)), EFAULT);\r\n    LxtCheckErrnoFailure(LxtLlistxattr(0x1, Buffer, sizeof(Buffer)), EFAULT);\r\n    LxtCheckErrnoFailure(LxtFlistxattr(-1, Buffer, sizeof(Buffer)), EBADF);\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    XattrTestDeletePaths(Fds);\r\n    return (int)Result;\r\n}\r\n\r\nint XattrGetTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[1024];\r\n    int Failures[] = {ENODATA, ENODATA, ENODATA, ENODATA, ENODATA, ENOTSUP, ENOTSUP};\r\n    int Fds[LXT_XATTR_PATH_COUNT];\r\n    int Index;\r\n    char* Name = \"security.capability\";\r\n    ssize_t Result;\r\n\r\n    LxtCheckErrno(XattrTestCreatePaths(Fds));\r\n    for (Index = 0; Index < LXT_XATTR_PATH_COUNT; ++Index)\r\n    {\r\n\r\n        //\r\n        // Check that the xattr syscalls return 0 for all entries to indicate no\r\n        // attributes are present.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[Index], Name, Buffer, sizeof(Buffer)), Failures[Index]);\r\n        LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[Index], Name, Buffer, sizeof(Buffer)), Failures[Index]);\r\n        LxtCheckErrnoFailure(LxtFgetxattr(Fds[Index], Name, Buffer, sizeof(Buffer)), Failures[Index]);\r\n\r\n        //\r\n        // Check that the buffer and size are not validated if there are no\r\n        // attributes.\r\n        //\r\n\r\n        LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[Index], Name, 0x1, sizeof(Buffer)), Failures[Index]);\r\n        LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[Index], Name, 0x1, sizeof(Buffer)), Failures[Index]);\r\n        LxtCheckErrnoFailure(LxtFgetxattr(Fds[Index], Name, 0x1, sizeof(Buffer)), Failures[Index]);\r\n        LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[Index], Name, Buffer, -1), Failures[Index]);\r\n        LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[Index], Name, Buffer, -1), Failures[Index]);\r\n        LxtCheckErrnoFailure(LxtFgetxattr(Fds[Index], Name, Buffer, -1), Failures[Index]);\r\n    }\r\n\r\n    //\r\n    // Check for invalid parameters.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtGetxattr(0x1, Name, Buffer, sizeof(Buffer)), EFAULT);\r\n    LxtCheckErrnoFailure(LxtLgetxattr(0x1, Name, Buffer, sizeof(Buffer)), EFAULT);\r\n    LxtCheckErrnoFailure(LxtFgetxattr(-1, Name, Buffer, sizeof(Buffer)), EBADF);\r\n\r\n    LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer)), EFAULT);\r\n    LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer)), EFAULT);\r\n    LxtCheckErrnoFailure(LxtFgetxattr(Fds[0], 0x1, Buffer, sizeof(Buffer)), EFAULT);\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], \"invalid.name\", Buffer, sizeof(Buffer)), ENOTSUP);\r\n        LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[0], \"invalid.name\", Buffer, sizeof(Buffer)), ENOTSUP);\r\n        LxtCheckErrnoFailure(LxtFgetxattr(Fds[0], \"invalid.name\", Buffer, sizeof(Buffer)), ENOTSUP);\r\n    }\r\n    else\r\n    {\r\n        // The virtioFs implementation does not restrict the allowed attribute names, but these values will not be present.\r\n        LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], \"invalid.name\", Buffer, sizeof(Buffer)), ENODATA);\r\n        LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[0], \"invalid.name\", Buffer, sizeof(Buffer)), ENODATA);\r\n        LxtCheckErrnoFailure(LxtFgetxattr(Fds[0], \"invalid.name\", Buffer, sizeof(Buffer)), ENODATA);\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    XattrTestDeletePaths(Fds);\r\n    return (int)Result;\r\n}\r\n\r\nint XattrRemoveTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char Buffer[1024];\r\n    int Fds[LXT_XATTR_PATH_COUNT];\r\n    int Index;\r\n    char* Name = \"security.capability\";\r\n    int Result;\r\n    char TestData[] = \"test\";\r\n\r\n    LxtCheckErrno(XattrTestCreatePaths(Fds));\r\n    for (Index = 0; Index < LXT_XATTR_PATH_COUNT - 1; ++Index)\r\n    {\r\n\r\n        LxtLogInfo(\"%s %s\", g_XattrPaths[Index], Name);\r\n\r\n        //\r\n        // Check that the xattr syscalls return the correct error code for all\r\n        // entries to indicate no attributes are present.\r\n        //\r\n        LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[Index], Name), ENODATA);\r\n        LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[Index], Name), ENODATA);\r\n        LxtCheckErrnoFailure(LxtFremovexattr(Fds[Index], Name), ENODATA);\r\n    }\r\n\r\n    //\r\n    // Create three ea's and delete them in various orders.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.1\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.2\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.3\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.1\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.2\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.3\"));\r\n\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.1\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.2\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.3\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.1\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.3\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.2\"));\r\n\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.1\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.2\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.3\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.2\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.1\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.3\"));\r\n\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.1\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.2\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.3\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.2\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.3\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.1\"));\r\n\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.1\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.2\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.3\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.3\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.1\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.2\"));\r\n\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.1\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.2\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.3\", TestData, sizeof(TestData), 0));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.3\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.2\"));\r\n    LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], \"user.1\"));\r\n\r\n    //\r\n    // Check for invalid parameters.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtRemovexattr(0x1, Name), EFAULT);\r\n    LxtCheckErrnoFailure(LxtLremovexattr(0x1, Name), EFAULT);\r\n    LxtCheckErrnoFailure(LxtFremovexattr(-1, Name), EBADF);\r\n\r\n    LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[0], 0x1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[0], 0x1), EFAULT);\r\n    LxtCheckErrnoFailure(LxtFremovexattr(Fds[0], 0x1), EFAULT);\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[0], \"invalid.name\"), ENOTSUP);\r\n        LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[0], \"invalid.name\"), ENOTSUP);\r\n        LxtCheckErrnoFailure(LxtFremovexattr(Fds[0], \"invalid.name\"), ENOTSUP);\r\n    }\r\n    else\r\n    {\r\n        // The virtioFs implementation does not restrict the allowed attribute names, but these values will not be present.\r\n        LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[0], \"invalid.name\"), ENODATA);\r\n        LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[0], \"invalid.name\"), ENODATA);\r\n        LxtCheckErrnoFailure(LxtFremovexattr(Fds[0], \"invalid.name\"), ENODATA);\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    XattrTestDeletePaths(Fds);\r\n    return Result;\r\n}\r\n\r\nint XattrSetTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    char* AttrName;\r\n    char Buffer[1024];\r\n    int Count;\r\n    char* DynamicBuffer;\r\n    int DynamicBufferSize;\r\n    int Fds[LXT_XATTR_PATH_COUNT];\r\n    int Index;\r\n    char* Name = \"security.foo\";\r\n    int NameIndex;\r\n    ssize_t Result;\r\n    int Size;\r\n    struct\r\n    {\r\n        char Name[256];\r\n        bool Found;\r\n    } TestXattrs[103];\r\n\r\n    int TotalCount;\r\n    int TotalSize;\r\n\r\n    DynamicBuffer = NULL;\r\n    LxtCheckErrno(XattrTestCreatePaths(Fds));\r\n\r\n    //\r\n    // Check for invalid parameters.\r\n    //\r\n\r\n    LxtCheckErrnoFailure(LxtSetxattr(0x1, Name, Buffer, sizeof(Buffer), 0), EFAULT);\r\n    LxtCheckErrnoFailure(LxtLsetxattr(0x1, Name, Buffer, sizeof(Buffer), 0), EFAULT);\r\n    LxtCheckErrnoFailure(LxtFsetxattr(-1, Name, Buffer, sizeof(Buffer), 0), EBADF);\r\n\r\n    LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer), 0), EFAULT);\r\n    LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer), 0), EFAULT);\r\n    LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], 0x1, Buffer, sizeof(Buffer), 0), EFAULT);\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)\r\n    {\r\n        LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], \"invalid.name\", Buffer, sizeof(Buffer), 0), ENOTSUP);\r\n        LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], \"invalid.name\", Buffer, sizeof(Buffer), 0), ENOTSUP);\r\n        LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], \"invalid.name\", Buffer, sizeof(Buffer), 0), ENOTSUP);\r\n\r\n        LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], \"security.\", Buffer, sizeof(Buffer), 0), EINVAL);\r\n        LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], \"security.\", Buffer, sizeof(Buffer), 0), EINVAL);\r\n        LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], \"security.\", Buffer, sizeof(Buffer), 0), EINVAL);\r\n\r\n        LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], \"trusted.\", Buffer, sizeof(Buffer), 0), EINVAL);\r\n        LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], \"trusted.\", Buffer, sizeof(Buffer), 0), EINVAL);\r\n        LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], \"trusted.\", Buffer, sizeof(Buffer), 0), EINVAL);\r\n\r\n        LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], \"user.\", Buffer, sizeof(Buffer), 0), EINVAL);\r\n        LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], \"user.\", Buffer, sizeof(Buffer), 0), EINVAL);\r\n        LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], \"user.\", Buffer, sizeof(Buffer), 0), EINVAL);\r\n    }\r\n    else\r\n    {\r\n        // The virtioFs implementation does not restrict the allowed attribute names.\r\n        LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"invalid.name\", Buffer, sizeof(Buffer), 0));\r\n        LxtCheckErrno(LxtLsetxattr(g_XattrPaths[0], \"invalid.name\", Buffer, sizeof(Buffer), 0));\r\n        LxtCheckErrno(LxtFsetxattr(Fds[0], \"invalid.name\", Buffer, sizeof(Buffer), 0));\r\n    }\r\n\r\n    LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], \"system.\", Buffer, sizeof(Buffer), 0), ENOTSUP);\r\n    LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], \"system.\", Buffer, sizeof(Buffer), 0), ENOTSUP);\r\n    LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], \"system.\", Buffer, sizeof(Buffer), 0), ENOTSUP);\r\n\r\n    AttrName =\r\n        \"user.\"\r\n        \"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\"\r\n        \"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\"\r\n        \"ooooooooooo\";\r\n    LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], AttrName, Buffer, sizeof(Buffer), 0), ERANGE);\r\n    LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], AttrName, Buffer, sizeof(Buffer), 0), ERANGE);\r\n    LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], AttrName, Buffer, sizeof(Buffer), 0), ERANGE);\r\n\r\n    LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], Name, Buffer, sizeof(Buffer), 10), EINVAL);\r\n\r\n    //\r\n    // Create an attribute and read it back, using various buffer sizes.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], \"user.test\", \"1234\", 4, 0));\r\n    LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], \"user.test\", NULL, 0));\r\n    LxtCheckEqual(Size, 4, \"%d\");\r\n    LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], \"user.test\", Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Size, 4, \"%d\");\r\n    Buffer[4] = '\\0';\r\n    LxtCheckStringEqual(Buffer, \"1234\");\r\n    LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], \"user.test\", Buffer, 4));\r\n    LxtCheckEqual(Size, 4, \"%d\");\r\n    Buffer[4] = '\\0';\r\n    LxtCheckStringEqual(Buffer, \"1234\");\r\n    LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], \"user.test\", Buffer, 3), ERANGE);\r\n    LxtCheckErrnoZeroSuccess(LxtRemovexattr(g_XattrPaths[0], \"user.test\"));\r\n\r\n    //\r\n    // Create a max length attribute, and ensure that it exists afterwards.\r\n    //\r\n    // N.B. Max length is based on ext4 limits; LxFs and DrvFs allow bigger\r\n    //      attributes.\r\n    //\r\n\r\n    DynamicBuffer = malloc(LXT_XATTR_SIZE_MAX);\r\n    for (Index = 0; Index < LXT_XATTR_SIZE_MAX; Index += 1)\r\n    {\r\n        DynamicBuffer[Index] = (char)Index;\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], \"user.0\", DynamicBuffer, LXT_XATTR_SIZE_MAX, 0));\r\n    LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], \"user.0\", NULL, 0));\r\n    LxtCheckEqual(Size, LXT_XATTR_SIZE_MAX, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(LxtRemovexattr(g_XattrPaths[0], \"user.0\"));\r\n\r\n    Count = 0;\r\n    memset(TestXattrs, 0, sizeof(TestXattrs));\r\n\r\n    //\r\n    // Create a zero length attribute.\r\n    //\r\n\r\n    LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], \"user.zero\", NULL, 0, 0));\r\n    LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], \"user.zero\", NULL, 0));\r\n    strncpy(TestXattrs[Count].Name, \"user.zero\", sizeof(TestXattrs[Count].Name) - 1);\r\n    Count += 1;\r\n\r\n    //\r\n    // Create an attribute with the maximum name length.\r\n    //\r\n\r\n    if (g_LxtFsInfo.Flags.DrvFsBehavior != 0)\r\n    {\r\n        AttrName =\r\n            \"user.\"\r\n            \"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\"\r\n            \"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\"\r\n            \"oooooooooooooo\";\r\n    }\r\n    else\r\n    {\r\n        AttrName =\r\n            \"user.\"\r\n            \"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\"\r\n            \"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\"\r\n            \"oooooooooooooooooo\";\r\n    }\r\n\r\n    LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], AttrName, NULL, 0, 0));\r\n    LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], AttrName, NULL, 0));\r\n    strncpy(TestXattrs[Count].Name, AttrName, sizeof(TestXattrs[Count].Name) - 1);\r\n    Count += 1;\r\n\r\n    //\r\n    // A bunch of attributes to exercise listing.\r\n    //\r\n\r\n    for (int Index = 0; Index < 100; Index += 1)\r\n    {\r\n        snprintf(TestXattrs[Count].Name, sizeof(TestXattrs[Count].Name), \"user.test%d\", Index);\r\n\r\n        LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], TestXattrs[Count].Name, DynamicBuffer, 10, 0));\r\n\r\n        Count += 1;\r\n    }\r\n\r\n    //\r\n    // Check the behavior of XATTR_CREATE.\r\n    //\r\n\r\n    LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], \"user.test0\", NULL, 0));\r\n    LxtCheckEqual(Size, 10, \"%d\");\r\n    LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], \"user.test0\", DynamicBuffer, 15, XATTR_CREATE), EEXIST);\r\n\r\n    LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], \"user.test0\", NULL, 0));\r\n    LxtCheckEqual(Size, 10, \"%d\");\r\n    LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], \"user.new\", DynamicBuffer, 15, XATTR_CREATE));\r\n\r\n    LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], \"user.new\", NULL, 0));\r\n    LxtCheckEqual(Size, 15, \"%d\");\r\n    strncpy(TestXattrs[Count].Name, \"user.new\", sizeof(TestXattrs[Count].Name) - 1);\r\n    Count += 1;\r\n    //\r\n    // Check the behavior of XATTR_REPLACE.\r\n    //\r\n\r\n    LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], \"user.test0\", NULL, 0));\r\n    LxtCheckEqual(Size, 10, \"%d\");\r\n    LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], \"user.new2\", DynamicBuffer, 15, XATTR_REPLACE), ENODATA);\r\n\r\n    LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], \"user.new2\", NULL, 0), ENODATA);\r\n\r\n    LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], \"user.new\", DynamicBuffer, 10, XATTR_REPLACE));\r\n\r\n    LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], \"user.new\", NULL, 0));\r\n    LxtCheckEqual(Size, 10, \"%d\");\r\n\r\n    //\r\n    // Set a zero-length extended attribute with XATTR_REPLACE.\r\n    //\r\n    // N.B. Plan 9 does not support this, as it treats this operation like a\r\n    //      remove.\r\n    //\r\n\r\n    if (g_LxtFsInfo.FsType != LxtFsTypePlan9)\r\n    {\r\n        LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], \"user.new\", NULL, 0, XATTR_REPLACE));\r\n\r\n        LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], \"user.new\", NULL, 0));\r\n        LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], \"user.new\", \"\", 0, XATTR_REPLACE));\r\n\r\n        LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], \"user.new\", NULL, 0));\r\n    }\r\n\r\n    //\r\n    // List the attributes.\r\n    //\r\n\r\n    TotalCount = Count;\r\n    for (Index = 0; Index < 10; Index += 1)\r\n    {\r\n        LxtCheckErrno(DynamicBufferSize = LxtListxattr(g_XattrPaths[0], 0, NULL));\r\n        LxtLogInfo(\"listxattr returned %d\", DynamicBufferSize);\r\n        DynamicBuffer = realloc(DynamicBuffer, DynamicBufferSize);\r\n        if (DynamicBuffer == NULL)\r\n        {\r\n            Result = LXT_RESULT_FAILURE;\r\n            LxtLogError(\"realloc(%p %d) failed\", DynamicBuffer, DynamicBufferSize);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Ensure that the number of extended attributes returned matches the number created.\r\n        //\r\n\r\n        DynamicBufferSize = LxtListxattr(g_XattrPaths[0], DynamicBuffer, DynamicBufferSize);\r\n        if (DynamicBufferSize > 0)\r\n        {\r\n            break;\r\n        }\r\n        else if (errno != ERANGE)\r\n        {\r\n            LxtLogError(\"listxattr returned %d\", errno);\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // Sleep before retrying.\r\n        //\r\n\r\n        sleep(1);\r\n    }\r\n\r\n    if (DynamicBufferSize < 0)\r\n    {\r\n        LxtLogError(\"listxattr returned %d\", errno);\r\n        Result = LXT_RESULT_FAILURE;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    for (Index = 0; Index < DynamicBufferSize; Index += strlen(&DynamicBuffer[Index]) + 1)\r\n    {\r\n        for (NameIndex = 0; NameIndex < TotalCount; NameIndex += 1)\r\n        {\r\n            if (strcmp(TestXattrs[NameIndex].Name, &DynamicBuffer[Index]) == 0)\r\n            {\r\n                if (TestXattrs[NameIndex].Found != false)\r\n                {\r\n                    LxtLogError(\"Duplicate attribute: %s\", &DynamicBuffer[Index]);\r\n                    Result = LXT_RESULT_FAILURE;\r\n                    goto ErrorExit;\r\n                }\r\n\r\n                TestXattrs[NameIndex].Found = true;\r\n                break;\r\n            }\r\n        }\r\n\r\n        if (NameIndex == TotalCount)\r\n        {\r\n            LxtLogError(\"Unknown attribute in listing: %s\", &DynamicBuffer[Index]);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n\r\n        LxtRemovexattr(g_XattrPaths[0], &DynamicBuffer[Index]);\r\n        Count -= 1;\r\n    }\r\n\r\n    for (NameIndex = 0; NameIndex < TotalCount; NameIndex += 1)\r\n    {\r\n        if (TestXattrs[NameIndex].Found == false)\r\n        {\r\n            LxtLogError(\"Attribute missing from listing: %s\", TestXattrs[NameIndex].Name);\r\n            Result = LXT_RESULT_FAILURE;\r\n            goto ErrorExit;\r\n        }\r\n    }\r\n\r\n    LxtCheckEqual(Count, 0, \"%d\");\r\n    LxtCheckEqual(LxtListxattr(g_XattrPaths[0], DynamicBuffer, DynamicBufferSize), 0, \"%d\");\r\n\r\n    //\r\n    // Ensure that two extended attributes with the same name but different\r\n    // cases can be created.\r\n    //\r\n    // N.B. DrvFs, WslFs and Plan 9 do not support this.\r\n    //\r\n\r\n    if (g_LxtFsInfo.Flags.DrvFsBehavior == 0)\r\n    {\r\n        LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.foo\", NULL, 0, 0));\r\n        LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], \"user.FOO\", NULL, 0, 0));\r\n        LxtCheckErrno(Result = LxtListxattr(g_XattrPaths[0], Buffer, sizeof(Buffer)));\r\n        Count = 0;\r\n        for (Index = 0; Index < Result; Index += strlen(&Buffer[Index]) + 1)\r\n        {\r\n            LxtLogInfo(\"%s\", &Buffer[Index]);\r\n            Count += 1;\r\n        }\r\n\r\n        LxtCheckEqual(Count, 2, \"%d\");\r\n    }\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    XattrTestDeletePaths(Fds);\r\n    if (DynamicBuffer != NULL)\r\n    {\r\n        free(DynamicBuffer);\r\n    }\r\n\r\n    return (int)Result;\r\n}\r\n\r\nint XattrAccessTest(PLXT_ARGS Args)\r\n\r\n/*++\r\n--*/\r\n\r\n{\r\n\r\n    // Example system.posix_acl_access structure created by \"setfacl -m u:root:r\"\r\n    char AclAccess[] = {2, 0, 0,    0,    1,    0,    6,  0, 0xff, 0xff, 0xff, 0xff, 2,    0,    4,  0, 0, 0, 0,  0,  4,  0,\r\n                        4, 0, 0xff, 0xff, 0xff, 0xff, 16, 0, 4,    0,    0xff, 0xff, 0xff, 0xff, 32, 0, 4, 0, -1, -1, -1, -1};\r\n\r\n    // Example security.capability structure created by \"setcap cap_net_raw+ep\"\r\n    char Capability[] = {1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\r\n\r\n    char Buffer[1024];\r\n    struct __user_cap_data_struct CapData[2];\r\n    struct __user_cap_header_struct CapHeader;\r\n    int ChildPid;\r\n    int Fd;\r\n    ssize_t Result;\r\n\r\n    ChildPid = -1;\r\n    Fd = -1;\r\n    LxtCheckErrno(Fd = creat(LXT_XATTR_ACCESS_FILE_PATH, 0777));\r\n\r\n    /*\r\n\r\n    //\r\n    // TODO: Enable this test variation once extended attributes in the system\r\n    // namespace are supported.\r\n    //\r\n\r\n    //\r\n    // Set the system.posix_acl_access EA and validate access to get, set, and list.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", AclAccess, sizeof(AclAccess), 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", Buffer, sizeof(Buffer)));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0) {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(LXT_XATTR_GID));\r\n        LxtCheckErrno(setuid(LXT_XATTR_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_FOWNER)].permitted |= CAP_TO_MASK(CAP_FOWNER);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"system.posix_acl_access\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", Buffer, sizeof(Buffer)));\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", AclAccess, sizeof(AclAccess), 0));\r\n\r\n        //\r\n        // Drop the CAP_FOWNER capability and attempt again.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"system.posix_acl_access\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", Buffer, sizeof(Buffer)));\r\n        LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", AclAccess, sizeof(AclAccess), 0),\r\n    EPERM); Result = LXT_RESULT_SUCCESS; goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));\r\n\r\n    //\r\n    // Set the system.posix_acl_access EA and ensure it is able to be queried.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", AclAccess, sizeof(AclAccess), 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", Buffer, sizeof(Buffer)));\r\n    LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, LXT_XATTR_UID, LXT_XATTR_GID));\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0) {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(LXT_XATTR_GID));\r\n        LxtCheckErrno(setuid(LXT_XATTR_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"system.posix_acl_access\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", Buffer, sizeof(Buffer)));\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", AclAccess, sizeof(AclAccess), 0));\r\n\r\n        //\r\n        // Drop the CAP_DAC_OVERRIDE capability and attempt again.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"system.posix_acl_access\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", Buffer, sizeof(Buffer)));\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\", AclAccess, sizeof(AclAccess), 0));\r\n        Result = LXT_RESULT_SUCCESS;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, \"system.posix_acl_access\"));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777));\r\n\r\n    */\r\n\r\n    //\r\n    // Set the security.capability EA and validate access to get, set, and list.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Capability, sizeof(Capability), 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Result, sizeof(Capability), \"%Iu\");\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(LXT_XATTR_GID));\r\n        LxtCheckErrno(setuid(LXT_XATTR_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SETFCAP)].permitted |= CAP_TO_MASK(CAP_SETFCAP);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"security.capability\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Buffer, sizeof(Buffer)));\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Capability, sizeof(Capability), 0));\r\n\r\n        //\r\n        // Drop the CAP_SETFCAP capability and attempt again.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"security.capability\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Buffer, sizeof(Buffer)));\r\n        LxtCheckEqual(Result, sizeof(Capability), \"%Iu\");\r\n        LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Capability, sizeof(Capability), 0), EPERM);\r\n        Result = LXT_RESULT_SUCCESS;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Validate that the security.capability EA is removed when the file changes owners.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Capability, sizeof(Capability), 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Buffer, sizeof(Buffer)));\r\n    LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, 0, 0));\r\n    LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n    LxtCheckEqual(Result, 0, \"%Iu\");\r\n    LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Buffer, sizeof(Buffer)), ENODATA);\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Capability, sizeof(Capability), 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Result, sizeof(Capability), \"%Iu\");\r\n    LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\"));\r\n    LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n    LxtCheckEqual(Result, 0, \"%Iu\");\r\n    LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.capability\", Buffer, sizeof(Buffer)), ENODATA);\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777));\r\n\r\n    //\r\n    // Set the security.foo EA and validate access to get, set, and list.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Capability, sizeof(Capability), 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Result, sizeof(Capability), \"%Iu\");\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setgid(LXT_XATTR_GID));\r\n        LxtCheckErrno(setuid(LXT_XATTR_UID));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SYS_ADMIN)].permitted |= CAP_TO_MASK(CAP_SYS_ADMIN);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"security.foo\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Buffer, sizeof(Buffer)));\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Capability, sizeof(Capability), 0));\r\n\r\n        //\r\n        // Drop the CAP_SYS_ADMIN capability and attempt again.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"security.foo\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Buffer, sizeof(Buffer)));\r\n        LxtCheckEqual(Result, sizeof(Capability), \"%Iu\");\r\n        LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Capability, sizeof(Capability), 0), EPERM);\r\n        Result = LXT_RESULT_SUCCESS;\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    //\r\n    // Validate that the security.foo EA is not removed when the file changes owners.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Capability, sizeof(Capability), 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Buffer, sizeof(Buffer)));\r\n    LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, 0, 0));\r\n    LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n    LxtCheckEqual(Result, sizeof(\"security.foo\"), \"%Iu\");\r\n    LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Buffer, sizeof(Buffer)));\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Capability, sizeof(Capability), 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Buffer, sizeof(Buffer)));\r\n    LxtCheckEqual(Result, sizeof(Capability), \"%Iu\");\r\n    LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\"));\r\n    LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n    LxtCheckEqual(Result, 0, \"%Iu\");\r\n    LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"security.foo\", Buffer, sizeof(Buffer)), ENODATA);\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777));\r\n\r\n    //\r\n    // Set the user.foo EA and ensure it is able to be queried.\r\n    //\r\n\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", NULL, 0));\r\n    LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, LXT_XATTR_UID, LXT_XATTR_GID));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0700));\r\n\r\n    //\r\n    // Fork and change the child to a UID that does not own the file.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(setuid(LXT_XATTR_UID));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"user.foo\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", NULL, 0));\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0070));\r\n\r\n    //\r\n    // Fork and change the child to a UID that does not own the file.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n\r\n        //\r\n        // First try with the CAP_DAC_OVERRIDE capability.\r\n        //\r\n\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        LxtCheckErrno(setuid(LXT_XATTR_UID + 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"user.foo\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", NULL, 0));\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));\r\n\r\n        //\r\n        // Drop the CAP_DAC_OVERRIDE capability and attempt again.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"user.foo\"), \"%Iu\");\r\n        LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", NULL, 0), EACCES);\r\n        LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0), EACCES);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0007));\r\n\r\n    //\r\n    // Fork and change the child to a UID that does not own the file with the\r\n    // other bits set.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(setuid(LXT_XATTR_UID + 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"user.foo\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", NULL, 0));\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0002));\r\n\r\n    //\r\n    // Fork and change the child to a UID that does not own the file with the\r\n    // other bits set.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(setuid(LXT_XATTR_UID + 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"user.foo\"), \"%Iu\");\r\n        LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", NULL, 0), EACCES);\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0));\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0004));\r\n\r\n    //\r\n    // Fork and change the child to a UID that does not own the file with the\r\n    // other bits set.\r\n    //\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(setuid(LXT_XATTR_UID + 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"user.foo\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", NULL, 0));\r\n        LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0), EACCES);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n    LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, \"user.foo\"));\r\n    LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, 0, 0));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777));\r\n\r\n    //\r\n    // Test the trusted namespace, the caller requires the CAP_SYS_ADMIN\r\n    // capability to read to write EA's in the trusted namespace.\r\n    //\r\n\r\n    Buffer[0] = 'y';\r\n    LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"trusted.overlay.opaque\", Buffer, 1, 0));\r\n    LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"trusted.overlay.opaque\", Buffer, sizeof(Buffer)));\r\n    LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000));\r\n\r\n    LxtCheckErrno(ChildPid = fork());\r\n    if (ChildPid == 0)\r\n    {\r\n        LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        CapData[CAP_TO_INDEX(CAP_SYS_ADMIN)].permitted |= CAP_TO_MASK(CAP_SYS_ADMIN);\r\n        CapData[0].effective = CapData[0].permitted;\r\n        CapData[1].effective = CapData[1].permitted;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        LxtCheckEqual(Result, sizeof(\"trusted.overlay.opaque\"), \"%Iu\");\r\n        LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, \"trusted.overlay.opaque\", NULL, 0));\r\n        LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"trusted.overlay.opaque\", Buffer, 1, 0));\r\n\r\n        //\r\n        // Drop the CAP_SYS_ADMIN capability and attempt again.\r\n        //\r\n        // N.B. Unlike other namespaces, names in the trusted namespace will\r\n        //      not be returned if the caller does not have the correct permission.\r\n        //      This is file system specific, and Plan 9 does not do this.\r\n        //\r\n\r\n        memset(&CapData, 0, sizeof(CapData));\r\n        memset(&CapHeader, 0, sizeof(CapHeader));\r\n        CapHeader.version = _LINUX_CAPABILITY_VERSION_3;\r\n        LxtCheckErrno(LxtCapSet(&CapHeader, CapData));\r\n        LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL));\r\n        if ((g_LxtFsInfo.FsType == LxtFsTypePlan9) || (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs))\r\n        {\r\n            LxtCheckEqual(Result, sizeof(\"trusted.overlay.opaque\"), \"%Iu\");\r\n        }\r\n        else\r\n        {\r\n            LxtCheckEqual(Result, 0, \"%Iu\");\r\n        }\r\n\r\n        LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"trusted.overlay.opaque\", NULL, 0), ENODATA);\r\n        LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, \"trusted.overlay.opaque\", Buffer, 1, 0), EPERM);\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Wait for the child to exit.\r\n    //\r\n\r\n    LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));\r\n\r\n    Result = LXT_RESULT_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Fd != 0)\r\n    {\r\n        LxtClose(Fd);\r\n    }\r\n\r\n    if (ChildPid == 0)\r\n    {\r\n        _exit(Result);\r\n    }\r\n\r\n    unlink(LXT_XATTR_ACCESS_FILE_PATH);\r\n    return (int)Result;\r\n}\r\n"
  },
  {
    "path": "test/windows/CMakeLists.txt",
    "content": "set(SOURCES\r\n    SimpleTests.cpp\r\n    UnitTests.cpp\r\n    MountTests.cpp\r\n    NetworkTests.cpp\r\n    Plan9Tests.cpp\r\n    DrvFsTests.cpp\r\n    Common.cpp\r\n    PluginTests.cpp\r\n    PolicyTests.cpp\r\n    InstallerTests.cpp)\r\n\r\nset(HEADERS\r\n    Common.h\r\n    PluginTests.h\r\n    lxsstest.h)\r\n\r\nadd_compile_definitions(INLINE_TEST_METHOD_MARKUP)\r\n\r\nadd_library(wsltests SHARED ${SOURCES} ${HEADERS})\r\n\r\ntarget_precompile_headers(wsltests REUSE_FROM common)\r\ntarget_link_libraries(wsltests\r\n                      common\r\n                      ${TAEF_LINK_LIBRARIES}\r\n                      ${COMMON_LINK_LIBRARIES}\r\n                      ${MSI_LINK_LIBRARIES}\r\n                      ${HCS_LINK_LIBRARIES}\r\n                      ${SERVICE_LINK_LIBRARIES}\r\n                      VirtDisk.lib\r\n                      Wer.lib\r\n                      Dbghelp.lib\r\n                      sfc.lib)\r\n\r\nadd_dependencies(wsltests wslserviceidl)\r\nadd_subdirectory(testplugin)"
  },
  {
    "path": "test/windows/Common.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Common.cpp\r\n\r\nAbstract:\r\n\r\n    This contains common used definitions used for testing.\r\n\r\n--*/\r\n\r\n// includes\r\n\r\n#include \"precomp.h\"\r\n#include \"Common.h\"\r\n#include \"LxssDynamicFunction.h\"\r\n#include <tlhelp32.h>\r\n#include <werapi.h>\r\n#include <Dbghelp.h>\r\n\r\nusing namespace WEX::Logging;\r\nusing namespace WEX::Common;\r\nusing namespace WEX::TestExecution;\r\n\r\nMODULE_SETUP(ModuleSetup);\r\nMODULE_CLEANUP(ModuleCleanup);\r\n\r\n// Defines\r\n#define LXSS_LOGS_DIRECTORY L\"logs\"\r\n#define LXSS_TEST_DIRECTORY L\"\\\\data\\\\test\"\r\n#define LXSS_TEST_LOG_SEPARATOR_CHAR L\"&\"\r\n#define LXSS_DEFAULT_TIMEOUT (15 * 1000)\r\n\r\n//\r\n// The instance test timeout should roughly be the maximum time to start an\r\n// instance.\r\n//\r\n\r\n#define LXSS_INSTANCE_TEST_TIMEOUT (3 * 1000)\r\n\r\n//\r\n// The watchdog timeout is set to 3 hours.\r\n//\r\n\r\n#define LXSS_WATCHDOG_TIMEOUT (3 * 60 * 60 * 1000)\r\n#define LXSS_WATCHDOG_TIMEOUT_WINDOW 1000\r\n\r\n//\r\n// Global variables\r\n//\r\n\r\nstatic HANDLE g_OriginalStdout;\r\nstatic HANDLE g_OriginalStderr;\r\nstatic BOOL g_RelogEverything = TRUE;\r\nstatic bool g_LogDmesgAfterEachTest = false;\r\nstatic PTP_TIMER g_WatchdogTimer;\r\nstatic BOOL g_VmMode;\r\nstatic std::wstring g_originalConfig;\r\nstatic std::wstring g_originalDefaultDistro;\r\nstd::wstring g_dumpFolder;\r\nstd::optional<std::wstring> g_dumpToolPath;\r\nstatic bool g_enableWerReport = false;\r\nstatic std::wstring g_pipelineBuildId;\r\nstd::wstring g_testDistroPath;\r\n\r\nstd::pair<wil::unique_handle, wil::unique_handle> CreateSubprocessPipe(bool inheritRead, bool inheritWrite, DWORD bufferSize, _In_opt_ SECURITY_ATTRIBUTES* sa)\r\n{\r\n    wil::unique_handle read;\r\n    wil::unique_handle write;\r\n    THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&read, &write, sa, bufferSize));\r\n\r\n    if (inheritWrite)\r\n    {\r\n        THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(write.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));\r\n    }\r\n\r\n    if (inheritRead)\r\n    {\r\n        THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(read.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));\r\n    }\r\n\r\n    return {std::move(read), std::move(write)};\r\n}\r\n\r\n// LxsstuLaunchWsl\r\n\r\nDWORD\r\nLxsstuLaunchWsl(_In_opt_ LPCWSTR Arguments, _In_opt_ HANDLE StandardInput, _In_opt_ HANDLE StandardOutput, _In_opt_ HANDLE StandardError, _In_opt_ HANDLE Token, _In_ DWORD Flags)\r\n{\r\n    // Launch wsl.exe to handle the operation.\r\n    auto CommandLine = LxssGenerateWslCommandLine(Arguments);\r\n\r\n    return LxsstuRunCommand(CommandLine.data(), StandardInput, StandardOutput, StandardError, Token, Flags);\r\n}\r\n\r\nDWORD\r\nLxsstuLaunchWsl(_In_opt_ const std::wstring& Arguments, _In_opt_ HANDLE StandardInput, _In_opt_ HANDLE StandardOutput, _In_opt_ HANDLE StandardError, _In_opt_ HANDLE Token)\r\n{\r\n    return LxsstuLaunchWsl(Arguments.data(), StandardInput, StandardOutput, StandardError, Token);\r\n}\r\n\r\n// LxsstuLaunchWslAndCaptureOutput\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchWslAndCaptureOutput(\r\n    _In_ LPCWSTR Cmd, _In_ int ExpectedExitCode, _In_opt_ HANDLE StandardInput, _In_opt_ HANDLE Token, _In_ DWORD Flags, _In_ LPCWSTR EntryPoint)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Run a WSL command and capture its output.\r\n\r\nArguments:\r\n\r\n    Cmd - The command line to run.\r\n\r\n    ExpectedExitCode - The expected exit code from the child process.\r\n\r\n    StandardInput - Handle to the process's standard input\r\n\r\nReturn Value:\r\n\r\n    A pair of strings with stdout and stderr output.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    auto CommandLine = LxssGenerateWslCommandLine(Cmd, EntryPoint);\r\n    return LxsstuLaunchCommandAndCaptureOutput(CommandLine.data(), ExpectedExitCode, StandardInput, Token, Flags);\r\n}\r\n\r\n// LxssGenerateWslCommandLine\r\n\r\nstd::wstring LxssGenerateWslCommandLine(_In_opt_ LPCWSTR Arguments, _In_ LPCWSTR EntryPoint)\r\n{\r\n    std::wstring CommandLine;\r\n    THROW_IF_FAILED(wil::GetSystemDirectoryW(CommandLine));\r\n\r\n    CommandLine += L\"\\\\\";\r\n    CommandLine += EntryPoint;\r\n    if (ARGUMENT_PRESENT(Arguments))\r\n    {\r\n        CommandLine += L\" \";\r\n        CommandLine += Arguments;\r\n    }\r\n\r\n    return CommandLine;\r\n}\r\n\r\n// LxsstuLaunchWslAndCaptureOutput\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchWslAndCaptureOutput(\r\n    _In_ const std::wstring& Cmd, _In_ int ExpectedExitCode, _In_opt_ HANDLE StandardInput, _In_opt_ HANDLE Token, _In_ DWORD Flags, _In_ LPCWSTR EntryPoint)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Run a wsl command and return its output.\r\n\r\nArguments:\r\n\r\n    Cmd - Supplies the wsl command to run.\r\n\r\n    ExpectedExitCode - The expected exit code from the child process.\r\n\r\n    StandardInput - Handle to the process's standard input\r\n\r\nReturn Value:\r\n\r\n    The command's stdout and stderr output.\r\n\r\n--*/\r\n\r\n{\r\n    return LxsstuLaunchWslAndCaptureOutput(Cmd.data(), ExpectedExitCode, StandardInput, Token, Flags, EntryPoint);\r\n}\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchCommandAndCaptureOutput(_In_ LPWSTR Cmd, _In_ LPCSTR StandardInput, _In_opt_ HANDLE Token, _In_ DWORD Flags)\r\n{\r\n    const auto inputSize = static_cast<DWORD>(strlen(StandardInput));\r\n    auto [read, write] = CreateSubprocessPipe(true, false, inputSize);\r\n    THROW_IF_WIN32_BOOL_FALSE(WriteFile(write.get(), StandardInput, inputSize, nullptr, nullptr));\r\n    write.reset();\r\n\r\n    return LxsstuLaunchCommandAndCaptureOutput(Cmd, 0, read.get(), Token, Flags);\r\n}\r\n\r\n// LxsstuLaunchCommandAndCaptureOutputWithResult\r\n\r\nstd::tuple<std::wstring, std::wstring, int> LxsstuLaunchCommandAndCaptureOutputWithResult(\r\n    _In_ LPWSTR Cmd, _In_opt_ HANDLE StandardInput, _In_opt_ HANDLE Token, _In_ DWORD Flags)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Run a command and capture its output.\r\n\r\nArguments:\r\n\r\n    Cmd - The command line to run.\r\n\r\nReturn Value:\r\n\r\n    A pair of strings with stdout and stderr output.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    wsl::windows::common::SubProcess process(nullptr, Cmd);\r\n    process.SetStdHandles(StandardInput, nullptr, nullptr);\r\n    process.SetToken(Token);\r\n    process.SetFlags(Flags);\r\n\r\n    auto result = process.RunAndCaptureOutput();\r\n\r\n    return {result.Stdout, result.Stderr, result.ExitCode};\r\n}\r\n\r\n// LxsstuLaunchCommandAndCaptureOutput\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchCommandAndCaptureOutput(\r\n    _In_ LPWSTR Cmd, _In_ int ExpectedExitCode, _In_opt_ HANDLE StandardInput, _In_opt_ HANDLE Token, _In_ DWORD Flags)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Run a command and capture its output.\r\n\r\nArguments:\r\n\r\n    Cmd - The command line to run.\r\n\r\nReturn Value:\r\n\r\n    A pair of strings with stdout and stderr output.\r\n\r\n--*/\r\n\r\n{\r\n    auto [Out, Err, ExitCode] = LxsstuLaunchCommandAndCaptureOutputWithResult(Cmd, StandardInput, Token, Flags);\r\n    if (ExitCode != ExpectedExitCode)\r\n    {\r\n        THROW_HR_MSG(\r\n            E_UNEXPECTED,\r\n            \"Command \\\"%ls\\\"\"\r\n            \"returned unexpected exit code (%lu != %i). \"\r\n            \"Stdout: '%ls'\"\r\n            \"Stderr: '%ls'\",\r\n            Cmd,\r\n            ExitCode,\r\n            ExpectedExitCode,\r\n            Out.c_str(),\r\n            Err.c_str());\r\n    }\r\n\r\n    return std::make_pair(Out, Err);\r\n}\r\n\r\n// LxsstuRunCommand\r\n\r\nDWORD\r\nLxsstuRunCommand(_In_ LPWSTR Command, _In_opt_ HANDLE StandardInput, _In_opt_ HANDLE StandardOutput, _In_opt_ HANDLE StandardError, _In_opt_ HANDLE Token, _In_ DWORD Flags)\r\n{\r\n    const auto Process = LxsstuStartProcess(Command, StandardInput, StandardOutput, StandardError, Token, Flags);\r\n    return wsl::windows::common::SubProcess::GetExitCode(Process.get());\r\n}\r\n\r\n// LxsstuStartProcess\r\n\r\nwil::unique_handle LxsstuStartProcess(\r\n    _In_ LPWSTR Command, _In_opt_ HANDLE StandardInput, _In_opt_ HANDLE StandardOutput, _In_opt_ HANDLE StandardError, _In_opt_ HANDLE Token, _In_ DWORD Flags)\r\n{\r\n    wsl::windows::common::SubProcess process(nullptr, Command);\r\n\r\n    process.SetStdHandles(\r\n        ARGUMENT_PRESENT(StandardInput) ? StandardInput : GetStdHandle(STD_INPUT_HANDLE),\r\n        ARGUMENT_PRESENT(StandardOutput) ? StandardOutput : GetStdHandle(STD_OUTPUT_HANDLE),\r\n        ARGUMENT_PRESENT(StandardError) ? StandardError : GetStdHandle(STD_ERROR_HANDLE));\r\n\r\n    process.SetToken(Token);\r\n    process.SetFlags(Flags);\r\n\r\n    return process.Start();\r\n}\r\n\r\n// FileFromHandle\r\n\r\nwil::unique_file FileFromHandle(_Inout_ wil::unique_handle& Handle, _In_ const char* Mode)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Create a FILE from a handle.\r\n\r\nArguments:\r\n    Handle - The handle to create the FILE from.\r\n\r\n    Mode - The mode to create the FILE with.\r\n\r\nReturn Value:\r\n\r\n    The created FILE.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    using UniqueFd = wil::unique_any<int, decltype(_close), _close, wil::details::pointer_access_all, int, int, -1>;\r\n\r\n    UniqueFd Fd(_open_osfhandle(reinterpret_cast<intptr_t>(Handle.get()), 0));\r\n    if (Fd.get() < 0)\r\n    {\r\n        THROW_LAST_ERROR_MSG(\"_open_osfhandle failed\");\r\n    }\r\n\r\n    Handle.release();\r\n\r\n    wil::unique_file File(_fdopen(Fd.get(), Mode));\r\n    VERIFY_IS_NOT_NULL(File.get());\r\n    Fd.release();\r\n\r\n    return File;\r\n}\r\n\r\n// LxsstuInitialize\r\n\r\nBOOL LxsstuInitialize(__in BOOLEAN RunInstanceTests)\r\n{\r\n    wil::unique_hkey Key;\r\n    LRESULT Result;\r\n    BOOL Success;\r\n    DWORD Value;\r\n\r\n    Success = FALSE;\r\n\r\n    THROW_IF_FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED));\r\n\r\n    //\r\n    // Don't fail if CoInitializeSecurity has already been called.\r\n    //\r\n\r\n    const auto Hr = CoInitializeSecurity(\r\n        nullptr, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_STATIC_CLOAKING, 0);\r\n\r\n    THROW_HR_IF(Hr, FAILED(Hr) && Hr != RPC_E_TOO_LATE);\r\n\r\n    WSADATA Data;\r\n    THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &Data));\r\n\r\n    VERIFY_IS_TRUE(SetEnvironmentVariableW(L\"WSL_UTF8\", L\"1\"));\r\n\r\n    if (LxsstuVmMode() == FALSE)\r\n    {\r\n        Result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, LXSS_REGISTRY_PATH, 0, KEY_ALL_ACCESS, &Key);\r\n\r\n        if (Result != ERROR_SUCCESS)\r\n        {\r\n            LogError(\"RegOpenKeyEx %s failed with %Id\", LXSS_REGISTRY_PATH, Result);\r\n\r\n            goto InitializeEnd;\r\n        }\r\n\r\n        //\r\n        // Set the error level to critical so the driver will not break into kd\r\n        // while the test is running.\r\n        //\r\n\r\n        Value = LxErrorLevel_Critical;\r\n        Result = RegSetValueEx(Key.get(), LX_QUERY_REGISTRY_ERROR_LEVEL_SUBKEY, 0, REG_DWORD, (const PBYTE)&Value, sizeof(DWORD));\r\n\r\n        if (Result != ERROR_SUCCESS)\r\n        {\r\n            LogError(\"RegSetValueEx %s failed with %Id\", LX_QUERY_REGISTRY_ERROR_LEVEL_SUBKEY, Result);\r\n\r\n            goto InitializeEnd;\r\n        }\r\n\r\n        //\r\n        // Disable breaking on syscall failures.\r\n        //\r\n\r\n        Value = FALSE;\r\n        Result = RegSetValueEx(Key.get(), LX_QUERY_REGISTRY_BREAK_ON_SYSCALL_FAILURE_SUBKEY, 0, REG_DWORD, (const PBYTE)&Value, sizeof(DWORD));\r\n\r\n        if (Result != ERROR_SUCCESS)\r\n        {\r\n            LogError(\"RegSetValueEx %s failed with %Id\", LX_QUERY_REGISTRY_BREAK_ON_SYSCALL_FAILURE_SUBKEY, Result);\r\n\r\n            goto InitializeEnd;\r\n        }\r\n\r\n        //\r\n        // Enable lxbus root access.\r\n        //\r\n\r\n        Value = TRUE;\r\n        Result = RegSetValueEx(Key.get(), LX_QUERY_REGISTRY_ROOT_LXBUS_ACCESS, 0, REG_DWORD, (const PBYTE)&Value, sizeof(DWORD));\r\n\r\n        if (Result != ERROR_SUCCESS)\r\n        {\r\n            LogError(\"RegSetValueEx %s failed with %Id\", LX_QUERY_REGISTRY_ROOT_LXBUS_ACCESS, Result);\r\n\r\n            goto InitializeEnd;\r\n        }\r\n\r\n        //\r\n        // Enable mounting DrvFs with case=force.\r\n        //\r\n\r\n        Value = TRUE;\r\n        Result = RegSetValueEx(Key.get(), LX_QUERY_REGISTRY_DRVFS_ALLOW_FORCE_CASE_SENSITIVITY, 0, REG_DWORD, (const PBYTE)&Value, sizeof(DWORD));\r\n\r\n        if (Result != ERROR_SUCCESS)\r\n        {\r\n            LogError(\"RegSetValueEx %s failed with %Id\", LX_QUERY_REGISTRY_DRVFS_ALLOW_FORCE_CASE_SENSITIVITY, Result);\r\n\r\n            goto InitializeEnd;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        const auto LogDirectory = LxsstuGetTestDirectory() + L\"\\\\log\";\r\n        wil::CreateDirectoryDeep(LogDirectory.c_str());\r\n    }\r\n\r\n    //\r\n    // Run the instance tests.\r\n    //\r\n\r\n    if (RunInstanceTests != FALSE)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuInstanceTests());\r\n    }\r\n\r\n    Success = TRUE;\r\n\r\nInitializeEnd:\r\n\r\n    return Success;\r\n}\r\n\r\n// LxxstuVmMode\r\n\r\nBOOL LxsstuVmMode(VOID)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Queries if the tests are being run in VM mode.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    TRUE if the tests are running in VM mode, FALSE otherwise.\r\n\r\n--*/\r\n\r\n{\r\n    return g_VmMode;\r\n}\r\n\r\n// LxsstuLaunchPowershellAndCaptureOutput\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchPowershellAndCaptureOutput(_In_ const std::wstring& Cmd, _In_ int ExpectedExitCode)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Run a powershell command and return its output.\r\n\r\nArguments:\r\n\r\n    Cmd - Supplies the powershell command to run.\r\n\r\n    ExpectedExitCode - The expected exit code from the child process.\r\n\r\nReturn Value:\r\ns\r\n    The command's stdout and stderr output.\r\n\r\n--*/\r\n\r\n{\r\n    auto CommandLine = L\"Powershell -NoProfile -Command \\\"\" + Cmd + L\"\\\"\";\r\n    LogInfo(\"Running the command: %ls\\n\", CommandLine.c_str());\r\n    return LxsstuLaunchCommandAndCaptureOutput(CommandLine.data(), ExpectedExitCode);\r\n}\r\n\r\n// LxsstuUninitialize\r\n\r\nVOID LxsstuUninitialize(__in BOOLEAN RunInstanceTests)\r\n{\r\n\r\n    wil::unique_hkey Key;\r\n    LRESULT Result;\r\n\r\n    //\r\n    // Run the instance tests again to make sure that the instance can be\r\n    // started and stopped (i.e. no leaked fs references).\r\n    //\r\n\r\n    if (RunInstanceTests != FALSE)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuInstanceTests());\r\n    }\r\n\r\n    if (LxsstuVmMode() == FALSE)\r\n    {\r\n\r\n        //\r\n        // Delete registry subkeys that were set by the test framework.\r\n        //\r\n\r\n        Result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, LXSS_REGISTRY_PATH, 0, KEY_ALL_ACCESS, &Key);\r\n\r\n        if (Result != ERROR_SUCCESS)\r\n        {\r\n            LogInfo(\"RegOpenKeyEx failed with %Id\", Result);\r\n        }\r\n        else\r\n        {\r\n            auto DeleteKey = [&](LPCWSTR KeyName) {\r\n                Result = RegDeleteKeyValue(Key.get(), nullptr, KeyName);\r\n                if (Result != ERROR_SUCCESS)\r\n                {\r\n                    LogInfo(\"RegDeleteKeyValue %s failed with %Id\", KeyName, Result);\r\n                }\r\n            };\r\n\r\n            DeleteKey(LX_QUERY_REGISTRY_ERROR_LEVEL_SUBKEY);\r\n            DeleteKey(LX_QUERY_REGISTRY_BREAK_ON_SYSCALL_FAILURE_SUBKEY);\r\n            DeleteKey(LX_QUERY_REGISTRY_ROOT_LXBUS_ACCESS);\r\n            DeleteKey(LX_QUERY_REGISTRY_DRVFS_ALLOW_FORCE_CASE_SENSITIVITY);\r\n        }\r\n    }\r\n\r\n    VERIFY_IS_TRUE(SetEnvironmentVariableW(L\"WSL_UTF8\", nullptr));\r\n\r\n    WSACleanup();\r\n\r\n    //\r\n    // Clear the winrt cache in case LookupLiftedPackage() is called again after another CoInitialize().\r\n    //\r\n\r\n    winrt::clear_factory_cache();\r\n\r\n    CoUninitialize();\r\n\r\n    return;\r\n}\r\n\r\n// LxssLogKernelOutput\r\n\r\nvoid LxssLogKernelOutput(VOID)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Write the kernel output in the test logs.\r\n\r\nArguments:\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n    if (!g_LogDmesgAfterEachTest)\r\n    {\r\n        return;\r\n    }\r\n\r\n    //\r\n    // dmesg -c isn't implemented on WSL1\r\n    //\r\n\r\n    const auto cmd = LxsstuVmMode() ? L\"dmesg -c\" : L\"dmesg\";\r\n    const auto Output = LxsstuLaunchWslAndCaptureOutput(cmd);\r\n    LogInfo(\"Kernel logs: '%ls'\", Output.first.c_str());\r\n}\r\n\r\n// LxsstuGetTestDirectory\r\n\r\nstd::wstring LxsstuGetTestDirectory(VOID)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine gets the test directory.\r\n\r\nParameters:\r\n\r\n    None.\r\n\r\nReturn:\r\n\r\n    The test directory.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    std::wstring TestDirectory = LxsstuGetLxssDirectory();\r\n    TestDirectory += L\"\\\\\" LXSS_ROOTFS_DIRECTORY LXSS_TEST_DIRECTORY;\r\n    return TestDirectory;\r\n}\r\n\r\n// LxsstuGetLxssDirectory\r\n\r\nstd::wstring LxsstuGetLxssDirectory(VOID)\r\n\r\n/*++\r\n\r\nDescription:\r\n\r\n    This routine gets the lxss directory.\r\n\r\nParameters:\r\n\r\n    None.\r\n\r\nReturn:\r\n\r\n    The lxss directory.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    const wil::unique_hkey LxssKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n    const std::wstring Default = wsl::windows::common::registry::ReadString(LxssKey.get(), nullptr, L\"DefaultDistribution\", nullptr);\r\n\r\n    std::wstring BasePath = wsl::windows::common::registry::ReadString(LxssKey.get(), Default.c_str(), L\"BasePath\", nullptr);\r\n\r\n    return BasePath;\r\n}\r\n\r\nvoid CaptureLiveDump()\r\n{\r\n    auto PrivilegeState = wsl::windows::common::security::AcquirePrivilege(SE_DEBUG_NAME);\r\n\r\n    const std::wstring targetFile = g_dumpFolder + L\"\\\\livedump.dmp\";\r\n    LogInfo(\"Writing livedump in: %ls\", targetFile.c_str());\r\n\r\n    wsl::windows::common::SubProcess dumpProcess{nullptr, std::format(L\"{} \\\"{}\\\"\", g_dumpToolPath->c_str(), targetFile.c_str()).c_str()};\r\n    const auto exitCode = dumpProcess.Run();\r\n    if (exitCode != 0)\r\n    {\r\n        LogError(\"Failed to capture livedump. ExitCode=%lu\", exitCode);\r\n        return;\r\n    }\r\n\r\n    LogInfo(\"Dump size: %llu\", std::filesystem::file_size(targetFile));\r\n\r\n    // Try to compress the dump.\r\n    std::wstring command = L\"Powershell.exe -NoProfile -ExecutionPolicy Bypass -Command \\\"Compress-Archive -Force -Path '\" +\r\n                           targetFile + L\"' -DestinationPath '\" + targetFile + L\".zip'\\\"\";\r\n    if (LxsstuRunCommand(command.data()) != 0)\r\n    {\r\n        // Note: powershell will fail to create the .zip if the dump is bigger than 2GB with:\r\n        // Exception calling \"Write\" with \"3\" argument(s): \"Stream was too long.\"\r\n        LogError(\"Failed to compress live dump\");\r\n    }\r\n    else\r\n    {\r\n        THROW_IF_WIN32_BOOL_FALSE(DeleteFile(targetFile.c_str()));\r\n    }\r\n}\r\n\r\nDEFINE_ENUM_FLAG_OPERATORS(MINIDUMP_TYPE);\r\n\r\nDWORD FindThreadInProcess(DWORD Pid)\r\n{\r\n    const wil::unique_handle Threads{CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)};\r\n\r\n    THREADENTRY32 ThreadInfo{};\r\n    ThreadInfo.dwSize = sizeof(ThreadInfo);\r\n    for (auto result = Thread32First(Threads.get(), &ThreadInfo); result; result = Thread32Next(Threads.get(), &ThreadInfo))\r\n    {\r\n        if (ThreadInfo.th32OwnerProcessID == Pid)\r\n        {\r\n            return ThreadInfo.th32ThreadID;\r\n        }\r\n    }\r\n\r\n    THROW_HR(HRESULT_FROM_WIN32(STATUS_NOT_FOUND));\r\n}\r\n\r\nPVOID GetModuleAddressInProcess(HANDLE Process, const std::wstring& Module)\r\n{\r\n    // From: https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodulesex\r\n    // Do not call CloseHandle on any of the handles returned by this function. The information comes from a snapshot, so there are no resources to be freed.\r\n\r\n    std::vector<HMODULE> Modules;\r\n    DWORD RequiredSize{};\r\n    bool Result{};\r\n    do\r\n    {\r\n        Modules.resize(RequiredSize / sizeof(HMODULE));\r\n        Result = EnumProcessModulesEx(Process, Modules.data(), static_cast<DWORD>(Modules.size() * sizeof(HMODULE)), &RequiredSize, LIST_MODULES_ALL);\r\n    } while (Result && RequiredSize / sizeof(HMODULE) > Modules.size());\r\n\r\n    for (const auto& e : Modules)\r\n    {\r\n        std::filesystem::path modulePath = wil::GetModuleFileNameExW<std::wstring>(Process, e);\r\n\r\n        if (wsl::windows::common::string::IsPathComponentEqual(modulePath.filename().native(), Module))\r\n        {\r\n            MODULEINFO Info{};\r\n            THROW_IF_WIN32_BOOL_FALSE(GetModuleInformation(Process, e, &Info, sizeof(Info)));\r\n\r\n            return Info.lpBaseOfDll;\r\n        }\r\n    }\r\n\r\n    THROW_HR(HRESULT_FROM_WIN32(STATUS_NOT_FOUND));\r\n}\r\n\r\nvoid CreateCrashReport(HANDLE Process, LPCWSTR ProcessName, DWORD Pid, std::wstring const& EventName)\r\n{\r\n    using unique_hreport = wil::unique_any<HREPORT, decltype(WerReportCloseHandle), WerReportCloseHandle>;\r\n\r\n    auto setProperty = [](LPWSTR Target, const std::wstring& Value, size_t BufferSize) {\r\n        wcsncpy(Target, Value.c_str(), std::min(BufferSize - 1, Value.size()));\r\n    };\r\n\r\n    WER_REPORT_INFORMATION Info{};\r\n    Info.dwSize = sizeof(Info);\r\n    Info.hProcess = Process;\r\n\r\n    setProperty(Info.wzDescription, EventName, ARRAYSIZE(Info.wzDescription));\r\n    setProperty(Info.wzApplicationName, ProcessName, ARRAYSIZE(Info.wzApplicationName));\r\n    setProperty(Info.wzApplicationPath, wil::GetModuleFileNameExW<std::wstring>(Process, nullptr), ARRAYSIZE(Info.wzApplicationPath));\r\n\r\n    unique_hreport Report;\r\n    THROW_IF_FAILED(WerReportCreate(EventName.c_str(), WerReportApplicationCrash, &Info, &Report));\r\n\r\n    const std::wstring DumpPath = g_dumpFolder + L\"\\\\\" + ProcessName + L\".\" + std::to_wstring(Pid) + L\".hdmp\";\r\n    wil::unique_hfile DumpFile{CreateFileW(DumpPath.c_str(), GENERIC_ALL, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)};\r\n    THROW_LAST_ERROR_IF(!DumpFile);\r\n\r\n    std::optional<MINIDUMP_EXCEPTION_INFORMATION> ExceptionInfo;\r\n    EXCEPTION_RECORD Record{};\r\n    EXCEPTION_POINTERS Pointers{};\r\n\r\n    // To get access to the dumps in AzureWatson, the exception address needs to point to a module\r\n    // that we own. To do that, load the main module and point the exception to its entrypoint.\r\n    try\r\n    {\r\n        Record.ExceptionAddress = GetModuleAddressInProcess(Process, ProcessName);\r\n        Record.ExceptionCode = EXCEPTION_BREAKPOINT;\r\n        Pointers.ExceptionRecord = &Record;\r\n\r\n        ExceptionInfo.emplace();\r\n        ExceptionInfo->ExceptionPointers = &Pointers;\r\n        ExceptionInfo->ThreadId = FindThreadInProcess(Pid);\r\n    }\r\n    catch (...)\r\n    {\r\n        LogError(\"Failed to find module address / thread id for %ls, 0x%x\", ProcessName, wil::ResultFromCaughtException());\r\n    }\r\n\r\n    THROW_IF_WIN32_BOOL_FALSE(MiniDumpWriteDump(\r\n        Process,\r\n        Pid,\r\n        DumpFile.get(),\r\n        MiniDumpWithDataSegs | MiniDumpWithProcessThreadData | MiniDumpWithHandleData | MiniDumpWithPrivateReadWriteMemory |\r\n            MiniDumpWithUnloadedModules | MiniDumpWithFullMemoryInfo | MiniDumpWithThreadInfo | MiniDumpWithTokenInformation |\r\n            MiniDumpWithPrivateWriteCopyMemory | MiniDumpWithCodeSegs,\r\n        ExceptionInfo.has_value() ? &ExceptionInfo.value() : nullptr,\r\n        nullptr,\r\n        nullptr));\r\n\r\n    DumpFile.reset();\r\n\r\n    THROW_IF_FAILED(WerReportAddFile(Report.get(), DumpPath.c_str(), WerFileTypeHeapdump, 0));\r\n\r\n    WER_SUBMIT_RESULT SubmitResult{};\r\n    const auto Result = WerReportSubmit(\r\n        Report.get(),\r\n        WerConsentApproved,\r\n        WER_SUBMIT_ADD_REGISTERED_DATA | WER_SUBMIT_NO_CLOSE_UI | WER_SUBMIT_BYPASS_DATA_THROTTLING | WER_SUBMIT_REPORT_MACHINE_ID | WER_SUBMIT_QUEUE,\r\n        &SubmitResult);\r\n\r\n    LogInfo(\"WerReportSubmit() returned 0x%x, SubmitResult = %i, EventName = %ls\", Result, SubmitResult, EventName.c_str());\r\n}\r\n\r\nvoid CreateProcessCrashReport(DWORD Pid, LPCWSTR ImageName, LPCWSTR EventName)\r\n{\r\n    try\r\n    {\r\n        LogInfo(\"Opening process %ls, Pid %lu\", ImageName, Pid);\r\n        const wil::unique_handle Process(OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid));\r\n        THROW_LAST_ERROR_IF_NULL(Process);\r\n\r\n        CreateCrashReport(Process.get(), ImageName, Pid, EventName);\r\n    }\r\n    catch (...)\r\n    {\r\n        LogError(\"Failed to create crash report for process %ls (%lu), %lu\", ImageName, Pid, wil::ResultFromCaughtException());\r\n    }\r\n}\r\n\r\nvoid CreateWerReports()\r\n{\r\n    static const std::set<std::wstring, wsl::shared::string::CaseInsensitiveCompare> WslProcesses{\r\n        L\"wsl.exe\", L\"wslhost.exe\", L\"wslrelay.exe\", L\"wslservice.exe\", L\"wslg.exe\", L\"vmcompute.exe\", L\"vmwp.exe\"};\r\n\r\n    auto PrivilegeState = wsl::windows::common::security::AcquirePrivilege(SE_DEBUG_NAME);\r\n    const std::wstring EventName = L\"WslTestHang-\" + g_pipelineBuildId;\r\n\r\n    LogInfo(\"Dumps here: https://azurewatson.microsoft.com/?EventType=%s\", EventName.c_str());\r\n\r\n    // Start by capturing the test process, since collect dmesg changes the state of the UVM.\r\n    try\r\n    {\r\n        CreateProcessCrashReport(GetCurrentProcessId(), L\"te.processhost.exe\", EventName.c_str());\r\n    }\r\n    CATCH_LOG();\r\n\r\n    PROCESSENTRY32 PE32;\r\n    PE32.dwSize = sizeof(PE32);\r\n    const wil::unique_handle ProcessSnapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));\r\n    THROW_LAST_ERROR_IF(ProcessSnapshot.get() == INVALID_HANDLE_VALUE);\r\n\r\n    try\r\n    {\r\n        if (Process32First(ProcessSnapshot.get(), &PE32))\r\n        {\r\n            do\r\n            {\r\n                if (WslProcesses.find(std::wstring(PE32.szExeFile)) == WslProcesses.end())\r\n                {\r\n                    continue;\r\n                }\r\n\r\n                try\r\n                {\r\n                    CreateProcessCrashReport(PE32.th32ProcessID, PE32.szExeFile, EventName.c_str());\r\n                }\r\n                CATCH_LOG();\r\n\r\n            } while (Process32Next(ProcessSnapshot.get(), &PE32));\r\n        }\r\n        THROW_LAST_ERROR_IF(GetLastError() != ERROR_NO_MORE_FILES);\r\n    }\r\n    CATCH_LOG();\r\n\r\n    // Also capture an HNS dump. Since the process name is svchost.exe, find its pid from its service.\r\n    const wil::unique_schandle manager{OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT)};\r\n    THROW_LAST_ERROR_IF_NULL(manager);\r\n\r\n    const wil::unique_schandle service{OpenService(manager.get(), L\"HNS\", SERVICE_QUERY_STATUS)};\r\n    THROW_LAST_ERROR_IF_NULL(service);\r\n\r\n    auto [_, pid] = GetServiceState(service.get());\r\n    CreateProcessCrashReport(pid, L\"svchost.exe\", EventName.c_str());\r\n}\r\n\r\nvoid DumpGuestProcesses()\r\n{\r\n    constexpr auto dumpScript =\r\n        R\"(\r\nset -ue\r\n\r\ndmesg\r\n\r\n# Try to install gdb\r\ntdnf install -y gdb || true\r\n\r\ndeclare -a pids_to_dump\r\n\r\nfor proc in /proc/[0-9]*; do\r\n  read -a stats < \"$proc/stat\" # Skip kernel threads to make the output easier to read\r\n  flags=${stats[8]}\r\n\r\n  if (( (\"$flags\" & 0x00200000) == 0x00200000 )); then\r\n    continue\r\n  fi\r\n\r\n  pid=$(basename \"$proc\")\r\n\r\n  pids_to_dump+=(\"$pid\")\r\n  parent=$(ps -o ppid= -p \"$pid\")\r\n\r\n  echo -e \"\\nProcess: $pid (parent: $parent) \"\r\n  echo -en \"cmd: \"\r\n  cat \"/proc/$pid/cmdline\" || true\r\n  echo -e \"\\nstat: \"\r\n  cat \"/proc/$pid/stat\" || true\r\n\r\n  for tid in $(ls \"/proc/$pid/task\" || true); do\r\n    echo -n \"tid: $tid - \"\r\n    cat \"/proc/$pid/task/$tid/comm\" || true\r\n    cat \"/proc/$pid/task/$tid/stack\" || true\r\n  done\r\n\r\n  echo \"fds: \"\r\n  ls -la \"/proc/$pid/fd\" || true\r\ndone\r\n\r\nfor pid in \"${pids_to_dump[@]}\" ; do\r\n   name=$(ps -p \"$pid\" -o comm=)\r\n   if [[ \"$name\" =~ ^(bash|login)$ ]]; then\r\n     echo \"Skipping dump for process: $name\"\r\n     continue\r\n   fi\r\n\r\n   echo \"Dumping process: $name ($pid) \"\r\n   if gcore -a -o core \"$pid\" ; then\r\n     if ! /wsl-capture-crash 0 \"$name\" \"$pid\" 0 < \"core.$pid\" ; then\r\n         echo \"Failed to dump process $pid\"\r\n     fi\r\n\r\n     rm \"core.$pid\" \r\n   fi\r\ndone\r\n\r\necho \"hvsockets: \"\r\nss -lap --vsock\r\n\r\necho \"meminfo: \"\r\ncat /proc/meminfo\r\n\r\npoweroff -f\r\n)\";\r\n\r\n    const std::wstring filePath = g_dumpFolder + L\"\\\\guest-state.txt\";\r\n    LogInfo(\"Dumping guest processes in: %ls\", filePath.c_str());\r\n\r\n    const wil::unique_hfile outputFile{CreateFileW(\r\n        filePath.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)};\r\n\r\n    THROW_LAST_ERROR_IF(!outputFile);\r\n    THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(outputFile.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));\r\n\r\n    auto [readPipe, writePipe] = CreateSubprocessPipe(true, false);\r\n\r\n    auto cmd = LxssGenerateWslCommandLine(L\"--debug-shell\");\r\n    const auto process = LxsstuStartProcess(cmd.data(), readPipe.get(), outputFile.get());\r\n\r\n    THROW_IF_WIN32_BOOL_FALSE(WriteFile(writePipe.get(), dumpScript, static_cast<DWORD>(strlen(dumpScript)), nullptr, nullptr));\r\n    writePipe.reset();\r\n\r\n    // Wait up to 5 minutes for that process\r\n    const auto result = WaitForSingleObject(process.get(), 60 * 1000 * 5);\r\n    if (result != WAIT_TIMEOUT)\r\n    {\r\n        LogError(\"Unexpected status waiting for the debug shell, %lu\", result);\r\n    }\r\n}\r\n\r\n// LxsstuWatchdogTimer\r\n\r\nVOID __stdcall LxsstuWatchdogTimer(_Inout_ PTP_CALLBACK_INSTANCE Instance, _Inout_opt_ PVOID ThreadpoolTimerContext, _Inout_ PTP_TIMER Timer)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Runs when the watch dog timer has fired to crash the process.\r\n\r\nArguments:\r\n\r\n    Instance - Not used.\r\n\r\n    ThreadpoolTimerContext - Not used.\r\n\r\n    Timer - Not used.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    UNREFERENCED_PARAMETER(Instance);\r\n    UNREFERENCED_PARAMETER(ThreadpoolTimerContext);\r\n    UNREFERENCED_PARAMETER(Timer);\r\n\r\n    try\r\n    {\r\n        if (g_enableWerReport)\r\n        {\r\n            CreateWerReports();\r\n        }\r\n        else\r\n        {\r\n            LogError(\"Wer reporting disabled, skipping\");\r\n        }\r\n    }\r\n    catch (...)\r\n    {\r\n        LogError(\"Failed to create WER report, 0x%x\", wil::ResultFromCaughtException());\r\n    }\r\n\r\n    if (LxsstuVmMode())\r\n    {\r\n        try\r\n        {\r\n            DumpGuestProcesses();\r\n        }\r\n        catch (...)\r\n        {\r\n            LogError(\"Failed to dump guest processes, 0x%x\", wil::ResultFromCaughtException());\r\n        }\r\n    }\r\n\r\n    try\r\n    {\r\n        if (g_enableWerReport && g_dumpToolPath.has_value())\r\n        {\r\n            CaptureLiveDump();\r\n        }\r\n    }\r\n    catch (...)\r\n    {\r\n        LogError(\"Failed to capture livedump, 0x%x\", wil::ResultFromCaughtException());\r\n    }\r\n\r\n    __fastfail(FAST_FAIL_FATAL_APP_EXIT);\r\n    return;\r\n}\r\n\r\n// LxsstuInstanceTests\r\n\r\nVOID LxsstuInstanceTests(VOID)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Runs the instance unit tests.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    ULONG Iteration;\r\n    ULONG NumberOfIterations;\r\n    ULONG SleepDuration;\r\n    unsigned int Seed;\r\n\r\n    //\r\n    // Start and stop an instance multiple times, sleeping a random duration\r\n    // between the start and stop.\r\n    //\r\n\r\n    NumberOfIterations = 5;\r\n    Seed = GetTickCount();\r\n    srand(Seed);\r\n    LogInfo(\"Starting instance tests, Seed = %u\", Seed);\r\n    for (Iteration = 0; Iteration < NumberOfIterations; Iteration++)\r\n    {\r\n        LogInfo(\"Create instance - Iteration %u of %u\", (Iteration + 1), NumberOfIterations);\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"/bin/true\"), 0u);\r\n        SleepDuration = rand() % LXSS_INSTANCE_TEST_TIMEOUT;\r\n        LogInfo(\"Sleeping %u milliseconds before destroying instance...\", SleepDuration);\r\n\r\n        SleepEx(SleepDuration, FALSE);\r\n        TerminateDistribution();\r\n    }\r\n\r\n    LogPass(\"Instance tests passed\");\r\n\r\n    return;\r\n}\r\n\r\n// LxxsSplitString\r\n\r\nstd::vector<std::wstring> LxssSplitString(_In_ const std::wstring& String, _In_ const std::wstring& Delim)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Split a string by a delimiter.\r\n\r\nArguments:\r\n\r\n    String - Supplies the string to split.\r\n\r\n    Delim - The delimiter to split the string on.\r\n\r\nReturn Value:\r\n\r\n    A vector of split string.\r\n\r\n--*/\r\n\r\n{\r\n    std::vector<std::wstring> output;\r\n\r\n    std::wistringstream input(String);\r\n    std::wstring entry;\r\n\r\n    std::string::size_type index = 0;\r\n    std::string::size_type previous_index = 0;\r\n\r\n    while ((index = String.find(Delim, previous_index)) != std::string::npos)\r\n    {\r\n        output.emplace_back(String.substr(previous_index, index - previous_index));\r\n        previous_index = index + Delim.size();\r\n    }\r\n\r\n    auto remaining = String.substr(previous_index);\r\n    if (remaining != Delim && !remaining.empty())\r\n    {\r\n        output.emplace_back(std::move(remaining));\r\n    }\r\n\r\n    return output;\r\n}\r\n\r\n// WslKeepAlive class definitions\r\n\r\nWslKeepAlive::WslKeepAlive(HANDLE Token) : m_token(Token)\r\n{\r\n    Set();\r\n}\r\n\r\nWslKeepAlive::~WslKeepAlive()\r\n{\r\n    Reset();\r\n}\r\n\r\nvoid WslKeepAlive::Set()\r\n{\r\n    std::tie(m_read, m_write) = CreateSubprocessPipe(true, false);\r\n\r\n    m_running.emplace();\r\n    m_thread = std::thread(std::bind(&WslKeepAlive::Run, this));\r\n    m_running->get_future().wait();\r\n}\r\n\r\nvoid WslKeepAlive::Run()\r\n{\r\n    try\r\n    {\r\n        // Create a pipe to read wsl's output\r\n        wil::unique_handle read;\r\n        wil::unique_handle write;\r\n        SECURITY_ATTRIBUTES attributes = {0};\r\n        attributes.nLength = sizeof(attributes);\r\n        attributes.bInheritHandle = true;\r\n        THROW_LAST_ERROR_IF(!CreatePipe(&read, &write, &attributes, sizeof(attributes)));\r\n\r\n        // Start a process that outputs 'running', then waits\r\n        const std::wstring expectedOutput = L\"running\";\r\n        std::wstring cmd = L\"wsl.exe echo -n \" + expectedOutput + L\" && read -n 1 \";\r\n        const auto process = LxsstuStartProcess(cmd.data(), m_read.get(), write.get(), nullptr, m_token);\r\n        write.reset();\r\n\r\n        // Wait until we read 'running'\r\n        std::string buffer(expectedOutput.size(), '\\0');\r\n        DWORD bytesRead = 0;\r\n        VERIFY_IS_TRUE(ReadFile(read.get(), buffer.data(), static_cast<DWORD>(expectedOutput.size()), &bytesRead, nullptr));\r\n\r\n        VERIFY_ARE_EQUAL(buffer, wsl::shared::string::WideToMultiByte(expectedOutput));\r\n\r\n        m_running->set_value();\r\n\r\n        WaitForSingleObject(process.get(), INFINITE);\r\n    }\r\n    catch (...)\r\n    {\r\n        LogError(\"Caught exception in WslKeepAlive::Run\");\r\n        m_running->set_exception(std::current_exception());\r\n    }\r\n}\r\n\r\nvoid WslKeepAlive::Reset()\r\n{\r\n    if (m_thread.joinable())\r\n    {\r\n        const char c = 'k';\r\n        THROW_LAST_ERROR_IF(!WriteFile(m_write.get(), &c, sizeof(c), nullptr, nullptr));\r\n        m_write.reset();\r\n        m_thread.join();\r\n    }\r\n}\r\n\r\nstd::pair<DWORD, DWORD> GetServiceState(SC_HANDLE service)\r\n{\r\n    DWORD dwBytesNeeded{};\r\n    SERVICE_STATUS_PROCESS status{};\r\n    if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(status), &dwBytesNeeded))\r\n    {\r\n        LogError(\"QueryServiceStatusEx() failed, %lu\", GetLastError());\r\n        VERIFY_FAIL();\r\n    }\r\n\r\n    return std::make_pair(status.dwCurrentState, status.dwProcessId);\r\n}\r\n\r\nvoid WaitForServiceState(SC_HANDLE service, DWORD state, DWORD previousPid)\r\n{\r\n    DWORD currentState{};\r\n    DWORD pid{};\r\n    auto pred = [&]() {\r\n        std::tie(currentState, pid) = GetServiceState(service);\r\n        if (pid != previousPid && state == SERVICE_STOPPED)\r\n        {\r\n            return;\r\n        }\r\n\r\n        THROW_HR_IF(E_ABORT, currentState != state && currentState != SERVICE_STOPPED);\r\n    };\r\n\r\n    try\r\n    {\r\n        wsl::shared::retry::RetryWithTimeout<void>(pred, std::chrono::milliseconds(100), std::chrono::minutes(2), [&]() {\r\n            return wil::ResultFromCaughtException() == E_ABORT;\r\n        });\r\n    }\r\n    catch (...)\r\n    {\r\n        LogError(\"Timed waiting for service to reach state: %lu. Current state: %lu, error: 0x%x\", state, currentState, wil::ResultFromCaughtException());\r\n    }\r\n}\r\n\r\nvoid StopService(SC_HANDLE service)\r\n{\r\n    // Some services don't accept SERVICE_CONTROL_STOP when starting.\r\n    // Wait for them to be running before stopping them\r\n    auto [state, pid] = GetServiceState(service);\r\n    if (state == SERVICE_START_PENDING)\r\n    {\r\n        WaitForServiceState(service, SERVICE_RUNNING, pid);\r\n    }\r\n\r\n    SERVICE_STATUS status{};\r\n    if (!ControlService(service, SERVICE_CONTROL_STOP, &status))\r\n    {\r\n        const auto error = GetLastError();\r\n        if (error != ERROR_SERVICE_NOT_ACTIVE)\r\n        {\r\n            LogError(\"Unexpected error code: 0x%x\", error);\r\n            VERIFY_FAIL();\r\n        }\r\n        return; // Service is not running\r\n    }\r\n\r\n    WaitForServiceState(service, SERVICE_STOPPED, pid);\r\n}\r\n\r\nvoid RestartWslService()\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Restart the WSL service.\r\n\r\nArguments:\r\n\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n{\r\n    LogInfo(\"Restarting WSLService\");\r\n    const wil::unique_schandle manager{OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT)};\r\n    VERIFY_IS_NOT_NULL(manager);\r\n\r\n    const wil::unique_schandle service{OpenService(manager.get(), L\"wslservice\", SERVICE_STOP | SERVICE_QUERY_STATUS | SERVICE_START)};\r\n    VERIFY_IS_NOT_NULL(service);\r\n\r\n    StopService(service.get());\r\n    if (!StartService(service.get(), 0, nullptr))\r\n    {\r\n        VERIFY_ARE_EQUAL(GetLastError(), ERROR_SERVICE_ALREADY_RUNNING);\r\n    }\r\n}\r\n\r\nvoid StopWslService()\r\n{\r\n    LogInfo(\"Stopping WSLService\");\r\n    const wil::unique_schandle manager{OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT)};\r\n    VERIFY_IS_NOT_NULL(manager);\r\n\r\n    const wil::unique_schandle service{OpenService(manager.get(), L\"wslservice\", SERVICE_STOP | SERVICE_QUERY_STATUS)};\r\n    VERIFY_IS_NOT_NULL(service);\r\n    StopService(service.get());\r\n}\r\n\r\nwil::unique_handle GetNonElevatedToken()\r\n{\r\n    const auto token = wil::open_current_access_token(TOKEN_ALL_ACCESS);\r\n\r\n    wil::unique_handle nonElevatedToken;\r\n    THROW_IF_WIN32_BOOL_FALSE(DuplicateTokenEx(token.get(), TOKEN_ALL_ACCESS, nullptr, SecurityImpersonation, TokenPrimary, &nonElevatedToken));\r\n\r\n    wil::unique_sid mediumIntegritySid;\r\n    THROW_LAST_ERROR_IF(!ConvertStringSidToSidA(\"S-1-16-8192\", &mediumIntegritySid));\r\n\r\n    TOKEN_MANDATORY_LABEL label = {0};\r\n    label.Label.Attributes = SE_GROUP_INTEGRITY;\r\n    label.Label.Sid = mediumIntegritySid.get();\r\n    THROW_IF_WIN32_BOOL_FALSE(SetTokenInformation(nonElevatedToken.get(), TokenIntegrityLevel, &label, sizeof(label)));\r\n\r\n    return nonElevatedToken;\r\n}\r\n\r\nWslConfigChange::WslConfigChange(const std::wstring& Content)\r\n{\r\n    m_originalContent = Update(Content);\r\n}\r\n\r\nWslConfigChange::WslConfigChange(WslConfigChange&& other) : m_originalContent(std::move(other.m_originalContent))\r\n{\r\n}\r\n\r\nstd::wstring WslConfigChange::Update(const std::wstring& Content)\r\n{\r\n    auto previous = LxssWriteWslConfig(Content);\r\n\r\n    if (previous != Content)\r\n    {\r\n        RestartWslService();\r\n    }\r\n\r\n    return previous;\r\n}\r\n\r\nWslConfigChange::~WslConfigChange()\r\n{\r\n    if (m_originalContent)\r\n    {\r\n        Update(m_originalContent.value());\r\n    }\r\n}\r\n\r\n// writes global WSL 2 config settings at %userprofile%/.wslconfig\r\nstd::wstring LxssWriteWslConfig(const std::wstring& Content)\r\n{\r\n    auto path = getenv(\"userprofile\") + std::string(\"\\\\.wslconfig\");\r\n\r\n    std::wifstream configRead(path);\r\n    auto previousContent = std::wstring{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n    configRead.close();\r\n\r\n    std::wofstream config(path);\r\n    VERIFY_IS_TRUE(config.good());\r\n    config << Content;\r\n\r\n    return previousContent;\r\n}\r\n\r\n// writes distro specific settings /etc/wsl.conf\r\nstd::string LxssWriteWslDistroConfig(const std::string& Content)\r\n{\r\n    std::string path = std::format(\"\\\\\\\\wsl.localhost\\\\{}\\\\etc\\\\wsl.conf\", LXSS_DISTRO_NAME_TEST);\r\n\r\n    std::ifstream distroConfigRead(path);\r\n    auto previousContent = std::string{std::istreambuf_iterator<char>(distroConfigRead), {}};\r\n    distroConfigRead.close();\r\n\r\n    std::ofstream distroConfig(path, std::ios_base::binary);\r\n    VERIFY_IS_TRUE(distroConfig.good());\r\n    distroConfig.write(Content.c_str(), Content.size());\r\n\r\n    return previousContent;\r\n}\r\n\r\n// generates a sample global WSL config for the tests\r\nstd::wstring LxssGenerateTestConfig(TestConfigDefaults Default)\r\n{\r\n    WEX::Common::String kernelLogsArg;\r\n    WEX::TestExecution::RuntimeParameters::TryGetValue(L\"KernelLogs\", kernelLogsArg);\r\n\r\n    std::wstring kernelLogs;\r\n    if (kernelLogsArg.IsEmpty())\r\n    {\r\n        kernelLogs = wil::GetCurrentDirectoryW().get() + std::wstring(L\"\\\\kernelLogs.txt\");\r\n    }\r\n    else\r\n    {\r\n        kernelLogs = kernelLogsArg;\r\n    }\r\n\r\n    auto boolOptionToString = [](LPCWSTR optionName, std::optional<bool> condition, bool defaultValue) {\r\n        std::wstring value{optionName};\r\n        value += L\"=\";\r\n        value += condition.value_or(defaultValue) ? L\"true\" : L\"false\";\r\n        value += L\"\\n\";\r\n        return value;\r\n    };\r\n\r\n    auto networkingModeToString = [](std::optional<wsl::core::NetworkingMode> mode) {\r\n        if (mode.has_value())\r\n        {\r\n            std::wstring value = L\"networkingMode=\";\r\n            value += wsl::shared::string::MultiByteToWide(wsl::core::ToString(mode.value()));\r\n            value += L\"\\n\";\r\n            return value;\r\n        }\r\n\r\n        return std::wstring{};\r\n    };\r\n\r\n    auto drvFsModeToString = [](std::optional<DrvFsMode> mode) {\r\n        std::wstring value;\r\n        switch (mode.value_or(DrvFsMode::Plan9))\r\n        {\r\n        case DrvFsMode::Plan9:\r\n            value = L\"virtio9p=false\";\r\n            break;\r\n        case DrvFsMode::Virtio9p:\r\n            value = L\"virtio9p=true\";\r\n            break;\r\n        case DrvFsMode::VirtioFs:\r\n            value = L\"virtiofs=true\";\r\n            break;\r\n        }\r\n\r\n        value += L\"\\n\";\r\n        return value;\r\n    };\r\n\r\n    std::wstring newConfig =\r\n        L\"[wsl2]\\n\"\r\n        L\"crashDumpFolder=\" +\r\n        EscapePath(Default.CrashDumpFolder.value_or(g_dumpFolder + L\"\\\\linux-crashes\")) + L\"\\nmaxCrashDumpCount=\" +\r\n        std::to_wstring(Default.crashDumpCount) + L\"\\nvmIdleTimeout=\" + std::to_wstring(Default.vmIdleTimeout.value_or(2000)) +\r\n        L\"\\n\"\r\n        L\"mountDeviceTimeout=120000\\n\"\r\n        L\"kernelBootTimeout=120000\\n\"\r\n        L\"debugConsoleLogFile=\" +\r\n        EscapePath(kernelLogs) +\r\n        L\"\\n\"\r\n        L\"telemetry=false\\n\" +\r\n        boolOptionToString(L\"safeMode\", Default.safeMode, false) + boolOptionToString(L\"guiApplications\", Default.guiApplications, true) +\r\n        L\"earlyBootLogging=false\\n\" + networkingModeToString(Default.networkingMode) + drvFsModeToString(Default.drvFsMode);\r\n\r\n    if (Default.kernel.has_value())\r\n    {\r\n        newConfig += L\"kernel=\" + EscapePath(Default.kernel.value()) + L\"\\n\";\r\n    }\r\n\r\n    if (Default.kernelCommandLine.has_value())\r\n    {\r\n        newConfig += L\"kernelCommandLine=\" + Default.kernelCommandLine.value() + L\"\\n\";\r\n    }\r\n\r\n    if (Default.kernelModules.has_value())\r\n    {\r\n        newConfig += L\"kernelModules=\" + EscapePath(Default.kernelModules.value()) + L\"\\n\";\r\n    }\r\n\r\n    if (Default.loadKernelModules.has_value())\r\n    {\r\n        newConfig += L\"loadKernelModules=\" + Default.loadKernelModules.value() + L\"\\n\";\r\n    }\r\n\r\n    if (Default.loadDefaultKernelModules.has_value())\r\n    {\r\n        newConfig +=\r\n            L\"loadDefaultKernelModules=\" + std::wstring(Default.loadDefaultKernelModules.value() ? L\"true\" : L\"false\") + L\"\\n\";\r\n    }\r\n\r\n    switch (Default.networkingMode.value_or(wsl::core::NetworkingMode::Nat))\r\n    {\r\n    case wsl::core::NetworkingMode::Nat:\r\n    {\r\n        if (Default.dnsProxy.has_value())\r\n        {\r\n            newConfig += boolOptionToString(L\"dnsProxy\", Default.dnsProxy, false);\r\n        }\r\n\r\n        if (Default.firewall.has_value())\r\n        {\r\n            newConfig += L\"[experimental]\\nfirewall=\";\r\n            newConfig += *Default.firewall ? L\"true\" : L\"false\";\r\n            newConfig += L\"\\n[wsl2]\\n\";\r\n        }\r\n\r\n        break;\r\n    }\r\n    case wsl::core::NetworkingMode::Bridged:\r\n    {\r\n        VERIFY_IS_TRUE(Default.vmSwitch.has_value());\r\n\r\n        newConfig += L\"vmSwitch=\" + *Default.vmSwitch;\r\n\r\n        if (Default.macAddress.has_value())\r\n        {\r\n            newConfig += L\"\\nmacAddress=\" + *Default.macAddress;\r\n        }\r\n\r\n        newConfig += L\"\\nipv6=\" + std::wstring(Default.ipv6 ? L\"true\" : L\"false\");\r\n        newConfig += L\"\\n\";\r\n\r\n        break;\r\n    }\r\n    }\r\n\r\n    if (Default.dnsTunneling.has_value())\r\n    {\r\n        newConfig += L\"\\n[experimental]\\n\";\r\n        newConfig += boolOptionToString(L\"dnsTunneling\", Default.dnsTunneling, false);\r\n        newConfig += L\"[wsl2]\\n\";\r\n    }\r\n\r\n    if (Default.dnsTunnelingIpAddress.has_value())\r\n    {\r\n        newConfig += L\"\\n[experimental]\\n\";\r\n        newConfig += L\"dnsTunnelingIpAddress=\" + Default.dnsTunnelingIpAddress.value() + L\"\\n\";\r\n        newConfig += L\"[wsl2]\\n\";\r\n    }\r\n\r\n    // always add this regardless if it has value, want to have it disabled by default for tests\r\n    newConfig += L\"\\n[experimental]\\n\";\r\n    newConfig += boolOptionToString(L\"autoProxy\", Default.autoProxy, false);\r\n    newConfig += L\"[wsl2]\\n\";\r\n\r\n    if (Default.sparse.has_value())\r\n    {\r\n        std::wstring value = Default.sparse.value() ? L\"true\" : L\"false\";\r\n        newConfig += L\"[experimental]\\nsparseVhd=\" + value + L\"\\n[wsl2]\";\r\n    }\r\n\r\n    if (Default.hostAddressLoopback.has_value())\r\n    {\r\n        newConfig += L\"\\n[experimental]\\n\";\r\n        newConfig += boolOptionToString(L\"hostAddressLoopback\", Default.hostAddressLoopback, false);\r\n        newConfig += L\"[wsl2]\\n\";\r\n    }\r\n\r\n    // TODO: Remove once SetVersion() truncated archive error is root caused.\r\n    newConfig += L\"\\n[experimental]\\nSetVersionDebug=true\\n[wsl2]\\n\";\r\n\r\n    return newConfig;\r\n}\r\n\r\nstd::wstring EscapePath(std::wstring_view Path)\r\n{\r\n    std::wstring escaped;\r\n    for (const auto e : Path)\r\n    {\r\n        escaped += e;\r\n\r\n        if (e == L'\\\\')\r\n        {\r\n            escaped += e;\r\n        }\r\n    }\r\n\r\n    return escaped;\r\n}\r\n\r\nNTSTATUS\r\nLxsstuParseLinuxLogFiles(__in PCWSTR LogFileName, __out PBOOL TestPassed)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Parses the output of the linux test and relogs the output.\r\n\r\nArguments:\r\n\r\n    LogFileName - Supplies a string containing the log files for the test\r\n        separated by LXSS_TEST_LOG_SEPARATOR_CHAR.\r\n\r\n    TestPassed - Supplies a buffer to receive a boolean value specifying if the\r\n        tests completed without errors.\r\n\r\nReturn Value:\r\n\r\n    NTSTATUS\r\n\r\n--*/\r\n\r\n{\r\n\r\n    HANDLE LinuxLogFile;\r\n    WCHAR LinuxLogPath[MAX_PATH];\r\n    WCHAR LocalLogFileBuffer[MAX_PATH];\r\n    PWCHAR LogFileToken;\r\n    DWORD PrintStatus;\r\n    NTSTATUS Status;\r\n    std::wstring TestDirectory;\r\n    LXSS_TEST_LAUNCHER_TEST TestRecord;\r\n    PWCHAR TokenState;\r\n\r\n    LinuxLogFile = INVALID_HANDLE_VALUE;\r\n    Status = STATUS_UNSUCCESSFUL;\r\n    *TestPassed = FALSE;\r\n    RtlZeroMemory(&TestRecord, sizeof(TestRecord));\r\n\r\n    //\r\n    // Make a copy of the log file name so wcstok can modify it.\r\n    //\r\n\r\n    PrintStatus = swprintf_s(LocalLogFileBuffer, RTL_NUMBER_OF(LocalLogFileBuffer), L\"%s\", LogFileName);\r\n\r\n    if (PrintStatus == -1)\r\n    {\r\n        Status = STATUS_UNSUCCESSFUL;\r\n        LogError(\"Increase LocalLogFileBuffer buffer\");\r\n        goto ErrorExit;\r\n    }\r\n\r\n    //\r\n    // Get the test directory.\r\n    //\r\n\r\n    TestDirectory = LxsstuGetTestDirectory();\r\n\r\n    //\r\n    // Parse the logs for the test and determine how many passes / errors there\r\n    // were.\r\n    //\r\n\r\n    LogFileToken = wcstok(LocalLogFileBuffer, LXSS_TEST_LOG_SEPARATOR_CHAR, &TokenState);\r\n\r\n    while (LogFileToken != NULL)\r\n    {\r\n        LogInfo(\"LOGFILE: %s\", LogFileToken);\r\n        PrintStatus = swprintf_s(LinuxLogPath, RTL_NUMBER_OF(LinuxLogPath), L\"%s\\\\log\\\\%s\", TestDirectory.c_str(), LogFileToken);\r\n\r\n        if (PrintStatus == -1)\r\n        {\r\n            Status = STATUS_UNSUCCESSFUL;\r\n            LogError(\"Increase LinuxLogPath buffer\");\r\n            goto ErrorExit;\r\n        }\r\n\r\n        //\r\n        // For VM Mode, copy the output file out of the ext4 volume so it can\r\n        // be read.\r\n        //\r\n\r\n        if (LxsstuVmMode())\r\n        {\r\n            std::wstring Command = std::format(L\"/bin/cp /data/test/log/{} $(wslpath '{}')\", LogFileToken, LinuxLogPath);\r\n            VERIFY_NO_THROW(LxsstuRunTest(Command.c_str()));\r\n        }\r\n\r\n        LinuxLogFile =\r\n            CreateFileW(LinuxLogPath, GENERIC_READ, (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r\n\r\n        if (LinuxLogFile == INVALID_HANDLE_VALUE)\r\n        {\r\n            Status = STATUS_UNSUCCESSFUL;\r\n            LogError(\"Could not open {:%s:} after running test, LastError %#x\", LinuxLogPath, GetLastError());\r\n\r\n            goto ErrorExit;\r\n        }\r\n\r\n        Status = LxsstuParseLogFile(LinuxLogFile, &TestRecord);\r\n        if (!NT_SUCCESS(Status))\r\n        {\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (TestRecord.NumberOfErrors > 0)\r\n        {\r\n            LogError(\"LOG FILE SUMMARY: %s - PASSED: %u ERRORS: %u\", LogFileToken, TestRecord.NumberOfPasses, TestRecord.NumberOfErrors);\r\n        }\r\n        else if (TestRecord.NumberOfPasses > 0)\r\n        {\r\n            LogPass(\"LOG FILE SUMMARY: %s - PASSED: %u ERRORS: %u\", LogFileToken, TestRecord.NumberOfPasses, TestRecord.NumberOfErrors);\r\n        }\r\n        else\r\n        {\r\n            LogError(\"LOG FILE SUMMARY: %s - log had no passes or errors, ensure test was actually run\", LogFileToken);\r\n        }\r\n\r\n        CloseHandle(LinuxLogFile);\r\n        LinuxLogFile = INVALID_HANDLE_VALUE;\r\n        LogFileToken = wcstok(NULL, LXSS_TEST_LOG_SEPARATOR_CHAR, &TokenState);\r\n    }\r\n\r\n    Status = STATUS_SUCCESS;\r\n\r\nErrorExit:\r\n    if (LinuxLogFile != INVALID_HANDLE_VALUE)\r\n    {\r\n        CloseHandle(LinuxLogFile);\r\n    }\r\n\r\n    if ((TestRecord.NumberOfErrors == 0) && (TestRecord.NumberOfPasses > 0))\r\n    {\r\n        *TestPassed = TRUE;\r\n    }\r\n\r\n    return Status;\r\n}\r\n\r\nNTSTATUS\r\nLxsstuParseLogFile(__in HANDLE FileHandle, __in PLXSS_TEST_LAUNCHER_TEST TestRecord)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Parses a single log file.\r\n\r\nArguments:\r\n\r\n    TestName - Name of the test.\r\n\r\n    LogFileName - string containing the log files for the test separated by\r\n        LXSS_TEST_LOG_SEPARATOR_CHAR.\r\n\r\nReturn Value:\r\n\r\n    NTSTATUS\r\n\r\n--*/\r\n\r\n{\r\n\r\n    PBYTE Buffer;\r\n    DWORD BytesRead;\r\n    DWORD FileSize;\r\n    DWORD FileSizeHigh;\r\n    PCHAR Message;\r\n    LXSS_TEST_LAUNCHER_MESSAGE_TYPE MessageType;\r\n    NTSTATUS Status;\r\n    PCHAR Token;\r\n\r\n    Buffer = NULL;\r\n    Status = STATUS_UNSUCCESSFUL;\r\n\r\n    FileSize = GetFileSize(FileHandle, &FileSizeHigh);\r\n    Buffer = (PBYTE)ALLOC(FileSize + 1);\r\n    if (Buffer == NULL)\r\n    {\r\n        goto ErrorExit;\r\n    }\r\n\r\n    Buffer[FileSize] = '\\0';\r\n\r\n    do\r\n    {\r\n        RtlZeroMemory(Buffer, FileSize);\r\n        if (ReadFile(FileHandle, Buffer, FileSize, &BytesRead, NULL) == FALSE)\r\n        {\r\n\r\n            Status = STATUS_UNSUCCESSFUL;\r\n            LogError(\"ReadFile failed, LastError %#x\", GetLastError());\r\n            goto ErrorExit;\r\n        }\r\n\r\n        if (BytesRead == 0)\r\n        {\r\n            break;\r\n        }\r\n\r\n        //\r\n        // Parse the log line-by-line.\r\n        //\r\n\r\n        Token = strtok((PCHAR)Buffer, \"\\n\");\r\n        while (Token != NULL)\r\n        {\r\n\r\n            //\r\n            // A well-formed message begins with a timestamp and then is either\r\n            // a start, info, error, or pass message.  For example:\r\n            // [12:30:05.432] ERROR: Something went wrong!\r\n            //\r\n            // Anything that does not fit this format is re-logged an an \"info\"\r\n            // message.\r\n            //\r\n\r\n            MessageType = LogInfoMessage;\r\n            if (Token[0] == '[')\r\n            {\r\n                Message = strchr(Token, ' ');\r\n                if ((Message == NULL) || (strlen(Message) < 2))\r\n                {\r\n                    break;\r\n                }\r\n\r\n                switch (Message[1])\r\n                {\r\n                case 'E':\r\n                case 'R':\r\n                    MessageType = LogErrorMessage;\r\n                    break;\r\n\r\n                case 'P':\r\n                    MessageType = LogPassMessage;\r\n                    break;\r\n                }\r\n            }\r\n\r\n            switch (MessageType)\r\n            {\r\n            case LogInfoMessage:\r\n                if (g_RelogEverything != FALSE)\r\n                {\r\n                    LogInfo(\"%S\", Token);\r\n                }\r\n\r\n                break;\r\n\r\n            case LogErrorMessage:\r\n                TestRecord->NumberOfErrors += 1;\r\n                if (g_RelogEverything != FALSE)\r\n                {\r\n                    LogError(\"%S\", Token);\r\n                }\r\n\r\n                break;\r\n\r\n            case LogPassMessage:\r\n                TestRecord->NumberOfPasses += 1;\r\n                if (g_RelogEverything != FALSE)\r\n                {\r\n                    LogPass(\"%S\", Token);\r\n                }\r\n\r\n                break;\r\n\r\n                DEFAULT_UNREACHABLE;\r\n            }\r\n\r\n            Token = strtok(NULL, \"\\n\");\r\n        }\r\n    } while (BytesRead > 0);\r\n\r\n    Status = STATUS_SUCCESS;\r\n\r\nErrorExit:\r\n    if (Buffer != NULL)\r\n    {\r\n        FREE(Buffer);\r\n    }\r\n\r\n    return Status;\r\n}\r\n\r\nVOID LxsstuRunTest(_In_ PCWSTR CommandLine, _In_opt_ PCWSTR LogFileName, _In_opt_ PCWSTR Username) noexcept(false)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Run an individual test.\r\n\r\nArguments:\r\n\r\n    CommandLine - Command line path and arguments to pass\r\n    LogFileName - Name of the linux log file\r\n    Username - User to run the test as, if one is not supplied the test\r\n        will be run as root\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n\r\n    BOOL TestPassed;\r\n    std::wstring LaunchArguments{};\r\n\r\n    if (ARGUMENT_PRESENT(Username))\r\n    {\r\n        LaunchArguments += WSL_USER_ARG L\" \";\r\n        LaunchArguments += Username;\r\n        LaunchArguments += L\" \";\r\n    }\r\n\r\n    LaunchArguments += CommandLine;\r\n    DWORD ExitCode = LxsstuLaunchWsl(LaunchArguments.c_str());\r\n    LogInfo(\"Test process exited with: %lu\", ExitCode);\r\n\r\n    //\r\n    // Parse the contents of the linux log(s) files and relog.\r\n    //\r\n\r\n    if (ARGUMENT_PRESENT(LogFileName))\r\n    {\r\n        THROW_IF_NTSTATUS_FAILED(LxsstuParseLinuxLogFiles(LogFileName, &TestPassed));\r\n\r\n        VERIFY_IS_TRUE(TestPassed);\r\n    }\r\n\r\n    VERIFY_ARE_EQUAL(0, ExitCode);\r\n\r\n    return;\r\n}\r\n\r\nbool ModuleSetup(VOID)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Configures the machine to run tests\r\n\r\nArguments:\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n// Don't crash for unknown exceptions (makes debugging testpasses harder)\r\n#ifndef _DEBUG\r\n    wil::g_fResultFailFastUnknownExceptions = false;\r\n#endif\r\n\r\n    WslTraceLoggingInitialize(LxssTelemetryProvider, true);\r\n    wsl::windows::common::EnableContextualizedErrors(false);\r\n\r\n    auto getOptionalTestParam = [](LPCWSTR Name) -> std::optional<std::wstring> {\r\n        WEX::Common::String Value;\r\n\r\n        WEX::TestExecution::RuntimeParameters::TryGetValue(Name, Value);\r\n\r\n        return Value.IsEmpty() ? std::optional<std::wstring>() : static_cast<LPCWSTR>(Value);\r\n    };\r\n\r\n    auto getTestParam = [&](LPCWSTR Name) -> std::wstring {\r\n        auto value = getOptionalTestParam(Name);\r\n        if (!value.has_value())\r\n        {\r\n            const std::wstring error = L\"Missing TE argument: \" + std::wstring(Name);\r\n            VERIFY_FAIL(error.c_str());\r\n        }\r\n\r\n        return value.value();\r\n    };\r\n\r\n    try\r\n    {\r\n        const auto buildString = wsl::windows::common::registry::ReadString(\r\n            HKEY_LOCAL_MACHINE, L\"SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\", L\"BuildLabEx\");\r\n\r\n        LogInfo(\"OS build string: %ls\", buildString.c_str());\r\n    }\r\n    CATCH_LOG();\r\n\r\n    try\r\n    {\r\n        const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n        g_originalDefaultDistro = wsl::windows::common::registry::ReadString(userKey.get(), nullptr, L\"DefaultDistribution\", L\"\");\r\n    }\r\n    CATCH_LOG();\r\n\r\n    g_originalConfig = LxssWriteWslConfig(LxssGenerateTestConfig());\r\n\r\n    const auto redirectStdout = getOptionalTestParam(L\"RedirectStdout\");\r\n    const auto redirectStderr = getOptionalTestParam(L\"RedirectStderr\");\r\n\r\n    if (redirectStdout.has_value())\r\n    {\r\n        g_OriginalStdout = LxssRedirectOutput(STD_OUTPUT_HANDLE, redirectStdout.value());\r\n    }\r\n\r\n    if (redirectStderr.has_value())\r\n    {\r\n        g_OriginalStderr = LxssRedirectOutput(STD_ERROR_HANDLE, redirectStderr.value());\r\n    }\r\n\r\n    g_dumpFolder = getOptionalTestParam(L\"DumpFolder\").value_or(L\".\");\r\n    g_dumpToolPath = getOptionalTestParam(L\"DumpTool\");\r\n    g_pipelineBuildId = getOptionalTestParam(L\"PipelineBuildId\").value_or(L\"\");\r\n\r\n    if (!g_pipelineBuildId.empty())\r\n    {\r\n        LogInfo(\"Pipeline build id: %ls\", g_pipelineBuildId.c_str());\r\n    }\r\n\r\n    WEX::TestExecution::RuntimeParameters::TryGetValue(L\"WerReport\", g_enableWerReport);\r\n    WEX::TestExecution::RuntimeParameters::TryGetValue(L\"LogDmesg\", g_LogDmesgAfterEachTest);\r\n\r\n    g_WatchdogTimer = CreateThreadpoolTimer(LxsstuWatchdogTimer, nullptr, nullptr);\r\n    VERIFY_IS_NOT_NULL(g_WatchdogTimer);\r\n\r\n    ULARGE_INTEGER fileTimeConvert{};\r\n    fileTimeConvert.QuadPart = LXSS_WATCHDOG_TIMEOUT;\r\n    fileTimeConvert.QuadPart *= (-1 * 1000 * 10i64); // fileTime is unsigned- took out -1; check if this causes errors later\r\n    FILETIME DueTime{};\r\n    DueTime.dwLowDateTime = fileTimeConvert.LowPart;\r\n    DueTime.dwHighDateTime = fileTimeConvert.HighPart;\r\n    SetThreadpoolTimer(g_WatchdogTimer, &DueTime, 0, LXSS_WATCHDOG_TIMEOUT_WINDOW);\r\n\r\n    const auto version = getTestParam(L\"Version\");\r\n    if (version == L\"1\")\r\n    {\r\n        g_VmMode = false;\r\n    }\r\n    else if (version == L\"2\")\r\n    {\r\n        g_VmMode = true;\r\n    }\r\n    else\r\n    {\r\n        LogError(\"Unexpected version: %ls\", version.c_str());\r\n        VERIFY_FAIL();\r\n    }\r\n\r\n    g_testDistroPath = getTestParam(L\"DistroPath\");\r\n\r\n    const auto setupScript = getOptionalTestParam(L\"SetupScript\");\r\n    if (!setupScript.has_value())\r\n    {\r\n        // If no setup script is present, mark test_distro as the default distro here for convenience.\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--set-default \" LXSS_DISTRO_NAME_TEST_L), 0L);\r\n\r\n        return true;\r\n    }\r\n\r\n    std::wstring Cmd =\r\n        L\"Powershell \\\r\n        -NoProfile \\\r\n        -ExecutionPolicy Bypass \\\r\n        -Command \\\"\" +\r\n        setupScript.value() + L\" -Version '\" + getTestParam(L\"Version\") + L\"'\" + L\" -DistroPath \" + g_testDistroPath +\r\n        L\" -DistroName \" + LXSS_DISTRO_NAME_TEST_L + L\" -Package '\" + getTestParam(L\"Package\") + L\"'\" + L\" -UnitTestsPath \" +\r\n        getOptionalTestParam(L\"UnitTestsPath\").value_or(L\"$null\");\r\n\r\n    if (getOptionalTestParam(L\"AllowUnsigned\") == L\"1\")\r\n    {\r\n        Cmd += L\" -AllowUnsigned\";\r\n    }\r\n\r\n    Cmd += +L\"\\\"\";\r\n\r\n    LogInfo(\"Running test setup command: %ls\", Cmd.c_str());\r\n\r\n    const auto ExitCode = LxsstuRunCommand(Cmd.data());\r\n    if (ExitCode != 0)\r\n    {\r\n        THROW_HR_MSG(E_FAIL, \"Test setup returned non-zero exit code %lu\", ExitCode);\r\n    }\r\n\r\n    return true;\r\n}\r\n\r\nbool ModuleCleanup(VOID)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Called after the tests cases have been executed.\r\n    Reverts WSL version upgrades, if any.\r\n\r\nArguments:\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n    LogInfo(\"Exiting UnitTests module\");\r\n\r\n    //\r\n    // Release the watchdog timer.\r\n    //\r\n\r\n    if (g_WatchdogTimer != NULL)\r\n    {\r\n        SetThreadpoolTimer(g_WatchdogTimer, nullptr, 0, 0);\r\n        WaitForThreadpoolTimerCallbacks(g_WatchdogTimer, true);\r\n        CloseThreadpoolTimer(g_WatchdogTimer);\r\n    }\r\n\r\n    // Save the Appx & defender logs in the dump folder\r\n    if (!g_pipelineBuildId.empty())\r\n    {\r\n        auto commandLine = std::format(L\"Get-AppPackageLog -All > \\\"{}\\\\appx-logs.txt\\\"\", g_dumpFolder);\r\n        LxsstuLaunchPowershellAndCaptureOutput(commandLine.data());\r\n\r\n        commandLine = std::format(L\"Get-MpThreatDetection > \\\"{}\\\\Get-MpThreatDetection.txt\\\"\", g_dumpFolder);\r\n        LxsstuLaunchPowershellAndCaptureOutput(commandLine.data());\r\n\r\n        commandLine = std::format(L\"Get-MpThreat > \\\"{}\\\\Get-MpThreat.txt\\\"\", g_dumpFolder);\r\n        LxsstuLaunchPowershellAndCaptureOutput(commandLine.data());\r\n\r\n        commandLine = std::format(L\"Get-MpPreference > \\\"{}\\\\Get-MpPreference.txt\\\"\", g_dumpFolder);\r\n        LxsstuLaunchPowershellAndCaptureOutput(commandLine.data());\r\n    }\r\n\r\n    if (!g_originalConfig.empty())\r\n    {\r\n        LogInfo(\"Restoring .wslconfig\");\r\n        LxssWriteWslConfig(g_originalConfig);\r\n    }\r\n\r\n    if (!g_originalDefaultDistro.empty())\r\n    {\r\n        // Edge case: If the previous default distro was the test distro, it might have been deleted during the testpass.\r\n        // Validate the distro exists before restoring.\r\n\r\n        const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n\r\n        try\r\n        {\r\n            wsl::windows::common::registry::OpenKey(userKey.get(), g_originalDefaultDistro.c_str(), KEY_READ);\r\n        }\r\n        catch (...)\r\n        {\r\n            LogInfo(\"Previous default distro doesn't exist anymore: '%ls', skipping restore\", g_originalDefaultDistro.c_str());\r\n            return true;\r\n        }\r\n\r\n        LogInfo(\"Restoring default distro: '%ls\", g_originalDefaultDistro.c_str());\r\n\r\n        wsl::windows::common::registry::WriteString(userKey.get(), nullptr, L\"DefaultDistribution\", g_originalDefaultDistro.c_str());\r\n    }\r\n\r\n    WslTraceLoggingUninitialize();\r\n\r\n    return true;\r\n}\r\n\r\nHANDLE\r\nLxssRedirectOutput(_In_ DWORD Stream, _In_ const std::wstring& File)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Redirect a standard stream to a file\r\n\r\nArguments:\r\n    Stream - The stream to redirect\r\n\r\n    File - The file to redirect the output to\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n    const HANDLE OriginalHandle = GetStdHandle(Stream);\r\n\r\n    SECURITY_ATTRIBUTES Attributes = {0};\r\n    Attributes.nLength = sizeof(Attributes);\r\n    Attributes.bInheritHandle = true;\r\n\r\n    const auto Handle =\r\n        CreateFileW(File.c_str(), FILE_APPEND_DATA, FILE_SHARE_READ, &Attributes, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);\r\n\r\n    VERIFY_IS_NOT_NULL(Handle);\r\n\r\n    VERIFY_IS_TRUE(SetStdHandle(Stream, Handle));\r\n\r\n    return OriginalHandle;\r\n}\r\n\r\nvoid CreateUser(_In_ const std::wstring& Username, _Out_ PULONG Uid, _Out_ PULONG Gid)\r\n{\r\n    //\r\n    // Create the user account.\r\n    //\r\n    // N.B. The user may already exist if the test was run previously.\r\n    //\r\n\r\n    std::wstring CreateUser{L\"/usr/sbin/adduser --quiet --force-badname --disabled-password --gecos \\\"\\\" \"};\r\n    CreateUser += Username.c_str();\r\n    LxsstuLaunchWsl(CreateUser.c_str());\r\n\r\n    //\r\n    // Create an unnamed pipe to read the output of the launched commands.\r\n    //\r\n\r\n    wil::unique_handle ReadPipe;\r\n    wil::unique_handle WritePipe;\r\n    THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&ReadPipe, &WritePipe, NULL, 0));\r\n\r\n    //\r\n    // Mark the write end of the pipe as inheritable.\r\n    //\r\n\r\n    THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(WritePipe.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));\r\n\r\n    //\r\n    // Query the UID.\r\n    //\r\n\r\n    std::wstring QueryUid{L\"/usr/bin/id -u \"};\r\n    QueryUid += Username.c_str();\r\n    THROW_HR_IF(E_UNEXPECTED, (LxsstuLaunchWsl(QueryUid.c_str(), nullptr, WritePipe.get()) != 0));\r\n\r\n    CHAR Buffer[64];\r\n    DWORD BytesRead;\r\n    THROW_IF_WIN32_BOOL_FALSE(ReadFile(ReadPipe.get(), Buffer, (sizeof(Buffer) - 1), &BytesRead, NULL));\r\n    Buffer[BytesRead] = ANSI_NULL;\r\n    const ULONG UidLocal = std::stoul(Buffer, nullptr, 10);\r\n\r\n    //\r\n    // Query the GID.\r\n    //\r\n\r\n    std::wstring QueryGid{L\"/usr/bin/id -g \"};\r\n    QueryGid += Username.c_str();\r\n    THROW_HR_IF(E_UNEXPECTED, (LxsstuLaunchWsl(QueryGid.c_str(), nullptr, WritePipe.get()) != 0));\r\n\r\n    THROW_IF_WIN32_BOOL_FALSE(ReadFile(ReadPipe.get(), Buffer, (sizeof(Buffer) - 1), &BytesRead, NULL));\r\n    Buffer[BytesRead] = ANSI_NULL;\r\n    const ULONG GidLocal = std::stoul(Buffer, nullptr, 10);\r\n\r\n    //\r\n    // Return the queried values to the user.\r\n    //\r\n\r\n    *Uid = UidLocal;\r\n    *Gid = GidLocal;\r\n}\r\n\r\nstd::pair<HANDLE, HANDLE> UseOriginalStdHandles(VOID)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Restores the original stdout & stderr handles, if any.\r\n\r\nArguments:\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    A pair of the previous stdout & stderr handles.\r\n\r\n--*/\r\n\r\n{\r\n    HANDLE PreviousStdout = GetStdHandle(STD_OUTPUT_HANDLE);\r\n    HANDLE PreviousStderr = GetStdHandle(STD_ERROR_HANDLE);\r\n\r\n    if (g_OriginalStdout != nullptr)\r\n    {\r\n        VERIFY_IS_TRUE(SetStdHandle(STD_OUTPUT_HANDLE, g_OriginalStdout));\r\n    }\r\n\r\n    if (g_OriginalStderr != nullptr)\r\n    {\r\n        VERIFY_IS_TRUE(SetStdHandle(STD_ERROR_HANDLE, g_OriginalStderr));\r\n    }\r\n\r\n    return {PreviousStdout, PreviousStderr};\r\n}\r\n\r\nvoid RestoreTestStdHandles(_In_ const std::pair<HANDLE, HANDLE>& handles)\r\n\r\n/*++\r\n\r\nRoutine Description:\r\n\r\n    Assign stdout & stderr handles.\r\n\r\nArguments:\r\n    None.\r\n\r\nReturn Value:\r\n\r\n    None.\r\n\r\n--*/\r\n\r\n{\r\n    VERIFY_IS_TRUE(SetStdHandle(STD_OUTPUT_HANDLE, handles.first));\r\n    VERIFY_IS_TRUE(SetStdHandle(STD_ERROR_HANDLE, handles.second));\r\n}\r\n\r\nbool TryLoadDnsResolverMethods() noexcept\r\n{\r\n    constexpr auto c_dnsModuleName = L\"dnsapi.dll\";\r\n    const wil::shared_hmodule dnsModule{LoadLibraryEx(c_dnsModuleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)};\r\n    if (!dnsModule)\r\n    {\r\n        return false;\r\n    }\r\n\r\n    try\r\n    {\r\n        // attempt to find the functions for the DNS tunneling OS APIs.\r\n        static LxssDynamicFunction<decltype(DnsQueryRaw)> dnsQueryRaw{dnsModule, \"DnsQueryRaw\"};\r\n        static LxssDynamicFunction<decltype(DnsCancelQueryRaw)> dnsCancelQueryRaw{dnsModule, \"DnsCancelQueryRaw\"};\r\n        static LxssDynamicFunction<decltype(DnsQueryRawResultFree)> dnsQueryRawResultFree{dnsModule, \"DnsQueryRawResultFree\"};\r\n\r\n        // Make a dummy call to the DNS APIs to verify if they are working. The APIs are going to be present\r\n        // on older OS versions, where they can be turned on/off using a KIR. If the KIR is turned off, the APIs\r\n        // will be unusable and will return ERROR_CALL_NOT_IMPLEMENTED.\r\n        THROW_HR_IF(E_NOTIMPL, dnsQueryRaw(nullptr, nullptr) == ERROR_CALL_NOT_IMPLEMENTED);\r\n    }\r\n    catch (...)\r\n    {\r\n        return false;\r\n    }\r\n    return true;\r\n}\r\n\r\nbool AreExperimentalNetworkingFeaturesSupported()\r\n{\r\n    constexpr auto NETWORKING_EXPERIMENTAL_FLOOR_BUILD = 25885;\r\n    constexpr auto GALLIUM_FLOOR_BUILD = 25846;\r\n    const auto build = wsl::windows::common::helpers::GetWindowsVersion();\r\n    return ((build.BuildNumber < GALLIUM_FLOOR_BUILD) || (build.BuildNumber >= GALLIUM_FLOOR_BUILD && build.BuildNumber >= NETWORKING_EXPERIMENTAL_FLOOR_BUILD));\r\n}\r\n\r\nbool IsHyperVFirewallSupported() noexcept\r\n{\r\n    try\r\n    {\r\n        // Query for the Hyper-V Firewall profile object. If this object is successfully queried, then\r\n        // the OS has the necessary Hyper-V firewall support.\r\n        LxsstuLaunchPowershellAndCaptureOutput(L\"Get-NetFirewallHyperVProfile\");\r\n    }\r\n    catch (...)\r\n    {\r\n        return false;\r\n    }\r\n    return true;\r\n}\r\n\r\nstd::optional<GUID> GetDistributionId(LPCWSTR Name)\r\n{\r\n    // Get the GUID of the test distro\r\n    wsl::windows::common::SvcComm service;\r\n    for (const auto& e : service.EnumerateDistributions())\r\n    {\r\n        if (wsl::shared::string::IsEqual(e.DistroName, Name))\r\n        {\r\n            return e.DistroGuid;\r\n        }\r\n    }\r\n\r\n    return {};\r\n}\r\n\r\nwil::unique_hkey OpenDistributionKey(LPCWSTR Name)\r\n{\r\n    const auto id = GetDistributionId(Name);\r\n    if (!id.has_value())\r\n    {\r\n        return {};\r\n    }\r\n\r\n    const auto idString = wsl::shared::string::GuidToString<wchar_t>(id.value());\r\n\r\n    const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n    return wsl::windows::common::registry::OpenKey(userKey.get(), idString.c_str(), KEY_ALL_ACCESS);\r\n}\r\n\r\nbool WslShutdown()\r\n{\r\n    return VERIFY_ARE_EQUAL(0u, LxsstuLaunchWsl(WSL_SHUTDOWN_ARG));\r\n}\r\n\r\nvoid TerminateDistribution(LPCWSTR DistributionName)\r\n{\r\n    VERIFY_ARE_EQUAL(0u, LxsstuLaunchWsl(std::format(L\"{} {}\", WSL_TERMINATE_ARG, DistributionName)));\r\n}\r\n\r\nvoid ValidateOutput(LPCWSTR CommandLine, const std::wstring& ExpectedOutput, const std::wstring& ExpectedWarnings, int ExitCode)\r\n{\r\n    auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(CommandLine, ExitCode);\r\n\r\n    VERIFY_ARE_EQUAL(ExpectedOutput, output);\r\n    VERIFY_ARE_EQUAL(ExpectedWarnings, warnings);\r\n}\r\n\r\n// Trim helper method\r\n\r\nvoid Trim(std::wstring& string)\r\n{\r\n    // Remove any extra chars (lf, spaces, ...)\r\n    std::erase_if(string, [](auto c) { return !isalnum(c); });\r\n}\r\n\r\nScopedEnvVariable::ScopedEnvVariable(const std::wstring& Name, const std::wstring& Value) : m_name(Name)\r\n{\r\n    VERIFY_IS_TRUE(SetEnvironmentVariable(Name.c_str(), Value.c_str()));\r\n}\r\n\r\nScopedEnvVariable::~ScopedEnvVariable()\r\n{\r\n    VERIFY_IS_TRUE(SetEnvironmentVariable(m_name.c_str(), nullptr));\r\n}\r\n\r\nUniqueWebServer::UniqueWebServer(LPCWSTR Endpoint, LPCWSTR Content)\r\n{\r\n    auto cmd = std::format(\r\n        LR\"(Powershell.exe -NoProfile -ExecutionPolicy Bypass -Command \"\r\n$ErrorActionPreference = 'Stop'\r\n$server = New-Object System.Net.HttpListener\r\n$server.Prefixes.Add('{}')\r\n$server.Start()\r\nwhile ($true)\r\n{{\r\n    $context = $server.GetContext()\r\n    $context.Response.StatusCode\r\n    $content = [Text.Encoding]::UTF8.GetBytes('{}')\r\n    $context.Response.OutputStream.Write($content , 0, $content.length)\r\n    $context.Response.close()\r\n}}\")\",\r\n        Endpoint,\r\n        Content);\r\n\r\n    m_process = LxsstuStartProcess(cmd.data());\r\n}\r\n\r\nUniqueWebServer::UniqueWebServer(LPCWSTR Endpoint, const std::filesystem::path& File)\r\n{\r\n    auto cmd = std::format(\r\n        LR\"(Powershell.exe -NoProfile -ExecutionPolicy Bypass -Command \"\r\n$ErrorActionPreference = 'Stop'\r\n$server = New-Object System.Net.HttpListener\r\n$server.Prefixes.Add('{}')\r\n$server.Start()\r\nwhile ($true)\r\n{{\r\n    $context = $server.GetContext()\r\n    $context.Response.StatusCode\r\n    $content = [System.IO.File]::ReadAllBytes('{}')\r\n    $context.Response.ContentLength64 = $content.length\r\n    $context.Response.ContentType = 'application/octet-stream'\r\n    $context.Response.OutputStream.Write($content, 0, $content.length)\r\n    $context.Response.close()\r\n}}\")\",\r\n        Endpoint,\r\n        File.wstring());\r\n\r\n    m_process = LxsstuStartProcess(cmd.data());\r\n}\r\n\r\nUniqueWebServer::~UniqueWebServer()\r\n{\r\n    if (!TerminateProcess(m_process.get(), 0))\r\n    {\r\n        LogError(\"TerminateProcess failed, %lu\", GetLastError());\r\n    }\r\n}\r\n\r\nDistroFileChange::DistroFileChange(LPCWSTR Path, bool exists) : m_path(Path)\r\n{\r\n    if (exists)\r\n    {\r\n        m_originalContent = LxsstuLaunchWslAndCaptureOutput(std::format(L\"cat '{}'\", m_path)).first;\r\n    }\r\n}\r\n\r\nDistroFileChange::~DistroFileChange()\r\n{\r\n    if (m_originalContent.has_value())\r\n    {\r\n        SetContent(m_originalContent->c_str());\r\n    }\r\n    else\r\n    {\r\n        Delete();\r\n    }\r\n}\r\n\r\nvoid DistroFileChange::SetContent(LPCWSTR Content)\r\n{\r\n    const auto cmd = LxssGenerateWslCommandLine(std::format(L\" -u root cat > '{}'\", m_path).c_str());\r\n    wsl::windows::common::SubProcess process(nullptr, cmd.c_str());\r\n\r\n    auto [read, write] = CreateSubprocessPipe(true, false);\r\n\r\n    process.SetStdHandles(read.get(), nullptr, nullptr);\r\n    const auto processHandle = process.Start();\r\n\r\n    const auto utf8content = wsl::shared::string::WideToMultiByte(Content);\r\n    auto index = 0;\r\n\r\n    while (index < utf8content.size())\r\n    {\r\n        DWORD written{};\r\n\r\n        VERIFY_IS_TRUE(WriteFile(write.get(), utf8content.data() + index, static_cast<DWORD>(utf8content.size() - index), &written, nullptr));\r\n\r\n        index += written;\r\n    }\r\n\r\n    write.reset();\r\n\r\n    VERIFY_ARE_EQUAL(wsl::windows::common::SubProcess::GetExitCode(processHandle.get()), 0L);\r\n}\r\n\r\nvoid DistroFileChange::Delete()\r\n{\r\n    VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"-u root rm -f '{}'\", m_path).c_str()), 0L);\r\n}\r\n"
  },
  {
    "path": "test/windows/Common.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Common.h\r\n\r\nAbstract:\r\n\r\n    This file contains common definitions used for testing.\r\n\r\n--*/\r\n\r\n#pragma once\r\n\r\n#include <WexTestClass.h>\r\n#include <LogController.h>\r\n#include <future>\r\n#include <vector>\r\n#include <string>\r\n#include <thread>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include \"precomp.h\"\r\n#include \"lxsstest.h\"\r\n#include \"wslutil.h\"\r\n#include \"WslCoreConfig.h\"\r\n\r\n//\r\n// N.B. This is also defined in 'lxtcommon.h' & 'lxsetup.ps1'. Update those\r\n//      files too, if the distro name changes here.\r\n//\r\n#define LXSS_DISTRO_NAME_TEST \"test_distro\"\r\n#define LXSS_DISTRO_NAME_TEST_L WIDEN(LXSS_DISTRO_NAME_TEST)\r\n\r\n#define LXSST_REMOVE_DISTRO_CONF_COMMAND_LINE L\"-u root -e rm /etc/wsl.conf\"\r\n\r\n#define WSL1_TEST_ONLY() \\\r\n    if (LxsstuVmMode()) \\\r\n    { \\\r\n        LogSkipped(\"This test is only applicable to WSL1\"); \\\r\n        return; \\\r\n    }\r\n\r\n#define WSL2_TEST_ONLY() \\\r\n    if (!LxsstuVmMode()) \\\r\n    { \\\r\n        LogSkipped(\"This test is only applicable to WSL2\"); \\\r\n        return; \\\r\n    }\r\n\r\n// macro for skipping tests that are currently failing due to not yet being fully implemented\r\n#define SKIP_TEST_NOT_IMPL() \\\r\n    { \\\r\n        LogSkipped(\"This test is skipped; not yet fully implemented\"); \\\r\n        return; \\\r\n    }\r\n\r\n#define WINDOWS_11_TEST_ONLY() \\\r\n    if (!wsl::windows::common::helpers::IsWindows11OrAbove()) \\\r\n    { \\\r\n        LogSkipped(\"This test is only applicable to Windows 11 and above\"); \\\r\n        return; \\\r\n    }\r\n\r\n#define WSL_TEST_VERSION_REQUIRED(_version) \\\r\n    if (wsl::windows::common::helpers::GetWindowsVersion().BuildNumber < _version) \\\r\n    { \\\r\n        LogSkipped(\"This test requires Windows version %u or later\", _version); \\\r\n        return; \\\r\n    }\r\n\r\n#define SKIP_TEST_ARM64() \\\r\n    { \\\r\n        if constexpr (wsl::shared::Arm64) \\\r\n        { \\\r\n            LogSkipped(\"This test is skipped for ARM64\"); \\\r\n            return; \\\r\n        } \\\r\n    }\r\n\r\n#define SKIP_TEST_UNSTABLE() \\\r\n    { \\\r\n        LogSkipped(\"This test is skipped because it's unstable\"); \\\r\n        return; \\\r\n    }\r\n\r\n#define WSL_SETTINGS_TEST() \\\r\n    if constexpr (!WSL_BUILD_WSL_SETTINGS) \\\r\n    { \\\r\n        LogSkipped(\"This test is skipped wslsettings wasn't built\"); \\\r\n        return; \\\r\n    }\r\n\r\n#define WSL_TEST_CLASS(_name) \\\r\n    BEGIN_TEST_CLASS(_name) \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"LxssManager.dll\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"LxssManagerProxyStub.dll\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"wslclient.dll\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"wslservice.exe\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"WslServiceProxyStub.dll\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"wslhost.exe\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"wslrelay.exe\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"wslconfig.exe\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"wsl.exe\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"wslg.exe\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"msrdc.exe\") \\\r\n        TEST_CLASS_PROPERTY(L\"BinaryUnderTest\", L\"msal.wsl.proxy.exe\") \\\r\n    END_TEST_CLASS()\r\n\r\n//\r\n// RAII Wrapper that prevents the UVM from timing out\r\n//\r\nclass WslKeepAlive\r\n{\r\npublic:\r\n    WslKeepAlive(HANDLE Token = nullptr);\r\n\r\n    ~WslKeepAlive();\r\n\r\n    WslKeepAlive(const WslKeepAlive&) = delete;\r\n    WslKeepAlive(WslKeepAlive&&) = delete;\r\n    const WslKeepAlive& operator=(WslKeepAlive&&) = delete;\r\n    const WslKeepAlive& operator=(WslKeepAlive&) = delete;\r\n\r\n    void Set();\r\n\r\n    void Run();\r\n\r\n    void Reset();\r\n\r\nprivate:\r\n    wil::unique_handle m_write;\r\n    wil::unique_handle m_read;\r\n    std::thread m_thread;\r\n    std::optional<std::promise<void>> m_running;\r\n    HANDLE m_token = nullptr;\r\n};\r\n\r\n//\r\n// RAII Wrapper for .wslconfig changes\r\n//\r\n\r\nclass WslConfigChange\r\n{\r\npublic:\r\n    WslConfigChange(const std::wstring& Content);\r\n\r\n    ~WslConfigChange();\r\n    WslConfigChange(const WslConfigChange&) = delete;\r\n    WslConfigChange(WslConfigChange&& other);\r\n    const WslConfigChange& operator=(WslConfigChange&&) = delete;\r\n    const WslConfigChange& operator=(WslConfigChange&) = delete;\r\n\r\n    static std::wstring Update(const std::wstring& Content);\r\n\r\nprivate:\r\n    std::optional<std::wstring> m_originalContent;\r\n};\r\n\r\ntemplate <typename T>\r\nclass RegistryKeyChange\r\n{\r\npublic:\r\n    RegistryKeyChange(HKEY Hive, LPCWSTR Key, LPCWSTR Name, const T& Value) : m_value(Name)\r\n    {\r\n        m_key = wsl::windows::common::registry::CreateKey(Hive, Key, KEY_ALL_ACCESS);\r\n\r\n        m_originalValue = Get();\r\n\r\n        Set(Value);\r\n    }\r\n\r\n    ~RegistryKeyChange()\r\n    {\r\n        if (m_key)\r\n        {\r\n            if (m_originalValue.has_value())\r\n            {\r\n                Set(m_originalValue.value());\r\n            }\r\n            else\r\n            {\r\n                wsl::windows::common::registry::DeleteKeyValue(m_key.get(), m_value.c_str());\r\n            }\r\n        }\r\n    }\r\n\r\n    RegistryKeyChange(const RegistryKeyChange&) = delete;\r\n    RegistryKeyChange(RegistryKeyChange&& other) = default;\r\n    const RegistryKeyChange& operator=(RegistryKeyChange&& other)\r\n    {\r\n        m_key = std::move(other.m_key);\r\n        m_value = std::move(other.m_value);\r\n    }\r\n\r\n    const RegistryKeyChange& operator=(RegistryKeyChange&) = delete;\r\n\r\n    void Set(const T& Value)\r\n    {\r\n        if constexpr (std::is_same_v<std::remove_reference_t<T>, DWORD>)\r\n        {\r\n            wsl::windows::common::registry::WriteDword(m_key.get(), nullptr, m_value.c_str(), Value);\r\n        }\r\n        else if constexpr (std::is_same_v<std::remove_reference_t<T>, std::wstring>)\r\n        {\r\n            wsl::windows::common::registry::WriteString(m_key.get(), nullptr, m_value.c_str(), Value.c_str());\r\n        }\r\n        else\r\n        {\r\n            static_assert(sizeof(T) != sizeof(T));\r\n        }\r\n    }\r\n\r\n    auto Get() const\r\n    {\r\n        if constexpr (std::is_same_v<T, DWORD>)\r\n        {\r\n            DWORD Value = 0;\r\n            DWORD Size = sizeof(Value);\r\n            const auto Result = RegGetValueW(m_key.get(), nullptr, m_value.c_str(), RRF_RT_REG_DWORD, nullptr, &Value, &Size);\r\n            if (Result == ERROR_SUCCESS)\r\n            {\r\n                WI_ASSERT(Size == sizeof(Value));\r\n                return std::optional<DWORD>{Value};\r\n            }\r\n            else if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND))\r\n            {\r\n                return std::optional<DWORD>{};\r\n            }\r\n            else\r\n            {\r\n                THROW_NTSTATUS(Result);\r\n            }\r\n        }\r\n        else if constexpr (std::is_same_v<std::remove_reference_t<T>, std::wstring>)\r\n        {\r\n            return wsl::windows::common::registry::ReadOptionalString(m_key.get(), nullptr, m_value.c_str());\r\n        }\r\n        else\r\n        {\r\n            static_assert(sizeof(T) != sizeof(T));\r\n        }\r\n    }\r\n\r\nprivate:\r\n    wil::unique_hkey m_key;\r\n    std::wstring m_value;\r\n    std::optional<T> m_originalValue;\r\n};\r\n\r\nclass ScopedEnvVariable\r\n{\r\npublic:\r\n    ScopedEnvVariable(const std::wstring& Name, const std::wstring& Value);\r\n    ~ScopedEnvVariable();\r\n\r\n    ScopedEnvVariable(const WslConfigChange&) = delete;\r\n    ScopedEnvVariable(WslConfigChange&&) = delete;\r\n    const ScopedEnvVariable& operator=(ScopedEnvVariable&&) = delete;\r\n    const ScopedEnvVariable& operator=(ScopedEnvVariable&) = delete;\r\n\r\nprivate:\r\n    std::wstring m_name;\r\n};\r\n\r\nclass UniqueWebServer\r\n{\r\npublic:\r\n    UniqueWebServer(LPCWSTR Endpoint, LPCWSTR ResponseContent);\r\n    UniqueWebServer(LPCWSTR Endpoint, const std::filesystem::path& path);\r\n    ~UniqueWebServer();\r\n    UniqueWebServer(const UniqueWebServer&) = delete;\r\n    UniqueWebServer(UniqueWebServer&&) = delete;\r\n\r\n    UniqueWebServer& operator=(const UniqueWebServer&) = delete;\r\n    UniqueWebServer& operator=(UniqueWebServer&&) = delete;\r\n\r\nprivate:\r\n    wil::unique_handle m_process;\r\n};\r\n\r\nclass DistroFileChange\r\n{\r\npublic:\r\n    DistroFileChange(LPCWSTR Path, bool exists = true);\r\n    ~DistroFileChange();\r\n    DistroFileChange(const DistroFileChange&) = delete;\r\n    DistroFileChange(DistroFileChange&&) = delete;\r\n    DistroFileChange& operator=(const DistroFileChange&) = delete;\r\n    DistroFileChange& operator=(DistroFileChange&&) = delete;\r\n\r\n    void SetContent(LPCWSTR Content);\r\n    void Delete();\r\n\r\nprivate:\r\n    std::optional<std::wstring> m_originalContent;\r\n    LPCWSTR m_path{};\r\n};\r\n\r\n//\r\n// Structs and enums.\r\n//\r\n\r\ntypedef struct _LXSS_TEST_LAUNCHER_TEST\r\n{\r\n    ULONG NumberOfErrors;\r\n    ULONG NumberOfPasses;\r\n} LXSS_TEST_LAUNCHER_TEST, *PLXSS_TEST_LAUNCHER_TEST;\r\n\r\ntypedef enum LXSS_TEST_LAUNCHER_MESSAGE_TYPE\r\n{\r\n    LogInfoMessage,\r\n    LogErrorMessage,\r\n    LogPassMessage\r\n} LXSS_TEST_LAUNCHER_MESSAGE_TYPE,\r\n    *PLXSS_TEST_LAUNCHER_MESSAGE_TYPE;\r\n\r\n// from nttpapi.h - need to find a way to include later\r\ntypedef LARGE_INTEGER TP_TIMESTAMP, *PTP_TIMESTAMP;\r\n\r\nstd::pair<wil::unique_handle, wil::unique_handle> CreateSubprocessPipe(\r\n    bool inheritRead, bool inheritWrite, DWORD bufferSize = 0, _In_opt_ SECURITY_ATTRIBUTES* sa = nullptr);\r\n\r\nstd::pair<DWORD, DWORD> GetServiceState(SC_HANDLE service);\r\n\r\nDWORD\r\nLxsstuLaunchWsl(\r\n    _In_opt_ LPCWSTR Arguments,\r\n    _In_opt_ HANDLE StandardInput = nullptr,\r\n    _In_opt_ HANDLE StandardOutput = nullptr,\r\n    _In_opt_ HANDLE StandardError = nullptr,\r\n    _In_opt_ HANDLE Token = nullptr,\r\n    _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);\r\n\r\nDWORD\r\nLxsstuLaunchWsl(\r\n    _In_opt_ const std::wstring& Arguments,\r\n    _In_opt_ HANDLE StandardInput = nullptr,\r\n    _In_opt_ HANDLE StandardOutput = nullptr,\r\n    _In_opt_ HANDLE StandardError = nullptr,\r\n    _In_opt_ HANDLE Token = nullptr);\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchWslAndCaptureOutput(\r\n    _In_ LPCWSTR Cmd,\r\n    _In_ int ExpectedExitCode = 0,\r\n    _In_opt_ HANDLE StandardInput = nullptr,\r\n    _In_opt_ HANDLE Token = nullptr,\r\n    _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT,\r\n    _In_ LPCWSTR Entrypoint = WSL_BINARY_NAME);\r\n\r\nstd::wstring LxssGenerateWslCommandLine(_In_opt_ LPCWSTR Arguments, _In_ LPCWSTR EntryPoint = WSL_BINARY_NAME);\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchWslAndCaptureOutput(\r\n    _In_ const std::wstring& Cmd,\r\n    _In_ int ExpectedExitCode = 0,\r\n    _In_opt_ HANDLE StandardInput = nullptr,\r\n    _In_opt_ HANDLE Token = nullptr,\r\n    _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT,\r\n    _In_ LPCWSTR EntryPoint = WSL_BINARY_NAME);\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchCommandAndCaptureOutput(\r\n    _In_ LPWSTR Cmd, _In_ LPCSTR StandardInput, _In_opt_ HANDLE Token = nullptr, _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);\r\n\r\nstd::tuple<std::wstring, std::wstring, int> LxsstuLaunchCommandAndCaptureOutputWithResult(\r\n    _In_ LPWSTR Cmd,\r\n    _In_opt_ HANDLE StandardInput = nullptr,\r\n    _In_opt_ HANDLE Token = nullptr,\r\n    _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchCommandAndCaptureOutput(\r\n    _In_ LPWSTR Cmd,\r\n    _In_ int ExpectedExitCode = 0,\r\n    _In_opt_ HANDLE StandardInput = nullptr,\r\n    _In_opt_ HANDLE Token = nullptr,\r\n    _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);\r\n\r\nDWORD\r\nLxsstuRunCommand(\r\n    _In_ LPWSTR Command,\r\n    _In_opt_ HANDLE StandardInput = nullptr,\r\n    _In_opt_ HANDLE StandardOutput = nullptr,\r\n    _In_opt_ HANDLE StandardError = nullptr,\r\n    _In_opt_ HANDLE Token = nullptr,\r\n    _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);\r\n\r\nwil::unique_handle LxsstuStartProcess(\r\n    _In_ LPWSTR Command,\r\n    _In_opt_ HANDLE StandardInput = nullptr,\r\n    _In_opt_ HANDLE StandardOutput = nullptr,\r\n    _In_opt_ HANDLE StandardError = nullptr,\r\n    _In_opt_ HANDLE Token = nullptr,\r\n    _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);\r\n\r\nwil::unique_file FileFromHandle(_Inout_ wil::unique_handle& Handle, _In_ const char* Mode);\r\n\r\nBOOL LxsstuInitialize(__in BOOLEAN RunInstanceTests);\r\n\r\nBOOL LxsstuVmMode(VOID);\r\n\r\nstd::pair<std::wstring, std::wstring> LxsstuLaunchPowershellAndCaptureOutput(_In_ const std::wstring& Cmd, _In_ int ExpectedExitCode = 0);\r\n\r\nVOID LxsstuUninitialize(__in BOOLEAN RunInstanceTests);\r\n\r\nvoid LxssLogKernelOutput();\r\n\r\nstd::wstring LxsstuGetTestDirectory(VOID);\r\n\r\nstd::wstring LxsstuGetLxssDirectory(VOID);\r\n\r\nVOID LxsstuInstanceTests(VOID);\r\n\r\nVOID __stdcall LxsstuWatchdogTimer(_Inout_ PTP_CALLBACK_INSTANCE Instance, _Inout_opt_ PVOID ThreadpoolTimerContext, _Inout_ PTP_TIMER Timer);\r\n\r\nstd::vector<std::wstring> LxssSplitString(_In_ const std::wstring& string, _In_ const std::wstring& delim = L\" \");\r\n\r\nvoid RestartWslService();\r\n\r\nwil::unique_handle GetNonElevatedToken();\r\n\r\nstd::wstring LxssWriteWslConfig(const std::wstring& Content);\r\n\r\nstd::string LxssWriteWslDistroConfig(const std::string& Content);\r\n\r\nenum class DrvFsMode\r\n{\r\n    WSL1,\r\n    Plan9,\r\n    Virtio9p,\r\n    VirtioFs\r\n};\r\n\r\nstruct TestConfigDefaults\r\n{\r\n    std::optional<size_t> vmIdleTimeout;\r\n    std::optional<bool> safeMode;\r\n    std::optional<bool> guiApplications;\r\n    std::optional<DrvFsMode> drvFsMode;\r\n    std::optional<wsl::core::NetworkingMode> networkingMode;\r\n    const std::optional<std::wstring> vmSwitch;\r\n    const std::optional<std::wstring> macAddress;\r\n    bool ipv6 = false;\r\n    std::optional<bool> dnsTunneling;\r\n    std::optional<std::wstring> dnsTunnelingIpAddress;\r\n    std::optional<bool> dnsProxy;\r\n    std::optional<bool> firewall;\r\n    std::optional<bool> autoProxy;\r\n    std::optional<std::wstring> kernel;\r\n    std::optional<std::wstring> kernelCommandLine;\r\n    std::optional<std::wstring> kernelModules;\r\n    std::optional<std::wstring> loadKernelModules;\r\n    std::optional<bool> loadDefaultKernelModules;\r\n    std::optional<bool> sparse;\r\n    std::optional<bool> hostAddressLoopback;\r\n    int crashDumpCount = 100;\r\n    std::optional<std::wstring> CrashDumpFolder;\r\n};\r\n\r\nstd::wstring LxssGenerateTestConfig(TestConfigDefaults Default = {});\r\n\r\nNTSTATUS\r\nLxsstuParseLinuxLogFiles(__in PCWSTR LogFileName, __out PBOOL TestPassed);\r\n\r\nVOID LxsstuRunTest(_In_ PCWSTR CommandLine, _In_opt_ PCWSTR LogFileName = NULL, _In_opt_ PCWSTR Username = nullptr) noexcept(false);\r\n\r\nNTSTATUS\r\nLxsstuParseLogFile(__in HANDLE FileHandle, __in PLXSS_TEST_LAUNCHER_TEST TestRecord);\r\n\r\nbool ModuleSetup();\r\n\r\nbool ModuleCleanup();\r\n\r\nHANDLE\r\nLxssRedirectOutput(_In_ DWORD stream, _In_ const std::wstring& file);\r\n\r\nstd::pair<HANDLE, HANDLE> UseOriginalStdHandles();\r\n\r\nvoid RestoreTestStdHandles(_In_ const std::pair<HANDLE, HANDLE>& Handles);\r\n\r\nvoid CreateUser(_In_ const std::wstring& Username, _Out_ PULONG Uid, _Out_ PULONG Gid);\r\n\r\nbool TryLoadDnsResolverMethods() noexcept;\r\n\r\nbool AreExperimentalNetworkingFeaturesSupported();\r\n\r\nbool IsHyperVFirewallSupported() noexcept;\r\n\r\nbool WslShutdown();\r\n\r\nvoid TerminateDistribution(LPCWSTR DistributionName = LXSS_DISTRO_NAME_TEST_L);\r\n\r\nvoid Trim(std::wstring& string);\r\n\r\ninline auto EnableSystemd(const std::string& extraConfig = \"\")\r\n{\r\n    // enable systemd on the test distro by editing /etc/wsl.conf\r\n    LxssWriteWslDistroConfig(\"[boot]\\nsystemd=true\\n\" + extraConfig);\r\n    TerminateDistribution();\r\n\r\n    return wil::scope_exit([] {\r\n        // clean up wsl.conf file\r\n        LxsstuLaunchWsl(LXSST_REMOVE_DISTRO_CONF_COMMAND_LINE);\r\n        TerminateDistribution();\r\n    });\r\n}\r\n\r\nstd::wstring EscapePath(std::wstring_view Path);\r\n\r\nvoid StopWslService();\r\n\r\nstd::optional<GUID> GetDistributionId(LPCWSTR Name);\r\nwil::unique_hkey OpenDistributionKey(LPCWSTR Name);\r\n\r\nvoid ValidateOutput(LPCWSTR CommandLine, const std::wstring& ExpectedOutput, const std::wstring& ExpectedWarnings = L\"\", int ExitCode = -1);"
  },
  {
    "path": "test/windows/DrvFsTests.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    DrvFsTests.cpp\r\n\r\nAbstract:\r\n\r\n    This file contains drvfs test cases.\r\n\r\n--*/\r\n\r\n#include \"precomp.h\"\r\n\r\n#include \"Common.h\"\r\n#include <AclAPI.h>\r\n#include <fstream>\r\n#include <filesystem>\r\n#include \"wslservice.h\"\r\n#include \"registry.hpp\"\r\n#include \"helpers.hpp\"\r\n#include \"svccomm.hpp\"\r\n#include <userenv.h>\r\n#include \"Distribution.h\"\r\n\r\n#define LXSST_DRVFS_TEST_DIR L\"C:\\\\drvfstest\"\r\n#define LXSST_DRVFS_RWX_TEST_FILE LXSST_DRVFS_TEST_DIR L\"\\\\rwx\"\r\n#define LXSST_DRVFS_READONLY_TEST_FILE LXSST_DRVFS_TEST_DIR L\"\\\\readonly\"\r\n#define LXSST_DRVFS_WRITEONLY_TEST_FILE LXSST_DRVFS_TEST_DIR L\"\\\\writeonly\"\r\n#define LXSST_DRVFS_EXECUTEONLY_TEST_FILE LXSST_DRVFS_TEST_DIR L\"\\\\executeonly\"\r\n#define LXSST_DRVFS_READONLYATTR_TEST_FILE LXSST_DRVFS_TEST_DIR L\"\\\\readonlyattr\"\r\n#define LXSST_DRVFS_READONLYATTRDEL_TEST_FILE LXSST_DRVFS_TEST_DIR L\"\\\\readonlyattrdel\"\r\n#define LXSST_DRVFS_EXECUTEONLY_TEST_DIR LXSST_DRVFS_TEST_DIR L\"\\\\executeonlydir\"\r\n#define LXSST_DRVFS_EXECUTEONLY_TEST_DIR_CHILD LXSST_DRVFS_EXECUTEONLY_TEST_DIR L\"\\\\child\"\r\n#define LXSST_DRVFS_READONLY_TEST_DIR LXSST_DRVFS_TEST_DIR L\"\\\\noexecutedir\"\r\n#define LXSST_DRVFS_METADATA_TEST_DIR L\"C:\\\\metadatatest\"\r\n\r\n#define LXSST_DRVFS_REPARSE_TEST_DIR L\"C:\\\\reparsetest\"\r\n\r\n#define LXSST_DRVFS_SYMLINK_TEST_DIR L\"C:\\\\symlink\"\r\n\r\n#define LXSST_DRVFS_METADATA_TEST_MODE (5)\r\n\r\n#define LXSST_TESTS_INSTALL_COMMAND_LINE L\"/bin/bash -c 'cd /data/test; ./build_tests.sh'\"\r\n\r\n#define LXSST_METADATA_EA_NAME_LENGTH (RTL_NUMBER_OF(LX_FILE_METADATA_UID_EA_NAME) - 1)\r\n\r\n#define LX_DRVFS_DISABLE_NONE (0)\r\n#define LX_DRVFS_DISABLE_QUERY_BY_NAME (1)\r\n#define LX_DRVFS_DISABLE_QUERY_BY_NAME_AND_STAT_INFO (2)\r\n\r\nusing wsl::windows::common::wslutil::GetSystemErrorString;\r\n\r\nnamespace DrvFsTests {\r\n\r\nclass DrvFsTests\r\n{\r\npublic:\r\n    std::wstring SkipUnstableTestEnvVar =\r\n        L\"WSL_DISABLE_VB_UNSTABLE_TESTS=\" + std::wstring{wsl::windows::common::helpers::IsWindows11OrAbove() ? L\"0\" : L\"1\"};\r\n\r\n    void DrvFsCommon(int TestMode, std::optional<DrvFsMode> DrvFsMode = {}) const\r\n    {\r\n        auto cleanup = wil::scope_exit([TestMode] {\r\n            RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\junction\");\r\n            RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\absolutelink\");\r\n            DeleteFileW(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\filelink\");\r\n            RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\relativelink\");\r\n            RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\test\\\\linktarget\");\r\n            DeleteFileW(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\test\\\\filetarget\");\r\n            RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\test\");\r\n            DeleteFileW(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\v1link\");\r\n            DeleteFileW(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\appexeclink\");\r\n            RemoveDirectory(LXSST_DRVFS_REPARSE_TEST_DIR);\r\n            SetFileAttributes(LXSST_DRVFS_RWX_TEST_FILE, FILE_ATTRIBUTE_NORMAL);\r\n            DeleteFileW(LXSST_DRVFS_RWX_TEST_FILE);\r\n            DeleteFileW(LXSST_DRVFS_READONLY_TEST_FILE);\r\n            DeleteFileW(LXSST_DRVFS_WRITEONLY_TEST_FILE);\r\n            DeleteFileW(LXSST_DRVFS_EXECUTEONLY_TEST_FILE);\r\n            DeleteFileW(LXSST_DRVFS_EXECUTEONLY_TEST_DIR_CHILD);\r\n            SetFileAttributes(LXSST_DRVFS_READONLYATTR_TEST_FILE, FILE_ATTRIBUTE_NORMAL);\r\n\r\n            DeleteFileW(LXSST_DRVFS_READONLYATTR_TEST_FILE);\r\n            SetFileAttributes(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE, FILE_ATTRIBUTE_NORMAL);\r\n\r\n            DeleteFileW(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE);\r\n            RemoveDirectory(LXSST_DRVFS_EXECUTEONLY_TEST_DIR);\r\n            RemoveDirectory(LXSST_DRVFS_READONLY_TEST_DIR);\r\n            RemoveDirectory(LXSST_DRVFS_TEST_DIR);\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\file.txt\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR L\"\\\\foo\\uf03abar\");\r\n            RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\dir\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink1\");\r\n            RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink2\");\r\n            RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink3\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink4\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink5\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink6\");\r\n            RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink7\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink8\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink1\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink2\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink3\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink4\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink5\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink6\");\r\n            DeleteFileW(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink7\");\r\n            RemoveDirectory(LXSST_DRVFS_SYMLINK_TEST_DIR);\r\n            if (TestMode == LXSST_DRVFS_METADATA_TEST_MODE)\r\n            {\r\n                DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L\"\\\\baduid\");\r\n                DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L\"\\\\badgid\");\r\n                DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L\"\\\\badmode\");\r\n                DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L\"\\\\badtype1\");\r\n                DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L\"\\\\badtype2\");\r\n                DeleteFileW(LXSST_DRVFS_METADATA_TEST_DIR L\"\\\\nondevice\");\r\n                RemoveDirectory(LXSST_DRVFS_METADATA_TEST_DIR);\r\n            }\r\n        });\r\n\r\n        VERIFY_NO_THROW(CreateDrvFsTestFiles(TestMode == LXSST_DRVFS_METADATA_TEST_MODE));\r\n        std::wstringstream Command;\r\n\r\n        Command << L\"/bin/bash -c \\\"\";\r\n        Command << SkipUnstableTestEnvVar;\r\n        Command << \" /data/test/wsl_unit_tests drvfs -d $(wslpath '\";\r\n        Command << LxsstuGetLxssDirectory();\r\n        Command << L\"') -m \";\r\n        Command << TestMode;\r\n        Command << L\"\\\"\";\r\n        std::wstringstream Logfile;\r\n        Logfile << L\"drvfs\";\r\n        Logfile << TestMode;\r\n        VERIFY_NO_THROW(LxsstuRunTest(Command.str().c_str(), Logfile.str().c_str()));\r\n\r\n        //\r\n        // Check that the read-only attribute has been changed.\r\n        //\r\n\r\n        DWORD Attributes = GetFileAttributes(LXSST_DRVFS_READONLYATTR_TEST_FILE);\r\n        DWORD Expected = FILE_ATTRIBUTE_NORMAL;\r\n        VERIFY_ARE_EQUAL(Expected, Attributes);\r\n        Attributes = GetFileAttributes(LXSST_DRVFS_RWX_TEST_FILE);\r\n        Expected = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_ARCHIVE;\r\n        VERIFY_ARE_EQUAL(Expected, Attributes);\r\n\r\n        //\r\n        // Check that the second read-only file was deleted.\r\n        //\r\n\r\n        Expected = INVALID_FILE_ATTRIBUTES;\r\n        Attributes = GetFileAttributes(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE);\r\n        VERIFY_ARE_EQUAL(Expected, Attributes);\r\n\r\n        //\r\n        // Check the NT symlinks.\r\n        //\r\n\r\n        VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink1\", L\"file.txt\", false));\r\n        VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink2\", L\"dir\", true));\r\n        VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink3\", L\"..\", true));\r\n        VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink4\", L\"..\\\\symlink\\\\file.txt\", false));\r\n        VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink5\", L\"dir\\\\..\\\\file.txt\", false));\r\n        VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink6\", L\"ntlink1\", false));\r\n        VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink7\", L\"ntlink2\", true));\r\n        VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\ntlink8\", L\"foo\\uf03abar\", false));\r\n\r\n        VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink1\"));\r\n        VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink2\"));\r\n\r\n        // Since target resolution is done on the Windows side in Plan 9 and VirtioFs, it is able to create an NT\r\n        // link if the target path traverses an existing NT link (this is actually better than WSL 1).\r\n        if (LxsstuVmMode())\r\n        {\r\n            VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink3\", L\"ntlink2\\\\..\\\\file.txt\", false));\r\n        }\r\n        else\r\n        {\r\n            VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink3\"));\r\n        }\r\n\r\n        VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink4\"));\r\n        VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink5\"));\r\n        VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink6\"));\r\n\r\n        // Plan 9 and VirtioFs don't know about the Linux mount point on \"dir\", so it creates an NT link in this case.\r\n        if (LxsstuVmMode())\r\n        {\r\n            VERIFY_NO_THROW(VerifyDrvFsSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink7\", L\"dir\\\\..\\\\file.txt\", false));\r\n        }\r\n        else\r\n        {\r\n            VERIFY_NO_THROW(VerifyDrvFsLxSymlink(LXSST_DRVFS_SYMLINK_TEST_DIR \"\\\\lxlink7\"));\r\n        }\r\n\r\n        //\r\n        // Check metadata is readable using Windows APIs.\r\n        //\r\n\r\n        if (TestMode == LXSST_DRVFS_METADATA_TEST_MODE)\r\n        {\r\n            VerifyDrvFsMetadata();\r\n        }\r\n    }\r\n\r\n    static void VfsAccessDrvFs()\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests vfsaccess drvfs\", L\"vfsaccess_drvfs\"));\r\n    }\r\n\r\n    static void FsCommonDrvFs()\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests fscommon drvfs\", L\"fscommon_drvfs\"));\r\n    }\r\n\r\n    void DrvFs(DrvFsMode Mode)\r\n    {\r\n        SKIP_TEST_ARM64();\r\n\r\n        VERIFY_NO_THROW(DrvFsCommon(LX_DRVFS_DISABLE_NONE, Mode));\r\n    }\r\n\r\n    void DrvFsFat(DrvFsMode Mode)\r\n    {\r\n        SKIP_TEST_ARM64();\r\n\r\n        constexpr auto MountPoint = \"C:\\\\lxss_fat\";\r\n        constexpr auto VhdPath = \"C:\\\\lxss_fat.vhdx\";\r\n        auto Cleanup = wil::scope_exit([MountPoint, VhdPath] { DeleteVolume(MountPoint, VhdPath); });\r\n\r\n        VERIFY_NO_THROW(CreateVolume(\"fat32\", 100, MountPoint, VhdPath));\r\n        VERIFY_NO_THROW(\r\n            LxsstuRunTest((L\"bash -c '\" + SkipUnstableTestEnvVar + L\" /data/test/wsl_unit_tests drvfs -m 3'\").c_str(), L\"drvfs3\"));\r\n    }\r\n\r\n    void DrvFsSmb(DrvFsMode Mode)\r\n    {\r\n        SKIP_TEST_ARM64();\r\n\r\n        if (Mode == DrvFsMode::VirtioFs)\r\n        {\r\n            LogSkipped(\"TODO: debug virtiofs handling of //localhost/C$ style paths\");\r\n            return;\r\n        }\r\n\r\n        VERIFY_NO_THROW(\r\n            LxsstuRunTest((L\"bash -c '\" + SkipUnstableTestEnvVar + L\" /data/test/wsl_unit_tests drvfs -m 4'\").c_str(), L\"drvfs4\"));\r\n    }\r\n\r\n    void DrvFsMetadata(DrvFsMode Mode)\r\n    {\r\n        SKIP_TEST_ARM64();\r\n\r\n        VERIFY_NO_THROW(DrvFsCommon(LXSST_DRVFS_METADATA_TEST_MODE, Mode));\r\n    }\r\n\r\n    void DrvfsMountElevated(DrvFsMode Mode)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added\r\n        SKIP_TEST_ARM64();\r\n\r\n        TerminateDistribution();\r\n        WslKeepAlive keepAlive;\r\n\r\n        ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);\r\n    }\r\n\r\n    void DrvfsMountElevatedDifferentConsole(DrvFsMode Mode)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added\r\n        SKIP_TEST_ARM64();\r\n\r\n        TerminateDistribution();\r\n        WslKeepAlive keepAlive;\r\n\r\n        ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, Mode);\r\n    }\r\n\r\n    void DrvfsMountNonElevated(DrvFsMode Mode)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added\r\n        SKIP_TEST_ARM64();\r\n\r\n        TerminateDistribution();\r\n\r\n        const auto nonElevatedToken = GetNonElevatedToken();\r\n        WslKeepAlive keepAlive(nonElevatedToken.get());\r\n\r\n        ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);\r\n    }\r\n\r\n    void DrvfsMountNonElevatedDifferentConsole(DrvFsMode Mode)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added\r\n        SKIP_TEST_ARM64();\r\n\r\n        TerminateDistribution();\r\n\r\n        const auto nonElevatedToken = GetNonElevatedToken();\r\n        WslKeepAlive keepAlive(nonElevatedToken.get());\r\n\r\n        ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, Mode);\r\n    }\r\n\r\n    void DrvfsMountElevatedSystemDistroEnabled(DrvFsMode Mode)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added\r\n        SKIP_TEST_ARM64();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig({.guiApplications = true, .drvFsMode = Mode}));\r\n        WslKeepAlive keepAlive;\r\n\r\n        ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);\r\n    }\r\n\r\n    void DrvfsMountNonElevatedSystemDistroEnabled(DrvFsMode Mode)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY(); // TODO: Enable on Windows 10 when virtio support is added\r\n        SKIP_TEST_ARM64();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig({.guiApplications = true, .drvFsMode = Mode}));\r\n\r\n        const auto nonElevatedToken = GetNonElevatedToken();\r\n        WslKeepAlive keepAlive(nonElevatedToken.get());\r\n\r\n        ValidateDrvfsMounts(CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, Mode);\r\n    }\r\n\r\n    static void XattrDrvFs(DrvFsMode Mode)\r\n    {\r\n        SKIP_TEST_ARM64();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests xattr drvfs\", L\"xattr_drvfs\"));\r\n    }\r\n\r\n    void DrvFsReFs(DrvFsMode Mode)\r\n    {\r\n        SKIP_TEST_ARM64();\r\n        WSL_TEST_VERSION_REQUIRED(wsl::windows::common::helpers::WindowsBuildNumbers::Germanium);\r\n\r\n        constexpr auto MountPoint = \"C:\\\\lxss_refs\";\r\n        constexpr auto VhdPath = \"C:\\\\lxss_refs.vhdx\";\r\n        auto Cleanup = wil::scope_exit([MountPoint, VhdPath] { DeleteVolume(MountPoint, VhdPath); });\r\n\r\n        VERIFY_NO_THROW(CreateVolume(\"refs\", 50000, MountPoint, VhdPath));\r\n        VERIFY_NO_THROW(\r\n            LxsstuRunTest((L\"bash -c '\" + SkipUnstableTestEnvVar + L\" /data/test/wsl_unit_tests drvfs -m 6'\").c_str(), L\"drvfs6\"));\r\n    }\r\n\r\n    void WslPath(DrvFsMode Mode)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests wslpath\", L\"wslpath\"));\r\n\r\n        auto testWslPath = [](const std::wstring& testDir) {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { std::filesystem::remove_all(testDir); });\r\n\r\n            std::filesystem::create_directory(testDir);\r\n\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"wslpath -aw '{}'\", testDir));\r\n            VERIFY_ARE_EQUAL((std::filesystem::canonical(std::filesystem::current_path()) / testDir).wstring() + L\"\\n\", out);\r\n\r\n            std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L\"wslpath -wa '{}'\", testDir));\r\n            VERIFY_ARE_EQUAL((std::filesystem::canonical(std::filesystem::current_path()) / testDir).wstring() + L\"\\n\", out);\r\n\r\n            std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L\"wslpath '{}'\", testDir));\r\n            VERIFY_ARE_EQUAL(std::format(L\"{}\\n\", testDir), out);\r\n\r\n            std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L\"wslpath -a '{}'\", testDir));\r\n            VERIFY_IS_TRUE(out.find(L\"/mnt/\") == 0);\r\n        };\r\n\r\n        testWslPath(L\"wslpath-test-dir\");\r\n        testWslPath(L\"wslpath-测试目录-テスト\");\r\n    }\r\n\r\n    void DrvFsMountUnicodePath(DrvFsMode Mode)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Create a Windows directory with unicode characters\r\n        constexpr auto unicodeDir = L\"C:\\\\drvfs-测试-テスト\";\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { std::filesystem::remove_all(unicodeDir); });\r\n\r\n        std::filesystem::create_directory(unicodeDir);\r\n\r\n        // Create a test file inside the directory\r\n        const auto testFilePath = std::filesystem::path(unicodeDir) / L\"test-file.txt\";\r\n        {\r\n            std::ofstream testFile(testFilePath);\r\n            testFile << \"hello from unicode path\";\r\n        }\r\n\r\n        // Mount the unicode directory using mount -t drvfs\r\n        constexpr auto mountPoint = L\"/tmp/unicode-mount-test\";\r\n        auto unmountCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            LxsstuLaunchWsl(std::format(L\"-u root umount '{}'\", mountPoint).c_str());\r\n            LxsstuLaunchWsl(std::format(L\"-u root rmdir '{}'\", mountPoint).c_str());\r\n        });\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"-u root mkdir -p '{}'\", mountPoint).c_str()), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"-u root mount -t drvfs '{}' '{}'\", unicodeDir, mountPoint).c_str()), 0);\r\n\r\n        // Verify we can read the test file through the mount\r\n        auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"cat '{}/test-file.txt'\", mountPoint));\r\n        VERIFY_ARE_EQUAL(L\"hello from unicode path\", out);\r\n\r\n        // Verify we can list the directory\r\n        std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L\"ls '{}'\", mountPoint));\r\n        VERIFY_IS_TRUE(out.find(L\"test-file.txt\") != std::wstring::npos);\r\n    }\r\n\r\n    // DrvFsTests Private Methods\r\nprivate:\r\n    static VOID CreateDrvFsTestFiles(bool Metadata)\r\n    {\r\n\r\n        THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_TEST_DIR, NULL));\r\n\r\n        //\r\n        // The rwx and readonlyattr test files need read/write EA permission for\r\n        // the metadata test mode because chmod will be called on them.\r\n        //\r\n\r\n        CreateTestFile(LXSST_DRVFS_RWX_TEST_FILE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_EXECUTE | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);\r\n\r\n        CreateTestFile(LXSST_DRVFS_READONLY_TEST_FILE, FILE_GENERIC_READ | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);\r\n\r\n        CreateTestFile(LXSST_DRVFS_WRITEONLY_TEST_FILE, FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES | FILE_READ_EA | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);\r\n\r\n        CreateTestFile(\r\n            LXSST_DRVFS_EXECUTEONLY_TEST_DIR,\r\n            FILE_TRAVERSE | FILE_DELETE_CHILD | FILE_ADD_FILE | FILE_READ_ATTRIBUTES | FILE_READ_EA | DELETE | SYNCHRONIZE | READ_CONTROL,\r\n            TRUE,\r\n            INVALID_HANDLE_VALUE);\r\n\r\n        CreateTestFile(LXSST_DRVFS_EXECUTEONLY_TEST_DIR_CHILD, FILE_GENERIC_READ | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);\r\n\r\n        CreateTestFile(LXSST_DRVFS_READONLY_TEST_DIR, FILE_GENERIC_READ | DELETE | SYNCHRONIZE, TRUE, INVALID_HANDLE_VALUE);\r\n\r\n        CreateTestFile(LXSST_DRVFS_READONLYATTR_TEST_FILE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_EXECUTE | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);\r\n\r\n        THROW_LAST_ERROR_IF(!SetFileAttributes(LXSST_DRVFS_READONLYATTR_TEST_FILE, FILE_ATTRIBUTE_READONLY));\r\n\r\n        CreateTestFile(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_EXECUTE | DELETE | SYNCHRONIZE, FALSE, INVALID_HANDLE_VALUE);\r\n\r\n        THROW_LAST_ERROR_IF(!SetFileAttributes(LXSST_DRVFS_READONLYATTRDEL_TEST_FILE, FILE_ATTRIBUTE_READONLY));\r\n\r\n        //\r\n        // Copy the wsl_unit_tests executable to an execute-only file on DrvFs.\r\n        //\r\n\r\n        const std::wstring Path = L\"\\\\\\\\wsl.localhost\\\\\" LXSS_DISTRO_NAME_TEST_L L\"\\\\data\\\\test\\\\wsl_unit_tests\";\r\n        const wil::unique_hfile File(CreateFile(\r\n            Path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));\r\n\r\n        THROW_LAST_ERROR_IF(!File);\r\n        CreateTestFile(\r\n            LXSST_DRVFS_EXECUTEONLY_TEST_FILE,\r\n            FILE_EXECUTE | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | DELETE | SYNCHRONIZE | READ_CONTROL,\r\n            FALSE,\r\n            File.get());\r\n\r\n        THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_REPARSE_TEST_DIR, nullptr));\r\n\r\n        THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\test\", nullptr));\r\n\r\n        THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\test\\\\linktarget\", nullptr));\r\n\r\n        THROW_LAST_ERROR_IF(!CreateSymbolicLink(\r\n            LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\absolutelink\", LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\test\\\\linktarget\", SYMBOLIC_LINK_FLAG_DIRECTORY));\r\n\r\n        THROW_LAST_ERROR_IF(!CreateSymbolicLink(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\relativelink\", L\"test\\\\linktarget\", SYMBOLIC_LINK_FLAG_DIRECTORY));\r\n\r\n        {\r\n            const wil::unique_hfile TargetFile(CreateFile(\r\n                LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\test\\\\filetarget\",\r\n                FILE_GENERIC_READ | FILE_GENERIC_WRITE,\r\n                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\r\n                nullptr,\r\n                CREATE_NEW,\r\n                FILE_ATTRIBUTE_NORMAL,\r\n                nullptr));\r\n\r\n            THROW_LAST_ERROR_IF(!TargetFile);\r\n        }\r\n\r\n        THROW_LAST_ERROR_IF(!CreateSymbolicLink(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\filelink\", L\"test\\\\filetarget\", 0));\r\n\r\n        CreateJunction(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\junction\", LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\test\\\\linktarget\");\r\n\r\n        // DrvFs does not create V1 symlinks anymore; create one here manually to ensure it can still\r\n        // read them.\r\n        CreateV1Symlink(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\v1link\", \"/v1/symlink/target\");\r\n        CreateAppExecLink(LXSST_DRVFS_REPARSE_TEST_DIR L\"\\\\appexeclink\");\r\n\r\n        if (Metadata != false)\r\n        {\r\n            THROW_LAST_ERROR_IF(!CreateDirectory(LXSST_DRVFS_METADATA_TEST_DIR, nullptr));\r\n\r\n            CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR \"\\\\baduid\", LX_UID_INVALID, 3001, LX_S_IFREG | 0644, 0, 0, false);\r\n\r\n            CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR \"\\\\badgid\", 3000, LX_GID_INVALID, LX_S_IFREG | 0644, 0, 0, false);\r\n\r\n            CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR \"\\\\badmode\", 3000, 3001, 0x10000 | LX_S_IFREG | 0644, 0, 0, false);\r\n\r\n            CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR \"\\\\badtype1\", 3000, 3001, LX_S_IFDIR | 0755, 0, 0, false);\r\n\r\n            CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR \"\\\\badtype2\", 3000, 3001, LX_S_IFLNK | 0777, 0, 0, false);\r\n\r\n            CreateMetadataTestFile(LXSST_DRVFS_METADATA_TEST_DIR \"\\\\nondevice\", 3000, 3001, LX_S_IFREG | 0644, 1, 2, true);\r\n        }\r\n    }\r\n\r\n    static VOID CreateTestFile(_In_z_ LPCWSTR Filename, _In_ DWORD Permissions, _In_ BOOLEAN Directory, _In_ HANDLE SourceFile)\r\n    {\r\n\r\n        BYTE Buffer[4096];\r\n        DWORD BytesRead;\r\n\r\n        //\r\n        // Create the SID for the BUILTIN\\Administrators group.\r\n        //\r\n\r\n        auto [AdminSid, SidBuffer] =\r\n            wsl::windows::common::security::CreateSid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);\r\n\r\n        //\r\n        // Set the permissions for the SID.\r\n        //\r\n\r\n        EXPLICIT_ACCESS Access;\r\n        RtlZeroMemory(&Access, sizeof(Access));\r\n        Access.grfAccessPermissions = Permissions;\r\n        Access.grfAccessMode = SET_ACCESS;\r\n        Access.grfInheritance = NO_INHERITANCE;\r\n        Access.Trustee.TrusteeForm = TRUSTEE_IS_SID;\r\n        Access.Trustee.TrusteeType = TRUSTEE_IS_GROUP;\r\n        Access.Trustee.ptstrName = (LPTSTR)AdminSid;\r\n\r\n        //\r\n        // Allocate an ACL with the permissions.\r\n        //\r\n\r\n        wil::unique_any<PACL, decltype(&::LocalFree), ::LocalFree> Acl;\r\n        THROW_IF_WIN32_ERROR(SetEntriesInAcl(1, &Access, NULL, &Acl));\r\n\r\n        //\r\n        // Create a security descriptor and set the ACL.\r\n        //\r\n\r\n        const wil::unique_hlocal_security_descriptor Descriptor(::LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));\r\n\r\n        THROW_LAST_ERROR_IF(!Descriptor);\r\n        THROW_LAST_ERROR_IF(!InitializeSecurityDescriptor(Descriptor.get(), SECURITY_DESCRIPTOR_REVISION));\r\n\r\n        THROW_LAST_ERROR_IF(!SetSecurityDescriptorDacl(Descriptor.get(), TRUE, Acl.get(), FALSE));\r\n\r\n        //\r\n        // Create security attributes that point to the descriptor.\r\n        //\r\n\r\n        SECURITY_ATTRIBUTES Attributes;\r\n        RtlZeroMemory(&Attributes, sizeof(Attributes));\r\n        Attributes.nLength = sizeof(SECURITY_ATTRIBUTES);\r\n        Attributes.lpSecurityDescriptor = Descriptor.get();\r\n        Attributes.bInheritHandle = FALSE;\r\n\r\n        //\r\n        // Create a file or directory with the security attributes.\r\n        //\r\n\r\n        if (Directory == FALSE)\r\n        {\r\n            const wil::unique_hfile File(\r\n                CreateFile(Filename, GENERIC_WRITE | SYNCHRONIZE, 0, &Attributes, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL));\r\n\r\n            THROW_LAST_ERROR_IF(!File);\r\n\r\n            //\r\n            // If a source file was specified, copy its contents.\r\n            //\r\n\r\n            if (SourceFile != INVALID_HANDLE_VALUE)\r\n            {\r\n                THROW_LAST_ERROR_IF(!ReadFile(SourceFile, Buffer, sizeof(Buffer), &BytesRead, NULL));\r\n\r\n                while (BytesRead > 0)\r\n                {\r\n                    THROW_LAST_ERROR_IF(!WriteFile(File.get(), Buffer, BytesRead, NULL, NULL));\r\n\r\n                    THROW_LAST_ERROR_IF(!ReadFile(SourceFile, Buffer, sizeof(Buffer), &BytesRead, NULL));\r\n                }\r\n            }\r\n        }\r\n        else\r\n        {\r\n            THROW_LAST_ERROR_IF(!CreateDirectory(Filename, &Attributes));\r\n        }\r\n    }\r\n\r\n    static VOID CreateMetadataTestFile(\r\n        _In_ LPCWSTR Filename, _In_ ULONG Uid, _In_ ULONG Gid, _In_ ULONG Mode, _In_ ULONG DeviceIdMajor, _In_ ULONG DeviceIdMinor, _In_ bool IncludeDeviceId)\r\n    {\r\n\r\n        //\r\n        // Each individual EA entry must be aligned on a 4 byte boundary, but the\r\n        // value inside each EA struct must not be. Therefore, set packing to 1\r\n        // byte, and add padding to manually align the entries.\r\n        //\r\n\r\n#pragma pack(push, 1)\r\n\r\n        struct\r\n        {\r\n            struct\r\n            {\r\n                union\r\n                {\r\n                    FILE_FULL_EA_INFORMATION Header;\r\n                    CHAR Buffer[FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + LXSST_METADATA_EA_NAME_LENGTH + 1];\r\n                };\r\n\r\n                ULONG Uid;\r\n            } Uid;\r\n\r\n            CHAR Padding1;\r\n            struct\r\n            {\r\n                union\r\n                {\r\n                    FILE_FULL_EA_INFORMATION Header;\r\n                    CHAR Buffer[FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + LXSST_METADATA_EA_NAME_LENGTH + 1];\r\n                };\r\n\r\n                ULONG Gid;\r\n            } Gid;\r\n\r\n            CHAR Padding2;\r\n            struct\r\n            {\r\n                union\r\n                {\r\n                    FILE_FULL_EA_INFORMATION Header;\r\n                    CHAR Buffer[FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + LXSST_METADATA_EA_NAME_LENGTH + 1];\r\n                };\r\n\r\n                ULONG Mode;\r\n            } Mode;\r\n\r\n            CHAR Padding3;\r\n            struct\r\n            {\r\n                union\r\n                {\r\n                    FILE_FULL_EA_INFORMATION Header;\r\n                    CHAR Buffer[FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + LXSST_METADATA_EA_NAME_LENGTH + 1];\r\n                };\r\n\r\n                ULONG DeviceIdMajor;\r\n                ULONG DeviceIdMinor;\r\n            } DeviceId;\r\n        } EaBuffer;\r\n\r\n#pragma pack(pop)\r\n\r\n        RtlZeroMemory(&EaBuffer, sizeof(EaBuffer));\r\n        EaBuffer.Uid.Header.EaNameLength = LXSST_METADATA_EA_NAME_LENGTH;\r\n        EaBuffer.Uid.Header.EaValueLength = sizeof(ULONG);\r\n        RtlCopyMemory(EaBuffer.Uid.Header.EaName, LX_FILE_METADATA_UID_EA_NAME, LXSST_METADATA_EA_NAME_LENGTH);\r\n\r\n        EaBuffer.Uid.Uid = Uid;\r\n        EaBuffer.Uid.Header.NextEntryOffset = (ULONG)((PUCHAR)&EaBuffer.Gid - (PUCHAR)&EaBuffer.Uid);\r\n        EaBuffer.Gid.Header.EaNameLength = LXSST_METADATA_EA_NAME_LENGTH;\r\n        EaBuffer.Gid.Header.EaValueLength = sizeof(ULONG);\r\n        RtlCopyMemory(EaBuffer.Gid.Header.EaName, LX_FILE_METADATA_GID_EA_NAME, LXSST_METADATA_EA_NAME_LENGTH);\r\n\r\n        EaBuffer.Gid.Gid = Gid;\r\n        EaBuffer.Gid.Header.NextEntryOffset = (ULONG)((PUCHAR)&EaBuffer.Mode - (PUCHAR)&EaBuffer.Gid);\r\n        EaBuffer.Mode.Header.EaNameLength = LXSST_METADATA_EA_NAME_LENGTH;\r\n        EaBuffer.Mode.Header.EaValueLength = sizeof(ULONG);\r\n        RtlCopyMemory(EaBuffer.Mode.Header.EaName, LX_FILE_METADATA_MODE_EA_NAME, LXSST_METADATA_EA_NAME_LENGTH);\r\n\r\n        EaBuffer.Mode.Mode = Mode;\r\n        if (IncludeDeviceId != false)\r\n        {\r\n            EaBuffer.Mode.Header.NextEntryOffset = (ULONG)((PUCHAR)&EaBuffer.DeviceId - (PUCHAR)&EaBuffer.Mode);\r\n            EaBuffer.DeviceId.Header.EaNameLength = LXSST_METADATA_EA_NAME_LENGTH;\r\n            EaBuffer.DeviceId.Header.EaValueLength = sizeof(ULONG);\r\n            RtlCopyMemory(EaBuffer.DeviceId.Header.EaName, LX_FILE_METADATA_DEVICE_ID_EA_NAME, LXSST_METADATA_EA_NAME_LENGTH);\r\n\r\n            EaBuffer.DeviceId.DeviceIdMajor = DeviceIdMajor;\r\n            EaBuffer.DeviceId.DeviceIdMinor = DeviceIdMinor;\r\n        }\r\n\r\n        const std::wstring NtPath{std::wstring(L\"\\\\DosDevices\\\\\") + Filename};\r\n        UNICODE_STRING Name;\r\n        RtlInitUnicodeString(&Name, NtPath.c_str());\r\n        OBJECT_ATTRIBUTES Attributes;\r\n        InitializeObjectAttributes(&Attributes, &Name, 0, nullptr, 0);\r\n        wil::unique_hfile File;\r\n        IO_STATUS_BLOCK IoStatus;\r\n        THROW_IF_NTSTATUS_FAILED(NtCreateFile(\r\n            &File,\r\n            FILE_GENERIC_READ,\r\n            &Attributes,\r\n            &IoStatus,\r\n            nullptr,\r\n            FILE_ATTRIBUTE_NORMAL,\r\n            (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),\r\n            FILE_CREATE,\r\n            0,\r\n            &EaBuffer,\r\n            sizeof(EaBuffer)));\r\n    }\r\n\r\n    static VOID VerifyDrvFsMetadata()\r\n    {\r\n        wil::unique_hfile File;\r\n        IO_STATUS_BLOCK IoStatus;\r\n        UNICODE_STRING Name;\r\n        RtlInitUnicodeString(&Name, L\"\\\\DosDevices\\\\\" LXSST_DRVFS_METADATA_TEST_DIR);\r\n        OBJECT_ATTRIBUTES Attributes;\r\n        InitializeObjectAttributes(&Attributes, &Name, 0, nullptr, 0);\r\n        THROW_IF_NTSTATUS_FAILED(NtCreateFile(\r\n            &File, FILE_READ_EA, &Attributes, &IoStatus, nullptr, 0, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), FILE_OPEN, FILE_DIRECTORY_FILE, nullptr, 0));\r\n\r\n        UCHAR Buffer[1000];\r\n        THROW_IF_NTSTATUS_FAILED(ZwQueryEaFile(File.get(), &IoStatus, Buffer, sizeof(Buffer), FALSE, nullptr, 0, nullptr, TRUE));\r\n\r\n        bool FoundUid = false;\r\n        bool FoundGid = false;\r\n        bool FoundMode = false;\r\n        PFILE_FULL_EA_INFORMATION EaInfo = (PFILE_FULL_EA_INFORMATION)Buffer;\r\n        for (;;)\r\n        {\r\n            VERIFY_ARE_EQUAL(EaInfo->EaNameLength, 6);\r\n            VERIFY_ARE_EQUAL(EaInfo->EaValueLength, 4);\r\n            std::string EaName{EaInfo->EaName};\r\n            ULONG Value = *(PULONG)((PCHAR)EaInfo->EaName + EaInfo->EaNameLength + 1);\r\n            if (EaName == LX_FILE_METADATA_UID_EA_NAME)\r\n            {\r\n                FoundUid = true;\r\n                VERIFY_ARE_EQUAL(Value, 0x11223344ul);\r\n            }\r\n            else if (EaName == LX_FILE_METADATA_GID_EA_NAME)\r\n            {\r\n                FoundGid = true;\r\n                VERIFY_ARE_EQUAL(Value, 0x55667788ul);\r\n            }\r\n            else if (EaName == LX_FILE_METADATA_MODE_EA_NAME)\r\n            {\r\n                FoundMode = true;\r\n                VERIFY_ARE_EQUAL(Value, (ULONG)(LX_S_IFDIR | 0775));\r\n            }\r\n            else\r\n            {\r\n                VERIFY_FAIL(L\"Unexpected EA on file.\");\r\n            }\r\n\r\n            if (EaInfo->NextEntryOffset == 0)\r\n            {\r\n                break;\r\n            }\r\n\r\n            EaInfo = (PFILE_FULL_EA_INFORMATION)((PCHAR)EaInfo + EaInfo->NextEntryOffset);\r\n        };\r\n\r\n        VERIFY_IS_TRUE(FoundUid);\r\n        VERIFY_IS_TRUE(FoundGid);\r\n        VERIFY_IS_TRUE(FoundMode);\r\n    }\r\n\r\n    static VOID CreateJunction(_In_ const std::wstring& Junction, _In_ const std::wstring& Target)\r\n    {\r\n\r\n        //\r\n        // The logic for creating a junction was taken from mklink.\r\n        //\r\n\r\n        THROW_LAST_ERROR_IF(!CreateDirectory(Junction.c_str(), NULL));\r\n        const wil::unique_hfile Dir(CreateFile(\r\n            Junction.c_str(), FILE_GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));\r\n\r\n        THROW_LAST_ERROR_IF(!Dir);\r\n\r\n        UNICODE_STRING LinkPath = {0};\r\n        auto Cleanup = wil::scope_exit([&] {\r\n            if (LinkPath.Buffer != nullptr)\r\n            {\r\n                FREE(LinkPath.Buffer);\r\n            }\r\n        });\r\n\r\n        THROW_IF_NTSTATUS_FAILED(RtlDosPathNameToNtPathName_U_WithStatus(Target.c_str(), &LinkPath, nullptr, nullptr));\r\n\r\n        //\r\n        // The buffer needs space for the substitute name and the print name, with\r\n        // NULL characters. This can't overflow since they are all paths with\r\n        // lengths less than MAXUSHORT.\r\n        //\r\n\r\n        const ULONG ReparseBufferSize = (ULONG)(FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer[0]) +\r\n                                                Target.length() * sizeof(WCHAR) + LinkPath.Length + 2 * sizeof(UNICODE_NULL));\r\n\r\n        //\r\n        // Allocate the reparse data buffer.\r\n        //\r\n\r\n        const std::unique_ptr<REPARSE_DATA_BUFFER> Reparse((PREPARSE_DATA_BUFFER) new char[ReparseBufferSize]);\r\n\r\n        ZeroMemory(Reparse.get(), ReparseBufferSize);\r\n        Reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;\r\n\r\n        //\r\n        // The data length is the buffer size excluding the header.\r\n        //\r\n\r\n        Reparse->ReparseDataLength = (USHORT)(ReparseBufferSize - REPARSE_DATA_BUFFER_HEADER_SIZE);\r\n\r\n        //\r\n        // Copy the NT path into the buffer for the substitute name.\r\n        //\r\n\r\n        Reparse->MountPointReparseBuffer.SubstituteNameLength = LinkPath.Length;\r\n        RtlCopyMemory(Reparse->MountPointReparseBuffer.PathBuffer, LinkPath.Buffer, LinkPath.Length);\r\n\r\n        const USHORT Offset = LinkPath.Length + sizeof(UNICODE_NULL);\r\n\r\n        //\r\n        // Copy the DOS path into the buffer for the print name.\r\n        //\r\n\r\n        Reparse->MountPointReparseBuffer.PrintNameOffset = Offset;\r\n        Reparse->MountPointReparseBuffer.PrintNameLength = (USHORT)(Target.length() * sizeof(WCHAR));\r\n\r\n        RtlCopyMemory((Reparse->MountPointReparseBuffer.PathBuffer + (Offset / sizeof(WCHAR))), Target.c_str(), Target.length() * sizeof(WCHAR));\r\n\r\n        //\r\n        // Set the reparse point on the file.\r\n        //\r\n\r\n        IO_STATUS_BLOCK IoStatus;\r\n        THROW_IF_NTSTATUS_FAILED(NtFsControlFile(\r\n            Dir.get(), nullptr, nullptr, nullptr, &IoStatus, FSCTL_SET_REPARSE_POINT, Reparse.get(), ReparseBufferSize, nullptr, 0));\r\n\r\n        return;\r\n    }\r\n\r\n    static VOID CreateV1Symlink(const std::wstring& Symlink, std::string_view Target)\r\n    {\r\n\r\n        //\r\n        // Create a symlink using the V1 LX symlink format, where the target is\r\n        // stored in the file data. The reparse data only contains a version\r\n        // number.\r\n        //\r\n\r\n        union\r\n        {\r\n            REPARSE_DATA_BUFFER Header;\r\n            struct\r\n            {\r\n                CHAR Buffer[REPARSE_DATA_BUFFER_HEADER_SIZE];\r\n                ULONG Version;\r\n            } Data;\r\n        } Reparse{};\r\n\r\n        const ULONG ReparseBufferSize = REPARSE_DATA_BUFFER_HEADER_SIZE + sizeof(ULONG);\r\n\r\n        //\r\n        // The data length is the buffer size excluding the header.\r\n        //\r\n\r\n        Reparse.Header.ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;\r\n        Reparse.Header.ReparseDataLength = sizeof(ULONG);\r\n        Reparse.Data.Version = 1;\r\n\r\n        const auto File = CreateReparsePoint(Symlink, &Reparse, ReparseBufferSize);\r\n\r\n        //\r\n        // Write the target to the file.\r\n        //\r\n\r\n        DWORD written;\r\n        THROW_IF_WIN32_BOOL_FALSE(WriteFile(File.get(), Target.data(), gsl::narrow_cast<DWORD>(Target.size()), &written, nullptr));\r\n        VERIFY_ARE_EQUAL(Target.size(), written);\r\n\r\n        return;\r\n    }\r\n\r\n    static VOID CreateAppExecLink(const std::wstring& Link)\r\n    {\r\n        //\r\n        // This link will not be valid from Windows's perspective, since it only\r\n        // contains the header and not any actual reparse data. However, it has the\r\n        // right reparse tag which is sufficient to test drvfs's behavior.\r\n        //\r\n\r\n        REPARSE_DATA_BUFFER Reparse{};\r\n        Reparse.ReparseTag = IO_REPARSE_TAG_APPEXECLINK;\r\n        Reparse.ReparseDataLength = 0;\r\n        CreateReparsePoint(Link, &Reparse, REPARSE_DATA_BUFFER_HEADER_SIZE);\r\n    }\r\n\r\n    static wil::unique_hfile CreateReparsePoint(_In_ const std::wstring& Path, _In_ PVOID ReparseBuffer, _In_ ULONG ReparseBufferSize)\r\n    {\r\n        wil::unique_hfile File{CreateFile(\r\n            Path.c_str(), FILE_GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr)};\r\n\r\n        THROW_LAST_ERROR_IF(!File);\r\n        IO_STATUS_BLOCK IoStatus;\r\n        THROW_IF_NTSTATUS_FAILED(NtFsControlFile(\r\n            File.get(), nullptr, nullptr, nullptr, &IoStatus, FSCTL_SET_REPARSE_POINT, ReparseBuffer, ReparseBufferSize, nullptr, 0));\r\n\r\n        return File;\r\n    }\r\n\r\n    static VOID CreateVolume(LPCSTR FileSystem, ULONG MaxSizeInMb, LPCSTR MountPoint, LPCSTR VhdPath)\r\n    {\r\n        THROW_LAST_ERROR_IF(!CreateDirectoryA(MountPoint, NULL));\r\n\r\n        const auto CreateScript = std::vformat(\r\n            \"create vdisk file={} maximum={} type=expandable\\n\"\r\n            \"select vdisk file={}\\n\"\r\n            \"attach vdisk\\n\"\r\n            \"create partition primary\\n\"\r\n            \"select partition 1\\n\"\r\n            \"online volume\\n\"\r\n            \"format fs={} quick\\n\"\r\n            \"assign mount={}\\n\",\r\n            std::make_format_args(VhdPath, MaxSizeInMb, VhdPath, FileSystem, MountPoint));\r\n\r\n        RunDiskpartScript(CreateScript.c_str());\r\n    }\r\n\r\n    static VOID RunDiskpartScript(LPCSTR Script)\r\n    {\r\n        const std::wstring ScriptFileName = wsl::windows::common::filesystem::GetTempFilename();\r\n\r\n        std::ofstream ScriptFile(ScriptFileName);\r\n        THROW_LAST_ERROR_IF(!ScriptFile);\r\n\r\n        auto Cleanup = wil::scope_exit([&] { DeleteFileW(ScriptFileName.c_str()); });\r\n\r\n        ScriptFile << Script;\r\n        ScriptFile.close();\r\n\r\n        std::wstring CommandLine = L\"diskpart.exe /s \" + ScriptFileName;\r\n        THROW_HR_IF(E_FAIL, ((wsl::windows::common::helpers::RunProcess(CommandLine)) != 0));\r\n    }\r\n\r\n    static VOID DeleteVolume(LPCSTR MountPoint, LPCSTR VhdPath)\r\n    {\r\n        const auto CleanupScript = std::vformat(\r\n            \"select vdisk file={}\\n\"\r\n            \"select partition 1\\n\"\r\n            \"remove all\\n\"\r\n            \"detach vdisk\\n\",\r\n            std::make_format_args(VhdPath));\r\n\r\n        RunDiskpartScript(CleanupScript.c_str());\r\n\r\n        RemoveDirectoryA(MountPoint);\r\n        DeleteFileA(VhdPath);\r\n    }\r\n\r\n    static void ValidateDrvfsMounts(DWORD CreateProcessFlags, DrvFsMode Mode)\r\n    {\r\n        auto validate = [CreateProcessFlags](const std::wstring& expectedType, HANDLE token) {\r\n            const auto commandLine = LxssGenerateWslCommandLine(L\"mount | grep -F '/mnt/c type'\");\r\n\r\n            wsl::windows::common::SubProcess process(nullptr, commandLine.c_str(), CreateProcessFlags);\r\n            process.SetToken(token);\r\n            process.SetShowWindow(SW_HIDE);\r\n\r\n            const auto output = process.RunAndCaptureOutput();\r\n            const auto lines = LxssSplitString(output.Stdout, L\"\\n\");\r\n\r\n            VERIFY_ARE_EQUAL(lines.size(), 1);\r\n\r\n            if (!expectedType.empty())\r\n            {\r\n                VERIFY_IS_TRUE(output.Stdout.find(expectedType) == 0);\r\n            }\r\n        };\r\n\r\n        std::wstring elevatedType;\r\n        std::wstring nonElevatedType;\r\n        switch (Mode)\r\n        {\r\n        case DrvFsMode::Plan9:\r\n            elevatedType = L\"C:\\\\\";\r\n            nonElevatedType = L\"C:\\\\\";\r\n            break;\r\n        case DrvFsMode::Virtio9p:\r\n            elevatedType = L\"drvfsa\";\r\n            nonElevatedType = L\"drvfs\";\r\n            break;\r\n        case DrvFsMode::VirtioFs:\r\n            // VirtioFs uses GUIDs as the tag so the value is not predictable.\r\n            break;\r\n\r\n        default:\r\n            LogError(\"Unexpected mode %d\", (int)Mode);\r\n            return;\r\n        }\r\n\r\n        // Validate that mount types are correct in both namespaces\r\n        validate(elevatedType, nullptr);\r\n\r\n        const auto nonElevatedToken = GetNonElevatedToken();\r\n        validate(nonElevatedType, nonElevatedToken.get());\r\n\r\n        // Elevated token should be able to create files at the root of the drive (/mnt/c)\r\n        {\r\n            const auto commandLine =\r\n                LxssGenerateWslCommandLine(L\"touch /mnt/c/elevated_test_file.tmp && rm /mnt/c/elevated_test_file.tmp\");\r\n\r\n            wsl::windows::common::SubProcess process(nullptr, commandLine.c_str(), CreateProcessFlags);\r\n            process.SetToken(nullptr);\r\n            process.SetShowWindow(SW_HIDE);\r\n\r\n            const auto output = process.RunAndCaptureOutput();\r\n            VERIFY_ARE_EQUAL(0, output.ExitCode, L\"Elevated token should be able to create files at /mnt/c\");\r\n        }\r\n\r\n        // Non-elevated token should NOT be able to create files at the root of the drive (/mnt/c)\r\n        {\r\n            const auto commandLine = LxssGenerateWslCommandLine(L\"touch /mnt/c/nonelevated_test_file.tmp\");\r\n\r\n            wsl::windows::common::SubProcess process(nullptr, commandLine.c_str(), CreateProcessFlags);\r\n            process.SetToken(nonElevatedToken.get());\r\n            process.SetShowWindow(SW_HIDE);\r\n\r\n            const auto output = process.RunAndCaptureOutput();\r\n            VERIFY_ARE_NOT_EQUAL(0, output.ExitCode, L\"Non-elevated token should NOT be able to create files at /mnt/c (C:\\\\)\");\r\n        }\r\n    }\r\n\r\n    static VOID VerifyDrvFsSymlink(const std::wstring& Path, const std::wstring& ExpectedTarget, bool Directory)\r\n    {\r\n\r\n        const std::wstring NtPath = L\"\\\\DosDevices\\\\\" + Path;\r\n        UNICODE_STRING Name;\r\n        RtlInitUnicodeString(&Name, NtPath.c_str());\r\n        OBJECT_ATTRIBUTES Attributes;\r\n        InitializeObjectAttributes(&Attributes, &Name, 0, nullptr, 0);\r\n\r\n        wil::unique_hfile Symlink;\r\n        IO_STATUS_BLOCK IoStatus;\r\n        THROW_IF_NTSTATUS_FAILED(NtCreateFile(\r\n            &Symlink, FILE_GENERIC_READ, &Attributes, &IoStatus, nullptr, 0, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), FILE_OPEN, FILE_OPEN_REPARSE_POINT, NULL, 0));\r\n\r\n        FILE_ATTRIBUTE_TAG_INFORMATION Info;\r\n        THROW_IF_NTSTATUS_FAILED(NtQueryInformationFile(Symlink.get(), &IoStatus, &Info, sizeof(Info), FileAttributeTagInformation));\r\n\r\n        VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0);\r\n        if (Directory != false)\r\n        {\r\n            VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);\r\n        }\r\n        else\r\n        {\r\n            VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);\r\n        }\r\n\r\n        VERIFY_ARE_EQUAL(Info.ReparseTag, IO_REPARSE_TAG_SYMLINK);\r\n\r\n        union\r\n        {\r\n            char Buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];\r\n            REPARSE_DATA_BUFFER Data;\r\n        } ReparseData;\r\n\r\n        THROW_IF_NTSTATUS_FAILED(NtFsControlFile(\r\n            Symlink.get(), nullptr, nullptr, nullptr, &IoStatus, FSCTL_GET_REPARSE_POINT, nullptr, 0, &ReparseData, sizeof(ReparseData)));\r\n\r\n        VERIFY_ARE_EQUAL(ReparseData.Data.ReparseTag, IO_REPARSE_TAG_SYMLINK);\r\n        VERIFY_ARE_EQUAL(ReparseData.Data.SymbolicLinkReparseBuffer.Flags, (ULONG)SYMLINK_FLAG_RELATIVE);\r\n        const std::wstring Target(\r\n            (PWCHAR)((PCHAR)ReparseData.Data.SymbolicLinkReparseBuffer.PathBuffer + ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameOffset),\r\n            (PWCHAR)((PCHAR)ReparseData.Data.SymbolicLinkReparseBuffer.PathBuffer + ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameOffset +\r\n                     ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameLength));\r\n\r\n        VERIFY_ARE_EQUAL(Target, ExpectedTarget);\r\n        const std::wstring Target2(\r\n            (PWCHAR)((PCHAR)ReparseData.Data.SymbolicLinkReparseBuffer.PathBuffer + ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameOffset),\r\n            (PWCHAR)((PCHAR)ReparseData.Data.SymbolicLinkReparseBuffer.PathBuffer + ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameOffset +\r\n                     ReparseData.Data.SymbolicLinkReparseBuffer.SubstituteNameLength));\r\n\r\n        VERIFY_ARE_EQUAL(Target2, ExpectedTarget);\r\n    }\r\n\r\n    static VOID VerifyDrvFsLxSymlink(const std::wstring& Path)\r\n    {\r\n\r\n        const std::wstring NtPath = L\"\\\\DosDevices\\\\\" + Path;\r\n        UNICODE_STRING Name;\r\n        RtlInitUnicodeString(&Name, NtPath.c_str());\r\n        OBJECT_ATTRIBUTES Attributes;\r\n        InitializeObjectAttributes(&Attributes, &Name, 0, nullptr, 0);\r\n\r\n        IO_STATUS_BLOCK IoStatus;\r\n        FILE_STAT_INFORMATION Info;\r\n        THROW_IF_NTSTATUS_FAILED(NtQueryInformationByName(&Attributes, &IoStatus, &Info, sizeof(Info), FileStatInformation));\r\n\r\n        VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0);\r\n        VERIFY_IS_TRUE((Info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);\r\n        VERIFY_ARE_EQUAL(Info.ReparseTag, IO_REPARSE_TAG_LX_SYMLINK);\r\n    }\r\n};\r\n\r\nclass WSL1 : public DrvFsTests\r\n{\r\n    WSL_TEST_CLASS(WSL1)\r\n\r\n    bool m_initialized{false};\r\n    TEST_CLASS_SETUP(TestClassSetup)\r\n    {\r\n        if (LxsstuVmMode())\r\n        {\r\n            LogSkipped(\"This test class is only applicable to WSL1\");\r\n        }\r\n        else\r\n        {\r\n            VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(LXSST_TESTS_INSTALL_COMMAND_LINE), 0);\r\n            m_initialized = true;\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_CLASS_CLEANUP(TestClassCleanup)\r\n    {\r\n        if (m_initialized)\r\n        {\r\n            LxsstuUninitialize(FALSE);\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD(DrvFsDisableQueryByName)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        VERIFY_NO_THROW(DrvFsCommon(LX_DRVFS_DISABLE_QUERY_BY_NAME));\r\n    }\r\n\r\n    TEST_METHOD(DrvFsDisableQueryByNameAndStatInfo)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        VERIFY_NO_THROW(DrvFsCommon(LX_DRVFS_DISABLE_QUERY_BY_NAME_AND_STAT_INFO));\r\n    }\r\n\r\n    TEST_METHOD(VfsAccessDrvFs)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        DrvFsTests::VfsAccessDrvFs();\r\n    }\r\n\r\n    TEST_METHOD(FsCommonDrvFs)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        DrvFsTests::FsCommonDrvFs();\r\n    }\r\n\r\n    TEST_METHOD(DrvFs)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        DrvFsTests::DrvFs(DrvFsMode::WSL1);\r\n    }\r\n\r\n    TEST_METHOD(DrvFsFat)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        DrvFsTests::DrvFsFat(DrvFsMode::WSL1);\r\n    }\r\n\r\n    TEST_METHOD(DrvFsSmb)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        DrvFsTests::DrvFsSmb(DrvFsMode::WSL1);\r\n    }\r\n\r\n    TEST_METHOD(DrvFsMetadata)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        DrvFsTests::DrvFsMetadata(DrvFsMode::WSL1);\r\n    }\r\n\r\n    TEST_METHOD(XattrDrvFs)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        DrvFsTests::XattrDrvFs(DrvFsMode::WSL1);\r\n    }\r\n\r\n    TEST_METHOD(WslPath)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n        DrvFsTests::WslPath(DrvFsMode::WSL1);\r\n    }\r\n};\r\n\r\n#define WSL2_DRVFS_TEST_CLASS(_mode) \\\r\n    class WSL2##_mode## : public DrvFsTests \\\r\n    { \\\r\n        WSL_TEST_CLASS(WSL2##_mode##) \\\r\n        std::unique_ptr<WslConfigChange> m_config; \\\r\n        TEST_CLASS_SETUP(TestClassSetup) \\\r\n        { \\\r\n            if (!LxsstuVmMode()) \\\r\n            { \\\r\n                LogSkipped(\"This test class is only applicable to WSL2\"); \\\r\n            } \\\r\n            else \\\r\n            { \\\r\n                VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE); \\\r\n                m_config.reset(new WslConfigChange(LxssGenerateTestConfig({.drvFsMode = DrvFsMode::##_mode##}))); \\\r\n                VERIFY_ARE_EQUAL(LxsstuLaunchWsl(LXSST_TESTS_INSTALL_COMMAND_LINE), 0); \\\r\n            } \\\r\n\\\r\n            return true; \\\r\n        } \\\r\n\\\r\n        TEST_CLASS_CLEANUP(TestClassCleanup) \\\r\n        { \\\r\n            if (m_config) \\\r\n            { \\\r\n                m_config.reset(); \\\r\n                LxsstuUninitialize(FALSE); \\\r\n            } \\\r\n\\\r\n            return true; \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(VfsAccessDrvFs) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::VfsAccessDrvFs(); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(FsCommonDrvFs) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::FsCommonDrvFs(); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvFs) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvFs(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvFsFat) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvFsFat(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvFsSmb) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvFsSmb(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvFsMetadata) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvFsMetadata(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvfsMountElevated) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvfsMountElevated(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvfsMountElevatedDifferentConsole) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvfsMountElevatedDifferentConsole(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvfsMountNonElevated) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvfsMountNonElevated(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvfsMountNonElevatedDifferentConsole) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvfsMountNonElevatedDifferentConsole(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvfsMountElevatedSystemDistroEnabled) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvfsMountElevatedSystemDistroEnabled(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvfsMountNonElevatedSystemDistroEnabled) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvfsMountNonElevatedSystemDistroEnabled(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(XattrDrvFs) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::XattrDrvFs(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvFsReFs) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvFsReFs(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(WslPath) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::WslPath(DrvFsMode::##_mode##); \\\r\n        } \\\r\n\\\r\n        TEST_METHOD(DrvFsMountUnicodePath) \\\r\n        { \\\r\n            WSL2_TEST_ONLY(); \\\r\n            DrvFsTests::DrvFsMountUnicodePath(DrvFsMode::##_mode##); \\\r\n        } \\\r\n    }\r\n\r\nWSL2_DRVFS_TEST_CLASS(Plan9);\r\n\r\nWSL2_DRVFS_TEST_CLASS(VirtioFs);\r\n\r\n// Disabled while an issue with the 6.1 Linux kernel causing disk corruption is investigated.\r\n// TODO: Enable again once the issue is resolved\r\n// WSL2_DRVFS_TEST_CLASS(Virtio9p);\r\n\r\n} // namespace DrvFsTests"
  },
  {
    "path": "test/windows/InstallerTests.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    InstallerTests.cpp\n\nAbstract:\n\n    This file contains test cases for the installation process.\n\n--*/\n\n#include \"precomp.h\"\n#include <Sfc.h>\n\n#include \"Common.h\"\n#include \"registry.hpp\"\n#include \"PluginTests.h\"\n\nusing namespace wsl::windows::common::registry;\n\nextern std::wstring g_dumpFolder;\nstatic std::wstring g_pipelineBuildId;\n\nclass InstallerTests\n{\n    std::wstring m_msixPackagePath;\n    std::wstring m_msiPath;\n    std::wstring m_msixInstalledPath;\n    std::filesystem::path m_installedPath;\n    bool m_initialized = false;\n    winrt::Windows::Management::Deployment::PackageManager m_packageManager;\n    wil::unique_hkey m_lxssKey = wsl::windows::common::registry::OpenLxssMachineKey(KEY_ALL_ACCESS);\n    wil::unique_schandle m_scm{OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE, GENERIC_READ | GENERIC_EXECUTE)};\n\n    wil::unique_hfile nulDevice{CreateFileW(\n        L\"nul\", GENERIC_READ, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};\n\n    WSL_TEST_CLASS(InstallerTests)\n\n    TEST_CLASS_SETUP(TestClassSetup)\n    {\n        VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE);\n        m_initialized = true;\n\n        WEX::Common::String MsixPackagePath;\n        WEX::TestExecution::RuntimeParameters::TryGetValue(L\"Package\", MsixPackagePath);\n        m_msixPackagePath = std::filesystem::weakly_canonical(static_cast<std::wstring>(MsixPackagePath)).wstring();\n        VERIFY_IS_FALSE(m_msixPackagePath.empty());\n\n        for (const auto& e : m_packageManager.FindPackages(wsl::windows::common::wslutil::c_msixPackageFamilyName))\n        {\n            VERIFY_IS_TRUE(m_msixInstalledPath.empty());\n            m_msixInstalledPath = std::wstring(e.InstalledLocation().Path().c_str());\n        }\n\n#ifdef WSL_DEV_THIN_MSI_PACKAGE\n\n        m_msiPath = std::filesystem::weakly_canonical(WSL_DEV_THIN_MSI_PACKAGE).wstring();\n\n#else\n\n        VERIFY_IS_TRUE(IsInstallerMsixInstalled(), L\"Installer MSIX absent, can't run the tests\");\n        m_msiPath = (std::filesystem::temp_directory_path() / L\"wsl.msi\").wstring();\n        VERIFY_IS_TRUE(std::filesystem::copy_file(m_msixInstalledPath + L\"\\\\wsl.msi\", m_msiPath, std::filesystem::copy_options::overwrite_existing));\n#endif\n\n        auto installPath = wsl::windows::common::wslutil::GetMsiPackagePath();\n        VERIFY_IS_TRUE(installPath.has_value());\n\n        m_installedPath = std::move(installPath.value());\n\n        wsl::windows::common::helpers::SetHandleInheritable(nulDevice.get());\n\n        return true;\n    }\n\n    TEST_CLASS_CLEANUP(TestClassCleanup)\n    {\n\n#ifndef WSL_DEV_THIN_MSI_PACKAGE\n\n        if (!m_msiPath.empty())\n        {\n            std::filesystem::remove(m_msiPath);\n        }\n\n#endif\n\n        if (m_initialized)\n        {\n            LxsstuUninitialize(FALSE);\n        }\n\n        return true;\n    }\n\n    DWORD GetWslInstallerServiceState() const\n    {\n        const wil::unique_schandle service{OpenServiceW(m_scm.get(), L\"Wslinstaller\", SERVICE_QUERY_STATUS)};\n        VERIFY_IS_NOT_NULL(service);\n\n        SERVICE_STATUS status{};\n        VERIFY_IS_TRUE(QueryServiceStatus(service.get(), &status));\n\n        return status.dwCurrentState;\n    }\n\n    void WaitForInstallerServiceStop()\n    {\n        DWORD lastState = -1;\n        auto pred = [&]() {\n            lastState = GetWslInstallerServiceState();\n            THROW_HR_IF(E_FAIL, lastState != SERVICE_STOPPED);\n        };\n\n        try\n        {\n            wsl::shared::retry::RetryWithTimeout<void>(pred, std::chrono::hours(3), std::chrono::minutes(2));\n        }\n        catch (...)\n        {\n            LogError(\"InstallerService state: %lu\", lastState);\n            VERIFY_FAIL(\"Timed out waiting for the installer service to stop\");\n        }\n    }\n\n    static std::wstring GenerateMsiLogPath()\n    {\n        // Note: canonical is required because msiexec seems to be unable to deal with symlinks.\n        static int counter = 0;\n        return std::format(L\"{}\\\\msi-install-{}.txt\", std::filesystem::canonical(g_dumpFolder).c_str(), counter++);\n    }\n\n    bool IsInstallerMsixInstalled() const\n    {\n        return std::filesystem::exists(m_msixInstalledPath + L\"\\\\wslinstaller.exe\");\n    }\n\n    bool IsMsixInstalled() const\n    {\n        return m_packageManager.FindPackagesForUser(L\"\", wsl::windows::common::wslutil::c_msixPackageFamilyName).First().HasCurrent();\n    }\n\n    static void CallMsiExec(const std::wstring& Args)\n    {\n        std::wstring commandLine;\n        THROW_IF_FAILED(wil::GetSystemDirectoryW(commandLine));\n        commandLine += L\"\\\\msiexec.exe \" + Args;\n\n        LogInfo(\"Calling msiexec: %ls\", commandLine.c_str());\n\n        // There is a potential race condition given that we have no easy way to know when the msiexec process\n        // created by the installer service will release the MSI mutex.\n        // If the mutex is still held when we call msiexec, it will fails with ERROR_INSTALL_ALREADY_RUNNING\n        // so retry for up to two minutes if we get that error back.\n\n        DWORD exitCode = -1;\n        wsl::shared::retry::RetryWithTimeout<void>(\n            [&]() {\n                exitCode = LxsstuRunCommand(commandLine.data());\n                THROW_HR_IF(E_ABORT, exitCode == ERROR_INSTALL_ALREADY_RUNNING);\n            },\n            std::chrono::seconds(1),\n            std::chrono::minutes(2),\n            []() { return wil::ResultFromCaughtException() == E_ABORT; });\n\n        VERIFY_ARE_EQUAL(0L, exitCode);\n    }\n\n    std::wstring GetMsiProductCode() const\n    {\n        return wsl::windows::common::registry::ReadString(m_lxssKey.get(), L\"MSI\", L\"ProductCode\", L\"\");\n    }\n\n    void UninstallMsi()\n    {\n        auto productCode = GetMsiProductCode();\n        VERIFY_IS_FALSE(productCode.empty());\n\n        CallMsiExec(std::format(L\"/qn /norestart /x {} /L*V {}\", productCode, GenerateMsiLogPath()));\n    }\n\n    void InstallMsi()\n    {\n        CallMsiExec(std::format(L\"/qn /norestart /i {} /L*V {}\", m_msiPath, GenerateMsiLogPath()));\n    }\n\n    void InstallMsix() const\n    {\n        const auto result =\n            m_packageManager\n                .AddPackageAsync(winrt::Windows::Foundation::Uri{m_msixPackagePath}, nullptr, winrt::Windows::Management::Deployment::DeploymentOptions::None)\n                .get();\n\n        VERIFY_ARE_EQUAL(result.ExtendedErrorCode(), S_OK);\n    }\n\n    void UninstallMsix() const\n    {\n        auto result = m_packageManager.DeprovisionPackageForAllUsersAsync(wsl::windows::common::wslutil::c_msixPackageFamilyName).get();\n        auto errorCode = result.ExtendedErrorCode();\n        if (FAILED(errorCode) && errorCode != HRESULT_FROM_WIN32(ERROR_NOT_FOUND))\n        {\n            LogError(\"Error deprovisioning the package: 0x%x, %ls\", errorCode, result.ErrorText().c_str());\n            VERIFY_FAIL();\n        }\n\n        for (const auto& e : m_packageManager.FindPackages(wsl::windows::common::wslutil::c_msixPackageFamilyName))\n        {\n            // Remove the package for the current user first.\n            result = m_packageManager.RemovePackageAsync(e.Id().FullName()).get();\n            errorCode = result.ExtendedErrorCode();\n            if (FAILED(errorCode) && errorCode != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))\n            {\n                LogError(\"Error deprovisioning the package: 0x%x, %ls\", errorCode, result.ErrorText().c_str());\n                VERIFY_FAIL();\n            }\n\n            // then remove the package for all users.\n            result = m_packageManager\n                         .RemovePackageAsync(e.Id().FullName(), winrt::Windows::Management::Deployment::RemovalOptions::RemoveForAllUsers)\n                         .get();\n            errorCode = result.ExtendedErrorCode();\n            if (FAILED(errorCode) && errorCode != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))\n            {\n                LogError(\"Error deprovisioning the package: 0x%x, %ls\", errorCode, result.ErrorText().c_str());\n                VERIFY_FAIL();\n            }\n        }\n    }\n\n    bool IsMsiPackageInstalled()\n    {\n        // Check for wslservice to be installed because MSI installs registry keys before installing services\n        return !GetMsiProductCode().empty() && wsl::windows::common::helpers::IsServicePresent(L\"wslservice\");\n    }\n\n    static bool IsMsixInstallerInstalled()\n    {\n        return wsl::windows::common::helpers::IsServicePresent(L\"wslinstaller\");\n    }\n\n    void WaitForMsiPackageInstall()\n    {\n        auto pred = [this]() { THROW_HR_IF(E_FAIL, !IsMsiPackageInstalled()); };\n\n        try\n        {\n            // It is possible for the 'DeprovisionMsix' stage of the MSI installation to take a long time.\n            // On vb_release, up to 7 minutes have been observed. Wait for up to 20 minutes to be safe.\n            wsl::shared::retry::RetryWithTimeout<void>(pred, std::chrono::seconds(1), std::chrono::minutes(20));\n        }\n        catch (...)\n        {\n            VERIFY_FAIL(\"Timed out waiting for MSI package installation\");\n        }\n    }\n\n    static void ValidateInstalledVersion(LPCWSTR expectedVersion)\n    {\n        auto pred = [expectedVersion]() {\n            // Validate that we're not using inbox WSL\n            auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"--version\");\n            if (output.find(expectedVersion) == std::string::npos)\n            {\n                LogInfo(\"Package is not installed yet. Output: %ls\", output.c_str());\n                THROW_HR(E_FAIL);\n            }\n\n            LogInfo(\"Package is properly installed. Output: %ls\", output.c_str());\n        };\n\n        // Sadly the provisioning of MSIX packages for user accounts isn't synchronous so we need to wait until the package\n        // becomes visible.\n        try\n        {\n            wsl::shared::retry::RetryWithTimeout<void>(pred, std::chrono::seconds(1), std::chrono::minutes(2));\n        }\n        catch (...)\n        {\n            VERIFY_FAIL(\"Timed out waiting for MSIX package to be available\");\n        }\n    }\n\n    void ValidatePackageInstalledProperly(LPCWSTR expectedVersion = WIDEN(WSL_PACKAGE_VERSION))\n    {\n        ValidateInstalledVersion(expectedVersion);\n\n        auto checkInstalled = []() {\n            auto commandLine = LxssGenerateWslCommandLine(L\"echo OK\");\n            auto [output, _, exitCode] = LxsstuLaunchCommandAndCaptureOutputWithResult(commandLine.data());\n            if (exitCode != 0 && output.find(L\"Wsl/ERROR_SHARING_VIOLATION\") != std::wstring::npos)\n            {\n                THROW_HR(HRESULT_FROM_WIN32(ERROR_RETRY));\n            }\n\n            return std::make_pair(exitCode, output);\n        };\n\n        // When upgrading from MSIX, wsl.exe might not be available right away. Retry if we get Wsl/ERROR_SHARING_VIOLATION back.\n        std::wstring output;\n        int exitCode = -1;\n        try\n        {\n            std::tie(exitCode, output) = wsl::shared::retry::RetryWithTimeout<std::pair<int, std::wstring>>(\n                checkInstalled, std::chrono::seconds(1), std::chrono::minutes(2), []() {\n                    return wil::ResultFromCaughtException() == HRESULT_FROM_WIN32(ERROR_RETRY);\n                });\n        }\n        catch (...)\n        {\n            VERIFY_FAIL(\"Timed out waiting for WSL to be installed.\");\n        }\n\n        VERIFY_ARE_EQUAL(exitCode, 0);\n        VERIFY_ARE_EQUAL(output, L\"OK\\n\");\n\n        // Check that the installed version is the one we expect\n        const auto key = wsl::windows::common::registry::OpenLxssMachineKey();\n        const auto version = wsl::windows::common::registry::ReadString(key.get(), L\"MSI\", L\"Version\", L\"\");\n\n        VERIFY_ARE_EQUAL(version, expectedVersion);\n        VERIFY_IS_FALSE(GetMsiProductCode().empty());\n\n        // TODO: check wslsupport, wslapi and p9rdr\n    }\n\n    void DeleteProductCode() const\n    {\n        const auto msiKey = wsl::windows::common::registry::OpenKey(m_lxssKey.get(), L\"MSI\", KEY_ALL_ACCESS);\n        wsl::windows::common::registry::DeleteKeyValue(msiKey.get(), L\"ProductCode\");\n    }\n\n    void InstallGitHubRelease(const std::wstring& version)\n    {\n        auto arch = wsl::shared::Arm64 ? L\".0.arm64\" : L\".0.x64\";\n\n        std::wstring installerFile;\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&installerFile]() { DeleteFile(installerFile.c_str()); });\n\n        if (auto found = L\"wsl.\" + version + arch + L\".msi\"; PathFileExists(found.c_str()))\n        {\n            installerFile = std::filesystem::weakly_canonical(found);\n            cleanup.release();\n        }\n        else if (auto found = L\"Microsoft.WSL_\" + version + L\".0_x64_ARM64.msixbundle\"; PathFileExists(found.c_str()))\n        {\n            installerFile = std::filesystem::weakly_canonical(found);\n            cleanup.release();\n        }\n        else\n        {\n            LogInfo(\"Downloading: %ls\", version.c_str());\n\n            VERIFY_IS_TRUE(g_pipelineBuildId.empty()); // Pipeline builds should have the installers already available\n            auto release = wsl::windows::common::wslutil::GetGitHubReleaseByTag(version);\n            auto asset = wsl::windows::common::wslutil::GetGitHubAssetFromRelease(release);\n            VERIFY_IS_TRUE(asset.has_value());\n\n            auto downloadPath = wsl::windows::common::wslutil::DownloadFile(asset->second.url, asset->second.name);\n            installerFile = std::move(downloadPath);\n        }\n\n        LogInfo(\"Installing: %ls\", installerFile.c_str());\n        if (wsl::shared::string::EndsWith<wchar_t>(installerFile, L\".msi\"))\n        {\n            CallMsiExec(std::format(L\"/qn /norestart /i {} /L*V {}\", installerFile, GenerateMsiLogPath()));\n        }\n        else\n        {\n            auto result =\n                m_packageManager\n                    .AddPackageAsync(winrt::Windows::Foundation::Uri{installerFile}, nullptr, winrt::Windows::Management::Deployment::DeploymentOptions::None)\n                    .get();\n\n            VERIFY_SUCCEEDED(result.ExtendedErrorCode());\n        }\n\n        ValidateInstalledVersion(version.c_str());\n    }\n\n    TEST_METHOD(UpgradeFromWsl130)\n    {\n        UninstallMsi();\n        InstallGitHubRelease(L\"1.3.17\");\n\n        // Note: we can't use wsl --update here because GitHubUrlOverride was introduced in 2.0.0\n        InstallMsi();\n        ValidatePackageInstalledProperly();\n    }\n\n    TEST_METHOD(UpgradeFromWsl200)\n    {\n        UninstallMsi();\n\n        // Note: we can't use wsl --update here because wsl 2.0.0 passes REINSTALL=ALL to msiexec\n        InstallGitHubRelease(L\"2.0.0\");\n        InstallMsi();\n        ValidatePackageInstalledProperly();\n    }\n\n    TEST_METHOD(UpgradeFromWsl202)\n    {\n        UninstallMsi();\n        InstallGitHubRelease(L\"2.0.2\");\n        CallWslUpdateViaMsi();\n    }\n\n    TEST_METHOD(MsrdcPluginKey)\n    {\n        // Remove the MSI package.\n        UninstallMsi();\n\n        // Create a volatile registry key to emulate what the old MSIX would do.\n        const auto key = wsl::windows::common::registry::CreateKey(\n            HKEY_LOCAL_MACHINE, L\"SOFTWARE\\\\Microsoft\\\\Terminal Server Client\\\\Default\", KEY_ALL_ACCESS);\n        VERIFY_IS_TRUE(!!key);\n\n        wsl::windows::common::registry::DeleteKey(key.get(), L\"OptionalAddIns\\\\WSLDVC_PACKAGE\");\n\n        const auto volatileKey =\n            wsl::windows::common::registry::CreateKey(key.get(), L\"OptionalAddIns\\\\WSLDVC_PACKAGE\", KEY_READ, nullptr, REG_OPTION_VOLATILE);\n        VERIFY_IS_TRUE(wsl::windows::common::registry::IsKeyVolatile(volatileKey.get()));\n\n        // Install the MSI.\n        InstallMsi();\n\n        // Validate that the key is correctly written to and isn't volatile anymore.\n        auto pluginPath = wsl::windows::common::wslutil::GetMsiPackagePath().value_or(L\"\");\n        VERIFY_IS_FALSE(pluginPath.empty());\n\n        pluginPath += L\"WSLDVCPlugin.dll\";\n\n        const auto pluginKey = wsl::windows::common::registry::OpenKey(\n            HKEY_LOCAL_MACHINE, L\"SOFTWARE\\\\Microsoft\\\\Terminal Server Client\\\\Default\\\\OptionalAddIns\\\\WSLDVC_PACKAGE\", KEY_READ);\n        const auto value = wsl::windows::common::registry::ReadString(pluginKey.get(), nullptr, L\"Name\", L\"\");\n        VERIFY_ARE_EQUAL(value, pluginPath);\n\n        VERIFY_IS_FALSE(wsl::windows::common::registry::IsKeyVolatile(pluginKey.get()));\n    }\n\n    TEST_METHOD(UninstallingMsiRemovesInstallerMsix)\n    {\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        InstallMsix();\n        WaitForMsiPackageInstall();\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_TRUE(IsMsixInstallerInstalled());\n\n        ValidatePackageInstalledProperly();\n    }\n\n    TEST_METHOD(InstallingMsiInstallsGluePackage)\n    {\n        // Remove the MSI package\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        // Installing it again and validate that the glue package was added\n        InstallMsi();\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_FALSE(IsMsixInstallerInstalled());\n        ValidatePackageInstalledProperly();\n\n        // Validate that removing it removes the glue package\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        // Validate that installing the installer gets the MSI installed properly again\n        InstallMsix();\n        WaitForMsiPackageInstall();\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_TRUE(IsMsixInstallerInstalled());\n        ValidatePackageInstalledProperly();\n    }\n\n    TEST_METHOD(UpgradeInstallsTheMsiPackage)\n    {\n        // Remove the MSIX package\n        UninstallMsix();\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        // Validate that upgrading the MSI installs the MSIX again\n        InstallMsi();\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_FALSE(IsMsixInstallerInstalled());\n        ValidatePackageInstalledProperly();\n    }\n\n    TEST_METHOD(MsixInstallerUpgrades)\n    {\n        // Remove the MSIX package\n        UninstallMsix();\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        // Remove the MSI package\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        RegistryKeyChange<std::wstring> change(\n            HKEY_LOCAL_MACHINE, L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\MSI\", L\"Version\", L\"1.0.0\");\n\n        DeleteProductCode();\n        VERIFY_IS_TRUE(GetMsiProductCode().empty());\n\n        // Validate that upgrading the MSIX upgrades the MSI\n        InstallMsix();\n        WaitForMsiPackageInstall();\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_TRUE(IsMsixInstallerInstalled());\n\n        // Validate that the version got upgraded\n        const auto key = wsl::windows::common::registry::OpenLxssMachineKey();\n        const auto versionValue = wsl::windows::common::registry::ReadString(key.get(), L\"MSI\", L\"Version\");\n\n        VERIFY_ARE_EQUAL(versionValue, WIDEN(WSL_PACKAGE_VERSION));\n    }\n\n    TEST_METHOD(MsixInstallerUpgradesWithLogFile)\n    {\n        // Remove the MSIX package\n        UninstallMsix();\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        // Remove the MSI package\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        RegistryKeyChange<std::wstring> version(\n            HKEY_LOCAL_MACHINE, L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\MSI\", L\"Version\", L\"1.0.0\");\n\n        auto logFilePath = std::filesystem::current_path() / \"msi-install-logs.txt\";\n\n        RegistryKeyChange<std::wstring> logFile(\n            HKEY_LOCAL_MACHINE, L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\MSI\", L\"UpgradeLogFile\", logFilePath.c_str());\n\n        auto cleanup =\n            wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&logFilePath]() { LOG_IF_WIN32_BOOL_FALSE(DeleteFile(logFilePath.c_str())); });\n\n        DeleteProductCode();\n        VERIFY_IS_TRUE(GetMsiProductCode().empty());\n\n        // Validate that upgrading the MSIX upgrades the MSI\n        InstallMsix();\n        WaitForMsiPackageInstall();\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_TRUE(IsMsixInstallerInstalled());\n\n        // Validate that the version got upgraded\n        const auto key = wsl::windows::common::registry::OpenLxssMachineKey();\n        const auto versionValue = wsl::windows::common::registry::ReadString(key.get(), L\"MSI\", L\"Version\");\n\n        VERIFY_ARE_EQUAL(versionValue, WIDEN(WSL_PACKAGE_VERSION));\n\n        // Validate that the log file exists and is not empty\n        VERIFY_IS_TRUE(std::filesystem::exists(logFilePath));\n        VERIFY_ARE_NOT_EQUAL(std::filesystem::file_size(logFilePath), 0);\n    }\n\n    TEST_METHOD(MsixInstallerDoesntDowngrade)\n    {\n        // Remove the MSIX package\n        UninstallMsix();\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        RegistryKeyChange<std::wstring> change(\n            HKEY_LOCAL_MACHINE, L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\MSI\", L\"Version\", L\"999.0.0\");\n\n        // Validate that upgrading the MSIX doesn't downgrade the MSI\n        InstallMsix();\n        WaitForInstallerServiceStop();\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_TRUE(IsMsixInstallerInstalled());\n\n        // Validate that the version dit not get upgrade\n        const auto key = wsl::windows::common::registry::OpenLxssMachineKey();\n        const auto versionValue = wsl::windows::common::registry::ReadString(key.get(), L\"MSI\", L\"Version\");\n\n        VERIFY_ARE_EQUAL(versionValue, L\"999.0.0\");\n    }\n\n    TEST_METHOD(MsixUpgradeManual)\n    {\n        // Registry key to disable auto upgrade on service start\n        RegistryKeyChange<DWORD> change(\n            HKEY_LOCAL_MACHINE, L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\MSI\", L\"AutoUpgradeViaMsix\", static_cast<DWORD>(0));\n\n        // Remove the MSI package\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        // Install the installer MSIX\n        InstallMsix();\n\n        // Validate that calling wsl.exe triggers the install\n        auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(L\"echo ok\");\n        VERIFY_ARE_EQUAL(L\"ok\\n\", output);\n        VERIFY_ARE_EQUAL(L\"WSL is finishing an upgrade...\\r\\n\", warnings);\n\n        ValidatePackageInstalledProperly();\n    }\n\n    TEST_METHOD(MsixUpgradeFails)\n    {\n        // Remove the MSI package\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { InstallMsi(); });\n\n        // Open a handle to wsl.exe in the install directory which will cause the installation to fail.\n        //\n        // N.B. The file handle will be closed before the cleanup lambda runs.\n        std::filesystem::create_directories(m_installedPath);\n        wil::unique_hfile fileHandle(::CreateFileW(\n            (m_installedPath / WSL_BINARY_NAME).c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));\n\n        // Install the installer MSIX\n        InstallMsix();\n\n        // Validate that calling wsl.exe triggers the install.\n        auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(L\"echo ok\", -1, nulDevice.get());\n        VERIFY_ARE_EQUAL(\n            L\"\\r\\nAnother application has exclusive access to the file 'C:\\\\Program Files\\\\WSL\\\\wsl.exe'.  Please shut down all \"\n            L\"other applications, then click Retry.\\r\\n\"\n            L\"Update failed (exit code: 1603).\\r\\n\"\n            L\"Error code: Wsl/CallMsi/Install/ERROR_INSTALL_FAILURE\\r\\n\",\n            output);\n    }\n\n    TEST_METHOD(WslUpdateNoNewVersion)\n    {\n        constexpr auto endpoint = L\"http://127.0.0.1:12345/\";\n        RegistryKeyChange<std::wstring> change(\n            HKEY_LOCAL_MACHINE, L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\", wsl::windows::common::wslutil::c_githubUrlOverrideRegistryValue, endpoint);\n\n        constexpr auto GitHubApiResponse =\n            LR\"({\n                    \\\"name\\\": \\\"1.0.0\\\",\n                    \\\"created_at\\\": \\\"2023-06-14T16:56:30Z\\\",\n                    \\\"assets\\\": [\n                        {\n                            \\\"url\\\": \\\"https://api.github.com/repos/microsoft/WSL/releases/assets/112754644\\\",\n                            \\\"id\\\": 112754644,\n                            \\\"name\\\": \\\"Microsoft.WSL_1.0.0.0_x64_ARM64.msixbundle\\\"\n                        }\n                     ]\n                 })\";\n\n        UniqueWebServer server(endpoint, GitHubApiResponse);\n\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"--update\");\n        VERIFY_ARE_EQUAL(\n            out, L\"Checking for updates.\\r\\nThe most recent version of Windows Subsystem for Linux is already installed.\\r\\n\");\n    }\n\n    TEST_METHOD(InstallRemovesStaleComRegistration)\n    {\n        constexpr auto clsid = L\"{A9B7A1B9-0671-405C-95F1-E0612CB4CE7E}\";\n\n        // Remove the MSI package\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        // Emulate a leaked packaged COM registration\n        auto packagedComClassIndex{wsl::windows::common::registry::OpenKey(\n            HKEY_LOCAL_MACHINE, L\"SOFTWARE\\\\Classes\\\\PackagedCom\\\\ClassIndex\", KEY_CREATE_SUB_KEY | KEY_READ)};\n\n        auto keyExists = [&packagedComClassIndex]() {\n            wil::unique_hkey subKey;\n\n            const auto result = RegOpenKeyEx(packagedComClassIndex.get(), clsid, 0, KEY_READ, &subKey);\n\n            return result == NO_ERROR;\n        };\n\n        wsl::windows::common::registry::CreateKey(packagedComClassIndex.get(), clsid);\n\n        VERIFY_IS_TRUE(keyExists());\n\n        // Install the package and validate that the registry key was removed.\n        InstallMsi();\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_FALSE(IsMsixInstallerInstalled());\n\n        VERIFY_IS_FALSE(keyExists());\n        ValidatePackageInstalledProperly();\n    }\n\n    TEST_METHOD(InstallremovesStaleServiceRegistration)\n    {\n        // Remove the MSI package.\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        // Emulate a leaked packaged service registration.\n        const wil::unique_schandle manager{OpenSCManager(nullptr, nullptr, SC_MANAGER_CREATE_SERVICE)};\n\n        wil::unique_schandle service{CreateService(\n            manager.get(),\n            L\"wslservice\",\n            L\"WSL test service\",\n            GENERIC_ALL,\n            SERVICE_WIN32_OWN_PROCESS,\n            SERVICE_DISABLED,\n            SERVICE_ERROR_IGNORE,\n            L\"C:\\\\windows\\\\system32\\\\cmd.exe\",\n            nullptr,\n            nullptr,\n            nullptr,\n            nullptr,\n            nullptr)};\n\n        service.reset();\n\n        wil::unique_hkey key = wsl::windows::common::registry::OpenKey(HKEY_LOCAL_MACHINE, L\"SYSTEM\\\\CurrentControlSet\\\\Services\", KEY_WRITE);\n        THROW_LAST_ERROR_IF(!key);\n\n        wsl::windows::common::registry::WriteString(key.get(), L\"wslservice\", L\"AppUserModelId\", L\"Dummy\");\n        key.reset();\n\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\n            service.reset(OpenService(manager.get(), L\"wslservice\", DELETE));\n            THROW_LAST_ERROR_IF(!service);\n\n            LOG_IF_WIN32_BOOL_FALSE(DeleteService(service.get()));\n        });\n\n        // Install the MSI package. It should delete the wslservice.\n        InstallMsi();\n        cleanup.release();\n\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n\n        ValidatePackageInstalledProperly();\n\n        // Validate that the AppUserModelId registry value is gone.\n        VERIFY_ARE_EQUAL(wsl::windows::common::registry::ReadString(key.get(), L\"wslservice\", L\"AppUserModelId\", L\"\"), L\"\");\n    }\n\n    TEST_METHOD(InstallSetsLspRegistration)\n    {\n        auto installPath = wsl::windows::common::wslutil::GetMsiPackagePath();\n        VERIFY_IS_TRUE(installPath.has_value());\n\n        // Remove the MSI package.\n        UninstallMsi();\n        VERIFY_IS_FALSE(IsMsiPackageInstalled());\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        // Validate that LSP flags are not set\n        auto getLspFlags = [&installPath](const auto* path) -> std::optional<DWORD> {\n            DWORD flags{};\n            INT error{};\n\n            if (WSCGetApplicationCategory(path, static_cast<DWORD>(wcslen(path)), nullptr, 0, &flags, &error) == SOCKET_ERROR)\n            {\n                if (error != WSASERVICE_NOT_FOUND)\n                {\n                    LogError(\"WSCGetApplicationCategory failed for: %ls, error: %i\", path, error);\n                }\n\n                return {};\n            }\n\n            return flags;\n        };\n\n        const std::vector<LPCWSTR> executables = {L\"wsl.exe\", L\"wslhost.exe\", L\"wslrelay.exe\", L\"wslg.exe\"};\n        for (const auto& e : executables)\n        {\n            auto fullPath = installPath.value() + e;\n            VERIFY_ARE_EQUAL(getLspFlags(fullPath.c_str()).value_or(0), 0);\n        }\n\n        // Install the package\n        InstallMsi();\n\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        ValidatePackageInstalledProperly();\n\n        // Validate that the LSP flags were correctly set\n        for (const auto& e : executables)\n        {\n            auto fullPath = installPath.value() + e;\n            VERIFY_ARE_EQUAL(getLspFlags(fullPath.c_str()).value_or(0), LSP_SYSTEM);\n        }\n    }\n\n    TEST_METHOD(InstallClearsExplorerState)\n    {\n        constexpr auto valueName = L\"Attributes\";\n\n        // Put the explorer in a state where the WSL shortcut is hidden.\n\n        const auto key = wsl::windows::common::registry::CreateKey(\n            HKEY_CURRENT_USER,\n            LR\"(Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}\\ShellFolder)\",\n            KEY_READ | KEY_WRITE);\n\n        wsl::windows::common::registry::WriteDword(key.get(), nullptr, valueName, SFGAO_NONENUMERATED);\n\n        // Install the package\n        InstallMsi();\n\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        ValidatePackageInstalledProperly();\n\n        // Validate that the installer removed the problematic flag\n\n        VERIFY_ARE_EQUAL(wsl::windows::common::registry::ReadDword(key.get(), nullptr, valueName, 0), 0);\n    }\n\n    TEST_METHOD(InstallUnprotectsKeys)\n    {\n        const auto installPath = wsl::windows::common::wslutil::GetMsiPackagePath();\n        VERIFY_IS_TRUE(installPath.has_value());\n\n        constexpr auto keyPath = LR\"(SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IdListAliasTranslations\\WSL)\";\n\n        // Create a protected key that the installer will write to\n        {\n            auto [localAdministratorsSid, adminSidBuffer] =\n                wsl::windows::common::security::CreateSid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);\n\n            EXPLICIT_ACCESS aces[2];\n            aces[0].grfAccessMode = SET_ACCESS;\n            aces[0].grfAccessPermissions = KEY_READ;\n            aces[0].grfInheritance = NO_INHERITANCE;\n            BuildTrusteeWithSid(&aces[0].Trustee, localAdministratorsSid);\n\n            aces[1].grfAccessMode = GRANT_ACCESS;\n            aces[1].grfAccessPermissions = KEY_ALL_ACCESS;\n            aces[1].grfInheritance = NO_INHERITANCE;\n            std::wstring trustedInstaller = L\"NT Service\\\\TrustedInstaller\";\n            BuildTrusteeWithName(&aces[1].Trustee, trustedInstaller.data());\n\n            wsl::windows::common::security::unique_acl newAcl{};\n            THROW_IF_WIN32_ERROR(SetEntriesInAcl(2, aces, nullptr, &newAcl));\n\n            SECURITY_DESCRIPTOR newDescriptor{};\n            THROW_IF_WIN32_BOOL_FALSE(InitializeSecurityDescriptor(&newDescriptor, SECURITY_DESCRIPTOR_REVISION));\n            THROW_IF_WIN32_BOOL_FALSE(SetSecurityDescriptorDacl(&newDescriptor, true, newAcl.get(), false));\n\n            auto restore = wsl::windows::common::security::AcquirePrivileges({SE_BACKUP_NAME, SE_RESTORE_NAME});\n\n            const auto key =\n                wsl::windows::common::registry::CreateKey(HKEY_LOCAL_MACHINE, keyPath, KEY_ALL_ACCESS, nullptr, REG_OPTION_BACKUP_RESTORE);\n            THROW_IF_WIN32_ERROR(RegSetKeySecurity(key.get(), DACL_SECURITY_INFORMATION, &newDescriptor));\n        }\n\n        VERIFY_IS_TRUE(SfcIsKeyProtected(HKEY_LOCAL_MACHINE, keyPath, KEY_WOW64_64KEY));\n\n        // Install the package\n        InstallMsi();\n\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        ValidatePackageInstalledProperly();\n\n        // Verify that key was unprotected.\n        VERIFY_IS_FALSE(SfcIsKeyProtected(HKEY_LOCAL_MACHINE, keyPath, KEY_WOW64_64KEY));\n    }\n\n    void CallWslUpdateViaMsi()\n    {\n\n#ifdef WSL_DEV_THIN_MSI_PACKAGE\n\n        LogSkipped(\"This test case cannot run with a thin MSI package\");\n        return;\n\n#endif\n\n        constexpr auto endpoint = L\"http://127.0.0.1:12345/\";\n        RegistryKeyChange<std::wstring> change(\n            HKEY_LOCAL_MACHINE, L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\", wsl::windows::common::wslutil::c_githubUrlOverrideRegistryValue, endpoint);\n\n        constexpr auto GitHubApiResponse =\n            LR\"({\n                    \\\"name\\\": \\\"999.0.0\\\",\n                    \\\"created_at\\\": \\\"2023-06-14T16:56:30Z\\\",\n                    \\\"assets\\\": [\n                        {\n                            \\\"url\\\": \\\"http://127.0.0.1:12346/wsl.testpackage.x64.msi\\\",\n                            \\\"id\\\": 0,\n                            \\\"name\\\": \\\"wsl.testpackage.x64.msi\\\"\n                        }\n                     ]\n                 })\";\n\n        UniqueWebServer apiServer(endpoint, GitHubApiResponse);\n        UniqueWebServer fileServer(L\"http://127.0.0.1:12346/\", std::filesystem::path(m_msiPath));\n\n        // DeleteProductCode();\n        // TODO: It would be good to remove something to validate that the MSI actually gets installed,\n        // but this doesn't work during the tests because the ProductCode is the same so the components\n        // don't actually get reinstalled and we can't use REINSTALLMODE because this would skip component removal\n        // during upgrade.\n\n        // The MSI upgrade can send a ctrl-c to wsl.exe, so create a new console so the test process doesn't receive the ctrl-c.\n        const auto commandLine = LxssGenerateWslCommandLine(L\"--update\");\n        wsl::windows::common::SubProcess process(nullptr, commandLine.data(), CREATE_NEW_CONSOLE);\n        process.SetShowWindow(SW_HIDE);\n        LogInfo(\"wsl --update exited with: %lu\", process.Run());\n\n        // wsl --update isn't synchronous since wsl.exe will be killed during the installation.\n        WaitForMsiPackageInstall();\n\n        ValidatePackageInstalledProperly();\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_TRUE(!GetMsiProductCode().empty());\n    }\n\n    TEST_METHOD(WslUpdateViaMsi)\n    {\n        CallWslUpdateViaMsi();\n    }\n\n    TEST_METHOD(WslUpdateViaMsix)\n    {\n        constexpr auto endpoint = L\"http://127.0.0.1:12345/\";\n        RegistryKeyChange<std::wstring> change(\n            HKEY_LOCAL_MACHINE, L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\", wsl::windows::common::wslutil::c_githubUrlOverrideRegistryValue, endpoint);\n\n        constexpr auto GitHubApiResponse =\n            LR\"({\n                    \\\"name\\\": \\\"999.0.0\\\",\n                    \\\"created_at\\\": \\\"2023-06-14T16:56:30Z\\\",\n                    \\\"assets\\\": [\n                        {\n                            \\\"url\\\": \\\"http://127.0.0.1:12346/wsl.testpackage.msixbundle\\\",\n                            \\\"id\\\": 0,\n                            \\\"name\\\": \\\"wsl.testpackage.x64.msixbundle\\\"\n                        }\n                     ]\n                 })\";\n\n        UniqueWebServer apiServer(endpoint, GitHubApiResponse);\n        UniqueWebServer fileServer(L\"http://127.0.0.1:12346/\", std::filesystem::path(m_msixPackagePath));\n\n        UninstallMsix();\n        VERIFY_IS_FALSE(IsMsixInstalled());\n\n        const auto installLocation = wsl::windows::common::wslutil::GetMsiPackagePath();\n        VERIFY_IS_TRUE(installLocation.has_value());\n        auto cmd = installLocation.value() + L\"\\\\wsl.exe --update\";\n        LxsstuRunCommand(cmd.data()); // Ignore the error code since wsl.exe will be killed by msiexec\n\n        VERIFY_IS_TRUE(IsMsiPackageInstalled());\n        VERIFY_IS_TRUE(IsMsixInstalled());\n        VERIFY_IS_TRUE(IsMsixInstallerInstalled());\n        ValidatePackageInstalledProperly();\n    }\n\n    static bool WslSettingsProtocolAssociationExists()\n    {\n        wil::com_ptr<IEnumAssocHandlers> enumAssocHandlers;\n        if (FAILED(SHAssocEnumHandlersForProtocolByApplication(L\"wsl-settings\", IID_PPV_ARGS(&enumAssocHandlers))))\n        {\n            return false;\n        }\n\n        ULONG elementsReturned;\n        wil::com_ptr<IAssocHandler> currentAssoc;\n        while (SUCCEEDED(enumAssocHandlers->Next(1, &currentAssoc, &elementsReturned)))\n        {\n            wil::unique_cotaskmem_string name;\n            if (FAILED(currentAssoc->GetName(&name)))\n            {\n                LogError(\"Failed to get association name, continuing...\");\n                continue;\n            }\n\n            if (::_wcsicmp(name.get(), L\"WSL Settings\") != 0)\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    void VerifyWslSettingsProtocolAssociationExistsWithRetry()\n    {\n        VERIFY_NO_THROW(wsl::shared::retry::RetryWithTimeout<void>(\n            [&]() { THROW_HR_IF(E_UNEXPECTED, !WslSettingsProtocolAssociationExists()); }, std::chrono::seconds(1), std::chrono::minutes(2)));\n    }\n\n    TEST_METHOD(WslValidateWslSettingsProtocol)\n    {\n        WSL_SETTINGS_TEST();\n\n        SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);\n        VerifyWslSettingsProtocolAssociationExistsWithRetry();\n\n        UninstallMsi();\n        SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);\n\n        wil::com_ptr<IEnumAssocHandlers> enumAssocHandlers;\n        const HRESULT hr = SHAssocEnumHandlersForProtocolByApplication(L\"wsl-settings\", IID_PPV_ARGS(&enumAssocHandlers));\n        if (FAILED(hr))\n        {\n            VERIFY_ARE_EQUAL(hr, HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION));\n        }\n        else\n        {\n            VERIFY_IS_FALSE(WslSettingsProtocolAssociationExists());\n        }\n\n        InstallMsi();\n        SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);\n        VerifyWslSettingsProtocolAssociationExistsWithRetry();\n    }\n};"
  },
  {
    "path": "test/windows/MountTests.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    MountTests.cpp\r\n\r\nAbstract:\r\n\r\n    This file contains test cases for the disk mounting logic.\r\n\r\n--*/\r\n\r\n#include \"precomp.h\"\r\n#include \"Common.h\"\r\n\r\n#define TEST_MOUNT_DISK L\"TestDisk.vhd\"\r\n#define TEST_MOUNT_VHD L\"TestVhd.vhd\"\r\n#define TEST_UNMOUNT_VHD_DNE L\"TestVhdNotHere.vhd\"\r\n#define TEST_MOUNT_NAME L\"testmount\"\r\n\r\n#define SKIP_UNSUPPORTED_ARM64_MOUNT_TEST() \\\r\n    if constexpr (wsl::shared::Arm64) \\\r\n    { \\\r\n        WSL_TEST_VERSION_REQUIRED(27653); \\\r\n    }\r\n\r\nnamespace MountTests {\r\n\r\n// Disks sometimes take a bit of time to become available when attached back to the host.\r\nconstexpr auto c_diskOpenTimeoutMs = 120000;\r\n\r\nclass SetAutoMountPolicy\r\n{\r\npublic:\r\n    SetAutoMountPolicy() = delete;\r\n    SetAutoMountPolicy(const SetAutoMountPolicy&) = delete;\r\n    SetAutoMountPolicy& operator=(const SetAutoMountPolicy&) = delete;\r\n\r\n    SetAutoMountPolicy(SetAutoMountPolicy&& Other) = default;\r\n    SetAutoMountPolicy& operator=(SetAutoMountPolicy&&) = default;\r\n\r\n    SetAutoMountPolicy(bool Enable) : PreviousState(GetAutoMountState())\r\n    {\r\n        if (Enable != PreviousState)\r\n        {\r\n            SetAutoMountState(Enable);\r\n        }\r\n        else\r\n        {\r\n            PreviousState.reset();\r\n        }\r\n    }\r\n\r\n    ~SetAutoMountPolicy()\r\n    {\r\n        if (PreviousState.has_value())\r\n        {\r\n            SetAutoMountState(PreviousState.value());\r\n        }\r\n    }\r\n\r\nprivate:\r\n    static bool GetAutoMountStateFromOutput(const std::wstring& Output)\r\n    {\r\n        if (Output.find(L\"Automatic mounting of new volumes enabled\") != std::wstring::npos)\r\n        {\r\n            return true;\r\n        }\r\n        else if (Output.find(L\"Automatic mounting of new volumes disabled\") != std::wstring::npos)\r\n        {\r\n            return false;\r\n        }\r\n\r\n        LogError(\"Unexpected diskpart output: '%s'\", Output.c_str());\r\n        VERIFY_FAIL(L\"Failed to parse diskpart's output\");\r\n        return false;\r\n    }\r\n\r\n    static bool GetAutoMountState()\r\n    {\r\n        std::wstring cmd = L\"diskpart.exe\";\r\n        return GetAutoMountStateFromOutput(LxsstuLaunchCommandAndCaptureOutput(cmd.data(), \"automount\\r\\n\").first);\r\n    }\r\n\r\n    static void SetAutoMountState(bool Enabled)\r\n    {\r\n        LogInfo(\"Setting automount policy to %i\", Enabled);\r\n\r\n        std::wstring cmd = L\"diskpart.exe\";\r\n        const auto input = std::string(\"automount \") + (Enabled ? \"enable\\r\\n\" : \"disable\\r\\n\");\r\n        auto [output, _] = LxsstuLaunchCommandAndCaptureOutput(cmd.data(), input.c_str());\r\n\r\n        VERIFY_ARE_EQUAL(Enabled, GetAutoMountStateFromOutput(output));\r\n    }\r\n\r\n    std::optional<bool> PreviousState;\r\n};\r\n\r\nclass MountTests\r\n{\r\n    std::wstring DiskDevice;\r\n    std::wstring VhdDevice;\r\n    wil::unique_tokeninfo_ptr<TOKEN_USER> User = wil::get_token_information<TOKEN_USER>();\r\n    std::unique_ptr<wsl::windows::common::security::privilege_context> PrivilegeState;\r\n    DWORD DiskNumber = 0;\r\n    SetAutoMountPolicy AutoMountPolicy{false};\r\n\r\n    struct ExpectedMountState\r\n    {\r\n        size_t PartitionIndex;\r\n        std::optional<std::wstring> Type;\r\n        std::optional<std::wstring> Options;\r\n    };\r\n\r\n    struct ExpectedDiskState\r\n    {\r\n        std::wstring Path;\r\n        std::vector<ExpectedMountState> Mounts;\r\n    };\r\n\r\n    WSL_TEST_CLASS(MountTests)\r\n\r\n    TEST_CLASS_SETUP(TestClassSetup)\r\n    {\r\n        VERIFY_ARE_EQUAL(LxsstuInitialize(false), TRUE);\r\n\r\n        if (!LxsstuVmMode())\r\n        {\r\n            return true;\r\n        }\r\n\r\n        // Needed to open processes under te\r\n        PrivilegeState = wsl::windows::common::security::AcquirePrivilege(SE_DEBUG_NAME);\r\n\r\n        // Create a 20MB vhd for testing mounting passthrough disks\r\n        DeleteFileW(TEST_MOUNT_DISK);\r\n\r\n        try\r\n        {\r\n            LxsstuLaunchPowershellAndCaptureOutput(L\"New-Vhd -Path \" TEST_MOUNT_DISK \" -SizeBytes 20MB\");\r\n        }\r\n        CATCH_LOG()\r\n\r\n        // Mount it in Windows\r\n        auto [output, _] = LxsstuLaunchPowershellAndCaptureOutput(L\"(Mount-VHD \" TEST_MOUNT_DISK \" -PassThru | Get-Disk).Number\");\r\n\r\n        Trim(output);\r\n        DiskNumber = std::stoul(output);\r\n\r\n        // Construct the disk path\r\n        DiskDevice = L\"\\\\\\\\.\\\\PhysicalDrive\" + output;\r\n        LogInfo(\"Mounted the passthrough test vhd as %ls\", DiskDevice.c_str());\r\n\r\n        // Create a 20MB vhd for testing mount --vhd\r\n        DeleteFileW(TEST_MOUNT_VHD);\r\n\r\n        LxsstuLaunchPowershellAndCaptureOutput(L\"New-Vhd -Path \" TEST_MOUNT_VHD \" -SizeBytes 20MB\");\r\n\r\n        VhdDevice = wsl::windows::common::filesystem::GetFullPath(TEST_MOUNT_VHD);\r\n        LogInfo(\"Create mount --vhd test vhd as %ls\", VhdDevice.c_str());\r\n\r\n        return true;\r\n    }\r\n\r\n    // Uninitialize the tests.\r\n    TEST_CLASS_CLEANUP(TestClassCleanup)\r\n    {\r\n        if (LxsstuVmMode())\r\n        {\r\n            PrivilegeState.reset();\r\n\r\n            LxsstuLaunchWsl(L\"--unmount\");\r\n            WaitForDiskReady();\r\n\r\n            try\r\n            {\r\n                LxsstuLaunchPowershellAndCaptureOutput(L\"Dismount-Vhd -Path \" TEST_MOUNT_DISK);\r\n            }\r\n            CATCH_LOG()\r\n\r\n            DeleteFileW(TEST_MOUNT_DISK);\r\n            DeleteFileW(TEST_MOUNT_VHD);\r\n        }\r\n\r\n        VERIFY_NO_THROW(LxsstuUninitialize(false));\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD_CLEANUP(MethodCleanup)\r\n    {\r\n        if (!LxsstuVmMode())\r\n        {\r\n            return true;\r\n        }\r\n\r\n        LxssLogKernelOutput();\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount\"), (DWORD)0);\r\n        WaitForDiskReady();\r\n\r\n        return true;\r\n    }\r\n\r\n    // Attach a vhd, but don't mount it\r\n    TEST_METHOD(TestBareMountVhd)\r\n    {\r\n        TestBareMountImpl(true);\r\n    }\r\n\r\n    // Mount one partition using --vhd and validate that options are correctly applied\r\n    TEST_METHOD(TestMountOnePartitionVhd)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountOnePartitionImpl(true);\r\n    }\r\n\r\n    // Mount two partitions using --vhd on the same disk\r\n    TEST_METHOD(TestMountTwoPartitionsVhd)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountTwoPartitionsImpl(true);\r\n    }\r\n\r\n    // Run a bare mount using --vhd and then mount a partition\r\n    TEST_METHOD(TestAttachThenMountVhd)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestAttachThenMountImpl(true);\r\n    }\r\n\r\n    // Mount the disk directly\r\n    TEST_METHOD(TestMountWholeDiskVhd)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountWholeDiskImpl(true);\r\n    }\r\n\r\n    // Test that mount state is deleted on shutdown (--vhd)\r\n    TEST_METHOD(TestMountStateIsDeletedOnShutdownVhd)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountStateIsDeletedOnShutdownImpl(true);\r\n    }\r\n\r\n    TEST_METHOD(TestFilesystemDetectionWholeDisk)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestFilesystemDetectionWholeDiskImpl(false);\r\n    }\r\n\r\n    TEST_METHOD(TestFilesystemDetectionWholeDiskVhd)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestFilesystemDetectionWholeDiskImpl(true);\r\n    }\r\n\r\n    TEST_METHOD(TestMountTwoPartitionsWithDetection)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountTwoPartitionsWithDetectionImpl(false);\r\n    }\r\n\r\n    TEST_METHOD(TestMountTwoPartitionsWithDetectionVhd)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountTwoPartitionsWithDetectionImpl(true);\r\n    }\r\n\r\n    TEST_METHOD(TestFilesystemDetectionFail)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestFilesystemDetectionFailImpl(false);\r\n    }\r\n\r\n    TEST_METHOD(TestFilesystemDetectionFailVhd)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestFilesystemDetectionFailImpl(true);\r\n    }\r\n\r\n    // Test specifying a mount name for a vhd\r\n    TEST_METHOD(SpecifyMountName)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto mountCommand = L\"--mount \" + VhdDevice + L\" --vhd --name \" + TEST_MOUNT_NAME;\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition\r\n        FormatDisk({L\"ext4\"}, true);\r\n\r\n        // Mount it\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 1\"), (DWORD)0);\r\n        auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        const std::wstring diskName(TEST_MOUNT_NAME);\r\n        auto mountTarget = L\"/mnt/wsl/\" + diskName;\r\n\r\n        ValidateMountPoint(disk + L\"1\", mountTarget);\r\n\r\n        ValidateDiskState({VhdDevice, {{1, {}, {}}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + VhdDevice), (DWORD)0);\r\n        WaitForDiskReady();\r\n\r\n        // Validate that the mount folder was deleted\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -e \" + mountTarget), (DWORD)1);\r\n\r\n        // Mount the same partition, but with a specific mount option\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 1 --options \\\"data=ordered\\\"\"), (DWORD)0);\r\n\r\n        // Validate that the mount option was properly passed\r\n        disk = GetBlockDeviceInWsl();\r\n        ValidateMountPoint(disk + L\"1\", mountTarget, L\"data=ordered\");\r\n        ValidateDiskState({VhdDevice, {{1, {}, L\"data=ordered\"}}}, keepAlive);\r\n\r\n        // Let the VM timeout\r\n        WaitForVmTimeout(keepAlive);\r\n\r\n        // Validate that the disk is re-mounted in the same place\r\n        disk = GetBlockDeviceInWsl();\r\n        ValidateMountPoint(disk + L\"1\", mountTarget);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + VhdDevice), (DWORD)0);\r\n        WaitForDiskReady();\r\n    }\r\n\r\n    // Test ensuring that name collision detection works in --mount --name\r\n    TEST_METHOD(SpecifyMountNameCollision)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto mountCommand = L\"--mount \" + VhdDevice + L\" --vhd --name \" + TEST_MOUNT_NAME;\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition and one fat partitions\r\n        FormatDisk({L\"ext4\", L\"vfat\"}, true);\r\n\r\n        // Attempt to mount both partitions with the same mount name; partition 2 should fail\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 1\"), (DWORD)0);\r\n        VERIFY_ARE_NOT_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 2 --type vfat\"), (DWORD)0);\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount first mount did succeed\r\n        const std::wstring diskName(TEST_MOUNT_NAME);\r\n        ValidateMountPoint(disk + L\"1\", L\"/mnt/wsl/\" + diskName, {}, L\"ext4\");\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + VhdDevice), (DWORD)0);\r\n        WaitForDiskReady();\r\n    }\r\n\r\n    // Test that multiple partitions can be mounted with --name\r\n    TEST_METHOD(SpecifyMountNameTwoPartitions)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto mountCommandOne = L\"--mount \" + VhdDevice + L\" --vhd --name \" + TEST_MOUNT_NAME + L\"p1\";\r\n        const auto mountCommandTwo = L\"--mount \" + VhdDevice + L\" --vhd --name \" + TEST_MOUNT_NAME + L\"p2\";\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition and one fat partitions\r\n        FormatDisk({L\"ext4\", L\"vfat\"}, true);\r\n\r\n        // Attempt to mount both partitions with the same mount name; partition 2 should fail\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommandOne + L\" --partition 1\"), (DWORD)0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommandTwo + L\" --partition 2 --type vfat\"), (DWORD)0);\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount first mount did succeed\r\n        const std::wstring diskName(TEST_MOUNT_NAME);\r\n        ValidateMountPoint(disk + L\"1\", L\"/mnt/wsl/\" + diskName + L\"p1\", {}, L\"ext4\");\r\n        ValidateMountPoint(disk + L\"2\", L\"/mnt/wsl/\" + diskName + L\"p2\", {}, L\"vfat\");\r\n        ValidateDiskState({VhdDevice, {{1, {}, {}}, {2, {L\"vfat\"}, {}}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + VhdDevice), (DWORD)0);\r\n        WaitForDiskReady();\r\n    }\r\n\r\n    // Test relative mount/unmounting of a --vhd\r\n    TEST_METHOD(RelativePathUnmount)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" TEST_MOUNT_VHD L\" --vhd --bare\"), (DWORD)0);\r\n\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" TEST_MOUNT_VHD), (DWORD)0);\r\n    }\r\n\r\n    // Test relative mount/unmounting of a --vhd that does not exist\r\n    TEST_METHOD(RelativePathUnmountNoFileExists)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" TEST_MOUNT_VHD L\" --vhd --bare\"), (DWORD)0);\r\n\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Try unmounting a VHD not created and verify that it was not successful\r\n        VERIFY_ARE_NOT_EQUAL(LxsstuLaunchWsl(L\"--unmount \" TEST_UNMOUNT_VHD_DNE), (DWORD)0);\r\n    }\r\n\r\n    TEST_METHOD(AbsolutePathVhdUnmount)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" TEST_MOUNT_VHD L\" --vhd --bare\"), (DWORD)0);\r\n\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        const auto absolutePath = std::filesystem::absolute(TEST_MOUNT_VHD);\r\n\r\n        // Validate that the vhd path doesn't start with '\\\\?'\r\n        VERIFY_IS_FALSE(absolutePath.wstring().starts_with(L\"\\\\\"));\r\n\r\n        // Validate the unmounting by absolute path is successful\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + absolutePath.wstring()), (DWORD)0);\r\n    }\r\n\r\n    // Attach a disk, but don't mount it\r\n    TEST_METHOD(TestBareMount)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestBareMountImpl(false);\r\n    }\r\n\r\n    // Validate that attached disks that were offline when attached\r\n    // are still offline when detached\r\n    TEST_METHOD(TestOfflineDiskStaysOffline)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        auto diskHandle = wsl::windows::common::disk::OpenDevice(DiskDevice.c_str(), GENERIC_ALL, c_diskOpenTimeoutMs);\r\n        wsl::windows::common::disk::SetOnline(diskHandle.get(), false);\r\n        diskHandle.reset();\r\n\r\n        ValidateOffline(true);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --bare\"), (DWORD)0);\r\n\r\n        auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        ValidateDiskState({DiskDevice, {}}, keepAlive);\r\n\r\n        disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_FALSE(GetBlockDeviceMount(disk).has_value());\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + DiskDevice), (DWORD)0);\r\n\r\n        ValidateOffline(true);\r\n        diskHandle = wsl::windows::common::disk::OpenDevice(DiskDevice.c_str(), GENERIC_ALL, c_diskOpenTimeoutMs);\r\n        wsl::windows::common::disk::SetOnline(diskHandle.get(), true);\r\n        diskHandle.reset();\r\n\r\n        ValidateOffline(false);\r\n    }\r\n\r\n    // Mount one partition and validate that options are correctly applied\r\n    TEST_METHOD(TestMountOnePartition)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountOnePartitionImpl(false);\r\n    }\r\n\r\n    // Mount two partitions on the same disk\r\n    TEST_METHOD(TestMountTwoPartitions)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountTwoPartitionsImpl(false);\r\n    }\r\n\r\n    // Mount a fat partition\r\n    TEST_METHOD(TestMountFatPartition)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ntfs partition\r\n        FormatDisk({L\"vfat\"}, false);\r\n\r\n        // Mount it\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 1\" + L\" --type vfat\"), (DWORD)0);\r\n\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(DiskDevice);\r\n        Trim(trimmedDiskName);\r\n        auto mountTarget = L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\";\r\n        ValidateMountPoint(disk + L\"1\", mountTarget, {}, L\"vfat\");\r\n        ValidateDiskState({DiskDevice, {{1, {L\"vfat\"}, {}}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + DiskDevice), (DWORD)0);\r\n        WaitForDiskReady();\r\n        ValidateOffline(false);\r\n    }\r\n\r\n    // Mount the disk directly\r\n    TEST_METHOD(TestMountWholeDisk)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountWholeDiskImpl(false);\r\n    }\r\n\r\n    TEST_METHOD(TestMountStateIsDeletedOnShutdown)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestMountStateIsDeletedOnShutdownImpl(false);\r\n    }\r\n\r\n    // Validate that a failure to mount a disk isn't fatal\r\n    TEST_METHOD(TestMountFailuresArentFatal)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition\r\n        FormatDisk({L\"ext4\"}, false);\r\n\r\n        // Mount it\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 1 --type ext4\"), (DWORD)0);\r\n        auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        ValidateDiskState({DiskDevice, {{1, {L\"ext4\"}, {}}}}, keepAlive);\r\n\r\n        // Check that the disk is still mounted properly (ValidateDiskState restarts the VM)\r\n        disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n        std::wstring trimmedDiskName(DiskDevice);\r\n        Trim(trimmedDiskName);\r\n        ValidateMountPoint(disk + L\"1\", L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\", {}, L\"ext4\");\r\n\r\n        // Wait for vm timeout\r\n        WaitForVmTimeout(keepAlive);\r\n\r\n        // Voluntarily set a wrong filesystem in the saved state\r\n        auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);\r\n        auto subKeys = wsl::windows::common::registry::EnumKeys(key.get(), KEY_ALL_ACCESS);\r\n        VERIFY_ARE_EQUAL(subKeys.size(), 1);\r\n\r\n        wsl::windows::common::registry::WriteString(subKeys.begin()->second.get(), L\"1\", L\"Type\", L\"badfs\");\r\n        keepAlive.Set();\r\n\r\n        // The disk should be present\r\n        disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // But not mounted\r\n        ValidateMountPoint(disk + L\"1\", {});\r\n\r\n        // Now put a bad disk path, so that the disk fails to attach\r\n        WaitForVmTimeout(keepAlive);\r\n        key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);\r\n        subKeys = wsl::windows::common::registry::EnumKeys(key.get(), KEY_ALL_ACCESS);\r\n        VERIFY_ARE_EQUAL(subKeys.size(), 1);\r\n        wsl::windows::common::registry::WriteString(subKeys.begin()->second.get(), nullptr, L\"Disk\", L\"BadDisk\");\r\n        keepAlive.Reset();\r\n\r\n        // Restart the service\r\n        RestartWslService();\r\n\r\n        // Run a dummy command to trigger a VM start\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"echo foo\"), (DWORD)0);\r\n\r\n        // The disk should still be online, because it failed to attach\r\n        ValidateOffline(false);\r\n    }\r\n\r\n    // wsl --unmount should succeed even when no disk is mounted\r\n    TEST_METHOD(UnmountWithoutAnyDisk)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount\"), (DWORD)0);\r\n    }\r\n\r\n    // Mount two partitions on the same disk and validate that the mount is restored\r\n    TEST_METHOD(TestMountTwoPartitionsAfterTimeout)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition and one fat partitions\r\n        FormatDisk({L\"ext4\", L\"vfat\"}, false);\r\n\r\n        // Mount then both\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 1\"), (DWORD)0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 2 --type vfat\"), (DWORD)0);\r\n\r\n        ValidateDiskState({DiskDevice, {{1, {}, {}}, {2, {L\"vfat\"}, {}}}}, keepAlive);\r\n\r\n        // Validate that our disk is still mounted\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(DiskDevice);\r\n        Trim(trimmedDiskName);\r\n\r\n        ValidateMountPoint(disk + L\"1\", L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\", {}, L\"ext4\");\r\n        ValidateMountPoint(disk + L\"2\", L\"/mnt/wsl/\" + trimmedDiskName + L\"p2\", {}, L\"vfat\");\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + DiskDevice), (DWORD)0);\r\n    }\r\n\r\n    // Validate that non-admin can remount saved disks\r\n    TEST_METHOD(TestMount1PartitionAndRemountAsNonAdmin)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        FormatDisk({L\"ext4\"}, false);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 1\"), (DWORD)0);\r\n\r\n        ValidateDiskState({DiskDevice, {{1, {}, {}}}}, keepAlive);\r\n        auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Let the UVM timeout\r\n        WaitForVmTimeout(keepAlive);\r\n\r\n        // Restart wsl as a non-elevated user\r\n        const auto nonElevatedToken = GetNonElevatedToken();\r\n\r\n        // Launch wsl non-elevated\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"echo dummy\", nullptr, nullptr, nullptr, nonElevatedToken.get()), (DWORD)0);\r\n        keepAlive.Set();\r\n\r\n        // Validate that our disk is still attached\r\n        disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(DiskDevice);\r\n        Trim(trimmedDiskName);\r\n\r\n        ValidateMountPoint(disk + L\"1\", L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\", {}, L\"ext4\");\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + DiskDevice), (DWORD)0);\r\n    }\r\n\r\n    // Run a bare mount and then mount a partition\r\n    TEST_METHOD(TestAttachThenMount)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n\r\n        TestAttachThenMountImpl(false);\r\n    }\r\n\r\n    // Validate that unmounting works when the UVM is not running\r\n    TEST_METHOD(TestMountOnePartitionAfterTimeout)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition\r\n        FormatDisk({L\"ext4\"}, false);\r\n\r\n        // Mount it\r\n        ValidateOffline(false);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 1\"), (DWORD)0);\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n        ValidateOffline(true);\r\n\r\n        // Wait for vm timeout\r\n        WaitForVmTimeout(keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + DiskDevice), (DWORD)0);\r\n\r\n        // The UVM shouldn't be running\r\n        VERIFY_IS_FALSE(GetVmmempPid().has_value());\r\n\r\n        // No state should be left in registry\r\n        const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);\r\n        VERIFY_ARE_EQUAL(wsl::windows::common::registry::EnumKeys(key.get(), KEY_READ).size(), 0);\r\n    }\r\n\r\n    // Validate that the proper mount error is returned if the filesystem type is wrong\r\n    TEST_METHOD(TestMountPartitionWithWrongFs)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition\r\n        FormatDisk({L\"ext4\"}, false);\r\n\r\n        // Mount it\r\n        wsl::windows::common::SvcComm service;\r\n        VERIFY_ARE_EQUAL(service.AttachDisk(DiskDevice.c_str(), LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH), S_OK);\r\n\r\n        const auto result = service.MountDisk(DiskDevice.c_str(), LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH, 1, nullptr, L\"vfat\", nullptr);\r\n\r\n        VERIFY_ARE_EQUAL(result.Result, -22); //-EINVAL\r\n        VERIFY_ARE_EQUAL(result.Step, 3);     // LxMiniInitMountStepMount\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + DiskDevice), (DWORD)0);\r\n    }\r\n\r\n    // Validate that the proper mount error is returned if the partition can't be found\r\n    TEST_METHOD(TestMountPartitionWithBadPartitionIndex)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 fat partition\r\n        FormatDisk({L\"vfat\"}, false);\r\n\r\n        // Try to mount a partition that doesn't exist\r\n        wsl::windows::common::SvcComm service;\r\n        VERIFY_ARE_EQUAL(service.AttachDisk(DiskDevice.c_str(), LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH), S_OK);\r\n\r\n        const auto result = service.MountDisk(DiskDevice.c_str(), LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH, 2, nullptr, L\"vfat\", nullptr);\r\n\r\n        VERIFY_ARE_EQUAL(result.Result, -2); // -ENOENT\r\n        VERIFY_ARE_EQUAL(result.Step, 2);    // LxMiniInitMountStepFindPartition\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + DiskDevice), (DWORD)0);\r\n    }\r\n\r\n    // Validate that disk aren't detached if in use by other processes\r\n    TEST_METHOD(TestDeviceCantBeMountedIfInUse)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        {\r\n            // Format-Volume fails without automount enabled\r\n            SetAutoMountPolicy AutoMountPolicy{true};\r\n\r\n            // Reset the disk\r\n            LxsstuLaunchPowershellAndCaptureOutput(L\"Clear-Disk -confirm:$false -RemoveData -Number \" + std::to_wstring(DiskNumber));\r\n\r\n            LxsstuLaunchPowershellAndCaptureOutput(L\"Initialize-Disk -confirm:$false -Number \" + std::to_wstring(DiskNumber));\r\n\r\n            // Create one fat partition\r\n            LxsstuLaunchPowershellAndCaptureOutput(\r\n                L\"New-Partition -DiskNumber \" + std::to_wstring(DiskNumber) +\r\n                L\" -UseMaximumSize \\\r\n                | Format-Volume -FileSystem FAT\");\r\n        }\r\n\r\n        // Mount it in Windows\r\n        auto [letter, _] = LxsstuLaunchPowershellAndCaptureOutput(\r\n            L\"Set-Partition  -DiskNumber \" + std::to_wstring(DiskNumber) + L\" -PartitionNumber 1\" + L\" -NewDriveLetter Y\");\r\n\r\n        // Open a file under that partition\r\n        wil::unique_handle file(CreateFile(L\"Y:\\\\foo.txt\", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr));\r\n\r\n        const char* fileContent = \"LOW!\";\r\n        THROW_LAST_ERROR_IF(!WriteFile(file.get(), fileContent, static_cast<DWORD>(strlen(fileContent)), nullptr, nullptr));\r\n\r\n        // Validate that the disk can't be mounted (TODO: Find a way to validate the failure reason)\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 1 --type vfat\"), (DWORD)-1);\r\n\r\n        // Close the file and mount it\r\n        file.reset();\r\n        WaitForDiskReady();\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 1 --type vfat\"), (DWORD)0);\r\n\r\n        // Validate that the file content is correct\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(DiskDevice);\r\n        Trim(trimmedDiskName);\r\n\r\n        ValidateMountPoint(disk + L\"1\", {L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\"}, {}, L\"vfat\");\r\n        auto [output, __] = LxsstuLaunchWslAndCaptureOutput(L\"cat /mnt/wsl/\" + trimmedDiskName + L\"p1/foo.txt\");\r\n\r\n        VERIFY_ARE_EQUAL(output, wsl::shared::string::MultiByteToWide(fileContent));\r\n    }\r\n\r\n    TEST_METHOD(TestMountWithFlagOption)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition\r\n        FormatDisk({L\"ext4\"}, false);\r\n\r\n        // Mount it\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 1 --options sync\"), (DWORD)0);\r\n        auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(DiskDevice);\r\n        Trim(trimmedDiskName);\r\n        auto mountTarget = L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\";\r\n\r\n        ValidateMountPoint(disk + L\"1\", mountTarget, L\"sync\");\r\n        ValidateDiskState({DiskDevice, {{1, {}, L\"sync\"}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + DiskDevice), (DWORD)0);\r\n        WaitForDiskReady();\r\n\r\n        // Mount the same partition, but with both a flag and a non-flag option\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + DiskDevice + L\" --partition 1 --options data=ordered,sync\"), (DWORD)0);\r\n\r\n        // Validate that the mount option was properly passed\r\n        disk = GetBlockDeviceInWsl();\r\n\r\n        ValidateMountPoint(disk + L\"1\", mountTarget, L\"ync,relatime,data=ordered\");\r\n\r\n        // Note: relatime is set by default\r\n        ValidateDiskState({DiskDevice, {{1, {}, L\"data=ordered,sync\"}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + DiskDevice), (DWORD)0);\r\n        WaitForDiskReady();\r\n    }\r\n\r\n    TEST_METHOD(TestAttachFailsWithoutWsl2Distro)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL1_TEST_ONLY();\r\n\r\n        // Attempt to mount a disk with only a WSL1 distro\r\n        wsl::windows::common::SvcComm service;\r\n        VERIFY_ARE_EQUAL(service.AttachDisk(L\"Dummy\", LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH), WSL_E_WSL2_NEEDED);\r\n    }\r\n\r\n    TEST_METHOD(VhdWithSpaces)\r\n    {\r\n        SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();\r\n        WSL2_TEST_ONLY();\r\n\r\n        LxsstuLaunchPowershellAndCaptureOutput(L\"New-Vhd -Path 'vhd with spaces.vhdx' -SizeBytes 20MB\");\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n            WslShutdown();\r\n\r\n            if (!DeleteFile(L\"vhd with spaces.vhdx\"))\r\n            {\r\n                LogInfo(\"Failed to delete vhd, %i\", GetLastError());\r\n            };\r\n        });\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Validate that relative path mounting and unmounting works\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \\\"vhd with spaces.vhdx\\\" --bare --vhd\"), (DWORD)0);\r\n        auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \\\"vhd with spaces.vhdx\\\"\"), (DWORD)0);\r\n\r\n        // Validate that absolute path mounting and unmounting works\r\n        const std::wstring fullPath = wsl::windows::common::filesystem::GetFullPath(L\"vhd with spaces.vhdx\");\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \\\"\" + fullPath + L\"\\\" --bare --vhd\"), (DWORD)0);\r\n        disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \\\"\" + fullPath + L\"\\\"\"), (DWORD)0);\r\n    }\r\n\r\n    void WaitForDiskReady() const\r\n    {\r\n        const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(30);\r\n        while (timeout > std::chrono::steady_clock::now())\r\n        {\r\n            try\r\n            {\r\n                auto disk = wsl::windows::common::disk::OpenDevice(DiskDevice.c_str(), GENERIC_READ, c_diskOpenTimeoutMs);\r\n                wsl::windows::common::disk::ValidateDiskVolumesAreReady(disk.get());\r\n                return;\r\n            }\r\n            catch (...)\r\n            {\r\n                auto error = std::system_category().message(wil::ResultFromCaughtException());\r\n                LogInfo(\"Caught '%S' while waiting for disk\", error.c_str());\r\n                std::this_thread::sleep_for(std::chrono::seconds(1));\r\n                continue;\r\n            }\r\n        }\r\n\r\n        VERIFY_FAIL(L\"Timeout waiting for disk\");\r\n    }\r\n\r\n    void ValidateOffline(bool offline) const\r\n    {\r\n        const auto disk = wsl::windows::common::disk::OpenDevice(DiskDevice.c_str(), FILE_READ_ATTRIBUTES, c_diskOpenTimeoutMs);\r\n        VERIFY_ARE_EQUAL(!offline, wsl::windows::common::disk::IsDiskOnline(disk.get()));\r\n    }\r\n\r\n    static std::wstring GetBlockDeviceInWsl()\r\n    {\r\n        // Wait for the disk to be attached\r\n        const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(30);\r\n\r\n        bool done = false;\r\n        while (true)\r\n        {\r\n            for (wchar_t name = 'a'; name < 'z'; name++)\r\n            {\r\n                std::wstring cmd = L\"-u root blockdev --getsize64 /dev/sd\";\r\n                cmd += name;\r\n\r\n                std::wstring out;\r\n                try\r\n                {\r\n                    out = LxsstuLaunchWslAndCaptureOutput(cmd.data()).first;\r\n                }\r\n                CATCH_LOG()\r\n\r\n                Trim(out);\r\n\r\n                // Disk size is 20MB, so 20 * 1024 * 1024 bytes\r\n                if (out == L\"20971520\")\r\n                {\r\n                    return std::wstring(L\"/dev/sd\") + name;\r\n                }\r\n            }\r\n\r\n            if (done)\r\n            {\r\n                break;\r\n            }\r\n\r\n            done = std::chrono::steady_clock::now() > timeout;\r\n        }\r\n\r\n        VERIFY_FAIL(L\"Failed to find the block device in WSL\");\r\n\r\n        // Unreachable.\r\n        return {};\r\n    }\r\n\r\n    static bool IsBlockDevicePresent(const std::wstring& Device)\r\n    {\r\n        const auto Cmd = L\"test -e \" + Device;\r\n        return LxsstuLaunchWsl(Cmd.data()) == 0;\r\n    }\r\n\r\n    static std::optional<std::vector<std::wstring>> GetBlockDeviceMount(const std::wstring& device)\r\n    {\r\n        const std::wstring cmd(L\"cat /proc/mounts\");\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(cmd.data());\r\n\r\n        LogInfo(\"/proc/mounts content: '%ls'\", out.c_str());\r\n        std::wistringstream output(out);\r\n        std::wstring line;\r\n\r\n        while (std::getline(output, line))\r\n        {\r\n            if (wcsstr(line.data(), device.data()) == line.data())\r\n            {\r\n                return LxssSplitString(line);\r\n            }\r\n        }\r\n\r\n        return {};\r\n    }\r\n\r\n    void ValidateDiskState(const ExpectedDiskState& State, WslKeepAlive& KeepAlive)\r\n    {\r\n        WaitForVmTimeout(KeepAlive);\r\n        const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);\r\n        const auto subKeys = wsl::windows::common::registry::EnumKeys(key.get(), KEY_READ);\r\n\r\n        VERIFY_ARE_EQUAL(subKeys.size(), 1);\r\n\r\n        const auto& diskKey = subKeys.begin()->second;\r\n\r\n        auto read = [](const auto& Key, LPCWSTR Name) -> std::optional<std::wstring> {\r\n            try\r\n            {\r\n                return wsl::windows::common::registry::ReadString(Key.get(), nullptr, Name);\r\n            }\r\n            catch (...)\r\n            {\r\n                return {};\r\n            }\r\n        };\r\n\r\n        VERIFY_ARE_EQUAL(read(diskKey, L\"Disk\").value(), State.Path);\r\n        VERIFY_ARE_EQUAL(wsl::windows::common::registry::EnumKeys(diskKey.get(), KEY_READ).size(), State.Mounts.size());\r\n\r\n        for (const auto& e : State.Mounts)\r\n        {\r\n            auto keyName = std::to_wstring(e.PartitionIndex);\r\n\r\n            auto mountKey = wsl::windows::common::registry::OpenKey(diskKey.get(), keyName.c_str(), KEY_READ);\r\n\r\n            VERIFY_ARE_EQUAL(read(mountKey, L\"Options\"), e.Options);\r\n            VERIFY_ARE_EQUAL(read(mountKey, L\"Type\"), e.Type);\r\n        }\r\n\r\n        KeepAlive.Set();\r\n    }\r\n\r\n    void WaitForVmTimeout(WslKeepAlive& KeepAlive)\r\n    {\r\n        const auto pid = GetVmmempPid();\r\n        VERIFY_IS_TRUE(pid.has_value());\r\n        KeepAlive.Reset();\r\n        const std::wstring cmd = std::wstring(L\"-t \") + std::wstring(LXSS_DISTRO_NAME_TEST_L);\r\n\r\n        // Terminate the distro to make the vm timeout faster\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(cmd.c_str()), (DWORD)0);\r\n\r\n        const wil::unique_process_handle process(OpenProcess(SYNCHRONIZE, false, pid.value()));\r\n        VERIFY_IS_NOT_NULL(process.get());\r\n\r\n        VERIFY_ARE_EQUAL((DWORD)WAIT_OBJECT_0, WaitForSingleObject(process.get(), INFINITE));\r\n    }\r\n\r\n    static std::optional<DWORD> GetVmmempPid()\r\n    {\r\n        for (auto pid : wsl::windows::common::wslutil::ListRunningProcesses())\r\n        {\r\n            wil::unique_process_handle process(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid));\r\n            if (!process)\r\n            {\r\n                continue;\r\n            }\r\n\r\n            std::wstring imageName(MAX_PATH, '\\0');\r\n            const DWORD length = GetProcessImageFileName(process.get(), imageName.data(), (DWORD)imageName.size() + 1);\r\n            if (length == 0)\r\n            {\r\n                continue;\r\n            }\r\n\r\n            imageName.resize(length);\r\n            if (imageName == L\"vmmemWSL\" || (!wsl::windows::common::helpers::IsWindows11OrAbove() && imageName == L\"vmmem\"))\r\n            {\r\n                return pid;\r\n            }\r\n        }\r\n\r\n        return {}; // Unreachable\r\n    }\r\n\r\n    void FormatDisk(const std::vector<std::wstring>& Partitions, bool isVhdTest)\r\n    {\r\n        WaitForDiskReady();\r\n        const auto deviceName = (isVhdTest) ? VhdDevice : DiskDevice;\r\n        if (isVhdTest)\r\n        {\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + deviceName + L\" --vhd --bare\"), (DWORD)0);\r\n        }\r\n        else\r\n        {\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--mount \" + deviceName + L\" --bare\"), (DWORD)0);\r\n        }\r\n\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Create a partition table\r\n        std::wstringstream Cmd;\r\n        Cmd << \"bash -c \\\"(\";\r\n        Cmd << L\"echo -e o\\n\"; // Create a new partition table\r\n\r\n        for (size_t i = 0; i < Partitions.size(); i++)\r\n        {\r\n            Cmd << L\"echo -e n\\n\";                             // Add a new partition\r\n            Cmd << L\"echo -e p\\n\";                             // Primary partition\r\n            Cmd << L\"echo -e \" << (i + 1) << L\"\\n\";            // Partition number\r\n            Cmd << L\"echo -e\\n\";                               // First sector (Accept default)\r\n            Cmd << L\"echo \" << 2049 + (i + 1) * 4096 << L\"\\n\"; // Last sector\r\n        }\r\n\r\n        Cmd << L\"echo -e w\\n\"; // Write changes\r\n        Cmd << L\") | fdisk \" + disk + L\"\\\"\";\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(Cmd.str()), (DWORD)0);\r\n\r\n        for (size_t i = 1; i <= Partitions.size(); i++)\r\n        {\r\n            auto partition = disk + std::to_wstring(i);\r\n\r\n            // mkfs.ext4 interactively asks for confirmation, -F disables that behavior\r\n            const auto forceFlag = Partitions[i - 1] == L\"ext4\" ? L\" -F \" : L\"\";\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"mkfs.\" + Partitions[i - 1] + forceFlag + L\" \" + partition), (DWORD)0);\r\n        }\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n\r\n        if (!isVhdTest)\r\n        {\r\n            WaitForDiskReady();\r\n        }\r\n    }\r\n\r\n    void ValidateMountPoint(\r\n        const std::wstring& BlockDevice,\r\n        const std::optional<std::wstring>& Mountpoint,\r\n        const std::optional<std::wstring>& ExpectedOption = {},\r\n        const std::optional<std::wstring>& ExpectedType = {})\r\n    {\r\n        auto mount = GetBlockDeviceMount(BlockDevice);\r\n        if (Mountpoint.has_value())\r\n        {\r\n            VERIFY_IS_TRUE(mount.has_value());\r\n        }\r\n        else\r\n        {\r\n            VERIFY_IS_FALSE(mount.has_value());\r\n            return;\r\n        }\r\n\r\n        VERIFY_ARE_EQUAL(mount.value()[1], Mountpoint.value());\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -d \" + Mountpoint.value()), (DWORD)0);\r\n\r\n        // If specified, validate that ExpectedOption is in the mount options\r\n        // (We don't want to do a direct compare because the kernel might add some like rw, ...)\r\n        if (ExpectedOption.has_value())\r\n        {\r\n            VERIFY_ARE_NOT_EQUAL(mount.value()[3].find(ExpectedOption.value()), std::string::npos);\r\n        }\r\n\r\n        // If specified, validate the filesystem\r\n        if (ExpectedType.has_value())\r\n        {\r\n            VERIFY_ARE_EQUAL(mount.value()[2], ExpectedType.value());\r\n        }\r\n    }\r\n\r\n    void TestBareMountImpl(bool isVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;\r\n        const auto mountCommand = (isVhd) ? (L\"--mount \" + deviceName + L\" --vhd\") : (L\"--mount \" + deviceName);\r\n\r\n        if (isVhd)\r\n        {\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --bare\"), (DWORD)0);\r\n        }\r\n        else\r\n        {\r\n            ValidateOffline(false);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --bare\"), (DWORD)0);\r\n            ValidateOffline(true);\r\n        }\r\n\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        VERIFY_IS_FALSE(GetBlockDeviceMount(disk).has_value());\r\n\r\n        ValidateDiskState({deviceName, {}}, keepAlive);\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n\r\n        if (!isVhd)\r\n        {\r\n            ValidateOffline(false);\r\n        }\r\n    }\r\n\r\n    void TestMountOnePartitionImpl(bool isVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;\r\n        const auto mountCommand = (isVhd) ? (L\"--mount \" + deviceName + L\" --vhd\") : (L\"--mount \" + deviceName);\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition\r\n        FormatDisk({L\"ext4\"}, isVhd);\r\n\r\n        // Mount it\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 1\"), (DWORD)0);\r\n        auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(deviceName);\r\n        Trim(trimmedDiskName);\r\n        auto mountTarget = L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\";\r\n\r\n        ValidateMountPoint(disk + L\"1\", mountTarget);\r\n\r\n        ValidateDiskState({deviceName, {{1, {}, {}}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n        WaitForDiskReady();\r\n\r\n        if (!isVhd)\r\n        {\r\n            ValidateOffline(false);\r\n        }\r\n\r\n        // Validate that the mount folder was deleted\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -e \" + mountTarget), (DWORD)1);\r\n\r\n        // Mount the same partition, but with a specific mount option\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 1 --options \\\"data=ordered\\\"\"), (DWORD)0);\r\n\r\n        // Validate that the mount option was properly passed\r\n        disk = GetBlockDeviceInWsl();\r\n        ValidateMountPoint(disk + L\"1\", mountTarget, L\"data=ordered\");\r\n        ValidateDiskState({deviceName, {{1, {}, L\"data=ordered\"}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n        WaitForDiskReady();\r\n\r\n        if (!isVhd)\r\n        {\r\n            ValidateOffline(false);\r\n        }\r\n    }\r\n\r\n    void TestMountTwoPartitionsImpl(bool isVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;\r\n        const auto mountCommand = (isVhd) ? (L\"--mount \" + deviceName + L\" --vhd\") : (L\"--mount \" + deviceName);\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition and one fat partitions\r\n        FormatDisk({L\"ext4\", L\"vfat\"}, isVhd);\r\n\r\n        // Mount then both\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 1\"), (DWORD)0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 2 --type vfat\"), (DWORD)0);\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(deviceName);\r\n        Trim(trimmedDiskName);\r\n\r\n        ValidateMountPoint(disk + L\"1\", L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\", {}, L\"ext4\");\r\n        ValidateMountPoint(disk + L\"2\", L\"/mnt/wsl/\" + trimmedDiskName + L\"p2\", {}, L\"vfat\");\r\n        ValidateDiskState({deviceName, {{1, {}, {}}, {2, {L\"vfat\"}, {}}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n        WaitForDiskReady();\r\n\r\n        if (!isVhd)\r\n        {\r\n            ValidateOffline(false);\r\n        }\r\n    }\r\n\r\n    void TestAttachThenMountImpl(bool isVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;\r\n        const auto mountCommand = (isVhd) ? (L\"--mount \" + deviceName + L\" --vhd\") : (L\"--mount \" + deviceName);\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        FormatDisk({L\"ext4\"}, isVhd);\r\n\r\n        // Mount then both\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --bare\"), (DWORD)0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 1\"), (DWORD)0);\r\n\r\n        ValidateDiskState({deviceName, {{1, {}, {}}}}, keepAlive);\r\n\r\n        // Validate that our disk is still mounted\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(deviceName);\r\n        Trim(trimmedDiskName);\r\n\r\n        ValidateMountPoint(disk + L\"1\", L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\", {}, {});\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n    }\r\n\r\n    void TestMountWholeDiskImpl(bool isVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;\r\n        const auto mountCommand = (isVhd) ? (L\"--mount \" + deviceName + L\" --vhd\") : (L\"--mount \" + deviceName);\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Format the volume as ext4\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --bare\"), (DWORD)0);\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"mkfs.ext4 -F \" + disk), (DWORD)0);\r\n\r\n        // Then mount it\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --type ext4\"), (DWORD)0);\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(deviceName);\r\n        Trim(trimmedDiskName);\r\n        auto mountTarget = L\"/mnt/wsl/\" + trimmedDiskName;\r\n        ValidateMountPoint(disk, mountTarget, {}, L\"ext4\");\r\n        ValidateDiskState({deviceName, {{0, {L\"ext4\"}, {}}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n\r\n        if (!isVhd)\r\n        {\r\n            WaitForDiskReady();\r\n            ValidateOffline(false);\r\n        }\r\n    }\r\n\r\n    void TestMountStateIsDeletedOnShutdownImpl(bool isVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;\r\n        const auto mountCommand = (isVhd) ? (L\"--mount \" + deviceName + L\" --vhd\") : (L\"--mount \" + deviceName);\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition\r\n        FormatDisk({L\"ext4\"}, isVhd);\r\n\r\n        // Mount it\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 1 --type ext4\"), (DWORD)0);\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        ValidateDiskState({deviceName, {{1, {L\"ext4\"}, {}}}}, keepAlive);\r\n        keepAlive.Reset();\r\n\r\n        // wsl --shutdown clears any disk state\r\n        WslShutdown();\r\n\r\n        if (!isVhd)\r\n        {\r\n            ValidateOffline(false);\r\n        }\r\n\r\n        // No state should be left in registry\r\n        const auto key = wsl::windows::common::registry::OpenOrCreateLxssDiskMountsKey(User->User.Sid);\r\n        VERIFY_ARE_EQUAL(wsl::windows::common::registry::EnumKeys(key.get(), KEY_READ).size(), 0);\r\n    }\r\n\r\n    void TestFilesystemDetectionWholeDiskImpl(bool isVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;\r\n        const auto mountCommand = (isVhd) ? (L\"--mount \" + deviceName + L\" --vhd\") : (L\"--mount \" + deviceName);\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Format the volume as fat\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --bare\"), (DWORD)0);\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"mkfs.fat --mbr=no -I \" + disk), (DWORD)0);\r\n\r\n        // Then mount it. The filesystem should be autodetected\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand), (DWORD)0);\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(deviceName);\r\n        Trim(trimmedDiskName);\r\n        auto mountTarget = L\"/mnt/wsl/\" + trimmedDiskName;\r\n        ValidateMountPoint(disk, mountTarget, {}, L\"vfat\");\r\n        ValidateDiskState({deviceName, {{0, {}, {}}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n\r\n        if (!isVhd)\r\n        {\r\n            WaitForDiskReady();\r\n            ValidateOffline(false);\r\n        }\r\n    }\r\n\r\n    void TestMountTwoPartitionsWithDetectionImpl(bool isVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;\r\n        const auto mountCommand = (isVhd) ? (L\"--mount \" + deviceName + L\" --vhd\") : (L\"--mount \" + deviceName);\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Create a MBR disk with 1 ext4 partition and one fat partitions\r\n        FormatDisk({L\"ext4\", L\"vfat\"}, isVhd);\r\n\r\n        // Mount then both (filesystems should be detected).\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 1\"), (DWORD)0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --partition 2\"), (DWORD)0);\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n\r\n        // Validate that the mount succeeded\r\n        std::wstring trimmedDiskName(deviceName);\r\n        Trim(trimmedDiskName);\r\n\r\n        ValidateMountPoint(disk + L\"1\", L\"/mnt/wsl/\" + trimmedDiskName + L\"p1\", {}, L\"ext4\");\r\n        ValidateMountPoint(disk + L\"2\", L\"/mnt/wsl/\" + trimmedDiskName + L\"p2\", {}, L\"vfat\");\r\n        ValidateDiskState({deviceName, {{1, {}, {}}, {2, {}, {}}}}, keepAlive);\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n\r\n        if (!isVhd)\r\n        {\r\n            WaitForDiskReady();\r\n            ValidateOffline(false);\r\n        }\r\n    }\r\n\r\n    void TestFilesystemDetectionFailImpl(bool isVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto deviceName = (isVhd) ? VhdDevice : DiskDevice;\r\n        const auto mountCommand = (isVhd) ? (L\"--mount \" + deviceName + L\" --vhd\") : (L\"--mount \" + deviceName);\r\n\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Write zeroes in the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(mountCommand + L\" --bare\"), (DWORD)0);\r\n        const auto disk = GetBlockDeviceInWsl();\r\n        VERIFY_IS_TRUE(IsBlockDevicePresent(disk));\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"dd bs=4M count=1 if=/dev/zero of=\" + disk), (DWORD)0);\r\n\r\n        // Then try to mount it\r\n        wsl::windows::common::SvcComm service;\r\n        const auto result = service.MountDisk(\r\n            deviceName.c_str(), isVhd ? LXSS_ATTACH_MOUNT_FLAGS_VHD : LXSS_ATTACH_MOUNT_FLAGS_PASS_THROUGH, 0, nullptr, nullptr, nullptr);\r\n\r\n        // Validate that the mount fail because the filesystem couldn't be detected\r\n        VERIFY_ARE_EQUAL(result.Result, -1); //-EINVAL\r\n        VERIFY_ARE_EQUAL(result.Step, 6);    // LxMiniInitMountStepDetectFilesystem\r\n\r\n        // Unmount the disk\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount \" + deviceName), (DWORD)0);\r\n\r\n        if (!isVhd)\r\n        {\r\n            WaitForDiskReady();\r\n            ValidateOffline(false);\r\n        }\r\n    }\r\n};\r\n} // namespace MountTests"
  },
  {
    "path": "test/windows/NetworkTests.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    NetworkTests.cpp\r\n\r\nAbstract:\r\n\r\n    This file contains test cases for the networking logic.\r\n\r\n--*/\r\n\r\n#include \"precomp.h\"\r\n#include \"computenetwork.h\"\r\n#include \"Common.h\"\r\n#include \"wslpolicies.h\"\r\n#include \"hns_schema.h\"\r\n\r\n#include <mstcpip.h>\r\n#include <winhttp.h>\r\n#include <winsock2.h>\r\n#include <netlistmgr.h>\r\n\r\nusing wsl::shared::hns::GuestEndpointResourceType;\r\nusing wsl::shared::hns::ModifyGuestEndpointSettingRequest;\r\nusing wsl::shared::hns::ModifyRequestType;\r\n\r\nbool TryLoadWinhttpProxyMethods() noexcept\r\n{\r\n    constexpr auto winhttpModuleName = L\"Winhttp.dll\";\r\n    const wil::shared_hmodule winhttpModule{LoadLibraryEx(winhttpModuleName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)};\r\n    if (!winhttpModule)\r\n    {\r\n        return false;\r\n    }\r\n\r\n    try\r\n    {\r\n        // attempt to find the functions for the Winhttp proxy APIs.\r\n        static LxssDynamicFunction<decltype(WinHttpRegisterProxyChangeNotification)> WinHttpRegisterProxyChangeNotification{\r\n            winhttpModule, \"WinHttpRegisterProxyChangeNotification\"};\r\n        static LxssDynamicFunction<decltype(WinHttpUnregisterProxyChangeNotification)> WinHttpUnregisterProxyChangeNotification{\r\n            winhttpModule, \"WinHttpUnregisterProxyChangeNotification\"};\r\n        static LxssDynamicFunction<decltype(WinHttpGetProxySettingsEx)> WinHttpGetProxySettingsEx{\r\n            winhttpModule, \"WinHttpGetProxySettingsEx\"};\r\n        static LxssDynamicFunction<decltype(WinHttpGetProxySettingsResultEx)> WinHttpGetProxySettingsResultEx{\r\n            winhttpModule, \"WinHttpGetProxySettingsResultEx\"};\r\n        static LxssDynamicFunction<decltype(WinHttpFreeProxySettingsEx)> WinHttpFreeProxySettingsEx{\r\n            winhttpModule, \"WinHttpFreeProxySettingsEx\"};\r\n    }\r\n    catch (...)\r\n    {\r\n        return false;\r\n    }\r\n    return true;\r\n}\r\n\r\n#define HYPERV_FIREWALL_TEST_ONLY() \\\r\n    { \\\r\n        WSL2_TEST_ONLY(); \\\r\n        WINDOWS_11_TEST_ONLY(); \\\r\n        if (!AreExperimentalNetworkingFeaturesSupported() || !IsHyperVFirewallSupported()) \\\r\n        { \\\r\n            LogSkipped(\"Hyper-V Firewall not supported on this OS. Skipping test...\"); \\\r\n            return; \\\r\n        } \\\r\n    }\r\n\r\n#define MIRRORED_NETWORKING_TEST_ONLY() \\\r\n    { \\\r\n        WSL2_TEST_ONLY(); \\\r\n        WINDOWS_11_TEST_ONLY(); \\\r\n        if (!AreExperimentalNetworkingFeaturesSupported() || !IsHyperVFirewallSupported()) \\\r\n        { \\\r\n            LogSkipped(\"Mirrored networking not supported on this OS. Skipping test..\"); \\\r\n            return; \\\r\n        } \\\r\n    }\r\n\r\n#define DNS_TUNNELING_TEST_ONLY() \\\r\n    { \\\r\n        WSL2_TEST_ONLY(); \\\r\n        WINDOWS_11_TEST_ONLY(); \\\r\n        if (!AreExperimentalNetworkingFeaturesSupported()) \\\r\n        { \\\r\n            LogSkipped(\"DNS tunneling not supported on this OS. Skipping test...\"); \\\r\n            return; \\\r\n        } \\\r\n        if (!TryLoadDnsResolverMethods()) \\\r\n        { \\\r\n            LogSkipped(\"DNS tunneling APIs not present on this OS. Skipping test...\"); \\\r\n            return; \\\r\n        } \\\r\n    }\r\n\r\n#define WINHTTP_PROXY_TEST_ONLY() \\\r\n    { \\\r\n        WSL2_TEST_ONLY(); \\\r\n        if (!TryLoadWinhttpProxyMethods()) \\\r\n        { \\\r\n            LogSkipped(\"Winhttp proxy APIs not present on this OS. Skipping test...\"); \\\r\n            return; \\\r\n        } \\\r\n    }\r\n\r\n#define VIRTIOPROXY_TEST_ONLY() \\\r\n    { \\\r\n        WSL2_TEST_ONLY(); \\\r\n    }\r\n\r\nstatic constexpr auto c_wslVmCreatorId = L\"\\'{40e0ac32-46a5-438a-A0B2-2B479E8F2E90}\\'\";\r\nstatic constexpr auto c_wsaVmCreatorId = L\"\\'{9E288F02-CE00-4D9E-BE2B-14CE463B0298}\\'\";\r\nstatic constexpr auto c_anyVmCreatorId = L\"\\'{00000000-0000-0000-0000-000000000000}\\'\";\r\nstatic constexpr auto c_firewallRuleActionBlock = L\"Block\";\r\nstatic constexpr auto c_firewallRuleActionAllow = L\"Allow\";\r\nstatic constexpr auto c_firewallTrafficTestCmd = L\"ping -c 3 -W 5 1.1.1.1\";\r\nstatic const std::wstring c_firewallTrafficTestPort = L\"80\";\r\nstatic const std::wstring c_firewallTestOtherPort = L\"443\";\r\nstatic const std::wstring c_dnsTunnelingDefaultIp = L\"10.255.255.254\";\r\n\r\n// Set ManualConnectivityValidation to true to manually check stdout from the test to verify the correct calls are made in Linux/Init\r\nstatic constexpr bool ManualConnectivityValidation = false;\r\n\r\nnamespace {\r\n\r\nstd::wstring GetMacAddress(const std::wstring& adapter = L\"eth0\")\r\n{\r\n    auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /sys/class/net/\" + adapter + L\"/address\", 0);\r\n    out.pop_back(); // remove LF\r\n    return out;\r\n}\r\n\r\ntemplate <class T>\r\nclass Stopwatch\r\n{\r\nprivate:\r\n    LARGE_INTEGER m_startQpc;\r\n    LARGE_INTEGER m_frequencyQpc;\r\n    T m_timeoutInterval;\r\n\r\npublic:\r\n    Stopwatch(_In_opt_ T TimeoutInterval = T::max()) : m_timeoutInterval(TimeoutInterval)\r\n    {\r\n        QueryPerformanceFrequency(&m_frequencyQpc);\r\n        QueryPerformanceCounter(&m_startQpc);\r\n    }\r\n\r\n    T Elapsed()\r\n    {\r\n        LARGE_INTEGER End;\r\n        UINT64 ElapsedQpc;\r\n\r\n        QueryPerformanceCounter(&End);\r\n        ElapsedQpc = End.QuadPart - m_startQpc.QuadPart;\r\n\r\n        return T((ElapsedQpc * T::period::den) / T::period::num / m_frequencyQpc.QuadPart);\r\n    }\r\n\r\n    bool IsExpired()\r\n    {\r\n        return Elapsed() >= m_timeoutInterval;\r\n    }\r\n};\r\n\r\n} // namespace\r\n\r\nnamespace NetworkTests {\r\n\r\nclass VirtioProxyTests;\r\n\r\nclass NetworkTests\r\n{\r\n    WSL_TEST_CLASS(NetworkTests)\r\n\r\n    friend class MirroredTests;\r\n    friend class BridgedTests;\r\n    friend class VirtioProxyTests;\r\n\r\n    struct IpAddress\r\n    {\r\n        std::wstring Address;\r\n        uint8_t PrefixLength;\r\n        bool Preferred = false;\r\n\r\n        bool operator==(const IpAddress& other) const\r\n        {\r\n            return Address == other.Address && PrefixLength == other.PrefixLength;\r\n        }\r\n\r\n        std::wstring GetPrefix() const\r\n        {\r\n            DWORD status = ERROR_INVALID_FUNCTION;\r\n            SOCKADDR_INET* address = nullptr;\r\n            unsigned char* addressPointer{};\r\n\r\n            NET_ADDRESS_INFO netAddrInfo{};\r\n            status = ParseNetworkString(Address.c_str(), NET_STRING_IP_ADDRESS, &netAddrInfo, nullptr, nullptr);\r\n            if (status != NO_ERROR)\r\n            {\r\n                return std::wstring(L\"\");\r\n            }\r\n\r\n            address = reinterpret_cast<SOCKADDR_INET*>(&netAddrInfo.IpAddress);\r\n            addressPointer = (address->si_family == AF_INET) ? reinterpret_cast<unsigned char*>(&address->Ipv4.sin_addr)\r\n                                                             : address->Ipv6.sin6_addr.u.Byte;\r\n\r\n            constexpr int c_numBitsPerByte = 8;\r\n            for (int i = 0, currPrefixLength = PrefixLength; i < INET_ADDR_LENGTH(address->si_family); i++, currPrefixLength -= c_numBitsPerByte)\r\n            {\r\n                if (currPrefixLength < c_numBitsPerByte)\r\n                {\r\n                    const int bitShiftAmt = (c_numBitsPerByte - std::max(currPrefixLength, 0));\r\n                    addressPointer[i] &= (0xFF >> bitShiftAmt) << bitShiftAmt;\r\n                }\r\n            }\r\n\r\n            return wsl::windows::common::string::SockAddrInetToWstring(*address) + L\"/\" + std::to_wstring(PrefixLength);\r\n        }\r\n    };\r\n\r\n    struct InterfaceState\r\n    {\r\n        std::wstring Name;\r\n        std::vector<IpAddress> V4Addresses;\r\n        std::optional<std::wstring> Gateway;\r\n        std::vector<IpAddress> V6Addresses;\r\n        std::optional<std::wstring> V6Gateway;\r\n\r\n        bool Up = false;\r\n        int Mtu = 0;\r\n        bool Rename = false;\r\n    };\r\n\r\n    struct Route\r\n    {\r\n        std::wstring Via;\r\n        std::wstring Device;\r\n        std::optional<std::wstring> Prefix;\r\n        int Metric = 0;\r\n\r\n        bool operator==(const Route& other) const\r\n        {\r\n            return Via == other.Via && Device == other.Device && Prefix == other.Prefix;\r\n        }\r\n    };\r\n\r\n    struct RoutingTableState\r\n    {\r\n        std::optional<Route> DefaultRoute;\r\n        std::vector<Route> Routes;\r\n    };\r\n\r\n    enum class FirewallType\r\n    {\r\n        Host,\r\n        HyperV\r\n    };\r\n\r\n    struct FirewallRule\r\n    {\r\n        FirewallType Type;\r\n        std::wstring Name;\r\n        std::wstring RemotePorts;\r\n        std::wstring Action;\r\n        std::wstring VmCreatorId;\r\n    };\r\n\r\n    GUID AdapterId;\r\n\r\n    TEST_CLASS_SETUP(TestClassSetup)\r\n    {\r\n        VERIFY_ARE_EQUAL(LxsstuInitialize(false), TRUE);\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_CLASS_CLEANUP(TestClassCleanup)\r\n    {\r\n        if (LxsstuVmMode())\r\n        {\r\n            WslShutdown();\r\n        }\r\n\r\n        VERIFY_NO_THROW(LxsstuUninitialize(false));\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD_SETUP(MethodSetup)\r\n    {\r\n        if (!LxsstuVmMode())\r\n        {\r\n            return true;\r\n        }\r\n\r\n        AdapterId = NetworkTests::QueryAdapterId();\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ln -f -s /init /gns\"), (DWORD)0);\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD(RemoveAndAddDefaultRoute)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        TestCase({{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n\r\n        // Verify that the default routes are set\r\n        auto state = GetIpv4RoutingTableState();\r\n        VERIFY_IS_TRUE(state.DefaultRoute.has_value());\r\n        VERIFY_ARE_EQUAL(state.DefaultRoute->Via, L\"192.168.0.1\");\r\n\r\n        auto v6State = GetIpv6RoutingTableState();\r\n        VERIFY_IS_TRUE(v6State.DefaultRoute.has_value());\r\n        VERIFY_ARE_EQUAL(v6State.DefaultRoute->Via, L\"fc00::1\");\r\n\r\n        // Now remove them\r\n        wsl::shared::hns::Route route;\r\n        route.NextHop = L\"192.168.0.1\";\r\n        route.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_PREFIX;\r\n        route.Family = AF_INET;\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Remove, GuestEndpointResourceType::Route);\r\n\r\n        wsl::shared::hns::Route v6Route;\r\n        v6Route.NextHop = L\"fc00::1\";\r\n        v6Route.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_V6_PREFIX;\r\n        v6Route.Family = AF_INET6;\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Remove, GuestEndpointResourceType::Route);\r\n\r\n        // Verify that the routes are removed\r\n        state = GetIpv4RoutingTableState();\r\n        VERIFY_IS_FALSE(state.DefaultRoute.has_value());\r\n\r\n        v6State = GetIpv6RoutingTableState();\r\n        VERIFY_IS_FALSE(v6State.DefaultRoute.has_value());\r\n\r\n        // Add them again\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Add, GuestEndpointResourceType::Route);\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Add, GuestEndpointResourceType::Route);\r\n\r\n        // Verify that the routes are restored\r\n        state = GetIpv4RoutingTableState();\r\n        VERIFY_IS_TRUE(state.DefaultRoute.has_value());\r\n        VERIFY_ARE_EQUAL(state.DefaultRoute->Via, L\"192.168.0.1\");\r\n        VERIFY_ARE_EQUAL(state.DefaultRoute->Device, L\"eth0\");\r\n\r\n        v6State = GetIpv6RoutingTableState();\r\n        VERIFY_IS_TRUE(v6State.DefaultRoute.has_value());\r\n        VERIFY_ARE_EQUAL(v6State.DefaultRoute->Via, L\"fc00::1\");\r\n        VERIFY_ARE_EQUAL(v6State.DefaultRoute->Device, L\"eth0\");\r\n    }\r\n\r\n    TEST_METHOD(SetInterfaceDownAndUp)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Disconnect interface\r\n        wsl::shared::hns::NetworkInterface link;\r\n        link.Connected = false;\r\n        RunGns(link, ModifyRequestType::Update, GuestEndpointResourceType::Interface);\r\n        VERIFY_IS_FALSE(GetInterfaceState(L\"eth0\").Up);\r\n\r\n        // Connect it again\r\n        link.Connected = true;\r\n        RunGns(link, ModifyRequestType::Update, GuestEndpointResourceType::Interface);\r\n        VERIFY_IS_TRUE(GetInterfaceState(L\"eth0\").Up);\r\n    }\r\n\r\n    TEST_METHOD(SetMtu)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Set MTU - must be 1280 bytes or above to meet IPv6 minimum MTU requirement\r\n        wsl::shared::hns::NetworkInterface link;\r\n        link.Connected = true;\r\n        link.NlMtu = 1280;\r\n        RunGns(link, ModifyRequestType::Update, GuestEndpointResourceType::Interface);\r\n        VERIFY_ARE_EQUAL(GetInterfaceState(L\"eth0\").Mtu, 1280);\r\n    }\r\n\r\n    TEST_METHOD(AddAndRemoveCustomRoute)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        TestCase({{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n\r\n        // Add custom routes, one per address family\r\n        wsl::shared::hns::Route route;\r\n        route.NextHop = L\"192.168.0.12\";\r\n        route.DestinationPrefix = L\"192.168.2.0/24\";\r\n        route.Family = AF_INET;\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n\r\n        wsl::shared::hns::Route v6Route;\r\n        v6Route.NextHop = L\"fc00::12\";\r\n        v6Route.DestinationPrefix = L\"fc00:abcd::/80\";\r\n        v6Route.Family = AF_INET6;\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n\r\n        // Check that the routes are there\r\n        const bool v4CustomRouteExists = RouteExists({L\"192.168.0.12\", L\"eth0\", L\"192.168.2.0/24\"});\r\n        const bool v6CustomRouteExists = RouteExists({L\"fc00::12\", L\"eth0\", L\"fc00:abcd::/80\"});\r\n\r\n        // Now remove them\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Remove, GuestEndpointResourceType::Route);\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Remove, GuestEndpointResourceType::Route);\r\n\r\n        // Check that the routes are gone\r\n        const bool v4CustomRouteGone = !RouteExists({L\"192.168.0.12\", L\"eth0\", L\"192.168.2.0/24\"});\r\n        const bool v6CustomRouteGone = !RouteExists({L\"fc00::12\", L\"eth0\", L\"fc00:abcd::/80\"});\r\n\r\n        VERIFY_IS_TRUE(v4CustomRouteExists);\r\n        VERIFY_IS_TRUE(v6CustomRouteExists);\r\n\r\n        VERIFY_IS_TRUE(v4CustomRouteGone);\r\n        VERIFY_IS_TRUE(v6CustomRouteGone);\r\n    }\r\n\r\n    TEST_METHOD(AddRouteWithMetrics)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        TestCase({{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n\r\n        // Add a custom route per address family\r\n        wsl::shared::hns::Route route;\r\n        route.NextHop = L\"192.168.0.12\";\r\n        route.DestinationPrefix = L\"192.168.2.0/24\";\r\n        route.Family = AF_INET;\r\n        route.Metric = 12;\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n\r\n        wsl::shared::hns::Route v6Route;\r\n        v6Route.NextHop = L\"fc00::12\";\r\n        v6Route.DestinationPrefix = L\"fc00:abcd::/64\";\r\n        v6Route.Family = AF_INET6;\r\n        v6Route.Metric = 12;\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n\r\n        // Check that the routes are there\r\n        const bool v4CustomRouteExists = RouteExists({L\"192.168.0.12\", L\"eth0\", L\"192.168.2.0/24\", 12});\r\n        const bool v6CustomRouteExists = RouteExists({L\"fc00::12\", L\"eth0\", L\"fc00:abcd::/64\", 12});\r\n\r\n        // Now remove them\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Remove, GuestEndpointResourceType::Route);\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Remove, GuestEndpointResourceType::Route);\r\n\r\n        // Check that the routes are gone\r\n        const bool v4CustomRouteGone = !RouteExists({L\"192.168.0.12\", L\"eth0\", L\"192.168.2.0/24\", 12});\r\n        const bool v6CustomRouteGone = !RouteExists({L\"fc00::12\", L\"eth0\", L\"fc00:abcd::/64\", 12});\r\n\r\n        VERIFY_IS_TRUE(v4CustomRouteExists);\r\n        VERIFY_IS_TRUE(v6CustomRouteExists);\r\n\r\n        VERIFY_IS_TRUE(v4CustomRouteGone);\r\n        VERIFY_IS_TRUE(v6CustomRouteGone);\r\n    }\r\n\r\n    TEST_METHOD(ResetRoutes)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        TestCase({{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n\r\n        // Add a custom route per address family\r\n        wsl::shared::hns::Route route;\r\n        route.NextHop = L\"192.168.0.12\";\r\n        route.DestinationPrefix = L\"192.168.2.0/24\";\r\n        route.Family = AF_INET;\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n\r\n        wsl::shared::hns::Route v6Route;\r\n        v6Route.NextHop = L\"fc00::12\";\r\n        v6Route.DestinationPrefix = L\"fc00:abcd::/80\";\r\n        v6Route.Family = AF_INET6;\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n\r\n        // Check that the custom routes are there\r\n        bool v4RouteExists = RouteExists({L\"192.168.0.12\", L\"eth0\", L\"192.168.2.0/24\"});\r\n        bool v6RouteExists = RouteExists({L\"fc00::12\", L\"eth0\", L\"fc00:abcd::/80\"});\r\n\r\n        // Reset the routing table\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Reset, GuestEndpointResourceType::Route);\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Reset, GuestEndpointResourceType::Route);\r\n\r\n        // Check that both routes are gone, per address family\r\n        bool v4RouteGoneAfterReset = !RouteExists({L\"192.168.0.12\", L\"eth0\", L\"192.168.2.0/24\"});\r\n        auto state = GetIpv4RoutingTableState();\r\n        bool v4GwGoneAfterReset = !state.DefaultRoute.has_value();\r\n\r\n        bool v6RouteGoneAfterReset = !RouteExists({L\"fc00::12\", L\"eth0\", L\"fc00:abcd::/80\"});\r\n        auto v6State = GetIpv6RoutingTableState();\r\n        bool v6GwGoneAfterReset = !v6State.DefaultRoute.has_value();\r\n\r\n        // Add the custom and default routes back\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n        route.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_PREFIX;\r\n        route.NextHop = L\"192.168.0.1\";\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n        v6Route.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_V6_PREFIX;\r\n        v6Route.NextHop = L\"fc00::1\";\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Route, ModifyRequestType::Update, GuestEndpointResourceType::Route);\r\n\r\n        // Verify that all the routes are there\r\n        bool v4RouteRestored = RouteExists({L\"192.168.0.12\", L\"eth0\", L\"192.168.2.0/24\"});\r\n        state = GetIpv4RoutingTableState();\r\n        bool v4GwRestored = state.DefaultRoute.has_value();\r\n        bool v4GwRestoredCorrectly = state.DefaultRoute->Via == L\"192.168.0.1\";\r\n\r\n        bool v6RouteRestored = RouteExists({L\"fc00::12\", L\"eth0\", L\"fc00:abcd::/80\"});\r\n        v6State = GetIpv6RoutingTableState();\r\n        bool v6GwRestored = v6State.DefaultRoute.has_value();\r\n        bool v6GwRestoredCorrectly = v6State.DefaultRoute->Via == L\"fc00::1\";\r\n\r\n        VERIFY_IS_TRUE(v4RouteExists);\r\n        VERIFY_IS_TRUE(v6RouteExists);\r\n\r\n        VERIFY_IS_TRUE(v4RouteGoneAfterReset);\r\n        VERIFY_IS_TRUE(v4GwGoneAfterReset);\r\n        VERIFY_IS_TRUE(v6RouteGoneAfterReset);\r\n        VERIFY_IS_TRUE(v6GwGoneAfterReset);\r\n\r\n        VERIFY_IS_TRUE(v4RouteRestored);\r\n        VERIFY_IS_TRUE(v4GwRestored);\r\n        VERIFY_IS_TRUE(v4GwRestoredCorrectly);\r\n        VERIFY_IS_TRUE(v6RouteRestored);\r\n        VERIFY_IS_TRUE(v6GwRestored);\r\n        VERIFY_IS_TRUE(v6GwRestoredCorrectly);\r\n    }\r\n\r\n    TEST_METHOD(ResetRoutesTwice)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        TestCase({{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n\r\n        auto state = GetIpv4RoutingTableState();\r\n        VERIFY_IS_TRUE(state.DefaultRoute.has_value());\r\n\r\n        auto v6State = GetIpv6RoutingTableState();\r\n        VERIFY_IS_TRUE(v6State.DefaultRoute.has_value());\r\n\r\n        // Reset the IPv4 table twice\r\n        wsl::shared::hns::Route route;\r\n        route.Family = AF_INET;\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Reset, GuestEndpointResourceType::Route);\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Reset, GuestEndpointResourceType::Route);\r\n\r\n        state = GetIpv4RoutingTableState();\r\n        VERIFY_IS_FALSE(state.DefaultRoute.has_value());\r\n        VERIFY_IS_TRUE(state.Routes.empty());\r\n\r\n        // Then reset the IPv6 table twice\r\n        route.Family = AF_INET6;\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Reset, GuestEndpointResourceType::Route);\r\n        SendDeviceSettingsRequest(L\"eth0\", route, ModifyRequestType::Reset, GuestEndpointResourceType::Route);\r\n\r\n        state = GetIpv6RoutingTableState();\r\n        VERIFY_IS_FALSE(state.DefaultRoute.has_value());\r\n        VERIFY_IS_TRUE(state.Routes.empty());\r\n    }\r\n\r\n    TEST_METHOD(UpdateIpAddress)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        TestCase({{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n\r\n        // Verify that the IPs are in the preferred state\r\n        auto interfaceState = GetInterfaceState(L\"eth0\");\r\n        VERIFY_ARE_EQUAL(1, interfaceState.V4Addresses.size());\r\n        VERIFY_ARE_EQUAL(L\"192.168.0.2\", interfaceState.V4Addresses[0].Address);\r\n        VERIFY_IS_TRUE(interfaceState.V4Addresses[0].Preferred);\r\n\r\n        VERIFY_ARE_EQUAL(1, interfaceState.V6Addresses.size());\r\n        VERIFY_ARE_EQUAL(L\"fc00::2\", interfaceState.V6Addresses[0].Address);\r\n        VERIFY_IS_TRUE(interfaceState.V6Addresses[0].Preferred);\r\n\r\n        // Change current ip addresses to be deprecated\r\n        wsl::shared::hns::IPAddress address;\r\n        address.Address = L\"192.168.0.2\";\r\n        address.OnLinkPrefixLength = 24;\r\n        address.Family = AF_INET;\r\n        address.PreferredLifetime = 0;\r\n        SendDeviceSettingsRequest(L\"eth0\", address, ModifyRequestType::Update, GuestEndpointResourceType::IPAddress);\r\n\r\n        wsl::shared::hns::IPAddress v6Address;\r\n        v6Address.Address = L\"fc00::2\";\r\n        v6Address.OnLinkPrefixLength = 64;\r\n        v6Address.Family = AF_INET6;\r\n        address.PreferredLifetime = 0;\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Address, ModifyRequestType::Update, GuestEndpointResourceType::IPAddress);\r\n\r\n        // Validate that the IPs are no longer preferred\r\n        interfaceState = GetInterfaceState(L\"eth0\");\r\n        VERIFY_ARE_EQUAL(1, interfaceState.V4Addresses.size());\r\n        VERIFY_ARE_EQUAL(L\"192.168.0.2\", interfaceState.V4Addresses[0].Address);\r\n        VERIFY_IS_FALSE(interfaceState.V4Addresses[0].Preferred);\r\n\r\n        VERIFY_ARE_EQUAL(1, interfaceState.V6Addresses.size());\r\n        VERIFY_ARE_EQUAL(L\"fc00::2\", interfaceState.V6Addresses[0].Address);\r\n        VERIFY_IS_FALSE(interfaceState.V6Addresses[0].Preferred);\r\n    }\r\n\r\n    enum IpPrefixOrigin\r\n    {\r\n        IpPrefixOriginOther = 0,\r\n        IpPrefixOriginManual,\r\n        IpPrefixOriginWellKnown,\r\n        IpPrefixOriginDhcp,\r\n        IpPrefixOriginRouterAdvertisement,\r\n    };\r\n\r\n    enum IpSuffixOrigin\r\n    {\r\n        IpSuffixOriginOther = 0,\r\n        IpSuffixOriginManual,\r\n        IpSuffixOriginWellKnown,\r\n        IpSuffixOriginDhcp,\r\n        IpSuffixOriginLinkLayerAddress,\r\n        IpSuffixOriginRandom,\r\n    };\r\n\r\n    TEST_METHOD(TemporaryAddress)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        TestCase({{L\"eth0\", {}, {}, {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n\r\n        // Make the address public\r\n        wsl::shared::hns::IPAddress v6Address;\r\n        v6Address.Address = L\"fc00::2\";\r\n        v6Address.OnLinkPrefixLength = 64;\r\n        v6Address.Family = AF_INET6;\r\n        v6Address.PrefixOrigin = IpPrefixOriginRouterAdvertisement;\r\n        v6Address.SuffixOrigin = IpSuffixOriginLinkLayerAddress;\r\n        v6Address.PreferredLifetime = 0xFFFFFFFF;\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Address, ModifyRequestType::Update, GuestEndpointResourceType::IPAddress);\r\n\r\n        // Add a temporary address\r\n        v6Address.Address = L\"fc00::abcd:1234:5678:9999\";\r\n        v6Address.OnLinkPrefixLength = 64;\r\n        v6Address.Family = AF_INET6;\r\n        v6Address.PrefixOrigin = IpPrefixOriginRouterAdvertisement;\r\n        v6Address.SuffixOrigin = IpSuffixOriginRandom;\r\n        v6Address.PreferredLifetime = 0xFFFFFFFF;\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Address, ModifyRequestType::Add, GuestEndpointResourceType::IPAddress);\r\n\r\n        // Wait for DAD to finish to avoid it being a factor in source address selection\r\n        std::this_thread::sleep_for(std::chrono::milliseconds(2000));\r\n\r\n        VERIFY_ARE_EQUAL(2, GetInterfaceState(L\"eth0\").V6Addresses.size());\r\n\r\n        // Ensure that the temporary address is preferred during source address selection\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"ip route get 2001::5\");\r\n        LogInfo(\"'ip route get 2001::5' - '%ls'\", out.c_str());\r\n\r\n        auto [out5, _5] = LxsstuLaunchWslAndCaptureOutput(L\"ip addr show eth0\");\r\n        LogInfo(\"[TemporaryAddress] ip addr show output:\\r\\n%ls\", FixLineEndings(out5).c_str());\r\n\r\n        std::wsmatch match;\r\n        std::wregex pattern(L\"2001::5 from :: via fc00::1 dev eth0 proto kernel src ([a-f,A-F,0-9,:]+)\");\r\n        VERIFY_IS_TRUE(std::regex_search(out, match, pattern));\r\n        VERIFY_ARE_EQUAL(2, match.size());\r\n        VERIFY_ARE_EQUAL(L\"fc00::abcd:1234:5678:9999\", match.str(1));\r\n\r\n        // Make another public address\r\n        v6Address.Address = L\"fc00::3\";\r\n        v6Address.OnLinkPrefixLength = 64;\r\n        v6Address.Family = AF_INET6;\r\n        v6Address.PrefixOrigin = IpPrefixOriginRouterAdvertisement;\r\n        v6Address.SuffixOrigin = IpSuffixOriginLinkLayerAddress;\r\n        v6Address.PreferredLifetime = 0xFFFFFFFF;\r\n        SendDeviceSettingsRequest(L\"eth0\", v6Address, ModifyRequestType::Add, GuestEndpointResourceType::IPAddress);\r\n\r\n        // Test source address selection again\r\n        auto [out2, _2] = LxsstuLaunchWslAndCaptureOutput(L\"ip route get 2001::6\");\r\n        LogInfo(\"'ip route get 2001::6' - '%ls'\", out2.c_str());\r\n\r\n        std::wregex pattern2(L\"2001::6 from :: via fc00::1 dev eth0 proto kernel src ([a-f,A-F,0-9,:]+)\");\r\n        VERIFY_IS_TRUE(std::regex_search(out2, match, pattern2));\r\n        VERIFY_ARE_EQUAL(2, match.size());\r\n        VERIFY_ARE_EQUAL(L\"fc00::abcd:1234:5678:9999\", match.str(1));\r\n    }\r\n\r\n    TEST_METHOD(SimpleCase)\r\n    {\r\n        TestCase({{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n    }\r\n\r\n    TEST_METHOD(AddressChange)\r\n    {\r\n        TestCase(\r\n            {{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"},\r\n             {L\"eth0\", {{L\"192.168.0.3\", 24}}, L\"192.168.0.1\", {{L\"fc00::3\", 64}}, L\"fc00::1\"}});\r\n    }\r\n\r\n    TEST_METHOD(GatewayChange)\r\n    {\r\n        TestCase(\r\n            {{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"},\r\n             {L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.3\", {{L\"fc00::2\", 64}}, L\"fc00::3\"}});\r\n    }\r\n\r\n    TEST_METHOD(NetworkChange)\r\n    {\r\n        TestCase(\r\n            {{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"},\r\n             {L\"eth0\", {{L\"10.0.0.2\", 16}}, L\"10.0.0.1\", {{L\"fc00:abcd::5\", 80}}, L\"fc00:abcd::1\"}});\r\n    }\r\n\r\n    TEST_METHOD(NetworkChangeAndBack)\r\n    {\r\n        TestCase(\r\n            {{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"},\r\n             {L\"eth0\", {{L\"10.0.0.2\", 16}}, L\"10.0.0.1\", {{L\"fc00:abcd::5\", 80}}, L\"fc00:abcd::1\"},\r\n             {L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n    }\r\n\r\n    TEST_METHOD(NoChange)\r\n    {\r\n        TestCase(\r\n            {{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"},\r\n             {L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\"}});\r\n    }\r\n\r\n    TEST_METHOD(MultipleIps)\r\n    {\r\n        TestCase(\r\n            {{L\"eth0\",\r\n              {{L\"192.168.0.2\", 24}, {L\"192.168.0.3\", 24}},\r\n              L\"192.168.0.1\",\r\n              {{L\"fc00::2\", 64}, {L\"fc00::3\", 64}},\r\n              L\"fc00::1\"}});\r\n    }\r\n\r\n    TEST_METHOD(MacAddressChangeAndBack)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto originalMac = GetMacAddress();\r\n\r\n        wsl::shared::hns::MacAddress macAddress;\r\n        macAddress.PhysicalAddress = \"AA-AA-FF-FF-FF-FF\";\r\n        SendDeviceSettingsRequest(L\"eth0\", macAddress, ModifyRequestType::Update, GuestEndpointResourceType::MacAddress);\r\n        VERIFY_ARE_EQUAL(GetMacAddress(), L\"aa:aa:ff:ff:ff:ff\");\r\n\r\n        macAddress.PhysicalAddress = wsl::shared::string::WideToMultiByte(originalMac);\r\n        std::replace(macAddress.PhysicalAddress.begin(), macAddress.PhysicalAddress.end(), ':', '-');\r\n        SendDeviceSettingsRequest(L\"eth0\", macAddress, ModifyRequestType::Update, GuestEndpointResourceType::MacAddress);\r\n        VERIFY_ARE_EQUAL(GetMacAddress(), originalMac);\r\n    }\r\n\r\n    static void VerifyDigDnsResolution(const std::wstring& digCommandLine)\r\n    {\r\n        // dig has exit code 0 when it receives a DNS response\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(digCommandLine.data(), 0);\r\n\r\n        // Verify dig returned a non-empty output\r\n        VERIFY_IS_TRUE(!out.empty());\r\n    }\r\n\r\n    static void VerifyDnsResolutionBasic()\r\n    {\r\n        // Verify basic DNS resolution using getent\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"getent ahosts bing.com\", 0);\r\n        VERIFY_IS_TRUE(!out.empty());\r\n    }\r\n\r\n    static void VerifyDnsResolutionDig()\r\n    {\r\n        if (HostHasInternetConnectivity(AF_INET))\r\n        {\r\n            // Test A record resolution (IPv4) with both UDP and TCP\r\n            VerifyDigDnsResolution(L\"dig +short +time=5 A bing.com\");\r\n            VerifyDigDnsResolution(L\"dig +tcp +short +time=5 A bing.com\");\r\n\r\n            // Test reverse DNS lookup\r\n            VerifyDigDnsResolution(L\"dig +short +time=5 -x 8.8.8.8\");\r\n            VerifyDigDnsResolution(L\"dig +tcp +short +time=5 -x 8.8.8.8\");\r\n        }\r\n        else\r\n        {\r\n            LogInfo(\"Host does not have IPv4 internet connectivity. Skipping IPv4 DNS tests.\");\r\n        }\r\n\r\n        if (HostHasInternetConnectivity(AF_INET6))\r\n        {\r\n            // Test AAAA record resolution (IPv6) with both UDP and TCP\r\n            VerifyDigDnsResolution(L\"dig +short +time=5 AAAA bing.com\");\r\n            VerifyDigDnsResolution(L\"dig +tcp +short +time=5 AAAA bing.com\");\r\n        }\r\n        else\r\n        {\r\n            LogInfo(\"Host does not have IPv6 internet connectivity. Skipping IPv6 DNS tests.\");\r\n        }\r\n    }\r\n\r\n    static void VerifyDnsResolutionRecordTypes()\r\n    {\r\n        // Test various DNS record types\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 MX bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 NS bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 TXT bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 SOA bing.com\");\r\n    }\r\n\r\n    static void VerifyDnsResolutionDigV6()\r\n    {\r\n        if (!HostHasInternetConnectivity(AF_INET6))\r\n        {\r\n            LogSkipped(\"Host does not have IPv6 internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        // Test AAAA record resolution (IPv6) with both UDP and TCP\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 AAAA bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +short +time=5 AAAA bing.com\");\r\n    }\r\n\r\n    static void VerifyDnsQueries()\r\n    {\r\n        // query for A/IPv4 records\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 A bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +short +time=5 A bing.com\");\r\n\r\n        // query for AAAA/IPv6 records\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 AAAA bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +short +time=5 AAAA bing.com\");\r\n\r\n        // query for MX records\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 MX bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +short +time=5 MX bing.com\");\r\n\r\n        // query for NS records\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 NS bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +short +time=5 NS bing.com\");\r\n\r\n        // reverse DNS lookup\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 -x 8.8.8.8\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +short +time=5 -x 8.8.8.8\");\r\n\r\n        // query for SOA records\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 SOA bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +short +time=5 SOA bing.com\");\r\n\r\n        // query for TXT records\r\n        VerifyDigDnsResolution(L\"dig +short +time=5 TXT bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +short +time=5 TXT bing.com\");\r\n\r\n        // query for CNAME records\r\n        VerifyDigDnsResolution(L\"dig +time=5 CNAME bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +time=5 CNAME bing.com\");\r\n\r\n        // query for SRV records\r\n        VerifyDigDnsResolution(L\"dig +time=5 SRV bing.com\");\r\n        VerifyDigDnsResolution(L\"dig +tcp +time=5 SRV bing.com\");\r\n\r\n        // query for ANY - for this option dig expects a large response so it will query directly over TCP,\r\n        // instead of trying UDP first and falling back to TCP.\r\n        VerifyDigDnsResolution(L\"dig +short ANY bing.com\");\r\n    }\r\n\r\n    static void VerifyDnsSuffixes()\r\n    {\r\n        bool foundSuffix = false;\r\n\r\n        // Verify global DNS suffixes are reflected in Linux\r\n        auto [outGlobal, errGlobal] = LxsstuLaunchPowershellAndCaptureOutput(\r\n            L\"Get-DnsClientGlobalSetting | Select-Object -Property SuffixSearchList | ForEach-Object {$_.SuffixSearchList}\");\r\n\r\n        const std::wstring separators = L\" \\n\\t\\r\";\r\n\r\n        for (const auto& suffix : wsl::shared::string::SplitByMultipleSeparators(outGlobal, separators))\r\n        {\r\n            if (!suffix.empty())\r\n            {\r\n                foundSuffix = true;\r\n                // use grep -F as suffixes can contain '.'\r\n                VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"cat /etc/resolv.conf | grep search | grep -F \" + suffix), static_cast<DWORD>(0));\r\n            }\r\n        }\r\n\r\n        // Verify per-interface DNS suffixes are reflected in Linux\r\n        auto [outPerInterface, errPerInterface] =\r\n            LxsstuLaunchPowershellAndCaptureOutput(L\"Get-DnsClient | ForEach-Object {$_.ConnectionSpecificSuffix}\");\r\n\r\n        for (const auto& suffix : wsl::shared::string::SplitByMultipleSeparators(outPerInterface, separators))\r\n        {\r\n            if (!suffix.empty())\r\n            {\r\n                foundSuffix = true;\r\n                // use grep -F as suffixes can contain '.'\r\n                VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"cat /etc/resolv.conf | grep search | grep -F \" + suffix), static_cast<DWORD>(0));\r\n            }\r\n        }\r\n\r\n        // No suffix was found - configure a dummy global suffix, verify it's reflected in Linux, then delete it\r\n        if (!foundSuffix)\r\n        {\r\n            LxsstuLaunchPowershellAndCaptureOutput(L\"Set-DnsClientGlobalSetting -SuffixSearchList @('test.com')\");\r\n            auto restoreGlobalSuffixes = wil::scope_exit(\r\n                [&] { LxsstuLaunchPowershellAndCaptureOutput(L\"Set-DnsClientGlobalSetting -SuffixSearchList @()\"); });\r\n\r\n            std::this_thread::sleep_for(std::chrono::seconds(1));\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"cat /etc/resolv.conf | grep search | grep -F test.com\"), static_cast<DWORD>(0));\r\n\r\n            LxsstuLaunchPowershellAndCaptureOutput(L\"Set-DnsClientGlobalSetting -SuffixSearchList @()\");\r\n            std::this_thread::sleep_for(std::chrono::seconds(1));\r\n\r\n            VERIFY_ARE_NOT_EQUAL(LxsstuLaunchWsl(L\"cat /etc/resolv.conf | grep search | grep -F test.com\"), static_cast<DWORD>(0));\r\n        }\r\n    }\r\n\r\n    static void VerifyEtcHosts()\r\n    {\r\n        const auto windowsHostsPath = \"C:\\\\Windows\\\\System32\\\\drivers\\\\etc\\\\hosts\";\r\n\r\n        // Save existing Windows /etc/hosts\r\n        std::wifstream windowsHostsRead(windowsHostsPath);\r\n        const auto oldWindowsHosts = std::wstring{std::istreambuf_iterator<wchar_t>(windowsHostsRead), {}};\r\n        windowsHostsRead.close();\r\n\r\n        auto restoreWindowsHosts = wil::scope_exit([&] {\r\n            std::wofstream windowsHostsWrite(windowsHostsPath);\r\n            windowsHostsWrite << oldWindowsHosts;\r\n        });\r\n\r\n        // Add dummy entry matching bing.com to IP 1.2.3.4\r\n        std::wofstream windowsHostsWrite(windowsHostsPath, std::ios_base::app);\r\n        windowsHostsWrite << \"\\n1.2.3.4 bing.com\";\r\n        windowsHostsWrite.close();\r\n\r\n        // Verify Linux /etc/hosts does *not* contain 1.2.3.4\r\n        VERIFY_ARE_NOT_EQUAL(LxsstuLaunchWsl(L\"cat /etc/hosts | grep -F 1.2.3.4\"), static_cast<DWORD>(0));\r\n\r\n        // Verify bing.com gets resolved to 1.2.3.4 by dig\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"dig bing.com | grep -F 1.2.3.4\"), static_cast<DWORD>(0));\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"dig +tcp bing.com | grep -F 1.2.3.4\"), static_cast<DWORD>(0));\r\n    }\r\n\r\n    static void VerifyDnsTunneling(const std::wstring& dnsTunnelingIpAddress)\r\n    {\r\n        // Verify /etc/resolv.conf is configured with the expected nameserver\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"cat /etc/resolv.conf | grep nameserver | grep -F \" + dnsTunnelingIpAddress), static_cast<DWORD>(0));\r\n\r\n        // Verify that we have a working connection.\r\n        GuestClient(L\"tcp-connect:bing.com:80\");\r\n\r\n        // Verify multiple types of DNS queries\r\n        VerifyDnsQueries();\r\n\r\n        // Verify resolution via Windows /etc/hosts\r\n        VerifyEtcHosts();\r\n\r\n        // Verify DNS tunneling works with systemd enabled\r\n        auto revert = EnableSystemd();\r\n\r\n        GuestClient(L\"tcp-connect:bing.com:80\");\r\n        VerifyDnsQueries();\r\n    }\r\n\r\n    TEST_METHOD(NatDnsTunneling)\r\n    {\r\n        DNS_TUNNELING_TEST_ONLY();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig({.dnsTunneling = true}));\r\n\r\n        VerifyDnsTunneling(c_dnsTunnelingDefaultIp);\r\n    }\r\n\r\n    TEST_METHOD(NatDnsTunnelingWithSpecificIp)\r\n    {\r\n        DNS_TUNNELING_TEST_ONLY();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig({.dnsTunneling = true, .dnsTunnelingIpAddress = L\"10.255.255.1\"}));\r\n\r\n        VerifyDnsTunneling(L\"10.255.255.1\");\r\n    }\r\n\r\n    TEST_METHOD(NatDnsTunnelingVerifySuffixes)\r\n    {\r\n        DNS_TUNNELING_TEST_ONLY();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig({.dnsTunneling = true}));\r\n\r\n        VerifyDnsSuffixes();\r\n    }\r\n\r\n    TEST_METHOD(NatWithoutIcsDnsProxy)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Verify WSL has connectivity in NAT mode when the ICS DNS proxy is turned off (in which case the DNS servers\r\n        // from Windows are mirrored in Linux)\r\n        WslConfigChange config(LxssGenerateTestConfig({.dnsProxy = false}));\r\n\r\n        GuestClient(L\"tcp-connect:bing.com:80\");\r\n    }\r\n\r\n    TEST_METHOD(DnsChange)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        wsl::shared::hns::DNS dns;\r\n        dns.ServerList = {L\"1.1.1.1\"};\r\n        dns.Options = LX_INIT_RESOLVCONF_FULL_HEADER;\r\n        RunGns(dns, ModifyRequestType::Update, GuestEndpointResourceType::DNS);\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /etc/resolv.conf\", 0);\r\n        const std::wstring expected = std::wstring(LX_INIT_RESOLVCONF_FULL_HEADER) + L\"nameserver 1.1.1.1\\n\";\r\n        VERIFY_ARE_EQUAL(expected, out.c_str());\r\n    }\r\n\r\n    TEST_METHOD(DnsChangeMultipleServerAndSearch)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        wsl::shared::hns::DNS dns;\r\n        dns.ServerList = L\"1.1.1.1,1.1.1.2\";\r\n        dns.Search = L\"foo.microsoft.com,bar.microsoft.com\";\r\n        dns.Options = LX_INIT_RESOLVCONF_FULL_HEADER;\r\n        RunGns(dns, ModifyRequestType::Update, GuestEndpointResourceType::DNS);\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /etc/resolv.conf\", 0);\r\n\r\n        const std::wstring expected = std::wstring(LX_INIT_RESOLVCONF_FULL_HEADER) +\r\n                                      L\"nameserver 1.1.1.1\\n\"\r\n                                      L\"nameserver 1.1.1.2\\n\"\r\n                                      L\"search foo.microsoft.com bar.microsoft.com\\n\";\r\n        VERIFY_ARE_EQUAL(expected, out.c_str());\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionBasic)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        NetworkTests::VerifyDnsResolutionBasic();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionDig)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        NetworkTests::VerifyDnsResolutionDig();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionRecordTypes)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        NetworkTests::VerifyDnsResolutionRecordTypes();\r\n    }\r\n\r\n    static void ClearHttpProxySettings(bool userScope)\r\n    {\r\n        auto command = L\"Set-WinhttpProxy -SettingScope Machine -Proxy \\\\\\\"\\\\\\\"\";\r\n        if (userScope)\r\n        {\r\n            command = L\"Set-WinhttpProxy -SettingScope User -Proxy \\\\\\\"\\\\\\\"\";\r\n        }\r\n        LxsstuLaunchPowershellAndCaptureOutput(command);\r\n    }\r\n\r\n    static void SetHttpProxySettings(const std::wstring& proxyString, const std::wstring& bypasses, const std::wstring& autoconfigUrl, bool userScope)\r\n    {\r\n        std::wstringstream proxySettings{};\r\n        if (userScope)\r\n        {\r\n            proxySettings << L\" -SettingScope User\";\r\n        }\r\n        else\r\n        {\r\n            proxySettings << L\" -SettingScope Machine\";\r\n        }\r\n        if (!proxyString.empty())\r\n        {\r\n            proxySettings << L\" -Proxy \" + proxyString;\r\n        }\r\n        if (!bypasses.empty())\r\n        {\r\n            proxySettings << L\" -ProxyBypass \\\\\\\"\" + bypasses + L\"\\\\\\\"\";\r\n        }\r\n        if (!autoconfigUrl.empty())\r\n        {\r\n            proxySettings << L\" -AutoconfigUrl \" + autoconfigUrl;\r\n        }\r\n        LogInfo(\"SetHttpProxySettings %ls\", proxySettings.str().c_str());\r\n        auto [out, _] = LxsstuLaunchPowershellAndCaptureOutput(L\"Set-WinhttpProxy\" + proxySettings.str());\r\n        LogInfo(\"WinhttpProxy %ls\", out.c_str());\r\n    }\r\n\r\n    static constexpr auto c_httpProxyLower = L\"http_proxy\";\r\n    static constexpr auto c_httpProxyUpper = L\"HTTP_PROXY\";\r\n    static constexpr auto c_httpsProxyLower = L\"https_proxy\";\r\n    static constexpr auto c_httpsProxyUpper = L\"HTTPS_PROXY\";\r\n    static constexpr auto c_proxyBypassLower = L\"no_proxy\";\r\n    static constexpr auto c_proxyBypassUpper = L\"NO_PROXY\";\r\n    static constexpr auto c_pacProxy = L\"WSL_PAC_URL\";\r\n    static constexpr auto c_httpProxyString = L\"http://test.com:8888\";\r\n    static constexpr auto c_httpProxyString2 = L\"http://otherServer.com:1234\";\r\n    static constexpr auto c_httpProxyLocalhost = L\"http://localhost:8888\";\r\n    static constexpr auto c_httpProxyLoopback = L\"http://loopback:8888\";\r\n    static constexpr auto c_httpProxyLocalhostv4 = L\"http://127.0.0.1:8888\";\r\n    static constexpr auto c_httpProxyLocalhostv6 = L\"http://[::1]:8888\";\r\n    static constexpr auto c_httpProxyIpV4 = L\"http://198.168.1.128:8888\";\r\n    static constexpr auto c_httpProxyIpV6 = L\"http://[2001::1]:8888\";\r\n    static constexpr auto c_httpProxyBypassString = L\"test\";\r\n    static constexpr auto c_httpProxyPACurl = L\"testpac.pac\";\r\n\r\n    static void VerifyWslEnvVariable(const std::wstring& envVar, const std::wstring& proxyString)\r\n    {\r\n        auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"echo -n $\" + envVar);\r\n        VERIFY_ARE_EQUAL(proxyString, output);\r\n    }\r\n\r\n    static void VerifyHttpProxyBypassesMirrored(const std::wstring& bypassString)\r\n    {\r\n        VerifyWslEnvVariable(c_proxyBypassLower, bypassString);\r\n        VerifyWslEnvVariable(c_proxyBypassUpper, bypassString);\r\n    }\r\n\r\n    static void VerifyHttpProxyPacUrlMirrored(const std::wstring& pacUrl)\r\n    {\r\n        VerifyWslEnvVariable(c_pacProxy, pacUrl);\r\n    }\r\n\r\n    static void VerifyHttpProxyStringMirrored(const std::wstring& proxyString)\r\n    {\r\n        VerifyWslEnvVariable(c_httpProxyLower, proxyString);\r\n        VerifyWslEnvVariable(c_httpProxyUpper, proxyString);\r\n        VerifyWslEnvVariable(c_httpsProxyLower, proxyString);\r\n        VerifyWslEnvVariable(c_httpsProxyUpper, proxyString);\r\n    }\r\n\r\n    static void VerifyHttpProxyEnvVariables(const std::wstring& proxyString, const std::wstring& bypassString, const std::wstring& pacUrl)\r\n    {\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"printenv\");\r\n        LogInfo(\"VerifyHttpProxyEnvVariables:\\r\\n%ls\", FixLineEndings(out).c_str());\r\n\r\n        VerifyHttpProxyStringMirrored(proxyString);\r\n        VerifyHttpProxyBypassesMirrored(bypassString);\r\n        VerifyHttpProxyPacUrlMirrored(pacUrl);\r\n    }\r\n\r\n    static void VerifyHttpProxySimple(bool userScope = true)\r\n    {\r\n        auto restoreProxySettings = wil::scope_exit([&] { ClearHttpProxySettings(userScope); });\r\n\r\n        SetHttpProxySettings(c_httpProxyString, L\"\", L\"\", userScope);\r\n        VerifyHttpProxyEnvVariables(c_httpProxyString, L\"\", L\"\");\r\n    }\r\n\r\n    static void VerifyNoHttpProxyConfigured(bool userScope = true)\r\n    {\r\n        ClearHttpProxySettings(userScope);\r\n        VerifyHttpProxyEnvVariables(L\"\", L\"\", L\"\");\r\n    }\r\n\r\n    static void VerifyHttpProxyWithBypassesConfigured(bool userScope = true)\r\n    {\r\n        auto restoreProxySettings = wil::scope_exit([&] { ClearHttpProxySettings(userScope); });\r\n\r\n        SetHttpProxySettings(c_httpProxyString, c_httpProxyBypassString, L\"\", userScope);\r\n        VerifyHttpProxyEnvVariables(c_httpProxyString, c_httpProxyBypassString, L\"\");\r\n    }\r\n\r\n    static void VerifyHttpProxyChange(bool userScope = true)\r\n    {\r\n        auto restoreProxySettings = wil::scope_exit([&] { ClearHttpProxySettings(userScope); });\r\n\r\n        SetHttpProxySettings(c_httpProxyString, L\"\", L\"\", userScope);\r\n        VerifyHttpProxyEnvVariables(c_httpProxyString, L\"\", L\"\");\r\n\r\n        SetHttpProxySettings(c_httpProxyString2, L\"\", L\"\", userScope);\r\n        VerifyHttpProxyEnvVariables(c_httpProxyString2, L\"\", L\"\");\r\n    }\r\n\r\n    static void VerifyHttpProxyAndWslEnv(bool userScope = true)\r\n    {\r\n        auto restoreProxySettings = wil::scope_exit([&] {\r\n            ClearHttpProxySettings(userScope);\r\n            THROW_LAST_ERROR_IF(!SetEnvironmentVariable(c_httpProxyLower, nullptr));\r\n            THROW_LAST_ERROR_IF(!SetEnvironmentVariable(L\"WSLENV\", nullptr));\r\n        });\r\n\r\n        THROW_LAST_ERROR_IF(!SetEnvironmentVariable(c_httpProxyLower, c_httpProxyString));\r\n        std::wstring wslEnvVal{c_httpProxyLower};\r\n        THROW_LAST_ERROR_IF(!SetEnvironmentVariable(L\"WSLENV\", wslEnvVal.append(L\"/u\").c_str()));\r\n\r\n        VerifyWslEnvVariable(c_httpProxyLower, c_httpProxyString);\r\n        SetHttpProxySettings(c_httpProxyString2, L\"\", L\"\", true);\r\n        // the user set environment variable should have priority over the proxy configured on host\r\n        VerifyWslEnvVariable(c_httpProxyLower, c_httpProxyString);\r\n        // this variable was not configured by user, so we use host configured proxy\r\n        VerifyWslEnvVariable(c_httpProxyUpper, c_httpProxyString2);\r\n    }\r\n\r\n    static void VerifyHttpProxyFilterByNetworkConfiguration(bool isNatMode)\r\n    {\r\n        auto restoreProxySettings = wil::scope_exit([&] { ClearHttpProxySettings(true); });\r\n\r\n        SetHttpProxySettings(c_httpProxyLocalhost, L\"\", L\"\", true);\r\n        if (isNatMode)\r\n        {\r\n            VerifyHttpProxyEnvVariables(L\"\", L\"\", L\"\");\r\n        }\r\n        else\r\n        {\r\n            VerifyHttpProxyEnvVariables(c_httpProxyLocalhost, L\"\", L\"\");\r\n        }\r\n\r\n        ClearHttpProxySettings(true);\r\n\r\n        SetHttpProxySettings(c_httpProxyLoopback, L\"\", L\"\", true);\r\n        if (isNatMode)\r\n        {\r\n            VerifyHttpProxyEnvVariables(L\"\", L\"\", L\"\");\r\n        }\r\n        else\r\n        {\r\n            VerifyHttpProxyEnvVariables(c_httpProxyLoopback, L\"\", L\"\");\r\n        }\r\n\r\n        ClearHttpProxySettings(true);\r\n\r\n        SetHttpProxySettings(c_httpProxyLocalhostv4, L\"\", L\"\", true);\r\n        if (isNatMode)\r\n        {\r\n            VerifyHttpProxyEnvVariables(L\"\", L\"\", L\"\");\r\n        }\r\n        else\r\n        {\r\n            VerifyHttpProxyEnvVariables(c_httpProxyLocalhostv4, L\"\", L\"\");\r\n        }\r\n\r\n        ClearHttpProxySettings(true);\r\n\r\n        SetHttpProxySettings(c_httpProxyLocalhostv4, c_httpProxyBypassString, L\"\", true);\r\n        if (isNatMode)\r\n        {\r\n            VerifyHttpProxyEnvVariables(L\"\", L\"\", L\"\");\r\n        }\r\n        else\r\n        {\r\n            VerifyHttpProxyEnvVariables(c_httpProxyLocalhostv4, c_httpProxyBypassString, L\"\");\r\n        }\r\n\r\n        ClearHttpProxySettings(true);\r\n        // validate nonloopback v4 still works\r\n        SetHttpProxySettings(c_httpProxyIpV4, L\"\", L\"\", true);\r\n        VerifyHttpProxyEnvVariables(c_httpProxyIpV4, L\"\", L\"\");\r\n\r\n        ClearHttpProxySettings(true);\r\n\r\n        SetHttpProxySettings(c_httpProxyIpV6, c_httpProxyBypassString, L\"\", true);\r\n        // v6 addresses is only supported in mirrored mode\r\n        if (isNatMode)\r\n        {\r\n            VerifyHttpProxyEnvVariables(L\"\", L\"\", L\"\");\r\n        }\r\n        else\r\n        {\r\n            VerifyHttpProxyEnvVariables(c_httpProxyIpV6, c_httpProxyBypassString, L\"\");\r\n        }\r\n\r\n        ClearHttpProxySettings(true);\r\n        // v6 loopback is unsupported in both network modes\r\n        SetHttpProxySettings(c_httpProxyLocalhostv6, L\"\", L\"\", true);\r\n        VerifyHttpProxyEnvVariables(L\"\", L\"\", L\"\");\r\n    }\r\n\r\n    static void VerifyHttpProxyFilterByNetworkConfigurationNAT()\r\n    {\r\n        VerifyHttpProxyFilterByNetworkConfiguration(true);\r\n    }\r\n\r\n    static void VerifyHttpProxyFilterByNetworkConfigurationMirrored()\r\n    {\r\n        VerifyHttpProxyFilterByNetworkConfiguration(false);\r\n    }\r\n\r\n    TEST_METHOD(NatHttpProxyVerifyConfigDisabled)\r\n    {\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.autoProxy = false}));\r\n\r\n        auto restoreProxySettings = wil::scope_exit([&] { ClearHttpProxySettings(true); });\r\n        SetHttpProxySettings(c_httpProxyString, L\"\", L\"\", true);\r\n        VerifyHttpProxyEnvVariables(L\"\", L\"\", L\"\");\r\n    }\r\n\r\n    TEST_METHOD(NatHttpProxySimple)\r\n    {\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.autoProxy = true}));\r\n\r\n        VerifyHttpProxySimple();\r\n    }\r\n\r\n    TEST_METHOD(NatHttpProxySimpleMachineScope)\r\n    {\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.autoProxy = true}));\r\n\r\n        // verify with machine scope\r\n        VerifyHttpProxySimple(false);\r\n    }\r\n\r\n    TEST_METHOD(NatNoHttpProxyConfigured)\r\n    {\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.autoProxy = true}));\r\n\r\n        VerifyNoHttpProxyConfigured();\r\n    }\r\n\r\n    TEST_METHOD(NatHttpProxyWithBypassesConfigured)\r\n    {\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.autoProxy = true}));\r\n        VerifyHttpProxyWithBypassesConfigured();\r\n    }\r\n\r\n    TEST_METHOD(NatHttpProxyChange)\r\n    {\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.autoProxy = true}));\r\n        VerifyHttpProxyChange();\r\n    }\r\n\r\n    TEST_METHOD(NatHttpProxyAndWslEnv)\r\n    {\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.autoProxy = true}));\r\n        VerifyHttpProxyAndWslEnv();\r\n    }\r\n\r\n    TEST_METHOD(NatHttpProxyFilterByNetworkConfiguration)\r\n    {\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.autoProxy = true}));\r\n        VerifyHttpProxyFilterByNetworkConfigurationNAT();\r\n    }\r\n\r\n    TEST_METHOD(RenameInterface)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Disconnect \"eth0\" interface so it can be renamed\r\n        wsl::shared::hns::NetworkInterface link;\r\n        link.Connected = false;\r\n        RunGns(link, ModifyRequestType::Update, GuestEndpointResourceType::Interface);\r\n        const bool eth0Disconnected = !GetInterfaceState(L\"eth0\").Up;\r\n\r\n        TestCase({{L\"myeth\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\", false, 1500, true}});\r\n        const bool myethConnected = GetInterfaceState(L\"myeth\").Up;\r\n\r\n        // Disconnect \"myeth\" interface so it can be restored\r\n        link.Connected = false;\r\n        RunGns(link, ModifyRequestType::Update, GuestEndpointResourceType::Interface);\r\n        const bool myethDisconnected = !GetInterfaceState(L\"myeth\").Up;\r\n\r\n        TestCase({{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\", false, 1500, true}});\r\n        const bool eth0Connected = GetInterfaceState(L\"eth0\").Up;\r\n\r\n        VERIFY_IS_TRUE(eth0Disconnected);\r\n        VERIFY_IS_TRUE(myethConnected);\r\n        VERIFY_IS_TRUE(myethDisconnected);\r\n        VERIFY_IS_TRUE(eth0Connected);\r\n    }\r\n\r\n    TEST_METHOD(RenameWifiInterface)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        std::wstring commandLine(L\"wsl.exe bash -c \\\"zcat /proc/config.gz | grep CONFIG_PROXY_WIFI=y\\\"\");\r\n        const auto out = std::get<0>(LxsstuLaunchCommandAndCaptureOutputWithResult(commandLine.data()));\r\n        if (out.empty())\r\n        {\r\n            LogSkipped(\"Kernel does not support PROXY_WIFI. Skipping test...\");\r\n            return;\r\n        }\r\n\r\n        // Disconnect \"eth0\" interface so it can be renamed\r\n        wsl::shared::hns::NetworkInterface link;\r\n        link.Connected = false;\r\n        RunGns(link, ModifyRequestType::Update, GuestEndpointResourceType::Interface);\r\n        const bool eth0Disconnected = !GetInterfaceState(L\"eth0\").Up;\r\n\r\n        TestCase({{L\"wlan0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\", false, 1500, true}});\r\n        const bool _wlan0Connected = GetInterfaceState(L\"_wlan0\").Up;\r\n\r\n        const bool _wlan0Deleted = LxsstuLaunchWsl(L\"ip link del wlan0\") == (DWORD)0;\r\n        TestCase({{L\"eth0\", {{L\"192.168.0.2\", 24}}, L\"192.168.0.1\", {{L\"fc00::2\", 64}}, L\"fc00::1\", false, 1500, true}});\r\n        const bool eth0Connected = GetInterfaceState(L\"eth0\").Up;\r\n\r\n        VERIFY_IS_TRUE(eth0Disconnected);\r\n        VERIFY_IS_TRUE(_wlan0Connected);\r\n        VERIFY_IS_TRUE(_wlan0Deleted);\r\n        VERIFY_IS_TRUE(eth0Connected);\r\n    }\r\n\r\n    TEST_METHOD(EnableLoopbackRouting)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Enable accept_local and route_localnet settings for eth0\r\n        wsl::shared::hns::VmNicCreatedNotification creationNotification{AdapterId};\r\n        RunGns(creationNotification, LxGnsMessageVmNicCreatedNotification);\r\n\r\n        // Verify the settings were enabled\r\n        const bool acceptLocalEnabled = LxsstuLaunchWsl(L\"sysctl net.ipv4.conf.eth0.accept_local | grep -w 1\") == (DWORD)0;\r\n        const bool routeLocalnetEnabled = LxsstuLaunchWsl(L\"sysctl net.ipv4.conf.eth0.route_localnet | grep -w 1\") == (DWORD)0;\r\n\r\n        VERIFY_IS_TRUE(acceptLocalEnabled);\r\n        VERIFY_IS_TRUE(routeLocalnetEnabled);\r\n    }\r\n\r\n    TEST_METHOD(InitializeLoopbackConfiguration)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Assume eth0 is the GELNIC\r\n        wsl::shared::hns::CreateDeviceRequest createDeviceRequest{wsl::shared::hns::DeviceType::Loopback, L\"loopback\", AdapterId};\r\n        RunGns(createDeviceRequest, LxGnsMessageCreateDeviceRequest);\r\n\r\n        // Verify the expected ip rules are present\r\n        const bool gelnicRuleTcpExists =\r\n            LxsstuLaunchWsl(L\"ip rule show | grep \\\"from all iif eth0 ipproto tcp lookup local\\\" | grep ^0:\") == (DWORD)0;\r\n        const bool gelnicRuleUdpExists =\r\n            LxsstuLaunchWsl(L\"ip rule show | grep \\\"from all iif eth0 ipproto tcp lookup local\\\" | grep ^0:\") == (DWORD)0;\r\n\r\n        const bool table127RuleTcpExists =\r\n            LxsstuLaunchWsl(L\"ip rule show | grep \\\"from all ipproto tcp lookup 127\\\" | grep ^1:\") == (DWORD)0;\r\n        const bool table127RuleUdpExists =\r\n            LxsstuLaunchWsl(L\"ip rule show | grep \\\"from all ipproto udp lookup 127\\\" | grep ^1:\") == (DWORD)0;\r\n        const bool table128RuleTcpExists =\r\n            LxsstuLaunchWsl(L\"ip rule show | grep \\\"from all ipproto tcp lookup 128\\\" | grep ^1:\") == (DWORD)0;\r\n        const bool table128RuleUdpExists =\r\n            LxsstuLaunchWsl(L\"ip rule show | grep \\\"from all ipproto udp lookup 128\\\" | grep ^1:\") == (DWORD)0;\r\n\r\n        const bool localTableRuleExists = LxsstuLaunchWsl(L\"ip rule show | grep \\\"from all lookup local\\\" | grep ^2:\") == (DWORD)0;\r\n\r\n        // Verify that the static neighbor entry was added for the gateway\r\n        const bool gatewayArpEntryExists =\r\n            LxsstuLaunchWsl(L\"ip neigh show dev eth0 | grep \\\"169\\\\.254\\\\.73\\\\.152 lladdr 00:11:22:33:44:55 PERMANENT\\\"\") == (DWORD)0;\r\n\r\n        // Verify route was added for destination 127.0.0.1, with preferred source 127.0.0.1\r\n        const bool routeToLoopbackRangeExists =\r\n            LxsstuLaunchWsl(\r\n                L\"ip route show table 127 | grep \\\"127\\\\.0\\\\.0\\\\.1 via 169\\\\.254\\\\.73\\\\.152 dev eth0\\\" | grep \"\r\n                L\"\\\"src 127\\\\.0\\\\.0\\\\.1\\\" | grep onlink\") == (DWORD)0;\r\n\r\n        const bool shutdownSuccessful = WslShutdown();\r\n\r\n        VERIFY_IS_TRUE(gelnicRuleTcpExists);\r\n        VERIFY_IS_TRUE(gelnicRuleUdpExists);\r\n        VERIFY_IS_TRUE(table127RuleTcpExists);\r\n        VERIFY_IS_TRUE(table127RuleUdpExists);\r\n        VERIFY_IS_TRUE(table128RuleTcpExists);\r\n        VERIFY_IS_TRUE(table128RuleUdpExists);\r\n        VERIFY_IS_TRUE(localTableRuleExists);\r\n\r\n        VERIFY_IS_TRUE(gatewayArpEntryExists);\r\n        VERIFY_IS_TRUE(routeToLoopbackRangeExists);\r\n\r\n        VERIFY_IS_TRUE(shutdownSuccessful);\r\n    }\r\n\r\n    TEST_METHOD(AddRemoveLoopbackRoutesv4)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const std::wstring interfaceName = L\"eth0\";\r\n        const std::vector<std::wstring> ipAddresses = {L\"127.0.0.1\", L\"127.0.0.2\"};\r\n\r\n        // Add routes on interface eth0 and verify that the routes were added in the custom local routing table (id 128)\r\n        for (const auto address : ipAddresses)\r\n        {\r\n            wsl::shared::hns::LoopbackRoutesRequest addRequest{interfaceName, wsl::shared::hns::OperationType::Create, AF_INET, address};\r\n            RunGns(addRequest, LxGnsMessageLoopbackRoutesRequest);\r\n        }\r\n\r\n        const bool firstRouteExists =\r\n            LxsstuLaunchWsl(\r\n                L\"ip route show table 128 | grep \\\"127\\\\.0\\\\.0\\\\.1 via 169\\\\.254\\\\.73\\\\.152 dev eth0\\\" | grep \\\"src \"\r\n                L\"127\\\\.0\\\\.0\\\\.1\\\" | grep onlink\") == (DWORD)0;\r\n        const bool secondRouteExists =\r\n            LxsstuLaunchWsl(\r\n                L\"ip route show table 128 | grep \\\"127\\\\.0\\\\.0\\\\.2 via 169\\\\.254\\\\.73\\\\.152 dev eth0\\\" | grep \\\"src \"\r\n                L\"127\\\\.0\\\\.0\\\\.2\\\" | grep onlink\") == (DWORD)0;\r\n\r\n        // Verify that the static neighbor entry was added for the gateway\r\n        const bool gatewayArpEntryExists =\r\n            LxsstuLaunchWsl(L\"ip neigh show dev eth0 | grep \\\"169\\\\.254\\\\.73\\\\.152 lladdr 00:11:22:33:44:55 PERMANENT\\\"\") == (DWORD)0;\r\n\r\n        // Verify that the routes are deleted\r\n        for (const auto address : ipAddresses)\r\n        {\r\n            wsl::shared::hns::LoopbackRoutesRequest removeRequest{interfaceName, wsl::shared::hns::OperationType::Remove, AF_INET, address};\r\n            RunGns(removeRequest, LxGnsMessageLoopbackRoutesRequest);\r\n        }\r\n\r\n        const bool firstRouteDeleted = LxsstuLaunchWsl(L\"ip route show table 128 | grep 127\\\\.0\\\\.0\\\\.1\") == (DWORD)1;\r\n        const bool secondRouteDeleted = LxsstuLaunchWsl(L\"ip route show table 128 | grep 127\\\\.0\\\\.0\\\\.2\") == (DWORD)1;\r\n\r\n        const bool shutdownSuccessful = WslShutdown();\r\n\r\n        VERIFY_IS_TRUE(firstRouteExists);\r\n        VERIFY_IS_TRUE(secondRouteExists);\r\n\r\n        VERIFY_IS_TRUE(gatewayArpEntryExists);\r\n\r\n        VERIFY_IS_TRUE(firstRouteDeleted);\r\n        VERIFY_IS_TRUE(secondRouteDeleted);\r\n\r\n        VERIFY_IS_TRUE(shutdownSuccessful);\r\n    }\r\n\r\n    /*\r\n        The test uses the \"ip route get\" command, which is equivalent to asking the OS what route it will take for a packet. It\r\n        functions as a small integration test.\r\n    */\r\n    TEST_METHOD(LoopbackGetRoute)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Verify that before configurations are applied, the route chosen for 127.0.0.1 tcp/udp uses the local routing table\r\n        const bool loopbackTcpUsesLocalTable =\r\n            LxsstuLaunchWsl(L\"ip route get from 127.0.0.1 127.0.0.1 ipproto tcp | grep local\") == (DWORD)0;\r\n        const bool loopbackUdpUsesLocalTable =\r\n            LxsstuLaunchWsl(L\"ip route get from 127.0.0.1 127.0.0.1 ipproto udp | grep local\") == (DWORD)0;\r\n\r\n        // Assume eth0 is the GELNIC\r\n        wsl::shared::hns::CreateDeviceRequest createDeviceRequest{wsl::shared::hns::DeviceType::Loopback, L\"loopback\", AdapterId};\r\n        RunGns(createDeviceRequest, LxGnsMessageCreateDeviceRequest);\r\n\r\n        // Verify that after configurations are applied, the route chosen for 127.0.0.1 tcp/udp is the desired one\r\n        const bool loopbackTcpUsesCustomTable =\r\n            LxsstuLaunchWsl(L\"ip route get from 127.0.0.1 127.0.0.1 ipproto tcp | grep \\\"via 169\\\\.254\\\\.73\\\\.152 dev eth0\\\"\") == (DWORD)0;\r\n        const bool loopbackUdpUsesCustomTable =\r\n            LxsstuLaunchWsl(L\"ip route get from 127.0.0.1 127.0.0.1 ipproto udp | grep \\\"via 169\\\\.254\\\\.73\\\\.152 dev eth0\\\"\") == (DWORD)0;\r\n\r\n        const bool shutdownSuccessful = WslShutdown();\r\n\r\n        VERIFY_IS_TRUE(loopbackTcpUsesLocalTable);\r\n        VERIFY_IS_TRUE(loopbackUdpUsesLocalTable);\r\n\r\n        VERIFY_IS_TRUE(loopbackTcpUsesCustomTable);\r\n        VERIFY_IS_TRUE(loopbackUdpUsesCustomTable);\r\n\r\n        VERIFY_IS_TRUE(shutdownSuccessful);\r\n    }\r\n\r\n    // Validate that adapter has an ip address, default route and DNS configuration in NAT mode\r\n    TEST_METHOD(NatConfiguration)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig());\r\n\r\n        const auto state = GetInterfaceState(L\"eth0\");\r\n        VERIFY_IS_FALSE(state.V4Addresses.empty());\r\n        VERIFY_IS_TRUE(state.Gateway.has_value());\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /etc/resolv.conf\", 0);\r\n        const std::wregex pattern(L\"(.|\\n)*nameserver [0-9\\\\. ]+(.|\\n)*\", std::regex::extended);\r\n\r\n        VERIFY_IS_TRUE(std::regex_match(out, pattern));\r\n    }\r\n\r\n    static void WriteNatConfiguration(const std::wstring& network, const std::wstring& gateway, const std::wstring& ipAddress)\r\n    {\r\n        using namespace wsl::windows::common;\r\n        const auto key = registry::OpenLxssMachineKey(KEY_SET_VALUE);\r\n\r\n        if (gateway == L\"delete\")\r\n        {\r\n            registry::DeleteValue(key.get(), L\"NatGatewayIpAddress\");\r\n        }\r\n        else if (!gateway.empty())\r\n        {\r\n            registry::WriteString(key.get(), nullptr, L\"NatGatewayIpAddress\", gateway.c_str());\r\n        }\r\n\r\n        if (network == L\"delete\")\r\n        {\r\n            registry::DeleteValue(key.get(), L\"NatNetwork\");\r\n        }\r\n        else if (!network.empty())\r\n        {\r\n            registry::WriteString(key.get(), nullptr, L\"NatNetwork\", network.c_str());\r\n        }\r\n\r\n        const auto userKey = registry::OpenLxssUserKey();\r\n        if (ipAddress == L\"delete\")\r\n        {\r\n            registry::DeleteValue(userKey.get(), L\"NatIpAddress\");\r\n        }\r\n        else if (!ipAddress.empty())\r\n        {\r\n            registry::WriteString(userKey.get(), nullptr, L\"NatIpAddress\", ipAddress.c_str());\r\n        }\r\n    }\r\n\r\n    struct NatNetworkingConfiguration\r\n    {\r\n        std::wstring networkRange;\r\n        std::wstring gatewayIpAddress;\r\n        std::wstring ipAddress;\r\n    };\r\n\r\n    static NatNetworkingConfiguration GetNatConfiguration()\r\n    {\r\n        using namespace wsl::windows::common;\r\n        const auto key = registry::OpenLxssMachineKey();\r\n\r\n        const auto userKey = registry::OpenLxssUserKey();\r\n\r\n        return {\r\n            registry::ReadString(key.get(), nullptr, L\"NatNetwork\", L\"\"),\r\n            registry::ReadString(key.get(), nullptr, L\"NatGatewayIpAddress\", L\"\"),\r\n            registry::ReadString(userKey.get(), nullptr, L\"NatIpAddress\", L\"\")};\r\n    }\r\n\r\n    static void ResetWslNetwork()\r\n    {\r\n        // N.B. This must be kept in sync with the network IDs in NatNetworking.cpp.\r\n        GUID natNetworkId;\r\n        if (!AreExperimentalNetworkingFeaturesSupported() || !IsHyperVFirewallSupported())\r\n        {\r\n            natNetworkId = {0xb95d0c5e, 0x57d4, 0x412b, {0xb5, 0x71, 0x18, 0xa8, 0x1a, 0x16, 0xe0, 0x05}};\r\n        }\r\n        else\r\n        {\r\n            natNetworkId = {0x790e58b4, 0x7939, 0x4434, {0x93, 0x58, 0x89, 0xae, 0x7d, 0xdb, 0xe8, 0x7e}};\r\n        }\r\n\r\n        wil::unique_cotaskmem_string error;\r\n        const auto hr = HcnDeleteNetwork(natNetworkId, &error);\r\n        VERIFY_SUCCEEDED(hr, error.get());\r\n    }\r\n\r\n    TEST_METHOD(NatInvalidRange)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig());\r\n        WriteNatConfiguration(L\"InvalidRange\", {}, {L\"delete\"});\r\n        ResetWslNetwork();\r\n        RestartWslService();\r\n\r\n        const auto state = GetInterfaceState(\r\n            L\"eth0\",\r\n            L\"wsl: Failed to create virtual network with address range: 'InvalidRange', created new network with range: \"\r\n            L\"'*.*.*.*/*', *.*\");\r\n\r\n        VERIFY_IS_FALSE(state.V4Addresses.empty());\r\n        VERIFY_IS_TRUE(state.Gateway.has_value());\r\n\r\n        const auto networkConfiguration = GetNatConfiguration();\r\n        VERIFY_IS_FALSE(networkConfiguration.networkRange.empty());\r\n        VERIFY_ARE_EQUAL(state.V4Addresses[0].Address, networkConfiguration.ipAddress);\r\n        VERIFY_ARE_EQUAL(state.Gateway.value_or(L\"\"), networkConfiguration.gatewayIpAddress);\r\n    }\r\n\r\n    TEST_METHOD(NatInvalidGateway)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig());\r\n        WriteNatConfiguration({}, L\"InvalidGateway\", {});\r\n        ResetWslNetwork();\r\n        RestartWslService();\r\n\r\n        const auto state = GetInterfaceState(\r\n            L\"eth0\",\r\n            L\"wsl: Failed to create virtual network with address range: '*.*.*.*/*', created new network with range: \"\r\n            L\"'*.*.*.*/*', *.*\");\r\n\r\n        VERIFY_IS_FALSE(state.V4Addresses.empty());\r\n        VERIFY_IS_TRUE(state.Gateway.has_value());\r\n\r\n        const auto networkConfiguration = GetNatConfiguration();\r\n        VERIFY_IS_FALSE(networkConfiguration.networkRange.empty());\r\n        VERIFY_ARE_EQUAL(state.V4Addresses[0].Address, networkConfiguration.ipAddress);\r\n        VERIFY_ARE_EQUAL(state.Gateway.value_or(L\"\"), networkConfiguration.gatewayIpAddress);\r\n    }\r\n\r\n    TEST_METHOD(NatInvalidAddress)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig());\r\n\r\n        const auto previousConfiguration = GetNatConfiguration();\r\n        WriteNatConfiguration({}, {}, L\"InvalidAddress\");\r\n        ResetWslNetwork();\r\n        RestartWslService();\r\n\r\n        const auto state = GetInterfaceState(\r\n            L\"eth0\", L\"wsl: Failed to create network endpoint with address: 'InvalidAddress', assigned new address: '*.*.*.*'*\");\r\n        VERIFY_IS_FALSE(state.V4Addresses.empty());\r\n        VERIFY_IS_TRUE(state.Gateway.has_value());\r\n\r\n        const auto networkConfiguration = GetNatConfiguration();\r\n        // The network range should be the same\r\n        VERIFY_ARE_EQUAL(networkConfiguration.networkRange, previousConfiguration.networkRange);\r\n\r\n        VERIFY_IS_FALSE(networkConfiguration.networkRange.empty());\r\n        VERIFY_ARE_EQUAL(state.V4Addresses[0].Address, networkConfiguration.ipAddress);\r\n        VERIFY_ARE_EQUAL(state.Gateway.value_or(L\"\"), networkConfiguration.gatewayIpAddress);\r\n    }\r\n\r\n    struct unique_kill_process\r\n    {\r\n        unique_kill_process()\r\n        {\r\n        }\r\n        unique_kill_process(wil::unique_handle&& process) : m_process(std::move(process))\r\n        {\r\n        }\r\n\r\n        unique_kill_process(unique_kill_process&&) = default;\r\n        unique_kill_process& operator=(unique_kill_process&&) = default;\r\n\r\n        unique_kill_process& operator=(const unique_kill_process&) = delete;\r\n        unique_kill_process(const unique_kill_process&) = delete;\r\n\r\n        ~unique_kill_process()\r\n        {\r\n            reset();\r\n        }\r\n\r\n        void reset()\r\n        {\r\n            if (m_process)\r\n            {\r\n                TerminateProcess(m_process.get(), 0);\r\n                m_process.reset();\r\n            }\r\n        }\r\n\r\n        wil::unique_handle m_process;\r\n    };\r\n\r\n    static void VerifyLoopbackHostToGuest(const std::wstring& address, int protocol, std::chrono::duration<int> timeout = std::chrono::minutes(5))\r\n    {\r\n        LogInfo(\"VerifyLoopbackHostToGuest(address=%ls, protocol=%d)\", address.c_str(), protocol);\r\n\r\n        SOCKADDR_INET addr = wsl::windows::common::string::StringToSockAddrInet(address);\r\n        SS_PORT(&addr) = htons(1234);\r\n\r\n        {\r\n            // Create listener in guest\r\n            std::optional<GuestListener> listener;\r\n\r\n            // Note: If a previous test case had the same port bound it can take a bit of time for the port to be released on the host.\r\n            auto createListener = [&]() { listener.emplace(addr, protocol); };\r\n            try\r\n            {\r\n                wsl::shared::retry::RetryWithTimeout<void>(\r\n                    createListener, std::chrono::seconds(1), timeout, []() { return wil::ResultFromCaughtException() == E_FAIL; });\r\n            }\r\n            catch (...)\r\n            {\r\n                LogError(\"Failed to bind %ls in the guest, 0x%x\", address.c_str(), wil::ResultFromCaughtException());\r\n                VERIFY_FAIL();\r\n            }\r\n\r\n            // If the guest is listening on any address, connect via loopback.\r\n            const auto ipAddress = (addr.si_family == AF_INET) ? reinterpret_cast<const void*>(&addr.Ipv4.sin_addr)\r\n                                                               : reinterpret_cast<const void*>(&addr.Ipv6.sin6_addr);\r\n            if (INET_IS_ADDR_UNSPECIFIED(addr.si_family, ipAddress))\r\n            {\r\n                INETADDR_SETLOOPBACK(reinterpret_cast<PSOCKADDR>(&addr));\r\n                SS_PORT(&addr) = htons(1234);\r\n            }\r\n\r\n            // Connect from a client on the host\r\n            const wil::unique_socket clientSocket(socket(addr.si_family, (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM, protocol));\r\n            VERIFY_ARE_NOT_EQUAL(clientSocket.get(), INVALID_SOCKET);\r\n            // The WSL2 loopback relay may have a one second delay after creation.\r\n\r\n            auto pred = [&]() {\r\n                if (protocol == IPPROTO_UDP)\r\n                {\r\n                    const char buffer = 'A';\r\n                    THROW_HR_IF(\r\n                        E_FAIL,\r\n                        sendto(clientSocket.get(), &buffer, sizeof(buffer), 0, reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr)) !=\r\n                            sizeof(buffer));\r\n                }\r\n                else\r\n                {\r\n                    THROW_HR_IF(E_FAIL, connect(clientSocket.get(), reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr)) == SOCKET_ERROR);\r\n                }\r\n            };\r\n\r\n            try\r\n            {\r\n                wsl::shared::retry::RetryWithTimeout<void>(pred, std::chrono::seconds(1), timeout);\r\n            }\r\n            catch (...)\r\n            {\r\n                LogError(\"Timed out trying to connect to %ls\", address.c_str());\r\n                VERIFY_FAIL();\r\n            }\r\n\r\n            // Verify the connection was accepted on the listener\r\n            listener->AcceptConnection();\r\n        }\r\n\r\n        // Wait until the guest has released its port\r\n        VerifyNotBound(addr, addr.si_family, protocol);\r\n    }\r\n\r\n    TEST_METHOD(HostToGuestLoopback)\r\n    {\r\n        BEGIN_TEST_METHOD_PROPERTIES()\r\n            TEST_METHOD_PROPERTY(L\"Data:NetConfig\", L\"{1, 2, 3, 4}\")\r\n        END_TEST_METHOD_PROPERTIES()\r\n\r\n        // All networking modes for both WSL1/2 are expected to support TCP/IPv4 host to guest loopback by default.\r\n        int networkingModeVal = 0;\r\n        WEX::TestExecution::TestData::TryGetValue(L\"NetConfig\", networkingModeVal);\r\n        auto networkingMode = static_cast<wsl::core::NetworkingMode>(networkingModeVal);\r\n        switch (networkingMode)\r\n        {\r\n        case wsl::core::NetworkingMode::Bridged:\r\n            WINDOWS_11_TEST_ONLY();\r\n            __fallthrough;\r\n        case wsl::core::NetworkingMode::Mirrored:\r\n        case wsl::core::NetworkingMode::VirtioProxy:\r\n            WSL2_TEST_ONLY();\r\n            break;\r\n        }\r\n\r\n        LogInfo(\"HostToGuestLoopback (networkingMode=%hs)\", ToString(networkingMode));\r\n        WslConfigChange config(LxssGenerateTestConfig({.networkingMode = networkingMode, .vmSwitch = L\"Default Switch\"}));\r\n        VerifyLoopbackHostToGuest(L\"127.0.0.1\", IPPROTO_TCP);\r\n        VerifyLoopbackHostToGuest(L\"0.0.0.0\", IPPROTO_TCP);\r\n    }\r\n\r\n    static void VerifyLoopbackGuestToHost(const std::wstring& address, int protocol)\r\n    {\r\n        LogInfo(\"VerifyLoopbackGuestToHost(address=%ls, protocol=%d)\", address.c_str(), protocol);\r\n\r\n        SOCKADDR_INET addr = wsl::windows::common::string::StringToSockAddrInet(address);\r\n        SS_PORT(&addr) = htons(1234);\r\n\r\n        // Create a listener on the host\r\n        const wil::unique_socket listenSocket(socket(addr.si_family, (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM, protocol));\r\n        VERIFY_ARE_NOT_EQUAL(listenSocket.get(), INVALID_SOCKET);\r\n        VERIFY_ARE_NOT_EQUAL(bind(listenSocket.get(), reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr)), SOCKET_ERROR);\r\n        if (protocol == IPPROTO_TCP)\r\n        {\r\n            VERIFY_ARE_NOT_EQUAL(listen(listenSocket.get(), SOMAXCONN), SOCKET_ERROR);\r\n        }\r\n\r\n        // Connect from a client in the guest\r\n        GuestClient client(addr, protocol);\r\n\r\n        // Accept the connection on the listener\r\n        SOCKADDR_INET remoteAddr{};\r\n        int remoteAddrLen = sizeof(remoteAddr);\r\n        if (protocol == IPPROTO_UDP)\r\n        {\r\n            char buffer[2048];\r\n            int Timeout = 3000;\r\n            VERIFY_ARE_NOT_EQUAL(setsockopt(listenSocket.get(), SOL_SOCKET, SO_RCVTIMEO, (char*)&Timeout, sizeof(Timeout)), SOCKET_ERROR);\r\n            VERIFY_ARE_NOT_EQUAL(\r\n                recvfrom(listenSocket.get(), buffer, sizeof(buffer), 0, reinterpret_cast<SOCKADDR*>(&remoteAddr), &remoteAddrLen), SOCKET_ERROR);\r\n        }\r\n        else\r\n        {\r\n            // TODO: this accept call needs to timeout to avoid indefinite wait\r\n            const wil::unique_socket acceptSocket(accept(listenSocket.get(), reinterpret_cast<SOCKADDR*>(&remoteAddr), &remoteAddrLen));\r\n            VERIFY_ARE_NOT_EQUAL(acceptSocket.get(), INVALID_SOCKET);\r\n        }\r\n    }\r\n\r\n    static void VerifyLoopbackGuestToGuest(const std::wstring& address, int protocol)\r\n    {\r\n        LogInfo(\"VerifyLoopbackGuestToGuest(address=%ls, protocol=%d)\", address.c_str(), protocol);\r\n\r\n        SOCKADDR_INET addr = wsl::windows::common::string::StringToSockAddrInet(address);\r\n        SS_PORT(&addr) = htons(1234);\r\n\r\n        {\r\n            std::optional<GuestListener> listener;\r\n\r\n            auto createListener = [&]() { listener.emplace(addr, protocol); };\r\n            try\r\n            {\r\n                wsl::shared::retry::RetryWithTimeout<void>(createListener, std::chrono::seconds(1), std::chrono::minutes(1), []() {\r\n                    return wil::ResultFromCaughtException() == E_FAIL;\r\n                });\r\n            }\r\n            catch (...)\r\n            {\r\n                LogError(\"Failed to bind %ls\", address.c_str());\r\n                VERIFY_FAIL();\r\n            }\r\n\r\n            // Create listener in guest\r\n\r\n            // Connect from a client in the guest\r\n            GuestClient client(addr, protocol);\r\n\r\n            // Verify the connection was accepted on the listener\r\n            listener->AcceptConnection();\r\n        }\r\n\r\n        // Wait until the guest has released its port\r\n        VerifyNotBound(addr, addr.si_family, protocol);\r\n    }\r\n\r\n    static void VerifyLoopbackConnectivity(const std::wstring& address)\r\n    {\r\n        // Verify guest to host\r\n        VerifyLoopbackGuestToHost(address, IPPROTO_UDP);\r\n        VerifyLoopbackGuestToHost(address, IPPROTO_TCP);\r\n\r\n        // Verify host to guest\r\n        VerifyLoopbackHostToGuest(address, IPPROTO_UDP);\r\n        VerifyLoopbackHostToGuest(address, IPPROTO_TCP);\r\n\r\n        // Verify guest to guest\r\n        VerifyLoopbackGuestToGuest(address, IPPROTO_UDP);\r\n        VerifyLoopbackGuestToGuest(address, IPPROTO_TCP);\r\n    }\r\n\r\n    static wil::unique_socket BindHostPort(uint16_t Port, int Type, int Protocol, bool ExpectSuccess, bool Ipv6 = false, bool Localhost = false)\r\n    {\r\n        int AddressFamily{};\r\n        const SOCKADDR* Address{};\r\n        int AddressSize{};\r\n        SOCKADDR_IN Address4{};\r\n        SOCKADDR_IN6 Address6{};\r\n        if (Ipv6)\r\n        {\r\n            AddressFamily = AF_INET6;\r\n            Address6.sin6_family = AF_INET6;\r\n            Address6.sin6_port = htons(Port);\r\n            if (Localhost)\r\n            {\r\n                Address6.sin6_addr = IN6ADDR_LOOPBACK_INIT;\r\n            }\r\n            Address = reinterpret_cast<SOCKADDR*>(&Address6);\r\n            AddressSize = sizeof(Address6);\r\n        }\r\n        else\r\n        {\r\n            AddressFamily = AF_INET;\r\n            Address4.sin_family = AF_INET;\r\n            Address4.sin_port = htons(Port);\r\n            if (Localhost)\r\n            {\r\n                Address4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r\n            }\r\n            Address = reinterpret_cast<SOCKADDR*>(&Address4);\r\n            AddressSize = sizeof(Address4);\r\n        }\r\n\r\n        wil::unique_socket listenSocket(socket(AddressFamily, Type, Protocol));\r\n        VERIFY_IS_TRUE(!!listenSocket);\r\n\r\n        VERIFY_ARE_EQUAL(bind(listenSocket.get(), Address, AddressSize) != SOCKET_ERROR, ExpectSuccess);\r\n\r\n        return listenSocket;\r\n    }\r\n\r\n    static std::tuple<unique_kill_process, bool, wil::unique_handle> BindGuestPortHelper(std::wstring_view BindSpec)\r\n    {\r\n        auto [stdErrRead, stdErrWrite] = CreateSubprocessPipe(false, true);\r\n        auto [stdOutRead, stdOutWrite] = CreateSubprocessPipe(false, true);\r\n        const std::wstring wslCmd = L\"socat -dd \" + std::wstring(BindSpec) + L\" STDOUT\";\r\n        auto cmd = LxssGenerateWslCommandLine(wslCmd.data());\r\n\r\n        auto process = LxsstuStartProcess(cmd.data(), nullptr, stdOutWrite.get(), stdErrWrite.get());\r\n        stdErrWrite.reset();\r\n        stdOutWrite.reset();\r\n\r\n        const std::map<std::string_view, bool> patterns = {\r\n            {\"listening on\", true},\r\n            {\"Address already in use\", false},\r\n        };\r\n\r\n        bool success = false;\r\n        bool finished = false;\r\n        DWORD writeOffset = 0;\r\n        constexpr DWORD readOffset = 0;\r\n        std::string output(512, '\\0');\r\n        while (!finished)\r\n        {\r\n            DWORD bytesRead = 0;\r\n            if (!ReadFile(stdErrRead.get(), output.data() + writeOffset, static_cast<DWORD>(output.size() - writeOffset), &bytesRead, nullptr))\r\n            {\r\n                break;\r\n            }\r\n\r\n            writeOffset += bytesRead;\r\n            LogInfo(\"output %hs\", output.c_str());\r\n            std::string_view outputView = output;\r\n            for (const auto& pattern : patterns)\r\n            {\r\n                DWORD patternOffset = readOffset;\r\n                auto matchString = pattern.first;\r\n                while (!finished && (patternOffset + matchString.length() < writeOffset))\r\n                {\r\n                    if (outputView.substr(patternOffset).starts_with(matchString))\r\n                    {\r\n                        finished = true;\r\n                        success = pattern.second;\r\n                    }\r\n                    patternOffset++;\r\n                }\r\n            }\r\n        }\r\n\r\n        VERIFY_IS_TRUE(finished);\r\n\r\n        return std::tuple(std::move(process), success, std::move(stdOutRead));\r\n    }\r\n\r\n    static std::tuple<unique_kill_process, wil::unique_handle> BindGuestPort(std::wstring_view BindSpec, bool ExpectSuccess)\r\n    {\r\n        auto [process, success, read] = BindGuestPortHelper(BindSpec);\r\n\r\n        VERIFY_ARE_EQUAL(ExpectSuccess, success);\r\n\r\n        return std::tuple(std::move(process), std::move(read));\r\n    }\r\n\r\n    // Bind port 0 in the guest and return the process handle and the kernel-assigned port.\r\n    // Uses socat's -dd output to extract the actual port from the \"listening on\" line.\r\n    static std::tuple<unique_kill_process, uint16_t> BindGuestPortZero(bool Ipv6 = false)\r\n    {\r\n        auto [stdErrRead, stdErrWrite] = CreateSubprocessPipe(false, true);\r\n        const std::wstring protocol = Ipv6 ? L\"TCP6-LISTEN:0\" : L\"TCP4-LISTEN:0\";\r\n        const std::wstring wslCmd = L\"socat -dd \" + protocol + L\" STDOUT\";\r\n        auto cmd = LxssGenerateWslCommandLine(wslCmd.data());\r\n\r\n        auto process = LxsstuStartProcess(cmd.data(), nullptr, nullptr, stdErrWrite.get());\r\n        stdErrWrite.reset();\r\n\r\n        // Parse the assigned port from socat's debug output.\r\n        // socat -dd prints a line like: \"... listening on AF=2 0.0.0.0:PORT\"\r\n        std::string output(512, '\\0');\r\n        DWORD writeOffset = 0;\r\n        uint16_t assignedPort = 0;\r\n        bool found = false;\r\n\r\n        while (!found)\r\n        {\r\n            // Grow the buffer if full to avoid zero-byte reads and infinite loops.\r\n            if (writeOffset == output.size())\r\n            {\r\n                output.resize(output.size() * 2);\r\n            }\r\n\r\n            DWORD bytesRead = 0;\r\n            if (!ReadFile(stdErrRead.get(), output.data() + writeOffset, static_cast<DWORD>(output.size() - writeOffset), &bytesRead, nullptr))\r\n            {\r\n                break;\r\n            }\r\n\r\n            if (bytesRead == 0)\r\n            {\r\n                break;\r\n            }\r\n\r\n            writeOffset += bytesRead;\r\n            LogInfo(\"output %hs\", output.c_str());\r\n            std::string_view outputView(output.data(), writeOffset);\r\n            auto pos = outputView.find(\"listening on\");\r\n            if (pos != std::string_view::npos)\r\n            {\r\n                // Limit the search to just the \"listening on\" line to avoid\r\n                // matching colons in subsequent debug lines socat may emit.\r\n                auto lineEnd = outputView.find('\\n', pos);\r\n                auto line = outputView.substr(pos, lineEnd != std::string_view::npos ? lineEnd - pos : std::string_view::npos);\r\n\r\n                // Find the last ':' before the port digits. For IPv6, socat outputs\r\n                // \"listening on AF=10 :::PORT\", so using find() would match the\r\n                // first colon in the address instead of the port separator.\r\n                auto colonPos = line.rfind(':');\r\n                if (colonPos != std::string_view::npos)\r\n                {\r\n                    auto portStr = line.substr(colonPos + 1);\r\n                    auto end = portStr.find_first_not_of(\"0123456789\");\r\n                    if (end != std::string_view::npos)\r\n                    {\r\n                        portStr = portStr.substr(0, end);\r\n                    }\r\n\r\n                    if (portStr.empty())\r\n                    {\r\n                        continue;\r\n                    }\r\n\r\n                    assignedPort = static_cast<uint16_t>(std::stoi(std::string(portStr)));\r\n                    found = true;\r\n                }\r\n            }\r\n        }\r\n\r\n        VERIFY_IS_TRUE(found);\r\n        VERIFY_IS_TRUE(assignedPort > 0);\r\n        LogInfo(\"Port-0 bind resolved to port %u\", assignedPort);\r\n\r\n        return {std::move(process), assignedPort};\r\n    }\r\n\r\n    static void VerifyPortZeroBindIsTracked(bool verifyRelease = true)\r\n    {\r\n        // Make sure the VM doesn't time out while we wait for async port resolution\r\n        WslKeepAlive keepAlive;\r\n\r\n        // Bind port 0 in the guest - the kernel assigns an ephemeral port.\r\n        // The port tracker intercepts the bind() via seccomp and defers lookup\r\n        // to a background thread that resolves the actual port via getsockname().\r\n        auto [guestProcess, assignedPort] = BindGuestPortZero();\r\n\r\n        // The port-0 resolution is asynchronous (deferred to a background thread).\r\n        // Retry until the host port tracker registers the port, blocking the host bind.\r\n        VERIFY_NO_THROW(wsl::shared::retry::RetryWithTimeout<void>(\r\n            [&assignedPort]() {\r\n                wil::unique_socket sock(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n                THROW_LAST_ERROR_IF(!sock);\r\n\r\n                SOCKADDR_IN addr{};\r\n                addr.sin_family = AF_INET;\r\n                addr.sin_port = htons(assignedPort);\r\n                THROW_HR_IF(E_FAIL, bind(sock.get(), reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr)) != SOCKET_ERROR);\r\n            },\r\n            std::chrono::seconds(1),\r\n            std::chrono::seconds(30)));\r\n\r\n        if (!verifyRelease)\r\n        {\r\n            return;\r\n        }\r\n\r\n        // Kill the guest process so the port tracker releases the port.\r\n        guestProcess.reset();\r\n\r\n        // Retry until the host can bind the port again, confirming it was released.\r\n        VERIFY_NO_THROW(wsl::shared::retry::RetryWithTimeout<void>(\r\n            [&assignedPort]() {\r\n                wil::unique_socket sock(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n                THROW_LAST_ERROR_IF(!sock);\r\n\r\n                SOCKADDR_IN addr{};\r\n                addr.sin_family = AF_INET;\r\n                addr.sin_port = htons(assignedPort);\r\n                THROW_HR_IF(E_FAIL, bind(sock.get(), reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr)) == SOCKET_ERROR);\r\n            },\r\n            std::chrono::seconds(1),\r\n            std::chrono::minutes(2)));\r\n    }\r\n\r\n    template <typename T>\r\n    static void VerifyNotBound(T& Address, int AddressFamily, int Protocol)\r\n    {\r\n        const wil::unique_socket listenSocket(socket(AddressFamily, (Protocol == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM, Protocol));\r\n        VERIFY_IS_TRUE(!!listenSocket);\r\n\r\n        const auto timeout = std::chrono::steady_clock::now() + std::chrono::minutes(2);\r\n\r\n        bool bound = false;\r\n        while (!bound && std::chrono::steady_clock::now() < timeout)\r\n        {\r\n            bound = bind(listenSocket.get(), reinterpret_cast<SOCKADDR*>(&Address), sizeof(Address)) != SOCKET_ERROR;\r\n            std::this_thread::sleep_for(std::chrono::seconds(1));\r\n        }\r\n\r\n        VERIFY_IS_TRUE(bound);\r\n    }\r\n\r\n    static void VerifyNotBoundLoopback(uint16_t port, bool Ipv6)\r\n    {\r\n        if (Ipv6)\r\n        {\r\n            SOCKADDR_IN6 Address{};\r\n            Address.sin6_family = AF_INET6;\r\n            Address.sin6_port = htons(port);\r\n            Address.sin6_addr = IN6ADDR_LOOPBACK_INIT;\r\n\r\n            VerifyNotBound(Address, Address.sin6_family, IPPROTO_TCP);\r\n        }\r\n        else\r\n        {\r\n            SOCKADDR_IN Address{};\r\n            Address.sin_family = AF_INET;\r\n            Address.sin_port = htons(port);\r\n            Address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r\n\r\n            VerifyNotBound(Address, Address.sin_family, IPPROTO_TCP);\r\n        }\r\n    }\r\n\r\n    struct\r\n    {\r\n        wchar_t const* const SocatServer = {};\r\n        bool const Ipv6 = false;\r\n        bool const expectRelay = true;\r\n    } LoopbackBindTests[5] = {\r\n        {\r\n            .SocatServer = L\"TCP4-LISTEN:1234,bind=127.0.0.1\",\r\n        },\r\n        {\r\n            .SocatServer = L\"TCP4-LISTEN:1234,bind=127.0.0.2\",\r\n            .expectRelay = false,\r\n        },\r\n        {\r\n            .SocatServer = L\"TCP4-LISTEN:1234,bind=0.0.0.0\",\r\n        },\r\n        {\r\n            .SocatServer = L\"TCP6-LISTEN:1234,bind=::1\",\r\n            .Ipv6 = true,\r\n        },\r\n        {\r\n            .SocatServer = L\"TCP6-LISTEN:1234,bind=::\",\r\n            .Ipv6 = true,\r\n        },\r\n    };\r\n\r\n    void NatGuestPortIsReleased()\r\n    {\r\n        constexpr uint16_t port = 1234;\r\n        for (auto const& test : LoopbackBindTests)\r\n        {\r\n            {\r\n                auto guestProcess = BindGuestPort(test.SocatServer, true);\r\n                std::this_thread::sleep_for(std::chrono::seconds(3));\r\n                BindHostPort(port, SOCK_STREAM, IPPROTO_TCP, !test.expectRelay, test.Ipv6, true);\r\n            }\r\n\r\n            VerifyNotBoundLoopback(port, test.Ipv6);\r\n        }\r\n    }\r\n\r\n    void NatHostPortCantBeBoundByGuest()\r\n    {\r\n        constexpr uint16_t port = 1234;\r\n        for (auto const& test : LoopbackBindTests)\r\n        {\r\n            {\r\n                auto hostPort = BindHostPort(port, SOCK_STREAM, IPPROTO_TCP, true, test.Ipv6, true);\r\n                BindGuestPort(test.SocatServer, !test.expectRelay);\r\n            }\r\n\r\n            VerifyNotBoundLoopback(port, test.Ipv6);\r\n        }\r\n    }\r\n\r\n    static void NatReusePortOnGuest()\r\n    {\r\n        constexpr uint16_t port = 1234;\r\n        {\r\n            auto [guestLocal, write] = BindGuestPort(L\"TCP4-LISTEN:1234,bind=127.0.0.1,reuseport\", true);\r\n            BindHostPort(port, SOCK_STREAM, IPPROTO_TCP, false, false, true);\r\n            auto guestWild = BindGuestPort(L\"TCP4-LISTEN:1234,bind=0.0.0.0,reuseport\", true);\r\n            BindHostPort(port, SOCK_STREAM, IPPROTO_TCP, false, false, true);\r\n            guestLocal.reset();\r\n            BindHostPort(port, SOCK_STREAM, IPPROTO_TCP, false, false, true);\r\n        }\r\n\r\n        VerifyNotBoundLoopback(port, false);\r\n    }\r\n\r\n    static void ValidateLocalhostRelayTraffic(ADDRESS_FAMILY addressFamily)\r\n    {\r\n        THROW_HR_IF(E_INVALIDARG, addressFamily != AF_INET && addressFamily != AF_INET6);\r\n\r\n        // Bind a port in the guest.\r\n        auto [guestProcess, read] =\r\n            BindGuestPort(addressFamily == AF_INET6 ? L\"TCP6-LISTEN:1234,bind=::1\" : L\"TCP4-LISTEN:1234,bind=127.0.0.1\", true);\r\n\r\n        // Connect to the port via the localhost relay\r\n        wil::unique_socket hostSocket;\r\n        SOCKADDR_INET addr{};\r\n        addr.si_family = addressFamily;\r\n        INETADDR_SETLOOPBACK((PSOCKADDR)&addr);\r\n        SS_PORT(&addr) = htons(1234);\r\n\r\n        auto pred = [&]() {\r\n            hostSocket.reset(socket(addressFamily, SOCK_STREAM, IPPROTO_TCP));\r\n            THROW_HR_IF(E_ABORT, !hostSocket);\r\n            THROW_HR_IF(E_FAIL, connect(hostSocket.get(), reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr)) == SOCKET_ERROR);\r\n        };\r\n\r\n        try\r\n        {\r\n            wsl::shared::retry::RetryWithTimeout<void>(pred, std::chrono::seconds(1), std::chrono::minutes(1));\r\n        }\r\n        catch (...)\r\n        {\r\n            LogError(\"Timed out trying to connect to relay, 0x%x\", wil::ResultFromCaughtException());\r\n            VERIFY_FAIL();\r\n        }\r\n\r\n        // Send data from host to guest.\r\n        constexpr auto buffer = \"test-relay-buffer\";\r\n        VERIFY_ARE_EQUAL(send(hostSocket.get(), buffer, static_cast<int>(strlen(buffer)), 0), strlen(buffer));\r\n\r\n        {\r\n            // Validate that the guest received the correct data.\r\n            std::string content(strlen(buffer), '\\0');\r\n\r\n            DWORD totalRead{};\r\n            while (totalRead < content.size())\r\n            {\r\n                DWORD bytesRead{};\r\n                VERIFY_IS_TRUE(ReadFile(read.get(), content.data() + totalRead, static_cast<DWORD>(content.size()) - totalRead, &bytesRead, nullptr));\r\n                LogInfo(\"Read %lu bytes\", bytesRead);\r\n\r\n                totalRead += bytesRead;\r\n            }\r\n            VERIFY_ARE_EQUAL(content, buffer);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(NatLocalhostRelay)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WslKeepAlive keepAlive;\r\n\r\n        ValidateLocalhostRelayTraffic(AF_INET);\r\n        ValidateLocalhostRelayTraffic(AF_INET6);\r\n    }\r\n\r\n    TEST_METHOD(NatLocalhostRelayNoIpv6)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig({.kernelCommandLine = L\"ipv6.disable=1\"}));\r\n        WslKeepAlive keepAlive;\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -f /proc/net/tcp6\"), 1L);\r\n        ValidateLocalhostRelayTraffic(AF_INET);\r\n    }\r\n\r\n    static void TestNonRootNamespaceEphemeralBind()\r\n    {\r\n        // Get the forwarding state.\r\n        auto [oldIpForwardState, _1] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/sys/net/ipv4/ip_forward\", 0);\r\n        std::wstring restoreIpForwardCommand = std::format(L\"sysctl -w net.ipv4.ip_forward={}\", oldIpForwardState.c_str());\r\n\r\n        // Ensure the ephemeral port range configured in the non-root networking namespace does not\r\n        // overlap with the ephemeral port range in the root networking namespace (use the 300 ports\r\n        // preceding the root networking namespace ephemeral port range).\r\n        auto [start, _2] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/sys/net/ipv4/ip_local_port_range | cut -f1\", 0);\r\n        start.pop_back();\r\n        int ephemeralRangeStart = std::stoi(start);\r\n\r\n        int ephemeralRangeEnd = ephemeralRangeStart - 1;\r\n        ephemeralRangeStart = ephemeralRangeEnd - 299;\r\n        VERIFY_IS_GREATER_THAN(ephemeralRangeStart, 1024);\r\n        VERIFY_IS_LESS_THAN_OR_EQUAL(ephemeralRangeEnd, UINT16_MAX);\r\n        const std::wstring ephemeralRangeCommand =\r\n            std::format(L\"ip netns exec testns sysctl -w net.ipv4.ip_local_port_range=\\\"{} {}\\\"\", ephemeralRangeStart, ephemeralRangeEnd);\r\n\r\n        // Clean up the below configurations.\r\n        auto revertConfig = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&restoreIpForwardCommand] {\r\n            LxsstuLaunchWsl(restoreIpForwardCommand.c_str());\r\n            LxsstuLaunchWsl(L\"--system --user root nft flush chain nat POSTROUTING\");\r\n            LxsstuLaunchWsl(L\"ip link delete veth-test-br\");\r\n            LxsstuLaunchWsl(L\"ip link delete testbridge\");\r\n            LxsstuLaunchWsl(L\"ip netns delete testns\");\r\n        });\r\n\r\n        // Set up a networking namespace and provide it external network access via a bridge, veth\r\n        // pair, SRCNAT iptables rule and forwarding.\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip netns add testns\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(ephemeralRangeCommand.c_str()), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link add testbridge type bridge\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link add veth-test type veth peer name veth-test-br\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set veth-test netns testns\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set veth-test-br master testbridge\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip -n testns link set veth-test up\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set veth-test-br up\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set testbridge up\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip -n testns addr add 192.168.15.2/24 dev veth-test\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip addr add 192.168.15.1/24 dev testbridge\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip -n testns route add default via 192.168.15.1 dev veth-test\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft add table nat\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft \\\"add chain nat POSTROUTING { type nat hook postrouting priority srcnat; }\\\"\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft add rule nat POSTROUTING ip saddr 192.168.15.0/24 oif != testbridge masquerade\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"sysctl -w net.ipv4.ip_forward=1\"), 0);\r\n\r\n        // Verify we have connectivity from the networking namespace when using ephemeral port selection.\r\n        auto [output, warnings] =\r\n            LxsstuLaunchWslAndCaptureOutput(L\"ip netns exec testns socat -dd tcp-connect:bing.com:80 create:/tmp/nonexistent\", 1);\r\n        LogInfo(\"output %s\", output.c_str());\r\n        LogInfo(\"warnings %s\", warnings.c_str());\r\n        VERIFY_ARE_NOT_EQUAL(warnings.find(L\"starting data transfer loop\"), std::string::npos);\r\n    }\r\n\r\n    TEST_METHOD(NatNonRootNamespaceEphemeralBind)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Because the test creates a new network namespace, the resolv.conf from the root network namespace\r\n        // is copied in the resolv.conf of the new network namespace. The DNS tunneling listener running in the root namespace\r\n        // needs to be accessible from the new namespace, so it can't use a 127* IP.\r\n        WslConfigChange config(LxssGenerateTestConfig({\r\n            .guiApplications = true,\r\n            .dnsTunneling = true,\r\n            .dnsTunnelingIpAddress = L\"10.255.255.254\",\r\n        }));\r\n\r\n        // Configure the root namespace ephemeral port range so we can guarantee a valid,\r\n        // non-overlapping ephemeral port range in the non-root namespace using the very simple port\r\n        // range selection logic in TestNonRootNamespaceEphemeralBind.\r\n        auto [originalRange, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/sys/net/ipv4/ip_local_port_range\", 0);\r\n        std::wstring restoreEphemeralPortRangeCommand =\r\n            std::format(L\"sysctl -w net.ipv4.ip_local_port_range=\\\"{}\\\"\", originalRange.c_str());\r\n        auto revertEphemeralPortRange = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&restoreEphemeralPortRangeCommand] {\r\n            LxsstuLaunchWsl(restoreEphemeralPortRangeCommand.c_str());\r\n        });\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"sysctl -w net.ipv4.ip_local_port_range=\\\"60400 60700\\\"\"), 0);\r\n\r\n        TestNonRootNamespaceEphemeralBind();\r\n    }\r\n\r\n    enum class FirewallObjects\r\n    {\r\n        Required,\r\n        NotRequired\r\n    };\r\n\r\n    static void ValidateInitialFirewallState(FirewallObjects expectHyperVFirewallObjects)\r\n    {\r\n        // Verify that we have an initially working connection.\r\n        // This also ensures that WSL is started to allow for\r\n        // validating the initial Hyper-V port state\r\n        GuestClient(L\"tcp-connect:bing.com:80\");\r\n\r\n        if (expectHyperVFirewallObjects == FirewallObjects::Required)\r\n        {\r\n            // Query for Hyper-V objects. At least one Hyper-V port is expected\r\n            auto [out, err] = LxsstuLaunchPowershellAndCaptureOutput(L\"Get-NetFirewallHyperVPort\");\r\n            LogInfo(\"out:[%ls] err:[%ls]\", out.c_str(), err.c_str());\r\n            VERIFY_IS_TRUE(!out.empty());\r\n        }\r\n    }\r\n\r\n    static auto AddFirewallRule(const FirewallRule& rule)\r\n    {\r\n        try\r\n        {\r\n            std::wstring cmdPrefix;\r\n            if (rule.Type == FirewallType::HyperV)\r\n            {\r\n                cmdPrefix = L\"New-NetFirewallHyperVRule -VmCreatorId \" + rule.VmCreatorId + L\" -RemotePorts \" + rule.RemotePorts;\r\n            }\r\n            else\r\n            {\r\n                cmdPrefix = L\"New-NetFirewallRule -Protocol TCP -RemotePort \" + rule.RemotePorts;\r\n            }\r\n\r\n            auto [out, _] = LxsstuLaunchPowershellAndCaptureOutput(\r\n                cmdPrefix + L\" -Name \" + rule.Name + L\" -DisplayName \" + rule.Name + L\" -Action \" + rule.Action +\r\n                L\" -Direction Outbound\");\r\n\r\n            LogInfo(\"AddRule output:\\r\\n%ls\", FixLineEndings(out).c_str());\r\n\r\n            // output what, if any, Hyper-V Firewall rules were created in response to the above\r\n            auto [query_output, __] = LxsstuLaunchPowershellAndCaptureOutput(L\"Get-NetFirewallHyperVRule -Name \" + rule.Name);\r\n            LogInfo(\"Get-NetFirewallHyperVRule output:\\r\\n%ls\", FixLineEndings(query_output).c_str());\r\n        }\r\n        CATCH_LOG()\r\n\r\n        return wil::scope_exit([rule]() {\r\n            try\r\n            {\r\n                LogInfo(\"Removing the test rule %ls\\n\", rule.Name.c_str());\r\n                std::wstring cmdPrefix;\r\n                if (rule.Type == FirewallType::HyperV)\r\n                {\r\n                    cmdPrefix = L\"Remove-NetFirewallHyperVRule\";\r\n                }\r\n                else\r\n                {\r\n                    cmdPrefix = L\"Remove-NetFirewallRule\";\r\n                }\r\n                LxsstuLaunchPowershellAndCaptureOutput(cmdPrefix + L\" -Name \" + rule.Name);\r\n            }\r\n            CATCH_LOG()\r\n        });\r\n    }\r\n\r\n    enum class FirewallTestConnectivity\r\n    {\r\n        Allowed,\r\n        Blocked\r\n    };\r\n\r\n    static auto AddFirewallRuleAndValidateTraffic(const FirewallRule& rule, FirewallTestConnectivity expectedConnectivityAfterRule)\r\n    {\r\n        LogInfo(\r\n            \"Validating ruleType=[%ls] name=[%ls] and expectedConnectivity=[%ls]\",\r\n            (rule.Type == FirewallType::Host) ? L\"Host\" : L\"HyperV\",\r\n            rule.Name.c_str(),\r\n            expectedConnectivityAfterRule == FirewallTestConnectivity::Allowed ? L\"Allowed\" : L\"Blocked\");\r\n\r\n        // Add rule and verify the connection is allowed/blocked as expected\r\n        auto firewallRuleCleanup = AddFirewallRule(rule);\r\n\r\n        GuestClient(L\"tcp-connect:bing.com:80,connect-timeout=5\", expectedConnectivityAfterRule);\r\n        return firewallRuleCleanup;\r\n    }\r\n\r\n    static auto ConfigureFirewallEnabled(FirewallType firewallType, bool settingValue, std::wstring vmCreatorId = L\"\")\r\n    {\r\n        LogInfo(\r\n            \"Configure FirewallEnabled for Type=[%ls] enabled=[%ls]\",\r\n            (firewallType == FirewallType::Host) ? L\"Host\" : L\"HyperV\",\r\n            settingValue ? L\"True\" : L\"False\");\r\n        try\r\n        {\r\n            std::wstring prefix;\r\n            if (firewallType == FirewallType::HyperV)\r\n            {\r\n                prefix = L\"Set-NetFirewallHyperVProfile -VmCreatorId \" + vmCreatorId;\r\n            }\r\n            else\r\n            {\r\n                prefix = L\"Set-NetFirewallProfile\";\r\n            }\r\n            LxsstuLaunchPowershellAndCaptureOutput(prefix + L\" -Profile Public -Enabled \" + (settingValue ? L\"True\" : L\"False\"));\r\n            LxsstuLaunchPowershellAndCaptureOutput(prefix + L\" -Profile Private -Enabled \" + (settingValue ? L\"True\" : L\"False\"));\r\n            LxsstuLaunchPowershellAndCaptureOutput(prefix + L\" -Profile Domain -Enabled \" + (settingValue ? L\"True\" : L\"False\"));\r\n        }\r\n        CATCH_LOG()\r\n\r\n        return wil::scope_exit([vmCreatorId, firewallType]() {\r\n            try\r\n            {\r\n                std::wstring prefix;\r\n                if (firewallType == FirewallType::HyperV)\r\n                {\r\n                    prefix = L\"Set-NetFirewallHyperVProfile -VmCreatorId \" + vmCreatorId;\r\n                }\r\n                else\r\n                {\r\n                    prefix = L\"Set-NetFirewallProfile\";\r\n                }\r\n\r\n                LxsstuLaunchPowershellAndCaptureOutput(prefix + L\" -Profile Public -Enabled NotConfigured\");\r\n                LxsstuLaunchPowershellAndCaptureOutput(prefix + L\" -Profile Private -Enabled NotConfigured\");\r\n                LxsstuLaunchPowershellAndCaptureOutput(prefix + L\" -Profile Domain -Enabled NotConfigured\");\r\n            }\r\n            CATCH_LOG()\r\n        });\r\n    }\r\n\r\n    static auto ConfigureHyperVFirewallLoopbackEnabled(bool settingValue, std::wstring vmCreatorId)\r\n    {\r\n        LogInfo(\"Configuring LoopbackEnabled=[%d]\", settingValue);\r\n        try\r\n        {\r\n            LxsstuLaunchPowershellAndCaptureOutput(\r\n                L\"Set-NetFirewallHyperVVMSetting -VmCreatorId \" + vmCreatorId + L\" -LoopbackEnabled \" + (settingValue ? L\"True\" : L\"False\"));\r\n        }\r\n        CATCH_LOG()\r\n\r\n        return wil::scope_exit([vmCreatorId]() {\r\n            try\r\n            {\r\n                LxsstuLaunchPowershellAndCaptureOutput(\r\n                    L\"Set-NetFirewallHyperVVMSetting -VmCreatorId \" + vmCreatorId + L\" -LoopbackEnabled NotConfigured\");\r\n            }\r\n            CATCH_LOG()\r\n        });\r\n    }\r\n\r\n    static void FirewallRuleBlockedTests(FirewallTestConnectivity expectedConnectivity)\r\n    {\r\n        // Adding a block rule should result in traffic being blocked\r\n        FirewallRule blockRule = {FirewallType::Host, L\"WSLTestBlockRule\", c_firewallTrafficTestPort, c_firewallRuleActionBlock};\r\n        AddFirewallRuleAndValidateTraffic(blockRule, expectedConnectivity);\r\n\r\n        // Adding both an allow and block rule should result in traffic being blocked\r\n        FirewallRule allowRule = {FirewallType::Host, L\"WSLTestAllowRule\", c_firewallTrafficTestPort, c_firewallRuleActionAllow};\r\n        auto allowRuleCleanup = AddFirewallRuleAndValidateTraffic(allowRule, FirewallTestConnectivity::Allowed);\r\n        AddFirewallRuleAndValidateTraffic(blockRule, expectedConnectivity);\r\n        allowRuleCleanup.reset();\r\n\r\n        // Adding a block rule should result in traffic being blocked\r\n        FirewallRule hyperVBlockRule = {\r\n            FirewallType::HyperV, L\"WSLTestBlockRuleHyperV\", c_firewallTrafficTestPort, c_firewallRuleActionBlock, c_wslVmCreatorId};\r\n        AddFirewallRuleAndValidateTraffic(hyperVBlockRule, expectedConnectivity);\r\n\r\n        // Adding both an allow and block rule should result in traffic being blocked\r\n        FirewallRule hyperVAllowRule = {\r\n            FirewallType::HyperV, L\"WSLTestAllowRuleHyperV\", c_firewallTrafficTestPort, c_firewallRuleActionAllow, c_wslVmCreatorId};\r\n        auto hyperVAllowRuleCleanup = AddFirewallRuleAndValidateTraffic(hyperVAllowRule, FirewallTestConnectivity::Allowed);\r\n        AddFirewallRuleAndValidateTraffic(hyperVBlockRule, expectedConnectivity);\r\n        hyperVAllowRuleCleanup.reset();\r\n\r\n        // Adding a rule with vm creator 'any' should result in traffic being blocked\r\n        FirewallRule anyHyperVBlockRule = {\r\n            FirewallType::HyperV, L\"WSLTestBlockRuleHyperVAny\", c_firewallTrafficTestPort, c_firewallRuleActionBlock, c_wslVmCreatorId};\r\n        AddFirewallRuleAndValidateTraffic(hyperVBlockRule, expectedConnectivity);\r\n    }\r\n\r\n    TEST_METHOD(NatFirewallRulesExpectedBlock)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.firewall = true}));\r\n\r\n        ValidateInitialFirewallState(FirewallObjects::Required);\r\n        FirewallRuleBlockedTests(FirewallTestConnectivity::Blocked);\r\n    }\r\n\r\n    TEST_METHOD(NatFirewallRulesExpectedBlockFirewallDisabled)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n        SKIP_TEST_UNSTABLE();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig({.firewall = false}));\r\n\r\n        ValidateInitialFirewallState(FirewallObjects::NotRequired);\r\n        FirewallRuleBlockedTests(FirewallTestConnectivity::Allowed);\r\n    }\r\n\r\n    TEST_METHOD(NatFirewallRulesExpectedBlockFirewallDisabledByPolicy)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n\r\n        RegistryKeyChange<DWORD> change(\r\n            HKEY_LOCAL_MACHINE, wsl::windows::policies::c_registryKey, wsl::windows::policies::c_allowCustomFirewallUserSetting, 0);\r\n\r\n        // the user tries to disable Hyper-V FW in the config file, but the admin disabled user control\r\n        WslConfigChange config(LxssGenerateTestConfig({.firewall = false}));\r\n\r\n        ValidateInitialFirewallState(FirewallObjects::NotRequired);\r\n        FirewallRuleBlockedTests(FirewallTestConnectivity::Blocked);\r\n    }\r\n\r\n    static void FirewallRuleAllowedTests(FirewallTestConnectivity expectedConnectivity)\r\n    {\r\n        // A host rule with different IP address should not affect traffic\r\n        FirewallRule differentIPRule = {FirewallType::Host, L\"WSLTestDifferentIPRule\", c_firewallTestOtherPort, c_firewallRuleActionBlock};\r\n        AddFirewallRuleAndValidateTraffic(differentIPRule, expectedConnectivity);\r\n\r\n        // A host rule with action allow should not affect traffic\r\n        FirewallRule allowRule = {FirewallType::Host, L\"WSLTestAllowRule\", c_firewallTrafficTestPort, c_firewallRuleActionAllow};\r\n        AddFirewallRuleAndValidateTraffic(allowRule, expectedConnectivity);\r\n\r\n        // A hyperv- rule with a different VM creator ID should not affect this traffic\r\n        FirewallRule differentVmCreatorRule = {\r\n            FirewallType::HyperV, L\"WSLTestDifferentVMCreatorIdRule\", c_firewallTrafficTestPort, c_firewallRuleActionBlock, c_wsaVmCreatorId};\r\n        AddFirewallRuleAndValidateTraffic(differentVmCreatorRule, expectedConnectivity);\r\n\r\n        // A hyper-v rule with a different IP address should not affect this traffic\r\n        FirewallRule differentIPHyperVRule = {\r\n            FirewallType::HyperV, L\"WSLTestDifferentIPRuleHyperV\", c_firewallTestOtherPort, c_firewallRuleActionBlock, c_wslVmCreatorId};\r\n        AddFirewallRuleAndValidateTraffic(differentIPHyperVRule, expectedConnectivity);\r\n\r\n        // A hyper-v rule with action allow should not affect traffic\r\n        FirewallRule allowHyperVRule = {\r\n            FirewallType::HyperV, L\"WSLTestAllowRuleHyperV\", c_firewallTrafficTestPort, c_firewallRuleActionAllow, c_wslVmCreatorId};\r\n        AddFirewallRuleAndValidateTraffic(allowHyperVRule, expectedConnectivity);\r\n    }\r\n\r\n    TEST_METHOD(NatFirewallRulesExpectedAllow)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.firewall = true}));\r\n\r\n        ValidateInitialFirewallState(FirewallObjects::Required);\r\n        FirewallRuleAllowedTests(FirewallTestConnectivity::Allowed);\r\n    }\r\n\r\n    TEST_METHOD(NatFirewallRulesExpectedAllowFirewallDisabled)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n        SKIP_TEST_UNSTABLE();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig({.firewall = false}));\r\n\r\n        ValidateInitialFirewallState(FirewallObjects::NotRequired);\r\n        FirewallRuleAllowedTests(FirewallTestConnectivity::Allowed);\r\n    }\r\n\r\n    static void FirewallSettingEnabledTests(bool isHyperVFirewallEnabled)\r\n    {\r\n        // Configure Firewall disabled\r\n        auto hostDisabledCleanup = ConfigureFirewallEnabled(FirewallType::Host, false);\r\n\r\n        // Add host block rule, which is expected to be enforced\r\n        FirewallRule blockRule = {FirewallType::Host, L\"WSLTestBlockRule\", c_firewallTrafficTestPort, c_firewallRuleActionBlock, c_wslVmCreatorId};\r\n        AddFirewallRuleAndValidateTraffic(blockRule, FirewallTestConnectivity::Allowed);\r\n        blockRule.Type = FirewallType::HyperV;\r\n        // Add hyper-v block rule, which is expected to be enforced\r\n        AddFirewallRuleAndValidateTraffic(blockRule, FirewallTestConnectivity::Allowed);\r\n        hostDisabledCleanup.reset();\r\n\r\n        // Configure Hyper-V firewall disabled\r\n        auto hyperVDisabledCleanup = ConfigureFirewallEnabled(FirewallType::HyperV, false, c_wslVmCreatorId);\r\n        // Add host block rule, which is expected to be enforced\r\n        blockRule.Type = FirewallType::Host;\r\n        AddFirewallRuleAndValidateTraffic(blockRule, FirewallTestConnectivity::Allowed);\r\n        // Add hyper-v block rule, which is expected to be enforced\r\n        blockRule.Type = FirewallType::HyperV;\r\n        AddFirewallRuleAndValidateTraffic(blockRule, FirewallTestConnectivity::Allowed);\r\n        hyperVDisabledCleanup.reset();\r\n\r\n        // host rules are propagated only if Hyper-V Firewall is enabled\r\n        // Configure conflicting policy for host and hyper-v (hyper-v policy takes precedence)\r\n        auto conflictingHostEnabledCleanup = ConfigureFirewallEnabled(FirewallType::Host, true);\r\n        // Add host block rule, which is expected to be enforced\r\n        blockRule.Type = FirewallType::Host;\r\n        AddFirewallRuleAndValidateTraffic(\r\n            blockRule, isHyperVFirewallEnabled ? FirewallTestConnectivity::Blocked : FirewallTestConnectivity::Allowed);\r\n        // Add hyper-v block rule, which is expected to be enforced\r\n        blockRule.Type = FirewallType::HyperV;\r\n        AddFirewallRuleAndValidateTraffic(\r\n            blockRule, isHyperVFirewallEnabled ? FirewallTestConnectivity::Blocked : FirewallTestConnectivity::Allowed);\r\n\r\n        // Configure hyper-v disabled\r\n        auto conflictingHyperVDisabledCleanup = ConfigureFirewallEnabled(FirewallType::HyperV, false, c_wslVmCreatorId);\r\n        // Add host block rule, which is expected to be NOT enforced (firewall is disabled)\r\n        blockRule.Type = FirewallType::Host;\r\n        AddFirewallRuleAndValidateTraffic(blockRule, FirewallTestConnectivity::Allowed);\r\n        // Add hyper-v block rule, which is expected to be NOT enforced (firewall is disabled)\r\n        blockRule.Type = FirewallType::HyperV;\r\n        AddFirewallRuleAndValidateTraffic(blockRule, FirewallTestConnectivity::Allowed);\r\n        conflictingHostEnabledCleanup.reset();\r\n        conflictingHyperVDisabledCleanup.reset();\r\n\r\n        // Configure conflicting policy for host and hyper-v (hyper-v policy takes precedence)\r\n        auto conflictingHyperVEnabledCleanup = ConfigureFirewallEnabled(FirewallType::HyperV, true, c_wslVmCreatorId);\r\n        // Add host block rule, which is expected to be enforced\r\n        blockRule.Type = FirewallType::Host;\r\n        AddFirewallRuleAndValidateTraffic(\r\n            blockRule, isHyperVFirewallEnabled ? FirewallTestConnectivity::Blocked : FirewallTestConnectivity::Allowed);\r\n        // Add hyper-v block rule, which is expected to be enforced\r\n        blockRule.Type = FirewallType::HyperV;\r\n        AddFirewallRuleAndValidateTraffic(\r\n            blockRule, isHyperVFirewallEnabled ? FirewallTestConnectivity::Blocked : FirewallTestConnectivity::Allowed);\r\n        // Configure host firewall disabled. Hyper-V firewall is still expected to be enforced, but host firewall rules will not be\r\n        auto conflictingHostDisabledCleanup = ConfigureFirewallEnabled(FirewallType::Host, false);\r\n        // Add host block rule, which is NOT expected to be enforced (host firewall disabled)\r\n        blockRule.Type = FirewallType::Host;\r\n        AddFirewallRuleAndValidateTraffic(blockRule, FirewallTestConnectivity::Allowed);\r\n        // Add hyper-v block rule, which is expected to be enforced (hyper-v firewall still enabled)\r\n        blockRule.Type = FirewallType::HyperV;\r\n        AddFirewallRuleAndValidateTraffic(\r\n            blockRule, isHyperVFirewallEnabled ? FirewallTestConnectivity::Blocked : FirewallTestConnectivity::Allowed);\r\n    }\r\n\r\n    TEST_METHOD(NatFirewallRulesEnabledSetting)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n        WslConfigChange config(LxssGenerateTestConfig({.firewall = true}));\r\n\r\n        ValidateInitialFirewallState(FirewallObjects::Required);\r\n        FirewallSettingEnabledTests(true);\r\n    }\r\n\r\n    TEST_METHOD(NatFirewallRulesEnabledSettingFirewallDisabled)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n        SKIP_TEST_UNSTABLE();\r\n        WslConfigChange config(LxssGenerateTestConfig({.firewall = false}));\r\n\r\n        ValidateInitialFirewallState(FirewallObjects::NotRequired);\r\n        FirewallSettingEnabledTests(false);\r\n    }\r\n\r\n    /* Network Tests Helper Methods */\r\n\r\n    static GUID QueryAdapterId()\r\n    {\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(\r\n            L\"readlink /sys/class/net/eth0 | grep -o -E '[[:xdigit:]]{8}(-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12}'\", 0);\r\n        out.pop_back();\r\n\r\n        const auto guid = wsl::shared::string::ToGuid(out);\r\n        VERIFY_IS_TRUE(guid.has_value());\r\n\r\n        return guid.value();\r\n    }\r\n\r\n    static void RunGns(const std::string& input, const std::optional<GUID>& adapter = {}, const std::optional<LX_MESSAGE_TYPE>& messageType = {}, int expectedErrorCode = 0)\r\n    {\r\n        constexpr auto InheritOnReadHandle = true;\r\n        constexpr auto DoNotEnableInheritOnWriteHandle = false;\r\n        SECURITY_ATTRIBUTES attributes = {sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE};\r\n        auto [read, write] =\r\n            CreateSubprocessPipe(InheritOnReadHandle, DoNotEnableInheritOnWriteHandle, static_cast<DWORD>(input.size()), &attributes);\r\n\r\n        THROW_IF_WIN32_BOOL_FALSE(WriteFile(write.get(), input.data(), static_cast<DWORD>(input.size()), nullptr, nullptr));\r\n        write.reset();\r\n\r\n        LogInfo(\"GNS Input: '%S'\", input.c_str());\r\n        const auto adapterArg =\r\n            adapter.has_value() ? L\"--adapter \" + wsl::shared::string::GuidToString<wchar_t>(adapter.value()) + std::wstring(L\" \") : L\"\";\r\n        const auto messageTypeArg =\r\n            messageType.has_value() ? L\"--msg_type \" + std::to_wstring(static_cast<int>(messageType.value())) + std::wstring(L\" \") : L\"\";\r\n        LxsstuLaunchWslAndCaptureOutput(L\"/gns \" + adapterArg + messageTypeArg, expectedErrorCode, read.get());\r\n    }\r\n\r\n    template <typename T>\r\n    void RunGns(T& input, ModifyRequestType action, GuestEndpointResourceType type)\r\n    {\r\n        ModifyGuestEndpointSettingRequest<T> request;\r\n        request.RequestType = action;\r\n        request.ResourceType = type;\r\n        request.Settings = input;\r\n\r\n        RunGns(wsl::shared::ToJson(request), AdapterId, LxGnsMessageNotification);\r\n    }\r\n\r\n    template <typename T>\r\n    void RunGns(T& input, const LX_MESSAGE_TYPE messageType)\r\n    {\r\n        RunGns(wsl::shared::ToJson(input), AdapterId, messageType);\r\n    }\r\n\r\n    template <typename T>\r\n    void SendDeviceSettingsRequest(std::wstring targetDevice, T& input, ModifyRequestType action, GuestEndpointResourceType type)\r\n    {\r\n        wsl::shared::hns::ModifyGuestEndpointSettingRequest<T> request;\r\n        request.targetDeviceName = targetDevice;\r\n        request.RequestType = action;\r\n        request.ResourceType = type;\r\n        request.Settings = input;\r\n\r\n        RunGns(request, LxGnsMessageDeviceSettingRequest);\r\n    }\r\n\r\n    // Convert Unix line endings (\\n) to Windows line endings (\\r\\n) for proper console display\r\n    static std::wstring FixLineEndings(const std::wstring& input)\r\n    {\r\n        std::wstring output;\r\n        for (size_t i = 0; i < input.length(); ++i)\r\n        {\r\n            if (input[i] == L'\\n')\r\n            {\r\n                output += L\"\\r\\n\";\r\n            }\r\n            else if (input[i] != L'\\r')\r\n            {\r\n                output += input[i];\r\n            }\r\n        }\r\n\r\n        return output;\r\n    }\r\n\r\n    static RoutingTableState GetRoutingTableState(std::wstring& out, std::wregex& defaultRoutePattern, std::wregex& routePattern)\r\n    {\r\n        RoutingTableState state;\r\n        std::wsmatch match;\r\n\r\n        std::wistringstream input(out);\r\n        std::wstring line;\r\n        while (std::getline(input, line) && !line.empty())\r\n        {\r\n            if (std::regex_search(line, match, defaultRoutePattern) && match.size() >= 3)\r\n            {\r\n                if (state.DefaultRoute.has_value())\r\n                {\r\n                    continue;\r\n                }\r\n\r\n                state.DefaultRoute = {{match.str(1), match.str(2), {}, match.size() > 4 && match[4].matched ? std::stoi(match.str(4)) : 0}};\r\n            }\r\n            else if (std::regex_search(line, match, routePattern) && match.size() >= 4)\r\n            {\r\n                state.Routes.emplace_back(Route{\r\n                    match.str(2), match.str(3), {match.str(1)}, match.size() > 5 && match[5].matched ? std::stoi(match.str(5)) : 0});\r\n            }\r\n        }\r\n\r\n        return state;\r\n    }\r\n\r\n    static RoutingTableState GetIpv4RoutingTableState()\r\n    {\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"ip route show\");\r\n        LogInfo(\"Ip route output:\\r\\n%ls\", FixLineEndings(out).c_str());\r\n\r\n        std::wregex defaultRoutePattern(L\"default via ([0-9,.]+) dev ([a-zA-Z0-9]*) *(metric ([0-9]+))?\");\r\n        std::wregex routePattern(L\"([0-9,.,/]+) via ([0-9,.]+) dev ([a-zA-Z0-9]*) *(metric ([0-9]+))?\");\r\n\r\n        return GetRoutingTableState(out, defaultRoutePattern, routePattern);\r\n    }\r\n\r\n    static void WaitForIpv6DefaultRoute()\r\n    {\r\n        const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(30);\r\n        while (std::chrono::steady_clock::now() < timeout)\r\n        {\r\n            auto state = GetIpv6RoutingTableState();\r\n            if (state.DefaultRoute.has_value())\r\n            {\r\n                return;\r\n            }\r\n\r\n            LogInfo(\"Waiting for IPv6 default route...\");\r\n            std::this_thread::sleep_for(std::chrono::seconds(1));\r\n        }\r\n\r\n        VERIFY_FAIL(L\"Timed out waiting for IPv6 default route\");\r\n    }\r\n\r\n    static RoutingTableState GetIpv6RoutingTableState()\r\n    {\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"ip -6 route show\");\r\n        LogInfo(\"Ip -6 route output:\\r\\n%ls\", FixLineEndings(out).c_str());\r\n\r\n        RoutingTableState state;\r\n        std::wregex defaultRoutePattern(L\"default via ([a-f,A-F,0-9,:]+) dev ([a-zA-Z0-9]*) *(metric ([0-9]+))?\");\r\n        std::wregex routePattern(L\"([a-f,A-F,0-9,:,/]+) via ([a-f,A-F,0-9,:]+) dev ([a-zA-Z0-9]*) *(metric ([0-9]+))?\");\r\n\r\n        return GetRoutingTableState(out, defaultRoutePattern, routePattern);\r\n    }\r\n\r\n    static InterfaceState GetInterfaceState(const std::wstring& name, const std::wstring& expectedWarnings = L\"\")\r\n    {\r\n        // Sample output from \"ip addr show\":\r\n        // 4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000\r\n        // link/ether 00:12:34:56:78:9A brd ff:ff:ff:ff:ff:ff\r\n        // inet 172.17.123.249/20 brd 172.17.127.255 scope global eth0\r\n        // valid_lft forever preferred_lft forever\r\n        // inet6 2001::1:2:3:4/64 scope global\r\n        // valid_lft forever preferred_lft 0sec\r\n        auto [out, warnings] = LxsstuLaunchWslAndCaptureOutput(L\"ip addr show \" + name);\r\n        LogInfo(\"ip addr show output:\\r\\n%ls\", FixLineEndings(out).c_str());\r\n\r\n        if (expectedWarnings.empty())\r\n        {\r\n            VERIFY_IS_TRUE(warnings.empty());\r\n        }\r\n        else\r\n        {\r\n            if (!PathMatchSpec(warnings.c_str(), expectedWarnings.c_str()))\r\n            {\r\n                LogError(\"Warning '%ls' didn't match pattern '%ls'\", warnings.c_str(), expectedWarnings.c_str());\r\n                VERIFY_FAIL();\r\n            }\r\n        }\r\n\r\n        std::wistringstream input(out);\r\n\r\n        std::wstring line;\r\n\r\n        InterfaceState state = {name};\r\n\r\n        // Drop first two lines\r\n        VERIFY_IS_TRUE(std::getline(input, line).good());\r\n        VERIFY_IS_TRUE(std::getline(input, line).good());\r\n\r\n        // Read the address lines\r\n        while (std::getline(input, line).good())\r\n        {\r\n            std::wregex v4Pattern(L\"inet ([0-9,.]+)\\\\/([0-9]+) brd ([0-9,.]+) scope global .*\" + name);\r\n            std::wregex v6Pattern(L\"inet6 ([a-f,A-F,0-9,:]+)\\\\/([0-9]+) scope global\");\r\n            std::wregex v4LocalPattern(L\"inet 169.254.([0-9,.]+)\\\\/([0-9]+) brd 169.254.255.255 scope link\");\r\n            std::wregex v6LocalPattern(L\"inet6 ([a-f,A-F,0-9,:]+)\\\\/([0-9]+) scope link\");\r\n            std::wregex v4LoopbackPattern(L\"inet 127.0.0.1/8 scope host\");\r\n            std::wregex v6LoopbackPattern(L\"inet6 ::1/128 scope host\");\r\n            std::wregex deprecatedPattern(L\"deprecated\");\r\n\r\n            std::wsmatch match, preferredStateMatch;\r\n            if (std::regex_search(line, match, v4Pattern) && match.size() == 4)\r\n            {\r\n                bool preferred = !std::regex_search(line, preferredStateMatch, deprecatedPattern);\r\n                state.V4Addresses.emplace_back(IpAddress{match.str(1), (uint8_t)std::stoul(match.str(2)), preferred});\r\n            }\r\n            else if (std::regex_search(line, match, v6Pattern) && match.size() == 3)\r\n            {\r\n                bool preferred = !std::regex_search(line, preferredStateMatch, deprecatedPattern);\r\n                state.V6Addresses.emplace_back(IpAddress{match.str(1), (uint8_t)std::stoul(match.str(2)), preferred});\r\n            }\r\n            else if (std::regex_search(line, match, v4LocalPattern) && match.size() == 3)\r\n            {\r\n                LogInfo(\"Skipping ipv4 link local address\");\r\n            }\r\n            else if (std::regex_search(line, match, v6LocalPattern) && match.size() == 3)\r\n            {\r\n                LogInfo(\"Skipping ipv6 link local address\");\r\n            }\r\n            else if (std::regex_search(line, match, v4LoopbackPattern) && match.size() == 1)\r\n            {\r\n                LogInfo(\"Skipping ipv4 loopback\");\r\n            }\r\n            else if (std::regex_search(line, match, v6LoopbackPattern) && match.size() == 1)\r\n            {\r\n                LogInfo(\"Skipping ipv6 loopback\");\r\n            }\r\n            else\r\n            {\r\n                LogInfo(\"Ip addr output:\\r\\n%ls\", FixLineEndings(out).c_str());\r\n                LogInfo(\"Current line: \\\"%ls\\\"\", line.c_str());\r\n                VERIFY_FAIL(L\"Failed to extract interface state\");\r\n            }\r\n\r\n            // Skip the lifetimes line\r\n            VERIFY_IS_TRUE(std::getline(input, line).good());\r\n        }\r\n\r\n        out = LxsstuLaunchWslAndCaptureOutput(L\"cat /sys/class/net/\" + name + L\"/operstate\").first;\r\n        state.Up = false;\r\n        if (out == L\"up\\n\")\r\n        {\r\n            state.Up = true;\r\n        }\r\n        else if ((out != L\"down\\n\") && ((name.substr(0, 4).compare(L\"wlan\") != 0) && (name != L\"lo\")))\r\n        {\r\n            LogInfo(\"Unexpected operstate: '%s'\", out.c_str());\r\n            VERIFY_FAIL();\r\n        }\r\n\r\n        out = LxsstuLaunchWslAndCaptureOutput(L\"cat /sys/class/net/\" + name + L\"/mtu\").first;\r\n        state.Mtu = std::stoi(out);\r\n\r\n        auto routingTableState = GetIpv4RoutingTableState();\r\n        if (routingTableState.DefaultRoute.has_value())\r\n        {\r\n            state.Gateway = routingTableState.DefaultRoute->Via;\r\n        }\r\n\r\n        auto v6RoutingTableState = GetIpv6RoutingTableState();\r\n        if (v6RoutingTableState.DefaultRoute.has_value())\r\n        {\r\n            state.V6Gateway = v6RoutingTableState.DefaultRoute->Via;\r\n        }\r\n\r\n        return state;\r\n    }\r\n\r\n    static std::vector<InterfaceState> GetAllInterfaceStates()\r\n    {\r\n        // Result output is a list of interface names with newline as the delimiter\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"ip -brief link show | awk -F '[@ ]' '{print $1}'\");\r\n        LogInfo(\"parsed ip link output:\\r\\n%ls\", FixLineEndings(out).c_str());\r\n\r\n        std::wistringstream input(out);\r\n\r\n        std::vector<InterfaceState> interfaceStates;\r\n        std::wstring line;\r\n\r\n        while (std::getline(input, line).good())\r\n        {\r\n            interfaceStates.push_back(GetInterfaceState(line));\r\n        }\r\n\r\n        return interfaceStates;\r\n    }\r\n\r\n    void TestCase(const std::vector<InterfaceState>& interfaceStates)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        for (const auto& state : interfaceStates)\r\n        {\r\n            if (state.Rename)\r\n            {\r\n                wsl::shared::hns::HNSEndpoint endpoint;\r\n                endpoint.ID = AdapterId;\r\n                endpoint.PortFriendlyName = state.Name;\r\n                RunGns(wsl::shared::ToJson(endpoint));\r\n            }\r\n\r\n            // Remove existing addresses not in goal state\r\n            auto currentInterfaceState = GetInterfaceState(state.Name);\r\n            for (auto it = currentInterfaceState.V4Addresses.begin(); it != currentInterfaceState.V4Addresses.end(); ++it)\r\n            {\r\n                if (std::find(state.V4Addresses.begin(), state.V4Addresses.end(), *it) == state.V4Addresses.end())\r\n                {\r\n                    wsl::shared::hns::IPAddress address;\r\n                    address.Address = it->Address;\r\n                    address.OnLinkPrefixLength = it->PrefixLength;\r\n                    address.Family = AF_INET;\r\n                    SendDeviceSettingsRequest(state.Name, address, ModifyRequestType::Remove, GuestEndpointResourceType::IPAddress);\r\n                }\r\n            }\r\n\r\n            for (auto it = currentInterfaceState.V6Addresses.begin(); it != currentInterfaceState.V6Addresses.end(); ++it)\r\n            {\r\n                if (std::find(state.V4Addresses.begin(), state.V4Addresses.end(), *it) == state.V4Addresses.end())\r\n                {\r\n                    wsl::shared::hns::IPAddress address;\r\n                    address.Address = it->Address;\r\n                    address.OnLinkPrefixLength = it->PrefixLength;\r\n                    address.Family = AF_INET6;\r\n                    SendDeviceSettingsRequest(state.Name, address, ModifyRequestType::Remove, GuestEndpointResourceType::IPAddress);\r\n                }\r\n            }\r\n\r\n            // Add or update addresses\r\n            for (auto it = state.V4Addresses.begin(); it != state.V4Addresses.end(); ++it)\r\n            {\r\n                wsl::shared::hns::IPAddress address;\r\n                address.Address = it->Address;\r\n                address.OnLinkPrefixLength = it->PrefixLength;\r\n                address.Family = AF_INET;\r\n                address.PreferredLifetime = 0xFFFFFFFF;\r\n                bool updateAddress =\r\n                    (std::find(currentInterfaceState.V4Addresses.begin(), currentInterfaceState.V4Addresses.end(), *it) !=\r\n                     currentInterfaceState.V4Addresses.end());\r\n                SendDeviceSettingsRequest(\r\n                    state.Name, address, updateAddress ? ModifyRequestType::Update : ModifyRequestType::Add, GuestEndpointResourceType::IPAddress);\r\n\r\n                Route prefixRoute{LX_INIT_UNSPECIFIED_ADDRESS, L\"eth0\", it->GetPrefix()};\r\n                if (!RouteExists(prefixRoute))\r\n                {\r\n                    // Add the prefix route for the newly added/updated address\r\n                    wsl::shared::hns::Route route;\r\n                    route.NextHop = prefixRoute.Via;\r\n                    route.DestinationPrefix = prefixRoute.Prefix.value();\r\n                    route.Family = AF_INET;\r\n                    SendDeviceSettingsRequest(state.Name, route, ModifyRequestType::Add, GuestEndpointResourceType::Route);\r\n                }\r\n            }\r\n\r\n            for (auto it = state.V6Addresses.begin(); it != state.V6Addresses.end(); ++it)\r\n            {\r\n                wsl::shared::hns::IPAddress address;\r\n                address.Address = it->Address;\r\n                address.OnLinkPrefixLength = it->PrefixLength;\r\n                address.Family = AF_INET6;\r\n                address.PreferredLifetime = 0xFFFFFFFF;\r\n                bool updateAddress =\r\n                    (std::find(currentInterfaceState.V6Addresses.begin(), currentInterfaceState.V6Addresses.end(), *it) !=\r\n                     currentInterfaceState.V6Addresses.end());\r\n                SendDeviceSettingsRequest(\r\n                    state.Name, address, updateAddress ? ModifyRequestType::Update : ModifyRequestType::Add, GuestEndpointResourceType::IPAddress);\r\n\r\n                Route prefixRoute{LX_INIT_UNSPECIFIED_V6_ADDRESS, L\"eth0\", it->GetPrefix()};\r\n                if (!RouteExists(prefixRoute))\r\n                {\r\n                    // Add the prefix route for the newly added/updated address\r\n                    wsl::shared::hns::Route route;\r\n                    route.NextHop = prefixRoute.Via;\r\n                    route.DestinationPrefix = prefixRoute.Prefix.value();\r\n                    route.Family = AF_INET6;\r\n                    SendDeviceSettingsRequest(state.Name, route, ModifyRequestType::Add, GuestEndpointResourceType::Route);\r\n                }\r\n            }\r\n\r\n            if (state.Gateway.has_value())\r\n            {\r\n                wsl::shared::hns::Route route;\r\n                route.NextHop = state.Gateway.value();\r\n                route.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_PREFIX;\r\n                route.Family = AF_INET;\r\n                bool updateGw = currentInterfaceState.Gateway.has_value();\r\n                SendDeviceSettingsRequest(\r\n                    state.Name, route, updateGw ? ModifyRequestType::Update : ModifyRequestType::Add, GuestEndpointResourceType::Route);\r\n            }\r\n\r\n            if (state.V6Gateway.has_value())\r\n            {\r\n                wsl::shared::hns::Route route;\r\n                route.NextHop = state.V6Gateway.value();\r\n                route.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_V6_PREFIX;\r\n                route.Family = AF_INET6;\r\n                bool updateGw = currentInterfaceState.V6Gateway.has_value();\r\n                SendDeviceSettingsRequest(\r\n                    state.Name, route, updateGw ? ModifyRequestType::Update : ModifyRequestType::Add, GuestEndpointResourceType::Route);\r\n            }\r\n        }\r\n\r\n        // Validate that the addresses and routes are in the final goal state\r\n        const auto& expectedInterfaceState = interfaceStates.back();\r\n\r\n        auto interfaceState = GetInterfaceState(expectedInterfaceState.Name);\r\n        for (auto it = expectedInterfaceState.V4Addresses.begin(); it != expectedInterfaceState.V4Addresses.end(); ++it)\r\n        {\r\n            VERIFY_IS_TRUE(\r\n                std::find(interfaceState.V4Addresses.begin(), interfaceState.V4Addresses.end(), *it) != interfaceState.V4Addresses.end());\r\n        }\r\n\r\n        if (expectedInterfaceState.Gateway.has_value())\r\n        {\r\n            VERIFY_ARE_EQUAL(expectedInterfaceState.Gateway, interfaceState.Gateway);\r\n        }\r\n\r\n        for (auto it = expectedInterfaceState.V6Addresses.begin(); it != expectedInterfaceState.V6Addresses.end(); ++it)\r\n        {\r\n            VERIFY_IS_TRUE(\r\n                std::find(interfaceState.V6Addresses.begin(), interfaceState.V6Addresses.end(), *it) != interfaceState.V6Addresses.end());\r\n        }\r\n\r\n        if (expectedInterfaceState.V6Gateway.has_value())\r\n        {\r\n            VERIFY_ARE_EQUAL(expectedInterfaceState.V6Gateway, interfaceState.V6Gateway);\r\n        }\r\n    }\r\n\r\n    static bool RouteExists(const Route& route)\r\n    {\r\n        auto v4State = GetIpv4RoutingTableState();\r\n        if (std::find(v4State.Routes.begin(), v4State.Routes.end(), route) != v4State.Routes.end())\r\n        {\r\n            return true;\r\n        }\r\n\r\n        auto v6State = GetIpv6RoutingTableState();\r\n        return std::find(v6State.Routes.begin(), v6State.Routes.end(), route) != v6State.Routes.end();\r\n    }\r\n\r\n    // Reads from the file until the substring is found, a timeout is reached, or ReadFile returns an error\r\n    // Returns true on success, false otherwise\r\n    static bool FindSubstring(wil::unique_handle& file, const std::string& substr, std::string& output)\r\n    {\r\n        char buffer[256];\r\n        DWORD bytesRead;\r\n        const HANDLE readFileThread = OpenThread(THREAD_ALL_ACCESS, false, GetCurrentThreadId());\r\n        const wil::unique_handle event(CreateEvent(nullptr, FALSE, FALSE, nullptr));\r\n        VERIFY_ARE_NOT_EQUAL(event.get(), INVALID_HANDLE_VALUE);\r\n\r\n        // ReadFile will block, so cancel the syscall if it is taking too long\r\n        const auto watchdogThread = std::async(std::launch::async, [&] {\r\n            if (WaitForSingleObject(event.get(), 30000) == WAIT_TIMEOUT)\r\n            {\r\n                LogInfo(\"Canceling synchronous IO\", GetTickCount());\r\n                CancelSynchronousIo(readFileThread);\r\n            }\r\n        });\r\n\r\n        do\r\n        {\r\n            if (!ReadFile(file.get(), buffer, sizeof(buffer) - 1, &bytesRead, nullptr))\r\n            {\r\n                LogInfo(\"ReadFile failed with %d\", GetLastError());\r\n                break;\r\n            }\r\n\r\n            buffer[bytesRead] = '\\0';\r\n            output += std::string(buffer);\r\n\r\n            if (output.find(substr) != std::string::npos)\r\n            {\r\n                break;\r\n            }\r\n        } while (true);\r\n\r\n        SetEvent(event.get());\r\n        watchdogThread.wait();\r\n\r\n        // Convert narrow string output to wide string for logging, and fix line endings\r\n        std::wstring wideOutput;\r\n        wideOutput.reserve(output.length());\r\n        for (char c : output)\r\n        {\r\n            if (c == '\\n')\r\n            {\r\n                wideOutput += L'\\r';\r\n                wideOutput += L'\\n';\r\n            }\r\n            else if (c != '\\r')\r\n            {\r\n                wideOutput += static_cast<wchar_t>(static_cast<unsigned char>(c));\r\n            }\r\n        }\r\n        LogInfo(\"output=\\r\\n%ls\", wideOutput.c_str());\r\n\r\n        return (output.find(substr) != std::string::npos);\r\n    }\r\n\r\n    static std::wstring CreateSocatString(const SOCKADDR_INET& si, int protocol, bool listen)\r\n    {\r\n        return std::wstring(((protocol == IPPROTO_TCP) ? L\"TCP\" : L\"UDP\")) + std::wstring(((si.si_family == AF_INET) ? L\"4\" : L\"6\")) +\r\n               std::wstring(L\"-\") + std::wstring((listen) ? L\"LISTEN:\" : ((IPPROTO_TCP) ? L\"CONNECT:\" : L\"SENDTO:\")) +\r\n               std::wstring(\r\n                   (listen) ? std::to_wstring(ntohs(SS_PORT(&si))) + std::wstring(L\",bind=\") +\r\n                                  wsl::windows::common::string::SockAddrInetToWstring(si)\r\n                            : wsl::windows::common::string::SockAddrInetToWstring(si) + std::wstring(L\":\") +\r\n                                  std::to_wstring(ntohs(SS_PORT(&si))));\r\n    }\r\n\r\n    struct GuestListener\r\n    {\r\n        GuestListener(const SOCKADDR_INET& addr, int protocol)\r\n        {\r\n            THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&readPipe, &writePipe, nullptr, 0));\r\n            THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(writePipe.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));\r\n\r\n            const auto wslCmd = L\"socat -dd \" + CreateSocatString(addr, protocol, true) + L\" STDOUT\";\r\n            auto cmd = LxssGenerateWslCommandLine(wslCmd.data());\r\n\r\n            process = unique_kill_process(LxsstuStartProcess(cmd.data(), nullptr, nullptr, writePipe.get()));\r\n            writePipe.reset();\r\n\r\n            std::string output;\r\n            THROW_HR_IF(E_FAIL, !NetworkTests::FindSubstring(readPipe, \"listening on\", output));\r\n        }\r\n\r\n        // Start a listener in a different network namespace\r\n        GuestListener(const SOCKADDR_INET& addr, int protocol, const std::wstring& namespaceName)\r\n        {\r\n            THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&readPipe, &writePipe, nullptr, 0));\r\n            THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(writePipe.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));\r\n\r\n            const auto wslCmd =\r\n                L\"ip netns exec \" + namespaceName + L\" socat -dd \" + CreateSocatString(addr, protocol, true) + L\" STDOUT\";\r\n            auto cmd = LxssGenerateWslCommandLine(wslCmd.data());\r\n\r\n            process = unique_kill_process(LxsstuStartProcess(cmd.data(), nullptr, nullptr, writePipe.get()));\r\n            writePipe.reset();\r\n\r\n            std::string output;\r\n            THROW_HR_IF(E_FAIL, !NetworkTests::FindSubstring(readPipe, \"listening on\", output));\r\n        }\r\n\r\n        void AcceptConnection()\r\n        {\r\n            std::string output;\r\n            VERIFY_IS_TRUE(NetworkTests::FindSubstring(readPipe, \"starting data transfer loop\", output));\r\n        }\r\n\r\n        wil::unique_handle dmesgFile;\r\n        unique_kill_process dmesg;\r\n        unique_kill_process process;\r\n        wil::unique_handle readPipe;\r\n        wil::unique_handle writePipe;\r\n    };\r\n\r\n    struct GuestClient\r\n    {\r\n        GuestClient(const SOCKADDR_INET& addr, int protocol) : GuestClient(CreateSocatString(addr, protocol, false))\r\n        {\r\n        }\r\n\r\n        GuestClient(const std::wstring& socatString, FirewallTestConnectivity expectedSuccess = FirewallTestConnectivity::Allowed)\r\n        {\r\n            const auto expectSuccess = expectedSuccess == FirewallTestConnectivity::Allowed;\r\n            const auto wslCmd = L\"echo A | socat -dd \" + socatString + L\" STDIN\";\r\n            auto cmd = LxssGenerateWslCommandLine(wslCmd.data());\r\n            const auto* connectionString = expectSuccess ? \"starting data transfer loop\" : \"Connection timed out\";\r\n            bool valueFound = false;\r\n            for (int i = 0; i < 3; ++i)\r\n            {\r\n                wil::unique_handle readPipe;\r\n                wil::unique_handle writePipe;\r\n                THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&readPipe, &writePipe, nullptr, 0));\r\n                THROW_IF_WIN32_BOOL_FALSE(SetHandleInformation(writePipe.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));\r\n\r\n                unique_kill_process process = unique_kill_process(LxsstuStartProcess(cmd.data(), nullptr, nullptr, writePipe.get()));\r\n                writePipe.reset();\r\n\r\n                std::string output;\r\n                valueFound = FindSubstring(readPipe, connectionString, output);\r\n\r\n                if (expectSuccess && !valueFound && (output.find(\"Temporary failure\") != std::string::npos))\r\n                {\r\n                    LogWarning(\"Temporary failure - retrying up to 3 times\");\r\n                    continue;\r\n                }\r\n\r\n                break;\r\n            }\r\n\r\n            VERIFY_IS_TRUE(valueFound, (expectSuccess) ? \"Verifying connection succeeded\" : \"Verifying connection failed\");\r\n        }\r\n    };\r\n\r\n    static std::wstring GetGelNicDeviceName()\r\n    {\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"ip route get from 127.0.0.1 127.0.0.1 | awk 'FNR <= 1 {print $7}'\");\r\n        out.pop_back();\r\n        return out;\r\n    }\r\n\r\n    static bool HostHasInternetConnectivity(ADDRESS_FAMILY family)\r\n    {\r\n        using ABI::Windows::Foundation::Collections::IVectorView;\r\n        using ABI::Windows::Networking::Connectivity::ConnectionProfile;\r\n        using ABI::Windows::Networking::Connectivity::INetworkAdapter;\r\n        using ABI::Windows::Networking::Connectivity::INetworkInformationStatics;\r\n        using ABI::Windows::Networking::Connectivity::NetworkConnectivityLevel;\r\n\r\n        // Get adapter addresses info.\r\n        const auto adapterAddresses = GetAdapterAddresses(family);\r\n\r\n        // Get connection profile info.\r\n        const auto roInit = wil::RoInitialize();\r\n        const auto networkInformationStatics =\r\n            wil::GetActivationFactory<INetworkInformationStatics>(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation);\r\n        THROW_HR_IF_NULL_MSG(E_OUTOFMEMORY, networkInformationStatics.get(), \"null INetworkInformationStatics\");\r\n        wil::com_ptr<IVectorView<ConnectionProfile*>> connectionList;\r\n        THROW_IF_FAILED(networkInformationStatics->GetConnectionProfiles(&connectionList));\r\n\r\n        // If we find a connection profile marked as having internet access and the associated\r\n        // adapter has a <family> unicast address and a <family> default gateway, then conclude the\r\n        // host has <family> internet connectivity.\r\n        for (const auto& connectionProfile : wil::get_range(connectionList.get()))\r\n        {\r\n            NetworkConnectivityLevel connectivityLevel{};\r\n            CONTINUE_IF_FAILED(connectionProfile->GetNetworkConnectivityLevel(&connectivityLevel));\r\n            if (connectivityLevel != NetworkConnectivityLevel::NetworkConnectivityLevel_InternetAccess)\r\n            {\r\n                continue;\r\n            }\r\n\r\n            wil::com_ptr<INetworkAdapter> networkAdapter;\r\n            CONTINUE_IF_FAILED(connectionProfile->get_NetworkAdapter(&networkAdapter));\r\n\r\n            GUID interfaceGuid{};\r\n            CONTINUE_IF_FAILED(networkAdapter->get_NetworkAdapterId(&interfaceGuid));\r\n\r\n            NET_LUID interfaceLuid{};\r\n            CONTINUE_IF_FAILED_WIN32(ConvertInterfaceGuidToLuid(&interfaceGuid, &interfaceLuid));\r\n\r\n            for (auto* adapter = reinterpret_cast<const IP_ADAPTER_ADDRESSES*>(adapterAddresses.data()); adapter != nullptr;\r\n                 adapter = adapter->Next)\r\n            {\r\n                if (interfaceLuid.Value == adapter->Luid.Value && adapter->FirstUnicastAddress != nullptr && adapter->FirstGatewayAddress != nullptr)\r\n                {\r\n                    return true;\r\n                }\r\n            }\r\n        }\r\n\r\n        return false;\r\n    }\r\n\r\n    static bool HostHasIpv6DnsServers()\r\n    {\r\n        ULONG bufferSize = 0;\r\n        constexpr ULONG flags = GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_INCLUDE_GATEWAYS;\r\n        std::vector<BYTE> buffer;\r\n        ULONG result = GetAdaptersAddresses(AF_INET6, flags, nullptr, nullptr, &bufferSize);\r\n        while (result == ERROR_BUFFER_OVERFLOW)\r\n        {\r\n            buffer.resize(bufferSize);\r\n            result = GetAdaptersAddresses(AF_INET6, flags, nullptr, reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.data()), &bufferSize);\r\n        }\r\n\r\n        if (result != NO_ERROR)\r\n        {\r\n            return false;\r\n        }\r\n\r\n        DWORD bestIndex = 0;\r\n        SOCKADDR_IN6 dest{};\r\n        dest.sin6_family = AF_INET6;\r\n        InetPtonW(AF_INET6, L\"2001:4860:4860::8888\", &dest.sin6_addr);\r\n\r\n        if (GetBestInterfaceEx(reinterpret_cast<SOCKADDR*>(&dest), &bestIndex) != NO_ERROR)\r\n        {\r\n            return false;\r\n        }\r\n\r\n        for (auto* adapter = reinterpret_cast<const IP_ADAPTER_ADDRESSES*>(buffer.data()); adapter != nullptr; adapter = adapter->Next)\r\n        {\r\n            if (adapter->IfIndex != bestIndex)\r\n            {\r\n                continue;\r\n            }\r\n\r\n            for (auto* dns = adapter->FirstDnsServerAddress; dns != nullptr; dns = dns->Next)\r\n            {\r\n                if (dns->Address.lpSockaddr->sa_family == AF_INET6)\r\n                {\r\n                    return true;\r\n                }\r\n            }\r\n        }\r\n\r\n        return false;\r\n    }\r\n\r\n    static std::vector<BYTE> GetAdapterAddresses(ADDRESS_FAMILY family)\r\n    {\r\n        constexpr ULONG flags =\r\n            (GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS);\r\n        ULONG bufferSize = 0;\r\n        std::vector<BYTE> buffer;\r\n        ULONG result = GetAdaptersAddresses(family, flags, nullptr, nullptr, &bufferSize);\r\n        while (result == ERROR_BUFFER_OVERFLOW)\r\n        {\r\n            buffer.resize(bufferSize);\r\n            result = GetAdaptersAddresses(family, flags, nullptr, reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.data()), &bufferSize);\r\n        }\r\n\r\n        VERIFY_WIN32_SUCCEEDED(result);\r\n\r\n        return buffer;\r\n    }\r\n\r\n    static void WaitForNATStateInLinux()\r\n    {\r\n        Stopwatch<std::chrono::seconds> Watchdog(std::chrono::seconds(30));\r\n\r\n        // NAT only supports IPv4 connectivity\r\n        // wait for the host to have v4 connectivity\r\n        do\r\n        {\r\n            if (HostHasInternetConnectivity(AF_INET))\r\n            {\r\n                break;\r\n            }\r\n\r\n            LogInfo(\"Waiting for Windows network connectivity...\");\r\n        } while (Sleep(1000), !Watchdog.IsExpired());\r\n        VERIFY_IS_FALSE(Watchdog.IsExpired());\r\n\r\n        // reset the watchdog\r\n        Watchdog = Stopwatch{std::chrono::seconds(30)};\r\n\r\n        do\r\n        {\r\n            // Count how many interfaces have v4 connectivity, as defined by having a gateway and at least 1 preferred address.\r\n            int interfacesWithV4Connectivity = 0;\r\n\r\n            // Get all interface info from the VM.\r\n            for (const auto& i : GetAllInterfaceStates())\r\n            {\r\n                if (i.Gateway.has_value())\r\n                {\r\n                    for (const auto& j : i.V4Addresses)\r\n                    {\r\n                        if (j.Preferred)\r\n                        {\r\n                            interfacesWithV4Connectivity++;\r\n                            break;\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n\r\n            // Consider mirroring to be complete if we have the same v4 connectivity in the VM as the host.\r\n            if (interfacesWithV4Connectivity > 0)\r\n            {\r\n                break;\r\n            }\r\n\r\n            LogInfo(\"Waiting for NAT state...\");\r\n        } while (Sleep(1000), !Watchdog.IsExpired());\r\n        VERIFY_IS_FALSE(Watchdog.IsExpired());\r\n    }\r\n\r\n    TEST_METHOD(ConnectivityCheckTestNATDefaultSuccess)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig());\r\n        WaitForNATStateInLinux();\r\n\r\n        const auto coInit = wil::CoInitializeEx();\r\n        const wil::com_ptr<INetworkListManager> networkListManager = wil::CoCreateInstance<NetworkListManager, INetworkListManager>();\r\n        VERIFY_IS_NOT_NULL(networkListManager.get());\r\n        NLM_CONNECTIVITY hostConnectivity{};\r\n        VERIFY_SUCCEEDED(networkListManager->GetConnectivity(&hostConnectivity));\r\n\r\n        // Windows\r\n        const wsl::shared::conncheck::ConnCheckResult hostResult =\r\n            wsl::shared::conncheck::CheckConnection(\"www.msftconnecttest.com\", \"ipv6.msftconnecttest.com\", \"80\");\r\n\r\n        if (hostConnectivity & NLM_CONNECTIVITY_IPV4_INTERNET)\r\n        {\r\n            VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::Success, hostResult.Ipv4Status);\r\n        }\r\n        else\r\n        {\r\n            // one of the 2 expected runtime failures\r\n            VERIFY_IS_TRUE(\r\n                hostResult.Ipv4Status == wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo ||\r\n                hostResult.Ipv4Status == wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect);\r\n        }\r\n        if (hostConnectivity & NLM_CONNECTIVITY_IPV6_INTERNET)\r\n        {\r\n            VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::Success, hostResult.Ipv4Status);\r\n        }\r\n        else\r\n        {\r\n            // one of the 2 expected runtime failures (sometimes v6 name resolution will fail, depending on the configuration)\r\n            VERIFY_IS_TRUE(\r\n                hostResult.Ipv6Status == wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo ||\r\n                hostResult.Ipv6Status == wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect);\r\n        }\r\n\r\n        // www.msftconnecttest.com will always fail IPv6 name resolution - it doesn't have any AAAA records registered for it\r\n        const int expectedErrorCode = static_cast<int>(hostResult.Ipv4Status) |\r\n                                      (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo) << 16);\r\n        LogInfo(\"RunGns(www.msftconnecttest.com, 0x%x)\", expectedErrorCode);\r\n        // TODO: pass 'expectedErrorCode' instead of 1, once the pipeline is fixed from running Init back to wsl.exe\r\n        // it returns 1 (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::Success)\r\n        // as that's the lowest 16 bit value (unknown where the upper 16 bits are trimmed)\r\n        // if ManualConnectivityValidation is set true, one can confirm from the stdout captured that the correct result was determined and returned by init.\r\n        constexpr auto testErrorCode =\r\n            ManualConnectivityValidation ? expectedErrorCode : static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::Success);\r\n        RunGns(\"www.msftconnecttest.com\", AdapterId, LxGnsMessageConnectTestRequest, testErrorCode);\r\n    }\r\n\r\n    TEST_METHOD(ConnectivityCheckTestNATNameResolutionFailure)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig());\r\n        WaitForNATStateInLinux();\r\n\r\n        // Windows\r\n        const wsl::shared::conncheck::ConnCheckResult result =\r\n            wsl::shared::conncheck::CheckConnection(\"asdlkfadsf.bbcxzncvb\", nullptr, \"80\");\r\n\r\n        VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo, result.Ipv4Status);\r\n        VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo, result.Ipv6Status);\r\n\r\n        constexpr int expectedErrorCode = static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo) |\r\n                                          (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo) << 16);\r\n        LogInfo(\"RunGns(asdlkfadsf.bbcxzncvb, 0x%x)\", expectedErrorCode);\r\n        // TODO: pass 'expectedErrorCode' instead of 1, once the pipeline is fixed from running Init back to wsl.exe\r\n        // it returns 2 (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo))\r\n        // as that's the lowest 16 bit value (unknown where the upper 16 bits are trimmed)\r\n        // if temporarily change this back to expectedErrorCode, one can confirm from the stdout captured that the correct result was determined and returned by init.\r\n        constexpr auto testErrorCode = ManualConnectivityValidation\r\n                                           ? expectedErrorCode\r\n                                           : static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo);\r\n        RunGns(\"asdlkfadsf.bbcxzncvb\", AdapterId, LxGnsMessageConnectTestRequest, testErrorCode);\r\n    }\r\n\r\n    TEST_METHOD(ConnectivityCheckTestNATNameResolvesButConnectivityFails)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig());\r\n        WaitForNATStateInLinux();\r\n\r\n        const auto* ncsiDnsOnlyName = \"dns.msftncsi.com\";\r\n        // v4 and v6 should succeed to resolve the name, but fail to connect,\r\n        // as this NCSI name is registered in global DNS, but there's not HTTP endpoint for it\r\n\r\n        // Windows\r\n        const wsl::shared::conncheck::ConnCheckResult result =\r\n            wsl::shared::conncheck::CheckConnection(ncsiDnsOnlyName, nullptr, \"80\");\r\n\r\n        VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect, result.Ipv4Status);\r\n        // v6 name resolution might fail, depending on the configuration\r\n        VERIFY_IS_TRUE(\r\n            (wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo == result.Ipv6Status) ||\r\n            (wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect == result.Ipv6Status));\r\n\r\n        constexpr int expectedErrorCode = static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect) |\r\n                                          (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect) << 16);\r\n        LogInfo(\"RunGns(%hs, 0x%x)\", ncsiDnsOnlyName, expectedErrorCode);\r\n        // TODO: pass 'expectedErrorCode' instead of 1, once the pipeline is fixed from running Init back to wsl.exe\r\n        // it returns 4 (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect))\r\n        // as that's the lowest 16 bit value (unknown where the upper 16 bits are trimmed)\r\n        // if ManualConnectivityValidation is set true, one can confirm from the stdout captured that the correct result was determined and returned by init.\r\n        constexpr auto testErrorCode = ManualConnectivityValidation\r\n                                           ? expectedErrorCode\r\n                                           : static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect);\r\n        RunGns(ncsiDnsOnlyName, AdapterId, LxGnsMessageConnectTestRequest, testErrorCode);\r\n    }\r\n};\r\n\r\nclass MirroredTests\r\n{\r\n    WSL_TEST_CLASS(MirroredTests)\r\n\r\n    std::optional<WslConfigChange> m_config;\r\n    GUID AdapterId;\r\n\r\n    TEST_CLASS_SETUP(TestClassSetup)\r\n    {\r\n        VERIFY_ARE_EQUAL(LxsstuInitialize(false), TRUE);\r\n\r\n        if (LxsstuVmMode())\r\n        {\r\n            m_config.emplace(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n\r\n            AdapterId = NetworkTests::QueryAdapterId();\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ln -f -s /init /gns\"), (DWORD)0);\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_CLASS_CLEANUP(TestClassCleanup)\r\n    {\r\n        m_config.reset();\r\n\r\n        VERIFY_NO_THROW(LxsstuUninitialize(false));\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD(DnsTunneling)\r\n    {\r\n        DNS_TUNNELING_TEST_ONLY();\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .dnsTunneling = true}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::VerifyDnsTunneling(c_dnsTunnelingDefaultIp);\r\n    }\r\n\r\n    TEST_METHOD(DnsTunnelingWithSpecificIp)\r\n    {\r\n        DNS_TUNNELING_TEST_ONLY();\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig(\r\n            {.networkingMode = wsl::core::NetworkingMode::Mirrored, .dnsTunneling = true, .dnsTunnelingIpAddress = L\"10.255.255.1\"}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::VerifyDnsTunneling(L\"10.255.255.1\");\r\n    }\r\n\r\n    TEST_METHOD(DnsTunnelingVerifySuffixes)\r\n    {\r\n        DNS_TUNNELING_TEST_ONLY();\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .dnsTunneling = true}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::VerifyDnsSuffixes();\r\n    }\r\n\r\n    TEST_METHOD(WithoutTunnelingVerifySuffixes)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .dnsTunneling = false}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::VerifyDnsSuffixes();\r\n    }\r\n\r\n    TEST_METHOD(HttpProxyVerifyConfigDisabled)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .autoProxy = false}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        auto restoreProxySettings = wil::scope_exit([&] { NetworkTests::ClearHttpProxySettings(true); });\r\n        NetworkTests::SetHttpProxySettings(NetworkTests::c_httpProxyString, L\"\", L\"\", true);\r\n        NetworkTests::VerifyHttpProxyEnvVariables(L\"\", L\"\", L\"\");\r\n    }\r\n\r\n    TEST_METHOD(HttpProxySimple)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .autoProxy = true}));\r\n        WaitForMirroredStateInLinux();\r\n        NetworkTests::VerifyHttpProxySimple();\r\n    }\r\n\r\n    TEST_METHOD(HttpProxySimpleMachineScope)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .autoProxy = true}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // verify with machine scope\r\n        NetworkTests::VerifyHttpProxySimple(false);\r\n    }\r\n\r\n    TEST_METHOD(NoHttpProxyConfigured)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .autoProxy = true}));\r\n        WaitForMirroredStateInLinux();\r\n        NetworkTests::VerifyNoHttpProxyConfigured();\r\n    }\r\n\r\n    TEST_METHOD(HttpProxyWithBypassesConfigured)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .autoProxy = true}));\r\n        WaitForMirroredStateInLinux();\r\n        NetworkTests::VerifyHttpProxyWithBypassesConfigured();\r\n    }\r\n\r\n    TEST_METHOD(HttpProxyChange)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .autoProxy = true}));\r\n        WaitForMirroredStateInLinux();\r\n        NetworkTests::VerifyHttpProxyChange();\r\n    }\r\n\r\n    TEST_METHOD(HttpProxyAndWslEnv)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .autoProxy = true}));\r\n        WaitForMirroredStateInLinux();\r\n        NetworkTests::VerifyHttpProxyAndWslEnv();\r\n    }\r\n\r\n    TEST_METHOD(HttpProxyFilterByNetworkConfiguration)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .autoProxy = true}));\r\n\r\n        NetworkTests::VerifyHttpProxyFilterByNetworkConfigurationMirrored();\r\n    }\r\n\r\n    TEST_METHOD(SmokeTest)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // Verify that we have a working connection\r\n        NetworkTests::GuestClient(L\"tcp-connect:bing.com:80\");\r\n    }\r\n\r\n    TEST_METHOD(InternetConnectivityV4)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        if (!NetworkTests::HostHasInternetConnectivity(AF_INET))\r\n        {\r\n            LogSkipped(\"Host does not have IPv4 internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        NetworkTests::GuestClient(L\"tcp4-connect:bing.com:80\");\r\n    }\r\n\r\n    TEST_METHOD(InternetConnectivityV6)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        if (!NetworkTests::HostHasInternetConnectivity(AF_INET6))\r\n        {\r\n            LogSkipped(\"Host does not have IPv6 internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        NetworkTests::GuestClient(L\"tcp6-connect:bing.com:80\");\r\n    }\r\n\r\n    TEST_METHOD(LoopbackLocal)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .hostAddressLoopback = true}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        std::vector<NetworkTests::InterfaceState> interfaceStates = NetworkTests::GetAllInterfaceStates();\r\n\r\n        // Verify loopback connectivity on assigned unicast addresses\r\n        for (auto i = interfaceStates.begin(); i != interfaceStates.end(); ++i)\r\n        {\r\n            for (auto j = i->V4Addresses.begin(); j != i->V4Addresses.end(); ++j)\r\n            {\r\n                // The IP used for DNS tunneling is not intended for guest<->host communication\r\n                if (j->Address != c_dnsTunnelingDefaultIp)\r\n                {\r\n                    NetworkTests::VerifyLoopbackConnectivity(j->Address);\r\n                }\r\n            }\r\n            for (auto j = i->V6Addresses.begin(); j != i->V6Addresses.end(); ++j)\r\n            {\r\n                // TODO: enable when v6 loopback is supported\r\n                // VerifyLoopbackConnectivity(j->Address);\r\n            }\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(LoopbackExplicit)\r\n    {\r\n        // TODO: re-enable once OS build 29555 loopback regression is resolved.\r\n        SKIP_TEST_UNSTABLE();\r\n\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // Verify loopback connectivity on loopback addresses\r\n        NetworkTests::VerifyLoopbackConnectivity(L\"127.0.0.1\");\r\n        // TODO: enable when v6 loopback is supported\r\n        // VerifyLoopbackConnectivity(L\"::1\");\r\n    }\r\n\r\n    TEST_METHOD(LoopbackSystemd)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // Write a .conf file to conflict with loopback settings.\r\n#define CONFIG_FILE_PATH L\"/etc/sysctl.d/MirroredLoopbackSystemd.conf\"\r\n        auto revertConfigFile = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [] {\r\n            const std::wstring deleteConfigFileCmd(L\"-u root -e rm \" CONFIG_FILE_PATH);\r\n            LxsstuLaunchWsl(deleteConfigFileCmd.data());\r\n        });\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"echo \\\"net.ipv4.conf.*.rp_filter=2\\\" > \" CONFIG_FILE_PATH), static_cast<DWORD>(0));\r\n\r\n        // Enable systemd which will apply the .conf file.\r\n        auto revertSystemd = EnableSystemd();\r\n\r\n        // Verify the settings configured in the systemd hardening logic.\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"sysctl net.ipv4.conf.all.rp_filter | grep -w 0\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"sysctl net.ipv4.conf.\" TEXT(LX_INIT_LOOPBACK_DEVICE_NAME) L\".rp_filter | grep -w 0\"), 0);\r\n\r\n        // Verify an E2E loopback scenario.\r\n        NetworkTests::VerifyLoopbackGuestToHost(L\"127.0.0.1\", IPPROTO_TCP);\r\n    }\r\n\r\n    TEST_METHOD(GuestPortCantBeBoundByHost)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        {\r\n            auto guestProcess = NetworkTests::BindGuestPort(L\"TCP4-LISTEN:1234\", true);\r\n            NetworkTests::BindHostPort(1234, SOCK_STREAM, IPPROTO_TCP, false);\r\n        }\r\n\r\n        {\r\n            auto guestProcess = NetworkTests::BindGuestPort(L\"UDP4-LISTEN:1234\", true);\r\n            NetworkTests::BindHostPort(1234, SOCK_DGRAM, IPPROTO_UDP, false);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(GuestPortIsReleased)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // Make sure the VM doesn't time out\r\n        WslKeepAlive keepAlive;\r\n\r\n        {\r\n            auto guestProcess = NetworkTests::BindGuestPort(L\"TCP4-LISTEN:1234\", true);\r\n            NetworkTests::BindHostPort(1234, SOCK_STREAM, IPPROTO_TCP, false);\r\n        }\r\n\r\n        const wil::unique_socket listenSocket(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n        VERIFY_IS_TRUE(!!listenSocket);\r\n\r\n        SOCKADDR_IN Address{};\r\n        Address.sin_family = AF_INET;\r\n        Address.sin_port = htons(1234);\r\n\r\n        const auto timeout = std::chrono::steady_clock::now() + std::chrono::minutes(2);\r\n\r\n        bool bound = false;\r\n        while (!bound && std::chrono::steady_clock::now() < timeout)\r\n        {\r\n            bound = bind(listenSocket.get(), reinterpret_cast<SOCKADDR*>(&Address), sizeof(Address)) != SOCKET_ERROR;\r\n            std::this_thread::sleep_for(std::chrono::seconds(1));\r\n        }\r\n\r\n        VERIFY_IS_TRUE(bound);\r\n    }\r\n\r\n    TEST_METHOD(HostPortCantBeBoundByGuest)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        {\r\n            auto hostPort = NetworkTests::BindHostPort(1234, SOCK_STREAM, IPPROTO_TCP, true);\r\n            NetworkTests::BindGuestPort(L\"TCP4-LISTEN:1234\", false);\r\n        }\r\n\r\n        {\r\n            auto hostPort = NetworkTests::BindHostPort(1234, SOCK_DGRAM, IPPROTO_UDP, true);\r\n            NetworkTests::BindGuestPort(L\"UDP4-LISTEN:1234\", false);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(UdpBindDoesNotPreventTcpBind)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        auto tcpPort = NetworkTests::BindGuestPort(L\"TCP4-LISTEN:1234\", true);\r\n        auto udpPort = NetworkTests::BindGuestPort(L\"UDP4-LISTEN:1234\", true);\r\n    }\r\n\r\n    TEST_METHOD(HostUdpBindDoesNotPreventGuestTcpBind)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        auto udpPort = NetworkTests::BindHostPort(2345, SOCK_DGRAM, IPPROTO_UDP, true);\r\n        auto tcpPort = NetworkTests::BindGuestPort(L\"TCP4-LISTEN:2345\", true);\r\n    }\r\n\r\n    TEST_METHOD(MultipleGuestBindOnSameTuple)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        auto bind1 = NetworkTests::BindGuestPort(L\"TCP4-LISTEN:1234,bind=127.0.0.1\", true);\r\n        {\r\n            auto bind2 = NetworkTests::BindGuestPort(L\"TCP6-LISTEN:1234,bind=::1\", true);\r\n\r\n            // Allow time for this second bind to be viewed as \"in use\" by the init port tracker\r\n            // before closing the socket. If the socket is closed before the init port tracker sees\r\n            // that the port allocation was in use, then the init port tracker will hold onto the\r\n            // allocation for a considerable amount of time (through the duration of this test case)\r\n            // before releasing it.\r\n            std::this_thread::sleep_for(std::chrono::seconds(3));\r\n        }\r\n\r\n        // Allow time for the init port tracker to detect the second port allocation as no longer in\r\n        // use and perform its cleanup of the second port allocation.\r\n        const auto timeout = std::chrono::steady_clock::now() + std::chrono::seconds(3);\r\n        while (std::chrono::steady_clock::now() < timeout)\r\n        {\r\n            // {TCP, 1234} should still be reserved for the guest from the first bind.\r\n            auto hostPort = NetworkTests::BindHostPort(1234, SOCK_STREAM, IPPROTO_TCP, false);\r\n            std::this_thread::sleep_for(std::chrono::seconds(1));\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(EphemeralBind)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        auto tcpPort = NetworkTests::BindGuestPort(L\"TCP4-LISTEN:0\", true);\r\n        auto udpPort = NetworkTests::BindGuestPort(L\"UDP4-LISTEN:0\", true);\r\n    }\r\n\r\n    TEST_METHOD(PortZeroBindIsTracked)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // Skip port-release verification in mirrored mode. The host reserves a contiguous\r\n        // ephemeral port range via HcnReserveGuestNetworkServicePortRange that no Windows\r\n        // process can bind for the lifetime of the VM. Port-0 binds resolve to ports within\r\n        // this range, so even after the guest releases the port the host still cannot bind\r\n        // it — the range-level reservation remains, making release unverifiable.\r\n        NetworkTests::VerifyPortZeroBindIsTracked(false);\r\n    }\r\n\r\n    TEST_METHOD(ExplicitEphemeralBind)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // Get ephemeral port range\r\n        auto [start, err1] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/sys/net/ipv4/ip_local_port_range | cut -f1\", 0);\r\n        start.pop_back();\r\n        const auto ephemeralRangeStart = std::stoi(start);\r\n\r\n        auto [end, err2] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/sys/net/ipv4/ip_local_port_range | cut -f2\", 0);\r\n        end.pop_back();\r\n        const auto ephemeralRangeEnd = std::stoi(end);\r\n\r\n        // Walk the ephemeral port range and verify we can bind to at least one port (some might be already taken, but the test\r\n        // assumes there should be at least one free).\r\n        bool canBindTcp = false;\r\n        bool canBindUdp = false;\r\n\r\n        for (int port = ephemeralRangeStart; port <= ephemeralRangeEnd; port++)\r\n        {\r\n            auto [tcpListener, tcpSuccess, read] = NetworkTests::BindGuestPortHelper(L\"TCP4-LISTEN:\" + std::to_wstring(port));\r\n            if (tcpSuccess)\r\n            {\r\n                canBindTcp = true;\r\n                break;\r\n            }\r\n        }\r\n\r\n        for (int port = ephemeralRangeStart; port <= ephemeralRangeEnd; port++)\r\n        {\r\n            auto [udpListener, udpSuccess, read] = NetworkTests::BindGuestPortHelper(L\"UDP4-LISTEN:\" + std::to_wstring(port));\r\n            if (udpSuccess)\r\n            {\r\n                canBindUdp = true;\r\n                break;\r\n            }\r\n        }\r\n\r\n        VERIFY_IS_TRUE(canBindTcp);\r\n        VERIFY_IS_TRUE(canBindUdp);\r\n    }\r\n\r\n    TEST_METHOD(NonRootNamespaceEphemeralBind)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        // Because the test creates a new network namespace, the resolv.conf from the root network namespace\r\n        // is copied in the resolv.conf of the new network namespace. The DNS tunneling listener running in the root namespace\r\n        // needs to be accessible from the new namespace, so it can't use a 127* IP\r\n        m_config->Update(LxssGenerateTestConfig(\r\n            {.guiApplications = true, .networkingMode = wsl::core::NetworkingMode::Mirrored, .dnsTunneling = true, .dnsTunnelingIpAddress = L\"10.255.255.254\"}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::TestNonRootNamespaceEphemeralBind();\r\n    }\r\n\r\n    // Verifies that in mirrored mode, Windows can connect to a listener running in a Linux network namespace different from\r\n    // the Linux root network namespace.\r\n    TEST_METHOD(PortForwardingToNonRootNamespace)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig(\r\n            {.guiApplications = true, .networkingMode = wsl::core::NetworkingMode::Mirrored, .hostAddressLoopback = true}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // We list the IPv4 addresses mirrored in Linux and use the first one we find in the test\r\n        std::vector<NetworkTests::InterfaceState> interfaceStates = NetworkTests::GetAllInterfaceStates();\r\n        std::wstring ipAddress;\r\n\r\n        for (auto i = interfaceStates.begin(); i != interfaceStates.end(); ++i)\r\n        {\r\n            for (auto j = i->V4Addresses.begin(); j != i->V4Addresses.end(); ++j)\r\n            {\r\n                // The IP used for DNS tunneling is not intended for guest<->host communication\r\n                if (j->Address != c_dnsTunnelingDefaultIp)\r\n                {\r\n                    ipAddress = j->Address;\r\n                    break;\r\n                }\r\n            }\r\n        }\r\n\r\n        // Get the forwarding state.\r\n        auto [oldIpForwardState, _1] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/sys/net/ipv4/ip_forward\", 0);\r\n        std::wstring restoreIpForwardCommand = std::format(L\"sysctl -w net.ipv4.ip_forward={}\", oldIpForwardState.c_str());\r\n\r\n        // Clean up the below configurations.\r\n        auto revertConfig = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&restoreIpForwardCommand] {\r\n            LxsstuLaunchWsl(restoreIpForwardCommand.c_str());\r\n            LxsstuLaunchWsl(L\"--system --user root nft flush chain nat POSTROUTING\");\r\n            LxsstuLaunchWsl(L\"--system --user root nft flush chain nat PREROUTING\");\r\n            LxsstuLaunchWsl(L\"ip link delete veth-test-br\");\r\n            LxsstuLaunchWsl(L\"ip link delete testbridge\");\r\n            LxsstuLaunchWsl(L\"ip netns delete testns\");\r\n        });\r\n\r\n        // Set up a networking namespace and provide it external network access via a bridge, veth\r\n        // pair, SRCNAT iptables rule and forwarding.\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip netns add testns\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link add testbridge type bridge\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link add veth-test type veth peer name veth-test-br\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set veth-test netns testns\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set veth-test-br master testbridge\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip -n testns link set veth-test up\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set veth-test-br up\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set testbridge up\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip -n testns addr add 192.168.15.2/24 dev veth-test\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip addr add 192.168.15.1/24 dev testbridge\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip -n testns route add default via 192.168.15.1 dev veth-test\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft add table nat\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft \\\"add chain nat POSTROUTING { type nat hook postrouting priority srcnat; }\\\"\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft add rule nat POSTROUTING ip saddr 192.168.15.0/24 oif != testbridge masquerade\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"sysctl -w net.ipv4.ip_forward=1\"), 0);\r\n\r\n        // Add rule for port forwarding traffic with destination port 8080 to port 80 in the new namespace\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft \\\"add chain nat PREROUTING { type nat hook prerouting priority dstnat; }\\\"\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft add rule nat PREROUTING tcp dport 8080 dnat to 192.168.15.2:80\"), 0);\r\n\r\n        // Start listeners in root namespace on port 8080 and new namespace on port 80\r\n        SOCKADDR_INET rootListenerAddr = wsl::windows::common::string::StringToSockAddrInet(L\"0.0.0.0\");\r\n        SS_PORT(&rootListenerAddr) = htons(8080);\r\n        NetworkTests::GuestListener rootListener(rootListenerAddr, IPPROTO_TCP);\r\n\r\n        SOCKADDR_INET namespaceListenerAddr = wsl::windows::common::string::StringToSockAddrInet(L\"0.0.0.0\");\r\n        SS_PORT(&namespaceListenerAddr) = htons(80);\r\n        NetworkTests::GuestListener namespaceListener(namespaceListenerAddr, IPPROTO_TCP, L\"testns\");\r\n\r\n        // Verify Windows can connect to port 8080\r\n        SOCKADDR_INET serverAddr = wsl::windows::common::string::StringToSockAddrInet(ipAddress);\r\n        SS_PORT(&serverAddr) = htons(8080);\r\n\r\n        wil::unique_socket clientSocket(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n        VERIFY_ARE_NOT_EQUAL(clientSocket.get(), INVALID_SOCKET);\r\n\r\n        VERIFY_ARE_EQUAL(connect(clientSocket.get(), reinterpret_cast<SOCKADDR*>(&serverAddr), sizeof(serverAddr)), 0);\r\n    }\r\n\r\n    TEST_METHOD(LinuxNonRootNamespaceConnectToWindowsHost)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig(\r\n            {.guiApplications = true, .networkingMode = wsl::core::NetworkingMode::Mirrored, .hostAddressLoopback = true}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // We list the IPv4 addresses mirrored in Linux and use the first one we find in the test\r\n        std::vector<NetworkTests::InterfaceState> interfaceStates = NetworkTests::GetAllInterfaceStates();\r\n        std::wstring ipAddress;\r\n\r\n        for (auto i = interfaceStates.begin(); i != interfaceStates.end(); ++i)\r\n        {\r\n            for (auto j = i->V4Addresses.begin(); j != i->V4Addresses.end(); ++j)\r\n            {\r\n                // The IP used for DNS tunneling is not intended for guest<->host communication\r\n                if (j->Address != c_dnsTunnelingDefaultIp)\r\n                {\r\n                    ipAddress = j->Address;\r\n                    break;\r\n                }\r\n            }\r\n        }\r\n\r\n        // Get the forwarding state.\r\n        auto [oldIpForwardState, _1] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/sys/net/ipv4/ip_forward\", 0);\r\n        std::wstring restoreIpForwardCommand = std::format(L\"sysctl -w net.ipv4.ip_forward={}\", oldIpForwardState.c_str());\r\n\r\n        // Clean up the below configurations.\r\n        auto revertConfig = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&restoreIpForwardCommand] {\r\n            LxsstuLaunchWsl(restoreIpForwardCommand.c_str());\r\n            LxsstuLaunchWsl(L\"--system --user root nft flush chain nat POSTROUTING\");\r\n            LxsstuLaunchWsl(L\"ip link delete veth-test-br\");\r\n            LxsstuLaunchWsl(L\"ip link delete testbridge\");\r\n            LxsstuLaunchWsl(L\"ip netns delete testns\");\r\n        });\r\n\r\n        // Set up a networking namespace and provide it external network access via a bridge, veth\r\n        // pair, SRCNAT iptables rule and forwarding.\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip netns add testns\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link add testbridge type bridge\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link add veth-test type veth peer name veth-test-br\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set veth-test netns testns\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set veth-test-br master testbridge\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip -n testns link set veth-test up\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set veth-test-br up\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip link set testbridge up\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip -n testns addr add 192.168.15.2/24 dev veth-test\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip addr add 192.168.15.1/24 dev testbridge\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"ip -n testns route add default via 192.168.15.1 dev veth-test\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft add table nat\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft \\\"add chain nat POSTROUTING { type nat hook postrouting priority srcnat; }\\\"\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system --user root nft add rule nat POSTROUTING ip saddr 192.168.15.0/24 oif != testbridge masquerade\"), 0);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"sysctl -w net.ipv4.ip_forward=1\"), 0);\r\n\r\n        // Create a listener on the Windows host on port 1234\r\n        SOCKADDR_INET addr = wsl::windows::common::string::StringToSockAddrInet(ipAddress);\r\n        SS_PORT(&addr) = htons(1234);\r\n\r\n        const wil::unique_socket listenSocket(socket(addr.si_family, SOCK_STREAM, IPPROTO_TCP));\r\n        VERIFY_ARE_NOT_EQUAL(listenSocket.get(), INVALID_SOCKET);\r\n        VERIFY_ARE_NOT_EQUAL(bind(listenSocket.get(), reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr)), SOCKET_ERROR);\r\n        VERIFY_ARE_NOT_EQUAL(listen(listenSocket.get(), SOMAXCONN), SOCKET_ERROR);\r\n\r\n        // Verify the new network namespace can connect to the Windows host listener\r\n        auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(\r\n            L\"ip netns exec testns socat -dd tcp-connect:\" + ipAddress + L\":1234 create:/tmp/nonexistent\", 1);\r\n        LogInfo(\"output %s\", output.c_str());\r\n        LogInfo(\"warnings %s\", warnings.c_str());\r\n        VERIFY_ARE_NOT_EQUAL(warnings.find(L\"starting data transfer loop\"), std::string::npos);\r\n    }\r\n\r\n    TEST_METHOD(ResolvConf)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /etc/resolv.conf\", 0);\r\n        const std::wregex pattern(L\"(.|\\n)*nameserver [0-9\\\\. ]+(.|\\n)*\", std::regex::extended);\r\n\r\n        VERIFY_IS_TRUE(std::regex_match(out, pattern));\r\n    }\r\n\r\n    TEST_METHOD(NetworkSettings)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        struct NetworkSetting\r\n        {\r\n            const std::wstring Path;\r\n            const std::wstring ExpectedValue;\r\n        };\r\n\r\n        std::vector<NetworkSetting> settings{\r\n            {L\"/proc/sys/net/ipv6/conf/all/accept_ra\", L\"0\\n\"},\r\n            {L\"/proc/sys/net/ipv6/conf/default/accept_ra\", L\"0\\n\"},\r\n            {L\"/proc/sys/net/ipv6/conf/all/dad_transmits\", L\"0\\n\"},\r\n            {L\"/proc/sys/net/ipv6/conf/default/dad_transmits\", L\"0\\n\"},\r\n            {L\"/proc/sys/net/ipv6/conf/all/autoconf\", L\"0\\n\"},\r\n            {L\"/proc/sys/net/ipv6/conf/default/autoconf\", L\"0\\n\"},\r\n            {L\"/proc/sys/net/ipv6/conf/all/addr_gen_mode\", L\"1\\n\"},\r\n            {L\"/proc/sys/net/ipv6/conf/default/addr_gen_mode\", L\"1\\n\"},\r\n            {L\"/proc/sys/net/ipv6/conf/all/use_tempaddr\", L\"0\\n\"},\r\n            {L\"/proc/sys/net/ipv6/conf/default/use_tempaddr\", L\"0\\n\"},\r\n            {L\"/proc/sys/net/ipv4/conf/all/arp_filter\", L\"1\\n\"},\r\n            {L\"/proc/sys/net/ipv4/conf/all/rp_filter\", L\"0\\n\"},\r\n        };\r\n\r\n        settings.push_back({L\"/proc/sys/net/ipv4/conf/\" + NetworkTests::GetGelNicDeviceName() + L\"/rp_filter\", L\"0\\n\"});\r\n\r\n        for (const auto& setting : settings)\r\n        {\r\n            auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat \" + setting.Path);\r\n            LogInfo(\"%ls\", (setting.Path + L\" : \" + out).c_str());\r\n            VERIFY_ARE_EQUAL(setting.ExpectedValue, out);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(FirewallRulesExpectedBlock)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        SKIP_TEST_UNSTABLE();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::ValidateInitialFirewallState(NetworkTests::FirewallObjects::Required);\r\n        NetworkTests::FirewallRuleBlockedTests(NetworkTests::FirewallTestConnectivity::Blocked);\r\n    }\r\n\r\n    TEST_METHOD(FirewallRulesExpectedAllow)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        SKIP_TEST_UNSTABLE();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::ValidateInitialFirewallState(NetworkTests::FirewallObjects::Required);\r\n        NetworkTests::FirewallRuleAllowedTests(NetworkTests::FirewallTestConnectivity::Allowed);\r\n    }\r\n\r\n    TEST_METHOD(FirewallRulesEnabledSetting)\r\n    {\r\n        HYPERV_FIREWALL_TEST_ONLY();\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        SKIP_TEST_UNSTABLE();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::ValidateInitialFirewallState(NetworkTests::FirewallObjects::Required);\r\n        NetworkTests::FirewallSettingEnabledTests(true);\r\n    }\r\n\r\n    TEST_METHOD(ConnectivityCheckTestDefaultSuccess)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        SKIP_TEST_UNSTABLE();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        const auto coInit = wil::CoInitializeEx();\r\n        const wil::com_ptr<INetworkListManager> networkListManager = wil::CoCreateInstance<NetworkListManager, INetworkListManager>();\r\n        VERIFY_IS_NOT_NULL(networkListManager.get());\r\n        NLM_CONNECTIVITY hostConnectivity{};\r\n        VERIFY_SUCCEEDED(networkListManager->GetConnectivity(&hostConnectivity));\r\n\r\n        // Windows\r\n        const wsl::shared::conncheck::ConnCheckResult hostResult =\r\n            wsl::shared::conncheck::CheckConnection(\"www.msftconnecttest.com\", \"ipv6.msftconnecttest.com\", \"80\");\r\n\r\n        if (hostConnectivity & NLM_CONNECTIVITY_IPV4_INTERNET)\r\n        {\r\n            VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::Success, hostResult.Ipv4Status);\r\n        }\r\n        else\r\n        {\r\n            // one of the 2 expected runtime failures\r\n            VERIFY_IS_TRUE(\r\n                hostResult.Ipv4Status == wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo ||\r\n                hostResult.Ipv4Status == wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect);\r\n        }\r\n\r\n        if (hostConnectivity & NLM_CONNECTIVITY_IPV6_INTERNET)\r\n        {\r\n            VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::Success, hostResult.Ipv4Status);\r\n        }\r\n        else\r\n        {\r\n            // one of the 2 expected runtime failures\r\n            VERIFY_IS_TRUE(\r\n                hostResult.Ipv6Status == wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo ||\r\n                hostResult.Ipv6Status == wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect);\r\n        }\r\n\r\n        // www.msftconnecttest.com will always fail IPv6 name resolution - it doesn't have any AAAA records registered for it\r\n        const int expectedErrorCode = static_cast<int>(hostResult.Ipv4Status) |\r\n                                      (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo) << 16);\r\n        LogInfo(\"RunGns(www.msftconnecttest.com, 0x%x)\", expectedErrorCode);\r\n        // TODO: pass 'expectedErrorCode' instead of 1, once the pipeline is fixed from running Init back to wsl.exe\r\n        // it returns 1 as that's the lowest 16 bit value (unknown where the upper 16 bits are trimmed)\r\n        // if ManualConnectivityValidation is set true, one can confirm from the stdout captured that the correct result was determined and returned by init.\r\n        constexpr auto testErrorCode = ManualConnectivityValidation ? expectedErrorCode : 1;\r\n        NetworkTests::RunGns(\"www.msftconnecttest.com\", AdapterId, LxGnsMessageConnectTestRequest, testErrorCode);\r\n    }\r\n\r\n    TEST_METHOD(ConnectivityCheckTestNameResolutionFailure)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        // Windows\r\n        const wsl::shared::conncheck::ConnCheckResult result =\r\n            wsl::shared::conncheck::CheckConnection(\"asdlkfadsf.bbcxzncvb\", nullptr, \"80\");\r\n\r\n        VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo, result.Ipv4Status);\r\n        VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo, result.Ipv6Status);\r\n\r\n        constexpr int expectedErrorCode = static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo) |\r\n                                          (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo) << 16);\r\n        LogInfo(\"RunGns(asdlkfadsf.bbcxzncvb, 0x%x)\", expectedErrorCode);\r\n        // TODO: pass 'expectedErrorCode' instead of 1, once the pipeline is fixed from running Init back to wsl.exe\r\n        // it returns 2 (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo))\r\n        // as that's the lowest 16 bit value (unknown where the upper 16 bits are trimmed)\r\n        // if temporarily change this back to expectedErrorCode, one can confirm from the stdout captured that the correct result was determined and returned by init.\r\n        constexpr auto testErrorCode = ManualConnectivityValidation\r\n                                           ? expectedErrorCode\r\n                                           : static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo);\r\n        NetworkTests::RunGns(\"asdlkfadsf.bbcxzncvb\", AdapterId, LxGnsMessageConnectTestRequest, testErrorCode);\r\n    }\r\n\r\n    TEST_METHOD(ConnectivityCheckTestNameResolvesButConnectivityFails)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        SKIP_TEST_UNSTABLE();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        const auto* ncsiDnsOnlyName = \"dns.msftncsi.com\";\r\n        // v4 and v6 should succeed to resolve the name, but fail to connect,\r\n        // as this NCSI name is registered in global DNS, but there's not HTTP endpoint for it\r\n\r\n        // Windows\r\n        const wsl::shared::conncheck::ConnCheckResult result =\r\n            wsl::shared::conncheck::CheckConnection(ncsiDnsOnlyName, nullptr, \"80\");\r\n\r\n        VERIFY_ARE_EQUAL(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect, result.Ipv4Status);\r\n        // v6 name resolution might fail, depending on the configuration\r\n        VERIFY_IS_TRUE(\r\n            (wsl::shared::conncheck::ConnCheckStatus::FailureGetAddrInfo == result.Ipv6Status) ||\r\n            (wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect == result.Ipv6Status));\r\n\r\n        constexpr int expectedErrorCode = static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect) |\r\n                                          (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect) << 16);\r\n        LogInfo(\"RunGns(%hs, 0x%x)\", ncsiDnsOnlyName, expectedErrorCode);\r\n        // TODO: pass 'expectedErrorCode' instead of 1, once the pipeline is fixed from running Init back to wsl.exe\r\n        // it returns 4 (static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect))\r\n        // as that's the lowest 16 bit value (unknown where the upper 16 bits are trimmed)\r\n        // if ManualConnectivityValidation is set true, one can confirm from the stdout captured that the correct result was determined and returned by init.\r\n        constexpr auto testErrorCode = ManualConnectivityValidation\r\n                                           ? expectedErrorCode\r\n                                           : static_cast<int>(wsl::shared::conncheck::ConnCheckStatus::FailureSocketConnect);\r\n        NetworkTests::RunGns(ncsiDnsOnlyName, AdapterId, LxGnsMessageConnectTestRequest, testErrorCode);\r\n    }\r\n\r\n    // Due to VM creation performance requirements, VM creation is allowed to finish even if all\r\n    // networking state has not been mirrored yet. This introduces a race condition between the\r\n    // mirroring of networking state and mirrored mode test case execution that relies on the\r\n    // networking state being mirrored.\r\n    //\r\n    // This routine resolves the race condition by waiting for networking state to be mirrored into\r\n    // the VM. Tracking all mirrored networking state is complicated, so we use a heuristic to\r\n    // simplify: default routes have been observed to be mirrored last, so if they are present in\r\n    // the VM then we consider mirroring to be completed.\r\n    static void WaitForMirroredStateInLinux()\r\n    {\r\n        const bool hostConnectivityV4 = NetworkTests::HostHasInternetConnectivity(AF_INET);\r\n        const bool hostConnectivityV6 = NetworkTests::HostHasInternetConnectivity(AF_INET6);\r\n\r\n        Stopwatch<std::chrono::seconds> Watchdog(std::chrono::seconds(30));\r\n\r\n        do\r\n        {\r\n            // Count how many interfaces have v4/v6 connectivity, as defined by having a gateway and at least 1 preferred address.\r\n            int interfacesWithV4Connectivity = 0;\r\n            int interfacesWithV6Connectivity = 0;\r\n\r\n            // Get all interface info from the VM.\r\n            for (const auto& i : NetworkTests::GetAllInterfaceStates())\r\n            {\r\n                if (i.Gateway.has_value())\r\n                {\r\n                    for (const auto& j : i.V4Addresses)\r\n                    {\r\n                        if (j.Preferred)\r\n                        {\r\n                            interfacesWithV4Connectivity++;\r\n                            break;\r\n                        }\r\n                    }\r\n                }\r\n                if (i.V6Gateway.has_value())\r\n                {\r\n                    for (const auto& j : i.V6Addresses)\r\n                    {\r\n                        if (j.Preferred)\r\n                        {\r\n                            interfacesWithV6Connectivity++;\r\n                            break;\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n\r\n            // Consider mirroring to be complete if we have the same v4/v6 connectivity in the VM as the host.\r\n            if ((!hostConnectivityV4 || interfacesWithV4Connectivity > 0) && (!hostConnectivityV6 || interfacesWithV6Connectivity > 0))\r\n            {\r\n                break;\r\n            }\r\n\r\n            LogInfo(\"Waiting for mirrored state...\");\r\n        } while (Sleep(1000), !Watchdog.IsExpired());\r\n\r\n        VERIFY_IS_FALSE(Watchdog.IsExpired());\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionBasic)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::VerifyDnsResolutionBasic();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionDig)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::VerifyDnsResolutionDig();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionRecordTypes)\r\n    {\r\n        MIRRORED_NETWORKING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n        WaitForMirroredStateInLinux();\r\n\r\n        NetworkTests::VerifyDnsResolutionRecordTypes();\r\n    }\r\n};\r\n\r\nclass BridgedTests\r\n{\r\n    WSL_TEST_CLASS(BridgedTests)\r\n\r\n    std::optional<WslConfigChange> m_config;\r\n\r\n    TEST_CLASS_SETUP(TestClassSetup)\r\n    {\r\n        VERIFY_ARE_EQUAL(LxsstuInitialize(false), TRUE);\r\n\r\n        if (LxsstuVmMode())\r\n        {\r\n            m_config.emplace(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Bridged, .vmSwitch = L\"Default Switch\"}));\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_CLASS_CLEANUP(TestClassCleanup)\r\n    {\r\n        m_config.reset();\r\n\r\n        VERIFY_NO_THROW(LxsstuUninitialize(false));\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD(Basic)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY();\r\n\r\n        // There's no way to guarantee that an external switch will work in the test environment\r\n        // So this test just validates that the VM successfully starts.\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Bridged, .vmSwitch = L\"Default Switch\"}));\r\n\r\n        // Verify that ipv6 is disabled by default.\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/sys/net/ipv6/conf/all/disable_ipv6\");\r\n        VERIFY_ARE_EQUAL(L\"1\\n\", out);\r\n    }\r\n\r\n    TEST_METHOD(CustomMac)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY();\r\n\r\n        constexpr auto mac = L\"aa:bb:cc:dd:ee:ff\";\r\n        m_config->Update(LxssGenerateTestConfig(\r\n            {.networkingMode = wsl::core::NetworkingMode::Bridged, .vmSwitch = L\"Default Switch\", .macAddress = mac}));\r\n\r\n        VERIFY_ARE_EQUAL(mac, GetMacAddress());\r\n    }\r\n\r\n    TEST_METHOD(CustomMacDashes)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY();\r\n\r\n        // Note: The SynthNic fails to start if the first byte of the mac address is 0xff.\r\n\r\n        std::wstring mac = L\"ee-ee-dd-cc-bb-aa\";\r\n        m_config->Update(LxssGenerateTestConfig(\r\n            {.networkingMode = wsl::core::NetworkingMode::Bridged, .vmSwitch = L\"Default Switch\", .macAddress = mac}));\r\n\r\n        std::replace(mac.begin(), mac.end(), L'-', L':');\r\n        VERIFY_ARE_EQUAL(mac, GetMacAddress());\r\n    }\r\n\r\n    TEST_METHOD(Ipv6)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig(\r\n            {.networkingMode = wsl::core::NetworkingMode::Bridged, .vmSwitch = L\"Default Switch\", .ipv6 = true}));\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/sys/net/ipv6/conf/all/disable_ipv6\");\r\n        VERIFY_ARE_EQUAL(L\"0\\n\", out);\r\n    }\r\n\r\n    TEST_METHOD(SmokeTest)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY();\r\n\r\n        if (!NetworkTests::HostHasInternetConnectivity(AF_INET) && !NetworkTests::HostHasInternetConnectivity(AF_INET6))\r\n        {\r\n            LogSkipped(\"Host does not have internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Bridged, .vmSwitch = L\"Default Switch\"}));\r\n\r\n        // Verify that we have a working connection\r\n        NetworkTests::GuestClient(L\"tcp-connect:bing.com:80\");\r\n    }\r\n\r\n    TEST_METHOD(InternetConnectivityV4)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY();\r\n\r\n        if (!NetworkTests::HostHasInternetConnectivity(AF_INET))\r\n        {\r\n            LogSkipped(\"Host does not have IPv4 internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Bridged, .vmSwitch = L\"Default Switch\"}));\r\n\r\n        NetworkTests::GuestClient(L\"tcp4-connect:bing.com:80\");\r\n    }\r\n\r\n    TEST_METHOD(InternetConnectivityV6)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY();\r\n\r\n        if (!NetworkTests::HostHasInternetConnectivity(AF_INET6))\r\n        {\r\n            LogSkipped(\"Host does not have IPv6 internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Bridged, .vmSwitch = L\"Default Switch\"}));\r\n\r\n        NetworkTests::GuestClient(L\"tcp6-connect:bing.com:80\");\r\n    }\r\n};\r\n\r\nclass VirtioProxyTests\r\n{\r\n    WSL_TEST_CLASS(VirtioProxyTests)\r\n\r\n    std::optional<WslConfigChange> m_config;\r\n\r\n    TEST_CLASS_SETUP(TestClassSetup)\r\n    {\r\n        VERIFY_ARE_EQUAL(LxsstuInitialize(false), TRUE);\r\n\r\n        if (LxsstuVmMode())\r\n        {\r\n            m_config.emplace(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_CLASS_CLEANUP(TestClassCleanup)\r\n    {\r\n        m_config.reset();\r\n\r\n        VERIFY_NO_THROW(LxsstuUninitialize(false));\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD(SmokeTest)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        // Verify that we have a working connection\r\n        NetworkTests::GuestClient(L\"tcp-connect:bing.com:80\");\r\n    }\r\n\r\n    TEST_METHOD(InternetConnectivityV4)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        if (!NetworkTests::HostHasInternetConnectivity(AF_INET))\r\n        {\r\n            LogSkipped(\"Host does not have IPv4 internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        NetworkTests::GuestClient(L\"tcp4-connect:bing.com:80\");\r\n    }\r\n\r\n    TEST_METHOD(InternetConnectivityV6)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        if (!NetworkTests::HostHasInternetConnectivity(AF_INET6))\r\n        {\r\n            LogSkipped(\"Host does not have IPv6 internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        NetworkTests::WaitForIpv6DefaultRoute();\r\n\r\n        NetworkTests::GuestClient(L\"tcp6-connect:bing.com:80\");\r\n    }\r\n\r\n    TEST_METHOD(Configuration)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        const auto state = NetworkTests::GetInterfaceState(L\"eth0\");\r\n        VERIFY_IS_FALSE(state.V4Addresses.empty());\r\n        VERIFY_IS_TRUE(state.Gateway.has_value());\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /etc/resolv.conf\", 0);\r\n        const std::wregex pattern(L\"(.|\\\\n)*nameserver [0-9. ]+(.|\\\\n)*\");\r\n\r\n        VERIFY_IS_TRUE(std::regex_match(out, pattern));\r\n\r\n        // Verify that /etc/resolv.conf contains a 'search' line if the host has DNS suffixes\r\n        auto [suffixOut, suffixErr] = LxsstuLaunchPowershellAndCaptureOutput(\r\n            L\"(@((Get-DnsClientGlobalSetting).SuffixSearchList) + @((Get-DnsClient).ConnectionSpecificSuffix) | Where-Object \"\r\n            L\"{$_}).Count\");\r\n        if (_wtoi(suffixOut.c_str()) > 0)\r\n        {\r\n            VERIFY_IS_TRUE(out.find(L\"search \") != std::wstring::npos);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(GuestPortIsReleased)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        // Make sure the VM doesn't time out\r\n        WslKeepAlive keepAlive;\r\n\r\n        {\r\n            auto guestProcess = NetworkTests::BindGuestPort(L\"TCP4-LISTEN:1234\", true);\r\n            NetworkTests::BindHostPort(1234, SOCK_STREAM, IPPROTO_TCP, false);\r\n        }\r\n\r\n        wsl::shared::retry::RetryWithTimeout<void>(\r\n            [&]() {\r\n                const wil::unique_socket listenSocket(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));\r\n                THROW_HR_IF(E_ABORT, !listenSocket);\r\n\r\n                SOCKADDR_IN Address{};\r\n                Address.sin_family = AF_INET;\r\n                Address.sin_port = htons(1234);\r\n                THROW_HR_IF(E_FAIL, bind(listenSocket.get(), reinterpret_cast<SOCKADDR*>(&Address), sizeof(Address)) == SOCKET_ERROR);\r\n            },\r\n            std::chrono::seconds(1),\r\n            std::chrono::minutes(2));\r\n    }\r\n\r\n    TEST_METHOD(LoopbackGuestToHost)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        // Verify guest can connect to host on loopback (TCP only, UDP not supported)\r\n        NetworkTests::VerifyLoopbackGuestToHost(L\"127.0.0.1\", IPPROTO_TCP);\r\n        NetworkTests::VerifyLoopbackGuestToHost(L\"0.0.0.0\", IPPROTO_TCP);\r\n        // TODO: enable when v6 loopback is supported\r\n        // NetworkTests::VerifyLoopbackGuestToHost(L\"::1\", IPPROTO_TCP);\r\n        // NetworkTests::VerifyLoopbackGuestToHost(L\"::\", IPPROTO_TCP);\r\n    }\r\n\r\n    TEST_METHOD(UdpBindDoesNotPreventTcpBind)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        auto tcpPort = NetworkTests::BindGuestPort(L\"TCP4-LISTEN:1234\", true);\r\n        auto udpPort = NetworkTests::BindGuestPort(L\"UDP4-LISTEN:1234\", true);\r\n    }\r\n\r\n    TEST_METHOD(HostUdpBindDoesNotPreventGuestTcpBind)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        auto udpPort = NetworkTests::BindHostPort(2345, SOCK_DGRAM, IPPROTO_UDP, true);\r\n        auto tcpPort = NetworkTests::BindGuestPort(L\"TCP4-LISTEN:2345\", true);\r\n    }\r\n\r\n    TEST_METHOD(PortZeroBindIsTracked)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        NetworkTests::VerifyPortZeroBindIsTracked();\r\n    }\r\n\r\n    TEST_METHOD(HttpProxySimple)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n        WINHTTP_PROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy, .autoProxy = true}));\r\n        NetworkTests::VerifyHttpProxySimple();\r\n    }\r\n\r\n    TEST_METHOD(ConfigurationV6)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        if (!NetworkTests::HostHasInternetConnectivity(AF_INET6))\r\n        {\r\n            LogSkipped(\"Host does not have IPv6 internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        // Wait for the device host to send an IPv6 Router Advertisement\r\n        // before querying the interface state.\r\n        NetworkTests::WaitForIpv6DefaultRoute();\r\n\r\n        const auto state = NetworkTests::GetInterfaceState(L\"eth0\");\r\n\r\n        // Verify that the guest has a global IPv6 address assigned\r\n        VERIFY_IS_FALSE(state.V6Addresses.empty());\r\n\r\n        // Verify that the guest has an IPv6 default gateway\r\n        VERIFY_IS_TRUE(state.V6Gateway.has_value());\r\n\r\n        // Verify the guest IPv6 address matches the host global IPv6 address\r\n        const auto adapterAddresses = NetworkTests::GetAdapterAddresses(AF_INET6);\r\n        DWORD bestIndex = 0;\r\n        SOCKADDR_IN6 dest{};\r\n        dest.sin6_family = AF_INET6;\r\n        InetPtonW(AF_INET6, L\"2001:4860:4860::8888\", &dest.sin6_addr);\r\n        VERIFY_ARE_EQUAL(NO_ERROR, GetBestInterfaceEx(reinterpret_cast<SOCKADDR*>(&dest), &bestIndex));\r\n\r\n        for (auto* adapter = reinterpret_cast<const IP_ADAPTER_ADDRESSES*>(adapterAddresses.data()); adapter != nullptr;\r\n             adapter = adapter->Next)\r\n        {\r\n            if (adapter->IfIndex != bestIndex)\r\n            {\r\n                continue;\r\n            }\r\n\r\n            for (auto* unicast = adapter->FirstUnicastAddress; unicast != nullptr; unicast = unicast->Next)\r\n            {\r\n                if (unicast->Address.lpSockaddr->sa_family != AF_INET6)\r\n                {\r\n                    continue;\r\n                }\r\n\r\n                const auto& sin6 = *reinterpret_cast<SOCKADDR_IN6*>(unicast->Address.lpSockaddr);\r\n                if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || IN6_IS_ADDR_LOOPBACK(&sin6.sin6_addr))\r\n                {\r\n                    continue;\r\n                }\r\n\r\n                SOCKADDR_INET hostAddr{};\r\n                hostAddr.Ipv6 = sin6;\r\n                const auto hostAddrString = wsl::windows::common::string::SockAddrInetToWstring(hostAddr);\r\n\r\n                // The host address may not be at index 0 due to SLAAC addresses from RA\r\n                bool addressFound = false;\r\n                for (const auto& v6Addr : state.V6Addresses)\r\n                {\r\n                    if (v6Addr.Address == hostAddrString)\r\n                    {\r\n                        addressFound = true;\r\n                        break;\r\n                    }\r\n                }\r\n                VERIFY_IS_TRUE(addressFound);\r\n                break;\r\n            }\r\n\r\n            break;\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(GuestPortIsReleasedV6)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n        WINDOWS_11_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        // Make sure the VM doesn't time out\r\n        WslKeepAlive keepAlive;\r\n\r\n        {\r\n            auto guestProcess = NetworkTests::BindGuestPort(L\"TCP6-LISTEN:1234,bind=::\", true);\r\n            NetworkTests::BindHostPort(1234, SOCK_STREAM, IPPROTO_TCP, false, true);\r\n        }\r\n\r\n        wsl::shared::retry::RetryWithTimeout<void>(\r\n            [&]() {\r\n                const wil::unique_socket listenSocket(socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP));\r\n                THROW_HR_IF(E_ABORT, !listenSocket);\r\n\r\n                SOCKADDR_IN6 Address{};\r\n                Address.sin6_family = AF_INET6;\r\n                Address.sin6_port = htons(1234);\r\n                THROW_HR_IF(E_FAIL, bind(listenSocket.get(), reinterpret_cast<SOCKADDR*>(&Address), sizeof(Address)) == SOCKET_ERROR);\r\n            },\r\n            std::chrono::seconds(1),\r\n            std::chrono::minutes(2));\r\n    }\r\n\r\n    TEST_METHOD(ConfigurationV6DnsServers)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\r\n\r\n        if (!NetworkTests::HostHasInternetConnectivity(AF_INET6))\r\n        {\r\n            LogSkipped(\"Host does not have IPv6 internet connectivity. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        if (!NetworkTests::HostHasIpv6DnsServers())\r\n        {\r\n            LogSkipped(\"Host does not have IPv6 DNS servers configured. Skipping...\");\r\n            return;\r\n        }\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /etc/resolv.conf\", 0);\r\n\r\n        // Verify that /etc/resolv.conf contains at least one IPv6 nameserver\r\n        const std::wregex v6Pattern(L\"(.|\\\\n)*nameserver [0-9a-fA-F:]+\\\\n(.|\\\\n)*\");\r\n        VERIFY_IS_TRUE(std::regex_match(out, v6Pattern));\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionBasicDnsTunneling)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n        DNS_TUNNELING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy, .dnsTunneling = true}));\r\n        NetworkTests::VerifyDnsResolutionBasic();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionDigDnsTunneling)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n        DNS_TUNNELING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy, .dnsTunneling = true}));\r\n        NetworkTests::VerifyDnsResolutionDig();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionRecordTypesDnsTunneling)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n        DNS_TUNNELING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy, .dnsTunneling = true}));\r\n        NetworkTests::VerifyDnsResolutionRecordTypes();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionDigV6DnsTunneling)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n        DNS_TUNNELING_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy, .dnsTunneling = true}));\r\n        NetworkTests::VerifyDnsResolutionDigV6();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionBasic)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy, .dnsTunneling = false}));\r\n        NetworkTests::VerifyDnsResolutionBasic();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionDig)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy, .dnsTunneling = false}));\r\n        NetworkTests::VerifyDnsResolutionDig();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionRecordTypes)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy, .dnsTunneling = false}));\r\n        NetworkTests::VerifyDnsResolutionRecordTypes();\r\n    }\r\n\r\n    TEST_METHOD(DnsResolutionDigV6)\r\n    {\r\n        VIRTIOPROXY_TEST_ONLY();\r\n\r\n        m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy, .dnsTunneling = false}));\r\n        NetworkTests::VerifyDnsResolutionDigV6();\r\n    }\r\n};\r\n} // namespace NetworkTests\r\n"
  },
  {
    "path": "test/windows/Plan9Tests.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    Plan9Tests.cpp\r\n\r\nAbstract:\r\n\r\n    This file contains test cases for the plan9 logic.\r\n\r\n--*/\r\n\r\n#include \"precomp.h\"\r\n#include \"Common.h\"\r\n\r\n#define LXSST_P9_PREFIX L\"\\\\\\\\wsl.localhost\\\\\" LXSS_DISTRO_NAME_TEST_L\r\n#define LXSST_P9_TEST_DIR LXSST_P9_PREFIX L\"\\\\data\\\\p9_test\"\r\n#define LXSST_P9_CLEANUP_COMMAND_LINE L\"/bin/bash -c \\\"rm -rf /data/p9_test\\\"\"\r\n\r\n#define VERIFY_LAST_ERROR(error) VERIFY_ARE_EQUAL(static_cast<DWORD>(error), GetLastError())\r\n\r\nnamespace Plan9Tests {\r\nclass Plan9Tests\r\n{\r\n    WSL_TEST_CLASS(Plan9Tests)\r\n\r\n    // Initialize the tests\r\n    TEST_CLASS_SETUP(TestClassSetup)\r\n    {\r\n        VERIFY_ARE_EQUAL(LxsstuInitialize(TRUE), TRUE);\r\n\r\n        const auto result = std::filesystem::create_directories(LXSST_P9_TEST_DIR);\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            if (!result)\r\n            {\r\n                auto [out, _] = LxsstuLaunchPowershellAndCaptureOutput(L\"(Get-Service P9rdr).Status\", 0);\r\n                LogInfo(\"p9rdr state: %s\", out.c_str());\r\n                VERIFY_NO_THROW(LxsstuUninitialize(TRUE));\r\n            }\r\n        });\r\n\r\n        VERIFY_IS_TRUE(result);\r\n        return true;\r\n    }\r\n\r\n    // Uninitialize the tests.\r\n    TEST_CLASS_CLEANUP(TestClassCleanup)\r\n    {\r\n        LxsstuLaunchWsl(LXSST_P9_CLEANUP_COMMAND_LINE);\r\n        VERIFY_NO_THROW(LxsstuUninitialize(TRUE));\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD_CLEANUP(MethodCleanup)\r\n    {\r\n        LxssLogKernelOutput();\r\n        return true;\r\n    }\r\n\r\n    // Tests creating a file, writing to it, and reading from it.\r\n    TEST_METHOD(TestReadWriteFile)\r\n    {\r\n        constexpr std::string_view data{\"test data\"};\r\n        const auto file = CreateNewTestFile(L\"\\\\readwritetest\", data);\r\n\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(SetFilePointerEx(file.get(), {}, nullptr, FILE_BEGIN));\r\n        char buffer[1024];\r\n        DWORD bytes;\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(ReadFile(file.get(), buffer, sizeof(buffer), &bytes, nullptr));\r\n        VERIFY_ARE_EQUAL(data.size(), bytes);\r\n        VERIFY_ARE_EQUAL(data, std::string_view(buffer, bytes));\r\n    }\r\n\r\n    // Tests using a large buffer to read/write a file.\r\n    TEST_METHOD(TestReadWriteFileLarge)\r\n    {\r\n        const auto file = CreateTestFile(L\"\\\\readwritelargetest\", FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_CREATE);\r\n        char buffer[64 * 1024];\r\n        for (size_t i = 0; i < sizeof(buffer); ++i)\r\n        {\r\n            buffer[i] = i % 26 + 'a';\r\n        }\r\n\r\n        for (int i = 0; i < 10; ++i)\r\n        {\r\n            DWORD bytesWritten;\r\n            VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(file.get(), buffer, sizeof(buffer), &bytesWritten, nullptr));\r\n            VERIFY_ARE_EQUAL(sizeof(buffer), bytesWritten);\r\n        }\r\n\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(SetFilePointerEx(file.get(), {}, nullptr, FILE_BEGIN));\r\n        char buffer2[64 * 1024];\r\n        DWORD bytesRead;\r\n        for (int i = 0; i < 10; ++i)\r\n        {\r\n            VERIFY_WIN32_BOOL_SUCCEEDED(ReadFile(file.get(), buffer2, sizeof(buffer2), &bytesRead, nullptr));\r\n            VERIFY_ARE_EQUAL(sizeof(buffer), bytesRead);\r\n            VERIFY_IS_TRUE(memcmp(buffer, buffer2, sizeof(buffer)) == 0);\r\n        }\r\n\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(ReadFile(file.get(), buffer2, sizeof(buffer2), &bytesRead, nullptr));\r\n        VERIFY_ARE_EQUAL(0u, bytesRead);\r\n    }\r\n\r\n    // Tests querying and setting file information.\r\n    TEST_METHOD(TestQuerySetInfo)\r\n    {\r\n        // Check the attributes on the test directory.\r\n        FILE_BASIC_INFO basicInfo{};\r\n        auto file = CreateTestFile({}, FILE_READ_ATTRIBUTES);\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(GetFileInformationByHandleEx(file.get(), FileBasicInfo, &basicInfo, sizeof(basicInfo)));\r\n        VERIFY_IS_TRUE(WI_IsFlagSet(basicInfo.FileAttributes, FILE_ATTRIBUTE_DIRECTORY));\r\n        VERIFY_ARE_NOT_EQUAL(0, basicInfo.ChangeTime.QuadPart);\r\n        VERIFY_ARE_NOT_EQUAL(0, basicInfo.CreationTime.QuadPart);\r\n        VERIFY_ARE_NOT_EQUAL(0, basicInfo.LastAccessTime.QuadPart);\r\n        VERIFY_ARE_NOT_EQUAL(0, basicInfo.LastWriteTime.QuadPart);\r\n        FILE_STANDARD_INFO standardInfo{};\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(GetFileInformationByHandleEx(file.get(), FileStandardInfo, &standardInfo, sizeof(basicInfo)));\r\n        VERIFY_IS_TRUE(standardInfo.Directory);\r\n        VERIFY_IS_FALSE(standardInfo.DeletePending);\r\n        const auto id = GetFileId({});\r\n        VERIFY_ARE_NOT_EQUAL(0ull, id);\r\n\r\n        // Check attributes on a file.\r\n        file = CreateNewTestFile(L\"\\\\queryinfotest\", \"0123456789\");\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(GetFileInformationByHandleEx(file.get(), FileBasicInfo, &basicInfo, sizeof(basicInfo)));\r\n        VERIFY_IS_FALSE(WI_IsFlagSet(basicInfo.FileAttributes, FILE_ATTRIBUTE_DIRECTORY));\r\n        VERIFY_ARE_NOT_EQUAL(0, basicInfo.ChangeTime.QuadPart);\r\n        VERIFY_ARE_NOT_EQUAL(0, basicInfo.CreationTime.QuadPart);\r\n        VERIFY_ARE_NOT_EQUAL(0, basicInfo.LastAccessTime.QuadPart);\r\n        VERIFY_ARE_NOT_EQUAL(0, basicInfo.LastWriteTime.QuadPart);\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(GetFileInformationByHandleEx(file.get(), FileStandardInfo, &standardInfo, sizeof(basicInfo)));\r\n        VERIFY_IS_FALSE(standardInfo.Directory);\r\n        VERIFY_IS_FALSE(standardInfo.DeletePending);\r\n        VERIFY_ARE_EQUAL(1u, standardInfo.NumberOfLinks);\r\n        VERIFY_ARE_EQUAL(10, standardInfo.EndOfFile.QuadPart);\r\n        const auto id2 = GetFileId(L\"\\\\queryinfotest\");\r\n        VERIFY_ARE_NOT_EQUAL(0ull, id2);\r\n        VERIFY_ARE_NOT_EQUAL(id, id2);\r\n\r\n        // Try truncating the file.\r\n        LARGE_INTEGER size;\r\n        size.QuadPart = 5;\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(SetFilePointerEx(file.get(), size, nullptr, FILE_BEGIN));\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(SetEndOfFile(file.get()));\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(GetFileInformationByHandleEx(file.get(), FileStandardInfo, &standardInfo, sizeof(basicInfo)));\r\n        VERIFY_ARE_EQUAL(5, standardInfo.EndOfFile.QuadPart);\r\n    }\r\n\r\n    // Tests deleting files and directories.\r\n    TEST_METHOD(TestDelete)\r\n    {\r\n        // Delete a file.\r\n        CreateNewTestFile(L\"\\\\deletetestfile\", \"0123456789\");\r\n        VERIFY_IS_TRUE(CheckFileExists(L\"\\\\deletetestfile\"));\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(DeleteFileW(LXSST_P9_TEST_DIR L\"\\\\deletetestfile\"));\r\n        VERIFY_IS_FALSE(CheckFileExists(L\"\\\\deletetestfile\"));\r\n\r\n        // Delete a directory.\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(CreateDirectory(LXSST_P9_TEST_DIR L\"\\\\deletetestdir\", nullptr));\r\n        VERIFY_IS_TRUE(CheckFileExists(L\"\\\\deletetestdir\"));\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(RemoveDirectory(LXSST_P9_TEST_DIR L\"\\\\deletetestdir\"));\r\n        VERIFY_IS_FALSE(CheckFileExists(L\"\\\\deletetestdir\"));\r\n\r\n        // Try to delete non-empty directory.\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(CreateDirectory(LXSST_P9_TEST_DIR L\"\\\\deletetestdir\", nullptr));\r\n        CreateNewTestFile(L\"\\\\deletetestdir\\\\testfile\", \"0123456789\");\r\n        VERIFY_WIN32_BOOL_FAILED(RemoveDirectory(LXSST_P9_TEST_DIR L\"\\\\deletetestdir\"));\r\n        VERIFY_LAST_ERROR(ERROR_DIR_NOT_EMPTY);\r\n        VERIFY_IS_TRUE(CheckFileExists(L\"\\\\deletetestdir\"));\r\n    }\r\n\r\n    // Tests renaming files and directories.\r\n    TEST_METHOD(TestRename)\r\n    {\r\n        // Rename a file.\r\n        CreateNewTestFile(L\"\\\\renametestfile\", \"0123456789\");\r\n        auto id = GetFileId(L\"\\\\renametestfile\");\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(MoveFile(LXSST_P9_TEST_DIR L\"\\\\renametestfile\", LXSST_P9_TEST_DIR L\"\\\\renametestfile2\"));\r\n        auto id2 = GetFileId(L\"\\\\renametestfile2\");\r\n        VERIFY_ARE_EQUAL(id, id2);\r\n        VERIFY_IS_FALSE(CheckFileExists(L\"\\\\renametestfile\"));\r\n        CreateNewTestFile(L\"\\\\renametestfile\", \"abcdefg\");\r\n        id = GetFileId(L\"\\\\renametestfile\");\r\n        VERIFY_ARE_NOT_EQUAL(id, id2);\r\n        VERIFY_WIN32_BOOL_FAILED(MoveFile(LXSST_P9_TEST_DIR L\"\\\\renametestfile\", LXSST_P9_TEST_DIR L\"\\\\renametestfile2\"));\r\n        VERIFY_LAST_ERROR(ERROR_ALREADY_EXISTS);\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(\r\n            MoveFileEx(LXSST_P9_TEST_DIR L\"\\\\renametestfile\", LXSST_P9_TEST_DIR L\"\\\\renametestfile2\", MOVEFILE_REPLACE_EXISTING));\r\n\r\n        id2 = GetFileId(L\"\\\\renametestfile2\");\r\n        VERIFY_ARE_EQUAL(id, id2);\r\n\r\n        // Rename a directory\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(CreateDirectory(LXSST_P9_TEST_DIR L\"\\\\renametestdir\", nullptr));\r\n        id = GetFileId(L\"\\\\renametestdir\");\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(MoveFile(LXSST_P9_TEST_DIR L\"\\\\renametestdir\", LXSST_P9_TEST_DIR L\"\\\\renametestdir2\"));\r\n        id2 = GetFileId(L\"\\\\renametestdir2\");\r\n        VERIFY_ARE_EQUAL(id, id2);\r\n        VERIFY_IS_FALSE(CheckFileExists(L\"\\\\renametestdir\"));\r\n\r\n        // Directory over a file.\r\n        VERIFY_WIN32_BOOL_FAILED(\r\n            MoveFileEx(LXSST_P9_TEST_DIR L\"\\\\renametestdir2\", LXSST_P9_TEST_DIR L\"\\\\renametestfile2\", MOVEFILE_REPLACE_EXISTING));\r\n\r\n        VERIFY_LAST_ERROR(ERROR_DIRECTORY);\r\n\r\n        // File over a directory.\r\n        VERIFY_WIN32_BOOL_FAILED(\r\n            MoveFileEx(LXSST_P9_TEST_DIR L\"\\\\renametestfile2\", LXSST_P9_TEST_DIR L\"\\\\renametestdir2\", MOVEFILE_REPLACE_EXISTING));\r\n\r\n        VERIFY_LAST_ERROR(ERROR_ACCESS_DENIED);\r\n    }\r\n\r\n    // Tests listing the files in a directory.\r\n    TEST_METHOD(TestReadDir)\r\n    {\r\n        constexpr int fileCount = 500;\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(CreateDirectory(LXSST_P9_TEST_DIR L\"\\\\readdirtest\", nullptr));\r\n        for (int i = 0; i < fileCount; ++i)\r\n        {\r\n            wchar_t path[MAX_PATH]{};\r\n            swprintf_s(path, L\"\\\\readdirtest\\\\%d\", i);\r\n            CreateNewTestFile(path, \"0123456789\");\r\n        }\r\n\r\n        WIN32_FIND_DATA findData{};\r\n        const wil::unique_hfind find{FindFirstFile(LXSST_P9_TEST_DIR L\"\\\\readdirtest\\\\*\", &findData)};\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(static_cast<bool>(find));\r\n        int count{};\r\n        bool foundFiles[fileCount]{};\r\n        do\r\n        {\r\n            ++count;\r\n            VERIFY_ARE_NOT_EQUAL(0u, findData.dwFileAttributes);\r\n            VERIFY_ARE_NOT_EQUAL(0ull, *reinterpret_cast<PULONGLONG>(&findData.ftCreationTime));\r\n            VERIFY_ARE_NOT_EQUAL(0ull, *reinterpret_cast<PULONGLONG>(&findData.ftLastAccessTime));\r\n            VERIFY_ARE_NOT_EQUAL(0ull, *reinterpret_cast<PULONGLONG>(&findData.ftLastWriteTime));\r\n            if (findData.cFileName[0] != L'.')\r\n            {\r\n                VERIFY_ARE_EQUAL(0u, findData.nFileSizeHigh);\r\n                VERIFY_ARE_EQUAL(10u, findData.nFileSizeLow);\r\n                int file = wcstol(findData.cFileName, nullptr, 10);\r\n                VERIFY_IS_GREATER_THAN_OR_EQUAL(file, 0);\r\n                VERIFY_IS_LESS_THAN(file, fileCount);\r\n                VERIFY_IS_FALSE(foundFiles[file]);\r\n                foundFiles[file] = true;\r\n            }\r\n\r\n        } while (FindNextFile(find.get(), &findData));\r\n\r\n        VERIFY_LAST_ERROR(ERROR_NO_MORE_FILES);\r\n        VERIFY_ARE_EQUAL(fileCount + 2, count);\r\n\r\n        for (int i = 0; i < fileCount; ++i)\r\n        {\r\n            VERIFY_IS_TRUE(foundFiles[i]);\r\n        }\r\n    }\r\n\r\n    // Tests using mount points inside the WSL instance.\r\n    TEST_METHOD(TestMounts)\r\n    {\r\n        // Check access into mounts like procfs is allowed.\r\n        wil::unique_hfile file{CreateFile(\r\n            LXSST_P9_PREFIX L\"\\\\proc\\\\stat\",\r\n            FILE_GENERIC_READ,\r\n            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\r\n            nullptr,\r\n            OPEN_EXISTING,\r\n            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,\r\n            nullptr)};\r\n\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(static_cast<bool>(file));\r\n\r\n        char buffer[1024];\r\n        DWORD bytes;\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(ReadFile(file.get(), buffer, sizeof(buffer), &bytes, nullptr));\r\n        VERIFY_IS_GREATER_THAN(bytes, 0u);\r\n\r\n        // Check access into drvfs mounts is not allowed.\r\n        file.reset(CreateFile(\r\n            LXSST_P9_PREFIX L\"\\\\mnt\\\\c\",\r\n            FILE_GENERIC_READ,\r\n            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\r\n            nullptr,\r\n            OPEN_EXISTING,\r\n            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,\r\n            nullptr));\r\n\r\n        VERIFY_IS_FALSE(static_cast<bool>(file));\r\n        VERIFY_LAST_ERROR(ERROR_ACCESS_DENIED);\r\n\r\n        file.reset(CreateFile(\r\n            LXSST_P9_PREFIX L\"\\\\mnt\\\\c\\\\Windows\",\r\n            FILE_GENERIC_READ,\r\n            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\r\n            nullptr,\r\n            OPEN_EXISTING,\r\n            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,\r\n            nullptr));\r\n\r\n        VERIFY_IS_FALSE(static_cast<bool>(file));\r\n        VERIFY_LAST_ERROR(ERROR_ACCESS_DENIED);\r\n    }\r\n\r\n    TEST_METHOD(TestCreate)\r\n    {\r\n        wil::unique_hfile file;\r\n        IO_STATUS_BLOCK ioStatus;\r\n\r\n        // Check error codes for non-existing files.\r\n        auto status = CreateFileNt(&file, LXSST_P9_PREFIX L\"\\\\dat\\\\p9_test\", FILE_GENERIC_READ, ioStatus);\r\n        VERIFY_ARE_EQUAL(STATUS_OBJECT_PATH_NOT_FOUND, status);\r\n        status = CreateFileNt(&file, LXSST_P9_PREFIX L\"\\\\data\\\\foo\", FILE_GENERIC_READ, ioStatus);\r\n        VERIFY_ARE_EQUAL(STATUS_OBJECT_NAME_NOT_FOUND, status);\r\n        status = CreateFileNt(&file, LXSST_P9_PREFIX L\"\\\\etc\\\\resolve.conf\\\\foo\", FILE_GENERIC_READ, ioStatus);\r\n        VERIFY_ARE_EQUAL(STATUS_OBJECT_PATH_NOT_FOUND, status);\r\n\r\n        // Create a file.\r\n        VERIFY_NT_SUCCESS(CreateFileNt(&file, LXSST_P9_TEST_DIR L\"\\\\testfile\", FILE_GENERIC_WRITE, ioStatus, FILE_CREATE));\r\n        VERIFY_ARE_EQUAL(static_cast<ULONG_PTR>(FILE_CREATED), ioStatus.Information);\r\n\r\n        // Write some test content.\r\n        const std::string contents{\"hello\"};\r\n        DWORD bytes;\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(file.get(), contents.data(), static_cast<DWORD>(contents.size()), &bytes, nullptr));\r\n        VERIFY_ARE_EQUAL(contents.size(), bytes);\r\n        file.reset();\r\n\r\n        // Exclusive create should fail now.\r\n        status = CreateFileNt(&file, LXSST_P9_TEST_DIR L\"\\\\testfile\", FILE_GENERIC_READ, ioStatus, FILE_CREATE);\r\n        VERIFY_ARE_EQUAL(STATUS_OBJECT_NAME_COLLISION, status);\r\n\r\n        // Open-if existing file.\r\n        VERIFY_NT_SUCCESS(CreateFileNt(&file, LXSST_P9_TEST_DIR L\"\\\\testfile\", FILE_GENERIC_READ, ioStatus, FILE_OPEN_IF));\r\n        VERIFY_ARE_EQUAL(static_cast<ULONG_PTR>(FILE_OPENED), ioStatus.Information);\r\n        LARGE_INTEGER size;\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(GetFileSizeEx(file.get(), &size));\r\n        VERIFY_ARE_EQUAL(5, size.QuadPart);\r\n\r\n        // Open-if new file.\r\n        VERIFY_NT_SUCCESS(CreateFileNt(&file, LXSST_P9_TEST_DIR L\"\\\\testfile2\", FILE_GENERIC_READ, ioStatus, FILE_OPEN_IF));\r\n        VERIFY_ARE_EQUAL(static_cast<ULONG_PTR>(FILE_CREATED), ioStatus.Information);\r\n\r\n        // Overwrite non-existing file.\r\n        status = CreateFileNt(&file, LXSST_P9_TEST_DIR L\"\\\\testfile3\", FILE_GENERIC_WRITE, ioStatus, FILE_OVERWRITE);\r\n        VERIFY_ARE_EQUAL(STATUS_OBJECT_NAME_NOT_FOUND, status);\r\n\r\n        VERIFY_NT_SUCCESS(CreateFileNt(&file, LXSST_P9_TEST_DIR L\"\\\\testfile3\", FILE_GENERIC_WRITE, ioStatus, FILE_OVERWRITE_IF));\r\n        VERIFY_ARE_EQUAL(static_cast<ULONG_PTR>(FILE_CREATED), ioStatus.Information);\r\n\r\n        // Overwrite existing file.\r\n        VERIFY_NT_SUCCESS(CreateFileNt(&file, LXSST_P9_TEST_DIR L\"\\\\testfile\", FILE_GENERIC_WRITE, ioStatus, FILE_OVERWRITE));\r\n        VERIFY_ARE_EQUAL(static_cast<ULONG_PTR>(FILE_OVERWRITTEN), ioStatus.Information);\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(GetFileSizeEx(file.get(), &size));\r\n        VERIFY_ARE_EQUAL(0, size.QuadPart);\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(file.get(), contents.data(), static_cast<DWORD>(contents.size()), &bytes, nullptr));\r\n        VERIFY_ARE_EQUAL(contents.size(), bytes);\r\n        file.reset();\r\n        VERIFY_NT_SUCCESS(CreateFileNt(&file, LXSST_P9_TEST_DIR L\"\\\\testfile\", FILE_GENERIC_WRITE, ioStatus, FILE_OVERWRITE_IF));\r\n        VERIFY_ARE_EQUAL(static_cast<ULONG_PTR>(FILE_OVERWRITTEN), ioStatus.Information);\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(GetFileSizeEx(file.get(), &size));\r\n        VERIFY_ARE_EQUAL(0, size.QuadPart);\r\n\r\n        // Open a directory with FILE_NON_DIRECTORY_FILE.\r\n        status = CreateFileNt(&file, LXSST_P9_TEST_DIR, FILE_GENERIC_READ, ioStatus, FILE_OPEN, 0, FILE_NON_DIRECTORY_FILE);\r\n        VERIFY_ARE_EQUAL(STATUS_FILE_IS_A_DIRECTORY, status);\r\n\r\n        // Open a file with FILE_DIRECTORY_FILE.\r\n        status = CreateFileNt(&file, LXSST_P9_TEST_DIR L\"\\\\testfile\", FILE_GENERIC_READ, ioStatus, FILE_OPEN, 0, FILE_DIRECTORY_FILE);\r\n        VERIFY_ARE_EQUAL(STATUS_NOT_A_DIRECTORY, status);\r\n    }\r\n\r\n    static auto EnablePlan9Logging()\r\n    {\r\n        LxssWriteWslDistroConfig(\"[fileServer]\\nlogFile=/plan9-logs.txt\\nlogTruncate=false\\nlogLevel=5\");\r\n\r\n        return wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [] {\r\n            // clean up wsl.conf file\r\n            LxsstuLaunchWsl(L\"rm /etc/wsl.conf\");\r\n            TerminateDistribution();\r\n        });\r\n    }\r\n\r\n    TEST_METHOD(TestPlan9ServerTimeout)\r\n    {\r\n        // This test has proven to be unstable, most likely because another program opens a file inside the distro, which prevents it from terminating.\r\n        SKIP_TEST_UNSTABLE();\r\n\r\n        auto revertLogging = EnablePlan9Logging();\r\n\r\n        auto dumpLogs = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n            const auto output = LxsstuLaunchWslAndCaptureOutput(L\"cat /plan9-logs.txt\");\r\n            LogInfo(\"Plan9 logs: %s\", output.first.c_str());\r\n        });\r\n\r\n        wsl::windows::common::SvcComm service;\r\n        auto distro = service.GetDefaultDistribution();\r\n        service.TerminateInstance(&distro);\r\n\r\n        auto getDistroState = [distro, &service]() -> LxssDistributionState {\r\n            auto distros = service.EnumerateDistributions();\r\n            const auto it =\r\n                std::find_if(distros.begin(), distros.end(), [&](const auto& e) { return IsEqualGUID(distro, e.DistroGuid); });\r\n\r\n            VERIFY_ARE_NOT_EQUAL(it, distros.end());\r\n\r\n            return it->State;\r\n        };\r\n\r\n        VERIFY_ARE_EQUAL(getDistroState(), LxssDistributionStateInstalled);\r\n\r\n        // Open a file via \\\\wsl.localhost and validate that the distro does not terminate\r\n        auto file = CreateTestFile(L\"\\\\9p-test-file\", GENERIC_ALL, FILE_CREATE, FILE_FLAG_DELETE_ON_CLOSE);\r\n\r\n        // Now the distro should be running\r\n        VERIFY_ARE_EQUAL(getDistroState(), LxssDistributionStateRunning);\r\n\r\n        // Validate that the distro does not terminate until the file is closed\r\n        // Note: Distributions time out after 10 seconds.\r\n        std::this_thread::sleep_for(std::chrono::seconds(20));\r\n\r\n        // Close the file and make sure that the distro terminates\r\n        file.reset();\r\n\r\n        // The distro should now time out and stop\r\n        const auto deadline = std::chrono::steady_clock::now() + std::chrono::minutes(1);\r\n        while (std::chrono::steady_clock::now() < deadline && getDistroState() != LxssDistributionStateInstalled)\r\n        {\r\n            std::this_thread::sleep_for(std::chrono::seconds(1));\r\n        }\r\n\r\n        VERIFY_ARE_EQUAL(getDistroState(), LxssDistributionStateInstalled);\r\n    }\r\n\r\n    TEST_METHOD(TestPlan9AdditionalGroupAccess)\r\n    {\r\n        ULONG Uid{};\r\n        ULONG Gid{};\r\n\r\n        // Create a user for this test\r\n        CreateUser(L\"plan9testuser\", &Uid, &Gid);\r\n\r\n        // Create a folder that's unaccessible to plan9testuser\r\n        VERIFY_ARE_EQUAL(\r\n            LxsstuLaunchWsl(L\"mkdir -p /tmp/plan9-group-test && groupadd -f plan9testgroup && chown root:plan9testgroup \"\r\n                            L\"/tmp/plan9-group-test && \"\r\n                            L\"echo -n foo > /tmp/plan9-group-test/bar && chmod 770 /tmp/plan9-group-test\"),\r\n            0u);\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            LxsstuLaunchWsl(L\"-u root rm -rf /etc/wsl.conf /tmp/plan9-group-test\");\r\n            TerminateDistribution();\r\n        });\r\n\r\n        // Make plan9testuser the default\r\n        LxssWriteWslDistroConfig(\"[user]\\ndefault=plan9testuser\\n\");\r\n        TerminateDistribution();\r\n\r\n        // Validate that folder isn't accessible\r\n        constexpr auto path = L\"\\\\\\\\wsl.localhost\\\\\" LXSS_DISTRO_NAME_TEST \"\\\\tmp\\\\plan9-group-test\\\\bar\";\r\n        std::wifstream file(path);\r\n        VERIFY_IS_FALSE(file.good());\r\n\r\n        // Add plan9testuser to plan9testgroup\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"-u root usermod -G plan9testgroup -a plan9testuser\"), 0u);\r\n\r\n        // Validate that the file can be accessed now\r\n        TerminateDistribution();\r\n        // There's a race condition on fe_release that can cause opening this file to fail.\r\n        try\r\n        {\r\n            wsl::shared::retry::RetryWithTimeout<void>(\r\n                [&file]() {\r\n                    file.open(path);\r\n                    LogInfo(\"Failed to open %ls, %d\", path, errno);\r\n                    THROW_HR_IF(E_ABORT, !file.good());\r\n                },\r\n                std::chrono::seconds(1),\r\n                std::chrono::minutes(2));\r\n        }\r\n        catch (...)\r\n        {\r\n            LogError(\"Timed out trying to open: %ls\", path);\r\n            VERIFY_FAIL();\r\n        }\r\n\r\n        std::wstring content(3, '\\0');\r\n        VERIFY_IS_TRUE(file.read(content.data(), content.size()).good());\r\n\r\n        VERIFY_ARE_EQUAL(content, L\"foo\");\r\n    }\r\n\r\n    /* Plan9 Test Helper Methods */\r\n\r\n    static wil::unique_hfile CreateTestFile(std::wstring_view path, DWORD desiredAccess, DWORD disposition = OPEN_EXISTING, DWORD flags = 0)\r\n    {\r\n        std::wstring fullPath{LXSST_P9_TEST_DIR};\r\n        fullPath += path;\r\n        wil::unique_hfile file{CreateFile(\r\n            fullPath.c_str(),\r\n            desiredAccess,\r\n            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\r\n            nullptr,\r\n            disposition,\r\n            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | flags,\r\n            nullptr)};\r\n\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(static_cast<bool>(file));\r\n\r\n        return file;\r\n    }\r\n\r\n    wil::unique_hfile CreateNewTestFile(const std::wstring& path, std::string_view contents)\r\n    {\r\n        auto file = CreateTestFile(path, FILE_GENERIC_WRITE | FILE_GENERIC_READ, CREATE_NEW);\r\n        DWORD bytes;\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(WriteFile(file.get(), contents.data(), static_cast<DWORD>(contents.size()), &bytes, nullptr));\r\n        VERIFY_ARE_EQUAL(contents.size(), bytes);\r\n        return file;\r\n    }\r\n\r\n    static NTSTATUS CreateFileNt(\r\n        PHANDLE handle, LPCWSTR name, ACCESS_MASK desiredAccess, IO_STATUS_BLOCK& ioStatus, ULONG disposition = FILE_OPEN, ULONG attributes = 0, ULONG createOptions = 0)\r\n    {\r\n        UNICODE_STRING pathu;\r\n        THROW_IF_NTSTATUS_FAILED(RtlDosPathNameToNtPathName_U_WithStatus(name, &pathu, nullptr, nullptr));\r\n        wil::unique_process_heap_ptr<WCHAR> buffer{pathu.Buffer};\r\n        OBJECT_ATTRIBUTES oa;\r\n        InitializeObjectAttributes(&oa, &pathu, OBJ_CASE_INSENSITIVE, nullptr, nullptr);\r\n        return NtCreateFile(\r\n            handle,\r\n            desiredAccess | SYNCHRONIZE,\r\n            &oa,\r\n            &ioStatus,\r\n            nullptr,\r\n            attributes,\r\n            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\r\n            disposition,\r\n            createOptions | FILE_SYNCHRONOUS_IO_ALERT,\r\n            nullptr,\r\n            0);\r\n    }\r\n\r\n    static bool CheckFileExists(std::wstring_view path)\r\n    {\r\n        std::wstring fullPath{LXSST_P9_TEST_DIR};\r\n        fullPath += path;\r\n        if (!PathFileExists(fullPath.c_str()))\r\n        {\r\n            VERIFY_LAST_ERROR(ERROR_FILE_NOT_FOUND);\r\n            return false;\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n    ULONGLONG GetFileId(std::wstring_view path)\r\n    {\r\n        BY_HANDLE_FILE_INFORMATION info;\r\n        const auto file = CreateTestFile(path, FILE_READ_ATTRIBUTES);\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(GetFileInformationByHandle(file.get(), &info));\r\n        return static_cast<ULONGLONG>(info.nFileIndexHigh) << 32 | info.nFileIndexLow;\r\n    }\r\n};\r\n} // namespace Plan9Tests"
  },
  {
    "path": "test/windows/PluginTests.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    PluginTests.cpp\n\nAbstract:\n\n    This file contains test cases for the plugin API.\n\n--*/\n\n#include \"precomp.h\"\n#include \"Common.h\"\n#include \"registry.hpp\"\n#include \"PluginTests.h\"\n\nusing namespace wsl::windows::common::registry;\n\nextern std::wstring g_testDistroPath;\n\nclass PluginTests\n{\n    std::wstring logFile;\n    bool m_initialized = false;\n    std::wstring pluginDll;\n    std::optional<WslConfigChange> config;\n\n    WSL_TEST_CLASS(PluginTests)\n\n    TEST_CLASS_SETUP(TestClassSetup)\n    {\n        VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE);\n        m_initialized = true;\n        logFile = wil::GetCurrentDirectoryW<std::wstring>() + L\"\\\\plugin-logs.txt\";\n\n        const auto currentDll = std::filesystem::path(wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()));\n        const auto pluginDllPath = currentDll.parent_path() / L\"testplugin.dll\";\n        if (!std::filesystem::exists(pluginDllPath))\n        {\n            const std::wstring message = L\"Plugin not found in: \" + pluginDllPath.wstring();\n            VERIFY_FAIL(message.c_str());\n            return false;\n        }\n\n        pluginDll = pluginDllPath.wstring();\n\n        // Disable VM timeouts during the plugin tests\n        config.emplace(LxssGenerateTestConfig({.vmIdleTimeout = -1}));\n\n        return true;\n    }\n\n    TEST_CLASS_CLEANUP(TestClassCleanup)\n    {\n\n        auto key = OpenLxssMachineKey(KEY_ALL_ACCESS);\n        DeleteKey(key.get(), L\"Test\");\n\n        key = OpenKey(key.get(), L\"Plugins\", KEY_SET_VALUE);\n        DeleteValue(key.get(), L\"TestPlugin\");\n\n        RestartWslService();\n\n        std::wifstream file(logFile);\n        const auto fileContent = std::wstring{std::istreambuf_iterator<wchar_t>(file), {}};\n        LogInfo(\"Logfile: %ls\", fileContent.c_str());\n        file.close();\n\n        StopWslService();\n        if (!DeleteFile(logFile.c_str()))\n        {\n            VERIFY_ARE_EQUAL(ERROR_FILE_NOT_FOUND, GetLastError());\n        }\n\n        if (m_initialized)\n        {\n            LxsstuUninitialize(FALSE);\n        }\n\n        return true;\n    }\n\n    void ConfigurePlugin(PluginTestType testCase) const\n    {\n        StopWslService();\n        if (!DeleteFile(logFile.c_str()))\n        {\n            VERIFY_ARE_EQUAL(ERROR_FILE_NOT_FOUND, GetLastError());\n        }\n\n        const auto testKey = OpenTestRegistryKey(KEY_SET_VALUE);\n        WriteDword(testKey.get(), nullptr, c_testType, static_cast<DWORD>(testCase));\n        WriteString(testKey.get(), nullptr, c_logFile, logFile.c_str());\n\n        const auto lxssKey =\n            CreateKey(HKEY_LOCAL_MACHINE, L\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\Plugins\", KEY_SET_VALUE, nullptr, 0);\n        WriteString(lxssKey.get(), nullptr, L\"TestPlugin\", pluginDll.c_str());\n\n        RestartWslService();\n    }\n\n    static void StartWsl(int expectedExitCode, LPCWSTR ExpectedOutput = nullptr)\n    {\n        auto [output, error] = LxsstuLaunchWslAndCaptureOutput(L\"echo -n OK\", expectedExitCode);\n        if (expectedExitCode == 0)\n        {\n            VERIFY_ARE_EQUAL(output, L\"OK\");\n        }\n        else\n        {\n            VERIFY_ARE_EQUAL(output, ExpectedOutput);\n        }\n    }\n\n    void ValidateLogFile(LPCWSTR expected) const\n    {\n        StopWslService();\n\n        std::wifstream file(logFile);\n        auto fileContent = std::wstring{std::istreambuf_iterator<wchar_t>(file), {}};\n        LogInfo(\"Logfile: %ls\", fileContent.c_str());\n\n        auto fileLines = wsl::shared::string::Split<wchar_t>(fileContent, '\\n');\n        auto expectedLines = wsl::shared::string::Split<wchar_t>(expected, '\\n');\n\n        for (size_t i = 0; i < std::max(fileLines.size(), expectedLines.size()); i++)\n        {\n            if (i >= fileLines.size())\n            {\n                std::wstring message = L\"Line is expected but not in log file: \" + expectedLines[i];\n                VERIFY_FAIL(message.c_str());\n            }\n            else if (i >= expectedLines.size())\n            {\n                std::wstring message = L\"Line is in file but not expected: \" + fileLines[i];\n                VERIFY_FAIL(message.c_str());\n            }\n\n            const auto& expected = expectedLines[i];\n            const auto& actual = fileLines[i];\n            if (!PathMatchSpec(actual.c_str(), expected.c_str()))\n            {\n                LogInfo(\"Plugin log: %ls\", fileContent.c_str());\n                std::wstring message = L\"Line (\" + actual + L\") didn't match pattern: \" + expected;\n                VERIFY_FAIL(message.c_str());\n            }\n        }\n    }\n\n    TEST_METHOD(Success)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=1\n            VM created (settings->CustomConfigurationFlags=0)\n            Folder mounted (* -> /test-plugin)\n            Process created\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::Success);\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(CustomKernelOverriddenByPolicy)\n    {\n        WSL2_TEST_ONLY();\n\n        RegistryKeyChange policy(\n            HKEY_LOCAL_MACHINE, wsl::windows::policies::c_registryKey, wsl::windows::policies::c_allowCustomKernelUserSetting, static_cast<DWORD>(0));\n\n        WslConfigChange config(LxssGenerateTestConfig({.kernel = L\"kernel-that-doesn't-exist\"}));\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=1\n            VM created (settings->CustomConfigurationFlags=0)\n            Folder mounted (* -> /test-plugin)\n            Process created\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::Success);\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(DuplicatedPlugin)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=1\n            VM created (settings->CustomConfigurationFlags=0)\n            Folder mounted (* -> /test-plugin)\n            Process created\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::Success);\n\n        // Register the same plugin dll twice. Validate that it's only called once.\n        const auto key =\n            CreateKey(HKEY_LOCAL_MACHINE, L\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\Plugins\", KEY_SET_VALUE, nullptr, 0);\n        WriteString(key.get(), nullptr, L\"TestPlugin-duplicated\", pluginDll.c_str());\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { DeleteValue(key.get(), L\"TestPlugin-duplicated\"); });\n        RestartWslService();\n\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(CustomKernel)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=1\n            VM created (settings->CustomConfigurationFlags=1)\n            OnVmStarted: E_ACCESSDENIED\n            VM Stopping)\";\n\n#ifdef WSL_KERNEL_PATH\n\n        std::wstring kernelPath = WIDEN(WSL_KERNEL_PATH);\n\n#else\n\n        auto kernelPath = wsl::windows::common::wslutil::GetMsiPackagePath().value_or(L\"\");\n        VERIFY_IS_FALSE(kernelPath.empty());\n        kernelPath += L\"\\\\tools\\\\kernel\";\n\n#endif\n\n        WslConfigChange config(LxssGenerateTestConfig({.vmIdleTimeout = 1, .kernel = kernelPath}));\n\n        ConfigurePlugin(PluginTestType::Success);\n        StartWsl(\n            -1,\n            L\"A fatal error was returned by plugin 'TestPlugin'\\r\\nError code: \"\n            L\"Wsl/Service/CreateInstance/CreateVm/Plugin/E_ACCESSDENIED\\r\\n\");\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(CustomKernelCommandLine)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=1\n            VM created (settings->CustomConfigurationFlags=2)\n            Folder mounted (* -> /test-plugin)\n            Process created\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            VM Stopping)\";\n\n        WslConfigChange config(LxssGenerateTestConfig({.vmIdleTimeout = 1, .kernelCommandLine = L\"custom\"}));\n\n        ConfigurePlugin(PluginTestType::Success);\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(DistroIdStaysTheSame)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=10\n            VM created (settings->CustomConfigurationFlags=0)\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            VM Stopping\n            VM created (settings->CustomConfigurationFlags=0)\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            OnDistroStarted: received same GUID\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::SameDistroId);\n        StartWsl(0);\n        WslShutdown();\n        StartWsl(0);\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(InitPidIsDifferent)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=14\n            VM created (settings->CustomConfigurationFlags=0)\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            Distribution Stopping, name=test_distro, package=, PidNs=*\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            Init's pid is different (* ! = *)\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::InitPidIsDifferent);\n        StartWsl(0);\n        TerminateDistribution();\n        StartWsl(0);\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(PluginUpdateRequired)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=9\n            OnLoad: WSL_E_PLUGINREQUIRESUPDATE)\";\n\n        ConfigurePlugin(PluginTestType::PluginRequiresUpdate);\n        StartWsl(\n            -1,\n            L\"The plugin 'TestPlugin' requires a newer version of WSL. Please run: wsl.exe --update\\r\\nError code: \"\n            L\"Wsl/Service/CreateInstance/CreateVm/Plugin/WSL_E_PLUGIN_REQUIRES_UPDATE\\r\\n\");\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(APIErrors)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=7\n            VM created (settings->CustomConfigurationFlags=0)\n            API error tests passed\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::ApiErrors);\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(SuccessWSL1)\n    {\n        WSL1_TEST_ONLY();\n\n        constexpr auto ExpectedOutput = LR\"(Plugin loaded. TestMode=1)\";\n\n        ConfigurePlugin(PluginTestType::Success);\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(LoadFailureFatalWSL2)\n    {\n        WSL2_TEST_ONLY();\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=2\n            OnLoad: E_UNEXPECTED)\";\n\n        ConfigurePlugin(PluginTestType::FailToLoad);\n        StartWsl(\n            -1,\n            L\"A fatal error was returned by plugin 'TestPlugin'\\r\\nError code: \"\n            L\"Wsl/Service/CreateInstance/CreateVm/Plugin/E_UNEXPECTED\\r\\n\");\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(LoadFailureNonFatalWSL1)\n    {\n        WSL1_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=2\n            OnLoad: E_UNEXPECTED)\";\n\n        ConfigurePlugin(PluginTestType::FailToLoad);\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(VmStartFailure)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=3\n            VM created (settings->CustomConfigurationFlags=0)\n            OnVmStarted: E_UNEXPECTED\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::FailToStartVm);\n        StartWsl(\n            -1,\n            L\"A fatal error was returned by plugin 'TestPlugin'\\r\\nError code: \"\n            L\"Wsl/Service/CreateInstance/CreateVm/Plugin/E_UNEXPECTED\\r\\n\");\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(VmStartFailureWithPluginErrorTwice)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=13\n            VM created (settings->CustomConfigurationFlags=0)\n            OnVmStarted: E_UNEXPECTED\n            VM Stopping\n            VM created (settings->CustomConfigurationFlags=0)\n            OnVmStarted: E_UNEXPECTED\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::FailToStartVmWithPluginErrorMessage);\n\n        StartWsl(\n            -1,\n            L\"A fatal error was returned by plugin 'TestPlugin'. Error message: 'Plugin error message'\\r\\nError code: \"\n            L\"Wsl/Service/CreateInstance/CreateVm/Plugin/E_UNEXPECTED\\r\\n\");\n\n        StartWsl(\n            -1,\n            L\"A fatal error was returned by plugin 'TestPlugin'. Error message: 'Plugin error message'\\r\\nError code: \"\n            L\"Wsl/Service/CreateInstance/CreateVm/Plugin/E_UNEXPECTED\\r\\n\");\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(VmStopFailure)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=5\n            VM created (settings->CustomConfigurationFlags=0)\n            Distribution started, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            VM Stopping\n            OnVmStopping: E_UNEXPECTED)\";\n\n        ConfigurePlugin(PluginTestType::FailToStopVm);\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(DistributionStartFailure)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=4\n            VM created (settings->CustomConfigurationFlags=0)\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            OnDistroStarted: E_UNEXPECTED\n            VM Stopping)\";\n\n        constexpr auto ExpectedError =\n            L\"A fatal error was returned by plugin 'TestPlugin'\\r\\nError code: \"\n            L\"Wsl/Service/CreateInstance/Plugin/E_UNEXPECTED\\r\\n\";\n\n        ConfigurePlugin(PluginTestType::FailToStartDistro);\n        StartWsl(-1, ExpectedError);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(DistributionStopFailure)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=6\n            VM created (settings->CustomConfigurationFlags=0)\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n            OnDistroStopping: E_UNEXPECTED\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::FailToStopDistro);\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(ErrorMessageStartVm)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=11\n            VM created (settings->CustomConfigurationFlags=0)\n            OnVmStarted: E_FAIL\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::ErrorMessageStartVm);\n        StartWsl(\n            -1,\n            L\"A fatal error was returned by plugin 'TestPlugin'. Error message: 'StartVm plugin error message'\\r\\nError code: \"\n            L\"Wsl/Service/CreateInstance/CreateVm/Plugin/E_FAIL\\r\\n\");\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(ErrorMessageStartDistro)\n    {\n        WSL2_TEST_ONLY();\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=12\n            VM created (settings->CustomConfigurationFlags=0)\n            Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n            OnDistroStarted: E_FAIL\n            VM Stopping)\";\n\n        ConfigurePlugin(PluginTestType::ErrorMessageStartDistro);\n        StartWsl(\n            -1,\n            L\"A fatal error was returned by plugin 'TestPlugin'. Error message: 'StartDistro plugin error message'\\r\\nError \"\n            L\"code: \"\n            L\"Wsl/Service/CreateInstance/Plugin/E_FAIL\\r\\n\");\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(RegisterSuccess)\n    {\n        WSL2_TEST_ONLY();\n\n        ConfigurePlugin(PluginTestType::Success);\n\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--import plugin-test-distro . \\\"\" + g_testDistroPath + L\"\\\" --version 2\"), 0L);\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unregister plugin-test-distro\"), 0L);\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=1\n                VM created (settings->CustomConfigurationFlags=0)\n                Folder mounted (* -> /test-plugin)\n                Process created\n                Distribution registered, name=plugin-test-distro, package=, Flavor=debian, Version=12\n                Distribution unregistered, name=plugin-test-distro, package=, Flavor=debian, Version=12\n                VM Stopping)\";\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(ImportInplaceSuccess)\n    {\n        WSL2_TEST_ONLY();\n\n        ConfigurePlugin(PluginTestType::Success);\n\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--import plugin-test-distro . \\\"\" + g_testDistroPath + L\"\\\" --version 2\"), 0L);\n        WslShutdown();\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--export plugin-test-distro plugin-test-distro.vhdx --format vhd\"), 0L);\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unregister plugin-test-distro\"), 0L);\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--import-in-place plugin-test-distro-vhd plugin-test-distro.vhdx\"), 0L);\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unregister plugin-test-distro-vhd\"), 0L);\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=1\n                VM created (settings->CustomConfigurationFlags=0)\n                Folder mounted (* -> /test-plugin)\n                Process created\n                Distribution registered, name=plugin-test-distro, package=, Flavor=debian, Version=12\n                VM Stopping\n                Distribution unregistered, name=plugin-test-distro, package=, Flavor=debian, Version=12\n                VM created (settings->CustomConfigurationFlags=0)\n                Folder mounted (* -> /test-plugin)\n                Process created\n                Distribution registered, name=plugin-test-distro-vhd, package=, Flavor=debian, Version=12\n                Distribution unregistered, name=plugin-test-distro-vhd, package=, Flavor=debian, Version=12\n                VM Stopping)\";\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(RegisterUnregisterFail)\n    {\n        WSL2_TEST_ONLY();\n\n        ConfigurePlugin(PluginTestType::FailToRegisterUnregisterDistro);\n\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--import plugin-test-distro . \\\"\" + g_testDistroPath + L\"\\\" --version 2\"), 0L);\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unregister plugin-test-distro\"), 0L);\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=15\n                VM created (settings->CustomConfigurationFlags=0)\n                Distribution registered, name=plugin-test-distro, package=, Flavor=debian, Version=12\n                OnDistributionRegistered: E_UNEXPECTED\n                Distribution unregistered, name=plugin-test-distro, package=, Flavor=debian, Version=12\n                OnDistributionUnregistered: E_UNEXPECTED\n                VM Stopping)\";\n\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(ExecuteDistroCommand)\n    {\n        WSL2_TEST_ONLY();\n\n        ConfigurePlugin(PluginTestType::RunDistroCommand);\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=16\n                VM created (settings->CustomConfigurationFlags=0)\n                Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n                Process created\n                Failed process launch returned:  -2147467259\n                Invalid distro launch returned:  -2147220717\n                Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n                VM Stopping)\";\n\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n\n    TEST_METHOD(PluginToken)\n    {\n        WSL2_TEST_ONLY();\n\n        ConfigurePlugin(PluginTestType::GetUsername);\n\n        constexpr auto ExpectedOutput =\n            LR\"(Plugin loaded. TestMode=17\n                VM created (settings->CustomConfigurationFlags=0)\n                Username: *\n                Distribution started, name=test_distro, package=, PidNs=*, InitPid=*, Flavor=debian, Version=12\n                Distribution Stopping, name=test_distro, package=, PidNs=*, Flavor=debian, Version=12\n                VM Stopping)\";\n\n        StartWsl(0);\n        ValidateLogFile(ExpectedOutput);\n    }\n    // This test must run last so it doesn't break test cases that depends on plugin signature.\n    TEST_METHOD(InvalidPluginSignature)\n    {\n        WSL2_TEST_ONLY();\n\n        if constexpr (!wsl::shared::OfficialBuild)\n        {\n            LogSkipped(\"This test only applies to signed builds\");\n            return;\n        }\n\n        StopWslService();\n\n        // Append one byte at the end of the plugin dll to break its signature\n        wil::unique_handle plugin{CreateFile(pluginDll.c_str(), FILE_APPEND_DATA, 0, nullptr, OPEN_EXISTING, 0, nullptr)};\n        VERIFY_IS_TRUE(plugin.is_valid());\n\n        char c{};\n        VERIFY_IS_TRUE(WriteFile(plugin.get(), &c, sizeof(c), nullptr, nullptr));\n        plugin.reset();\n\n        ConfigurePlugin(PluginTestType::ErrorMessageStartDistro);\n        StartWsl(\n            -1,\n            L\"A fatal error was returned by plugin 'TestPlugin'\\r\\nError code: \"\n            L\"Wsl/Service/CreateInstance/CreateVm/Plugin/TRUST_E_NOSIGNATURE\\r\\n\");\n    }\n};"
  },
  {
    "path": "test/windows/PluginTests.h",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    PluginTests.h\n\nAbstract:\n\n    This file contains shared definitions used in the plugin tests.\n\n--*/\n\n#pragma once\n\n#include \"registry.hpp\"\n#include <wil/resource.h>\nconstexpr auto c_configKey = L\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\Test\";\n\nenum class PluginTestType\n{\n    Invalid,\n    Success,\n    FailToLoad,\n    FailToStartVm,\n    FailToStartDistro,\n    FailToStopVm,\n    FailToStopDistro,\n    ApiErrors,\n    PluginError,\n    PluginRequiresUpdate,\n    SameDistroId,\n    ErrorMessageStartVm,\n    ErrorMessageStartDistro,\n    FailToStartVmWithPluginErrorMessage,\n    InitPidIsDifferent,\n    FailToRegisterUnregisterDistro,\n    RunDistroCommand,\n    GetUsername\n};\n\nconstexpr auto c_testType = L\"TestType\";\nconstexpr auto c_logFile = L\"LogFile\";\n\ninline wil::unique_hkey OpenTestRegistryKey(REGSAM AccessMask)\n{\n    return wsl::windows::common::registry::CreateKey(HKEY_LOCAL_MACHINE, c_configKey, AccessMask, nullptr, REG_OPTION_VOLATILE);\n}"
  },
  {
    "path": "test/windows/PolicyTests.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    PolicyTests.cpp\n\nAbstract:\n\n    This file contains test cases for WSL policies.\n\n--*/\n\n#include \"precomp.h\"\n#include \"Common.h\"\n#include \"registry.hpp\"\n#include \"wslpolicies.h\"\n\nusing namespace wsl::windows::policies;\nusing namespace wsl::windows::common::registry;\n\nclass PolicyTest\n{\n    WSL_TEST_CLASS(PolicyTest)\n\n    bool m_initialized = false;\n\n    TEST_CLASS_SETUP(TestClassSetup)\n    {\n        const auto policies = OpenKey(HKEY_LOCAL_MACHINE, ROOT_POLICIES_KEY, KEY_CREATE_SUB_KEY, 0);\n        VERIFY_IS_TRUE(!!policies);\n\n        const auto wslPolicies = CreateKey(policies.get(), L\"WSL\");\n        VERIFY_IS_TRUE(!!wslPolicies);\n\n        VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE);\n        m_initialized = true;\n        return true;\n    }\n\n    TEST_CLASS_CLEANUP(TestClassCleanup)\n    {\n        if (m_initialized)\n        {\n            LxsstuUninitialize(FALSE);\n        }\n\n        return true;\n    }\n\n    static auto SetPolicy(LPCWSTR Name, DWORD Value)\n    {\n        return RegistryKeyChange(HKEY_LOCAL_MACHINE, c_registryKey, Name, Value);\n    }\n\n    static void ValidateWarnings(const std::wstring& expectedWarnings, bool pattern = false)\n    {\n        auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(L\"echo ok\");\n        VERIFY_ARE_EQUAL(L\"ok\\n\", output);\n\n        if (pattern)\n        {\n            if (!PathMatchSpec(warnings.c_str(), expectedWarnings.c_str()))\n            {\n                LogError(\"Warning '%ls' didn't match pattern '%ls'\", warnings.c_str(), expectedWarnings.c_str());\n                VERIFY_FAIL();\n            }\n        }\n        else\n        {\n            VERIFY_ARE_EQUAL(expectedWarnings, warnings);\n        }\n    };\n\n    TEST_METHOD(MountPolicyAllowed)\n    {\n        SKIP_TEST_ARM64();\n        WSL2_TEST_ONLY();\n\n        auto revert = SetPolicy(c_allowDiskMount, 1);\n        ValidateOutput(\n            L\"--mount DoesNotExist\",\n            L\"Failed to attach disk 'DoesNotExist' to WSL2: The system cannot find the file specified. \\r\\n\"\n            L\"Error code: Wsl/Service/AttachDisk/MountDisk/HCS/ERROR_FILE_NOT_FOUND\\r\\n\");\n    }\n\n    TEST_METHOD(MountPolicyDisabled)\n    {\n        SKIP_TEST_ARM64();\n        WSL2_TEST_ONLY();\n\n        auto revert = SetPolicy(c_allowDiskMount, 0);\n        ValidateOutput(\n            L\"--mount DoesNotExist\",\n            L\"wsl.exe --mount is disabled by the computer policy.\\r\\nError code: Wsl/Service/WSL_E_DISK_MOUNT_DISABLED\\r\\n\");\n    }\n\n    void ValidatePolicy(LPCWSTR Name, LPCWSTR Config, LPCWSTR ExpectedWarnings, const std::function<void(DWORD)>& Validate = [](auto) {})\n    {\n        WslConfigChange config(LxssGenerateTestConfig() + Config);\n\n        // Validate behavior with policy allowed\n        {\n            auto revert = SetPolicy(Name, 1);\n            WslShutdown();\n\n            ValidateWarnings(L\"\"); // Expect no warnings\n            Validate(1);\n        }\n\n        // Validate behavior with policy disabled\n        {\n            auto revert = SetPolicy(Name, 0);\n            WslShutdown();\n\n            ValidateWarnings(ExpectedWarnings);\n            Validate(0);\n        }\n\n        // Validate behavior with an invalid policy value\n        {\n            auto revert = SetPolicy(Name, 12);\n            WslShutdown();\n\n            ValidateWarnings(L\"\");\n            Validate(12);\n        }\n    }\n\n    TEST_METHOD(KernelCommandLine)\n    {\n        WSL2_TEST_ONLY();\n\n        auto validate = [](DWORD policyValue) {\n            auto [commandLine, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /proc/cmdline\");\n\n            if (policyValue == 0)\n            {\n                VERIFY_IS_FALSE(commandLine.find(L\"dummy-cmd-arg\") != std::wstring::npos);\n            }\n            else\n            {\n                VERIFY_IS_TRUE(commandLine.find(L\"dummy-cmd-arg\") != std::wstring::npos);\n            }\n        };\n\n        ValidatePolicy(\n            c_allowCustomKernelCommandLineUserSetting,\n            L\"kernelCommandLine=dummy-cmd-arg\",\n            L\"wsl: The .wslconfig setting 'wsl2.kernelCommandLine' is disabled by the computer policy.\\r\\n\",\n            validate);\n    }\n\n    TEST_METHOD(NestedVirtualization)\n    {\n        SKIP_TEST_ARM64();\n        WSL2_TEST_ONLY();\n        WINDOWS_11_TEST_ONLY();\n\n        ValidatePolicy(\n            c_allowNestedVirtualizationUserSetting,\n            L\"nestedVirtualization=true\",\n            L\"wsl: The .wslconfig setting 'wsl2.nestedVirtualization' is disabled by the computer policy.\\r\\n\");\n    }\n\n    TEST_METHOD(KernelDebugging)\n    {\n        WSL2_TEST_ONLY();\n\n        WINDOWS_11_TEST_ONLY();\n\n        ValidatePolicy(\n            c_allowKernelDebuggingUserSetting,\n            L\"kernelDebugPort=1234\",\n            L\"wsl: The .wslconfig setting 'wsl2.kernelDebugPort' is disabled by the computer policy.\\r\\n\");\n    }\n\n    TEST_METHOD(CustomKernel)\n    {\n        WSL2_TEST_ONLY();\n\n        const std::wstring wslConfigPath = wsl::windows::common::helpers::GetWslConfigPath();\n        const std::wstring nonExistentFile = L\"DoesNotExist\";\n        WslConfigChange config(LxssGenerateTestConfig({.kernel = nonExistentFile.c_str(), .kernelModules = nonExistentFile.c_str()}));\n\n        {\n            auto revert = SetPolicy(c_allowCustomKernelUserSetting, 1);\n            WslShutdown();\n\n            ValidateOutput(\n                L\"echo ok\",\n                std::format(\n                    L\"{}\\r\\nError code: Wsl/Service/CreateInstance/CreateVm/WSL_E_CUSTOM_KERNEL_NOT_FOUND\\r\\n\",\n                    wsl::shared::Localization::MessageCustomKernelNotFound(wslConfigPath, nonExistentFile)));\n        }\n\n        // Disable the custom kernel policy and validate that the expected warnings are shown.\n        {\n            auto revert = SetPolicy(c_allowCustomKernelUserSetting, 0);\n            WslShutdown();\n\n            const auto kernelWarning =\n                std::format(L\"wsl: {}\\r\\n\", wsl::shared::Localization::MessageSettingOverriddenByPolicy(L\"wsl2.kernel\"));\n            const auto modulesWarning =\n                std::format(L\"wsl: {}\\r\\n\", wsl::shared::Localization::MessageSettingOverriddenByPolicy(L\"wsl2.kernelModules\"));\n\n            ValidateWarnings(std::format(L\"{}{}\", kernelWarning, modulesWarning));\n\n            config.Update(LxssGenerateTestConfig({.kernel = nonExistentFile.c_str()}));\n            ValidateWarnings(kernelWarning);\n\n            config.Update(LxssGenerateTestConfig({.kernelModules = nonExistentFile.c_str()}));\n            ValidateWarnings(modulesWarning);\n        }\n    }\n\n    TEST_METHOD(CustomSystemDistro)\n    {\n        WSL2_TEST_ONLY();\n\n        WslConfigChange config(LxssGenerateTestConfig() + L\"systemDistro=DoesNotExist\");\n        const std::wstring wslConfigPath = wsl::windows::common::helpers::GetWslConfigPath();\n\n        {\n            auto revert = SetPolicy(c_allowCustomSystemDistroUserSetting, 1);\n            WslShutdown();\n\n            ValidateOutput(\n                L\"echo ok\",\n                L\"The custom system distribution specified in \" + wslConfigPath +\n                    L\" was not found or is not the correct format.\\r\\nError code: \"\n                    L\"Wsl/Service/CreateInstance/CreateVm/WSL_E_CUSTOM_SYSTEM_DISTRO_ERROR\\r\\n\");\n        }\n\n        {\n            auto revert = SetPolicy(c_allowCustomSystemDistroUserSetting, 0);\n            WslShutdown();\n\n            ValidateWarnings(L\"wsl: The .wslconfig setting 'wsl2.systemDistro' is disabled by the computer policy.\\r\\n\");\n        }\n    }\n\n    TEST_METHOD(CustomNetworkingMode)\n    {\n        WSL2_TEST_ONLY();\n\n        WslConfigChange config(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\n\n        {\n            auto revert = SetPolicy(c_allowCustomNetworkingModeUserSetting, 1);\n            WslShutdown();\n\n            ValidateWarnings(L\"\");\n        }\n\n        {\n            auto revertCustomMode = SetPolicy(c_allowCustomNetworkingModeUserSetting, 0);\n            WslShutdown();\n\n            ValidateWarnings(L\"wsl: The .wslconfig setting 'wsl2.networkingMode' is disabled by the computer policy.\\r\\n\");\n\n            // Validate that no warnings are shown for NAT or None\n            config.Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Nat}));\n            ValidateWarnings(L\"\");\n\n            config.Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::None}));\n            ValidateWarnings(L\"\");\n\n            // Validate that no warnings are shown if the default networking mode is set to the same value as .wslconfig.\n            auto revertDefault = SetPolicy(c_defaultNetworkingMode, static_cast<DWORD>(wsl::core::NetworkingMode::VirtioProxy));\n            config.Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::VirtioProxy}));\n            ValidateWarnings(L\"\");\n        }\n    }\n\n    TEST_METHOD(DebugShell)\n    {\n        WSL2_TEST_ONLY();\n\n        auto revert = SetPolicy(c_allowDebugShellUserSetting, 0);\n        WslShutdown();\n\n        // Only testing the negative case since the debug shell is difficult to programmatically exit.\n\n        WslKeepAlive keepAlive;\n        ValidateOutput(L\"--debug-shell\", L\"The debug shell is disabled by the computer policy.\\r\\n\", L\"\", 1);\n    }\n\n    TEST_METHOD(WSL1)\n    {\n        // Test policy registry key with allow key explicitly set.\n        {\n            auto revert = SetPolicy(c_allowWSL1, 1);\n            WslShutdown();\n\n            ValidateWarnings(L\"\");\n        }\n\n        // Disable WSL1.\n        {\n            auto revert = SetPolicy(c_allowWSL1, 0);\n            WslShutdown();\n\n            // If running as WSL2, attempt to convert the distro to WSL1. If running as WSL1, attempt to run a command.\n            if (LxsstuVmMode())\n            {\n                ValidateOutput(\n                    L\"--set-version \" LXSS_DISTRO_NAME_TEST_L L\" 1\",\n                    L\"WSL1 is disabled by the computer policy.\\r\\nError code: Wsl/Service/WSL_E_WSL1_DISABLED\\r\\n\");\n            }\n            else\n            {\n                ValidateOutput(\n                L\"echo ok\",\n                L\"WSL1 is disabled by the computer policy.\\r\\nPlease run 'wsl.exe --set-version \" LXSS_DISTRO_NAME_TEST_L L\" 2' to upgrade to WSL2.\\r\\nError code: Wsl/Service/CreateInstance/WSL_E_WSL1_DISABLED\\r\\n\");\n            }\n        }\n    }\n\n    TEST_METHOD(DisableWsl)\n    {\n        // N.B. Modifying one of the policy registry keys triggers a registry watcher in the service.\n        //      Retry for up to 30 seconds to ensure the registry watcher has time to take effect.\n        auto createInstance = [&](HRESULT expectedResult) {\n            HRESULT result;\n            const auto stop = std::chrono::steady_clock::now() + std::chrono::seconds{30};\n            for (;;)\n            {\n                wil::com_ptr<ILxssUserSession> session;\n                result = CoCreateInstance(CLSID_LxssUserSession, nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&session));\n                if (result == expectedResult || std::chrono::steady_clock::now() > stop)\n                {\n                    break;\n                }\n\n                std::this_thread::sleep_for(std::chrono::milliseconds{250});\n            }\n\n            VERIFY_ARE_EQUAL(expectedResult, result);\n            if (SUCCEEDED(result))\n            {\n                VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"/bin/true\"), 0u);\n            }\n            else\n            {\n                auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"/bin/true\", -1);\n                VERIFY_ARE_EQUAL(\n                    output,\n                    L\"This program is blocked by group policy. For more information, contact your system administrator. \"\n                    L\"\\r\\nError \"\n                    L\"code: Wsl/ERROR_ACCESS_DISABLED_BY_POLICY\\r\\n\");\n            }\n        };\n\n        // Set the policy registry key and validate that user session creation returns the expected result,\n        // then delete the key and ensure user session can be created.\n        auto testPolicy = [&](LPCWSTR policy, HRESULT expectedResult, bool restartService) {\n            {\n                auto revert = SetPolicy(policy, 0);\n                if (restartService)\n                {\n                    RestartWslService();\n                }\n\n                createInstance(expectedResult);\n            }\n\n            if (restartService)\n            {\n                RestartWslService();\n            }\n            createInstance(S_OK);\n        };\n\n        for (const auto restartService : {false, true})\n        {\n            // Ensure the top-level disable WSL policy works.\n            testPolicy(wsl::windows::policies::c_allowWSL, HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY), restartService);\n\n            // Verify the disable inbox WSL policy does not block lifted.\n            testPolicy(wsl::windows::policies::c_allowInboxWSL, S_OK, restartService);\n        }\n\n        // Delete and recreate the key without restarting the service to ensure the registry watcher continues to work.\n        wsl::windows::common::registry::DeleteKey(HKEY_LOCAL_MACHINE, wsl::windows::policies::c_registryKey);\n        auto key = wsl::windows::common::registry::CreateKey(HKEY_LOCAL_MACHINE, wsl::windows::policies::c_registryKey);\n        testPolicy(wsl::windows::policies::c_allowWSL, HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY), false);\n    }\n\n    TEST_METHOD(DefaultNetworkingMode)\n    {\n        WSL2_TEST_ONLY();\n\n        WslConfigChange config(LxssGenerateTestConfig());\n\n        {\n            auto revert = SetPolicy(c_defaultNetworkingMode, static_cast<DWORD>(wsl::core::NetworkingMode::None));\n            WslShutdown();\n\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"wslinfo --networking-mode | grep -iF 'none'\"), 0u);\n        }\n\n        {\n            auto revert = SetPolicy(c_defaultNetworkingMode, static_cast<DWORD>(wsl::core::NetworkingMode::VirtioProxy));\n            WslShutdown();\n\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"wslinfo --networking-mode | grep -iF 'virtioproxy'\"), 0u);\n        }\n    }\n};"
  },
  {
    "path": "test/windows/SimpleTests.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    SimpleTests.cpp\r\n\r\nAbstract:\r\n\r\n    This file contains smoke tests for WSL.\r\n\r\n--*/\r\n\r\n#include \"precomp.h\"\r\n#include \"Common.h\"\r\n\r\nnamespace SimpleTests {\r\nclass SimpleTests\r\n{\r\n    WSL_TEST_CLASS(SimpleTests)\r\n\r\n    // Initialize the tests\r\n    TEST_CLASS_SETUP(TestClassSetup)\r\n    {\r\n        VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE);\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_CLASS_CLEANUP(TestClassCleanup)\r\n    {\r\n        LxsstuUninitialize(FALSE);\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD(EchoTest)\r\n    {\r\n        const std::wstring echoExpected = L\"LOW!\\n\";\r\n        auto [output, __] = LxsstuLaunchWslAndCaptureOutput(L\"echo LOW!\");\r\n        VERIFY_ARE_EQUAL(output, echoExpected);\r\n    }\r\n\r\n    TEST_METHOD(WhoamiTest)\r\n    {\r\n        const std::wstring whoamiExpected = L\"root\\n\";\r\n        auto [output, __] = LxsstuLaunchWslAndCaptureOutput(L\"-u root whoami\");\r\n        VERIFY_ARE_EQUAL(output, whoamiExpected);\r\n    }\r\n\r\n    TEST_METHOD(ChangeDirTest)\r\n    {\r\n        const std::wstring cdExpected = L\"/root\\n\";\r\n        auto [output, __] = LxsstuLaunchWslAndCaptureOutput(L\"--cd ~ --user root pwd\");\r\n        VERIFY_ARE_EQUAL(output, cdExpected);\r\n    }\r\n\r\n    TEST_METHOD(Daemonize)\r\n    {\r\n        WslConfigChange config(LxssGenerateTestConfig({.vmIdleTimeout = 0}));\r\n        WslShutdown();\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"-- eval \\\"touch /dev/shm/backgroundmagic; daemonize $(which sleep) 30\\\"\"), (DWORD)0);\r\n\r\n        std::this_thread::sleep_for(std::chrono::seconds(20));\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"-- ls /dev/shm/backgroundmagic\"), (DWORD)0);\r\n    }\r\n\r\n    static void VerifySparse(wchar_t const* path, bool sparse)\r\n    {\r\n        DWORD attributes = ::GetFileAttributesW(path);\r\n        VERIFY_IS_FALSE(attributes == INVALID_FILE_ATTRIBUTES);\r\n        VERIFY_IS_TRUE(WI_IsFlagSet(attributes, FILE_ATTRIBUTE_SPARSE_FILE) == sparse);\r\n    }\r\n\r\n    TEST_METHOD(CheckSparse)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslConfigChange config(LxssGenerateTestConfig({.sparse = true}));\r\n\r\n        std::filesystem::path tar = std::tmpnam(nullptr);\r\n        tar += \".tar\";\r\n        LogInfo(\"tar %ls\", tar.c_str());\r\n        auto cleanupTar = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\r\n            try\r\n            {\r\n                std::filesystem::remove(tar);\r\n            }\r\n            CATCH_LOG()\r\n        });\r\n\r\n        const std::wstring tempDistro = L\"temp_distro\";\r\n        const std::filesystem::path vhdDir = std::tmpnam(nullptr);\r\n        LogInfo(\"vhdDir %ls\", vhdDir.c_str());\r\n        VERIFY_IS_TRUE(std::filesystem::create_directory(vhdDir));\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\r\n            try\r\n            {\r\n                LxsstuLaunchWsl(std::format(L\"{} {}\", WSL_UNREGISTER_ARG, tempDistro).c_str());\r\n                std::filesystem::remove_all(vhdDir);\r\n            }\r\n            CATCH_LOG()\r\n        });\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"{} {} {}\", WSL_EXPORT_ARG, LXSS_DISTRO_NAME_TEST, tar.wstring()).c_str()), (DWORD)0);\r\n        LxsstuLaunchWsl(std::format(L\"{} {}\", WSL_UNREGISTER_ARG, tempDistro).c_str());\r\n        ValidateOutput(\r\n            std::format(L\"{} {} {} {}\", WSL_IMPORT_ARG, tempDistro, vhdDir.wstring(), tar.wstring()).c_str(),\r\n            L\"The operation completed successfully. \\r\\n\",\r\n            L\"wsl: Sparse VHD support is currently disabled due to potential data corruption.\\r\\n\"\r\n            L\"To force a distribution to use a sparse VHD, please run:\\r\\n\"\r\n            L\"wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe\\r\\n\",\r\n            0);\r\n\r\n        std::filesystem::path vhdPath = vhdDir / LXSS_VM_MODE_VHD_NAME;\r\n        VerifySparse(vhdPath.c_str(), false);\r\n\r\n        WslShutdown();\r\n\r\n        // Setting a distro VHD to sparse requires the allow unsafe flag.\r\n        ValidateOutput(\r\n            std::format(L\"{} {} {} {}\", WSL_MANAGE_ARG, tempDistro, WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG, L\"true\").c_str(),\r\n            L\"Sparse VHD support is currently disabled due to potential data corruption.\\r\\n\"\r\n            L\"To force a distribution to use a sparse VHD, please run:\\r\\n\"\r\n            L\"wsl.exe --manage <DistributionName> --set-sparse true --allow-unsafe\\r\\nError code: Wsl/Service/E_INVALIDARG\\r\\n\",\r\n            L\"\",\r\n            -1);\r\n\r\n        VerifySparse(vhdPath.c_str(), false);\r\n\r\n        ValidateOutput(\r\n            std::format(L\"{} {} {} {} {}\", WSL_MANAGE_ARG, tempDistro, WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG, L\"true\", WSL_MANAGE_ARG_ALLOW_UNSAFE)\r\n                .c_str(),\r\n            L\"The operation completed successfully. \\r\\n\",\r\n            L\"\",\r\n            0);\r\n\r\n        VerifySparse(vhdPath.c_str(), true);\r\n\r\n        // Disabling sparse on a VHD does not require the allow unsafe flag.\r\n        ValidateOutput(\r\n            std::format(L\"{} {} {} {}\", WSL_MANAGE_ARG, tempDistro, WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG, L\"false\").c_str(),\r\n            L\"The operation completed successfully. \\r\\n\",\r\n            L\"\",\r\n            0);\r\n\r\n        VerifySparse(vhdPath.c_str(), false);\r\n    }\r\n\r\n    TEST_METHOD(StringHelpers)\r\n    {\r\n        std::string string1 = \"aaaBBB\";\r\n        std::string string2 = \"aaabbb\";\r\n        VERIFY_IS_TRUE(wsl::shared::string::IsEqual(string1, string2, true));\r\n        VERIFY_IS_FALSE(wsl::shared::string::IsEqual(string1, string2, false));\r\n        VERIFY_IS_TRUE(wsl::shared::string::IsEqual(string1.c_str(), string2.c_str(), true));\r\n        VERIFY_IS_FALSE(wsl::shared::string::IsEqual(string1.c_str(), string2.c_str(), false));\r\n        VERIFY_IS_TRUE(wsl::shared::string::StartsWith(string1, string2.substr(0, 3), true));\r\n        VERIFY_IS_FALSE(wsl::shared::string::StartsWith(string1, string2, false));\r\n\r\n        std::wstring wstring1 = L\"aaaBBB\";\r\n        std::wstring wstring2 = L\"aaabbb\";\r\n        VERIFY_IS_TRUE(wsl::shared::string::IsEqual(wstring1, wstring2, true));\r\n        VERIFY_IS_FALSE(wsl::shared::string::IsEqual(wstring1, wstring2, false));\r\n        VERIFY_IS_TRUE(wsl::shared::string::IsEqual(wstring1.c_str(), wstring2.c_str(), true));\r\n        VERIFY_IS_FALSE(wsl::shared::string::IsEqual(wstring1.c_str(), wstring2.c_str(), false));\r\n        VERIFY_IS_TRUE(wsl::shared::string::StartsWith(wstring1, wstring2.substr(0, 3), true));\r\n        VERIFY_IS_FALSE(wsl::shared::string::StartsWith(wstring1, wstring2, false));\r\n\r\n        // Test wsl::shared::string::ParseBool\r\n        std::vector<std::pair<LPCSTR, std::optional<bool>>> boolTests = {\r\n            {\"1\", true},\r\n            {\"0\", false},\r\n            {\"true\", true},\r\n            {\"false\", false},\r\n            {\"True\", true},\r\n            {\"False\", false},\r\n            {\"t\", std::nullopt},\r\n            {\"f\", std::nullopt},\r\n            {\"T\", std::nullopt},\r\n            {\"F\", std::nullopt},\r\n            {nullptr, std::nullopt},\r\n            {\"\", std::nullopt},\r\n            {\"2\", std::nullopt},\r\n            {\"true_\", std::nullopt},\r\n            {\"false_\", std::nullopt},\r\n        };\r\n\r\n        for (const auto& [input, expected] : boolTests)\r\n        {\r\n            VERIFY_ARE_EQUAL(expected, wsl::shared::string::ParseBool(input));\r\n\r\n            std::wstring wideString = wsl::shared::string::MultiByteToWide(input);\r\n            VERIFY_ARE_EQUAL(expected, wsl::shared::string::ParseBool(wideString.c_str()));\r\n        }\r\n\r\n        // Test wsl::shared::string::ParseMemoryString\r\n        const std::vector<std::pair<LPCSTR, std::optional<uint64_t>>> testCases{\r\n            {\"0\", 0},\r\n            {\"1\", 1},\r\n            {\" 1\", 1},\r\n            {\"1B\", 1},\r\n            {\"1K\", 1024},\r\n            {\"1KB\", 1024},\r\n            {\"2M\", 2 * 1024 * 1024},\r\n            {\"100MB\", 100 * 1024 * 1024},\r\n            {\"9G\", 9 * 1024ULL * 1024ULL * 1024ULL},\r\n            {\"44GB\", 44 * 1024ULL * 1024ULL * 1024ULL},\r\n            {\"1TB\", 1ULL << 40},\r\n            {\"2T\", 2ULL << 40},\r\n            {\"1 B\", std::nullopt},\r\n            {nullptr, std::nullopt},\r\n            {\"\", std::nullopt},\r\n            {\"foo\", std::nullopt}};\r\n\r\n        for (const auto& [input, expected] : testCases)\r\n        {\r\n            VERIFY_ARE_EQUAL(wsl::shared::string::ParseMemorySize(input), expected);\r\n\r\n            const auto wideInput = wsl::shared::string::MultiByteToWide(input);\r\n            VERIFY_ARE_EQUAL(wsl::shared::string::ParseMemorySize(wideInput.c_str()), expected);\r\n        }\r\n\r\n        // Test wsl::shared::string GUID helpers\r\n        const GUID guid = {0x1234567a, 0x1234, 0x5678, {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78}};\r\n        const std::string guidString = \"{1234567a-1234-5678-1234-567812345678}\";\r\n        const std::string guidStringNoBraces = \"1234567a-1234-5678-1234-567812345678\";\r\n        const std::vector<std::pair<LPCSTR, std::optional<GUID>>> guidTestCases{\r\n            {guidString.c_str(), guid},\r\n            {guidStringNoBraces.c_str(), guid},\r\n            {nullptr, std::nullopt},\r\n            {\"\", std::nullopt},\r\n            {\"foo\", std::nullopt},\r\n            {\"1234567G-1234-5678-1234-5678123456789\", std::nullopt},\r\n            {\"{1234567a-1234-5678-1234-567812345678\", std::nullopt},\r\n            {\"{1234567aB-1234-5678-1234-567812345678}\", std::nullopt}};\r\n\r\n        for (const auto& [input, expected] : guidTestCases)\r\n        {\r\n            VERIFY_ARE_EQUAL(expected, wsl::shared::string::ToGuid(input));\r\n            const auto wideInput = wsl::shared::string::MultiByteToWide(input);\r\n            VERIFY_ARE_EQUAL(expected, wsl::shared::string::ToGuid(wideInput));\r\n        }\r\n\r\n        VERIFY_ARE_EQUAL(guidString, wsl::shared::string::GuidToString<char>(guid));\r\n        VERIFY_ARE_EQUAL(guidString, wsl::shared::string::GuidToString<char>(guid, wsl::shared::string::GuidToStringFlags::AddBraces));\r\n        VERIFY_ARE_EQUAL(guidStringNoBraces, wsl::shared::string::GuidToString<char>(guid, wsl::shared::string::GuidToStringFlags::None));\r\n\r\n        auto upperCaseGuidString = guidStringNoBraces;\r\n        std::transform(upperCaseGuidString.begin(), upperCaseGuidString.end(), upperCaseGuidString.begin(), toupper);\r\n        VERIFY_ARE_EQUAL(upperCaseGuidString, wsl::shared::string::GuidToString<char>(guid, wsl::shared::string::GuidToStringFlags::Uppercase));\r\n\r\n        const auto wideGuidString = wsl::shared::string::MultiByteToWide(guidString);\r\n        VERIFY_ARE_EQUAL(wideGuidString, wsl::shared::string::GuidToString<wchar_t>(guid));\r\n\r\n        VERIFY_ARE_EQUAL(wideGuidString, wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::AddBraces));\r\n        const auto wideGuidStringNoBraces = wsl::shared::string::MultiByteToWide(guidStringNoBraces);\r\n        VERIFY_ARE_EQUAL(wideGuidStringNoBraces, wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::None));\r\n\r\n        auto upperCaseGuidStringWide = wideGuidStringNoBraces;\r\n        std::transform(upperCaseGuidStringWide.begin(), upperCaseGuidStringWide.end(), upperCaseGuidStringWide.begin(), toupper);\r\n        VERIFY_ARE_EQUAL(upperCaseGuidStringWide, wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::Uppercase));\r\n    }\r\n\r\n    TEST_METHOD(WindowsPathWithSpaces)\r\n    {\r\n        wil::unique_environstrings_ptr originalPath;\r\n        const DWORD pathLength = GetEnvironmentVariableW(L\"PATH\", nullptr, 0);\r\n        if (pathLength > 0)\r\n        {\r\n            originalPath.reset(static_cast<PWSTR>(HeapAlloc(GetProcessHeap(), 0, pathLength * sizeof(wchar_t))));\r\n            THROW_LAST_ERROR_IF_NULL(originalPath.get());\r\n            THROW_LAST_ERROR_IF(GetEnvironmentVariableW(L\"PATH\", originalPath.get(), pathLength) == 0);\r\n        }\r\n\r\n        auto cleanup = wil::scope_exit([&]() {\r\n            if (originalPath)\r\n            {\r\n                THROW_LAST_ERROR_IF(!SetEnvironmentVariableW(L\"PATH\", originalPath.get()));\r\n            }\r\n        });\r\n\r\n        const wchar_t* testPath =\r\n            L\"C:\\\\Program Files\\\\Git\\\\cmd;\"\r\n            L\"C:\\\\Program Files\\\\PowerShell\\\\7;\"\r\n            L\"C:\\\\Program Files (x86)\\\\Common Files;\"\r\n            L\"C:\\\\Users\\\\Test User\\\\AppData\\\\Local\\\\Programs\\\\Microsoft VS Code\\\\bin\";\r\n\r\n        THROW_LAST_ERROR_IF(!SetEnvironmentVariableW(L\"PATH\", testPath));\r\n\r\n        auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"echo $PATH\");\r\n\r\n        VERIFY_IS_TRUE(output.find(L\"/mnt/c/Program Files/Git/cmd\") != std::wstring::npos);\r\n        VERIFY_IS_TRUE(output.find(L\"/mnt/c/Program Files/PowerShell/7\") != std::wstring::npos);\r\n        VERIFY_IS_TRUE(output.find(L\"/mnt/c/Program Files (x86)/Common Files\") != std::wstring::npos);\r\n        VERIFY_IS_TRUE(output.find(L\"/mnt/c/Users/Test User/AppData/Local/Programs/Microsoft VS Code/bin\") != std::wstring::npos);\r\n    }\r\n};\r\n} // namespace SimpleTests"
  },
  {
    "path": "test/windows/UnitTests.cpp",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    UnitTests.cpp\r\n\r\nAbstract:\r\n\r\n    This file contains unit tests for WSL.\r\n\r\n--*/\r\n\r\n#include \"precomp.h\"\r\n\r\n#include \"Common.h\"\r\n#include \"install.h\"\r\n#include <AclAPI.h>\r\n#include <fstream>\r\n#include <filesystem>\r\n#include \"wslservice.h\"\r\n#include \"registry.hpp\"\r\n#include \"helpers.hpp\"\r\n#include \"svccomm.hpp\"\r\n#include \"lxfsshares.h\"\r\n#include <userenv.h>\r\n#include <nlohmann/json.hpp>\r\n#include \"Distribution.h\"\r\n#include \"WslCoreConfigInterface.h\"\r\n#include \"CommandLine.h\"\r\n\r\n#define LXSST_TEST_USERNAME L\"kerneltest\"\r\n\r\n#define LXSST_LXFS_TEST_DIR L\"lxfstest\"\r\n#define LXSST_LXFS_MKDIR_COMMAND_LINE \\\r\n    L\"/bin/bash -c \\\"mkdir /\" LXSST_LXFS_TEST_DIR \"; chown 1000:1001 /\" LXSST_LXFS_TEST_DIR L\"\\\"\"\r\n#define LXSST_LXFS_CLEANUP_COMMAND_LINE L\"/bin/bash -c \\\"rm -rf /\" LXSST_LXFS_TEST_DIR L\"\\\"\"\r\n#define LXSST_LXFS_TEST_SUB_DIR L\"testdir\"\r\n\r\n#define LXSST_FSTAB_BACKUP_COMMAND_LINE L\"/bin/bash -c 'cp /etc/fstab /etc/fstab.bak'\"\r\n#define LXSST_FSTAB_SETUP_COMMAND_LINE L\"/bin/bash -c 'echo C:\\\\\\\\ /mnt/c drvfs metadata 0 0 >> /etc/fstab'\"\r\n#define LXSST_FSTAB_CLEANUP_COMMAND_LINE L\"/bin/bash -c \\\"cp /etc/fstab.bak /etc/fstab\\\"\"\r\n\r\n#define LXSST_TESTS_INSTALL_COMMAND_LINE L\"/bin/bash -c 'cd /data/test; ./build_tests.sh'\"\r\n\r\n#define LXSST_IMPORT_DISTRO_TEST_DIR L\"C:\\\\importtest\\\\\"\r\n\r\n#define LXSST_UID_ROOT 0\r\n#define LXSST_GID_ROOT 0\r\n#define LXSST_USERNAME_ROOT L\"root\"\r\n\r\n#define LXSS_OOBE_COMPLETE_NAME L\"OOBEComplete\"\r\n\r\nconstexpr auto c_testDistributionEndpoint = L\"http://127.0.0.1:12345/\";\r\nconstexpr auto c_testDistributionJson =\r\n    LR\"({\r\n\\\"Distributions\\\":[\r\n    {\r\n        \\\"Name\\\": \\\"Debian\\\",\r\n        \\\"FriendlyName\\\": \\\"Debian\\\",\r\n        \\\"StoreAppId\\\": \\\"Dummy\\\",\r\n        \\\"Amd64\\\": true,\r\n        \\\"Arm64\\\": true,\r\n        \\\"Amd64PackageUrl\\\": null,\r\n        \\\"Arm64PackageUrl\\\": null,\r\n        \\\"PackageFamilyName\\\": \\\"Dummy\\\"\r\n    }\r\n]})\";\r\n\r\nusing wsl::windows::common::wslutil::GetSystemErrorString;\r\n\r\nextern std::wstring g_testDistroPath;\r\n\r\nnamespace UnitTests {\r\nclass UnitTests\r\n{\r\n    WSL_TEST_CLASS(UnitTests)\r\n\r\n    TEST_CLASS_SETUP(TestClassSetup)\r\n    {\r\n        VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE);\r\n\r\n        // Build the unit tests on the Linux side\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(LXSST_TESTS_INSTALL_COMMAND_LINE), (DWORD)0);\r\n\r\n        return true;\r\n    }\r\n\r\n    TEST_CLASS_CLEANUP(TestClassCleanup)\r\n    {\r\n        LxsstuLaunchWsl(LXSST_LXFS_CLEANUP_COMMAND_LINE);\r\n        LxsstuUninitialize(FALSE);\r\n        return true;\r\n    }\r\n\r\n    TEST_METHOD_CLEANUP(MethodCleanup)\r\n    {\r\n        LxssLogKernelOutput();\r\n        return true;\r\n    }\r\n\r\n    // Note: This test should run first since other test cases create files extended attributes, which causes bdstar to emit warnings during export.\r\n    TEST_METHOD(ExportDistro)\r\n    {\r\n        constexpr auto tarPath = L\"exported-test-distro.tar\";\r\n        constexpr auto vhdPath = L\"exported-test-distro.vhdx\";\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n            LOG_IF_WIN32_BOOL_FALSE(DeleteFile(tarPath));\r\n            LOG_IF_WIN32_BOOL_FALSE(DeleteFile(vhdPath));\r\n        });\r\n\r\n        {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--export {} {}\", LXSS_DISTRO_NAME_TEST_L, tarPath));\r\n\r\n            VERIFY_ARE_EQUAL(out, L\"The operation completed successfully. \\r\\n\");\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n\r\n        // Validate that the file is a valid tar\r\n        {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"bash -c 'tar tf {} | grep -iF /root/.bashrc'\", tarPath));\r\n            VERIFY_ARE_EQUAL(out, L\"./root/.bashrc\\n\");\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n\r\n        // Validate that gzip compression works\r\n        {\r\n            auto [out, err] =\r\n                LxsstuLaunchWslAndCaptureOutput(std::format(L\"--export {} {} --format tar.gz\", LXSS_DISTRO_NAME_TEST_L, tarPath));\r\n\r\n            VERIFY_ARE_EQUAL(out, L\"The operation completed successfully. \\r\\n\");\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"gzip -t {}\", tarPath)), 0L);\r\n        }\r\n\r\n        // Verify that xzip compression works\r\n        {\r\n            auto [out, err] =\r\n                LxsstuLaunchWslAndCaptureOutput(std::format(L\"--export {} {} --format tar.xz\", LXSS_DISTRO_NAME_TEST_L, tarPath));\r\n\r\n            VERIFY_ARE_EQUAL(out, L\"The operation completed successfully. \\r\\n\");\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"xz -t {}\", tarPath)), 0L);\r\n        }\r\n\r\n        // Validate that exporting as vhd works\r\n        if (LxsstuVmMode())\r\n        {\r\n            WslShutdown(); // TODO: detach disk when distribution is stopped to remove this requirement.\r\n\r\n            auto [out, err] =\r\n                LxsstuLaunchWslAndCaptureOutput(std::format(L\"--export {} {} --format vhd\", LXSS_DISTRO_NAME_TEST_L, vhdPath));\r\n\r\n            VERIFY_ARE_EQUAL(out, L\"The operation completed successfully. \\r\\n\");\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n\r\n            auto [vhdType, _] = LxsstuLaunchPowershellAndCaptureOutput(std::format(L\"(Get-VHD '{}').VhdType\", vhdPath));\r\n            VERIFY_ARE_EQUAL(vhdType, L\"Dynamic\\r\\n\");\r\n        }\r\n        else\r\n        {\r\n            auto [out, err] =\r\n                LxsstuLaunchWslAndCaptureOutput(std::format(L\"--export {} {} --format vhd\", LXSS_DISTRO_NAME_TEST_L, vhdPath), -1);\r\n\r\n            VERIFY_ARE_EQUAL(out, L\"This operation is only supported by WSL2.\\r\\nError code: Wsl/Service/WSL_E_WSL2_NEEDED\\r\\n\");\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(SystemdSafeMode)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        SKIP_TEST_UNSTABLE(); // TODO: Re-enable when this issue is solved in main.\r\n\r\n        auto revert = EnableSystemd();\r\n\r\n        // generate a new test config with safe mode enabled\r\n        WslConfigChange config(LxssGenerateTestConfig({.safeMode = true}));\r\n\r\n        // verify that even though systemd is enabled, safe mode prevents it from executing\r\n        VERIFY_IS_FALSE(IsSystemdRunning(L\"--system\", 1));\r\n\r\n        config.Update(L\"\");\r\n\r\n        // disable safe mode and verify that it systemd runs\r\n        VERIFY_IS_TRUE(IsSystemdRunning(L\"--system\"));\r\n    }\r\n\r\n    TEST_METHOD(SystemdDisabled)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // tests that systemd does not run without the wsl.conf option enabled\r\n        // run and check the output of systemctl --system\r\n        VERIFY_IS_FALSE(IsSystemdRunning(L\"--system\", 1));\r\n    }\r\n\r\n    TEST_METHOD(SystemdSystem)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        auto cleanup = wil::scope_exit([] {\r\n            // clean up wsl.conf file\r\n            const std::wstring disableSystemdCmd(LXSST_REMOVE_DISTRO_CONF_COMMAND_LINE);\r\n            LxsstuLaunchWsl(disableSystemdCmd);\r\n            TerminateDistribution();\r\n        });\r\n\r\n        auto revert = EnableSystemd();\r\n        VERIFY_IS_TRUE(IsSystemdRunning(L\"--system\"));\r\n\r\n        // Validate that systemd-networkd-wait-online.service is masked.\r\n        auto [out, _] =\r\n            LxsstuLaunchWslAndCaptureOutput(L\"systemctl status systemd-networkd-wait-online.service  | grep -iF Loaded:\");\r\n\r\n        VERIFY_ARE_EQUAL(out, L\"     Loaded: masked (Reason: Unit systemd-networkd-wait-online.service is masked.)\\n\");\r\n\r\n        // Validate that NetworkManager-wait-online.service is masked.\r\n        auto [outNm, __] =\r\n            LxsstuLaunchWslAndCaptureOutput(L\"systemctl status NetworkManager-wait-online.service  | grep -iF Loaded:\");\r\n\r\n        VERIFY_ARE_EQUAL(outNm, L\"     Loaded: masked (Reason: Unit NetworkManager-wait-online.service is masked.)\\n\");\r\n    }\r\n\r\n    TEST_METHOD(SystemdUser)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // enable systemd before creating the user.\r\n        // if not called first, the runtime directories needed for --user will not have been created\r\n        auto cleanup = EnableSystemd();\r\n\r\n        // create test user and run test as that user\r\n        ULONG TestUid;\r\n        ULONG TestGid;\r\n        CreateUser(LXSST_TEST_USERNAME, &TestUid, &TestGid);\r\n        auto userCleanup = wil::scope_exit([]() { LxsstuLaunchWsl(L\"userdel \" LXSST_TEST_USERNAME); });\r\n\r\n        auto validateUserSession = [&]() {\r\n            // verify that the user service is running\r\n            const std::wstring isServiceActiveCmd =\r\n                std::format(L\"-u {} systemctl is-active user@{}.service ; exit 0\", LXSST_TEST_USERNAME, TestUid);\r\n            std::wstring out;\r\n            std::wstring err;\r\n\r\n            try\r\n            {\r\n                std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(isServiceActiveCmd.data());\r\n            }\r\n            CATCH_LOG();\r\n\r\n            Trim(out);\r\n\r\n            if (out.compare(L\"active\") != 0)\r\n            {\r\n                LogError(\r\n                    \"Unexpected output from systemd: %ls. Stderr: %ls, cmd: %ls\", out.c_str(), err.c_str(), isServiceActiveCmd.c_str());\r\n                VERIFY_FAIL();\r\n            }\r\n\r\n            // Verify that /run/user/<uid> is a writable tmpfs mount visible in both mount namespaces.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"touch /run/user/\" + std::to_wstring(TestUid) + L\"/dummy-test-file\"), 0u);\r\n            auto command = L\"mount | grep -iF 'tmpfs on /run/user/\" + std::to_wstring(TestUid) + L\" type tmpfs (rw'\";\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(command), 0u);\r\n\r\n            const auto nonElevatedToken = GetNonElevatedToken();\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(command, nullptr, nullptr, nullptr, nonElevatedToken.get()), 0u);\r\n        };\r\n\r\n        // Validate user sessions state with gui apps disabled.\r\n        WslConfigChange config(LxssGenerateTestConfig({.guiApplications = false}));\r\n        {\r\n            validateUserSession();\r\n\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--user {} echo $DISPLAY\", LXSST_TEST_USERNAME));\r\n            VERIFY_ARE_EQUAL(out, L\"\\n\");\r\n\r\n            // N.B. The XDG_RUNTIME_DIR variable is always set by init even if gui apps are disabled.\r\n            std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--user {} echo $XDG_RUNTIME_DIR\", LXSST_TEST_USERNAME));\r\n            VERIFY_ARE_EQUAL(out, std::format(L\"/run/user/{}\\n\", TestUid));\r\n        }\r\n\r\n        // Validate user sessions state with gui apps enabled.\r\n        {\r\n            config.Update(LxssGenerateTestConfig({.guiApplications = true}));\r\n\r\n            validateUserSession();\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--user {} echo $DISPLAY\", LXSST_TEST_USERNAME));\r\n            VERIFY_ARE_EQUAL(out, L\":0\\n\");\r\n\r\n            std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--user {} echo $XDG_RUNTIME_DIR\", LXSST_TEST_USERNAME));\r\n            VERIFY_ARE_EQUAL(out, std::format(L\"/run/user/{}\\n\", TestUid));\r\n        }\r\n\r\n        // Create a 'broken' /run/user and validate that the warning is correctly displayed.\r\n        {\r\n            TerminateDistribution();\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"chmod 000 /run/user\"), 0L);\r\n\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"-u {} echo OK\", LXSST_TEST_USERNAME));\r\n\r\n            VERIFY_ARE_EQUAL(out, L\"OK\\n\");\r\n            VERIFY_ARE_EQUAL(\r\n                err, L\"wsl: Failed to start the systemd user session for 'kerneltest'. See journalctl for more details.\\n\");\r\n        }\r\n    }\r\n\r\n    static bool IsSystemdRunning(const std::wstring& SystemdScope, int ExpectedExitCode = 0)\r\n    {\r\n        // run and check the output of systemctl --system\r\n        const auto systemctlCmd = std::format(L\"systemctl '{}' is-system-running ; exit 0\", SystemdScope);\r\n        std::wstring out;\r\n        std::wstring error;\r\n\r\n        // capture the output of systemctl and trim for good measure\r\n        try\r\n        {\r\n            std::tie(out, error) = LxsstuLaunchWslAndCaptureOutput(systemctlCmd.c_str(), ExpectedExitCode);\r\n        }\r\n        CATCH_LOG()\r\n        Trim(out);\r\n\r\n        // ensure that systemd is either running in a degraded or running state\r\n        if ((out.compare(L\"degraded\") == 0) || (out.compare(L\"running\") == 0))\r\n        {\r\n            return true;\r\n        }\r\n        LogInfo(\r\n            \"Error when checking if systemd is running: %ls (scope: %ls, stderr: %ls)\", out.c_str(), SystemdScope.c_str(), error.c_str());\r\n        return false;\r\n    }\r\n\r\n    TEST_METHOD(SystemdNoClearTmpUnit)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // ensures that we don't leave state on exit\r\n        auto cleanup = EnableSystemd(\"initTimeout=0\");\r\n\r\n        // Wait for systemd to be started\r\n        VERIFY_NO_THROW(wsl::shared::retry::RetryWithTimeout<void>(\r\n            [&]() { THROW_HR_IF(E_UNEXPECTED, !IsSystemdRunning(L\"--system\")); }, std::chrono::seconds(1), std::chrono::minutes(1)));\r\n\r\n        // Validate that the X11 socket has not been deleted\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -d /tmp/.X11-unix\"), 0L);\r\n    }\r\n\r\n    TEST_METHOD(SystemdBinfmtIsRestored)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Override WSL's binfmt interpreter\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"echo ':WSLInterop:M::MZ::/bin/echo:PF' > /usr/lib/binfmt.d/dummy.conf\"), 0L);\r\n\r\n        auto cleanupBinfmt = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n            LxsstuLaunchWsl(L\"rm /usr/lib/binfmt.d/dummy.conf\");\r\n            WslShutdown(); // Required since this test registers a custom binfmt interpreter.\r\n        });\r\n\r\n        {\r\n            // Enable systemd (restarts distro).\r\n            auto cleanupSystemd = EnableSystemd();\r\n\r\n            auto validateBinfmt = []() {\r\n                // Validate that WSL's binfmt interpreter is still in place.\r\n                auto [cmdOutput, _] = LxsstuLaunchWslAndCaptureOutput(L\"cmd.exe /c echo ok\");\r\n                VERIFY_ARE_EQUAL(cmdOutput, L\"ok\\r\\n\");\r\n            };\r\n\r\n            validateBinfmt();\r\n\r\n            // Validate that this still works after restarting the distribution.\r\n            TerminateDistribution();\r\n            validateBinfmt();\r\n\r\n            // Validate that stopping or restarting systemd-binfmt doesn't break interop.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"systemctl stop systemd-binfmt.service\"), 0u);\r\n            validateBinfmt();\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"systemctl restart systemd-binfmt.service\"), 0u);\r\n            validateBinfmt();\r\n\r\n            // Validate that the unit is regenerated after a daemon-reload.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"systemctl daemon-reload && systemctl restart systemd-binfmt.service\"), 0u);\r\n            validateBinfmt();\r\n        }\r\n\r\n        {\r\n            // Enable systemd (restarts distro).\r\n            auto cleanupSystemd = EnableSystemd(\"protectBinfmt=false\");\r\n\r\n            // Validate that WSL's binfmt interpreter is overridden\r\n            auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"cmd.exe /c echo ok\");\r\n            VERIFY_IS_TRUE(wsl::shared::string::IsEqual(output, L\"/mnt/c/Windows/system32/cmd.exe cmd.exe /c echo ok\\n\", true));\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(Dup)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests dup\", L\"Dup\"));\r\n    }\r\n\r\n    TEST_METHOD(Epoll)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests epoll\", L\"Epoll\"));\r\n    }\r\n\r\n    TEST_METHOD(EventFd)\r\n    {\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests eventfd\", L\"EventFd\"));\r\n    }\r\n\r\n    TEST_METHOD(Flock)\r\n    {\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests flock\", L\"Flock\"));\r\n    }\r\n\r\n    TEST_METHOD(Fork)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests fork\", L\"Fork\"));\r\n    }\r\n\r\n    TEST_METHOD(FsCommonLxFs)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests fscommon\", L\"fscommon_lxfs\"));\r\n    }\r\n\r\n    TEST_METHOD(GetSetId)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests get_set_id\", L\"get_set_id\"));\r\n    }\r\n\r\n    TEST_METHOD(Inotify)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests inotify\", L\"INOTIFY\"));\r\n    }\r\n\r\n#if !defined(_ARM64_)\r\n\r\n    TEST_METHOD(ResourceLimits)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests resourcelimits\", L\"resourcelimits\"));\r\n    }\r\n\r\n    TEST_METHOD(Select)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests select\", L\"Select\"));\r\n    }\r\n\r\n#endif\r\n\r\n    TEST_METHOD(Madvise)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests madvise\", L\"madvise\"));\r\n    }\r\n\r\n    TEST_METHOD(Mprotect)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests mprotect\", L\"mprotect\"));\r\n    }\r\n\r\n    TEST_METHOD(Pipe)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests pipe\", L\"Pipe\"));\r\n    }\r\n\r\n    TEST_METHOD(Sched)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests sched\", L\"sched\"));\r\n    }\r\n\r\n    TEST_METHOD(SocketNonblocking)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests socket_nonblock\", L\"socket_nonblocking\"));\r\n    }\r\n\r\n    TEST_METHOD(Splice)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests splice\", L\"Splice\"));\r\n    }\r\n\r\n    TEST_METHOD(Sysfs)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests sysfs\", L\"SysFs\"));\r\n    }\r\n\r\n    TEST_METHOD(Tty)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        auto OriginalHandles = UseOriginalStdHandles();\r\n\r\n        auto Restore = wil::scope_exit([&OriginalHandles]() { RestoreTestStdHandles(OriginalHandles); });\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests tty\", L\"tty\"));\r\n    }\r\n\r\n    TEST_METHOD(Utimensat)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests utimensat\", L\"Utimensat\"));\r\n    }\r\n\r\n    TEST_METHOD(WaitPid)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests waitpid\", L\"WaitPid\"));\r\n    }\r\n\r\n    TEST_METHOD(Brk)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests brk\", L\"brk\"));\r\n    }\r\n\r\n    TEST_METHOD(Mremap)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests mremap\", L\"mremap\"));\r\n    }\r\n\r\n    TEST_METHOD(VfsAccess)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests vfsaccess\", L\"vfsaccess\"));\r\n    }\r\n\r\n    TEST_METHOD(DevPt)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        auto OriginalHandles = UseOriginalStdHandles();\r\n\r\n        auto Restore = wil::scope_exit([&OriginalHandles]() { RestoreTestStdHandles(OriginalHandles); });\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests dev_pt\", L\"dev_pt\"));\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests dev_pt_2\", L\"dev_pt_2\"));\r\n    }\r\n\r\n    TEST_METHOD(Timer)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests timer\", L\"timer\"));\r\n    }\r\n\r\n    TEST_METHOD(SysInfo)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests sysinfo\", L\"Sysinfo\"));\r\n    }\r\n\r\n    TEST_METHOD(TimerFd)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests timerfd\", L\"timerfd\"));\r\n    }\r\n\r\n    TEST_METHOD(Ioprio)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests ioprio\", L\"Ioprio\"));\r\n    }\r\n\r\n    TEST_METHOD(Interop)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests interop\", L\"interop\"));\r\n\r\n        //\r\n        // Run wsl.exe with a very long command line. This ensures that the buffer\r\n        // resizing logic that is used by the WSL init daemon is able to correctly\r\n        // handle very long messages.\r\n        //\r\n        // N.B. /bin/true ignores all arguments and always returns 0.\r\n        //\r\n\r\n        std::wstring Command{L\"/bin/true \"};\r\n        Command += std::wstring(0x1000, L'x');\r\n        VERIFY_IS_TRUE(LxsstuLaunchWsl(Command.c_str()) == 0);\r\n\r\n        // Validate that windows executable can run from the linux filesystem. See: https://github.com/microsoft/WSL/issues/10812\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"cp /mnt/c/Program\\\\ Files/WSL/wsl.exe /tmp\"), 0L);\r\n        auto [out, _] =\r\n            LxsstuLaunchWslAndCaptureOutput(L\"WSLENV=WSL_UTF8 WSL_UTF8=1 WSL_INTEROP=/run/WSL/1_interop /tmp/wsl.exe --version\");\r\n\r\n        VERIFY_IS_TRUE(out.find(TEXT(WSL_PACKAGE_VERSION)) != std::string::npos);\r\n    }\r\n\r\n    static std::wstring FormUserCommandLine(_In_ const std::wstring& Username, _In_ ULONG Uid, _In_ ULONG Gid)\r\n    {\r\n        return std::format(L\"/data/test/wsl_unit_tests user {} {} {}\", Username, Uid, Gid);\r\n    }\r\n\r\n    TEST_METHOD(User)\r\n    {\r\n        //\r\n        // Create a test user and run the test as that user.\r\n        //\r\n\r\n        ULONG TestUid;\r\n        ULONG TestGid;\r\n        CreateUser(LXSST_TEST_USERNAME, &TestUid, &TestGid);\r\n        std::wstring CommandLine = FormUserCommandLine(LXSST_TEST_USERNAME, TestUid, TestGid);\r\n        LogInfo(\"Running test as user %s\", LXSST_TEST_USERNAME);\r\n        VERIFY_NO_THROW(LxsstuRunTest(CommandLine.c_str(), L\"user\", LXSST_TEST_USERNAME));\r\n\r\n        //\r\n        // Add the user to 64 more groups to make sure > 32 groups is supported.\r\n        //\r\n\r\n        {\r\n            DistroFileChange groups(L\"/etc/group\", true);\r\n            CommandLine = std::format(L\"-- for i in $(seq 1 64); do groupadd group$i; usermod -a -G group$i {}; done\", LXSST_TEST_USERNAME);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(CommandLine), (DWORD)0);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"{} {} {}\", WSL_USER_ARG_LONG, LXSST_TEST_USERNAME, \"echo success\")), (DWORD)0);\r\n        }\r\n\r\n        //\r\n        // Run the test as root.\r\n        //\r\n\r\n        ULONG RootUid;\r\n        ULONG RootGid;\r\n        CreateUser(LXSST_USERNAME_ROOT, &RootUid, &RootGid);\r\n        CommandLine = FormUserCommandLine(LXSST_USERNAME_ROOT, LXSST_UID_ROOT, LXSST_GID_ROOT);\r\n        LogInfo(\"Running test as user %s\", LXSST_USERNAME_ROOT);\r\n        VERIFY_NO_THROW(LxsstuRunTest(CommandLine.c_str(), L\"user\", LXSST_USERNAME_ROOT));\r\n\r\n        //\r\n        // Set the default user to the newly created user.\r\n        //\r\n        // N.B. Modifying the default UID should cause the instance to be recreated and the plan9 server launched as the default user.\r\n        //\r\n\r\n        const auto wslSupport =\r\n            wil::CoCreateInstance<LxssUserSession, IWslSupport>(CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA);\r\n\r\n        ULONG Version;\r\n        ULONG DefaultUid;\r\n        wil::unique_cotaskmem_array_ptr<wil::unique_cotaskmem_ansistring> DefaultEnvironment{};\r\n        ULONG WslFlags;\r\n        VERIFY_SUCCEEDED(wslSupport->GetDistributionConfiguration(\r\n            LXSS_DISTRO_NAME_TEST_L, &Version, &DefaultUid, DefaultEnvironment.size_address<ULONG>(), &DefaultEnvironment, &WslFlags));\r\n\r\n        VERIFY_SUCCEEDED(wslSupport->SetDistributionConfiguration(LXSS_DISTRO_NAME_TEST_L, TestUid, WslFlags));\r\n        auto cleanup = wil::scope_exit([&] {\r\n            try\r\n            {\r\n                VERIFY_SUCCEEDED(wslSupport->SetDistributionConfiguration(LXSS_DISTRO_NAME_TEST_L, DefaultUid, WslFlags));\r\n            }\r\n            catch (...)\r\n            {\r\n                LogError(\"Error while restoring default user\");\r\n            }\r\n        });\r\n\r\n        //\r\n        // Create a new file using the 9p server.\r\n        //\r\n\r\n        const std::wstring Path = L\"\\\\\\\\wsl.localhost\\\\\" LXSS_DISTRO_NAME_TEST_L L\"\\\\data\\\\test\\\\default_user_test\";\r\n        const wil::unique_hfile File(CreateFile(\r\n            Path.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));\r\n\r\n        if (!File)\r\n        {\r\n            LogError(\"Failed to create file, error=%lu\", GetLastError());\r\n            VERIFY_FAIL();\r\n        }\r\n\r\n        //\r\n        // Ensure the new file was created with the correct uid.\r\n        //\r\n\r\n        VERIFY_ARE_EQUAL(\r\n            LxsstuLaunchWsl(L\"stat -c %U /data/test/default_user_test | grep -iF kerneltest\", nullptr, nullptr, nullptr, nullptr), 0u);\r\n    }\r\n\r\n    TEST_METHOD(Execve)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests execve\", L\"Execve\"));\r\n    }\r\n\r\n    TEST_METHOD(Xattr)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests xattr\", L\"xattr\"));\r\n    }\r\n\r\n    TEST_METHOD(Namespace)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests namespace\", L\"Namespace\"));\r\n    }\r\n\r\n    TEST_METHOD(BinFmt)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests binfmt\", L\"BinFmt\"));\r\n\r\n        //\r\n        // Perform a shutdown since the binfmt test modifies the binfmt config.\r\n        //\r\n\r\n        WslShutdown();\r\n    }\r\n\r\n    TEST_METHOD(Cgroup)\r\n    {\r\n        //\r\n        // For WSL1, run the cgroup unit test. For WSL2, ensure the cgroupv2 filesystem is mounted in the expected location.\r\n        //\r\n\r\n        if (!LxsstuVmMode())\r\n        {\r\n            VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests cgroup\", L\"cgroup\"));\r\n        }\r\n        else\r\n        {\r\n            VERIFY_ARE_EQUAL(\r\n                LxsstuLaunchWsl(\r\n                    L\"mount | grep -iF 'cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)'\", nullptr, nullptr, nullptr, nullptr),\r\n                0u);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(Netlink)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests netlink\", L\"Netlink\"));\r\n    }\r\n\r\n    TEST_METHOD(Random)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests random\", L\"random\"));\r\n    }\r\n\r\n    TEST_METHOD(Keymgmt)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests keymgmt\", L\"Keymgmt\"));\r\n    }\r\n\r\n    TEST_METHOD(Shm)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests shm\", L\"shm\"));\r\n    }\r\n\r\n    TEST_METHOD(Sem)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests sem\", L\"sem\"));\r\n    }\r\n\r\n    TEST_METHOD(Ttys)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests ttys\", L\"Ttys\"));\r\n    }\r\n\r\n    TEST_METHOD(OverlayFs)\r\n    {\r\n        WSL1_TEST_ONLY();\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests overlayfs\", L\"OverlayFs\"));\r\n    }\r\n\r\n    TEST_METHOD(Auxv)\r\n    {\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests auxv\", L\"auxv\"));\r\n    }\r\n\r\n    TEST_METHOD(WslInfo)\r\n    {\r\n        if (LxsstuVmMode())\r\n        {\r\n            // Ensure the `-n` option to not print newline works by validating newline counts.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"wslinfo --networking-mode | wc -l | grep 1\"), 0u);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"wslinfo --networking-mode -n | wc -l | grep 0\"), 0u);\r\n\r\n            // Ensure various wslinfo functionally works as expected.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"wslinfo --networking-mode | grep -iF 'nat'\"), 0u);\r\n\r\n            WslConfigChange config(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::None}));\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"wslinfo --networking-mode | grep -iF 'none'\"), 0u);\r\n\r\n            if (AreExperimentalNetworkingFeaturesSupported() && IsHyperVFirewallSupported())\r\n            {\r\n                config.Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n                VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"wslinfo --networking-mode | grep -iF 'mirrored'\"), 0u);\r\n            }\r\n\r\n            for (const auto enabled : {true, false})\r\n            {\r\n                config.Update(LxssGenerateTestConfig({.guiApplications = enabled}));\r\n\r\n#ifdef WSL_DEV_INSTALL_PATH\r\n\r\n                VERIFY_ARE_EQUAL(\r\n                    LxsstuLaunchWsl(std::format(L\"wslinfo --msal-proxy-path | grep -iF $(wslpath '{}')\", TEXT(WSL_DEV_INSTALL_PATH))), 0u);\r\n\r\n#else\r\n\r\n                VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"wslinfo --msal-proxy-path | grep -iF '/mnt/c/Program Files/WSL/msal.wsl.proxy.exe'\"), 0u);\r\n\r\n#endif\r\n            }\r\n        }\r\n        else\r\n        {\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"wslinfo --networking-mode | grep -iF 'wsl1'\"), 0u);\r\n        }\r\n\r\n        {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"wslinfo --version\");\r\n            VERIFY_ARE_EQUAL(out, std::format(L\"{}\\n\", WSL_PACKAGE_VERSION));\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n\r\n        {\r\n            // Ensure the old version query command still works.\r\n            const auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"wslinfo --wsl-version\");\r\n            VERIFY_ARE_EQUAL(out, std::format(L\"{}\\n\", WSL_PACKAGE_VERSION));\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n\r\n        {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"wslinfo --invalid\", 1);\r\n            VERIFY_ARE_EQUAL(out, L\"\");\r\n            VERIFY_ARE_EQUAL(\r\n                err,\r\n                L\"Invalid command line argument: --invalid\\nPlease use 'wslinfo --help' to get a list of supported \"\r\n                L\"arguments.\\n\");\r\n        }\r\n\r\n        {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"wslinfo --vm-id -n\");\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n            if (LxsstuVmMode())\r\n            {\r\n                // Ensure that the response from wslinfo has the VM ID.\r\n                auto guid = wsl::shared::string::ToGuid(out);\r\n                VERIFY_IS_TRUE(guid.has_value());\r\n                VERIFY_IS_FALSE(IsEqualGUID(guid.value(), GUID_NULL));\r\n\r\n                // Validate that the VM ID is not propagated to user commands.\r\n                std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(L\"echo -n \\\"$WSL2_VM_ID\\\"\");\r\n                VERIFY_ARE_EQUAL(out, L\"\");\r\n                VERIFY_ARE_EQUAL(err, L\"\");\r\n            }\r\n            else\r\n            {\r\n                VERIFY_ARE_EQUAL(out, L\"wsl1\");\r\n            }\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(FsTab)\r\n    {\r\n        //\r\n        // Revert the fstab file and restart the instance so everything is back in\r\n        // the default state after this test.\r\n        //\r\n\r\n        auto cleanup = wil::scope_exit([&] {\r\n            try\r\n            {\r\n                LxsstuLaunchWsl(LXSST_FSTAB_CLEANUP_COMMAND_LINE);\r\n                TerminateDistribution();\r\n                VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"/bin/true\"), 0u);\r\n            }\r\n            catch (...)\r\n            {\r\n                LogError(\"Error while cleaning up the fstab\");\r\n            }\r\n        });\r\n\r\n        //\r\n        // Create an entry in the /etc/fstab file to explicitly mount C:.\r\n        //\r\n\r\n        VERIFY_ARE_EQUAL(0u, LxsstuLaunchWsl(LXSST_FSTAB_BACKUP_COMMAND_LINE));\r\n        VERIFY_ARE_EQUAL(0u, LxsstuLaunchWsl(LXSST_FSTAB_SETUP_COMMAND_LINE));\r\n        TerminateDistribution();\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"/bin/true\"), 0u);\r\n\r\n        //\r\n        // The test will make sure /mnt/c is mounted with the options specified in\r\n        // /etc/fstab, and that it's mounted only once.\r\n        //\r\n\r\n        VERIFY_NO_THROW(LxsstuRunTest(L\"/data/test/wsl_unit_tests fstab\", L\"fstab\"));\r\n    }\r\n\r\n    TEST_METHOD(X11SocketOverTmpMount)\r\n    {\r\n        if (!LxsstuVmMode())\r\n        {\r\n            return;\r\n        }\r\n\r\n        auto cleanup = wil::scope_exit([&] {\r\n            try\r\n            {\r\n                LxsstuLaunchWsl(LXSST_FSTAB_CLEANUP_COMMAND_LINE);\r\n                TerminateDistribution();\r\n            }\r\n            catch (...)\r\n            {\r\n                LogError(\"Error while cleaning up the fstab\");\r\n            }\r\n        });\r\n\r\n        WslConfigChange configChange(LxssGenerateTestConfig({.guiApplications = true}));\r\n\r\n        //\r\n        // Create an entry in the /etc/fstab file to add a tmpfs over /tmp.\r\n        //\r\n\r\n        VERIFY_ARE_EQUAL(0u, LxsstuLaunchWsl(LXSST_FSTAB_BACKUP_COMMAND_LINE));\r\n        VERIFY_ARE_EQUAL(0u, LxsstuLaunchWsl(L\"echo 'tmpfs /tmp tmpfs rw,nodev,nosuid,size=50M 0 0' > /etc/fstab\"));\r\n        TerminateDistribution();\r\n\r\n        auto ValidateBindMount = [](HANDLE Token) {\r\n            //\r\n            // Validate that the bind mount is present.\r\n            //\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\" mount | grep -iF 'none on /tmp/.X11-unix type tmpfs'\", nullptr, nullptr, nullptr, Token), 0u);\r\n        };\r\n\r\n        //\r\n        // Verify that /tmp is mounted in both namespaces.\r\n        //\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"mount | grep -iF 'tmpfs on /tmp type tmpfs'\", nullptr, nullptr, nullptr, nullptr), 0u);\r\n\r\n        const auto nonElevatedToken = GetNonElevatedToken();\r\n        VERIFY_ARE_EQUAL(\r\n            LxsstuLaunchWsl(L\"mount | grep -iF 'tmpfs on /tmp type tmpfs'\", nullptr, nullptr, nullptr, nonElevatedToken.get()), 0u);\r\n\r\n        //\r\n        // Validate that the X11 bind mount is present and valid in both namespaces.\r\n        //\r\n\r\n        ValidateBindMount(nullptr);\r\n        ValidateBindMount(nonElevatedToken.get());\r\n    }\r\n\r\n    TEST_METHOD(ImportDistro)\r\n    {\r\n        const auto tarFileName = LXSST_IMPORT_DISTRO_TEST_DIR L\"test.tar\";\r\n        const auto rootfsDirectoryName = LXSST_IMPORT_DISTRO_TEST_DIR L\"rootfs\";\r\n        const auto vhdFileName = LXSST_IMPORT_DISTRO_TEST_DIR L\"ext4.vhdx\";\r\n        auto cleanup = wil::scope_exit([&] {\r\n            try\r\n            {\r\n                VERIFY_IS_TRUE(DeleteFileW(tarFileName));\r\n                VERIFY_IS_TRUE(RemoveDirectoryW(rootfsDirectoryName));\r\n                VERIFY_IS_TRUE(DeleteFileW(vhdFileName));\r\n                VERIFY_IS_TRUE(RemoveDirectoryW(LXSST_IMPORT_DISTRO_TEST_DIR));\r\n            }\r\n            catch (...)\r\n            {\r\n                LogError(\"Error during cleanup\")\r\n            }\r\n        });\r\n\r\n        //\r\n        // Create a dummy tar file, rootfs folder, and vhdx. These will be used\r\n        // to ensure that the user cannot import a distribution over an existing one\r\n        // even if distro registration registry keys are not present.\r\n        //\r\n\r\n        VERIFY_IS_TRUE(CreateDirectoryW(LXSST_IMPORT_DISTRO_TEST_DIR, NULL));\r\n        VERIFY_IS_TRUE(CreateDirectoryW(rootfsDirectoryName, NULL));\r\n\r\n        {\r\n            const wil::unique_hfile tarFile{CreateFileW(\r\n                tarFileName, GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL)};\r\n\r\n            VERIFY_IS_FALSE(!tarFile);\r\n\r\n            const wil::unique_hfile vhdFile{CreateFileW(\r\n                vhdFileName, GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL)};\r\n\r\n            VERIFY_IS_FALSE(!vhdFile);\r\n        }\r\n\r\n        auto validateOutput = [](LPCWSTR commandLine, LPCWSTR expectedOutput, DWORD expectedExitCode = -1) {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(commandLine, expectedExitCode);\r\n            VERIFY_ARE_EQUAL(expectedOutput, out);\r\n            VERIFY_ARE_EQUAL(L\"\", err);\r\n        };\r\n\r\n        auto version = LxsstuVmMode() ? 2 : 1;\r\n        auto commandLine = std::format(L\"--import dummy {} {} --version {}\", LXSST_IMPORT_DISTRO_TEST_DIR, tarFileName, version);\r\n        validateOutput(\r\n            commandLine.c_str(),\r\n            L\"The supplied install location is already in use.\\r\\n\"\r\n            L\"Error code: Wsl/Service/RegisterDistro/ERROR_FILE_EXISTS\\r\\n\");\r\n\r\n        commandLine = std::format(L\"--import dummy {} {} --version {}\", LXSST_IMPORT_DISTRO_TEST_DIR, vhdFileName, version);\r\n        validateOutput(commandLine.c_str(), L\"This looks like a VHD file. Use --vhd to import a VHD instead of a tar.\\r\\n\");\r\n\r\n        if (!LxsstuVmMode())\r\n        {\r\n            commandLine = std::format(L\"--import dummy {} {} --vhd --version 1\", LXSST_IMPORT_DISTRO_TEST_DIR, vhdFileName);\r\n            validateOutput(\r\n                commandLine.c_str(),\r\n                L\"This operation is only supported by WSL2.\\r\\n\"\r\n                L\"Error code: Wsl/Service/RegisterDistro/WSL_E_WSL2_NEEDED\\r\\n\");\r\n        }\r\n\r\n        //\r\n        // Create and import a new distro that where /bin/sh is an absolute symlink.\r\n        //\r\n\r\n        auto newDistroName = L\"symlink_distro\";\r\n        auto newDistroTar = L\"symlink_distro.tar\";\r\n        validateOutput(\r\n            std::format(L\"--export {} {}\", LXSS_DISTRO_NAME_TEST_L, newDistroTar).c_str(),\r\n            L\"The operation completed successfully. \\r\\n\",\r\n            0);\r\n\r\n        auto deleteNewDistro = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            VERIFY_IS_TRUE(DeleteFileW(newDistroTar));\r\n            LxsstuLaunchWsl(std::format(L\"--unregister {}\", newDistroName));\r\n        });\r\n\r\n        validateOutput(\r\n            std::format(L\"--import {} . {} --version {}\", newDistroName, newDistroTar, version).c_str(),\r\n            L\"The operation completed successfully. \\r\\n\",\r\n            0);\r\n        validateOutput(std::format(L\"-d {} -- ln -f -s /bin/bash /bin/sh\", newDistroName).c_str(), L\"\", 0);\r\n        validateOutput(\r\n            std::format(L\"--export {} {}\", newDistroName, newDistroTar).c_str(), L\"The operation completed successfully. \\r\\n\", 0);\r\n        validateOutput(std::format(L\"--unregister {}\", newDistroName).c_str(), L\"The operation completed successfully. \\r\\n\", 0);\r\n        validateOutput(\r\n            std::format(L\"--import {} . {} --version {}\", newDistroName, newDistroTar, version).c_str(),\r\n            L\"The operation completed successfully. \\r\\n\",\r\n            0);\r\n    }\r\n\r\n    TEST_METHOD(ImportDistroInvalidTar)\r\n    {\r\n        const auto commandLine = std::format(\r\n            L\"--import dummy {} C:\\\\windows\\\\system32\\\\drivers\\\\etc\\\\hosts --version {}\", LXSST_IMPORT_DISTRO_TEST_DIR, LxsstuVmMode() ? 2 : 1);\r\n\r\n        auto [out, err] = LxsstuLaunchWslAndCaptureOutput(commandLine.c_str(), -1);\r\n\r\n        VERIFY_ARE_EQUAL(\r\n            out, L\"Importing the distribution failed.\\r\\nError code: Wsl/Service/RegisterDistro/WSL_E_IMPORT_FAILED\\r\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"bsdtar: Error opening archive: Unrecognized archive format\\n\");\r\n    }\r\n\r\n    TEST_METHOD(AppxDistroDeletion)\r\n    {\r\n        // Create a dummy distro registration\r\n        const auto key = wsl::windows::common::registry::CreateKey(\r\n            HKEY_CURRENT_USER, L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\{baa405ef-1822-4bbe-84e2-30e4c6330d41}\");\r\n\r\n        wsl::windows::common::registry::WriteDword(key.get(), nullptr, L\"State\", 1);\r\n        wsl::windows::common::registry::WriteString(key.get(), nullptr, L\"DistributionName\", L\"DistroToBeDeleted\");\r\n        wsl::windows::common::registry::WriteString(\r\n            key.get(), nullptr, L\"PackageFamilyName\", L\"Microsoft.AppThatIsntInstalledForSure.1.0.0.0_8wekyb3d8bbwe\");\r\n        wsl::windows::common::registry::WriteDword(key.get(), nullptr, L\"Version\", 2);\r\n\r\n        const auto vhdDir = std::filesystem::current_path();\r\n        wsl::windows::common::registry::WriteString(key.get(), nullptr, L\"BasePath\", vhdDir.c_str());\r\n        wsl::windows::common::registry::WriteDword(key.get(), nullptr, L\"DefaultUid\", 0);\r\n        wsl::windows::common::registry::WriteDword(key.get(), nullptr, L\"Flags\", LXSS_DISTRO_FLAGS_VM_MODE);\r\n\r\n        // Create a dummy vhd\r\n        const auto vhdPath = vhdDir.string() + \"\\\\ext4.vhdx\";\r\n\r\n        wil::unique_handle vhdHandle(CreateFileA(vhdPath.c_str(), GENERIC_READ, 0, nullptr, CREATE_ALWAYS, 0, nullptr));\r\n        VERIFY_IS_TRUE(vhdHandle.is_valid());\r\n        vhdHandle.reset();\r\n\r\n        wsl::windows::common::SvcComm service;\r\n        auto isDistroListed = [&]() {\r\n            auto distros = service.EnumerateDistributions();\r\n\r\n            return std::find_if(distros.begin(), distros.end(), [&](const auto& e) {\r\n                       return wsl::shared::string::IsEqual(e.DistroName, L\"DistroToBeDeleted\", false);\r\n                   }) != distros.end();\r\n        };\r\n\r\n        // The distro should still be there, because the vhd exists.\r\n        VERIFY_IS_TRUE(isDistroListed());\r\n\r\n        // Delete the VHD\r\n        VERIFY_IS_TRUE(DeleteFileA(vhdPath.c_str()));\r\n\r\n        // Now the distro should be deleted.\r\n        VERIFY_IS_FALSE(isDistroListed());\r\n    }\r\n\r\n    // Validate that the default distribution is correctly displayed\r\n    TEST_METHOD(DefaultDistro)\r\n    {\r\n        auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"--list\");\r\n\r\n        VERIFY_IS_TRUE(out.find(std::format(L\"{} (Default)\", LXSS_DISTRO_NAME_TEST_L)) != std::wstring::npos);\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n    }\r\n\r\n    // TODO: Add test coverage for the Linux => Windows code paths of $WSLENV\r\n    TEST_METHOD(WslEnv)\r\n    {\r\n        auto validateEnv = [&](const std::map<std::wstring, std::wstring>& inputVariables,\r\n                               const std::map<std::wstring, std::wstring>& expectedOutput) {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n                for (const auto& e : inputVariables)\r\n                {\r\n                    THROW_LAST_ERROR_IF(!SetEnvironmentVariable(e.first.c_str(), nullptr));\r\n                }\r\n            });\r\n\r\n            for (const auto& e : inputVariables)\r\n            {\r\n                THROW_LAST_ERROR_IF(!SetEnvironmentVariable(e.first.c_str(), e.second.c_str()));\r\n            }\r\n\r\n            for (const auto& e : expectedOutput)\r\n            {\r\n                auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"echo -n $\" + e.first);\r\n\r\n                VERIFY_ARE_EQUAL(e.second, output);\r\n            }\r\n        };\r\n\r\n        validateEnv({{L\"a\", L\"b\"}, {L\"c\", L\"d\"}, {L\"WSLENV\", L\"a/u:c/u\"}}, {{L\"a\", L\"b\"}, {L\"c\", L\"d\"}});\r\n        validateEnv(\r\n            {{L\"a\", L\"C:\\\\Users\"}, {L\"b\", L\"C:\\\\Users\"}, {L\"WSLENV\", L\"a/l:b/p\"}},\r\n            {{L\"a\", L\"/mnt/c/Users\"}, {L\"b\", L\"/mnt/c/Users\"}});\r\n\r\n        validateEnv(\r\n            {{L\"a\", L\"C:\\\\Users;C:\\\\Windows\"},\r\n             {L\"b\", L\"C:\\\\Users;C:\\\\Windows\"},\r\n             {L\"c\", L\"C:\\\\Users;C:\\\\Windows\"},\r\n             {L\"d\", L\"C:\\\\Users;C:\\\\Windows\"},\r\n             {L\"WSLENV\", L\"a/l:b/p:c/pl:d/lp\"}},\r\n            {{L\"a\", L\"/mnt/c/Users:/mnt/c/Windows\"},\r\n             {L\"b\", L\"/mnt/c/Users:/mnt/c/Windows\"},\r\n             {L\"c\", L\"/mnt/c/Users:/mnt/c/Windows\"},\r\n             {L\"d\", L\"/mnt/c/Users:/mnt/c/Windows\"}});\r\n\r\n        validateEnv(\r\n            {{L\"a\", L\"C:\\\\Users;C:\\\\Windows\\\\System32\"}, {L\"b\", L\"C:\\\\Users;C:\\\\Windows\"}, {L\"WSLENV\", L\"a/l:b/l:a/l\"}},\r\n            {{L\"a\", L\"/mnt/c/Users:/mnt/c/Windows/System32\"}, {L\"b\", L\"/mnt/c/Users:/mnt/c/Windows\"}});\r\n\r\n        validateEnv(\r\n            {{L\"a\", L\"C:\\\\Users;C:\\\\Windows\\\\System32\"}, {L\"b\", L\"C:\\\\Users;C:\\\\Windows\"}, {L\"WSLENV\", L\"a/u:b/u:a/u\"}},\r\n            {{L\"a\", L\"C:\\\\Users;C:\\\\Windows\\\\System32\"}, {L\"b\", L\"C:\\\\Users;C:\\\\Windows\"}});\r\n\r\n        validateEnv({{L\"a\", L\"C:\\\\Users;C:\\\\Windows\\\\System32\"}, {L\"WSLENV\", L\"a/w\"}}, {{L\"a\", L\"\"}});\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n            THROW_LAST_ERROR_IF(!SetEnvironmentVariable(L\"Empty\", nullptr));\r\n            THROW_LAST_ERROR_IF(!SetEnvironmentVariable(L\"WSLENV\", nullptr));\r\n        });\r\n\r\n        THROW_LAST_ERROR_IF(!SetEnvironmentVariable(L\"Empty\", L\"\"));\r\n        THROW_LAST_ERROR_IF(!SetEnvironmentVariable(L\"WSLENV\", L\"Empty/u\"));\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"[ -z ${Empty+x} ]\"), (DWORD)1);\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"[ -z ${SanityCheck+x} ]\"), (DWORD)0);\r\n    }\r\n\r\n    static void ValidateErrorMessage(\r\n        const std::wstring& Cmd,\r\n        const std::wstring& Message,\r\n        const std::wstring& Code,\r\n        const std::optional<std::wstring>& ExtraConfig = {},\r\n        LPCWSTR EntryPoint = WSL_BINARY_NAME,\r\n        bool ignoreCasing = false)\r\n    {\r\n        std::optional<std::wstring> previousConfig;\r\n\r\n        if (ExtraConfig.has_value())\r\n        {\r\n            previousConfig = LxssWriteWslConfig(L\"[wsl2]\\n\" + ExtraConfig.value());\r\n            RestartWslService();\r\n        }\r\n\r\n        auto revertConfig = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            if (previousConfig.has_value())\r\n            {\r\n                LxssWriteWslConfig(previousConfig.value());\r\n                RestartWslService();\r\n            };\r\n        });\r\n\r\n        auto [output, _] = LxsstuLaunchWslAndCaptureOutput(\r\n            Cmd.c_str(), wcscmp(EntryPoint, L\"bash.exe\") == 0 ? 1 : -1, nullptr, nullptr, EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, EntryPoint);\r\n\r\n        const auto expectedOutput = Message + L\"\\r\\nError code: \" + Code + L\"\\r\\n\";\r\n\r\n        if (!wsl::shared::string::IsEqual(output, expectedOutput, ignoreCasing))\r\n        {\r\n            LogError(\"Expected error message: '%ls', actual error message: '%ls'\", expectedOutput.c_str(), output.c_str());\r\n            VERIFY_FAIL();\r\n        }\r\n    }\r\n\r\n    static void VerifyOutput(const std::wstring& Cmd, const std::wstring& ExpectedOutput, int ExpectedExitCode = 0, LPCWSTR EntryPoint = WSL_BINARY_NAME)\r\n    {\r\n        auto [output, _] = LxsstuLaunchWslAndCaptureOutput(\r\n            Cmd.c_str(), ExpectedExitCode, nullptr, nullptr, EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, EntryPoint);\r\n\r\n        VERIFY_ARE_EQUAL(output, ExpectedOutput);\r\n    }\r\n\r\n    TEST_METHOD(ErrorMessages)\r\n    {\r\n        if (LxsstuVmMode()) // wsl --mount and bridged networking only exist in WSL2.\r\n        {\r\n            if (!wsl::shared::Arm64 && wsl::windows::common::helpers::GetWindowsVersion().BuildNumber >= 27653)\r\n            {\r\n                ValidateErrorMessage(\r\n                    L\"--mount DoesNotExist\",\r\n                    L\"Failed to attach disk 'DoesNotExist' to WSL2: The system cannot find the file specified. \",\r\n                    L\"Wsl/Service/AttachDisk/MountDisk/HCS/ERROR_FILE_NOT_FOUND\");\r\n            }\r\n\r\n            ValidateErrorMessage(\r\n                L\"--unmount DoesNotExist\",\r\n                GetSystemErrorString(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)),\r\n                L\"Wsl/Service/DetachDisk/ERROR_FILE_NOT_FOUND\");\r\n\r\n            ValidateErrorMessage(\r\n                WSL_MANAGE_ARG L\" \" LXSS_DISTRO_NAME_TEST L\" \" WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG L\" false_\",\r\n                L\"false_ is not a valid boolean, <true|false>\",\r\n                L\"Wsl/E_INVALIDARG\");\r\n\r\n            const std::wstring wslConfigPath = wsl::windows::common::helpers::GetWslConfigPath();\r\n            {\r\n                // Create a distro registration pointing to a vhdx that doesn't exist and validate that the error message reports that correctly.\r\n\r\n                const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n                const auto distroKey =\r\n                    wsl::windows::common::registry::CreateKey(userKey.get(), L\"{baa405ef-1822-4bbe-84e2-30e4c6330d42}\");\r\n                auto revert = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\r\n                    wsl::windows::common::registry::DeleteKey(userKey.get(), L\"{baa405ef-1822-4bbe-84e2-30e4c6330d42}\");\r\n                });\r\n\r\n                wsl::windows::common::registry::WriteString(distroKey.get(), nullptr, L\"BasePath\", L\"C:\\\\DoesNotExit\");\r\n                wsl::windows::common::registry::WriteString(distroKey.get(), nullptr, L\"DistributionName\", L\"DummyBrokenDistro\");\r\n                wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"DefaultUid\", 0);\r\n                wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"Version\", LXSS_DISTRO_VERSION_2);\r\n                wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"State\", LxssDistributionStateInstalled);\r\n                wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"Flags\", LXSS_DISTRO_FLAGS_VM_MODE);\r\n\r\n                ValidateErrorMessage(\r\n                    L\"-d DummyBrokenDistro\",\r\n                    L\"Failed to attach disk 'C:\\\\DoesNotExit\\\\ext4.vhdx' to WSL2: The system cannot find the path \"\r\n                    L\"specified. \",\r\n                    L\"Wsl/Service/CreateInstance/MountDisk/HCS/ERROR_PATH_NOT_FOUND\");\r\n\r\n                // Purposefully set an incorrect value type to validate registry error handling.\r\n                wsl::windows::common::registry::WriteString(distroKey.get(), nullptr, L\"Version\", L\"Broken\");\r\n\r\n                const auto tokenInfo = wil::get_token_information<TOKEN_USER>();\r\n                const auto Sid = std::wstring(wsl::windows::common::wslutil::SidToString(tokenInfo->User.Sid).get());\r\n\r\n                //  N.B. casing is ignored because the 'Software' key is sometimes uppercase, sometimes not.\r\n                ValidateErrorMessage(\r\n                    L\"-d DummyBrokenDistro\",\r\n                    L\"An error occurred accessing the registry. Path: '\\\\REGISTRY\\\\USER\\\\\" + Sid +\r\n                        L\"\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\\\\{baa405ef-1822-4bbe-84e2-30e4c6330d42}\"\r\n                        L\"\\\\Version'.\"\r\n                        L\" \"\r\n                        L\"Error: Data of this type is not supported. \",\r\n                    L\"Wsl/Service/ReadDistroConfig/ERROR_UNSUPPORTED_TYPE\",\r\n                    {},\r\n                    L\"wsl.exe\",\r\n                    true);\r\n            }\r\n\r\n            ValidateErrorMessage(\r\n                L\"echo ok\",\r\n                std::format(L\"Invalid mac address 'foo' for key 'wsl2.macAddress' in {}:2\", wslConfigPath),\r\n                L\"Wsl/Service/CreateInstance/CreateVm/ParseConfig/E_INVALIDARG\",\r\n                L\"macAddress=foo\");\r\n        }\r\n        else\r\n        {\r\n            // wsl.exe --manage --resize requires WSL2.\r\n            ValidateErrorMessage(\r\n                L\"--manage test_distro --resize 10GB\",\r\n                L\"This operation is only supported by WSL2.\",\r\n                L\"Wsl/Service/WSL_E_WSL2_NEEDED\");\r\n        }\r\n\r\n        ValidateErrorMessage(\r\n            L\"--import a b c\", GetSystemErrorString(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)), L\"Wsl/ERROR_FILE_NOT_FOUND\");\r\n\r\n        ValidateErrorMessage(\r\n            L\"-d DoesNotExist echo foo\",\r\n            L\"There is no distribution with the supplied name.\",\r\n            L\"Wsl/Service/WSL_E_DISTRO_NOT_FOUND\");\r\n\r\n        ValidateErrorMessage(\r\n            L\"--export DoesNotExist FileName\",\r\n            L\"There is no distribution with the supplied name.\",\r\n            L\"Wsl/Service/WSL_E_DISTRO_NOT_FOUND\");\r\n\r\n        ValidateErrorMessage(\r\n            L\"--import-in-place DoesNotExist FileName\",\r\n            GetSystemErrorString(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)),\r\n            L\"Wsl/ERROR_FILE_NOT_FOUND\");\r\n\r\n        ValidateErrorMessage(\r\n            L\"--set-default-version 3\",\r\n            GetSystemErrorString(HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR)),\r\n            L\"Wsl/ERROR_VERSION_PARSE_ERROR\");\r\n\r\n        ValidateErrorMessage(\r\n            L\"--manage DoesNotExist --resize 10GB\",\r\n            L\"There is no distribution with the supplied name.\",\r\n            L\"Wsl/Service/WSL_E_DISTRO_NOT_FOUND\");\r\n\r\n        ValidateErrorMessage(L\"--manage test_distro --resize foo\", L\"Invalid size: foo\", L\"Wsl/E_INVALIDARG\");\r\n\r\n        ValidateErrorMessage(\r\n            L\"--install --distribution debian --no-distribution\",\r\n            L\"Arguments --no-distribution and --distribution can't be specified at same time.\",\r\n            L\"Wsl/E_INVALIDARG\");\r\n\r\n        ValidateErrorMessage(\r\n            L\"--install debian --from-file foo --distribution foo\",\r\n            L\"Arguments --from-file and --distribution can't be specified at same time.\",\r\n            L\"Wsl/E_INVALIDARG\");\r\n\r\n        ValidateErrorMessage(\r\n            L\"--install foo --fixed-vhd\", L\"Argument --fixed-vhd requires the --vhd-size argument.\", L\"Wsl/E_INVALIDARG\");\r\n\r\n        {\r\n            UniqueWebServer server(c_testDistributionEndpoint, c_testDistributionJson);\r\n            RegistryKeyChange<std::wstring> keyChange(\r\n                HKEY_LOCAL_MACHINE, LXSS_REGISTRY_PATH, wsl::windows::common::distribution::c_distroUrlRegistryValue, c_testDistributionEndpoint);\r\n            ValidateErrorMessage(\r\n                L\"--install -d DoesNotExist\",\r\n                L\"Invalid distribution name: 'DoesNotExist'.\\r\\nTo get a list of valid distributions, use 'wsl.exe --list \"\r\n                L\"--online'.\",\r\n                L\"Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND\");\r\n        }\r\n\r\n        {\r\n            const auto lxssKey = wsl::windows::common::registry::OpenLxssMachineKey(KEY_READ | KEY_SET_VALUE);\r\n            std::optional<std::wstring> revertValue;\r\n\r\n            try\r\n            {\r\n                revertValue = wsl::windows::common::registry::ReadString(\r\n                    lxssKey.get(), nullptr, wsl::windows::common::distribution::c_distroUrlRegistryValue);\r\n            }\r\n            catch (...)\r\n            {\r\n                // Expected if the value isn't set\r\n            }\r\n\r\n            auto revert = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n                if (revertValue.has_value())\r\n                {\r\n                    wsl::windows::common::registry::WriteString(\r\n                        lxssKey.get(), nullptr, wsl::windows::common::distribution::c_distroUrlRegistryValue, revertValue->c_str());\r\n                }\r\n                else\r\n                {\r\n                    wsl::windows::common::registry::DeleteValue(lxssKey.get(), wsl::windows::common::distribution::c_distroUrlRegistryValue);\r\n                }\r\n            });\r\n\r\n            wsl::windows::common::registry::WriteString(\r\n                lxssKey.get(), nullptr, wsl::windows::common::distribution::c_distroUrlRegistryValue, L\"http://127.0.0.1:6666\");\r\n\r\n            ValidateErrorMessage(\r\n                L\"--install -d ubuntu\",\r\n                L\"Failed to fetch the distribution list from 'http://127.0.0.1:6666'. \" +\r\n                    GetSystemErrorString(HRESULT_FROM_WIN32(WININET_E_CANNOT_CONNECT)),\r\n                L\"Wsl/InstallDistro/WININET_E_CANNOT_CONNECT\");\r\n\r\n            ValidateErrorMessage(\r\n                L\"--list --online\",\r\n                L\"Failed to fetch the distribution list from 'http://127.0.0.1:6666'. \" +\r\n                    GetSystemErrorString(HRESULT_FROM_WIN32(WININET_E_CANNOT_CONNECT)),\r\n                L\"Wsl/WININET_E_CANNOT_CONNECT\");\r\n        }\r\n\r\n        ValidateErrorMessage(\r\n            L\"/u foo\",\r\n            L\"There is no distribution with the supplied name.\",\r\n            L\"WslConfig/Service/WSL_E_DISTRO_NOT_FOUND\",\r\n            {},\r\n            L\"wslconfig.exe\");\r\n\r\n        ValidateErrorMessage(\r\n            L\"e7bef681-c148-4687-8a0f-8c8be93bac93\", // GUID for a distro that's not installed.\r\n            L\"There is no distribution with the supplied name.\",\r\n            L\"Bash/Service/CreateInstance/ReadDistroConfig/WSL_E_DISTRO_NOT_FOUND\",\r\n            {},\r\n            L\"bash.exe\");\r\n\r\n        VerifyOutput(L\"--install --no-distribution\", L\"The operation completed successfully. \\r\\n\");\r\n\r\n        {\r\n            std::wstring expectedUsageMessage;\r\n            for (auto e : wsl::shared::Localization::MessageWslUsage())\r\n            {\r\n                if (e == L'\\n')\r\n                {\r\n                    expectedUsageMessage += L'\\r';\r\n                }\r\n\r\n                expectedUsageMessage += e;\r\n            }\r\n\r\n            VerifyOutput(L\"--manage --move .\", expectedUsageMessage + L\"\\r\\n\", -1);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(CommandLineParsing)\r\n    {\r\n        VerifyOutput(L\"echo -n \\\\\\\"\", L\"\\\"\");\r\n        VerifyOutput(L\"echo -n \\\\\\'\", L\"\\'\");\r\n        VerifyOutput(L\"echo -n \\\" \\\"\", L\" \");\r\n        VerifyOutput(L\"echo -n $USER\", L\"root\");\r\n        VerifyOutput(L\"echo -n \\\"$USER\\\"\", L\"root\");\r\n        VerifyOutput(L\"echo -n '\\\"$USER\\\"'\", L\"\\\"$USER\\\"\");\r\n        VerifyOutput(L\"echo -n '\\\\\\\"$USER\\\\\\\"'\", L\"\\\\\\\"$USER\\\\\\\"\");\r\n        VerifyOutput(L\"echo -n '$USER'\", L\"$USER\");\r\n        VerifyOutput(L\"echo -n a \\\" \\\" b\", L\"a   b\");\r\n        VerifyOutput(L\"echo -n a \\\"\\\" b\", L\"a  b\");\r\n        VerifyOutput(L\"echo -n a b \\\"\\\"\", L\"a b \");\r\n        VerifyOutput(L\"echo -n \\\"a\\\"\\\"b\\\"\", L\"ab\");\r\n\r\n        VerifyOutput(L\"--exec echo -n \\\"a\\\"\", L\"a\");\r\n        VerifyOutput(L\"--exec echo -n $USER\", L\"$USER\");\r\n        VerifyOutput(L\"--exec echo -n \\\\\\\"a\\\\\\\"\", L\"\\\"a\\\"\");\r\n        VerifyOutput(L\"--exec echo -n \\\\\\\"a\\\\\\\"\", L\"\\\"a\\\"\");\r\n        VerifyOutput(L\"--exec echo -n \\\"a\\\"\\\"b\\\"\", L\"a\\\"b\");\r\n        VerifyOutput(L\"--exec echo -n \\\\\\\"\", L\"\\\"\");\r\n    }\r\n\r\n    // This test validates that the help messages for wsl.exe and wsl.config are correctly displayed.\r\n    // Notes:\r\n    // - This test will fail if the help messages are changed. If that's the case, simply update the below strings\r\n    // - This test assumes that English is the configured language.\r\n    TEST_METHOD(UsageMessages)\r\n    {\r\n        const std::wstring WslHelpMessage =\r\n            LR\"\"\"(Copyright (c) Microsoft Corporation. All rights reserved.\r\nFor privacy information about this product please visit https://aka.ms/privacy.\r\n\r\nUsage: wsl.exe [Argument] [Options...] [CommandLine]\r\n\r\nArguments for running Linux binaries:\r\n\r\n    If no command line is provided, wsl.exe launches the default shell.\r\n\r\n    --exec, -e <CommandLine>\r\n        Execute the specified command without using the default Linux shell.\r\n\r\n    --shell-type <standard|login|none>\r\n        Execute the specified command with the provided shell type.\r\n\r\n    --\r\n        Pass the remaining command line as-is.\r\n\r\nOptions:\r\n    --cd <Directory>\r\n        Sets the specified directory as the current working directory.\r\n        If ~ is used the Linux user's home path will be used. If the path begins\r\n        with a / character, it will be interpreted as an absolute Linux path.\r\n        Otherwise, the value must be an absolute Windows path.\r\n\r\n    --distribution, -d <DistroName>\r\n        Run the specified distribution.\r\n\r\n    --distribution-id <DistroGuid>\r\n        Run the specified distribution ID.\r\n\r\n    --user, -u <UserName>\r\n        Run as the specified user.\r\n\r\n    --system\r\n        Launches a shell for the system distribution.\r\n\r\nArguments for managing Windows Subsystem for Linux:\r\n\r\n    --help\r\n        Display usage information.\r\n\r\n    --debug-shell\r\n        Open a WSL2 debug shell for diagnostics purposes.\r\n\r\n    --install [Distro] [Options...]\r\n        Install a Windows Subsystem for Linux distribution.\r\n        For a list of valid distributions, use 'wsl.exe --list --online'.\r\n\r\n        Options:\r\n            --enable-wsl1\r\n                Enable WSL1 support.\r\n\r\n            --fixed-vhd\r\n                Create a fixed-size disk to store the distribution.\r\n\r\n            --from-file <Path>\r\n                Install a distribution from a local file.\r\n\r\n            --legacy\r\n                Use the legacy distribution manifest.\r\n\r\n            --location <Location>\r\n                Set the install path for the distribution.\r\n\r\n            --name <Name>\r\n                Set the name of the distribution.\r\n\r\n            --no-distribution\r\n                Only install the required optional components, does not install a distribution.\r\n\r\n            --no-launch, -n\r\n                Do not launch the distribution after install.\r\n\r\n            --version <Version>\r\n                Specifies the version to use for the new distribution.\r\n\r\n            --vhd-size <MemoryString>\r\n                Specifies the size of the disk to store the distribution.\r\n\r\n            --web-download\r\n                Download the distribution from the internet instead of the Microsoft Store.\r\n\r\n    --manage <Distro> <Options...>\r\n        Changes distro specific options.\r\n\r\n        Options:\r\n            --move <Location>\r\n                Move the distribution to a new location.\r\n\r\n            --set-sparse, -s <true|false>\r\n                Set the VHD of distro to be sparse, allowing disk space to be automatically reclaimed.\r\n\r\n            --set-default-user <Username>\r\n                Set the default user of the distribution.\r\n\r\n            --resize <MemoryString>\r\n                Resize the disk of the distribution to the specified size.\r\n\r\n    --mount <Disk>\r\n        Attaches and mounts a physical or virtual disk in all WSL 2 distributions.\r\n\r\n        Options:\r\n            --vhd\r\n                Specifies that <Disk> refers to a virtual hard disk.\r\n\r\n            --bare\r\n                Attach the disk to WSL2, but don't mount it.\r\n\r\n            --name <Name>\r\n                Mount the disk using a custom name for the mountpoint.\r\n\r\n            --type <Type>\r\n                Filesystem to use when mounting a disk, if not specified defaults to ext4.\r\n\r\n            --options <Options>\r\n                Additional mount options.\r\n\r\n            --partition <Index>\r\n                Index of the partition to mount, if not specified defaults to the whole disk.\r\n\r\n    --set-default-version <Version>\r\n        Changes the default install version for new distributions.\r\n\r\n    --shutdown\r\n        Immediately terminates all running distributions and the WSL 2\r\n        lightweight utility virtual machine.\r\n\r\n        Options:\r\n            --force\r\n                Terminate the WSL 2 virtual machine even if an operation is in progress. Can cause data loss.\r\n\r\n    --status\r\n        Show the status of Windows Subsystem for Linux.\r\n\r\n    --unmount [Disk]\r\n        Unmounts and detaches a disk from all WSL2 distributions.\r\n        Unmounts and detaches all disks if called without argument.\r\n\r\n    --uninstall\r\n        Uninstalls the Windows Subsystem for Linux package from this machine.\r\n\r\n    --update\r\n        Update the Windows Subsystem for Linux package.\r\n\r\n        Options:\r\n            --pre-release\r\n                Download a pre-release version if available.\r\n\r\n    --version, -v\r\n        Display version information.\r\n\r\nArguments for managing distributions in Windows Subsystem for Linux:\r\n\r\n    --export <Distro> <FileName> [Options]\r\n        Exports the distribution to a tar file.\r\n        The filename can be - for stdout.\r\n\r\n        Options:\r\n            --format <Format>\r\n                Specifies the export format. Supported values: tar, tar.gz, tar.xz, vhd.\r\n\r\n    --import <Distro> <InstallLocation> <FileName> [Options]\r\n        Imports the specified tar file as a new distribution.\r\n        The filename can be - for stdin.\r\n\r\n        Options:\r\n            --version <Version>\r\n                Specifies the version to use for the new distribution.\r\n\r\n            --vhd\r\n                Specifies that the provided file is a .vhd or .vhdx file, not a tar file.\r\n                This operation makes a copy of the VHD file at the specified install location.\r\n\r\n    --import-in-place <Distro> <FileName>\r\n        Imports the specified VHD file as a new distribution.\r\n        This virtual hard disk must be formatted with the ext4 filesystem type.\r\n\r\n    --list, -l [Options]\r\n        Lists distributions.\r\n\r\n        Options:\r\n            --all\r\n                List all distributions, including distributions that are\r\n                currently being installed or uninstalled.\r\n\r\n            --running\r\n                List only distributions that are currently running.\r\n\r\n            --quiet, -q\r\n                Only show distribution names.\r\n\r\n            --verbose, -v\r\n                Show detailed information about all distributions.\r\n\r\n            --online, -o\r\n                Displays a list of available distributions for install with 'wsl.exe --install'.\r\n\r\n    --set-default, -s <Distro>\r\n        Sets the distribution as the default.\r\n\r\n    --set-version <Distro> <Version>\r\n        Changes the version of the specified distribution.\r\n\r\n    --terminate, -t <Distro>\r\n        Terminates the specified distribution.\r\n\r\n    --unregister <Distro>\r\n        Unregisters the distribution and deletes the root filesystem.\r\n)\"\"\";\r\n\r\n        const std::wstring WslConfigHelpMessage =\r\n            LR\"\"\"(Performs administrative operations on Windows Subsystem for Linux\r\n\r\nUsage:\r\n    /l, /list [Option]\r\n        Lists registered distributions.\r\n        /all - Optionally list all distributions, including distributions that\r\n               are currently being installed or uninstalled.\r\n\r\n        /running - List only distributions that are currently running.\r\n\r\n    /s, /setdefault <DistributionName>\r\n        Sets the distribution as the default.\r\n\r\n    /t, /terminate <DistributionName>\r\n        Terminates the distribution.\r\n\r\n    /u, /unregister <DistributionName>\r\n        Unregisters the distribution and deletes the root filesystem.\r\n)\"\"\";\r\n\r\n        const std::wstring WslInstallHelpMessage =\r\n            LR\"\"\"(Invalid distribution name: 'foo'.\r\nTo get a list of valid distributions, use 'wsl.exe --list --online'.\r\nError code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND\r\n)\"\"\";\r\n\r\n        auto AddCrlf = [](const std::wstring& Input) {\r\n            std::wstring MessageWithCrlf;\r\n\r\n            for (const auto e : Input)\r\n            {\r\n                if (e == '\\n')\r\n                {\r\n                    MessageWithCrlf += '\\r';\r\n                }\r\n                MessageWithCrlf += e;\r\n            }\r\n\r\n            return MessageWithCrlf;\r\n        };\r\n\r\n        // Note: There is no easy way to validate wslg's help message, since it displays a blocking\r\n        // message box before exiting.\r\n\r\n        VerifyOutput(L\"--help\", AddCrlf(WslHelpMessage), -1);\r\n        VerifyOutput(L\"--help\", AddCrlf(WslConfigHelpMessage), -1, L\"wslconfig.exe\");\r\n\r\n        UniqueWebServer server(c_testDistributionEndpoint, c_testDistributionJson);\r\n        RegistryKeyChange<std::wstring> keyChange(\r\n            HKEY_LOCAL_MACHINE, LXSS_REGISTRY_PATH, wsl::windows::common::distribution::c_distroUrlRegistryValue, c_testDistributionEndpoint);\r\n\r\n        VerifyOutput(L\"--install foo\", AddCrlf(WslInstallHelpMessage), -1);\r\n    }\r\n\r\n    TEST_METHOD(TestExistingSwapVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Create a 100MB swap vhdx.\r\n        auto swapVhd = wil::GetCurrentDirectoryW<std::wstring>() + L\"\\\\TestSwap.vhdx\";\r\n\r\n        VIRTUAL_STORAGE_TYPE storageType{};\r\n        storageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_VHDX;\r\n        storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT;\r\n\r\n        CREATE_VIRTUAL_DISK_PARAMETERS createVhdParameters{};\r\n        createVhdParameters.Version = CREATE_VIRTUAL_DISK_VERSION_2;\r\n        createVhdParameters.Version2.BlockSizeInBytes = 1024 * 1024;\r\n        createVhdParameters.Version2.MaximumSize = 100 * 1024 * 1024;\r\n\r\n        wil::unique_hfile vhd{};\r\n        VERIFY_ARE_EQUAL(\r\n            ::CreateVirtualDisk(\r\n                &storageType, swapVhd.c_str(), VIRTUAL_DISK_ACCESS_NONE, nullptr, CREATE_VIRTUAL_DISK_FLAG_SUPPORT_COMPRESSED_VOLUMES, 0, &createVhdParameters, nullptr, &vhd),\r\n            0l);\r\n\r\n        vhd.reset();\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            WslShutdown();\r\n            DeleteFile(swapVhd.c_str());\r\n        });\r\n\r\n        // Update .wslconfig. Update the swapVhd path to replace single backslash\r\n        // with double backslashes so as to be compatible with .wslconfig parsing.\r\n        // The following regex replacement only works as intended if the path contains\r\n        // single backslashes. Negative lookahead can be used to handle paths with double\r\n        // backslashes but then the negative lookbehind case should also be used but the\r\n        // latter is not supported in std::regex.\r\n        swapVhd = std::regex_replace(swapVhd, std::wregex(L\"\\\\\\\\\"), L\"\\\\\\\\\");\r\n        WslConfigChange configChange(LxssGenerateTestConfig() + L\"\\nswap=256MB\\nswapFile=\" + swapVhd);\r\n\r\n        auto validateSwapSize = [](LPCWSTR Expected) {\r\n            auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"swapon | awk 'END {print $3}'\");\r\n\r\n            VERIFY_ARE_EQUAL(Expected + std::wstring(L\"\\n\"), output);\r\n        };\r\n\r\n        validateSwapSize(L\"256M\");\r\n\r\n        // Validate that the vhdx is resized correctly if the swap size changes\r\n        configChange.Update(LxssGenerateTestConfig() + L\"\\nswap=200MB\\nswapFile=\" + swapVhd);\r\n        validateSwapSize(L\"200M\");\r\n    }\r\n\r\n    TEST_METHOD(InitDoesntBlockSignals)\r\n    {\r\n        auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"grep -iF SigBlk < /proc/1/status\");\r\n        VERIFY_ARE_EQUAL(L\"SigBlk:\\t0000000000000000\\n\", output);\r\n    }\r\n\r\n    TEST_METHOD(InitReadonly)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\" grep '^rootfs /init rootfs ro,' /proc/self/mounts\", nullptr, nullptr, nullptr, nullptr), 0u);\r\n    }\r\n\r\n    TEST_METHOD(GpuMounts)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        auto ValidateGpuMounts = [](HANDLE Token) {\r\n            VERIFY_ARE_EQUAL(\r\n                LxsstuLaunchWsl(\r\n                    L\"mount | grep -iF 'none on /usr/lib/wsl/lib type overlay (rw,nosuid,nodev,noatime,lowerdir=/gpu_\" TEXT(LXSS_GPU_PACKAGED_LIB_SHARE) L\":/gpu_\" TEXT(\r\n                        LXSS_GPU_INBOX_LIB_SHARE) L\",upperdir=/gpu_lib/rw/upper,workdir=/gpu_lib/rw/work,uuid=on)'\",\r\n                    nullptr,\r\n                    nullptr,\r\n                    nullptr,\r\n                    Token),\r\n                0u);\r\n\r\n            // Ensure the lib directory is writable.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\" touch /usr/lib/wsl/lib/foo && rm /usr/lib/wsl/lib/foo\", nullptr, nullptr, nullptr, Token), 0u);\r\n\r\n            VERIFY_ARE_EQUAL(\r\n                LxsstuLaunchWsl(\r\n                    L\"mount | grep -iF '\" TEXT(\r\n                        LXSS_GPU_DRIVERS_SHARE) L\" on /usr/lib/wsl/drivers type 9p (ro,nosuid,nodev,noatime,aname=\" TEXT(LXSS_GPU_DRIVERS_SHARE) L\";fmask=222;dmask=222,cache=5,access=client,msize=65536,trans=fd,rfd=8,wfd=8)'\",\r\n                    nullptr,\r\n                    nullptr,\r\n                    nullptr,\r\n                    Token),\r\n                0u);\r\n        };\r\n\r\n        auto cleanUp = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { WslShutdown(); });\r\n\r\n        // Validate that GPU mounts are present in both namespaces.\r\n        const auto nonElevatedToken = GetNonElevatedToken();\r\n        WslShutdown();\r\n        ValidateGpuMounts(nullptr);\r\n        ValidateGpuMounts(nonElevatedToken.get());\r\n\r\n        // Create a new instance with a non-elevated token as the creator.\r\n        WslShutdown();\r\n        ValidateGpuMounts(nonElevatedToken.get());\r\n        ValidateGpuMounts(nullptr);\r\n    }\r\n\r\n    TEST_METHOD(InteropCornerCases)\r\n    {\r\n        auto validateInterop = [](const std::wstring& binaryName) {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { LxsstuLaunchWsl(L\"rm /tmp/'\" + binaryName + L\"'\"); });\r\n\r\n            // The \"|| echo fail\" part is needed because bash will exec instead of forking() of only one non-builtin command is passed.\r\n            // If bash exec's then this test is useless since the binfmt interpreter would not be a child of a process with a weird name.\r\n\r\n            const std::wstring commandLine =\r\n                L\"cp /bin/bash /tmp/'\" + binaryName + L\"' && '/tmp/\" + binaryName +\r\n                L\"' -c 'export WSL_INTEROP=\\\"\\\" && echo -n $WSL_INTEROP && cmd.exe /c \\\"echo ok\\\" || echo fail'\";\r\n            auto [output, _] = LxsstuLaunchWslAndCaptureOutput(commandLine);\r\n\r\n            VERIFY_ARE_EQUAL(output, L\"ok\\r\\n\");\r\n        };\r\n\r\n        validateInterop(L\"bash with spaces\");\r\n        validateInterop(L\"bash )\");\r\n        validateInterop(L\"bash (\");\r\n        validateInterop(L\"(bash)\");\r\n        validateInterop(L\"(bash(\");\r\n        validateInterop(L\"()\");\r\n        validateInterop(L\"(\");\r\n        validateInterop(L\")\");\r\n    }\r\n\r\n    TEST_METHOD(InteropPid1)\r\n    {\r\n        // Validate that interop works as pid 1.\r\n        auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"unshare -pf --wd $(dirname $(which cmd.exe)) cmd.exe /c echo ok\");\r\n        VERIFY_ARE_EQUAL(output, L\"ok\\r\\n\");\r\n    }\r\n\r\n    TEST_METHOD(Hostname)\r\n    {\r\n        auto cleanup = wil::scope_exit([] {\r\n            LxsstuLaunchWsl(LXSST_REMOVE_DISTRO_CONF_COMMAND_LINE);\r\n\r\n            TerminateDistribution();\r\n        });\r\n\r\n        auto validate = [](const std::string& input, const std::wstring& expectedOutput) {\r\n            LxssWriteWslDistroConfig(\"[network]\\nhostname=\" + input);\r\n            TerminateDistribution();\r\n\r\n            auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L\"hostname\");\r\n            VERIFY_ARE_EQUAL(output, expectedOutput + L\"\\n\");\r\n\r\n            output = LxsstuLaunchWslAndCaptureOutput(L\"cat /etc/hostname\").first;\r\n            VERIFY_ARE_EQUAL(output, expectedOutput + L\"\\n\");\r\n        };\r\n\r\n        validate(\"SimpleHostname\", L\"SimpleHostname\");\r\n        validate(\"Simple-Hostname\", L\"Simple-Hostname\");\r\n        validate(\"Simple_Hostname\", L\"SimpleHostname\");\r\n        validate(\"-hostname\", L\"hostname\");\r\n        validate(\"--hostname\", L\"hostname\");\r\n        validate(\"hostname.-\", L\"hostname\");\r\n        validate(\".hostname\", L\"hostname\");\r\n        validate(\"hostname.\", L\"hostname\");\r\n        validate(\"host.name.\", L\"host.name\");\r\n        validate(\"host..name\", L\"host.name\");\r\n        validate(\"host|name\", L\"hostname\");\r\n        validate(\".a-\", L\"a\");\r\n        validate(\".a-b\", L\"a-b\");\r\n        validate(\".\", L\"localhost\");\r\n        validate(\"-\", L\"localhost\");\r\n        validate(\"-.-\", L\"localhost\");\r\n        // Validate hostname is limited to 64 characters.\r\n        const std::string longHostName(\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\");\r\n        validate(longHostName, wsl::shared::string::MultiByteToWide(longHostName.substr(0, 64)));\r\n    }\r\n\r\n    TEST_METHOD(WslConfWarnings)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        DistroFileChange configChange(L\"/etc/wsl.conf\", false);\r\n\r\n        auto validateWarnings = [&configChange](const std::wstring& config, const std::wstring& expectedWarnings) {\r\n            configChange.SetContent(config.c_str());\r\n\r\n            TerminateDistribution();\r\n\r\n            // This loop is here because of a race condition when starting WSL to get the warnings.\r\n            // If a p9rdr distribution startup notification arrives just before wsl.exe calls CreateInstance(),\r\n            // the warnings will be 'consumed' before wsl.exe can read them.\r\n            // To work around that, loop for up to 2 minutes while we don't get any warnings\r\n\r\n            const auto deadline = std::chrono::steady_clock::now() + std::chrono::minutes(2);\r\n\r\n            while (std::chrono::steady_clock::now() < deadline)\r\n            {\r\n                auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(L\"-u root echo ok\");\r\n                VERIFY_ARE_EQUAL(L\"ok\\n\", output);\r\n\r\n                if (!warnings.empty() || expectedWarnings.empty())\r\n                {\r\n                    VERIFY_ARE_EQUAL(expectedWarnings, warnings);\r\n                    return;\r\n                }\r\n\r\n                LogInfo(\"Received empty warnings, trying again\");\r\n                WslShutdown();\r\n            }\r\n\r\n            LogError(\"Timed out waiting for warnings. Expected warnings: %ls\", expectedWarnings.c_str());\r\n            VERIFY_FAIL();\r\n        };\r\n\r\n        validateWarnings(L\"[foo]\\na=b\", L\"wsl: Unknown key 'foo.a' in /etc/wsl.conf:2\\r\\n\");\r\n        validateWarnings(L\"a=a\\\\m\", L\"wsl: Invalid escaped character: 'm' in /etc/wsl.conf:1\\r\\n\");\r\n        validateWarnings(L\"[=b\", L\"wsl: Invalid section name in /etc/wsl.conf:1\\r\\n\");\r\n        validateWarnings(L\"\\r\\n\\r\\n[foo]\\r\\na=b\", L\"wsl: Unknown key 'foo.a' in /etc/wsl.conf:5\\r\\n\");\r\n\r\n        // Validate that CRLF is correctly handled\r\n        {\r\n            configChange.SetContent(L\"[network]\\r\\nhostname=foo\\r\\n\");\r\n            TerminateDistribution();\r\n\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"hostname\");\r\n            VERIFY_ARE_EQUAL(out, L\"foo\\n\");\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(Warnings)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslConfigChange configChange(LxssGenerateTestConfig());\r\n\r\n        auto validateWarnings = [&configChange](\r\n                                    const std::wstring& config,\r\n                                    const std::wstring& expectedWarnings,\r\n                                    const std::wstring& prefix = LxssGenerateTestConfig(),\r\n                                    bool fnmatch = false) {\r\n            WEX::Logging::Log::Comment(config.c_str());\r\n            WEX::Logging::Log::Comment(expectedWarnings.c_str());\r\n            configChange.Update(prefix + config);\r\n\r\n            // This loop is here because of a race condition when starting WSL to get the warnings.\r\n            // If a p9rdr distribution startup notification arrives just before wsl.exe calls CreateInstance(),\r\n            // the warnings will be 'consumed' before wsl.exe can read them.\r\n            // To work around that, loop for up to 2 minutes while we don't get any warnings\r\n\r\n            const auto deadline = std::chrono::steady_clock::now() + std::chrono::minutes(2);\r\n\r\n            while (std::chrono::steady_clock::now() < deadline)\r\n            {\r\n                auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(L\"echo ok\");\r\n                VERIFY_ARE_EQUAL(L\"ok\\n\", output);\r\n\r\n                if (!warnings.empty() || expectedWarnings.empty())\r\n                {\r\n                    if (fnmatch)\r\n                    {\r\n                        if (!PathMatchSpec(warnings.c_str(), expectedWarnings.c_str()))\r\n                        {\r\n                            LogError(\"Warning '%ls' didn't match pattern '%ls'\", warnings.c_str(), expectedWarnings.c_str());\r\n                            VERIFY_FAIL();\r\n                        }\r\n                    }\r\n                    else\r\n                    {\r\n                        VERIFY_ARE_EQUAL(expectedWarnings, warnings);\r\n                    }\r\n                    return;\r\n                }\r\n\r\n                LogInfo(\"Received empty warnings, trying again\");\r\n                WslShutdown();\r\n            }\r\n\r\n            LogError(\"Timed out waiting for warnings. Expected warnings: %ls\", expectedWarnings.c_str());\r\n            VERIFY_FAIL();\r\n        };\r\n\r\n        const std::wstring wslConfigPath = wsl::windows::common::helpers::GetWslConfigPath();\r\n\r\n        validateWarnings(L\"a=b\", std::format(L\"wsl: Unknown key 'wsl2.a' in {}:21\\r\\n\", wslConfigPath));\r\n        validateWarnings(L\"[=b\", std::format(L\"wsl: Invalid section name in {}:21\\r\\n\", wslConfigPath));\r\n\r\n        validateWarnings(\r\n            L\"dhcpTimeout=NotANumber\",\r\n            std::format(L\"wsl: Invalid integer value 'NotANumber' for key 'wsl2.dhcpTimeout' in {}:21\\r\\n\", wslConfigPath));\r\n\r\n        validateWarnings(L\"ipv6=NotABoolean\", std::format(L\"wsl: Invalid boolean value 'NotABoolean' for key 'wsl2.ipv6' in {}:21\\r\\n\", wslConfigPath));\r\n\r\n        validateWarnings(L\"[sectionNotComplete\", std::format(L\"wsl: Expected ']' in {}:21\\r\\n\", wslConfigPath));\r\n        validateWarnings(L\"NoEqual\", std::format(L\"wsl: Expected '=' in {}:21\\r\\n\", wslConfigPath));\r\n        validateWarnings(\r\n            L\"networkingMode=InvalidMode\",\r\n            std::format(L\"wsl: Invalid value 'InvalidMode' for config key 'wsl2.networkingMode' in {}:2 (Valid values: Bridged, Mirrored, Nat, None, VirtioProxy)\\r\\n\", wslConfigPath),\r\n            L\"[wsl2]\\n\");\r\n        validateWarnings(\r\n            L\"networkingMode=a\\\\m\", std::format(L\"wsl: Invalid escaped character: 'm' in {}:2\\r\\n\", wslConfigPath), L\"[wsl2]\\n\");\r\n\r\n        validateWarnings(\r\n            L\"\\nswap=200MB\\nswapFile=C:\\\\\\\\DoesNotExist\\\\\\\\swap.vhdx\",\r\n            L\"wsl: Failed to create the swap disk in 'C:\\\\DoesNotExist\\\\swap.vhdx': The system cannot find the path \"\r\n            L\"specified. \\r\\n\");\r\n\r\n        validateWarnings(L\"\\nswap=/\", std::format(L\"wsl: Invalid memory string '/' for .wslconfig entry 'wsl2.swap' in {}:22\\r\\n\", wslConfigPath));\r\n        validateWarnings(L\"\\nswap=0GB\", L\"\");\r\n        validateWarnings(L\"\\nswap=0foo\", std::format(L\"wsl: Invalid memory string '0foo' for .wslconfig entry 'wsl2.swap' in {}:22\\r\\n\", wslConfigPath));\r\n        validateWarnings(L\"safeMode=true\", L\"wsl: SAFE MODE ENABLED - many features will be disabled\\r\\n\", L\"[wsl2]\\n\");\r\n        validateWarnings(L\"processors=\", std::format(L\"wsl: Invalid integer value '' for key 'wsl2.processors' in {}:21\\r\\n\", wslConfigPath));\r\n        validateWarnings(L\"memory=\", std::format(L\"wsl: Invalid memory string '' for .wslconfig entry 'wsl2.memory' in {}:21\\r\\n\", wslConfigPath));\r\n        validateWarnings(L\"debugConsole=\", std::format(L\"wsl: Invalid boolean value '' for key 'wsl2.debugConsole' in {}:21\\r\\n\", wslConfigPath));\r\n        validateWarnings(\r\n            L\"networkingMode=\",\r\n            std::format(L\"wsl: Invalid value '' for config key 'wsl2.networkingMode' in {}:21 (Valid values: Bridged, Mirrored, Nat, None, VirtioProxy)\\r\\n\", wslConfigPath));\r\n\r\n        validateWarnings(\r\n            L\"ipv6=true\\nipv6=false\",\r\n            std::format(L\"wsl: Duplicated config key 'wsl2.ipv6' in {}:22 (Conflicting key: 'wsl2.ipv6' in {}:21)\\r\\n\", wslConfigPath, wslConfigPath));\r\n\r\n        validateWarnings(\r\n            L\"networkingMode=NAT\\n[experimental]\\nnetworkingMode=Mirrored\",\r\n            std::format(L\"wsl: Duplicated config key 'experimental.networkingMode' in {}:4 (Conflicting key: 'wsl2.networkingMode' in {}:2)\\r\\n\", wslConfigPath, wslConfigPath),\r\n            L\"[wsl2]\\n\");\r\n\r\n        validateWarnings(\r\n            L\"networkingMode=bridged\",\r\n            L\"wsl: Bridged networking requires wsl2.vmSwitch to be set.\\r\\n\"\r\n            L\"Error code: CreateInstance/CreateVm/ConfigureNetworking/WSL_E_VMSWITCH_NOT_SET\\r\\n\"\r\n            L\"wsl: Failed to configure network (networkingMode Bridged), falling back to networkingMode None.\\r\\n\",\r\n            L\"[wsl2]\\n\");\r\n\r\n        validateWarnings(\r\n            L\"networkingMode=bridged\\nvmSwitch=DoesNotExist\",\r\n            L\"wsl: The VmSwitch 'DoesNotExist' was not found. Available switches:*\\r\\n\"\r\n            L\"Error code: CreateInstance/CreateVm/ConfigureNetworking/WSL_E_VMSWITCH_NOT_FOUND\\r\\n\"\r\n            L\"wsl: Failed to configure network (networkingMode Bridged), falling back to networkingMode None.\\r\\n\",\r\n            L\"[wsl2]\\n\",\r\n            true);\r\n\r\n        if (!AreExperimentalNetworkingFeaturesSupported())\r\n        {\r\n            validateWarnings(\r\n                L\"[experimental]\\nnetworkingMode=mirrored\",\r\n                L\"wsl: Experimental networking features are not supported, falling back to default settings\\r\\n\",\r\n                L\"[wsl2]\\n\");\r\n\r\n            validateWarnings(\r\n                L\"[experimental]\\ndnsTunneling=true\",\r\n                L\"wsl: Experimental networking features are not supported, falling back to default settings\\r\\n\",\r\n                L\"[wsl2]\\n\");\r\n\r\n            validateWarnings(\r\n                L\"[experimental]\\nfirewall=true\",\r\n                L\"wsl: Experimental networking features are not supported, falling back to default settings\\r\\n\",\r\n                L\"[wsl2]\\n\");\r\n        }\r\n        else\r\n        {\r\n            if (TryLoadDnsResolverMethods())\r\n            {\r\n                // Verify DNS tunneling settings are parsed correctly\r\n                validateWarnings(L\"[experimental]\\ndnsTunneling=true\\nbestEffortDnsParsing=true\", L\"\");\r\n                validateWarnings(L\"[experimental]\\ndnsTunneling=true\\ndnsTunnelingIpAddress=10.255.255.1\", L\"\");\r\n\r\n                validateWarnings(\r\n                    L\"[experimental]\\ndnsTunneling=true\\ndnsTunnelingIpAddress=1.2.3\",\r\n                    std::format(L\"wsl: Invalid IP value '1.2.3' for key 'experimental.dnsTunnelingIpAddress' in {}:23\\r\\n\", wslConfigPath));\r\n            }\r\n        }\r\n\r\n        validateWarnings(\r\n            L\"[experimental]\\nignoredPorts=NotANumber\",\r\n            std::format(L\"wsl: Invalid integer value 'NotANumber' for key 'experimental.ignoredPorts' in {}:22\\r\\n\", wslConfigPath));\r\n\r\n        validateWarnings(\r\n            L\"[experimental]\\nignoredPorts=65536\",\r\n            std::format(L\"wsl: Invalid integer value '65536' for key 'experimental.ignoredPorts' in {}:22\\r\\n\", wslConfigPath));\r\n\r\n        // Verify that the vhdSize setting is parsed correctly.\r\n        validateWarnings(L\"[wsl2]\\ndefaultVhdSize=64GB\\n\", L\"\");\r\n\r\n        auto maxProcessorCount = wsl::windows::common::wslutil::GetLogicalProcessorCount();\r\n        validateWarnings(\r\n            std::format(L\"processors={}\", maxProcessorCount + 1).c_str(),\r\n            std::format(L\"wsl: wsl2.processors cannot exceed the number of logical processors on the system ({} > {})\\r\\n\", maxProcessorCount + 1, maxProcessorCount));\r\n\r\n        // Exclusively open .wslconfig to make it unreadable\r\n        const wil::unique_handle wslConfig{\r\n            CreateFile(wslConfigPath.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};\r\n        VERIFY_IS_NOT_NULL(wslConfig);\r\n\r\n        WslShutdown();\r\n        auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(L\"echo ok\");\r\n        VERIFY_ARE_EQUAL(L\"ok\\n\", output);\r\n\r\n        VERIFY_ARE_EQUAL(\r\n            std::format(L\"wsl: Failed to open config file {}, The process cannot access the file because it is being used by another process. \\r\\n\", wslConfigPath),\r\n            warnings);\r\n\r\n        {\r\n            DistroFileChange fstab(L\"/etc/fstab\");\r\n            fstab.SetContent(L\"invalid fs tab content\");\r\n            TerminateDistribution();\r\n\r\n            std::tie(output, warnings) = LxsstuLaunchWslAndCaptureOutput(L\"echo ok\");\r\n            VERIFY_ARE_EQUAL(L\"ok\\n\", output);\r\n            VERIFY_ARE_EQUAL(L\"wsl: Processing /etc/fstab with mount -a failed.\\n\", warnings);\r\n        }\r\n\r\n        // Validate that WSL_DISABLE_WARNINGS silence the stderr output\r\n        ScopedEnvVariable disableWarnings(L\"WSL_DISABLE_WARNINGS\", L\"1\");\r\n        WslShutdown();\r\n\r\n        std::tie(output, warnings) = LxsstuLaunchWslAndCaptureOutput(L\"echo ok\");\r\n        VERIFY_ARE_EQUAL(L\"ok\\n\", output);\r\n        VERIFY_ARE_EQUAL(L\"\", warnings);\r\n    }\r\n\r\n    TEST_METHOD(Processors)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslConfigChange configChange(LxssGenerateTestConfig() + L\"\\nprocessors=1\");\r\n\r\n        auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(L\"nproc --all\");\r\n        VERIFY_ARE_EQUAL(L\"1\\n\", output);\r\n        VERIFY_ARE_EQUAL(L\"\", warnings);\r\n    }\r\n\r\n    TEST_METHOD(GuiApplications)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        auto validateEnvironment = [&](bool systemdEnabled) {\r\n            WslConfigChange configChange(LxssGenerateTestConfig({.guiApplications = true}));\r\n\r\n            // Validate that running the system distro works.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--system true\"), 0L);\r\n\r\n            // Validate that $DISPLAY and $WAYLAND_DISPLAY are set\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"env | grep DISPLAY=\"), 0L);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"env | grep WAYLAND_DISPLAY=\"), 0L);\r\n\r\n            // Validate the X11 socket is in the expected location and that we can connect to it.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -d /tmp/.X11-unix\"), 0L);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"socat - UNIX-CONNECT:/tmp/.X11-unix/X0 < /dev/null\"), 0L);\r\n\r\n            // Validate the runtime dir exists and the wayland-0 socket is in the expected location.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"env | grep XDG_RUNTIME_DIR=\"), 0L);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -d $XDG_RUNTIME_DIR\"), 0L);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -S $XDG_RUNTIME_DIR/wayland-0\"), 0L);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"socat - UNIX-CONNECT:$XDG_RUNTIME_DIR/wayland-0 < /dev/null\"), 0L);\r\n\r\n            // Validate that WSLg can be disabled.\r\n            configChange.Update(LxssGenerateTestConfig({.guiApplications = false}));\r\n\r\n            // Validate that WSL starts successfully\r\n            auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(L\"echo ok\");\r\n            VERIFY_ARE_EQUAL(L\"ok\\n\", output);\r\n            VERIFY_ARE_EQUAL(L\"\", warnings);\r\n\r\n            // Validate that WSLg-related environment variables are not present.\r\n            //\r\n            // N.B. XDG_RUNTIME_DIR is set when systemd is enabled even if GUI apps are disabled.\r\n            std::vector<std::wstring> variables = {L\"$DISPLAY\", L\"$WAYLAND_DISPLAY\"};\r\n            if (!systemdEnabled)\r\n            {\r\n                variables.emplace_back(L\"$XDG_RUNTIME_DIR\");\r\n            }\r\n\r\n            for (const auto& variable : variables)\r\n            {\r\n                std::tie(output, warnings) = LxsstuLaunchWslAndCaptureOutput(L\"echo -n \" + variable);\r\n                VERIFY_ARE_EQUAL(L\"\", output);\r\n                VERIFY_ARE_EQUAL(L\"\", warnings);\r\n            }\r\n\r\n            // Validate that wsl --system does not start\r\n            std::tie(output, warnings) = LxsstuLaunchWslAndCaptureOutput(L\"--system echo not ok\", -1);\r\n\r\n            const std::wstring configPath = wsl::windows::common::helpers::GetWslConfigPath();\r\n            const auto expectedOutput =\r\n                L\"GUI application support is disabled via \" + configPath +\r\n                L\" or /etc/wsl.conf.\\r\\nError code: Wsl/Service/CreateInstance/WSL_E_GUI_APPLICATIONS_DISABLED\\r\\n\";\r\n\r\n            VERIFY_ARE_EQUAL(output, expectedOutput);\r\n            VERIFY_ARE_EQUAL(L\"\", warnings);\r\n        };\r\n\r\n        LogInfo(\"Validate WSLg state with systemd disabled.\");\r\n        validateEnvironment(false);\r\n\r\n        LogInfo(\"Validate WSLg state with systemd enabled.\");\r\n        auto revert = EnableSystemd();\r\n        VERIFY_IS_TRUE(IsSystemdRunning(L\"--system\"));\r\n        validateEnvironment(true);\r\n    }\r\n\r\n    TEST_METHOD(GuiApplicationsSystemd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        DistroFileChange wslConf(L\"/etc/wsl.conf\", false);\r\n        wslConf.SetContent(L\"[boot]\\nsystemd=true\\n\");\r\n        WslConfigChange config{LxssGenerateTestConfig({.guiApplications = true})};\r\n\r\n        auto validateSocketExists = [](bool exists) {\r\n            LxsstuLaunchWsl(L\"ls -a /tmp/.X11-unix/\");\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -e /tmp/.X11-unix/X0\"), exists ? 0L : 1L);\r\n        };\r\n\r\n        // Validate that wslg.service restores the socket if it's deleted.\r\n        {\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -f /run/systemd/generator/wslg.service\"), 0L);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -e /run/systemd/generator/default.target.wants/wslg.service\"), 0L);\r\n\r\n            validateSocketExists(true);\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"umount /tmp/.X11-unix\"), 0L);\r\n\r\n            validateSocketExists(false);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"systemctl restart wslg.service\"), 0L);\r\n            validateSocketExists(true);\r\n        }\r\n\r\n        // Validate that the unit isn't create when GUI apps are disabled\r\n        {\r\n            config.Update(LxssGenerateTestConfig({.guiApplications = false}));\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -e /run/systemd/generator/wslg.service\"), 1L);\r\n        }\r\n\r\n        // Validate that the unit isn't create when GUI apps are disabled inside the distro.\r\n        {\r\n            wslConf.SetContent(L\"[boot]\\nsystemd=true\\n[general]\\nguiApplications=false\");\r\n            TerminateDistribution();\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"test -e /run/systemd/generator/wslg.service\"), 1L);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(RegistryKeys)\r\n    {\r\n        auto openKey = [&](LPCWSTR keyName) {\r\n            LogInfo(\"OpenKey(HKEY_LOCAL_MACHINE, %ls, KEY_READ)\", keyName);\r\n            return wsl::windows::common::registry::OpenKey(HKEY_LOCAL_MACHINE, keyName, KEY_READ);\r\n        };\r\n\r\n        // Keys that are created by the optional component and the service.\r\n        const std::vector<LPCWSTR> inboxKeys{\r\n            L\"SOFTWARE\\\\Classes\\\\CLSID\\\\{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}\",\r\n            L\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\Desktop\\\\NameSpace\\\\{B2B4A4D1-2754-4140-A2EB-\"\r\n            L\"9A76D9D7CDC6}\",\r\n            L\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\IdListAliasTranslations\\\\WSL\",\r\n            L\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\IdListAliasTranslations\\\\WSLLegacy\",\r\n            L\"SOFTWARE\\\\Classes\\\\Directory\\\\shell\\\\WSL\",\r\n            L\"SOFTWARE\\\\Classes\\\\Directory\\\\Background\\\\shell\\\\WSL\",\r\n            L\"SOFTWARE\\\\Classes\\\\Drive\\\\shell\\\\WSL\"};\r\n\r\n        for (const auto* keyName : inboxKeys)\r\n        {\r\n            auto key = openKey(keyName);\r\n            VERIFY_IS_TRUE(!!key);\r\n        }\r\n\r\n        // Keys that are only created by the MSI.\r\n        const std::vector<LPCWSTR> serviceKeys{\r\n            L\"SOFTWARE\\\\Microsoft\\\\Terminal Server Client\\\\Default\\\\OptionalAddIns\\\\WSLDVC_PACKAGE\",\r\n            L\"SOFTWARE\\\\Classes\\\\CLSID\\\\{7e6ad219-d1b3-42d5-b8ee-d96324e64ff6}\",\r\n            L\"SOFTWARE\\\\Classes\\\\AppID\\\\{17696EAC-9568-4CF5-BB8C-82515AAD6C09}\"};\r\n\r\n        for (const auto* keyName : serviceKeys)\r\n        {\r\n            auto key = openKey(keyName);\r\n            VERIFY_IS_TRUE(!!key);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(BinariesAreSigned)\r\n    {\r\n        if (!wsl::shared::OfficialBuild)\r\n        {\r\n            LogSkipped(\"Build is not signed, skipping test\");\r\n            return;\r\n        }\r\n\r\n        auto installPath = wsl::windows::common::wslutil::GetMsiPackagePath();\r\n        VERIFY_IS_TRUE(installPath.has_value());\r\n\r\n        size_t signedFiles = 0;\r\n\r\n        for (const auto& e : std::filesystem::recursive_directory_iterator(installPath.value()))\r\n        {\r\n            if (wsl::windows::common::string::IsPathComponentEqual(e.path().extension().native(), L\".dll\") ||\r\n                wsl::windows::common::string::IsPathComponentEqual(e.path().extension().native(), L\".exe\"))\r\n            {\r\n                LogInfo(\"Validating signature for: %ls\", e.path().c_str());\r\n\r\n                wsl::windows::common::install::ValidateFileSignature(e.path().c_str());\r\n                signedFiles++;\r\n            }\r\n        }\r\n\r\n        // Sanity check\r\n        VERIFY_ARE_NOT_EQUAL(signedFiles, 0);\r\n    }\r\n\r\n    TEST_METHOD(CorruptedVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Create a 100MB vhd without a filesystem.\r\n        auto distroPath = std::filesystem::weakly_canonical(wil::GetCurrentDirectoryW<std::wstring>());\r\n        auto vhdPath = distroPath / L\"CorruptedTest.vhdx\";\r\n\r\n        VIRTUAL_STORAGE_TYPE storageType{};\r\n        storageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_VHDX;\r\n        storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT;\r\n\r\n        CREATE_VIRTUAL_DISK_PARAMETERS createVhdParameters{};\r\n        createVhdParameters.Version = CREATE_VIRTUAL_DISK_VERSION_2;\r\n        createVhdParameters.Version2.BlockSizeInBytes = 1024 * 1024;\r\n        createVhdParameters.Version2.MaximumSize = 100 * 1024 * 1024;\r\n\r\n        wil::unique_hfile vhd{};\r\n        VERIFY_ARE_EQUAL(\r\n            ::CreateVirtualDisk(\r\n                &storageType, vhdPath.c_str(), VIRTUAL_DISK_ACCESS_NONE, nullptr, CREATE_VIRTUAL_DISK_FLAG_SUPPORT_COMPRESSED_VOLUMES, 0, &createVhdParameters, nullptr, &vhd),\r\n            0l);\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            vhd.reset();\r\n            DeleteFileW(vhdPath.c_str());\r\n        });\r\n\r\n        auto validateOutput = [&](const std::wstring& command, const std::wstring& expectedOutput) {\r\n            auto [output, _] = LxsstuLaunchWslAndCaptureOutput(command.c_str(), -1);\r\n            VERIFY_ARE_EQUAL(output, expectedOutput);\r\n        };\r\n\r\n        // Attempt to import a vhd with an open handle.\r\n        validateOutput(\r\n            std::format(L\"--import-in-place test-distro-corrupted \\\"{}\\\"\", vhdPath.wstring()),\r\n            std::format(\r\n                L\"Failed to attach disk '\\\\\\\\?\\\\{}' to WSL2: The process cannot access the file because it is being used by \"\r\n                L\"another process. \\r\\n\"\r\n                L\"Error code: Wsl/Service/RegisterDistro/MountDisk/HCS/ERROR_SHARING_VIOLATION\\r\\n\",\r\n                vhdPath.wstring()));\r\n\r\n        vhd.reset();\r\n\r\n        // Create a broken distribution registration\r\n        {\r\n            const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n            const auto distroKey =\r\n                wsl::windows::common::registry::CreateKey(userKey.get(), L\"{baa405ef-1822-4bbe-84e2-30e4c6330d42}\");\r\n\r\n            auto revert = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\r\n                wsl::windows::common::registry::DeleteKey(userKey.get(), L\"{baa405ef-1822-4bbe-84e2-30e4c6330d42}\");\r\n            });\r\n\r\n            wsl::windows::common::registry::WriteString(distroKey.get(), nullptr, L\"BasePath\", distroPath.c_str());\r\n            wsl::windows::common::registry::WriteString(distroKey.get(), nullptr, L\"VhdFileName\", L\"CorruptedTest.vhdx\");\r\n            wsl::windows::common::registry::WriteString(distroKey.get(), nullptr, L\"DistributionName\", L\"BrokenDistro\");\r\n            wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"DefaultUid\", 0);\r\n            wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"Version\", LXSS_DISTRO_VERSION_2);\r\n            wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"State\", LxssDistributionStateInstalled);\r\n            wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"Flags\", LXSS_DISTRO_FLAGS_VM_MODE);\r\n\r\n            // Validate that starting the distribution fails with the correct error code.\r\n            validateOutput(\r\n                L\"-d BrokenDistro echo ok\",\r\n                L\"The distribution failed to start because its virtual disk is corrupted.\\r\\n\"\r\n                L\"Error code: Wsl/Service/CreateInstance/WSL_E_DISK_CORRUPTED\\r\\n\");\r\n\r\n            // Validate that trying to export the distribution fails with the correct error code.\r\n            validateOutput(\r\n                L\"--export BrokenDistro dummy.tar\",\r\n                L\"The distribution failed to start because its virtual disk is corrupted.\\r\\n\"\r\n                L\"Error code: Wsl/Service/WSL_E_DISK_CORRUPTED\\r\\n\");\r\n\r\n            // Shutdown WSL to force the disk to detach.\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--shutdown\"), 0L);\r\n        }\r\n\r\n        // Import a corrupted vhd.\r\n        validateOutput(\r\n            std::format(L\"--import-in-place test-distro-corrupted \\\"{}\\\"\", vhdPath.wstring()),\r\n            L\"The distribution failed to start because its virtual disk is corrupted.\\r\\n\"\r\n            L\"Error code: Wsl/Service/RegisterDistro/WSL_E_DISK_CORRUPTED\\r\\n\");\r\n\r\n        // Ensure the VHD can be deleted to make sure it was properly ejected from the VM.\r\n        VERIFY_ARE_EQUAL(DeleteFileW(vhdPath.c_str()), TRUE);\r\n    }\r\n\r\n    static void ValidateDistributionShortcut(LPCWSTR DistroName, HANDLE ExpectedIcon)\r\n    {\r\n        auto distroKey = OpenDistributionKey(DistroName);\r\n        auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n        auto shellLink = wil::CoCreateInstance<IShellLink>(CLSID_ShellLink);\r\n        auto startMenu = wsl::windows::common::filesystem::GetKnownFolderPath(FOLDERID_StartMenu, KF_FLAG_CREATE);\r\n\r\n        // Validate that the shortcut is actually in the start menu\r\n        VERIFY_IS_TRUE(shortcutPath.find(startMenu) != std::string::npos);\r\n\r\n        auto storage = shellLink.query<IPersistFile>();\r\n\r\n        VERIFY_SUCCEEDED(storage->Load(shortcutPath.c_str(), 0));\r\n\r\n        std::wstring target(MAX_PATH, '\\0');\r\n\r\n        WIN32_FIND_DATA findData{};\r\n        VERIFY_SUCCEEDED(shellLink->GetPath(target.data(), static_cast<int>(target.size()), &findData, SLGP_RAWPATH));\r\n        target.resize(wcslen(target.c_str()));\r\n\r\n        static auto wslExePath = wsl::windows::common::wslutil::GetMsiPackagePath().value() + L\"wsl.exe\";\r\n        VERIFY_ARE_EQUAL(target, wslExePath);\r\n\r\n        std::wstring arguments(MAX_PATH, '\\0');\r\n        VERIFY_SUCCEEDED(shellLink->GetArguments(arguments.data(), static_cast<int>(arguments.size())));\r\n        arguments.resize(wcslen(arguments.c_str()));\r\n\r\n        auto distroId = GetDistributionId(DistroName);\r\n        VERIFY_IS_TRUE(distroId.has_value());\r\n\r\n        VERIFY_ARE_EQUAL(\r\n            std::format(L\"{} {} {} {}\", WSL_DISTRIBUTION_ID_ARG, wsl::shared::string::GuidToString<wchar_t>(distroId.value()), WSL_CHANGE_DIRECTORY_ARG, WSL_CWD_HOME),\r\n            arguments);\r\n\r\n        std::wstring iconLocation(MAX_PATH, '\\0');\r\n        int id{};\r\n        THROW_IF_FAILED(shellLink->GetIconLocation(iconLocation.data(), static_cast<int>(iconLocation.size()), &id));\r\n        iconLocation.resize(wcslen(iconLocation.c_str()));\r\n\r\n        if (ExpectedIcon == nullptr)\r\n        {\r\n            VERIFY_ARE_EQUAL(iconLocation, wslExePath);\r\n        }\r\n        else\r\n        {\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            // Validate that the icon is under the distribution folder.\r\n            VERIFY_IS_TRUE(iconLocation.find(basePath) != std::string::npos);\r\n\r\n            // Validate that the icon has the content we expect.\r\n            wil::unique_handle distroIcon{CreateFile(iconLocation.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)};\r\n            VERIFY_ARE_EQUAL(GetFileSize(ExpectedIcon, nullptr), GetFileSize(distroIcon.get(), nullptr));\r\n        }\r\n    }\r\n\r\n    static std::pair<nlohmann::json, std::wstring> ValidateDistributionTerminalProfile(const std::wstring& DistroName, bool defaultIcon)\r\n    {\r\n        using namespace wsl::windows::common::wslutil;\r\n        using namespace wsl::windows::common::string;\r\n\r\n        auto distroKey = OpenDistributionKey(DistroName.c_str());\r\n        auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n\r\n        auto distroId = GetDistributionId(DistroName.c_str());\r\n        VERIFY_IS_TRUE(distroId.has_value());\r\n\r\n        auto distroIdString = wsl::shared::string::GuidToString<wchar_t>(distroId.value());\r\n        auto distributionProfileId =\r\n            wsl::shared::string::GuidToString<wchar_t>(CreateV5Uuid(WslTerminalNamespace, std::as_bytes(std::span{distroIdString})));\r\n\r\n        auto profilePath = wsl::windows::common::filesystem::GetLocalAppDataPath(nullptr) / L\"Microsoft\" / L\"Windows Terminal\" /\r\n                           L\"Fragments\" / L\"Microsoft.WSL\" / (distributionProfileId + L\".json\");\r\n\r\n        std::ifstream file{profilePath};\r\n        VERIFY_IS_TRUE(file.good());\r\n\r\n        nlohmann::json json;\r\n        VERIFY_IS_TRUE((file >> json).good());\r\n\r\n        VERIFY_IS_TRUE(json.is_object());\r\n\r\n        auto profiles = json.find(\"profiles\");\r\n        VERIFY_ARE_NOT_EQUAL(profiles, json.end());\r\n        VERIFY_IS_TRUE(profiles->is_array());\r\n\r\n        VERIFY_IS_TRUE(profiles->size() >= 2);\r\n        const auto profileHide = profiles->at(0);\r\n\r\n        auto expectedHideGuid = wsl::shared::string::GuidToString<wchar_t>(\r\n            CreateV5Uuid(GeneratedProfilesTerminalNamespace, std::as_bytes(std::span{DistroName})));\r\n        VERIFY_ARE_EQUAL(profileHide[\"updates\"], wsl::shared::string::WideToMultiByte(expectedHideGuid));\r\n        VERIFY_ARE_EQUAL(profileHide[\"hidden\"], true);\r\n\r\n        const auto launchProfile = profiles->at(1);\r\n\r\n        auto expectedId =\r\n            wsl::shared::string::GuidToString<wchar_t>(CreateV5Uuid(WslTerminalNamespace, std::as_bytes(std::span{distroIdString})));\r\n        VERIFY_ARE_EQUAL(launchProfile[\"guid\"].get<std::string>(), wsl::shared::string::WideToMultiByte(expectedId));\r\n        VERIFY_ARE_EQUAL(launchProfile[\"name\"].get<std::string>(), wsl::shared::string::WideToMultiByte(DistroName));\r\n        VERIFY_ARE_EQUAL(launchProfile[\"pathTranslationStyle\"].get<std::string>(), \"wsl\");\r\n\r\n        std::wstring systemDir;\r\n        wil::GetSystemDirectoryW(systemDir);\r\n\r\n        VERIFY_ARE_EQUAL(\r\n            std::format(\"{}\\\\{} {} {}\", systemDir, WSL_BINARY_NAME, WSL_DISTRIBUTION_ID_ARG, distroIdString),\r\n            launchProfile[\"commandline\"].get<std::string>());\r\n\r\n        // Verify that startingDirectory is set to home directory\r\n        VERIFY_ARE_EQUAL(launchProfile[\"startingDirectory\"].get<std::string>(), \"~\");\r\n\r\n        auto iconLocation = wsl::shared::string::MultiByteToWide(launchProfile[\"icon\"].get<std::string>());\r\n        if (defaultIcon)\r\n        {\r\n            static auto wslExePath = wsl::windows::common::wslutil::GetMsiPackagePath().value() + L\"wsl.exe\";\r\n            VERIFY_ARE_EQUAL(iconLocation, wslExePath);\r\n        }\r\n        else\r\n        {\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            // Validate that the icon is under the distribution folder.\r\n            VERIFY_IS_TRUE(iconLocation.find(basePath) == 0);\r\n        }\r\n\r\n        return std::make_pair(json, profilePath);\r\n    }\r\n\r\n    TEST_METHOD(ConvertDistro)\r\n    {\r\n        std::wstring originalVersion;\r\n        std::wstring targetVersion;\r\n        if (LxsstuVmMode())\r\n        {\r\n            originalVersion = L\"2\";\r\n            targetVersion = L\"1\";\r\n        }\r\n        else\r\n        {\r\n            originalVersion = L\"1\";\r\n            targetVersion = L\"2\";\r\n        }\r\n\r\n        auto cleanup =\r\n            wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { LxsstuLaunchWsl(L\"--set-version test_distro \" + originalVersion); });\r\n\r\n        // Convert the test distribution to the target version and back to the original.\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--set-version test_distro \" + targetVersion), 0u);\r\n        ValidateDistributionShortcut(LXSS_DISTRO_NAME_TEST_L, nullptr);\r\n        ValidateDistributionTerminalProfile(LXSS_DISTRO_NAME_TEST_L, true);\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--set-version test_distro \" + originalVersion), 0u);\r\n        ValidateDistributionShortcut(LXSS_DISTRO_NAME_TEST_L, nullptr);\r\n        ValidateDistributionTerminalProfile(LXSS_DISTRO_NAME_TEST_L, true);\r\n\r\n        // Do not convert the test distribution if it is already in the original version.\r\n        cleanup.release();\r\n    }\r\n\r\n    TEST_METHOD(ManualDistroShutdown)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Terminate a distribution from within WSL. This command should be terminated by the VM terminating\r\n        LxsstuLaunchWsl(L\"echo foo > /dev/shm/bar ; reboot -f ; sleep 1d\");\r\n\r\n        // Wait for distribution to be terminated to avoid running the next command as it shuts down\r\n        auto pred = []() {\r\n            const auto commandLine = LxssGenerateWslCommandLine(L\"--list --running\");\r\n            wsl::windows::common::SubProcess process(nullptr, commandLine.c_str());\r\n\r\n            // Don't check the exit code since that command returns -1 when no distros are running.\r\n            const auto output = process.RunAndCaptureOutput();\r\n            THROW_HR_IF(E_ABORT, output.Stdout.find(LXSS_DISTRO_NAME_TEST_L) != std::string::npos);\r\n        };\r\n\r\n        wsl::shared::retry::RetryWithTimeout<void>(pred, std::chrono::seconds(1), std::chrono::minutes(2));\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"test -f /dev/shm/bar2  || echo -n ok\");\r\n        VERIFY_ARE_EQUAL(out, L\"ok\");\r\n    }\r\n\r\n    TEST_METHOD(KernelModules)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Get the kernel version and strip off everything after the first dash.\r\n        std::wstring kernelVersion{TEXT(KERNEL_VERSION)};\r\n        auto position = kernelVersion.find_first_of(L\"-\");\r\n        if (position != kernelVersion.npos)\r\n        {\r\n            kernelVersion = kernelVersion.substr(0, position);\r\n        }\r\n\r\n        kernelVersion += L\"-microsoft-standard-WSL2\";\r\n\r\n        // Ensure the kernel modules folder is mounted correctly.\r\n        std::wstring command = std::format(\r\n            L\"mount | grep -iF 'none on /usr/lib/modules/{} type overlay \"\r\n            L\"(rw,nosuid,nodev,noatime,lowerdir=/modules,upperdir=/lib/modules/{}/rw/upper,workdir=/lib/modules/{}/rw/\"\r\n            L\"work,uuid=on)'\",\r\n            kernelVersion,\r\n            kernelVersion,\r\n            kernelVersion);\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(command.c_str(), nullptr, nullptr, nullptr, nullptr), 0u);\r\n\r\n        // Update .wslconfig and ensure an error is displayed if nonexistent kernel or modules is specified.\r\n        const std::wstring wslConfigPath = wsl::windows::common::helpers::GetWslConfigPath();\r\n        const std::wstring nonExistentFile = L\"DoesNotExist\";\r\n        WslConfigChange configChange(LxssGenerateTestConfig({.kernel = nonExistentFile.c_str()}));\r\n        ValidateOutput(\r\n            L\"echo ok\",\r\n            std::format(\r\n                L\"{}\\r\\nError code: Wsl/Service/CreateInstance/CreateVm/WSL_E_CUSTOM_KERNEL_NOT_FOUND\\r\\n\",\r\n                wsl::shared::Localization::MessageCustomKernelNotFound(wslConfigPath, nonExistentFile)),\r\n            L\"\");\r\n\r\n        configChange.Update(LxssGenerateTestConfig({.kernelModules = nonExistentFile.c_str()}));\r\n        ValidateOutput(\r\n            L\"echo ok\",\r\n            std::format(\r\n                L\"{}\\r\\nError code: Wsl/Service/CreateInstance/CreateVm/WSL_E_CUSTOM_KERNEL_NOT_FOUND\\r\\n\",\r\n                wsl::shared::Localization::MessageCustomKernelModulesNotFound(wslConfigPath, nonExistentFile)),\r\n            L\"\");\r\n\r\n#ifdef WSL_DEV_INSTALL_PATH\r\n\r\n        std::wstring kernelPath = WSL_DEV_INSTALL_PATH L\"/kernel\";\r\n        std::wstring kernelModulesPath = WSL_DEV_INSTALL_PATH L\"/modules.vhd\";\r\n\r\n#else\r\n\r\n        auto installPath = wsl::windows::common::wslutil::GetMsiPackagePath();\r\n        VERIFY_IS_TRUE(installPath.has_value());\r\n\r\n        std::filesystem::path wslInstallPath(installPath.value());\r\n\r\n        std::wstring kernelPath = wslInstallPath / \"tools\" / \"kernel\";\r\n        std::wstring kernelModulesPath = wslInstallPath / \"tools\" / \"modules.vhd\";\r\n\r\n#endif\r\n\r\n        // Verify that no modules are mounted for a custom kernel with no modules specified.\r\n        kernelPath = std::regex_replace(kernelPath, std::wregex(L\"\\\\\\\\\"), L\"\\\\\\\\\");\r\n        configChange.Update(LxssGenerateTestConfig({.kernel = kernelPath.c_str()}));\r\n        ValidateOutput(command.c_str(), L\"\", L\"\", 1);\r\n\r\n        // Verify the error message if custom kernel modules are used with the default kernel.\r\n        kernelModulesPath = std::regex_replace(kernelModulesPath, std::wregex(L\"\\\\\\\\\"), L\"\\\\\\\\\");\r\n        configChange.Update(LxssGenerateTestConfig({.kernelModules = kernelModulesPath.c_str()}));\r\n        ValidateOutput(\r\n            L\"echo ok\",\r\n            std::format(\r\n                L\"{}\\r\\nError code: Wsl/Service/CreateInstance/CreateVm/WSL_E_CUSTOM_KERNEL_NOT_FOUND\\r\\n\",\r\n                wsl::shared::Localization::MessageMismatchedKernelModulesError()),\r\n            L\"\");\r\n\r\n        configChange.Update(LxssGenerateTestConfig());\r\n\r\n        // Validate that tun is loaded by default.\r\n        ValidateOutput(L\"grep -i '^tun' /proc/modules | wc -l\", L\"1\\n\", L\"\", 0);\r\n\r\n        // Validate a VM can boot with no extra additional kernel modules.\r\n        configChange.Update(LxssGenerateTestConfig({.loadDefaultKernelModules = false}));\r\n        ValidateOutput(L\"grep -i '^tun' /proc/modules | wc -l\", L\"0\\n\", L\"\", 0);\r\n\r\n        // Validate that the user can pass additional modules to load at boot.\r\n        ValidateOutput(L\"grep -iE '^(usb_storage|dm_crypt)' /proc/modules  | wc -l\", L\"0\\n\", L\"\", 0);\r\n\r\n        configChange.Update(LxssGenerateTestConfig({.loadKernelModules = L\"usb_storage,dm_crypt\"}));\r\n        ValidateOutput(L\"grep -iE '^(usb_storage|dm_crypt)' /proc/modules  | wc -l\", L\"2\\n\", L\"\", 0);\r\n\r\n        // Validate that failing to load a module shows a warning in dmesg.\r\n        configChange.Update(LxssGenerateTestConfig({.loadKernelModules = L\"not-found\"}));\r\n        ValidateOutput(L\"dmesg | grep -iF \\\"failed to load module 'not-found'\\\" | wc -l\", L\"1\\n\", L\"\", 0);\r\n    }\r\n\r\n    TEST_METHOD(CrashCollection)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        const auto folder = std::filesystem::absolute(L\"test-crash-dumps\");\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            std::error_code error;\r\n            std::filesystem::remove_all(folder, error);\r\n        });\r\n\r\n        auto countCrashes = [&]() {\r\n            std::error_code error;\r\n            return std::distance(std::filesystem::directory_iterator{folder, error}, std::filesystem::directory_iterator{});\r\n        };\r\n\r\n        auto waitForCrashes = [&](int expected) {\r\n            wsl::shared::retry::RetryWithTimeout<void>(\r\n                [&]() { THROW_HR_IF(E_UNEXPECTED, countCrashes() < expected); }, std::chrono::seconds(1), std::chrono::minutes(2));\r\n\r\n            VERIFY_ARE_EQUAL(countCrashes(), expected);\r\n        };\r\n\r\n        auto crash = []() { LxsstuLaunchWsl(L\"kill -SEGV $$\"); };\r\n\r\n        WslConfigChange change(LxssGenerateTestConfig({.crashDumpCount = 2, .CrashDumpFolder = folder.wstring()}));\r\n\r\n        VERIFY_ARE_EQUAL(countCrashes(), 0);\r\n\r\n        crash();\r\n        waitForCrashes(1);\r\n\r\n        crash();\r\n        waitForCrashes(2);\r\n\r\n        crash();\r\n        waitForCrashes(2);\r\n\r\n        // Create a dummy file and validate that the file limit logic doesn't remove it.\r\n        std::filesystem::remove_all(folder);\r\n        std::filesystem::create_directory(folder);\r\n        std::ofstream(folder / \"dummy\").close();\r\n\r\n        crash();\r\n        waitForCrashes(2);\r\n\r\n        crash();\r\n        waitForCrashes(3);\r\n\r\n        crash();\r\n        waitForCrashes(3);\r\n\r\n        VERIFY_IS_TRUE(std::filesystem::exists(folder / \"dummy\"));\r\n    }\r\n\r\n    // UnitTests Private Methods\r\n\r\n    static VOID VerifyCaseSensitiveDirectory(_In_ PCWSTR RelativePath)\r\n    {\r\n\r\n        const std::wstring Path = LxsstuGetLxssDirectory() + L\"\\\\\" + RelativePath;\r\n        const wil::unique_hfile Directory{CreateFileW(\r\n            Path.c_str(),\r\n            FILE_READ_ATTRIBUTES,\r\n            (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),\r\n            nullptr,\r\n            OPEN_EXISTING,\r\n            (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT),\r\n            nullptr)};\r\n\r\n        THROW_LAST_ERROR_IF(!Directory);\r\n        IO_STATUS_BLOCK IoStatus;\r\n        FILE_CASE_SENSITIVE_INFORMATION CaseInfo;\r\n        THROW_IF_NTSTATUS_FAILED(NtQueryInformationFile(Directory.get(), &IoStatus, &CaseInfo, sizeof(CaseInfo), FileCaseSensitiveInformation));\r\n\r\n        VERIFY_ARE_EQUAL(CaseInfo.Flags, (ULONG)FILE_CS_FLAG_CASE_SENSITIVE_DIR);\r\n    }\r\n\r\n    TEST_METHOD(Move)\r\n    {\r\n        constexpr auto name = L\"move-test-distro\";\r\n        constexpr auto testFolder = L\"move-test-test-folder\";\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--import {} . \\\"{}\\\" --version 2\", name, g_testDistroPath)), 0L);\r\n\r\n        auto cleanupName = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [name]() {\r\n            LxsstuLaunchWsl(std::format(L\"--unregister {}\", name));\r\n            std::filesystem::remove_all(testFolder);\r\n        });\r\n\r\n        auto validateDistro = []() {\r\n            auto [cmdOutput, _] = LxsstuLaunchWslAndCaptureOutput(L\"echo ok\");\r\n            VERIFY_ARE_EQUAL(cmdOutput, L\"ok\\n\");\r\n        };\r\n\r\n        // Move the distro to a different folder (relative path)\r\n        {\r\n            WslShutdown();\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--manage {} --move {}\", name, testFolder)), 0L);\r\n\r\n            // Validate that the distribution still starts\r\n            validateDistro();\r\n            VERIFY_IS_TRUE(std::filesystem::exists(std::format(L\"{}\\\\ext4.vhdx\", testFolder)));\r\n        }\r\n\r\n        auto absolutePath = std::filesystem::weakly_canonical(\".\").wstring();\r\n\r\n        // Move the distro to a different folder (absolute path)\r\n        {\r\n            WslShutdown();\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--manage {} --move {}\", name, absolutePath)), 0L);\r\n\r\n            // Validate that the distribution still starts\r\n            validateDistro();\r\n            VERIFY_IS_TRUE(std::filesystem::exists(std::format(L\"{}\\\\ext4.vhdx\", absolutePath)));\r\n        }\r\n\r\n        // Try to move the distribution to a folder that's already in use\r\n        {\r\n            WslShutdown();\r\n\r\n            wil::unique_cotaskmem_string path;\r\n            THROW_IF_FAILED(::SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path));\r\n            auto targetPath = std::format(L\"{}\\\\lxss\", path.get());\r\n            auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--manage {} --move {}\", name, targetPath), -1);\r\n\r\n            VERIFY_ARE_EQUAL(\r\n                out,\r\n                L\"The supplied install location is already in use.\\r\\nError code: \"\r\n                L\"Wsl/Service/MoveDistro/ERROR_FILE_EXISTS\\r\\n\");\r\n            // Validate that the distribution still starts and that the vhd hasn't moved.\r\n            validateDistro();\r\n            VERIFY_IS_TRUE(std::filesystem::exists(std::format(L\"{}\\\\ext4.vhdx\", absolutePath)));\r\n        }\r\n\r\n        // Try to move the distribution to an invalid path\r\n        {\r\n            WslShutdown();\r\n\r\n            auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--manage {} --move :\", name), -1);\r\n\r\n            VERIFY_ARE_EQUAL(\r\n                out,\r\n                L\"The filename, directory name, or volume label syntax is incorrect. \\r\\nError code: \"\r\n                L\"Wsl/Service/MoveDistro/ERROR_INVALID_NAME\\r\\n\");\r\n            // Validate that the distribution still starts and that the vhd hasn't moved.\r\n            validateDistro();\r\n            VERIFY_IS_TRUE(std::filesystem::exists(std::format(L\"{}\\\\ext4.vhdx\", absolutePath)));\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(Resize)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        constexpr auto name = L\"resize-test-distro\";\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--import {} . \\\"{}\\\" --version 2\", name, g_testDistroPath)), 0L);\r\n        WslShutdown();\r\n\r\n        auto cleanupName =\r\n            wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [name]() { LxsstuLaunchWsl(std::format(L\"--unregister {}\", name)); });\r\n\r\n        auto validateDistro = [name](LPCWSTR size, LPCWSTR expectedSize, LPCWSTR expectedError = nullptr) {\r\n            auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--manage {} --resize {}\", name, size), expectedError ? -1 : 0);\r\n            if (expectedError)\r\n            {\r\n                VERIFY_ARE_EQUAL(expectedError, out);\r\n                return;\r\n            }\r\n\r\n            std::tie(out, _) = LxsstuLaunchWslAndCaptureOutput(std::format(L\"-d {} df -h / --output=size | sed 1d\", name));\r\n            VERIFY_ARE_EQUAL(std::format(L\" {}\\n\", expectedSize), out);\r\n            WslShutdown();\r\n        };\r\n\r\n        validateDistro(L\"1500G\", L\"1.5T\");\r\n        validateDistro(L\"500G\", L\"492G\");\r\n        validateDistro(L\"1M\", nullptr, L\"Failed to resize disk.\\r\\nError code: Wsl/Service/E_FAIL\\r\\n\");\r\n\r\n        {\r\n            WslKeepAlive keepAlive;\r\n            auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"--manage test_distro --resize 1500GB\", -1);\r\n            VERIFY_ARE_EQUAL(\r\n                L\"The operation could not be completed because the VHD is currently in use. To force WSL to stop use: wsl.exe \"\r\n                L\"--shutdown\\r\\nError code: Wsl/Service/WSL_E_DISTRO_NOT_STOPPED\\r\\n\",\r\n                out);\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(FileOffsets)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { DeleteFile(L\"output.txt\"); });\r\n\r\n        std::ofstream file(\"output.txt\");\r\n        VERIFY_IS_TRUE(file.good() && file << \"previous content\\n\");\r\n        file.close();\r\n\r\n        std::wstring cmd(L\"C:\\\\windows\\\\system32\\\\cmd.exe /c \\\"wsl.exe echo ok >> output.txt && type output.txt\\\"\");\r\n        auto [output, _] = LxsstuLaunchCommandAndCaptureOutput(cmd.data());\r\n\r\n        VERIFY_ARE_EQUAL(output, L\"previous content\\r\\nok\\n\");\r\n    }\r\n\r\n    TEST_METHOD(GlobalFlagsOverride)\r\n    {\r\n        auto isDriveMountingEnabled = []() { return LxsstuLaunchWsl(L\"test -d /mnt/c/Windows\") == 0; };\r\n\r\n        VERIFY_IS_TRUE(isDriveMountingEnabled());\r\n\r\n        {\r\n            RegistryKeyChange<DWORD> key(HKEY_LOCAL_MACHINE, LXSS_SERVICE_REGISTRY_PATH, L\"DistributionFlags\", ~LXSS_DISTRO_FLAGS_ENABLE_DRIVE_MOUNTING);\r\n\r\n            TerminateDistribution();\r\n            VERIFY_IS_FALSE(isDriveMountingEnabled());\r\n        }\r\n\r\n        TerminateDistribution();\r\n        VERIFY_IS_TRUE(isDriveMountingEnabled());\r\n    }\r\n\r\n    TEST_METHOD(WriteWslConfig)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n        WSL_SETTINGS_TEST();\r\n\r\n        auto installPath = wsl::windows::common::wslutil::GetMsiPackagePath();\r\n        VERIFY_IS_TRUE(installPath.has_value());\r\n\r\n        std::filesystem::path wslInstallPath(installPath.value());\r\n        std::filesystem::path libWslDllPath = wslInstallPath / \"libwsl.dll\";\r\n        VERIFY_IS_TRUE(std::filesystem::exists(libWslDllPath));\r\n\r\n        LxssDynamicFunction<decltype(GetWslConfigFilePath)> getWslConfigFilePath(libWslDllPath.c_str(), \"GetWslConfigFilePath\");\r\n        LxssDynamicFunction<decltype(CreateWslConfig)> createWslConfig(libWslDllPath.c_str(), \"CreateWslConfig\");\r\n        LxssDynamicFunction<decltype(FreeWslConfig)> freeWslConfig(libWslDllPath.c_str(), \"FreeWslConfig\");\r\n        LxssDynamicFunction<decltype(GetWslConfigSetting)> getWslConfigSetting(libWslDllPath.c_str(), \"GetWslConfigSetting\");\r\n        LxssDynamicFunction<decltype(SetWslConfigSetting)> setWslConfigSetting(libWslDllPath.c_str(), \"SetWslConfigSetting\");\r\n\r\n        // Reset the test config file. The original has already been saved as part of module setup.\r\n        auto wslConfigFilePath = getenv(\"userprofile\") + std::string(\"\\\\.wslconfig\");\r\n        WslConfigChange config{L\"\"};\r\n\r\n        auto apiWslConfigFilePath = getWslConfigFilePath();\r\n        VERIFY_IS_TRUE(std::filesystem::path(wslConfigFilePath) == std::filesystem::path(apiWslConfigFilePath));\r\n\r\n        auto wslConfigDefaults = createWslConfig(nullptr);\r\n        VERIFY_IS_NOT_NULL(wslConfigDefaults);\r\n        auto wslConfig = createWslConfig(apiWslConfigFilePath);\r\n        VERIFY_IS_NOT_NULL(wslConfig);\r\n\r\n        freeWslConfig(wslConfigDefaults);\r\n        freeWslConfig(wslConfig);\r\n\r\n        WslConfigSetting wslConfigSettingWriteOut;\r\n        WslConfigSetting wslConfigSettingReadIn;\r\n\r\n        auto testLoop = [&](auto& testPlan, auto& updateWslConfigSettingWriteOutValue, auto& verifyWslConfigSettingValueReadEqual) {\r\n            wslConfigSettingWriteOut = wslConfigSettingReadIn = WslConfigSetting{};\r\n            for (const auto testEntry : testPlan)\r\n            {\r\n                wslConfigSettingWriteOut = testEntry.first;\r\n                for (const auto& test : testEntry.second)\r\n                {\r\n                    const auto& writeValue = test.first;\r\n                    const auto& expectedValue = test.second;\r\n                    {\r\n                        // This scenario tests writing a value to the config file and reading it back. If the write succeeded,\r\n                        // the written value will be cached in the WslConfig object. The read will then return the cached value.\r\n                        wslConfig = createWslConfig(apiWslConfigFilePath);\r\n                        VERIFY_IS_NOT_NULL(wslConfig);\r\n                        auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n                        updateWslConfigSettingWriteOutValue(wslConfigSettingWriteOut, writeValue);\r\n\r\n                        VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigSettingWriteOut), ERROR_SUCCESS);\r\n                        wslConfigSettingReadIn = getWslConfigSetting(wslConfig, wslConfigSettingWriteOut.ConfigEntry);\r\n                        VERIFY_ARE_EQUAL(wslConfigSettingReadIn.ConfigEntry, wslConfigSettingWriteOut.ConfigEntry);\r\n                        verifyWslConfigSettingValueReadEqual(wslConfigSettingReadIn, expectedValue);\r\n                    }\r\n                    {\r\n                        // This scenario tests reading a value from the config file. Specifically, it will parse in the\r\n                        // written value to the wsl config file from the previous scenario. This validates parsing the value\r\n                        // from the file (e.g. that it was written correctly and then parsed as expected).\r\n                        wslConfig = createWslConfig(apiWslConfigFilePath);\r\n                        auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n                        wslConfigSettingReadIn = getWslConfigSetting(wslConfig, wslConfigSettingWriteOut.ConfigEntry);\r\n                        VERIFY_ARE_EQUAL(wslConfigSettingReadIn.ConfigEntry, wslConfigSettingWriteOut.ConfigEntry);\r\n                        verifyWslConfigSettingValueReadEqual(wslConfigSettingReadIn, expectedValue);\r\n                    }\r\n                }\r\n            }\r\n        };\r\n\r\n        {\r\n            // Enable NetworkingMode::Mirrored for IgnoredPorts to be set correctly upon parsing.\r\n            WslConfigChange config(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored}));\r\n\r\n            // std::pair[0] = Written value, std::pair[1] = Actual/Expected value\r\n            static const std::vector<std::pair<PCWSTR, PCWSTR>> filePathsToTest{\r\n                {L\"C:\\\\DoesNotExit\\\\ext4.vhdx\", L\"C:\\\\DoesNotExit\\\\ext4.vhdx\"},\r\n                {L\"\\\\DoesNotExit\\\\ext4.vhdx\", L\"\\\\DoesNotExit\\\\ext4.vhdx\"},\r\n                {L\"\", L\"\"},\r\n            };\r\n\r\n            // tuple: WslConfigSetting, expectedValue, actualValue\r\n            std::vector<std::pair<WslConfigSetting, std::vector<std::pair<PCWSTR, PCWSTR>>>> wslConfigSettingStringTestPlan{\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::SwapFilePath},\r\n                    filePathsToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::IgnoredPorts},\r\n                    {\r\n                        {L\"1,2,300,4455,65535\", L\"1,2,300,4455,65535\"},\r\n                        {L\"10,20,-100,p\", L\"10,20\"},\r\n                        {L\"100,200,notaport\", L\"100,200\"},\r\n                        {L\"1000,2000;3.4\", L\"1000,2000\"},\r\n                        {L\"10000, 20000,        30000,40000        ,50000\", L\"10000,20000,30000,40000,50000\"},\r\n                        {L\"\", L\"\"},\r\n                        {L\"notaport\", L\"\"},\r\n                        {L\"-5555\", L\"\"},\r\n                        {L\"C:\\\\DoesNotExit\\\\ext4.vhdx\", L\"\"},\r\n                    },\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::KernelPath},\r\n                    filePathsToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::SystemDistroPath},\r\n                    filePathsToTest,\r\n                },\r\n            };\r\n\r\n            auto updateWslConfigSettingWriteOutStringValue = [](auto& wslConfigSettingWriteOut, auto& writeValue) {\r\n                wslConfigSettingWriteOut.StringValue = writeValue;\r\n            };\r\n\r\n            auto verifyWslConfigSettingReadStringValueEqual = [](auto& wslConfigSettingReadIn, auto& expectedValue) {\r\n                VERIFY_ARE_EQUAL(std::wstring_view(wslConfigSettingReadIn.StringValue), std::wstring_view(expectedValue));\r\n            };\r\n\r\n            testLoop(wslConfigSettingStringTestPlan, updateWslConfigSettingWriteOutStringValue, verifyWslConfigSettingReadStringValueEqual);\r\n        }\r\n\r\n        {\r\n            wslConfigSettingWriteOut = wslConfigSettingReadIn = WslConfigSetting{};\r\n            wslConfigSettingWriteOut.ConfigEntry = WslConfigEntry::NoEntry;\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            wslConfigSettingReadIn = getWslConfigSetting(wslConfig, wslConfigSettingWriteOut.ConfigEntry);\r\n            VERIFY_ARE_EQUAL(wslConfigSettingReadIn.ConfigEntry, wslConfigSettingWriteOut.ConfigEntry);\r\n        }\r\n\r\n        SYSTEM_INFO systemInfo{};\r\n        GetSystemInfo(&systemInfo);\r\n        {\r\n            // std::pair[0] = Written value, std::pair[1] = Actual/Expected value\r\n            static const std::vector<std::pair<int, int>> timeoutValuesToTest{\r\n                {-132445, -132445},\r\n                {0, 0},\r\n                {1, 1},\r\n                {13456, 13456},\r\n                {100000000, 100000000},\r\n            };\r\n\r\n            // tuple: WslConfigSetting, expectedValue, actualValue\r\n            std::vector<std::pair<WslConfigSetting, std::vector<std::pair<int, int>>>> wslConfigSettingInt32TestPlan{\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::ProcessorCount},\r\n                    {\r\n                        {-123443, systemInfo.dwNumberOfProcessors},\r\n                        {-1, systemInfo.dwNumberOfProcessors},\r\n                        {1, 1},\r\n                        {2, std::min(2, static_cast<int>(systemInfo.dwNumberOfProcessors))},\r\n                        {systemInfo.dwNumberOfProcessors, systemInfo.dwNumberOfProcessors},\r\n                        {1234, systemInfo.dwNumberOfProcessors},\r\n                    },\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::InitialAutoProxyTimeout},\r\n                    timeoutValuesToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::VMIdleTimeout},\r\n                    timeoutValuesToTest,\r\n                },\r\n            };\r\n\r\n            auto updateWslConfigSettingWriteOutInt32Value = [](auto& wslConfigSettingWriteOut, auto& writeValue) {\r\n                wslConfigSettingWriteOut.Int32Value = writeValue;\r\n            };\r\n\r\n            auto verifyWslConfigSettingReadInt32ValueEqual = [](auto& wslConfigSettingReadIn, auto& expectedValue) {\r\n                VERIFY_ARE_EQUAL(wslConfigSettingReadIn.Int32Value, expectedValue);\r\n            };\r\n\r\n            testLoop(wslConfigSettingInt32TestPlan, updateWslConfigSettingWriteOutInt32Value, verifyWslConfigSettingReadInt32ValueEqual);\r\n        }\r\n\r\n        {\r\n            MEMORYSTATUSEX memInfo{sizeof(MEMORYSTATUSEX)};\r\n            THROW_IF_WIN32_BOOL_FALSE(GlobalMemoryStatusEx(&memInfo));\r\n            const auto minimumMemorySizeBytes = 256 * _1MB;\r\n            const auto maximumMemorySizeBytes = memInfo.ullTotalPhys;\r\n\r\n            // std::pair[0] = Written value, std::pair[1] = Actual/Expected value\r\n            static const std::vector<std::pair<unsigned long long, unsigned long long>> fileSizesBytesToTest{\r\n                {0, 0}, {1, 1}, {13456, 13456}, {100000000, 100000000}, {9223372036854775807, 9223372036854775807}};\r\n\r\n            // tuple: WslConfigSetting, expectedValue, actualValue\r\n            std::vector<std::pair<WslConfigSetting, std::vector<std::pair<unsigned long long, unsigned long long>>>> wslConfigSettingUInt64TestPlan{\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::MemorySizeBytes},\r\n                    {\r\n                        {0, maximumMemorySizeBytes / 2},\r\n                        {minimumMemorySizeBytes / 2, minimumMemorySizeBytes},\r\n                        {minimumMemorySizeBytes, minimumMemorySizeBytes},\r\n                        {maximumMemorySizeBytes / 2, maximumMemorySizeBytes / 2},\r\n                        {maximumMemorySizeBytes, maximumMemorySizeBytes},\r\n                        {maximumMemorySizeBytes * 2, maximumMemorySizeBytes},\r\n                    },\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::SwapSizeBytes},\r\n                    fileSizesBytesToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::VhdSizeBytes},\r\n                    fileSizesBytesToTest,\r\n                },\r\n            };\r\n\r\n            auto updateWslConfigSettingWriteOutUInt64Value = [](auto& wslConfigSettingWriteOut, auto& writeValue) {\r\n                wslConfigSettingWriteOut.UInt64Value = writeValue;\r\n            };\r\n\r\n            auto verifyWslConfigSettingReadUInt64ValueEqual = [](auto& wslConfigSettingReadIn, auto& expectedValue) {\r\n                VERIFY_ARE_EQUAL(wslConfigSettingReadIn.UInt64Value, expectedValue);\r\n            };\r\n\r\n            testLoop(wslConfigSettingUInt64TestPlan, updateWslConfigSettingWriteOutUInt64Value, verifyWslConfigSettingReadUInt64ValueEqual);\r\n        }\r\n\r\n        {\r\n            // Enable NetworkingMode::Mirrored for IgnoredPorts to be set correctly upon parsing.\r\n            WslConfigChange config(LxssGenerateTestConfig());\r\n\r\n            // std::pair[0] = Written value, std::pair[1] = Actual/Expected value\r\n            static const std::vector<std::pair<bool, bool>> booleansToTest{{false, false}, {true, true}};\r\n\r\n            // tuple: WslConfigSetting, expectedValue, actualValue\r\n            std::vector<std::pair<WslConfigSetting, std::vector<std::pair<bool, bool>>>> wslConfigSettingBooleanTestPlan{\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::FirewallEnabled},\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::LocalhostForwardingEnabled},\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::HostAddressLoopbackEnabled},\r\n                    // This setting is only enabled when NetworkingMode != Mirrored.\r\n                    {{false, false}, {true, false}},\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::AutoProxyEnabled},\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::DNSProxyEnabled},\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::DNSTunnelingEnabled},\r\n                    // This setting is only enabled when NetworkingMode != Nat && NetworkingMode != Mirrored\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::BestEffortDNSParsingEnabled},\r\n                    // This setting is only enabled when DNSTunnelingEnabled = true\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::GUIApplicationsEnabled},\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::NestedVirtualizationEnabled},\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::SafeModeEnabled},\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::SparseVHDEnabled},\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::DebugConsoleEnabled},\r\n                    booleansToTest,\r\n                },\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::HardwarePerformanceCountersEnabled},\r\n                    // This setting is disabled when SafeModeEnabled = true.\r\n                    // Since testing SafeModeEnabled is tested earlier and left as\r\n                    // true (.wslconfig is re-used), this setting should be false.\r\n                    {{false, false}, {true, false}},\r\n                },\r\n            };\r\n\r\n            auto updateWslConfigSettingWriteOutBooleanValue = [](auto& wslConfigSettingWriteOut, auto& writeValue) {\r\n                wslConfigSettingWriteOut.BoolValue = writeValue;\r\n            };\r\n\r\n            auto verifyWslConfigSettingReadBooleanValueEqual = [](auto& wslConfigSettingReadIn, auto& expectedValue) {\r\n                VERIFY_ARE_EQUAL(wslConfigSettingReadIn.BoolValue, expectedValue);\r\n            };\r\n\r\n            testLoop(wslConfigSettingBooleanTestPlan, updateWslConfigSettingWriteOutBooleanValue, verifyWslConfigSettingReadBooleanValueEqual);\r\n        }\r\n\r\n        {\r\n            // std::pair[0] = Written value, std::pair[1] = Actual/Expected value\r\n            static const std::vector<std::pair<NetworkingConfiguration, NetworkingConfiguration>> networkingConfigurationsToTest{\r\n                {NetworkingConfiguration::None, NetworkingConfiguration::None},\r\n                {NetworkingConfiguration::Nat, NetworkingConfiguration::Nat},\r\n                {NetworkingConfiguration::Bridged, NetworkingConfiguration::Bridged},\r\n                {NetworkingConfiguration::Mirrored, NetworkingConfiguration::Mirrored},\r\n                {NetworkingConfiguration::VirtioProxy, NetworkingConfiguration::VirtioProxy},\r\n            };\r\n\r\n            // tuple: WslConfigSetting, expectedValue, actualValue\r\n            std::vector<std::pair<WslConfigSetting, std::vector<std::pair<NetworkingConfiguration, NetworkingConfiguration>>>> wslConfigSettingNetworkingConfigurationTestPlan{\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::Networking},\r\n                    networkingConfigurationsToTest,\r\n                },\r\n            };\r\n\r\n            auto updateWslConfigSettingWriteOutNetworkingConfigurationValue = [](auto& wslConfigSettingWriteOut, auto& writeValue) {\r\n                wslConfigSettingWriteOut.NetworkingConfigurationValue = writeValue;\r\n            };\r\n\r\n            auto verifyWslConfigSettingReadNetworkingConfigurationValueEqual = [](auto& wslConfigSettingReadIn, auto& expectedValue) {\r\n                VERIFY_ARE_EQUAL(expectedValue, wslConfigSettingReadIn.NetworkingConfigurationValue);\r\n            };\r\n\r\n            testLoop(\r\n                wslConfigSettingNetworkingConfigurationTestPlan,\r\n                updateWslConfigSettingWriteOutNetworkingConfigurationValue,\r\n                verifyWslConfigSettingReadNetworkingConfigurationValueEqual);\r\n        }\r\n\r\n        {\r\n            // std::pair[0] = Written value, std::pair[1] = Actual/Expected value\r\n            static const std::vector<std::pair<MemoryReclaimConfiguration, MemoryReclaimConfiguration>> memoryReclaimModesToTest{\r\n                {MemoryReclaimConfiguration::Disabled, MemoryReclaimConfiguration::Disabled},\r\n                {MemoryReclaimConfiguration::Gradual, MemoryReclaimConfiguration::Gradual},\r\n                {MemoryReclaimConfiguration::DropCache, MemoryReclaimConfiguration::DropCache},\r\n            };\r\n\r\n            // tuple: WslConfigSetting, expectedValue, actualValue\r\n            std::vector<std::pair<WslConfigSetting, std::vector<std::pair<MemoryReclaimConfiguration, MemoryReclaimConfiguration>>>> wslConfigSettingMemoryReclaimModeTestPlan{\r\n                {\r\n                    {.ConfigEntry = WslConfigEntry::AutoMemoryReclaim},\r\n                    memoryReclaimModesToTest,\r\n                },\r\n            };\r\n\r\n            auto updateWslConfigSettingWriteOutMemoryReclaimModeValue = [](auto& wslConfigSettingWriteOut, auto& writeValue) {\r\n                wslConfigSettingWriteOut.MemoryReclaimModeValue = writeValue;\r\n            };\r\n\r\n            auto verifyWslConfigSettingReadMemoryReclaimModeValueEqual = [](auto& wslConfigSettingReadIn, auto& expectedValue) {\r\n                VERIFY_ARE_EQUAL(wslConfigSettingReadIn.MemoryReclaimModeValue, expectedValue);\r\n            };\r\n\r\n            testLoop(wslConfigSettingMemoryReclaimModeTestPlan, updateWslConfigSettingWriteOutMemoryReclaimModeValue, verifyWslConfigSettingReadMemoryReclaimModeValueEqual);\r\n        }\r\n\r\n        {\r\n            std::wstring customWslConfigContentOut{\r\n                LR\"(\r\n[wsl2] # trailing section comment\r\nvmIdleTimeout=200          # property trailing comment\r\nvmIdleTimeout=20000          # property trailing comment\r\nvmIdleTimeout=20000          # property trailing comment\r\nmountDeviceTimeout=120\\\r\n000\r\nkernelBootTimeout=120000\r\n\r\n# property comment\r\nswapfile=E:\\\\wsl-b\\\r\nuild\\\\src\\\\win\\\r\ndows\\\\wslc\\\r\nore\\\\lib\\\\swap.vhdx # multi-line property with trailing comment\r\ntelemetry=false\r\nsafeMode=false\r\nguiApplications=true\r\nearlyBootLogging=false\r\n# comment 1\r\n# comment 2\r\n# \\t \\b\r\nvirtio9p=true # property trailing comment, ensure new property is appended to the section while preserving this comment\r\n\r\n# section comment\r\n[experimental]\r\nautoProxy=false\r\n\r\n[wsl2]\r\n\r\n# end comment\r\n)\"};\r\n\r\n            WslConfigChange config(customWslConfigContentOut);\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            // The config contains multiple vmIdleTimeout entries. The first one should be updated/written.\r\n            wslConfigSettingWriteOut = WslConfigSetting{};\r\n            wslConfigSettingWriteOut.ConfigEntry = WslConfigEntry::VMIdleTimeout;\r\n            wslConfigSettingWriteOut.Int32Value = 1234;\r\n\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigSettingWriteOut), ERROR_SUCCESS);\r\n\r\n            // Replace the swapfile path, which is a multi-line property with a trailing comment.\r\n            // The multi-line value should be replaced with the new value and trailing comment preserved.\r\n            wslConfigSettingWriteOut.ConfigEntry = WslConfigEntry::SwapFilePath;\r\n            wslConfigSettingWriteOut.StringValue = LR\"(C:\\DoesNotExist\\swap.vhdx)\";\r\n\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigSettingWriteOut), ERROR_SUCCESS);\r\n\r\n            // Write out a new setting that doesn't exist in the original config but its section\r\n            // does. The new setting should be appended to that section. There are two cases here::\r\n            wslConfigSettingWriteOut.ConfigEntry = WslConfigEntry::HardwarePerformanceCountersEnabled;\r\n            wslConfigSettingWriteOut.BoolValue = true;\r\n\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigSettingWriteOut), ERROR_SUCCESS);\r\n\r\n            wslConfigSettingWriteOut.ConfigEntry = WslConfigEntry::AutoMemoryReclaim;\r\n            wslConfigSettingWriteOut.MemoryReclaimModeValue = MemoryReclaimConfiguration::Gradual;\r\n\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigSettingWriteOut), ERROR_SUCCESS);\r\n\r\n            std::wstring customWslConfigContentExpected{\r\n                LR\"(\r\n[wsl2] # trailing section comment\r\nvmIdleTimeout=1234          # property trailing comment\r\nvmIdleTimeout=20000          # property trailing comment\r\nvmIdleTimeout=20000          # property trailing comment\r\nmountDeviceTimeout=120\\\r\n000\r\nkernelBootTimeout=120000\r\n\r\n# property comment\r\nswapfile=C:\\\\DoesNotExist\\\\swap.vhdx # multi-line property with trailing comment\r\ntelemetry=false\r\nsafeMode=false\r\nguiApplications=true\r\nearlyBootLogging=false\r\n# comment 1\r\n# comment 2\r\n# \\t \\b\r\nvirtio9p=true # property trailing comment, ensure new property is appended to the section while preserving this comment\r\n\r\n# section comment\r\n[experimental]\r\nautoProxy=false\r\nautoMemoryReclaim=Gradual\r\n\r\n[wsl2]\r\n\r\n# end comment\r\n)\"};\r\n\r\n            std::wifstream configRead(apiWslConfigFilePath);\r\n            auto customWslConfigContentActual = std::wstring{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n            configRead.close();\r\n            VERIFY_ARE_EQUAL(customWslConfigContentExpected, customWslConfigContentActual);\r\n        }\r\n\r\n        {\r\n            // This test contains an invalid line ('babyshark') in the wsl2 section.\r\n            // The line should be preserved and no additional spacing/lines should be added.\r\n            std::wstring customWslConfigContentOut{\r\n                LR\"(\r\n[wsl2]\r\nmemory=32G\r\nprocessors=12\r\nhostAddressLoopback=false\r\ndnsTunneling=true\r\ndefaultVhdSize=1099511627776\r\nbabyshark\r\nlocalhostForwarding=true\r\nautoProxy=false\r\n)\"};\r\n\r\n            WslConfigChange config(customWslConfigContentOut);\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            auto wslConfigSetting = getWslConfigSetting(wslConfig, WslConfigEntry::AutoProxyEnabled);\r\n            const auto autoProxyEnabled = false;\r\n            VERIFY_ARE_EQUAL(wslConfigSetting.BoolValue, autoProxyEnabled);\r\n\r\n            wslConfigSetting.BoolValue = !autoProxyEnabled;\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigSetting), ERROR_SUCCESS);\r\n\r\n            std::wstring customWslConfigContentExpected{\r\n                LR\"(\r\n[wsl2]\r\nmemory=32G\r\nprocessors=12\r\nhostAddressLoopback=false\r\ndnsTunneling=true\r\ndefaultVhdSize=1099511627776\r\nbabyshark\r\nlocalhostForwarding=true\r\n)\"};\r\n\r\n            std::wifstream configRead(apiWslConfigFilePath);\r\n            auto customWslConfigContentActual = std::wstring{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n            configRead.close();\r\n            VERIFY_ARE_EQUAL(customWslConfigContentActual, customWslConfigContentExpected);\r\n        }\r\n\r\n        {\r\n            // This test verifies removal of a setting from the .wslconfig when a default value for the particular setting is\r\n            // set. This gives wsl control over the default value.\r\n            std::wstring customWslConfigContentOut{\r\n                LR\"(\r\n[wsl2]\r\nmemory=32G\r\nprocessors=12 # property trailing comment\r\nhostAddressLoopback=false\r\ndnsTunneling=true\r\ndefaultVhdSize=1099511627776\r\nlocalhostForwarding=true\r\nautoProxy=false\r\n)\"};\r\n\r\n            WslConfigChange config(customWslConfigContentOut);\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            wslConfigDefaults = createWslConfig(nullptr);\r\n            VERIFY_IS_NOT_NULL(wslConfigDefaults);\r\n            auto cleanupWslConfigDefaults = wil::scope_exit([&] { freeWslConfig(wslConfigDefaults); });\r\n\r\n            // This setting should be removed from the .wslconfig file.\r\n            auto wslConfigDefaultSettingMemorySize = getWslConfigSetting(wslConfigDefaults, WslConfigEntry::MemorySizeBytes);\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigDefaultSettingMemorySize), ERROR_SUCCESS);\r\n\r\n            // This setting should be removed from the .wslconfig file but trailing comment preserved.\r\n            auto wslConfigDefaultSettingProcessorCount = getWslConfigSetting(wslConfigDefaults, WslConfigEntry::ProcessorCount);\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigDefaultSettingProcessorCount), ERROR_SUCCESS);\r\n\r\n            // This setting should be preserved with an updated value in the .wslconfig file.\r\n            auto wslConfigDefaultSettingVhdSize = getWslConfigSetting(wslConfigDefaults, WslConfigEntry::VhdSizeBytes);\r\n            wslConfigDefaultSettingVhdSize.UInt64Value -= 1;\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigDefaultSettingVhdSize), ERROR_SUCCESS);\r\n\r\n            // This setting should be removed from the .wslconfig file.\r\n            auto wslConfigDefaultSettingAutoProxy = getWslConfigSetting(wslConfigDefaults, WslConfigEntry::AutoProxyEnabled);\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigDefaultSettingAutoProxy), ERROR_SUCCESS);\r\n\r\n            // This setting should not be written to the .wslconfig file.\r\n            auto wslConfigDefaultSettingGuiApplications = getWslConfigSetting(wslConfigDefaults, WslConfigEntry::GUIApplicationsEnabled);\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, wslConfigDefaultSettingGuiApplications), ERROR_SUCCESS);\r\n\r\n            std::wstring customWslConfigContentExpected{\r\n                LR\"(\r\n[wsl2]\r\n# property trailing comment\r\nhostAddressLoopback=false\r\ndnsTunneling=true\r\ndefaultVhdSize=1099511627775\r\nlocalhostForwarding=true\r\n)\"};\r\n\r\n            std::wifstream configRead(apiWslConfigFilePath);\r\n            auto customWslConfigContentActual = std::wstring{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n            configRead.close();\r\n            VERIFY_ARE_EQUAL(customWslConfigContentActual, customWslConfigContentExpected);\r\n        }\r\n\r\n        // Regression test for GitHub issue #12671:\r\n        // Ensure that section headers always appear BEFORE their key-value pairs.\r\n        // Bug: WSL Settings GUI was writing keys before the section header, causing \"Unknown key\" errors.\r\n        {\r\n            std::wstring bugScenarioConfig =\r\n                LR\"([wsl2]\r\n[experimental]\r\n[wsl2]\r\n)\";\r\n            WslConfigChange config{bugScenarioConfig.c_str()};\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            // Write memory setting - this should NOT appear before the first [wsl2]\r\n            WslConfigSetting memorySetting{};\r\n            memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;\r\n            memorySetting.UInt64Value = 17825792000ULL; // Value from bug report\r\n\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);\r\n\r\n            // Read and verify\r\n            std::wifstream configRead(apiWslConfigFilePath);\r\n            std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n            configRead.close();\r\n\r\n            // Find FIRST occurrence of [wsl2] and memory=\r\n            auto firstWsl2Pos = fileContent.find(L\"[wsl2]\");\r\n            auto memoryPos = fileContent.find(L\"memory=\");\r\n\r\n            VERIFY_ARE_NOT_EQUAL(firstWsl2Pos, std::wstring::npos);\r\n            VERIFY_ARE_NOT_EQUAL(memoryPos, std::wstring::npos);\r\n\r\n            // The critical assertion: memory= must NOT appear before [wsl2]\r\n            VERIFY_IS_TRUE(firstWsl2Pos < memoryPos);\r\n\r\n            // Additional check: memory should appear after the first [wsl2], not after line 1\r\n            auto firstLineEnd = fileContent.find(L'\\n');\r\n            VERIFY_IS_TRUE(memoryPos > firstLineEnd);\r\n        }\r\n\r\n        // Test: Empty file - should create proper [wsl2] section structure\r\n        {\r\n            std::wofstream emptyConfig(apiWslConfigFilePath, std::ios::trunc);\r\n            emptyConfig.close();\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            WslConfigSetting memorySetting{};\r\n            memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;\r\n            memorySetting.UInt64Value = 4294967296ULL; // 4GB\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);\r\n\r\n            std::wifstream configRead(apiWslConfigFilePath);\r\n            std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n            configRead.close();\r\n\r\n            // Should create [wsl2] section and add memory key\r\n            VERIFY_IS_TRUE(fileContent.find(L\"[wsl2]\") != std::wstring::npos);\r\n            VERIFY_IS_TRUE(fileContent.find(L\"memory=\") != std::wstring::npos);\r\n            // Verify [wsl2] comes before memory=\r\n            VERIFY_IS_TRUE(fileContent.find(L\"[wsl2]\") < fileContent.find(L\"memory=\"));\r\n        }\r\n\r\n        // Test: Multiple same-section instances - should update first occurrence\r\n        {\r\n            std::wofstream configFile(apiWslConfigFilePath, std::ios::trunc);\r\n            configFile << L\"[wsl2]\\n\";\r\n            configFile << L\"processors=4\\n\";\r\n            configFile << L\"\\n\";\r\n            configFile << L\"[experimental]\\n\";\r\n            configFile << L\"autoProxy=true\\n\";\r\n            configFile << L\"\\n\";\r\n            configFile << L\"[wsl2]\\n\"; // Second [wsl2] section\r\n            configFile << L\"swap=0\\n\";\r\n            configFile.close();\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            WslConfigSetting memorySetting{};\r\n            memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;\r\n            memorySetting.UInt64Value = 8589934592ULL; // 8GB\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);\r\n\r\n            std::wifstream configRead(apiWslConfigFilePath);\r\n            std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n            configRead.close();\r\n\r\n            // Find first and second [wsl2]\r\n            auto firstWsl2 = fileContent.find(L\"[wsl2]\");\r\n            auto secondWsl2 = fileContent.find(L\"[wsl2]\", firstWsl2 + 1);\r\n            auto memoryPos = fileContent.find(L\"memory=\");\r\n\r\n            VERIFY_ARE_NOT_EQUAL(firstWsl2, std::wstring::npos);\r\n            VERIFY_ARE_NOT_EQUAL(secondWsl2, std::wstring::npos);\r\n            VERIFY_ARE_NOT_EQUAL(memoryPos, std::wstring::npos);\r\n\r\n            // Memory should be added to FIRST [wsl2] section, not second\r\n            VERIFY_IS_TRUE(memoryPos > firstWsl2);\r\n            VERIFY_IS_TRUE(memoryPos < secondWsl2);\r\n        }\r\n\r\n        // Test: EOF without trailing newline\r\n        {\r\n            std::wofstream configFile(apiWslConfigFilePath, std::ios::trunc);\r\n            configFile << L\"[wsl2]\\n\";\r\n            configFile << L\"processors=2\"; // No trailing newline\r\n            configFile.close();\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            WslConfigSetting memorySetting{};\r\n            memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;\r\n            memorySetting.UInt64Value = 3221225472ULL; // 3GB\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);\r\n\r\n            std::wifstream configRead(apiWslConfigFilePath);\r\n            std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n            configRead.close();\r\n\r\n            // Should properly append memory key even without trailing newline on last line\r\n            VERIFY_IS_TRUE(fileContent.find(L\"processors=2\") != std::wstring::npos);\r\n            VERIFY_IS_TRUE(fileContent.find(L\"memory=\") != std::wstring::npos);\r\n\r\n            // Verify both keys are in the same section\r\n            auto wsl2Pos = fileContent.find(L\"[wsl2]\");\r\n            auto processorsPos = fileContent.find(L\"processors=2\");\r\n            auto memoryPos = fileContent.find(L\"memory=\");\r\n            VERIFY_IS_TRUE(wsl2Pos < processorsPos);\r\n            VERIFY_IS_TRUE(wsl2Pos < memoryPos);\r\n\r\n            // Memory should come after processors in the same section\r\n            VERIFY_IS_TRUE(processorsPos < memoryPos);\r\n        }\r\n\r\n        // Test: Empty section followed by another section\r\n        {\r\n            std::wofstream configFile(apiWslConfigFilePath, std::ios::trunc);\r\n            configFile << L\"[wsl2]\\n\";\r\n            configFile << L\"[experimental]\\n\";\r\n            configFile << L\"autoProxy=true\\n\";\r\n            configFile.close();\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            WslConfigSetting memorySetting{};\r\n            memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;\r\n            memorySetting.UInt64Value = 5368709120ULL; // 5GB\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);\r\n\r\n            std::wifstream configRead(apiWslConfigFilePath);\r\n            std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n            configRead.close();\r\n\r\n            // Should insert memory into empty [wsl2] section before [experimental]\r\n            auto wsl2Pos = fileContent.find(L\"[wsl2]\");\r\n            auto memoryPos = fileContent.find(L\"memory=\");\r\n            auto experimentalPos = fileContent.find(L\"[experimental]\");\r\n\r\n            VERIFY_ARE_NOT_EQUAL(wsl2Pos, std::wstring::npos);\r\n            VERIFY_ARE_NOT_EQUAL(memoryPos, std::wstring::npos);\r\n            VERIFY_ARE_NOT_EQUAL(experimentalPos, std::wstring::npos);\r\n\r\n            // Order should be: [wsl2], memory=, [experimental]\r\n            VERIFY_IS_TRUE(wsl2Pos < memoryPos);\r\n            VERIFY_IS_TRUE(memoryPos < experimentalPos);\r\n        }\r\n\r\n        // Test: Section header at EOF with no content\r\n        {\r\n            std::wofstream configFile(apiWslConfigFilePath, std::ios::trunc);\r\n            configFile << L\"[wsl2]\"; // Section at EOF, no newline, no content\r\n            configFile.close();\r\n\r\n            wslConfig = createWslConfig(apiWslConfigFilePath);\r\n            VERIFY_IS_NOT_NULL(wslConfig);\r\n            auto cleanupWslConfig = wil::scope_exit([&] { freeWslConfig(wslConfig); });\r\n\r\n            WslConfigSetting memorySetting{};\r\n            memorySetting.ConfigEntry = WslConfigEntry::MemorySizeBytes;\r\n            memorySetting.UInt64Value = 6442450944ULL; // 6GB\r\n            VERIFY_ARE_EQUAL(setWslConfigSetting(wslConfig, memorySetting), ERROR_SUCCESS);\r\n\r\n            std::wifstream configRead(apiWslConfigFilePath);\r\n            std::wstring fileContent{std::istreambuf_iterator<wchar_t>(configRead), {}};\r\n            configRead.close();\r\n\r\n            // Should properly add key to section at EOF\r\n            VERIFY_IS_TRUE(fileContent.find(L\"[wsl2]\") != std::wstring::npos);\r\n            VERIFY_IS_TRUE(fileContent.find(L\"memory=\") != std::wstring::npos);\r\n            VERIFY_IS_TRUE(fileContent.find(L\"[wsl2]\") < fileContent.find(L\"memory=\"));\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(LaunchWslSettingsFromProtocol)\r\n    {\r\n        WSL_SETTINGS_TEST();\r\n\r\n        SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);\r\n\r\n        SHELLEXECUTEINFOW execInfo{};\r\n        execInfo.cbSize = sizeof(execInfo);\r\n        execInfo.fMask = SEE_MASK_CLASSNAME | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;\r\n        execInfo.lpClass = L\"wsl-settings\";\r\n        execInfo.lpFile = L\"wsl-settings://\";\r\n        execInfo.nShow = SW_HIDE;\r\n\r\n        VERIFY_WIN32_BOOL_SUCCEEDED(ShellExecuteExW(&execInfo));\r\n        const wil::unique_process_handle process{execInfo.hProcess};\r\n        VERIFY_IS_NOT_NULL(process.get());\r\n\r\n        auto killProcess = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&process]() {\r\n            if (process)\r\n            {\r\n                LOG_IF_WIN32_BOOL_FALSE(TerminateProcess(process.get(), 0));\r\n            }\r\n        });\r\n\r\n        const auto moduleFileName = wil::GetModuleFileNameExW<std::wstring>(process.get(), nullptr);\r\n        const auto findExeName = moduleFileName.find(L\"wslsettings.exe\");\r\n        VERIFY_ARE_NOT_EQUAL(findExeName, std::wstring::npos);\r\n    }\r\n\r\n    TEST_METHOD(ManageDefaultUid)\r\n    {\r\n        const auto distroKey = OpenDistributionKey(LXSS_DISTRO_NAME_TEST_L);\r\n\r\n        auto assertDefaultUid = [&](ULONG ExpectedUid) {\r\n            const auto uid = wsl::windows::common::registry::ReadDword(distroKey.get(), nullptr, L\"DefaultUid\", 0);\r\n\r\n            VERIFY_ARE_EQUAL(ExpectedUid, uid);\r\n\r\n            auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"id -u\");\r\n            while (!out.empty() && (out.back() == '\\n' || out.back() == '\\r'))\r\n            {\r\n                out.pop_back();\r\n            }\r\n\r\n            VERIFY_ARE_EQUAL(out, std::to_wstring(ExpectedUid));\r\n        };\r\n\r\n        assertDefaultUid(0);\r\n\r\n        auto validateUidChange =\r\n            [&](const std::wstring& User, ULONG expectedDefaultUid, LPCWSTR ExpectedOutput, const std::wstring& ExpectedError, int ExpectedExitCode) {\r\n                auto [out, err] = LxsstuLaunchWslAndCaptureOutput(\r\n                    std::format(L\"--manage {} --set-default-user {}\", LXSS_DISTRO_NAME_TEST_L, User), ExpectedExitCode);\r\n\r\n                VERIFY_ARE_EQUAL(out, ExpectedOutput);\r\n                VERIFY_ARE_EQUAL(err, ExpectedError);\r\n\r\n                assertDefaultUid(expectedDefaultUid);\r\n            };\r\n\r\n        validateUidChange(L\"root\", 0, L\"The operation completed successfully. \\r\\n\", L\"\", 0);\r\n\r\n        constexpr auto TestUser = L\"testuser\";\r\n\r\n        auto cleanup = wil::scope_exit_log(\r\n            WI_DIAGNOSTICS_INFO, [TestUser]() { LxsstuLaunchWsl(std::format(L\"-u root userdel -f {}\", TestUser)); });\r\n\r\n        ULONG Uid{};\r\n        ULONG Gid{};\r\n        CreateUser(TestUser, &Uid, &Gid);\r\n        VERIFY_ARE_NOT_EQUAL(Uid, 0);\r\n\r\n        validateUidChange(L\"testuser\", Uid, L\"The operation completed successfully. \\r\\n\", L\"\", 0);\r\n        validateUidChange(L\"root\", 0, L\"The operation completed successfully. \\r\\n\", L\"\", 0);\r\n\r\n        const std::wstring invalidUser = L\"Nonexistent\";\r\n        validateUidChange(invalidUser, 0, L\"\", L\"/usr/bin/id: \\u2018\" + invalidUser + L\"\\u2019: no such user\\n\", 1);\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"--manage nonexistent --set-default-user root\", -1);\r\n\r\n        VERIFY_ARE_EQUAL(\r\n            out, L\"There is no distribution with the supplied name.\\r\\nError code: Wsl/Service/WSL_E_DISTRO_NOT_FOUND\\r\\n\");\r\n    }\r\n\r\n    TEST_METHOD(PostDistroRegistrationSettingsOOBE)\r\n    {\r\n        WSL_SETTINGS_TEST();\r\n\r\n        wsl::windows::common::SvcComm service;\r\n        const auto distros = service.EnumerateDistributions();\r\n        if (distros.size() != 1)\r\n        {\r\n            LogSkipped(\"Test distro as the only distro is required to run this test.\");\r\n            return;\r\n        }\r\n\r\n        const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n        // Test setup should set OOBEComplete\r\n        VERIFY_ARE_EQUAL(bool(wsl::windows::common::registry::ReadDword(lxssKey.get(), nullptr, LXSS_OOBE_COMPLETE_NAME, false)), true);\r\n\r\n        // Delete the OOBEComplete reg value to simulate OOBE not being complete\r\n        wsl::windows::common::registry::DeleteValue(lxssKey.get(), LXSS_OOBE_COMPLETE_NAME);\r\n\r\n        // Restore the OOBEComplete reg value in case of failure\r\n        auto restoreOOBEComplete = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            wsl::windows::common::registry::WriteDword(lxssKey.get(), nullptr, LXSS_OOBE_COMPLETE_NAME, true);\r\n        });\r\n\r\n        constexpr auto wslSettingsWindowName = L\"Welcome to Windows Subsystem for Linux\";\r\n        VERIFY_ARE_EQUAL(FindWindowEx(nullptr, nullptr, nullptr, wslSettingsWindowName), nullptr);\r\n\r\n        auto testDistro = distros.front();\r\n        VERIFY_IS_TRUE(wsl::shared::string::IsEqual(testDistro.DistroName, LXSS_DISTRO_NAME_TEST_L, false));\r\n        // Get the original BasePath in order to restore the test distro as before.\r\n        auto guidStringWithBraces = wsl::shared::string::GuidToString<wchar_t>(testDistro.DistroGuid);\r\n        auto testDistroBasePath =\r\n            wsl::windows::common::registry::ReadString(lxssKey.get(), guidStringWithBraces.c_str(), L\"BasePath\", L\"\");\r\n        VERIFY_ARE_NOT_EQUAL(testDistroBasePath, L\"\");\r\n\r\n        if (LxsstuVmMode())\r\n        {\r\n            const auto testDistroVhdPath = std::filesystem::path(testDistroBasePath) / LXSS_VM_MODE_VHD_NAME;\r\n            VERIFY_IS_TRUE(std::filesystem::exists(testDistroVhdPath));\r\n            const auto testDistroVhdPathExported = std::filesystem::path(testDistroBasePath) / L\"exported.vhdx\";\r\n\r\n            WslShutdown();\r\n            VERIFY_ARE_EQUAL(\r\n                LxsstuLaunchWsl(std::format(L\"--export {} \\\"{}\\\" --vhd\", testDistro.DistroName, testDistroVhdPathExported.c_str())), 0u);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--unregister {}\", testDistro.DistroName)), 0u);\r\n            VERIFY_IS_FALSE(std::filesystem::exists(testDistroVhdPath));\r\n            VERIFY_IS_TRUE(service.EnumerateDistributions().empty());\r\n\r\n            std::error_code ec{};\r\n            std::filesystem::rename(testDistroVhdPathExported, testDistroVhdPath, ec);\r\n\r\n            VERIFY_ARE_EQUAL(\r\n                LxsstuLaunchWsl(std::format(L\"--import-in-place {} \\\"{}\\\"\", testDistro.DistroName, testDistroVhdPath.c_str())), 0L);\r\n        }\r\n        else\r\n        {\r\n            const auto testDistroRootfsPath = std::filesystem::path(testDistroBasePath) / LXSS_ROOTFS_DIRECTORY;\r\n            VERIFY_IS_TRUE(std::filesystem::exists(testDistroRootfsPath));\r\n            const auto testDistroExported = std::filesystem::path(testDistroBasePath) / L\"exported.tar\";\r\n            auto deleteTar = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { DeleteFile(testDistroExported.c_str()); });\r\n\r\n            WslShutdown();\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--export {} \\\"{}\\\"\", testDistro.DistroName, testDistroExported.c_str())), 0u);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--unregister {}\", testDistro.DistroName)), 0u);\r\n            VERIFY_IS_FALSE(std::filesystem::exists(testDistroRootfsPath));\r\n            VERIFY_IS_TRUE(service.EnumerateDistributions().empty());\r\n            VERIFY_ARE_EQUAL(\r\n                LxsstuLaunchWsl(std::format(\r\n                    L\"--import {} \\\"{}\\\" \\\"{}\\\" --version 1\", testDistro.DistroName, testDistroBasePath, testDistroExported.c_str())),\r\n                0L);\r\n        }\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--set-default {}\", testDistro.DistroName)), 0);\r\n\r\n        VERIFY_ARE_EQUAL(service.EnumerateDistributions().size(), 1);\r\n        HWND wslSettingsWindow{};\r\n        const auto findWslSettingsWindowAttempts = 60;\r\n        for (auto attempt = 0; attempt < findWslSettingsWindowAttempts; ++attempt)\r\n        {\r\n            wslSettingsWindow = FindWindowEx(nullptr, nullptr, nullptr, wslSettingsWindowName);\r\n            if (wslSettingsWindow)\r\n            {\r\n                break;\r\n            }\r\n\r\n            Sleep(500);\r\n        }\r\n\r\n        VERIFY_ARE_NOT_EQUAL(wslSettingsWindow, nullptr);\r\n        SendMessage(wslSettingsWindow, WM_CLOSE, 0, 0);\r\n        VERIFY_ARE_EQUAL(bool(wsl::windows::common::registry::ReadDword(lxssKey.get(), nullptr, LXSS_OOBE_COMPLETE_NAME, false)), true);\r\n    }\r\n\r\n    TEST_METHOD(VersionFlavorParsing)\r\n    {\r\n        DWORD currentVersion = LxsstuVmMode() ? 2 : 1;\r\n        DWORD convertVersion = LxsstuVmMode() ? 1 : 2;\r\n\r\n        const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n\r\n        auto validateFlavorVersion = [&](LPCWSTR Distro, LPCWSTR ExpectedFlavor, LPCWSTR ExpectedVersion) {\r\n            const auto testDistroId = GetDistributionId(Distro);\r\n            VERIFY_IS_TRUE(testDistroId.has_value());\r\n\r\n            const auto distroId = wsl::shared::string::GuidToString<wchar_t>(testDistroId.value());\r\n\r\n            TerminateDistribution(Distro);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"-d {} cat /etc/os-release || true\", Distro).c_str()), 0L);\r\n\r\n            const auto flavor = wsl::windows::common::registry::ReadString(lxssKey.get(), distroId.c_str(), L\"Flavor\", L\"\");\r\n            const auto version = wsl::windows::common::registry::ReadString(lxssKey.get(), distroId.c_str(), L\"OsVersion\", L\"\");\r\n\r\n            VERIFY_ARE_EQUAL(ExpectedFlavor, flavor);\r\n            VERIFY_ARE_EQUAL(ExpectedVersion, version);\r\n        };\r\n\r\n        validateFlavorVersion(LXSS_DISTRO_NAME_TEST_L, L\"debian\", L\"12\");\r\n\r\n        constexpr auto testTar = L\"exported-distro.tar\";\r\n        constexpr auto tmpDistroName = L\"tmpdistro\";\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [tmpDistroName]() {\r\n            DeleteFile(testTar);\r\n            LxsstuLaunchWsl(std::format(L\"--unregister {}\", tmpDistroName));\r\n        });\r\n\r\n        DistroFileChange osRelease(L\"/etc/os-release\");\r\n\r\n        {\r\n            osRelease.SetContent(\r\n                LR\"(\r\nID=Distro\r\nVERSION_ID=Version\r\n            )\");\r\n\r\n            validateFlavorVersion(LXSS_DISTRO_NAME_TEST_L, L\"Distro\", L\"Version\");\r\n        }\r\n\r\n        {\r\n            osRelease.SetContent(\r\n                LR\"(\r\nDISTRO_I=Wrong\r\nID=\"DistroWithQuotes\"\r\nVERSION_ID=\"VersionWithQuotes\"\r\nSomething else\r\n            )\");\r\n\r\n            validateFlavorVersion(LXSS_DISTRO_NAME_TEST_L, L\"DistroWithQuotes\", L\"VersionWithQuotes\");\r\n        }\r\n\r\n        {\r\n            osRelease.SetContent(\r\n                LR\"(\r\nID=\"InvalidFormat!\"\r\nVERSION_ID=\"ValidFormat\"\r\n            )\");\r\n\r\n            validateFlavorVersion(LXSS_DISTRO_NAME_TEST_L, L\"DistroWithQuotes\", L\"ValidFormat\");\r\n        }\r\n\r\n        {\r\n            osRelease.SetContent(\r\n                LR\"(\r\nID=\"Distro-_.,\"\r\nVERSION_ID=\"ValidFormat\"\r\n            )\");\r\n\r\n            validateFlavorVersion(LXSS_DISTRO_NAME_TEST_L, L\"Distro-_.,\", L\"ValidFormat\");\r\n        }\r\n\r\n        {\r\n            osRelease.SetContent(\r\n                LR\"(\r\nID=\"Invalid|Format\"\r\nVERSION_ID=\"Invalid|Format\"\r\n            )\");\r\n\r\n            validateFlavorVersion(LXSS_DISTRO_NAME_TEST_L, L\"Distro-_.,\", L\"ValidFormat\");\r\n        }\r\n\r\n        {\r\n            osRelease.Delete(); // Nothing should happen if the file is deleted, but the distro should still work.\r\n            validateFlavorVersion(LXSS_DISTRO_NAME_TEST_L, L\"Distro-_.,\", L\"ValidFormat\");\r\n        }\r\n\r\n        // Validate that importing a distro without os-release works.\r\n        {\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--export {} {}\", LXSS_DISTRO_NAME_TEST_L, testTar).c_str()), 0L);\r\n            VERIFY_ARE_EQUAL(\r\n                LxsstuLaunchWsl(std::format(L\"--import {} . {} --version {}\", tmpDistroName, testTar, currentVersion).c_str()), 0L);\r\n\r\n            validateFlavorVersion(tmpDistroName, L\"\", L\"\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"-d {} echo -e 'VERSION_ID=v' > /etc/os-release\", tmpDistroName).c_str()), 0L);\r\n            validateFlavorVersion(tmpDistroName, L\"\", L\"v\");\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--unregister {}\", tmpDistroName).c_str()), 0L);\r\n        }\r\n\r\n        // Validate that importing and then converting also behaves correctly when there's no os-release\r\n        {\r\n            VERIFY_ARE_EQUAL(\r\n                LxsstuLaunchWsl(std::format(L\"--import {} . {} --version {}\", tmpDistroName, testTar, convertVersion).c_str()), 0L);\r\n            validateFlavorVersion(tmpDistroName, L\"\", L\"\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--set-version {} {}\", tmpDistroName, currentVersion).c_str()), 0L);\r\n\r\n            validateFlavorVersion(tmpDistroName, L\"\", L\"\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"-d {} echo -e 'VERSION_ID=v2' > /etc/os-release\", tmpDistroName).c_str()), 0L);\r\n            validateFlavorVersion(tmpDistroName, L\"\", L\"v2\");\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--unregister {}\", tmpDistroName).c_str()), 0L);\r\n        }\r\n\r\n        // Verify that importing a distribution with an os-release as then converting works as well\r\n        VERIFY_ARE_EQUAL(\r\n            LxsstuLaunchWsl(std::format(L\"--import {} . {} --version {}\", tmpDistroName, g_testDistroPath, convertVersion).c_str()), 0L);\r\n        validateFlavorVersion(tmpDistroName, L\"debian\", L\"12\");\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--set-version {} {}\", tmpDistroName, currentVersion).c_str()), 0L);\r\n        validateFlavorVersion(tmpDistroName, L\"debian\", L\"12\");\r\n    }\r\n\r\n    TEST_METHOD(DistributionId)\r\n    {\r\n        using namespace wsl::windows::common::string;\r\n        const auto testDistroId = GetDistributionId(LXSS_DISTRO_NAME_TEST_L);\r\n        VERIFY_IS_TRUE(testDistroId.has_value());\r\n\r\n        auto validateOutput = [](const std::wstring& Cmd, LPCWSTR ExpectedOutput, int ExitCode = 0) {\r\n            auto [out, _] = LxsstuLaunchWslAndCaptureOutput(Cmd, ExitCode);\r\n\r\n            VERIFY_ARE_EQUAL(out, ExpectedOutput);\r\n        };\r\n\r\n        validateOutput(\r\n            std::format(\r\n                L\"--distribution-id {} echo -n OK\",\r\n                wsl::shared::string::GuidToString<wchar_t>(testDistroId.value(), wsl::shared::string::GuidToStringFlags::None)),\r\n            L\"OK\");\r\n\r\n        validateOutput(\r\n            std::format(\r\n                L\"--distribution-id {} echo -n OK\",\r\n                wsl::shared::string::GuidToString<wchar_t>(testDistroId.value(), wsl::shared::string::GuidToStringFlags::AddBraces)),\r\n            L\"OK\");\r\n\r\n        validateOutput(\r\n            std::format(\r\n                L\"--distribution-id {} echo -n OK\",\r\n                wsl::shared::string::GuidToString<wchar_t>(testDistroId.value(), wsl::shared::string::GuidToStringFlags::Uppercase)),\r\n            L\"OK\");\r\n\r\n        validateOutput(L\"--distribution-id InvalidGuid\", L\"The parameter is incorrect. \\r\\nError code: Wsl/E_INVALIDARG\\r\\n\", -1);\r\n        validateOutput(\r\n            L\"--distribution-id  {C13B2B63-F9D5-4840-8105-F6ABECCF46CA}\",\r\n            L\"There is no distribution with the supplied name.\\r\\nError code: \"\r\n            L\"Wsl/Service/CreateInstance/ReadDistroConfig/WSL_E_DISTRO_NOT_FOUND\\r\\n\",\r\n            -1);\r\n    }\r\n\r\n    TEST_METHOD(ModernOOBE)\r\n    {\r\n        const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n        const auto testDistroId = GetDistributionId(LXSS_DISTRO_NAME_TEST_L);\r\n        VERIFY_IS_TRUE(testDistroId.has_value());\r\n        const auto testDistroIdString = wsl::shared::string::GuidToString<wchar_t>(testDistroId.value());\r\n\r\n        DistroFileChange distributionconf(L\"/etc/wsl-distribution.conf\", false);\r\n        distributionconf.SetContent(L\"[oobe]\\ncommand = /bin/bash -c 'echo OOBE'\\n\");\r\n\r\n        RegistryKeyChange<DWORD> runOOBE(lxssKey.get(), testDistroIdString.c_str(), L\"RunOOBE\", 1);\r\n        const RegistryKeyChange<DWORD> defaultUid(lxssKey.get(), testDistroIdString.c_str(), L\"DefaultUid\", 0);\r\n\r\n        auto validateOutput = [](LPCWSTR Cmd, LPCWSTR ExpectedOutput, LPCWSTR ExpectedWarnings = L\"\", DWORD ExpectedExitCode = 0) {\r\n            auto [read, write] = CreateSubprocessPipe(true, false);\r\n            write.reset();\r\n\r\n            wsl::windows::common::SubProcess process(nullptr, LxssGenerateWslCommandLine(Cmd).c_str());\r\n            process.SetStdHandles(read.get(), nullptr, nullptr);\r\n\r\n            const auto output = process.RunAndCaptureOutput();\r\n\r\n            VERIFY_ARE_EQUAL(ExpectedExitCode, output.ExitCode);\r\n\r\n            VERIFY_ARE_EQUAL(ExpectedOutput, output.Stdout);\r\n            VERIFY_ARE_EQUAL(ExpectedWarnings, output.Stderr);\r\n        };\r\n\r\n        {\r\n            TerminateDistribution();\r\n\r\n            // Non-interactive commands shouldn't trigger OOBE\r\n            validateOutput(L\"echo no oobe\", L\"no oobe\\n\");\r\n            VERIFY_ARE_EQUAL(runOOBE.Get(), 1);\r\n\r\n            // Interactive shell should trigger OOBE\r\n            validateOutput(nullptr, L\"OOBE\\n\");\r\n            VERIFY_ARE_EQUAL(runOOBE.Get(), 0);\r\n\r\n            // OOBE should only trigger once\r\n            validateOutput(L\"\", L\"\");\r\n        }\r\n\r\n        {\r\n            runOOBE.Set(1);\r\n            distributionconf.SetContent(L\"[oobe]\\ncommand = /bin/bash -c 'echo failed OOBE && exit 1'\\n\");\r\n\r\n            TerminateDistribution();\r\n\r\n            constexpr auto expectedStdErr = L\"OOBE command \\\"/bin/bash -c 'echo failed OOBE && exit 1'\\\" failed, exiting\\n\";\r\n\r\n            validateOutput(nullptr, L\"failed OOBE\\n\", expectedStdErr, 1);\r\n            VERIFY_ARE_EQUAL(runOOBE.Get(), 1);\r\n\r\n            // Failed OOBE command should be retried\r\n            TerminateDistribution();\r\n            validateOutput(nullptr, L\"failed OOBE\\n\", expectedStdErr, 1);\r\n            VERIFY_ARE_EQUAL(runOOBE.Get(), 1);\r\n        }\r\n\r\n        {\r\n            runOOBE.Set(1);\r\n            distributionconf.SetContent(\r\n                L\"[oobe]\\ncommand = /bin/bash -c 'echo OOBE && useradd -u 1010 -m -s /bin/bash user'\\n defaultUid = 1010\\n\");\r\n\r\n            TerminateDistribution();\r\n\r\n            validateOutput(nullptr, L\"OOBE\\n\");\r\n            VERIFY_ARE_EQUAL(runOOBE.Get(), 0);\r\n\r\n            // Validate that DefaultUid was set\r\n            validateOutput(L\"id -u\", L\"1010\\n\");\r\n            VERIFY_ARE_EQUAL(defaultUid.Get(), 1010);\r\n        }\r\n\r\n        // Verify that the default UID isn't changed if it's not present in wsl-distribution.conf.\r\n        {\r\n            runOOBE.Set(1);\r\n\r\n            distributionconf.SetContent(L\"[oobe]\\ncommand = /bin/bash -c 'echo OOBE'\");\r\n            TerminateDistribution();\r\n\r\n            validateOutput(nullptr, L\"OOBE\\n\");\r\n            VERIFY_ARE_EQUAL(defaultUid.Get(), 1010);\r\n        }\r\n\r\n        // Verify that OOBE doesn't run if a distribution is installed via wsl --import\r\n        {\r\n            constexpr auto testDir = L\"test-oobe-import\";\r\n            constexpr auto testDistroName = L\"test-oobe-import\";\r\n\r\n            std::filesystem::create_directory(testDir);\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [this, testDistroName]() {\r\n                LxsstuLaunchWsl(std::format(L\"--unregister {}\", testDistroName));\r\n                std::error_code error;\r\n                std::filesystem::remove_all(testDir, error);\r\n            });\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--export {} {}/exported.tar\", LXSS_DISTRO_NAME_TEST_L, testDir)), 0L);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--import {} {} {}/exported.tar\", testDistroName, testDir, testDistroName)), 0L);\r\n\r\n            const auto distroKey = OpenDistributionKey(testDistroName);\r\n\r\n            VERIFY_ARE_EQUAL(wsl::windows::common::registry::ReadDword(distroKey.get(), nullptr, L\"RunOOBE\", 1), 0);\r\n            validateOutput(nullptr, L\"\");\r\n        }\r\n\r\n        // Make sure the defaultUid is reset for next test case.\r\n        TerminateDistribution();\r\n    }\r\n\r\n    static void ValidateDistributionStarts(LPCWSTR Name)\r\n    {\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"-d {} echo -n OK\", Name));\r\n        VERIFY_ARE_EQUAL(out, L\"OK\");\r\n    }\r\n\r\n    TEST_METHOD(InstallWithBrokenDefault)\r\n    {\r\n        // This test case validates that a broken 'DefaultDistribution' value doesn't prevent installing new distributions.\r\n\r\n        // Create a broken default\r\n        RegistryKeyChange defaultDistro(\r\n            HKEY_CURRENT_USER,\r\n            L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Lxss\",\r\n            L\"DefaultDistribution\",\r\n            std::wstring{L\"{1DB260CB-912D-432A-B898-518DFD0F374E}\"});\r\n\r\n        // Validate that installing a new distribution succeeds.\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { LxsstuLaunchWsl(L\"--unregister test_new_default\"); });\r\n\r\n        VERIFY_ARE_EQUAL(\r\n            LxsstuLaunchWsl(std::format(L\"--install --from-file \\\"{}\\\" --no-launch --name test_new_default\", g_testDistroPath)), 0L);\r\n\r\n        auto [out, error] = LxsstuLaunchWslAndCaptureOutput(L\"-d test_new_default echo OK\");\r\n        VERIFY_ARE_EQUAL(out, L\"OK\\n\");\r\n        VERIFY_ARE_EQUAL(error, L\"\");\r\n\r\n        // Verify that the default distribution is updated\r\n        const auto key = wsl::windows::common::registry::OpenLxssUserKey();\r\n\r\n        const auto defaultValue = wsl::windows::common::registry::ReadString(key.get(), nullptr, L\"DefaultDistribution\");\r\n\r\n        VERIFY_ARE_EQUAL(GetDistributionId(L\"test_new_default\").value_or(GUID_NULL), wsl::shared::string::ToGuid(defaultValue));\r\n    }\r\n\r\n    TEST_METHOD(ModernInstall)\r\n    {\r\n        using namespace wsl::windows::common::wslutil;\r\n        using namespace wsl::windows::common::string;\r\n        constexpr auto IconPath = L\"test-icon.ico\";\r\n\r\n        auto CreateTarFromManifest = [](LPCWSTR Manifest, LPCWSTR TarName) {\r\n            DistroFileChange distributionconf(L\"/etc/wsl-distribution.conf\", false);\r\n            distributionconf.SetContent(Manifest);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--export test_distro {}\", TarName)), 0L);\r\n        };\r\n\r\n        auto InstallFromTar =\r\n            [](LPCWSTR TarName, LPCWSTR ExtraArgs = L\"\", int ExpectedExitCode = 0, LPCWSTR ExpectedOutput = nullptr, LPCWSTR ExpectedWarnings = nullptr) {\r\n                auto [out, err] = LxsstuLaunchWslAndCaptureOutput(\r\n                    std::format(L\"--install --no-launch --from-file {} {}\", TarName, ExtraArgs), ExpectedExitCode);\r\n\r\n                if (ExpectedOutput != nullptr)\r\n                {\r\n                    VERIFY_ARE_EQUAL(ExpectedOutput, out);\r\n                }\r\n\r\n                if (ExpectedWarnings != nullptr)\r\n                {\r\n                    VERIFY_ARE_EQUAL(ExpectedWarnings, err);\r\n                }\r\n            };\r\n\r\n        auto installLocation = wsl::windows::common::wslutil::GetMsiPackagePath();\r\n        VERIFY_IS_TRUE(installLocation.has_value());\r\n\r\n        auto wslExePath = installLocation.value() + L\"wsl.exe\";\r\n\r\n        wil::unique_hmodule wslExe{LoadLibrary(wslExePath.c_str())};\r\n        VERIFY_IS_TRUE(!!wslExe);\r\n\r\n        auto resource = FindResource(wslExe.get(), MAKEINTRESOURCE(1), RT_ICON);\r\n        VERIFY_IS_TRUE(resource != nullptr);\r\n\r\n        auto loadedResource = LoadResource(wslExe.get(), resource);\r\n        const void* iconAddress = LockResource(loadedResource);\r\n\r\n        wil::unique_handle icon{CreateFile(IconPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, nullptr)};\r\n        VERIFY_IS_TRUE(!!icon);\r\n\r\n        DWORD bytes{};\r\n        VERIFY_IS_TRUE(WriteFile(icon.get(), iconAddress, SizeofResource(wslExe.get(), resource), &bytes, nullptr));\r\n        LogInfo(\"Created icon %ls (%lu bytes)\", IconPath, bytes);\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"cp '{}' /icon.ico\", IconPath)), 0L);\r\n\r\n        // Distribution with default name and icon\r\n        {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n                LxsstuLaunchWsl(L\"--unregister test-default-name\");\r\n                DeleteFile(L\"distro-default-name-icon.tar\");\r\n            });\r\n\r\n            CreateTarFromManifest(\r\n                L\"[shortcut]\\nicon = /icon.ico\\n[oobe]\\ndefaultName = test-default-name\", L\"distro-default-name-icon.tar\");\r\n\r\n            //\r\n            // Validate that the distribution icon path is also correct when installing via wsl --import.\r\n            //\r\n\r\n            {\r\n                constexpr auto distroName = L\"TestCustomLocation\";\r\n\r\n                auto currentDirectory = std::filesystem::absolute(std::filesystem::current_path()).wstring();\r\n                for (const auto& location : {currentDirectory, std::wstring(L\".\")})\r\n                {\r\n                    auto cleanup = wil::scope_exit_log(\r\n                        WI_DIAGNOSTICS_INFO, [&]() { LxsstuLaunchWsl(std::format(L\"--unregister {}\", distroName)); });\r\n\r\n                    VERIFY_ARE_EQUAL(\r\n                        LxsstuLaunchWsl(\r\n                            std::format(L\"--import {} \\\"{}\\\" {}\", distroName, location, \"distro-default-name-icon.tar\")),\r\n                        0L);\r\n\r\n                    auto [json, profile_path] = ValidateDistributionTerminalProfile(distroName, false);\r\n                    VERIFY_ARE_EQUAL(\r\n                        json[\"profiles\"][1][\"icon\"].get<std::string>(), (std::filesystem::absolute(\".\") / \"shortcut.ico\").string());\r\n                }\r\n            }\r\n\r\n            InstallFromTar(L\"distro-default-name-icon.tar\");\r\n            ValidateDistributionStarts(L\"test-default-name\");\r\n\r\n            // Validate that the distribution was installed under the right name\r\n            auto distroKey = OpenDistributionKey(L\"test-default-name\");\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n\r\n            ValidateDistributionShortcut(L\"test-default-name\", icon.get());\r\n            auto [json, profile_path] = ValidateDistributionTerminalProfile(L\"test-default-name\", false);\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(profile_path));\r\n            cleanup.reset();\r\n\r\n            // Terminal profile should be removed when the distribution is unregistered.\r\n            VERIFY_IS_FALSE(std::filesystem::exists(profile_path));\r\n\r\n            // Validate that the base path is removed and that the shortcut is gone*\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_FALSE(std::filesystem::exists(basePath));\r\n        }\r\n\r\n        // Distribution with default name and no icon\r\n        {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n                LxsstuLaunchWsl(L\"--unregister test-default-name\");\r\n                DeleteFile(L\"distro-default-name-no-icon.tar\");\r\n            });\r\n\r\n            CreateTarFromManifest(L\"\\n[oobe]\\ndefaultName = test-default-name\", L\"distro-default-name-no-icon.tar\");\r\n            InstallFromTar(L\"distro-default-name-no-icon.tar\");\r\n            ValidateDistributionStarts(L\"test-default-name\");\r\n\r\n            // Validate that the distribution was installed under the right name and icon\r\n            auto distroKey = OpenDistributionKey(L\"test-default-name\");\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n            ValidateDistributionShortcut(L\"test-default-name\", nullptr);\r\n\r\n            cleanup.reset();\r\n\r\n            // Validate that the base path is removed and that the shortcut is gone*\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_FALSE(std::filesystem::exists(basePath));\r\n        }\r\n\r\n        // Distribution with no default name\r\n        {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n                LxsstuLaunchWsl(L\"--unregister test-distro-no-default-name\");\r\n                DeleteFile(L\"distro-no-default-name.tar\");\r\n            });\r\n\r\n            CreateTarFromManifest(L\"\", L\"distro-no-default-name.tar\");\r\n\r\n            // Import should fail without --name\r\n            constexpr auto expectedOutput =\r\n                L\"Installing: distro-no-default-name.tar\\r\\n\\\r\nThis distribution doesn't contain a default name. Use --name to chose the distribution name.\\r\\n\\\r\nError code: Wsl/Service/RegisterDistro/WSL_E_DISTRIBUTION_NAME_NEEDED\\r\\n\";\r\n\r\n            InstallFromTar(L\"distro-no-default-name.tar\", L\"\", -1, expectedOutput);\r\n\r\n            // And succeed with --name\r\n            InstallFromTar(L\"distro-no-default-name.tar\", L\"--name test-distro-no-default-name\");\r\n            ValidateDistributionStarts(L\"test-distro-no-default-name\");\r\n\r\n            auto distroKey = OpenDistributionKey(L\"test-distro-no-default-name\");\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n            ValidateDistributionShortcut(L\"test-distro-no-default-name\", nullptr);\r\n\r\n            cleanup.reset();\r\n\r\n            // Validate that the base path is removed and that the shortcut is gone*\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_FALSE(std::filesystem::exists(basePath));\r\n        }\r\n\r\n        // Distribution specifying a VHD size.\r\n        auto InstallWithVhdSize = [&](bool FixedVhd) {\r\n            constexpr auto distroName = L\"distro-vhd-size\";\r\n            constexpr auto tarFileName = L\"distro-vhd-size.tar\";\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n                LxsstuLaunchWsl(std::format(L\"--unregister {}\", distroName));\r\n                DeleteFile(tarFileName);\r\n            });\r\n\r\n            CreateTarFromManifest(std::format(L\"[shortcut]\\nicon = /icon.ico\\n[oobe]\\ndefaultName = {}\", distroName).c_str(), tarFileName);\r\n\r\n            InstallFromTar(tarFileName, std::format(L\"--vhd-size 1GB {}\", FixedVhd ? L\"--fixed-vhd\" : L\"\").c_str());\r\n            ValidateDistributionStarts(distroName);\r\n\r\n            // Terminate the VM to make sure the VHD is not in use.\r\n            WslShutdown();\r\n\r\n            // Validate that the distribution was installed under the right name\r\n            auto distroKey = OpenDistributionKey(distroName);\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n\r\n            ValidateDistributionShortcut(distroName, icon.get());\r\n            auto [json, profile_path] = ValidateDistributionTerminalProfile(distroName, false);\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(profile_path));\r\n\r\n            // Verify that the is the correct type.\r\n            {\r\n                std::filesystem::path vhdFilePath = std::filesystem::path(basePath) / LXSS_VM_MODE_VHD_NAME;\r\n                VIRTUAL_STORAGE_TYPE storageType{};\r\n                storageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;\r\n                storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN;\r\n                wil::unique_handle disk;\r\n                THROW_IF_WIN32_ERROR(OpenVirtualDisk(\r\n                    &storageType, vhdFilePath.c_str(), VIRTUAL_DISK_ACCESS_GET_INFO, OPEN_VIRTUAL_DISK_FLAG_NONE, nullptr, &disk));\r\n\r\n                GET_VIRTUAL_DISK_INFO diskInfo{};\r\n                diskInfo.Version = GET_VIRTUAL_DISK_INFO_VIRTUAL_STORAGE_TYPE;\r\n                ULONG diskInfoSize = sizeof(diskInfo);\r\n                THROW_IF_WIN32_ERROR(GetVirtualDiskInformation(disk.get(), &diskInfoSize, &diskInfo, nullptr));\r\n\r\n                VERIFY_IS_TRUE(diskInfo.VirtualStorageType.DeviceId == VIRTUAL_STORAGE_TYPE_DEVICE_VHDX);\r\n\r\n                diskInfo.Version = GET_VIRTUAL_DISK_INFO_PROVIDER_SUBTYPE;\r\n                diskInfoSize = sizeof(diskInfo);\r\n                THROW_IF_WIN32_ERROR(GetVirtualDiskInformation(disk.get(), &diskInfoSize, &diskInfo, nullptr));\r\n\r\n                VERIFY_ARE_EQUAL(FixedVhd, diskInfo.ProviderSubtype == 2);\r\n            }\r\n\r\n            // Unregister the distribution.\r\n            cleanup.reset();\r\n\r\n            // Terminal profile should be removed when the distribution is unregistered.\r\n            VERIFY_IS_FALSE(std::filesystem::exists(profile_path));\r\n\r\n            // Validate that the base path is removed and that the shortcut is gone*\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_FALSE(std::filesystem::exists(basePath));\r\n        };\r\n\r\n        InstallWithVhdSize(false);\r\n        InstallWithVhdSize(true);\r\n\r\n        // Distribution imported in place\r\n        if (LxsstuVmMode())\r\n        {\r\n            auto CreateVhdFromManifest = [](LPCWSTR Manifest, LPCWSTR VhdName) {\r\n                DistroFileChange distributionconf(L\"/etc/wsl-distribution.conf\", false);\r\n                distributionconf.SetContent(Manifest);\r\n                WslShutdown();\r\n                VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--export test_distro {} --format vhd\", VhdName)), 0L);\r\n            };\r\n\r\n            auto InstallFromVhd =\r\n                [](LPCWSTR DistroName, LPCWSTR VhdName, int ExpectedExitCode = 0, LPCWSTR ExpectedOutput = nullptr, LPCWSTR ExpectedWarnings = nullptr) {\r\n                    auto [out, err] =\r\n                        LxsstuLaunchWslAndCaptureOutput(std::format(L\"--import-in-place {} {}\", DistroName, VhdName), ExpectedExitCode);\r\n\r\n                    if (ExpectedOutput != nullptr)\r\n                    {\r\n                        VERIFY_ARE_EQUAL(ExpectedOutput, out);\r\n                    }\r\n\r\n                    if (ExpectedWarnings != nullptr)\r\n                    {\r\n                        VERIFY_ARE_EQUAL(ExpectedWarnings, err);\r\n                    }\r\n                };\r\n\r\n            const auto distroName = L\"distro-import-in-place\";\r\n            const auto vhdName = L\"distro-import-in-place.vhdx\";\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n                LxsstuLaunchWsl(std::format(L\"--unregister {}\", distroName).c_str());\r\n                DeleteFileW(vhdName);\r\n            });\r\n\r\n            CreateVhdFromManifest(L\"\", vhdName);\r\n\r\n            InstallFromVhd(distroName, vhdName);\r\n            ValidateDistributionStarts(distroName);\r\n\r\n            // Validate that the distribution was installed under the right name\r\n            auto distroKey = OpenDistributionKey(distroName);\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n            ValidateDistributionShortcut(distroName, nullptr);\r\n            auto [json, profile_path] = ValidateDistributionTerminalProfile(distroName, true);\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(profile_path));\r\n            cleanup.reset();\r\n\r\n            // Terminal profile should be removed when the distribution is unregistered.\r\n            VERIFY_IS_FALSE(std::filesystem::exists(profile_path));\r\n\r\n            // Validate that the shortcut is gone\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n        }\r\n\r\n        // Distribution with overridden default location\r\n        {\r\n            auto cleanup = wil::scope_exit_log(\r\n                WI_DIAGNOSTICS_INFO, []() { LxsstuLaunchWsl(L\"--unregister test-overridden-default-location\"); });\r\n\r\n            auto currentPath = std::filesystem::current_path();\r\n            WslConfigChange wslconfig(std::format(L\"[general]\\ndistributionInstallPath = {}\", EscapePath(currentPath.wstring())));\r\n\r\n            InstallFromTar(g_testDistroPath.c_str(), L\"--name test-overridden-default-location\");\r\n            ValidateDistributionStarts(L\"test-overridden-default-location\");\r\n\r\n            auto distroKey = OpenDistributionKey(L\"test-overridden-default-location\");\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n\r\n            // Validate that the distribution was created in the correct path\r\n            VERIFY_ARE_EQUAL(std::filesystem::path(basePath).parent_path().string(), currentPath.string());\r\n\r\n            ValidateDistributionShortcut(L\"test-overridden-default-location\", nullptr);\r\n\r\n            cleanup.reset();\r\n\r\n            // Validate that the base path is removed and that the shortcut is gone*\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_FALSE(std::filesystem::exists(basePath));\r\n        }\r\n\r\n        // Distribution installed in a custom location\r\n\r\n        {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { LxsstuLaunchWsl(L\"--unregister test-custom-location\"); });\r\n\r\n            InstallFromTar(g_testDistroPath.c_str(), L\"--name test-custom-location --location test-distro-folder\");\r\n            ValidateDistributionStarts(L\"test-custom-location\");\r\n\r\n            // Validate that the distribution was installed under the right name\r\n            auto distroKey = OpenDistributionKey(L\"test-custom-location\");\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n            VERIFY_ARE_EQUAL(std::filesystem::absolute(\"test-distro-folder\").wstring(), basePath);\r\n\r\n            ValidateDistributionShortcut(L\"test-custom-location\", nullptr);\r\n\r\n            cleanup.reset();\r\n\r\n            // Validate that the base path is removed and that the shortcut is gone*\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_FALSE(std::filesystem::exists(basePath));\r\n        }\r\n\r\n        // Distribution installed from stdin\r\n        {\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { LxsstuLaunchWsl(L\"--unregister test-install-stdin\"); });\r\n\r\n            wil::unique_handle importTar{\r\n                CreateFile(g_testDistroPath.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, HANDLE_FLAG_INHERIT, nullptr)};\r\n\r\n            VERIFY_IS_TRUE(!!importTar);\r\n\r\n            VERIFY_IS_TRUE(SetHandleInformation(importTar.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--install --no-launch --from-file - --name test-install-stdin\", importTar.get()), 0L);\r\n\r\n            ValidateDistributionStarts(L\"test-install-stdin\");\r\n\r\n            // Validate that the distribution was installed under the right name\r\n            auto distroKey = OpenDistributionKey(L\"test-install-stdin\");\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n\r\n            ValidateDistributionShortcut(L\"test-install-stdin\", nullptr);\r\n\r\n            cleanup.reset();\r\n\r\n            // Validate that the base path is removed and that the shortcut is gone*\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_FALSE(std::filesystem::exists(basePath));\r\n        }\r\n\r\n        // Distribution default name conflicts with already installed distribution\r\n        {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { DeleteFile(L\"conflict.tar\"); });\r\n\r\n            CreateTarFromManifest(L\"[oobe]\\ndefaultName = test_distro\", L\"conflict.tar\");\r\n\r\n            constexpr auto expectedOutput =\r\n                L\"Installing: conflict.tar\\r\\n\\\r\nA distribution with the supplied name already exists. Use --name to chose a different name.\\r\\n\\\r\nError code: Wsl/Service/RegisterDistro/ERROR_ALREADY_EXISTS\\r\\n\";\r\n\r\n            InstallFromTar(L\"conflict.tar\", L\"\", -1, expectedOutput);\r\n        }\r\n\r\n        // Distribution default name is invalid\r\n        {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { DeleteFile(L\"invalid.tar\"); });\r\n\r\n            CreateTarFromManifest(L\"[oobe]\\ndefaultName = invalid!\", L\"invalid.tar\");\r\n\r\n            constexpr auto expectedOutput =\r\n                L\"Installing: invalid.tar\\r\\n\\\r\nInvalid distribution name: \\\"invalid!\\\".\\r\\n\\\r\nError code: Wsl/Service/RegisterDistro/E_INVALIDARG\\r\\n\";\r\n\r\n            InstallFromTar(L\"invalid.tar\", L\"\", -1, expectedOutput);\r\n        }\r\n\r\n        // Distribution icon file is too big\r\n        {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n                DeleteFile(L\"big-icon.tar\");\r\n                LxsstuLaunchWsl(L\"--unregister big-icon\");\r\n            });\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"fallocate /icon.ico -l 20MB\"), 0L);\r\n\r\n            CreateTarFromManifest(L\"[shortcut]\\nicon = /icon.ico\", L\"big-icon.tar\");\r\n\r\n            WslKeepAlive keepAlive;\r\n            InstallFromTar(L\"big-icon.tar\", L\"--name big-icon\");\r\n            ValidateDistributionStarts(L\"big-icon\");\r\n\r\n            if (LxsstuVmMode())\r\n            {\r\n                VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"dmesg | grep -iz 'File.*is too big' > /dev/null\"), 0L);\r\n            }\r\n\r\n            // Validate that the distribution was installed under the right name\r\n            auto distroKey = OpenDistributionKey(L\"big-icon\");\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n\r\n            ValidateDistributionShortcut(L\"big-icon\", nullptr);\r\n\r\n            cleanup.reset();\r\n\r\n            // Validate that the base path is removed and that the shortcut is gone*\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_FALSE(std::filesystem::exists(basePath));\r\n        }\r\n\r\n        // Distribution icon file doesn't exist\r\n        {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n                DeleteFile(L\"icon-not-found.tar\");\r\n                LxsstuLaunchWsl(L\"--unregister icon-not-found\");\r\n            });\r\n\r\n            CreateTarFromManifest(L\"[shortcut]\\nicon = /does-not-exist.ico\", L\"icon-not-found.tar\");\r\n\r\n            InstallFromTar(L\"icon-not-found.tar\", L\"--name icon-not-found\");\r\n            ValidateDistributionStarts(L\"icon-not-found\");\r\n\r\n            // Validate that the distribution was installed under the right name\r\n            auto distroKey = OpenDistributionKey(L\"icon-not-found\");\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto shortcutPath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\");\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_TRUE(std::filesystem::exists(basePath));\r\n\r\n            ValidateDistributionShortcut(L\"icon-not-found\", nullptr);\r\n\r\n            cleanup.reset();\r\n\r\n            // Validate that the base path is removed and that the shortcut is gone*\r\n            VERIFY_IS_FALSE(std::filesystem::exists(shortcutPath));\r\n            VERIFY_IS_FALSE(std::filesystem::exists(basePath));\r\n        }\r\n\r\n        // Distribution with a custom terminal profile\r\n        {\r\n            constexpr auto distroName = L\"custom-terminal-profile\";\r\n            constexpr auto tarName = L\"custom-terminal-profile.tar\";\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [distroName]() {\r\n                DeleteFile(tarName);\r\n                LxsstuLaunchWsl(std::format(L\"--unregister {}\", distroName));\r\n            });\r\n\r\n            DistroFileChange profileTemplate(L\"/terminal.json\", false);\r\n\r\n            constexpr auto templateContent =\r\n                LR\"(\r\n            {\r\n                \"profiles\": [{\"custom-field\": \"custom-value\"}],\r\n                \"schemes\": [{\"name\": \"my-scheme\"}]\r\n            })\";\r\n\r\n            profileTemplate.SetContent(templateContent);\r\n\r\n            CreateTarFromManifest(L\"[windowsterminal]\\nprofileTemplate = /terminal.json\", tarName);\r\n\r\n            InstallFromTar(tarName, std::format(L\"--name {}\", distroName).c_str());\r\n            ValidateDistributionStarts(distroName);\r\n\r\n            auto distroKey = OpenDistributionKey(distroName);\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n            auto [json, profile_path] = ValidateDistributionTerminalProfile(distroName, true);\r\n\r\n            VERIFY_ARE_EQUAL(json[\"profiles\"][1][\"custom-field\"].get<std::string>(), \"custom-value\");\r\n            VERIFY_ARE_EQUAL(json[\"schemes\"][0][\"name\"].get<std::string>(), \"my-scheme\");\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(profile_path));\r\n            cleanup.reset();\r\n\r\n            // Terminal profile should be removed when the distribution is unregistered.\r\n            VERIFY_IS_FALSE(std::filesystem::exists(profile_path));\r\n        }\r\n\r\n        // Distribution with an invalid terminal profile json\r\n        {\r\n            constexpr auto distroName = L\"custom-terminal-profile-bad-json\";\r\n            constexpr auto tarName = L\"custom-terminal-profile-bad-json.tar\";\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [distroName]() {\r\n                DeleteFile(tarName);\r\n                LxsstuLaunchWsl(std::format(L\"--unregister {}\", distroName));\r\n            });\r\n\r\n            DistroFileChange profileTemplate(L\"/terminal.json\", false);\r\n            profileTemplate.SetContent(L\"bad-json\");\r\n\r\n            CreateTarFromManifest(L\"[windowsterminal]\\nprofileTemplate = /terminal.json\", tarName);\r\n\r\n            // Validate the invalid json blob generates a warning.\r\n            InstallFromTar(\r\n                tarName,\r\n                std::format(L\"--name {}\", distroName).c_str(),\r\n                0,\r\n                nullptr,\r\n                L\"wsl: Failed to parse terminal profile while registering distribution: [json.exception.parse_error.101] \"\r\n                L\"parse \"\r\n                L\"error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'b'\\r\\n\");\r\n\r\n            ValidateDistributionStarts(distroName);\r\n        }\r\n\r\n        // Distribution with a preexisting hide profile.\r\n        {\r\n            constexpr auto distroName = L\"custom-terminal-profile-hide\";\r\n            constexpr auto tarName = L\"custom-terminal-profile-hide.tar\";\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [distroName]() {\r\n                DeleteFile(tarName);\r\n                LxsstuLaunchWsl(std::format(L\"--unregister {}\", distroName));\r\n            });\r\n\r\n            auto profileGuid = wsl::shared::string::GuidToString<wchar_t>(\r\n                CreateV5Uuid(GeneratedProfilesTerminalNamespace, std::as_bytes(std::span{std::wstring_view{distroName}})));\r\n\r\n            auto content = std::format(\r\n                LR\"({{\"profiles\": [{{ \"updates\": \"{}\", \"hidden\": true, \"custom\": true}}, {{\"name\": \"my-profile\"}}]}})\", profileGuid);\r\n\r\n            DistroFileChange profileTemplate(L\"/terminal.json\", false);\r\n            profileTemplate.SetContent(content.c_str());\r\n\r\n            CreateTarFromManifest(L\"[windowsterminal]\\nprofileTemplate = /terminal.json\", tarName);\r\n            InstallFromTar(tarName, std::format(L\"--name {}\", distroName).c_str());\r\n\r\n            ValidateDistributionStarts(distroName);\r\n\r\n            auto distroKey = OpenDistributionKey(distroName);\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            // Validate that the default terminal profile is still generated.\r\n            auto basePath = wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"BasePath\", L\"\");\r\n            auto [json, profile_path] = ValidateDistributionTerminalProfile(distroName, true);\r\n            VERIFY_ARE_EQUAL(json[\"profiles\"][0][\"custom\"].get<bool>(), true);\r\n            VERIFY_ARE_EQUAL(json[\"profiles\"].size(), 2);\r\n\r\n            VERIFY_IS_TRUE(std::filesystem::exists(profile_path));\r\n\r\n            VERIFY_ARE_EQUAL(\r\n                profile_path, wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"TerminalProfilePath\", L\"\"));\r\n\r\n            cleanup.reset();\r\n\r\n            // Terminal profile should be removed when the distribution is unregistered.\r\n            VERIFY_IS_FALSE(std::filesystem::exists(profile_path));\r\n        }\r\n\r\n        // Distribution opting-out of terminal profile generation\r\n        {\r\n            constexpr auto distroName = L\"no-terminal-profile\";\r\n            constexpr auto tarName = L\"no-terminal-profile.tar\";\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [distroName]() {\r\n                DeleteFile(tarName);\r\n                LxsstuLaunchWsl(std::format(L\"--unregister {}\", distroName));\r\n            });\r\n\r\n            CreateTarFromManifest(L\"[windowsterminal]\\nenabled = false\", tarName);\r\n\r\n            InstallFromTar(tarName, std::format(L\"--name {}\", distroName).c_str());\r\n\r\n            auto distroKey = OpenDistributionKey(distroName);\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            // Validate that no terminal profile is generated.\r\n            VERIFY_ARE_EQUAL(\r\n                L\"\", wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"TerminalProfilePath\", L\"\"));\r\n        }\r\n\r\n        // Distribution opting-out of shortcut generation\r\n        {\r\n            constexpr auto distroName = L\"no-shortcut\";\r\n            constexpr auto tarName = L\"no-shortcut.tar\";\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [distroName]() {\r\n                DeleteFile(tarName);\r\n                LxsstuLaunchWsl(std::format(L\"--unregister {}\", distroName));\r\n            });\r\n\r\n            CreateTarFromManifest(L\"[shortcut]\\nenabled = false\", tarName);\r\n\r\n            InstallFromTar(tarName, std::format(L\"--name {}\", distroName).c_str());\r\n\r\n            auto distroKey = OpenDistributionKey(distroName);\r\n            VERIFY_IS_TRUE(!!distroKey);\r\n\r\n            // Validate that no terminal profile is generated.\r\n            VERIFY_ARE_EQUAL(L\"\", wsl::windows::common::registry::ReadString(distroKey.get(), nullptr, L\"ShortcutPath\", L\"\"));\r\n        }\r\n    }\r\n\r\n    static auto SetManifest(const std::string& Content, bool Append = false)\r\n    {\r\n        auto file = wsl::windows::common::filesystem::TempFile(GENERIC_WRITE, FILE_SHARE_READ, OPEN_EXISTING);\r\n        VERIFY_IS_TRUE(WriteFile(file.Handle.get(), Content.c_str(), static_cast<DWORD>(Content.size()), nullptr, nullptr));\r\n\r\n        RegistryKeyChange<std::wstring> manifestOverride{\r\n            HKEY_LOCAL_MACHINE,\r\n            LXSS_REGISTRY_PATH,\r\n            Append ? wsl::windows::common::distribution::c_distroUrlAppendRegistryValue : wsl::windows::common::distribution::c_distroUrlRegistryValue,\r\n            L\"file://\" + file.Path.wstring()};\r\n\r\n        return std::make_pair(std::move(file), std::move(manifestOverride));\r\n    }\r\n\r\n    static void ValidateInstall(const std::wstring& cmd, LPCWSTR ExpectedOutput = nullptr)\r\n    {\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--install --no-launch {}\", cmd));\r\n\r\n        if (ExpectedOutput != nullptr)\r\n        {\r\n            VERIFY_ARE_EQUAL(ExpectedOutput, out);\r\n        }\r\n    }\r\n\r\n    static void ValidateInstallError(\r\n        const std::wstring& cmd, const std::wstring& expectedOutput, const std::wstring& expectedWarnings = L\"\")\r\n    {\r\n        auto [out, err] = LxsstuLaunchWslAndCaptureOutput(cmd, -1);\r\n\r\n        VERIFY_ARE_EQUAL(expectedOutput, out);\r\n        VERIFY_ARE_EQUAL(expectedWarnings, err);\r\n    }\r\n\r\n    static void UnregisterDistribution(LPCWSTR Name)\r\n    {\r\n        LxsstuLaunchWsl(std::format(L\"--unregister {}\", Name));\r\n    }\r\n\r\n    TEST_METHOD(FileUrl)\r\n    {\r\n        auto check = [](LPCWSTR Input, const std::optional<std::filesystem::path>& ExpectedOutput) {\r\n            const auto output = wsl::windows::common::filesystem::TryGetPathFromFileUrl(Input);\r\n\r\n            VERIFY_ARE_EQUAL(output.has_value(), ExpectedOutput.has_value());\r\n\r\n            if (output.has_value())\r\n            {\r\n                VERIFY_ARE_EQUAL(output.value(), ExpectedOutput.value());\r\n            }\r\n        };\r\n\r\n        check(L\"file://C:/File\", L\"C:\\\\File\");\r\n        check(L\"file://C:\\\\File\", L\"C:\\\\File\");\r\n        check(L\"file:///C:\\\\File\", L\"C:\\\\File\");\r\n        check(L\"file:///RelativeFile\", L\"RelativeFile\");\r\n        check(L\"file:///RelativeFile\\\\SubPath/SubPath\", L\"RelativeFile\\\\SubPath\\\\SubPath\");\r\n        check(L\"notfile:///C:\\\\File\", {});\r\n    }\r\n\r\n    TEST_METHOD(MacAddressParsing)\r\n    {\r\n        using namespace wsl::shared::string;\r\n        auto testParse = [](const std::wstring& Input, const std::optional<MacAddress>& ExpectedOutput, char separator = '\\0') {\r\n            const auto result = wsl::shared::string::ParseMacAddressNoThrow<wchar_t>(Input, separator);\r\n\r\n            VERIFY_ARE_EQUAL(result.has_value(), ExpectedOutput.has_value());\r\n            if (result.has_value())\r\n            {\r\n                VERIFY_ARE_EQUAL(ExpectedOutput.value(), result.value());\r\n            }\r\n        };\r\n\r\n        testParse(L\"\", {});\r\n        testParse(L\"-\", {});\r\n        testParse(L\"00:00:00:00:00:0\", {});\r\n        testParse(L\"00::00:00:00:00:00\", {});\r\n        testParse(L\"000:00:00:00:00:00\", {});\r\n        testParse(L\"000:00:00:00:00:0g\", {});\r\n        testParse(L\"00:00:00:00:00:00\", {{0, 0, 0, 0, 0, 0}});\r\n        testParse(L\"01:23:45:67:89:AB\", {{0x01, 0x23, 0x45, 0x67, 0x89, 0xab}});\r\n        testParse(L\"01-23-45-67-89-AB\", {{0x01, 0x23, 0x45, 0x67, 0x89, 0xab}});\r\n        testParse(L\"01-23-45-67-89-AB\", {{0x01, 0x23, 0x45, 0x67, 0x89, 0xab}}, '-');\r\n        testParse(L\"01-23-45-67-89-AB\", {}, ':');\r\n        testParse(L\"01-23-45-67-89:AB\", {});\r\n        testParse(L\"01,23,45,67,89,AB\", {});\r\n\r\n        VERIFY_ARE_EQUAL(wsl::shared::string::FormatMacAddress({0x01, 0x23, 0x45, 0x67, 0x89, 0xab}, '-'), \"01-23-45-67-89-AB\");\r\n        VERIFY_ARE_EQUAL(wsl::shared::string::FormatMacAddress({0x01, 0x23, 0x45, 0x67, 0x89, 0xab}, ':'), \"01:23:45:67:89:AB\");\r\n\r\n        VERIFY_ARE_EQUAL(\r\n            wsl::shared::string::FormatMacAddress({0x01, 0x23, 0x45, 0x67, 0x89, 0xab}, L'-'),\r\n            wsl::shared::string::MultiByteToWide(\"01-23-45-67-89-AB\"));\r\n        VERIFY_ARE_EQUAL(\r\n            wsl::shared::string::FormatMacAddress({0x01, 0x23, 0x45, 0x67, 0x89, 0xab}, L':'),\r\n            wsl::shared::string::MultiByteToWide(\"01:23:45:67:89:AB\"));\r\n    }\r\n\r\n    TEST_METHOD(ModernDistroInstall)\r\n    {\r\n        auto tarPath = \"file://\" + wsl::shared::string::WideToMultiByte(EscapePath(g_testDistroPath));\r\n\r\n        wil::unique_handle tarHandle{CreateFile(g_testDistroPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)};\r\n        VERIFY_IS_TRUE(!!tarHandle);\r\n\r\n        auto tarHash = wsl::shared::string::WideToMultiByte(\r\n            wsl::windows::common::string::BytesToHex(wsl::windows::common::wslutil::HashFile(tarHandle.get(), CALG_SHA_256)));\r\n\r\n        // Install a modern distribution\r\n        {\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"debian-12\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Default\": true,\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"{}\"\r\n                }}\r\n            }}\r\n        ]\r\n    }}}})\",\r\n                tarPath,\r\n                tarHash);\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { UnregisterDistribution(L\"debian-12\"); });\r\n\r\n            ValidateInstall(L\"debian --no-launch --name debian-12\");\r\n\r\n            ValidateDistributionStarts(L\"debian-12\");\r\n\r\n            UnregisterDistribution(L\"debian-12\");\r\n\r\n            ValidateInstall(L\"debian-12 --no-launch --name debian-12\");\r\n            ValidateDistributionStarts(L\"debian-12\");\r\n\r\n            ValidateInstallError(\r\n                L\"--install DoesNotExists\",\r\n                L\"Invalid distribution name: 'DoesNotExists'.\\r\\n\\\r\nTo get a list of valid distributions, use 'wsl.exe --list --online'.\\r\\n\\\r\nError code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND\\r\\n\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unregister debian-12\"), 0L);\r\n\r\n            // Verify that name matching is not case-sensitive on the version.\r\n            ValidateInstall(L\"Debian-12 --no-launch --name debian-12\");\r\n            ValidateDistributionStarts(L\"debian-12\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unregister debian-12\"), 0L);\r\n\r\n            // Verify that name matching is not case-sensitive on the flavor.\r\n            ValidateInstall(L\"Debian --no-launch --name debian-12\");\r\n            ValidateDistributionStarts(L\"debian-12\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unregister debian-12\"), 0L);\r\n\r\n            // Validate an install with a vhd size.\r\n            ValidateInstall(L\"Debian --no-launch --name debian-12 --vhd-size 1GB\");\r\n            ValidateDistributionStarts(L\"debian-12\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unregister debian-12\"), 0L);\r\n\r\n            // Validate an install with a vhd size and fixed vhd.\r\n            ValidateInstall(L\"Debian --no-launch --name debian-12 --vhd-size 1GB --fixed-vhd\");\r\n            ValidateDistributionStarts(L\"debian-12\");\r\n        }\r\n\r\n        // Validate that default works correctly\r\n        {\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"Default\": \"debian\",\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"debian-nondefault\",\r\n                \"FriendlyName\": \"\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"\",\r\n                    \"Sha256\": \"\"\r\n                }}\r\n            }},\r\n            {{\r\n                \"Name\": \"debian-default\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Default\": true,\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"{}\"\r\n                }}\r\n            }}\r\n        ],\r\n\r\n \"ubuntu\": [\r\n            {{\r\n                \"Name\": \"ubuntu-nondefault\",\r\n                \"FriendlyName\": \"\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"\",\r\n                    \"Sha256\": \"\"\r\n                }}\r\n            }},\r\n            {{\r\n                \"Name\": \"ubuntu-default\",\r\n                \"FriendlyName\": \"UbuntuFriendlyName\",\r\n                \"Default\": true,\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"{}\"\r\n                }}\r\n            }}\r\n        ]\r\n    }}}})\",\r\n                tarPath,\r\n                tarHash,\r\n                tarPath,\r\n                tarHash);\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n                UnregisterDistribution(L\"debian-default\");\r\n                UnregisterDistribution(L\"ubuntu-default\");\r\n            });\r\n\r\n            ValidateInstall(\r\n                L\"--no-launch --name debian-default\",\r\n                L\"Installing: DebianFriendlyName\\r\\n\\\r\nDistribution successfully installed. It can be launched via 'wsl.exe -d debian-default'\\r\\n\");\r\n\r\n            ValidateDistributionStarts(L\"debian-default\");\r\n\r\n            ValidateInstall(\r\n                L\"ubuntu --no-launch --name ubuntu-default\",\r\n                L\"Installing: UbuntuFriendlyName\\r\\n\\\r\nDistribution successfully installed. It can be launched via 'wsl.exe -d ubuntu-default'\\r\\n\");\r\n\r\n            ValidateDistributionStarts(L\"ubuntu-default\");\r\n\r\n            // Validate that default can be override via the 'Append' manifest\r\n            auto overrideRestore = SetManifest(R\"({\"Default\": \"ubuntu\"})\", true);\r\n\r\n            UnregisterDistribution(L\"ubuntu-default\");\r\n\r\n            ValidateInstall(\r\n                L\"--no-launch --name ubuntu-default\",\r\n                L\"Installing: UbuntuFriendlyName\\r\\n\\\r\nDistribution successfully installed. It can be launched via 'wsl.exe -d ubuntu-default'\\r\\n\");\r\n\r\n            ValidateDistributionStarts(L\"ubuntu-default\");\r\n        }\r\n\r\n        // Install a legacy distribution\r\n        {\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"debian-12\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"\",\r\n                    \"Sha256\": \"\"\r\n                }}\r\n            }}\r\n        ]\r\n    }},\r\n    \"Distributions\": [\r\n        {{\"Name\": \"legacy\",\r\n          \"FriendlyName\": \"legacy\",\r\n          \"StoreAppId\": \"Dummy\",\r\n          \"PackageFamilyName\": \"Dummy\",\r\n          \"Amd64\": true,\r\n          \"Arm64\": true,\r\n          \"Amd64PackageUrl\": \"http://127.0.0.1:12/dummyUrl\" }}]\r\n}})\",\r\n                tarPath);\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            // There's no easy way to automate the appx package installation, but verify that we take the legacy path\r\n            ValidateInstallError(\r\n                L\"--install legacy --no-launch --web-download\",\r\n                L\"Downloading: legacy\\r\\n\\\r\nA connection with the server could not be established \\r\\n\\\r\nError code: Wsl/InstallDistro/WININET_E_CANNOT_CONNECT\\r\\n\",\r\n                L\"wsl: Using legacy distribution registration. Consider using a tar based distribution instead.\\r\\n\");\r\n\r\n            ValidateInstallError(\r\n                L\"--install legacy --no-launch --web-download --legacy\",\r\n                L\"Downloading: legacy\\r\\n\\\r\nA connection with the server could not be established \\r\\n\\\r\nError code: Wsl/InstallDistro/WININET_E_CANNOT_CONNECT\\r\\n\",\r\n                L\"wsl: Using legacy distribution registration. Consider using a tar based distribution instead.\\r\\n\");\r\n        }\r\n\r\n        // Validate that modern distros takes precedences, but can be overridden.\r\n        {\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"debian-12\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"{}\"\r\n                }}\r\n            }}\r\n        ]\r\n    }},\r\n    \"Distributions\": [\r\n        {{\"Name\": \"debian-12\",\r\n          \"FriendlyName\": \"debian-12\",\r\n          \"StoreAppId\": \"Dummy\",\r\n          \"PackageFamilyName\": \"Dummy\",\r\n          \"Amd64\": true,\r\n          \"Arm64\": true,\r\n          \"Amd64PackageUrl\": \"http://127.0.0.1:12/dummyUrl\" }}]\r\n}})\",\r\n                tarPath,\r\n                tarHash);\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { UnregisterDistribution(L\"debian-12\"); });\r\n\r\n            ValidateInstall(L\"debian-12 --no-launch --name debian-12\");\r\n            ValidateDistributionStarts(L\"debian-12\");\r\n\r\n            // Validate that --legacy takes the appx path.\r\n            ValidateInstallError(\r\n                L\"--install debian-12 --no-launch --web-download --legacy\",\r\n                L\"Downloading: debian-12\\r\\n\\\r\nA connection with the server could not be established \\r\\n\\\r\nError code: Wsl/InstallDistro/WININET_E_CANNOT_CONNECT\\r\\n\",\r\n                L\"wsl: Using legacy distribution registration. Consider using a tar based distribution instead.\\r\\n\");\r\n        }\r\n\r\n        // Validate that distribution can be overridden\r\n        {\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"debian-12\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"{}\"\r\n                }}\r\n            }},\r\n            {{\r\n                \"Name\": \"debian-base\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Default\": true,\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"\"\r\n                }}\r\n            }}\r\n        ]\r\n    }},\r\n    \"Distributions\": [{{\"Name\": \"Dummy\", \"FriendlyName\": \"Dummy\", \"StoreAppId\": \"Dummy\", \"Amd64\": true, \"Arm64\": true }}]\r\n}})\",\r\n                \"DoesNotExist\",\r\n                tarPath,\r\n                tarHash);\r\n\r\n            auto overrideManifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"debian-12\",\r\n                \"FriendlyName\": \"DebianFriendlyNameOverridden\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"{}\"\r\n                }}\r\n            }}\r\n        ]\r\n    }}\r\n}})\",\r\n                tarPath,\r\n                tarHash);\r\n\r\n            auto restore = SetManifest(manifest);\r\n            auto override = SetManifest(overrideManifest, true);\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n                UnregisterDistribution(L\"debian-12\");\r\n                UnregisterDistribution(L\"debian-base\");\r\n            });\r\n\r\n            ValidateInstall(L\"debian-12 --no-launch --name debian-12\");\r\n\r\n            // Validate that distros coming from the 'main' manifest can still be installed.\r\n            ValidateInstall(L\"debian-12 --no-launch --name debian-base\");\r\n        }\r\n\r\n        // Validate that the distribution default name comes from the manifest, event if oobe.defaultName isn't set\r\n        {\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"test-default-manifest-name\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"{}\"\r\n                }}\r\n            }}\r\n        ]\r\n    }}\r\n}})\",\r\n                tarPath,\r\n                tarHash);\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { UnregisterDistribution(L\"test-default-manifest-name\"); });\r\n\r\n            ValidateInstall(L\"test-default-manifest-name\");\r\n            ValidateDistributionStarts(L\"test-default-manifest-name\");\r\n        }\r\n\r\n        // Validate that install fails if hash doesn't match\r\n        {\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"debian-12\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"0x12\"\r\n                }}\r\n            }}\r\n        ]\r\n    }}\r\n}})\",\r\n                tarPath);\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            ValidateInstallError(\r\n                L\"--install debian-12\",\r\n                std::format(\r\n                    L\"Installing: DebianFriendlyName\\r\\n\\\r\nThe distribution hash doesn't match. Expected: 0x12, actual hash: {}\\r\\n\\\r\nError code: Wsl/InstallDistro/VerifyChecksum/TRUST_E_BAD_DIGEST\\r\\n\",\r\n                    wsl::shared::string::MultiByteToWide(tarHash)),\r\n                L\"\");\r\n        }\r\n\r\n        // Validate that we fail if the hash format is incorrect\r\n        {\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"debian-12\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}\",\r\n                    \"Sha256\": \"wrongformat\"\r\n                }}\r\n            }}\r\n        ]\r\n    }}\r\n}})\",\r\n                tarPath);\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            ValidateInstallError(\r\n                L\"--install debian-12\",\r\n                L\"Installing: DebianFriendlyName\\r\\n\\\r\nInvalid hex string: wrongformat\\r\\n\\\r\nError code: Wsl/InstallDistro/VerifyChecksum/E_INVALIDARG\\r\\n\",\r\n                L\"\");\r\n        }\r\n\r\n        // Validate various command line error paths\r\n        {\r\n            auto manifest =\r\n                R\"({\r\n    \"Distributions\": [\r\n        {\"Name\": \"debian-12\",\r\n          \"FriendlyName\": \"debian-12\",\r\n          \"StoreAppId\": \"Dummy\",\r\n          \"PackageFamilyName\": \"Dummy\",\r\n          \"Amd64\": true,\r\n          \"Arm64\": true,\r\n          \"Amd64PackageUrl\": \"\" }]\r\n})\";\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            ValidateInstallError(\r\n                L\"--install debian-12 --location foo\",\r\n                L\"'--location' is not supported when installing legacy distributions.\\r\\n\",\r\n                L\"\");\r\n\r\n            ValidateInstallError(\r\n                L\"--install debian-12 --name foo\", L\"'--name' is not supported when installing legacy distributions.\\r\\n\", L\"\");\r\n\r\n            ValidateInstallError(\r\n                L\"--install debian-12 --vhd-size 1GB\",\r\n                L\"'--vhd-size' is not supported when installing legacy distributions.\\r\\n\",\r\n                L\"\");\r\n\r\n            ValidateInstallError(\r\n                L\"--install invalid\",\r\n                L\"Invalid distribution name: 'invalid'.\\r\\n\\\r\nTo get a list of valid distributions, use 'wsl.exe --list --online'.\\r\\n\\\r\nError code: Wsl/InstallDistro/WSL_E_DISTRO_NOT_FOUND\\r\\n\",\r\n                L\"\");\r\n        }\r\n\r\n        // Validate that a distribution isn't downloaded if its name is already in use.\r\n        {\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"debian\": [\r\n            {{\r\n                \"Name\": \"{}\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"file://nonexistent\",\r\n                    \"Sha256\": \"\"\r\n                }}\r\n            }},\r\n            {{\r\n                \"Name\": \"dummy\",\r\n                \"FriendlyName\": \"dummy\",\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"file://nonexistent\",\r\n                    \"Sha256\": \"\"\r\n                }}\r\n            }}\r\n        ]\r\n    }}\r\n}})\",\r\n                LXSS_DISTRO_NAME_TEST);\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            {\r\n                auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--install {}\", LXSS_DISTRO_NAME_TEST_L), -1);\r\n\r\n                VERIFY_ARE_EQUAL(\r\n                    out,\r\n                    L\"A distribution with the supplied name already exists. Use --name to chose a different name.\\r\\n\"\r\n                    L\"Error code: Wsl/InstallDistro/ERROR_ALREADY_EXISTS\\r\\n\");\r\n\r\n                VERIFY_ARE_EQUAL(err, L\"\");\r\n            }\r\n\r\n            {\r\n                auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--install dummy --name {}\", LXSS_DISTRO_NAME_TEST_L), -1);\r\n\r\n                VERIFY_ARE_EQUAL(\r\n                    out,\r\n                    L\"A distribution with the supplied name already exists. Use --name to chose a different name.\\r\\n\"\r\n                    L\"Error code: Wsl/InstallDistro/ERROR_ALREADY_EXISTS\\r\\n\");\r\n\r\n                VERIFY_ARE_EQUAL(err, L\"\");\r\n            }\r\n        }\r\n\r\n        // Validate handling of case where no default install distro is configured.\r\n        {\r\n            auto manifest =\r\n                R\"({\r\n    \"ModernDistributions\": {\r\n        \"debian\": [\r\n            {\r\n                \"Name\": \"debian-12\",\r\n                \"FriendlyName\": \"DebianFriendlyName\",\r\n                \"Amd64Url\": {\r\n                    \"Url\": \"\",\r\n                    \"Sha256\": \"\"\r\n                }\r\n            }\r\n        ]\r\n    }\r\n})\";\r\n\r\n            auto restore = SetManifest(manifest);\r\n            ValidateInstallError(\r\n                L\"--install\",\r\n                L\"No default distribution has been configured. Please provide a distribution to install.\\r\\n\\\r\nError code: Wsl/InstallDistro/E_UNEXPECTED\\r\\n\",\r\n                L\"\");\r\n        }\r\n\r\n        // Validate that invalid json errors are correctly handled.\r\n        {\r\n            auto restore = SetManifest(\"Bad json\");\r\n\r\n            ValidateInstallError(\r\n                L\"--install debian\",\r\n                L\"Invalid JSON document. Parse error: [json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'B'\\r\\n\\\r\nError code: Wsl/InstallDistro/WSL_E_INVALID_JSON\\r\\n\",\r\n                L\"\");\r\n        }\r\n\r\n        // Validate that url parameters are correctly handled.\r\n        {\r\n            constexpr auto tarEndpoint = L\"http://127.0.0.1:6667/\";\r\n\r\n            UniqueWebServer fileServer(tarEndpoint, std::filesystem::path(g_testDistroPath));\r\n\r\n            wil::unique_handle tarHandle{CreateFile(g_testDistroPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)};\r\n            VERIFY_IS_TRUE(!!tarHandle);\r\n\r\n            auto manifest = std::format(\r\n                R\"({{\r\n    \"ModernDistributions\": {{\r\n        \"test\": [\r\n            {{\r\n                \"Name\": \"test-url-download\",\r\n                \"FriendlyName\": \"FriendlyName\",\r\n                \"Default\": true,\r\n                \"Amd64Url\": {{\r\n                    \"Url\": \"{}/distro.tar?foo=bar&key=value\",\r\n                    \"Sha256\": \"{}\"\r\n                }}\r\n            }}\r\n        ]\r\n    }}}})\",\r\n                tarEndpoint,\r\n                tarHash);\r\n\r\n            auto restore = SetManifest(manifest);\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { UnregisterDistribution(L\"test-url-download\"); });\r\n\r\n            auto [output, error] = LxsstuLaunchWslAndCaptureOutput(L\"--install --no-launch test-url-download\");\r\n            VERIFY_ARE_EQUAL(\r\n                output,\r\n                L\"Downloading: FriendlyName\\r\\nInstalling: FriendlyName\\r\\nDistribution successfully installed. It can be \"\r\n                L\"launched via 'wsl.exe -d test-url-download'\\r\\n\");\r\n\r\n            VERIFY_ARE_EQUAL(error, L\"\");\r\n        }\r\n\r\n        // Validate that manifest distribution ordering is preserved.\r\n        {\r\n            auto validateOrder = [](const std::vector<LPCWSTR>& expected) {\r\n                auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"--list --online\");\r\n\r\n                auto lines = wsl::shared::string::Split<wchar_t>(out, '\\n');\r\n\r\n                for (size_t i = 0; i < expected.size(); i++)\r\n                {\r\n                    auto end = lines[i + 4].find_first_of(L\" \\t\");\r\n                    VERIFY_ARE_NOT_EQUAL(end, std::wstring::npos);\r\n\r\n                    auto distro = lines[i + 4].substr(0, end);\r\n\r\n                    VERIFY_ARE_EQUAL(expected[i], distro);\r\n                }\r\n            };\r\n\r\n            {\r\n                auto manifest =\r\n                    R\"({\r\n    \"ModernDistributions\": {\r\n        \"distro1\": [\r\n            {\r\n                \"Name\": \"distro1\",\r\n                \"FriendlyName\": \"distro1Name\",\r\n                \"Amd64Url\": {\"Url\": \"\",\"Sha256\": \"\"}\r\n            }\r\n        ],\r\n        \"distro2\": [\r\n            {\r\n                \"Name\": \"distro2\",\r\n                \"FriendlyName\": \"distro2Name\",\r\n                \"Amd64Url\": {\"Url\": \"\",\"Sha256\": \"\"}\r\n            }\r\n        ]\r\n    }\r\n})\";\r\n\r\n                auto restore = SetManifest(manifest);\r\n                validateOrder({L\"distro1\", L\"distro2\"});\r\n            }\r\n\r\n            {\r\n                auto manifest =\r\n                    R\"({\r\n    \"ModernDistributions\": {\r\n        \"distro2\": [\r\n            {\r\n                \"Name\": \"distro2\",\r\n                \"FriendlyName\": \"distro2Name\",\r\n                \"Amd64Url\": {\"Url\": \"\",\"Sha256\": \"\"}\r\n            }\r\n        ],\r\n        \"distro1\": [\r\n            {\r\n                \"Name\": \"distro1\",\r\n                \"FriendlyName\": \"distro1Name\",\r\n                \"Amd64Url\": {\"Url\": \"\",\"Sha256\": \"\"}\r\n            }\r\n        ]\r\n    }\r\n})\";\r\n\r\n                auto restore = SetManifest(manifest);\r\n\r\n                validateOrder({L\"distro2\", L\"distro1\"});\r\n            }\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(ModernInstallEndToEnd)\r\n    {\r\n        constexpr auto tarName = L\"end2end.tar\";\r\n\r\n        DistroFileChange distributionconf(L\"/etc/wsl-distribution.conf\", false);\r\n        distributionconf.SetContent(\r\n            L\"[oobe]\\ncommand = /bin/bash -c 'echo OOBE && useradd -u 1011 -m -s /bin/bash myuser'\\n defaultUid = 1011\\n\");\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--export test_distro {}\", tarName)), 0L);\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() {\r\n            DeleteFile(tarName);\r\n\r\n            LxsstuLaunchWsl(L\"--unregister end2end\");\r\n        });\r\n\r\n        wil::unique_handle tarHandle{CreateFile(tarName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)};\r\n        VERIFY_IS_TRUE(!!tarHandle);\r\n\r\n        auto tarHash = wsl::windows::common::string::BytesToHex(wsl::windows::common::wslutil::HashFile(tarHandle.get(), CALG_SHA_256));\r\n\r\n        constexpr auto manifestEndpoint = L\"http://127.0.0.1:6666/\";\r\n        constexpr auto tarEndpoint = L\"http://127.0.0.1:6667/\";\r\n\r\n        auto manifest = std::format(\r\n            LR\"({{\r\n    \\\"ModernDistributions\\\": {{\r\n        \\\"end2end\\\": [\r\n            {{\r\n                \\\"Name\\\": \\\"end2end\\\",\r\n                \\\"FriendlyName\\\": \\\"FriendlyName\\\",\r\n                \\\"Default\\\": true,\r\n                \\\"Amd64Url\\\": {{\r\n                    \\\"Url\\\": \\\"{}/distro.tar\\\",\r\n                    \\\"Sha256\\\": \\\"{}\\\"\r\n                }}\r\n            }}\r\n        ]\r\n    }}}})\",\r\n            tarEndpoint,\r\n            tarHash);\r\n\r\n        UniqueWebServer apiServer(manifestEndpoint, manifest.c_str());\r\n        UniqueWebServer fileServer(tarEndpoint, std::filesystem::path(tarName));\r\n\r\n        RegistryKeyChange<std::wstring> manifestOverride{\r\n            HKEY_LOCAL_MACHINE, LXSS_REGISTRY_PATH, wsl::windows::common::distribution::c_distroUrlRegistryValue, manifestEndpoint};\r\n\r\n        {\r\n            auto [output, error] = LxsstuLaunchWslAndCaptureOutput(L\"--install --no-launch end2end\");\r\n            VERIFY_ARE_EQUAL(\r\n                output,\r\n                L\"Downloading: FriendlyName\\r\\nInstalling: FriendlyName\\r\\nDistribution successfully installed. It can be \"\r\n                L\"launched via 'wsl.exe -d end2end'\\r\\n\");\r\n\r\n            VERIFY_ARE_EQUAL(error, L\"\");\r\n        }\r\n\r\n        // Check that OOBE runs\r\n        {\r\n            auto [read, write] = CreateSubprocessPipe(true, false);\r\n            write.reset();\r\n\r\n            wsl::windows::common::SubProcess process(nullptr, LxssGenerateWslCommandLine(L\"-d end2end\").c_str());\r\n            process.SetStdHandles(read.get(), nullptr, nullptr);\r\n\r\n            auto oobeResult = process.RunAndCaptureOutput();\r\n            VERIFY_ARE_EQUAL(oobeResult.Stdout, L\"OOBE\\n\");\r\n            VERIFY_ARE_EQUAL(oobeResult.Stderr, L\"\");\r\n            VERIFY_ARE_EQUAL(oobeResult.ExitCode, 0);\r\n        }\r\n\r\n        // Run the command again to check that oobe doesn't run twice\r\n        {\r\n            auto [read, write] = CreateSubprocessPipe(true, false);\r\n            write.reset();\r\n\r\n            wsl::windows::common::SubProcess process(nullptr, LxssGenerateWslCommandLine(L\"-d end2end\").c_str());\r\n            process.SetStdHandles(read.get(), nullptr, nullptr);\r\n\r\n            auto oobeResult = process.RunAndCaptureOutput();\r\n            VERIFY_ARE_EQUAL(oobeResult.Stdout, L\"\");\r\n            VERIFY_ARE_EQUAL(oobeResult.Stderr, L\"\");\r\n            VERIFY_ARE_EQUAL(oobeResult.ExitCode, 0);\r\n        }\r\n\r\n        // Validate UID\r\n        auto [output, error] = LxsstuLaunchWslAndCaptureOutput(L\"-d end2end id -u\");\r\n        VERIFY_ARE_EQUAL(output, L\"1011\\n\");\r\n        VERIFY_ARE_EQUAL(error, L\"\");\r\n    }\r\n\r\n    TEST_METHOD(DistroTarFormats)\r\n    {\r\n        auto version = LxsstuVmMode() ? L\"2\" : L\"1\";\r\n\r\n        auto convert = [](LPCWSTR Command, LPCWSTR FileName) {\r\n            const wil::unique_handle output{CreateFile(FileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)};\r\n            VERIFY_IS_TRUE(!!output);\r\n\r\n            wsl::windows::common::helpers::SetHandleInheritable(output.get());\r\n\r\n            LxsstuLaunchWsl(std::format(L\"xz -d -c $(wslpath '{}') | {}\", g_testDistroPath, Command), nullptr, output.get());\r\n\r\n            return wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [FileName]() { std::filesystem::remove(FileName); });\r\n        };\r\n\r\n        auto importAndTest = [&version](LPCWSTR FileName) {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [FileName]() { LxsstuLaunchWsl(L\"--unregister test-format\"); });\r\n            LxsstuLaunchWsl(std::format(L\"--install --no-launch --from-file {} --name test-format --version {}\", FileName, version));\r\n\r\n            auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"-d test-format echo OK\");\r\n            VERIFY_ARE_EQUAL(out, L\"OK\\n\");\r\n        };\r\n\r\n        // Tar bz2\r\n        {\r\n            auto cleanup = convert(L\"bzip2\", L\"test-distro.tar.bz2\");\r\n            importAndTest(L\"test-distro.tar.bz2\");\r\n        }\r\n\r\n        // Tar gz\r\n        {\r\n            auto cleanup = convert(L\"gzip\", L\"test-distro.tar.gz\");\r\n            importAndTest(L\"test-distro.tar.gz\");\r\n        }\r\n\r\n        // N.B. tar xz is already covered since it's the format of the test distro.\r\n        VERIFY_IS_TRUE(wsl::shared::string::EndsWith(g_testDistroPath, std::wstring_view{L\".xz\"}));\r\n    }\r\n\r\n    TEST_METHOD(InnerCommandLineParsing)\r\n    {\r\n        using namespace wsl::windows::common;\r\n        using namespace wsl::shared;\r\n\r\n        constexpr auto entryPoint = L\"dummy\";\r\n\r\n        auto parse = [&](ArgumentParser& Parser, LPCWSTR ExpectedError = nullptr) {\r\n            const ExecutionContext context(Context::Wsl);\r\n            std::optional<std::wstring> error;\r\n\r\n            try\r\n            {\r\n                Parser.Parse();\r\n            }\r\n            catch (...)\r\n            {\r\n                if (context.ReportedError().has_value())\r\n                {\r\n                    error = wslutil::ErrorToString(context.ReportedError().value()).Message;\r\n                }\r\n                else\r\n                {\r\n                    error = wslutil::ErrorCodeToString(wil::ResultFromCaughtException());\r\n                    THROW_HR(wil::ResultFromCaughtException());\r\n                }\r\n            }\r\n\r\n            if (error.has_value())\r\n            {\r\n                VERIFY_ARE_EQUAL(ExpectedError, error.value());\r\n            }\r\n            else\r\n            {\r\n                VERIFY_IS_NULL(ExpectedError);\r\n            }\r\n        };\r\n\r\n        {\r\n            ArgumentParser parser(L\"--a b --c d pos-value\", entryPoint, 0);\r\n            std::wstring a;\r\n            std::wstring c;\r\n            std::wstring e;\r\n            std::wstring pos;\r\n            parser.AddArgument(a, L\"--a\");\r\n            parser.AddArgument(c, L\"--c\");\r\n            parser.AddArgument(e, L\"--e\");\r\n            parser.AddPositionalArgument(pos, 0);\r\n\r\n            parse(parser);\r\n\r\n            VERIFY_ARE_EQUAL(a, L\"b\");\r\n            VERIFY_ARE_EQUAL(c, L\"d\");\r\n            VERIFY_ARE_EQUAL(pos, L\"pos-value\");\r\n            VERIFY_ARE_EQUAL(e, L\"\");\r\n        }\r\n\r\n        {\r\n            ArgumentParser parser(L\"--a b -- --c\", entryPoint, 0);\r\n            std::wstring a;\r\n            std::wstring c;\r\n            std::wstring e;\r\n            std::wstring pos;\r\n            parser.AddArgument(a, L\"--a\");\r\n            parser.AddArgument(e, L\"--e\");\r\n            parser.AddPositionalArgument(pos, 0);\r\n\r\n            parse(parser);\r\n\r\n            VERIFY_ARE_EQUAL(a, L\"b\");\r\n            VERIFY_ARE_EQUAL(pos, L\"--c\");\r\n            VERIFY_ARE_EQUAL(e, L\"\");\r\n        }\r\n\r\n        {\r\n            GUID expectedGuid = {0x12345678, 0x1234, 0x1234, {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}};\r\n            auto commandLine = std::format(\r\n                L\"--flag b --arg value pos-arg2 pos-arg3 --flag3 --flag4 value4 --guid {}\",\r\n                wsl::shared::string::GuidToString<wchar_t>(expectedGuid));\r\n\r\n            ArgumentParser parser(commandLine.c_str(), entryPoint, 0);\r\n            bool flag{};\r\n            std::wstring arg;\r\n            std::wstring pos1;\r\n            std::wstring pos2;\r\n            std::wstring pos3;\r\n            bool flag3{};\r\n            std::wstring value4;\r\n            bool dummy{};\r\n            GUID parsedGuid;\r\n\r\n            parser.AddArgument(flag, L\"--flag\");\r\n            parser.AddArgument(arg, L\"--arg\");\r\n            parser.AddPositionalArgument(pos1, 0);\r\n            parser.AddPositionalArgument(pos2, 1);\r\n            parser.AddPositionalArgument(pos3, 2);\r\n            parser.AddArgument(flag3, L\"--flag3\");\r\n            parser.AddArgument(value4, L\"--flag4\");\r\n            parser.AddArgument(dummy, L\"--dummy\");\r\n            parser.AddArgument(parsedGuid, L\"--guid\");\r\n\r\n            parse(parser);\r\n\r\n            VERIFY_IS_TRUE(flag);\r\n            VERIFY_ARE_EQUAL(arg, L\"value\");\r\n            VERIFY_ARE_EQUAL(pos1, L\"b\");\r\n            VERIFY_ARE_EQUAL(pos2, L\"pos-arg2\");\r\n            VERIFY_ARE_EQUAL(pos3, L\"pos-arg3\");\r\n            VERIFY_IS_TRUE(flag3);\r\n            VERIFY_ARE_EQUAL(L\"value4\", value4);\r\n            VERIFY_IS_FALSE(dummy);\r\n            VERIFY_ARE_EQUAL(expectedGuid, parsedGuid);\r\n        }\r\n\r\n        {\r\n            ArgumentParser parser(L\"--a\", entryPoint, 0);\r\n            std::wstring a;\r\n            parser.AddArgument(a, L\"--a\");\r\n\r\n            parse(\r\n                parser,\r\n                std::format(\r\n                    L\"Command line argument --a requires a value.\\n\"\r\n                    \"Please use '{} --help' to get a list of supported arguments.\",\r\n                    entryPoint)\r\n                    .c_str());\r\n        }\r\n\r\n        {\r\n            ArgumentParser parser(L\"--does-not-exist --a b -- --c\", entryPoint, 0);\r\n            parser.AddArgument(NoOp{}, L\"--a\");\r\n            parser.AddArgument(NoOp{}, L\"--e\");\r\n            parser.AddPositionalArgument(NoOp{}, 0);\r\n\r\n            parse(\r\n                parser,\r\n                std::format(\r\n                    L\"Invalid command line argument: --does-not-exist\\n\"\r\n                    \"Please use '{} --help' to get a list of supported arguments.\",\r\n                    entryPoint)\r\n                    .c_str());\r\n        }\r\n\r\n        {\r\n            ArgumentParser parser(L\"--guid foo\", entryPoint, 0);\r\n            GUID guid;\r\n            parser.AddArgument(guid, L\"--guid\");\r\n\r\n            parse(parser, L\"Invalid GUID format: 'foo'\");\r\n        }\r\n\r\n        {\r\n            ArgumentParser parser(L\"-abc pos-value\", entryPoint, 0);\r\n            bool aLong{};\r\n            bool a{};\r\n            bool b{};\r\n            bool c{};\r\n            bool d{};\r\n            std::wstring pos;\r\n\r\n            parser.AddArgument(aLong, L\"--a\");\r\n            parser.AddArgument(a, nullptr, 'a');\r\n            parser.AddArgument(b, nullptr, 'b');\r\n            parser.AddArgument(c, nullptr, 'c');\r\n            parser.AddArgument(d, nullptr, 'd');\r\n            parser.AddPositionalArgument(pos, 0);\r\n\r\n            parse(parser);\r\n\r\n            VERIFY_IS_TRUE(a);\r\n            VERIFY_IS_TRUE(b);\r\n            VERIFY_IS_TRUE(c);\r\n            VERIFY_IS_FALSE(d);\r\n            VERIFY_IS_FALSE(aLong);\r\n            VERIFY_ARE_EQUAL(pos, L\"pos-value\");\r\n        }\r\n\r\n        {\r\n            ArgumentParser parser(L\"-abc\", entryPoint, 0);\r\n\r\n            parser.AddArgument(NoOp{}, nullptr, 'a');\r\n            parser.AddArgument(NoOp{}, nullptr, 'c');\r\n\r\n            parse(\r\n                parser,\r\n                std::format(\r\n                    L\"Invalid command line argument: -abc\\n\"\r\n                    \"Please use '{} --help' to get a list of supported arguments.\",\r\n                    entryPoint)\r\n                    .c_str());\r\n        }\r\n\r\n        {\r\n            ArgumentParser parser(L\"- --\", entryPoint, 0);\r\n\r\n            parse(\r\n                parser,\r\n                std::format(\r\n                    L\"Invalid command line argument: -\\n\"\r\n                    \"Please use '{} --help' to get a list of supported arguments.\",\r\n                    entryPoint)\r\n                    .c_str());\r\n        }\r\n\r\n        {\r\n            ArgumentParser parser(L\"--foo -\", entryPoint, 0);\r\n            bool a{};\r\n            std::wstring pos;\r\n\r\n            parser.AddArgument(a, L\"--foo\");\r\n            parser.AddPositionalArgument(pos, 0);\r\n\r\n            parse(parser);\r\n            VERIFY_IS_TRUE(a);\r\n            VERIFY_ARE_EQUAL(pos, L\"-\");\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(CaseSensitivity)\r\n    {\r\n        auto setCaseSensitivity = [](const std::wstring& Path, bool enable) {\r\n            auto cmd = std::format(L\"fsutil.exe file setCaseSensitiveInfo \\\"{}\\\" {}\", Path.c_str(), enable ? L\"enable\" : L\"disable\");\r\n            LxsstuLaunchCommandAndCaptureOutput(cmd.data());\r\n        };\r\n\r\n        auto getCaseSensitivity = [](const std::wstring& Path) {\r\n            auto cmd = std::format(L\"fsutil.exe file queryCaseSensitiveInfo \\\"{}\\\"\", Path);\r\n            auto [out, _] = LxsstuLaunchCommandAndCaptureOutput(cmd.data());\r\n            if (out.find(L\"is disabled\") != std::string::npos)\r\n            {\r\n                return false;\r\n            }\r\n            else if (out.find(L\"is enabled\") != std::string::npos)\r\n            {\r\n                return true;\r\n            }\r\n\r\n            LogError(\"Failed to parse fsutil output: %ls\", out.c_str());\r\n            VERIFY_FAIL();\r\n\r\n            return true;\r\n        };\r\n\r\n        constexpr auto testDir = L\"case-test\";\r\n        constexpr auto flags = wsl::windows::common::filesystem::c_case_sensitive_folders_only | LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE;\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { std::filesystem::remove_all(testDir); });\r\n\r\n        std::filesystem::create_directories(testDir);\r\n        setCaseSensitivity(testDir, false);\r\n        VERIFY_IS_FALSE(getCaseSensitivity(testDir));\r\n\r\n        wsl::windows::common::filesystem::EnsureCaseSensitiveDirectory(testDir, flags);\r\n        VERIFY_IS_TRUE(getCaseSensitivity(testDir));\r\n        setCaseSensitivity(testDir, false);\r\n\r\n        std::filesystem::create_directories(std::format(L\"{}/l1/l2/l3\", testDir));\r\n        setCaseSensitivity(std::format(L\"{}/l1/l2/l3\", testDir), false);\r\n        setCaseSensitivity(std::format(L\"{}/l1/l2\", testDir), false);\r\n\r\n        std::filesystem::create_directories(std::format(L\"{}/l1/l2/l3-other\", testDir));\r\n        setCaseSensitivity(std::format(L\"{}/l1/l2/l3-other\", testDir), false);\r\n\r\n        VERIFY_IS_FALSE(getCaseSensitivity(std::format(L\"{}/l1/l2\", testDir)));\r\n        VERIFY_IS_FALSE(getCaseSensitivity(std::format(L\"{}/l1/l2/l3\", testDir)));\r\n        VERIFY_IS_FALSE(getCaseSensitivity(std::format(L\"{}/l1/l2/l3-other\", testDir)));\r\n\r\n        wsl::windows::common::filesystem::EnsureCaseSensitiveDirectory(testDir, flags);\r\n\r\n        VERIFY_IS_TRUE(getCaseSensitivity(std::format(L\"{}/l1/l2/l3\", testDir)));\r\n        VERIFY_IS_TRUE(getCaseSensitivity(std::format(L\"{}/l1/l2/l3-other\", testDir)));\r\n        VERIFY_IS_TRUE(getCaseSensitivity(std::format(L\"{}/l1/l2\", testDir)));\r\n        VERIFY_IS_TRUE(getCaseSensitivity(std::format(L\"{}/l1\", testDir)));\r\n        VERIFY_IS_TRUE(getCaseSensitivity(testDir));\r\n    }\r\n\r\n    TEST_METHOD(AutomountRespectedWithElevation)\r\n    {\r\n        DistroFileChange distributionconf(L\"/etc/wsl.conf\", false);\r\n        distributionconf.SetContent(L\"[automount]\\nenabled=false\\n\");\r\n\r\n        DistroFileChange distributionFstab(L\"/etc/fstab\", false);\r\n        distributionFstab.SetContent(L\"\");\r\n        TerminateDistribution();\r\n\r\n        const auto nonElevatedToken = GetNonElevatedToken();\r\n        VERIFY_ARE_EQUAL(0u, LxsstuLaunchWsl(L\"echo dummy\", nullptr, nullptr, nullptr, nonElevatedToken.get()));\r\n        auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"mountpoint /mnt/c\", 32u);\r\n        VERIFY_ARE_EQUAL(out, L\"/mnt/c is not a mountpoint\\n\");\r\n    }\r\n\r\n    TEST_METHOD(FstabRespectedWithElevationAndAutomountDisabled)\r\n    {\r\n        DistroFileChange distributionconf(L\"/etc/wsl.conf\", false);\r\n        distributionconf.SetContent(L\"[automount]\\nenabled=false\\n\");\r\n\r\n        DistroFileChange distributionFstab(L\"/etc/fstab\", false);\r\n        distributionFstab.SetContent(L\"C:\\\\\\\\ /mnt/c drvfs metadata 0 0\");\r\n\r\n        TerminateDistribution();\r\n\r\n        const auto nonElevatedToken = GetNonElevatedToken();\r\n        VERIFY_ARE_EQUAL(0u, LxsstuLaunchWsl(L\"echo dummy\", nullptr, nullptr, nullptr, nonElevatedToken.get()));\r\n        auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"mountpoint /mnt/c\", 0u);\r\n        VERIFY_ARE_EQUAL(out, L\"/mnt/c is a mountpoint\\n\");\r\n    }\r\n\r\n    // This test case validates that the pipeline doesn't get stuck when both stdout & stdin are a pipe.\r\n    // See: https://github.com/microsoft/WSL/issues/12523\r\n    TEST_METHOD(DualPipeRelay)\r\n    {\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { DeleteFile(L\"compressed.gz\"); });\r\n\r\n        wsl::windows::common::SubProcess process{\r\n            nullptr, L\"cmd /c type \\\"C:\\\\Program Files\\\\WSL\\\\wsl.exe\\\" | wsl gzip > compressed.gz\"};\r\n\r\n        VERIFY_ARE_EQUAL(process.Run(), 0L);\r\n\r\n        wil::unique_handle file{CreateFile(L\"compressed.gz\", GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr)};\r\n        VERIFY_IS_TRUE(!!file);\r\n\r\n        wsl::windows::common::helpers::SetHandleInheritable(file.get());\r\n\r\n        // Validate that the relay didn't get stuck, and that its output is correct.\r\n        auto [expandedHash, _] = LxsstuLaunchWslAndCaptureOutput(L\"gzip -d -| md5sum -\", 0, file.get());\r\n        auto [expectedHash, __] =\r\n            LxsstuLaunchWslAndCaptureOutput(L\"cat \\\"$(wslpath 'C:\\\\Program Files\\\\WSL\\\\wsl.exe')\\\" |  md5sum - \");\r\n\r\n        VERIFY_ARE_EQUAL(expandedHash, expectedHash);\r\n    }\r\n\r\n    TEST_METHOD(EtcHosts)\r\n    {\r\n        {\r\n            // Verify that setting network.generateHosts=false doesn't create /etc/hosts\r\n\r\n            DistroFileChange wslConf{L\"/etc/wsl.conf\", false};\r\n            wslConf.SetContent(L\"[network]\\ngenerateHosts=false\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"rm /etc/hosts\"), 0L);\r\n\r\n            TerminateDistribution();\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"! test -f /etc/hosts\"), 0L);\r\n        }\r\n\r\n        {\r\n            // Verify that /etc/hosts generation is correct.\r\n            TerminateDistribution();\r\n\r\n            auto [content, _] = LxsstuLaunchWslAndCaptureOutput(L\"cat /etc/hosts\");\r\n            auto [hostname, domain] = wsl::windows::common::filesystem::GetHostAndDomainNames();\r\n\r\n            const auto lines = wsl::shared::string::Split(content, L'\\n');\r\n            VERIFY_IS_TRUE(lines.size() > 4);\r\n            VERIFY_ARE_EQUAL(lines[0] + L\"\\n\", WIDEN(LX_INIT_AUTO_GENERATED_FILE_HEADER));\r\n            VERIFY_ARE_EQUAL(lines[1], L\"# [network]\");\r\n            VERIFY_ARE_EQUAL(lines[2], L\"# generateHosts = false\");\r\n            VERIFY_ARE_EQUAL(lines[3], L\"127.0.0.1\\tlocalhost\");\r\n            VERIFY_ARE_EQUAL(lines[4], std::format(L\"127.0.1.1\\t{}.{}\\t{}\", hostname, domain, hostname));\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(ExecEmptyArg)\r\n    {\r\n        // See: https://github.com/microsoft/WSL/issues/12649\r\n\r\n        {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"--exec echo \\\"\\\"\");\r\n            VERIFY_ARE_EQUAL(out, L\"\\n\");\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n\r\n        {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"--exec echo foo \\\"\\\" bar\");\r\n            VERIFY_ARE_EQUAL(out, L\"foo  bar\\n\"); // Two spaces because echo adds one between each argument.\r\n            VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(DistroTimeout)\r\n    {\r\n        WslConfigChange config(LxssGenerateTestConfig() + L\"[general]\\ninstanceIdleTimeout=-1\");\r\n        auto distroId = GetDistributionId(LXSS_DISTRO_NAME_TEST_L);\r\n\r\n        auto getDistroState = [&]() {\r\n            wsl::windows::common::SvcComm service;\r\n\r\n            for (const auto& e : service.EnumerateDistributions())\r\n            {\r\n                if (wsl::shared::string::IsEqual(e.DistroName, LXSS_DISTRO_NAME_TEST_L))\r\n                {\r\n                    return e.State;\r\n                }\r\n            }\r\n\r\n            return LxssDistributionStateInvalid;\r\n        };\r\n\r\n        // Validate that distributions don't time out when timeout is -1\r\n        {\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"echo OK\"), 0L);\r\n\r\n            std::this_thread::sleep_for(std::chrono::seconds(20));\r\n            VERIFY_ARE_EQUAL(getDistroState(), LxssDistributionStateRunning);\r\n        }\r\n\r\n        // Validate that distributions time out when timeout value is > 0\r\n        {\r\n            config.Update(LxssGenerateTestConfig() + L\"[general]\\ninstanceIdleTimeout=2000\");\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"echo OK\"), 0L);\r\n\r\n            const auto deadline = std::chrono::steady_clock::now() + std::chrono::minutes(1);\r\n\r\n            unsigned long iterations = 0;\r\n            while (std::chrono::steady_clock::now() < deadline)\r\n            {\r\n                if (getDistroState() == LxssDistributionStateInstalled)\r\n                {\r\n                    LogInfo(\"Distribution stopped after %lu iterations\", iterations);\r\n                    return;\r\n                }\r\n\r\n                std::this_thread::sleep_for(std::chrono::seconds(1));\r\n                iterations++;\r\n            }\r\n\r\n            LogError(\"Distribution failed to time out after %lu iterations. State: %i\", iterations, getDistroState());\r\n            VERIFY_FAIL();\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(WslUpdate)\r\n    {\r\n        // Test the regular wsl --update logic\r\n        {\r\n            auto json =\r\n                LR\"(\r\n        {\r\n          \"name\": \"2.4.12\",\r\n          \"assets\": [\r\n            {\r\n              \"url\": \"http://arm-url\",\r\n              \"id\": 1,\r\n              \"name\": \"wsl.2.4.12.0.arm64.msi\"\r\n            },\r\n            {\r\n              \"url\": \"http://x64-url\",\r\n              \"id\": 2,\r\n              \"name\": \"wsl.2.4.12.0.x64.msi\"\r\n            }]})\";\r\n\r\n            auto [version, asset] = wsl::windows::common::wslutil::GetLatestGitHubRelease(false, json);\r\n\r\n            VERIFY_ARE_EQUAL(version, L\"2.4.12\");\r\n            VERIFY_ARE_EQUAL(asset.id, 2);\r\n            VERIFY_ARE_EQUAL(asset.url, L\"http://x64-url\");\r\n            VERIFY_ARE_EQUAL(asset.name, L\"wsl.2.4.12.0.x64.msi\");\r\n        }\r\n\r\n        // Test wsl --update --pre-release\r\n        {\r\n            auto json =\r\n                LR\"([\r\n        {\r\n          \"name\": \"2.4.12\"\r\n        },\r\n        {\r\n          \"name\": \"2.5.1\",\r\n          \"assets\": [\r\n            {\r\n              \"url\": \"http://arm-url\",\r\n              \"id\": 1,\r\n              \"name\": \"wsl.2.5.1.0.arm64.msi\"\r\n            },\r\n            {\r\n              \"url\": \"http://x64-url\",\r\n              \"id\": 2,\r\n              \"name\": \"wsl.2.5.1.0.x64.msi\"\r\n            }\r\n            ]\r\n        },\r\n        {\r\n          \"name\": \"2.4.13\"\r\n        }])\";\r\n\r\n            auto [version, asset] = wsl::windows::common::wslutil::GetLatestGitHubRelease(true, json);\r\n\r\n            VERIFY_ARE_EQUAL(version, L\"2.5.1\");\r\n            VERIFY_ARE_EQUAL(asset.id, 2);\r\n            VERIFY_ARE_EQUAL(asset.url, L\"http://x64-url\");\r\n            VERIFY_ARE_EQUAL(asset.name, L\"wsl.2.5.1.0.x64.msi\");\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(CustomModulesVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n#ifdef WSL_DEV_INSTALL_PATH\r\n\r\n        auto modulesPath = std::format(L\"{}\\\\modules.vhd\", WSL_DEV_INSTALL_PATH);\r\n        auto kernelPath = std::format(L\"{}\\\\kernel\", WSL_DEV_INSTALL_PATH);\r\n\r\n#else\r\n        auto modulesPath = std::format(L\"{}\\\\tools\\\\modules.vhd\", wsl::windows::common::wslutil::GetMsiPackagePath().value());\r\n        auto kernelPath = std::format(L\"{}\\\\tools\\\\kernel\", wsl::windows::common::wslutil::GetMsiPackagePath().value());\r\n\r\n#endif\r\n\r\n        // Create a copy of the modules vhd\r\n        auto testModules = std::filesystem::current_path() / \"test-modules.vhd\";\r\n\r\n        VERIFY_IS_TRUE(CopyFile(modulesPath.c_str(), testModules.c_str(), false));\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { std::filesystem::remove(testModules); });\r\n\r\n        auto cmd = std::format(\r\n            LR\"($acl = Get-Acl '{}' ; $acl.RemoveAccessRuleAll((New-Object System.Security.AccessControl.FileSystemAccessRule(\\\"Everyone\\\", \\\"Read\\\", \\\"None\\\", \\\"None\\\", \\\"Allow\\\"))); Set-Acl -Path '{}' -AclObject $acl)\",\r\n            testModules,\r\n            testModules);\r\n\r\n        LxsstuLaunchPowershellAndCaptureOutput(cmd);\r\n\r\n        // Update .wslconfig to point to the copied kernel\r\n        WslConfigChange config{LxssGenerateTestConfig({.kernel = kernelPath, .kernelModules = testModules.wstring()})};\r\n\r\n        // Validate that WSL starts correctly\r\n        auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"echo OK\");\r\n        VERIFY_ARE_EQUAL(out, L\"OK\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n    }\r\n\r\n    TEST_METHOD(BrokenDistroImport)\r\n    { // Validate that importing an empty tar fails.\r\n        {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"--import broken-test-distro . NUL\", -1);\r\n\r\n            VERIFY_ARE_EQUAL(\r\n                out,\r\n                L\"The imported file is not a valid Linux distribution.\\r\\nError code: \"\r\n                L\"Wsl/Service/RegisterDistro/WSL_E_NOT_A_LINUX_DISTRO\\r\\n\");\r\n\r\n            // TODO: Uncomment once SetVersionDebug is removed from the tests .wslconfig.\r\n            // VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n\r\n        // Validate that importing an empty tar via wsl --install fails.\r\n        {\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"--install --from-file NUL --name broken-test-distro\", -1);\r\n\r\n            VERIFY_ARE_EQUAL(\r\n                out,\r\n                L\"Installing: NUL\\r\\nThe imported file is not a valid Linux distribution.\\r\\nError code: \"\r\n                L\"Wsl/Service/RegisterDistro/WSL_E_NOT_A_LINUX_DISTRO\\r\\n\");\r\n            // TODO: Uncomment once SetVersionDebug is removed from the tests .wslconfig.\r\n            // VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n\r\n        // Validate that importing an empty VHDX fails.\r\n        if (LxsstuVmMode())\r\n        {\r\n            constexpr auto testVhd = L\"EmptyVhd.vhdx\";\r\n\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { DeleteFile(testVhd); });\r\n\r\n            LxsstuLaunchPowershellAndCaptureOutput(std::format(L\"New-Vhd {}  -SizeBytes 20MB\", testVhd));\r\n\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L\"--mount {} --vhd --bare\", testVhd)), 0L);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"mkfs.ext4 /dev/sde\"), 0L);\r\n            VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"--unmount\"), 0L);\r\n\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--import-in-place broken-test-distro {}\", testVhd), -1);\r\n\r\n            VERIFY_ARE_EQUAL(\r\n                out,\r\n                L\"The imported file is not a valid Linux distribution.\\r\\nError code: \"\r\n                L\"Wsl/Service/RegisterDistro/WSL_E_NOT_A_LINUX_DISTRO\\r\\n\");\r\n            // TODO: Uncomment once SetVersionDebug is removed from the tests .wslconfig.\r\n            // VERIFY_ARE_EQUAL(err, L\"\");\r\n        }\r\n\r\n        // Validate that tars containing /etc, but not /bin/sh are accepted.\r\n        if (LxsstuVmMode())\r\n        {\r\n            auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { LxsstuLaunchWsl(L\"--unregister empty-distro\"); });\r\n\r\n            DistroFileChange conf(L\"/etc/wsl.conf\", false);\r\n            conf.SetContent(L\"\");\r\n\r\n            auto [out, err] = LxsstuLaunchWslAndCaptureOutput(\r\n                L\"tar cf - /etc/wsl.conf | wsl.exe --install --from-file - --name empty-distro --no-launch \"\r\n                L\"--version 2\");\r\n        }\r\n    }\r\n\r\n    TEST_METHOD(ImportExportStdout)\r\n    {\r\n        constexpr auto test_distro = L\"import-test-distro\";\r\n        auto cleanup = wil::scope_exit_log(\r\n            WI_DIAGNOSTICS_INFO, [test_distro]() { LxsstuLaunchWsl(std::format(L\"--unregister {}\", test_distro)); });\r\n\r\n        // The below logline makes it easier to find the bsdtar output when debugging this test case.\r\n        fprintf(stderr, \"Starting ImportExportStdout test case\\n\");\r\n\r\n        auto commandLine = std::format(L\"cmd.exe /c wsl --export {} - | wsl --import {} . -\", LXSS_DISTRO_NAME_TEST_L, test_distro);\r\n\r\n        VERIFY_ARE_EQUAL(LxsstuRunCommand(commandLine.data()), 0L);\r\n\r\n        auto [out, err] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"-d {} echo ok\", test_distro));\r\n        VERIFY_ARE_EQUAL(out, L\"ok\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n    }\r\n\r\n    TEST_METHOD(EtcHostsParsing)\r\n    {\r\n        constexpr auto inputFileName = L\"test-etc-hosts.txt\";\r\n\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { DeleteFile(inputFileName); });\r\n\r\n        auto validate = [](const std::string& Input, const std::string& ExpectedOutput) {\r\n            wil::unique_handle inputFile{CreateFile(inputFileName, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, 0, nullptr)};\r\n\r\n            VERIFY_IS_TRUE(WriteFile(inputFile.get(), Input.c_str(), static_cast<DWORD>(Input.size()), nullptr, nullptr));\r\n\r\n            auto output = wsl::windows::common::filesystem::GetWindowsHosts(inputFileName);\r\n\r\n            VERIFY_ARE_EQUAL(ExpectedOutput, output);\r\n        };\r\n\r\n        validate(\"127.0.0.1 microsoft.com\", \"127.0.0.1\\tmicrosoft.com\\n\");\r\n        validate(\"\\xEF\\xBB\\xBF 127.0.0.1 microsoft.com\", \"127.0.0.1\\tmicrosoft.com\\n\"); // Validate that BOM headers are ignored.\r\n        validate(\"#Comment 127.0.0.1 microsoft.com windows.microsoft.com\\n#AnotherComment\", \"\");\r\n        validate(\r\n            \"#Comment 127.0.0.1 microsoft.com windows.microsoft.com\\n#AnotherComment\\n127.0.0.1 wsl.dev\", \"127.0.0.1\\twsl.dev\\n\");\r\n    }\r\n\r\n    // Validate that a distribution can be unregistered even if its BasePath doesn't exist.\r\n    // See https://github.com/microsoft/WSL/issues/13004\r\n    TEST_METHOD(BrokenDistroUnregister)\r\n    {\r\n        const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();\r\n        const auto distroKey = wsl::windows::common::registry::CreateKey(userKey.get(), L\"{baa405ef-1822-4bbe-84e2-30e4c6330d42}\");\r\n\r\n        auto revert = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {\r\n            wsl::windows::common::registry::DeleteKey(userKey.get(), L\"{baa405ef-1822-4bbe-84e2-30e4c6330d42}\");\r\n        });\r\n\r\n        wsl::windows::common::registry::WriteString(distroKey.get(), nullptr, L\"BasePath\", L\"C:\\\\DoesNotExit\");\r\n        wsl::windows::common::registry::WriteString(distroKey.get(), nullptr, L\"DistributionName\", L\"DummyBrokenDistro\");\r\n        wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"DefaultUid\", 0);\r\n        wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"Version\", LXSS_DISTRO_VERSION_2);\r\n        wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"State\", LxssDistributionStateInstalled);\r\n        wsl::windows::common::registry::WriteDword(distroKey.get(), nullptr, L\"Flags\", LXSS_DISTRO_FLAGS_VM_MODE);\r\n\r\n        auto [out, err] = LxsstuLaunchWslAndCaptureOutput(L\"--unregister DummyBrokenDistro\");\r\n\r\n        VERIFY_ARE_EQUAL(out, L\"The operation completed successfully. \\r\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n    }\r\n\r\n    // Validate that calling the binfmt interpreter with tty fd's but not controlling terminal doesn't display a warning.\r\n    // See https://github.com/microsoft/WSL/issues/13173.\r\n    TEST_METHOD(SetSidNoWarning)\r\n    {\r\n        auto [out, err] =\r\n            LxsstuLaunchWslAndCaptureOutput(L\"socat - 'EXEC:setsid --wait cmd.exe /c echo OK',pty,setsid,ctty,stderr\");\r\n\r\n        VERIFY_ARE_EQUAL(out, L\"OK\\r\\r\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n    }\r\n\r\n    TEST_METHOD(WslDebug)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        // Verify that hvsocket debug events are logged to dmesg.\r\n        WslConfigChange config(LxssGenerateTestConfig({.kernelCommandLine = L\"WSL_DEBUG=hvsocket\"}));\r\n        VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L\"dmesg | grep -iF 'vmbus_send_tl_connect_request'\"), 0L);\r\n    }\r\n\r\n    TEST_METHOD(CGroupv1)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        auto expectedMount = [](const char* path, const wchar_t* expected) {\r\n            auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L\"findmnt -ln '{}' || true\", path));\r\n\r\n            VERIFY_ARE_EQUAL(out, expected);\r\n        };\r\n\r\n        // Validate that cgroupv2 is mounted by default.\r\n        expectedMount(\"/sys/fs/cgroup\", L\"/sys/fs/cgroup cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate\\n\");\r\n\r\n        // Validate that setting cgroup=v1 causes unified cgroups to be mounted.\r\n        DistroFileChange wslConf(L\"/etc/wsl.conf\", false);\r\n        wslConf.SetContent(L\"[automount]\\ncgroups=v1\");\r\n\r\n        TerminateDistribution();\r\n\r\n        expectedMount(\r\n            \"/sys/fs/cgroup/unified\", L\"/sys/fs/cgroup/unified cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate\\n\");\r\n\r\n        // Validate that the cgroupv1 mounts are present.\r\n        expectedMount(\"/sys/fs/cgroup/cpu\", L\"/sys/fs/cgroup/cpu cgroup cgroup rw,nosuid,nodev,noexec,relatime,cpu\\n\");\r\n\r\n        // Validate that having cgroup_no_v1=all causes the distribution to fall back to v2.\r\n        WslConfigChange wslConfig(LxssGenerateTestConfig({.kernelCommandLine = L\"cgroup_no_v1=all\"}));\r\n\r\n        expectedMount(\"/sys/fs/cgroup/unified\", L\"\");\r\n        expectedMount(\"/sys/fs/cgroup\", L\"/sys/fs/cgroup cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate\\n\");\r\n\r\n        auto [dmesg, __] = LxsstuLaunchWslAndCaptureOutput(L\"dmesg\");\r\n        VERIFY_ARE_NOT_EQUAL(\r\n            dmesg.find(\r\n                L\"Distribution has cgroupv1 enabled, but kernel command line has cgroup_no_v1=all. Falling back to cgroupv2\"),\r\n            std::wstring::npos);\r\n    }\r\n\r\n    TEST_METHOD(InitPermissions)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L\"stat -c %a /init\");\r\n\r\n        VERIFY_ARE_EQUAL(out, L\"755\\n\");\r\n    }\r\n\r\n    TEST_METHOD(ExportImportVhd)\r\n    {\r\n        WSL2_TEST_ONLY();\r\n\r\n        WslShutdown();\r\n\r\n        constexpr auto vhdPath = L\"exported-test-distro.vhd\";\r\n        constexpr auto vhdxPath = L\"exported-test-distro.vhdx\";\r\n        constexpr auto exportedVhdPath = L\"exported-vhd.vhd\";\r\n        constexpr auto newDistroName = L\"imported-test-distro\";\r\n        auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {\r\n            LOG_IF_WIN32_BOOL_FALSE(DeleteFile(vhdPath));\r\n            LOG_IF_WIN32_BOOL_FALSE(DeleteFile(vhdxPath));\r\n            LOG_IF_WIN32_BOOL_FALSE(DeleteFile(exportedVhdPath));\r\n            LxsstuLaunchWsl(std::format(L\"--unregister {}\", newDistroName));\r\n        });\r\n\r\n        // Attempt to export the distribution to a .vhd (should fail).\r\n        auto [out, err] =\r\n            LxsstuLaunchWslAndCaptureOutput(std::format(L\"--export {} {} --format vhd\", LXSS_DISTRO_NAME_TEST_L, vhdPath), -1);\r\n        VERIFY_ARE_EQUAL(\r\n            out, L\"The specified file must have the .vhdx file extension.\\r\\nError code: Wsl/Service/WSL_E_EXPORT_FAILED\\r\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n\r\n        // Export the distribution to a .vhdx.\r\n        std::tie(out, err) =\r\n            LxsstuLaunchWslAndCaptureOutput(std::format(L\"--export {} {} --format vhd\", LXSS_DISTRO_NAME_TEST_L, vhdxPath));\r\n        VERIFY_ARE_EQUAL(out, L\"The operation completed successfully. \\r\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n\r\n        // Convert the .vhdx to .vhd.\r\n        LxsstuLaunchPowershellAndCaptureOutput(std::format(L\"Convert-VHD -Path '{}' -DestinationPath '{}'\", vhdxPath, vhdPath));\r\n\r\n        // Import a new distribution from the .vhd file.\r\n        std::tie(out, err) =\r\n            LxsstuLaunchWslAndCaptureOutput(std::format(L\"--import {} {} {} --vhd\", newDistroName, newDistroName, vhdPath));\r\n        VERIFY_ARE_EQUAL(out, L\"The operation completed successfully. \\r\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n\r\n        // Export the newly imported distribution to another .vhd file.\r\n        std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--export {} {} --format vhd\", newDistroName, exportedVhdPath));\r\n        VERIFY_ARE_EQUAL(out, L\"The operation completed successfully. \\r\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n\r\n        // Attempt to export to a .vhdx (should fail).\r\n        std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(std::format(L\"--export {} {} --format vhd\", newDistroName, vhdxPath), -1);\r\n        VERIFY_ARE_EQUAL(\r\n            out, L\"The specified file must have the .vhd file extension.\\r\\nError code: Wsl/Service/WSL_E_EXPORT_FAILED\\r\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n\r\n        // Attempt to import to a non VHD file.\r\n        auto tempFile = wsl::windows::common::filesystem::TempFile(\r\n            GENERIC_ALL, 0, CREATE_ALWAYS, wsl::windows::common::filesystem::TempFileFlags::None, L\"txt\");\r\n\r\n        tempFile.Handle.reset();\r\n\r\n        constexpr auto negativeVariationDistro = L\"negative-variation-distro\";\r\n        std::tie(out, err) = LxsstuLaunchWslAndCaptureOutput(\r\n            std::format(L\"--import {} {} {} --vhd\", negativeVariationDistro, negativeVariationDistro, tempFile.Path), -1);\r\n        VERIFY_ARE_EQUAL(\r\n            out,\r\n            L\"The specified file must have the .vhd or .vhdx file extension.\\r\\nError code: \"\r\n            L\"Wsl/Service/RegisterDistro/WSL_E_IMPORT_FAILED\\r\\n\");\r\n        VERIFY_ARE_EQUAL(err, L\"\");\r\n    }\r\n\r\n}; // namespace UnitTests\r\n} // namespace UnitTests\r\n"
  },
  {
    "path": "test/windows/lxsstest.h",
    "content": "/*++\r\n\r\nCopyright (c) Microsoft. All rights reserved.\r\n\r\nModule Name:\r\n\r\n    lxsstest.h\r\n\r\nAbstract:\r\n\r\n    Common definitions for lxss tests.\r\n\r\n--*/\r\n\r\n#pragma once\r\n\r\n#define STR2WSTR_INNER(str) L##str\r\n#define STR2WSTR(str) STR2WSTR_INNER(str)\r\n\r\n//\r\n// Logging macros\r\n//\r\n\r\n#define LogError(str, ...) \\\r\n    { \\\r\n        WEX::Logging::Log::Error(WEX::Common::String().Format(STR2WSTR(str), __VA_ARGS__)); \\\r\n    }\r\n\r\n#define LogInfo(str, ...) \\\r\n    { \\\r\n        WEX::Logging::Log::Comment(WEX::Common::String().Format(STR2WSTR(str), __VA_ARGS__)); \\\r\n    }\r\n\r\n#define LogWarning(str, ...) \\\r\n    { \\\r\n        WEX::Logging::Log::Warning(WEX::Common::String().Format(STR2WSTR(str), __VA_ARGS__)); \\\r\n    }\r\n\r\n#define LogPass(str, ...) \\\r\n    { \\\r\n        WEX::Logging::Log::Result(WEX::Logging::TestResults::Passed, WEX::Common::String().Format(STR2WSTR(str), __VA_ARGS__)); \\\r\n    }\r\n\r\n#define LogSkipped(str, ...) \\\r\n    { \\\r\n        WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped, WEX::Common::String().Format(STR2WSTR(str), __VA_ARGS__)); \\\r\n    }\r\n\r\n//\r\n// Helper macros\r\n//\r\n\r\n#define ALLOC(_size) HeapAlloc(GetProcessHeap(), 0, (_size))\r\n#define FREE(_ptr) WI_VERIFY(HeapFree(GetProcessHeap(), 0, (_ptr)) != FALSE)\r\n"
  },
  {
    "path": "test/windows/testplugin/CMakeLists.txt",
    "content": "set(SOURCES Plugin.cpp)\n\nadd_library(testplugin SHARED ${SOURCES})\nset_target_properties(testplugin PROPERTIES EXCLUDE_FROM_ALL FALSE)\ninclude_directories(testplugin ${CMAKE_CURRENT_SOURCE_DIR}/..)\ntarget_link_libraries(testplugin ${COMMON_LINK_LIBRARIES} legacy_stdio_definitions common)\ntarget_precompile_headers(testplugin REUSE_FROM common)"
  },
  {
    "path": "test/windows/testplugin/Plugin.cpp",
    "content": "/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    Plugin.cpp\n\nAbstract:\n\n    This file contains a test plugin.\n\n--*/\n\n#include \"precomp.h\"\n#include \"WslPluginApi.h\"\n\n#include \"PluginTests.h\"\n\nusing namespace wsl::windows::common::registry;\n\nstd::ofstream g_logfile;\nstd::optional<GUID> g_distroGuid;\n\nconst WSLPluginAPIV1* g_api = nullptr;\nPluginTestType g_testType = PluginTestType::Invalid;\n\nstd::optional<uint32_t> g_previousInitPid;\n\nstd::vector<char> ReadFromSocket(SOCKET socket)\n{\n    // Simplified error handling for the sake of the demo.\n    int result = 0;\n    int offset = 0;\n\n    std::vector<char> content(1024);\n    while ((result = recv(socket, content.data() + offset, 1024, 0)) > 0)\n    {\n        offset += result;\n        content.resize(offset + 1024);\n    }\n\n    content.resize(offset);\n    return content;\n}\n\nHRESULT OnVmStarted(const WSLSessionInformation* Session, const WSLVmCreationSettings* Settings)\n{\n    g_logfile << \"VM created (settings->CustomConfigurationFlags=\" << Settings->CustomConfigurationFlags << \")\" << std::endl;\n\n    if (g_testType == PluginTestType::FailToStartVm)\n    {\n        g_logfile << \"OnVmStarted: E_UNEXPECTED\" << std::endl;\n        return E_UNEXPECTED;\n    }\n    else if (g_testType == PluginTestType::FailToStartVmWithPluginErrorMessage)\n    {\n        g_logfile << \"OnVmStarted: E_UNEXPECTED\" << std::endl;\n        g_api->PluginError(L\"Plugin error message\");\n        return E_UNEXPECTED;\n    }\n    else if (WI_IsFlagSet(Settings->CustomConfigurationFlags, WSLUserConfigurationCustomKernel))\n    {\n        g_logfile << \"OnVmStarted: E_ACCESSDENIED\" << std::endl;\n        return E_ACCESSDENIED;\n    }\n    else if (g_testType == PluginTestType::Success)\n    {\n        // Get the current module's directory\n        std::filesystem::path modulePath = wil::GetModuleFileNameW(wil::GetModuleInstanceHandle()).get();\n        auto mountSource = modulePath.parent_path().wstring();\n\n        // Mount the folder with the linux binary in the vm\n        RETURN_IF_FAILED(\n            g_api->MountFolder(Session->SessionId, mountSource.c_str(), L\"/test-plugin/deep/folder\", true, L\"test-plugin-mount\"));\n\n        g_logfile << \"Folder mounted (\" << wsl::shared::string::WideToMultiByte(mountSource) << \" -> /test-plugin)\" << std::endl;\n\n        // Create a file with dummy content\n        std::ofstream file(mountSource + L\"\\\\test-file.txt\");\n        if (!file || !(file << \"OK\"))\n        {\n            g_logfile << \"Failed to open test-file.txt in: \" << wsl::shared::string::WideToMultiByte(mountSource) << std::endl;\n            return E_ABORT;\n        }\n\n        file.close();\n\n        // Launch the process\n        std::vector<const char*> arguments = {\"/bin/cat\", \"/test-plugin/deep/folder/test-file.txt\", nullptr};\n        wil::unique_socket socket;\n        RETURN_IF_FAILED(g_api->ExecuteBinary(Session->SessionId, arguments[0], arguments.data(), &socket));\n        g_logfile << \"Process created\" << std::endl;\n\n        // Read the socket output\n        auto output = ReadFromSocket(socket.get());\n        if (output != std::vector<char>{'O', 'K'})\n        {\n            g_logfile << \"Got unexpected output from bash\" << std::endl;\n            return E_ABORT;\n        }\n    }\n    else if (g_testType == PluginTestType::ApiErrors)\n    {\n        auto result = g_api->MountFolder(Session->SessionId, L\"C:\\\\DoesNotExit\", L\"/dummy\", true, L\"test-plugin-mount\");\n        if (result != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))\n        {\n            g_logfile << \"Unexpected error for MountFolder(): \" << result << std::endl;\n            return E_ABORT;\n        }\n\n        wil::unique_socket socket;\n        std::vector<const char*> arguments = {\"/bin/does-no-exist\", nullptr};\n        result = g_api->ExecuteBinary(Session->SessionId, arguments[0], arguments.data(), &socket);\n        if (result != E_FAIL)\n        {\n            g_logfile << \"Unexpected error for ExecuteBinary(): \" << result << std::endl;\n            return E_ABORT;\n        }\n\n        result = g_api->ExecuteBinary(0xcafe, arguments[0], arguments.data(), &socket);\n        if (result != RPC_E_DISCONNECTED)\n        {\n            g_logfile << \"Unexpected error for ExecuteBinary(): \" << result << std::endl;\n            return E_ABORT;\n        }\n\n        // Call PluginError asynchronously to verify that we handle this properly.\n\n        std::thread thread{[Session]() {\n            const auto result = g_api->PluginError(L\"Dummy\");\n\n            if (result != E_ILLEGAL_METHOD_CALL)\n            {\n                g_logfile << \"Unexpected error for async PluginError(): \" << result << std::endl;\n            }\n        }};\n\n        thread.join();\n\n        g_logfile << \"API error tests passed\" << std::endl;\n    }\n    else if (g_testType == PluginTestType::ErrorMessageStartVm)\n    {\n        auto result = g_api->PluginError(L\"StartVm plugin error message\");\n        if (FAILED(result))\n        {\n            g_logfile << \"Unexpected error from PluginError(): \" << result << std::endl;\n        }\n        g_logfile << \"OnVmStarted: E_FAIL\" << std::endl;\n        return E_FAIL;\n    }\n    else if (g_testType == PluginTestType::GetUsername)\n    {\n        try\n        {\n            auto info = wil::get_token_information<TOKEN_USER>(Session->UserToken);\n\n            DWORD size{};\n            DWORD domainSize{};\n            SID_NAME_USE use{};\n            LookupAccountSid(nullptr, info->User.Sid, nullptr, &size, nullptr, &domainSize, &use);\n\n            THROW_HR_IF(E_UNEXPECTED, size < 1);\n            std::wstring user(size - 1, '\\0');\n            std::wstring domain(domainSize - 1, '\\0');\n\n            THROW_IF_WIN32_BOOL_FALSE(LookupAccountSid(nullptr, info->User.Sid, user.data(), &size, domain.data(), &domainSize, &use));\n\n            g_logfile << \"Username: \" << wsl::shared::string::WideToMultiByte(domain) << \"\\\\\"\n                      << wsl::shared::string::WideToMultiByte(user) << std::endl;\n        }\n        catch (...)\n        {\n            g_logfile << \"OnVmStarted: get_token_information failed: \" << wil::ResultFromCaughtException() << std::endl;\n            return E_FAIL;\n        }\n\n        return S_OK;\n    }\n\n    return S_OK;\n}\n\nHRESULT OnVmStopping(const WSLSessionInformation* Session)\n{\n    g_logfile << \"VM Stopping\" << std::endl;\n\n    if (g_testType == PluginTestType::FailToStopVm)\n    {\n        g_logfile << \"OnVmStopping: E_UNEXPECTED\" << std::endl;\n        return E_UNEXPECTED;\n    }\n\n    return S_OK;\n}\n\nHRESULT OnDistroStarted(const WSLSessionInformation* Session, const WSLDistributionInformation* Distribution)\n{\n    g_logfile << \"Distribution started, name=\" << wsl::shared::string::WideToMultiByte(Distribution->Name)\n              << \", package=\" << wsl::shared::string::WideToMultiByte(Distribution->PackageFamilyName)\n              << \", PidNs=\" << Distribution->PidNamespace << \", InitPid=\" << Distribution->InitPid\n              << \", Flavor=\" << wsl::shared::string::WideToMultiByte(Distribution->Flavor)\n              << \", Version=\" << wsl::shared::string::WideToMultiByte(Distribution->Version) << std::endl;\n\n    if (g_testType == PluginTestType::FailToStartDistro)\n    {\n        g_logfile << \"OnDistroStarted: E_UNEXPECTED\" << std::endl;\n        return E_UNEXPECTED;\n    }\n    else if (g_testType == PluginTestType::SameDistroId)\n    {\n        if (g_distroGuid.has_value())\n        {\n            if (IsEqualGUID(g_distroGuid.value(), Distribution->Id))\n            {\n                g_logfile << \"OnDistroStarted: received same GUID\" << std::endl;\n            }\n            else\n            {\n                g_logfile << \"OnDistroStarted: received different GUID\" << std::endl;\n            }\n        }\n        else\n        {\n            g_distroGuid = Distribution->Id;\n        }\n    }\n    else if (g_testType == PluginTestType::ErrorMessageStartDistro)\n    {\n        g_logfile << \"OnDistroStarted: E_FAIL\" << std::endl;\n        g_api->PluginError(L\"StartDistro plugin error message\");\n        return E_FAIL;\n    }\n    else if (g_testType == PluginTestType::InitPidIsDifferent)\n    {\n        if (g_previousInitPid.has_value())\n        {\n            if (g_previousInitPid.value() != Distribution->InitPid)\n            {\n                g_logfile << \"Init's pid is different (\" << Distribution->InitPid << \" ! = \" << g_previousInitPid.value() << \")\" << std::endl;\n            }\n            else\n            {\n                g_logfile << \"Init's pid did not change (\" << g_previousInitPid.value() << \")\" << std::endl;\n                return E_FAIL;\n            }\n        }\n        else\n        {\n            g_previousInitPid = Distribution->InitPid;\n        }\n    }\n    else if (g_testType == PluginTestType::RunDistroCommand)\n    {\n        // Launch a process\n        std::vector<const char*> arguments = {\"/bin/sh\", \"-c\", \"cat /etc/issue.net\", nullptr};\n        wil::unique_socket socket;\n        RETURN_IF_FAILED(g_api->ExecuteBinaryInDistribution(Session->SessionId, &Distribution->Id, arguments[0], arguments.data(), &socket));\n        g_logfile << \"Process created\" << std::endl;\n\n        // Validate that the process actually ran inside the distro.\n        auto output = ReadFromSocket(socket.get());\n        const auto expected = \"Debian GNU/Linux 12\\n\";\n        if (std::string(output.begin(), output.end()) != expected)\n        {\n            g_logfile << \"Got unexpected output from bash: \" << std::string(output.begin(), output.end())\n                      << \", expected: \" << expected << std::endl;\n            return E_ABORT;\n        }\n\n        // Verify that failure to launch a process behaves properly.\n        arguments = {\"/does-not-exist\"};\n        g_logfile << \"Failed process launch returned:  \"\n                  << g_api->ExecuteBinaryInDistribution(Session->SessionId, &Distribution->Id, arguments[0], arguments.data(), &socket)\n                  << std::endl;\n\n        const GUID guid{};\n        g_logfile << \"Invalid distro launch returned:  \"\n                  << g_api->ExecuteBinaryInDistribution(Session->SessionId, &guid, arguments[0], arguments.data(), &socket) << std::endl;\n    }\n\n    return S_OK;\n}\n\nHRESULT OnDistroStopping(const WSLSessionInformation* Session, const WSLDistributionInformation* Distribution)\n{\n    g_logfile << \"Distribution Stopping, name=\" << wsl::shared::string::WideToMultiByte(Distribution->Name)\n              << \", package=\" << wsl::shared::string::WideToMultiByte(Distribution->PackageFamilyName)\n              << \", PidNs=\" << Distribution->PidNamespace << \", Flavor=\" << wsl::shared::string::WideToMultiByte(Distribution->Flavor)\n              << \", Version=\" << wsl::shared::string::WideToMultiByte(Distribution->Version) << std::endl;\n\n    if (g_testType == PluginTestType::FailToStopDistro)\n    {\n        g_logfile << \"OnDistroStopping: E_UNEXPECTED\" << std::endl;\n        return E_UNEXPECTED;\n    }\n    else if (g_testType == PluginTestType::SameDistroId && g_distroGuid.has_value())\n    {\n        if (!IsEqualGUID(g_distroGuid.value(), Distribution->Id))\n        {\n            g_logfile << \"OnDistroStarted: received different GUID\" << std::endl;\n        }\n    }\n\n    return S_OK;\n}\n\nHRESULT OnDistributionRegistered(const WSLSessionInformation* Session, const WslOfflineDistributionInformation* Distribution)\n{\n    g_logfile << \"Distribution registered, name=\" << wsl::shared::string::WideToMultiByte(Distribution->Name)\n              << \", package=\" << wsl::shared::string::WideToMultiByte(Distribution->PackageFamilyName)\n              << \", Flavor=\" << wsl::shared::string::WideToMultiByte(Distribution->Flavor)\n              << \", Version=\" << wsl::shared::string::WideToMultiByte(Distribution->Version) << std::endl;\n\n    if (g_testType == PluginTestType::FailToRegisterUnregisterDistro)\n    {\n        g_logfile << \"OnDistributionRegistered: E_UNEXPECTED\" << std::endl;\n        return E_UNEXPECTED;\n    }\n\n    return S_OK;\n}\n\nHRESULT OnDistributionUnregistered(const WSLSessionInformation* Session, const WslOfflineDistributionInformation* Distribution)\n{\n    g_logfile << \"Distribution unregistered, name=\" << wsl::shared::string::WideToMultiByte(Distribution->Name)\n              << \", package=\" << wsl::shared::string::WideToMultiByte(Distribution->PackageFamilyName)\n              << \", Flavor=\" << wsl::shared::string::WideToMultiByte(Distribution->Flavor)\n              << \", Version=\" << wsl::shared::string::WideToMultiByte(Distribution->Version) << std::endl;\n\n    if (g_testType == PluginTestType::FailToRegisterUnregisterDistro)\n    {\n        g_logfile << \"OnDistributionUnregistered: E_UNEXPECTED\" << std::endl;\n        return E_UNEXPECTED;\n    }\n\n    return S_OK;\n}\n\nEXTERN_C __declspec(dllexport) HRESULT WSLPLUGINAPI_ENTRYPOINTV1(const WSLPluginAPIV1* Api, WSLPluginHooksV1* Hooks)\n{\n    try\n    {\n        const auto key = OpenTestRegistryKey(KEY_READ);\n\n        const std::wstring outputFile = ReadString(key.get(), nullptr, c_logFile);\n        g_logfile.open(outputFile);\n        THROW_HR_IF(E_UNEXPECTED, !g_logfile);\n\n        g_testType = static_cast<PluginTestType>(ReadDword(key.get(), nullptr, c_testType, static_cast<DWORD>(PluginTestType::Invalid)));\n        THROW_HR_IF(E_INVALIDARG, static_cast<DWORD>(g_testType) <= 0 || static_cast<DWORD>(g_testType) > static_cast<DWORD>(PluginTestType::GetUsername));\n\n        g_logfile << \"Plugin loaded. TestMode=\" << static_cast<DWORD>(g_testType) << std::endl;\n        g_api = Api;\n        Hooks->OnVMStarted = &OnVmStarted;\n        Hooks->OnVMStopping = &OnVmStopping;\n        Hooks->OnDistributionStarted = &OnDistroStarted;\n        Hooks->OnDistributionStopping = &OnDistroStopping;\n        Hooks->OnDistributionRegistered = &OnDistributionRegistered;\n        Hooks->OnDistributionUnregistered = &OnDistributionUnregistered;\n\n        if (g_testType == PluginTestType::FailToLoad)\n        {\n            g_logfile << \"OnLoad: E_UNEXPECTED\" << std::endl;\n            return E_UNEXPECTED;\n        }\n        else if (g_testType == PluginTestType::PluginRequiresUpdate)\n        {\n            g_logfile << \"OnLoad: WSL_E_PLUGINREQUIRESUPDATE\" << std::endl;\n\n            WSL_PLUGIN_REQUIRE_VERSION(9999, 99, 99, Api);\n        }\n    }\n    catch (...)\n    {\n        const auto error = wil::ResultFromCaughtException();\n        if (g_logfile)\n        {\n            g_logfile << \"Failed to initialize plugin, \" << error << std::endl;\n        }\n\n        return error;\n    }\n    return S_OK;\n}"
  },
  {
    "path": "tools/FormatSource.ps1.in",
    "content": "<#\r\n# Copyright (c) Microsoft Corporation. All rights reserved.\r\n# Licensed under the MIT License. See LICENSE in the project root for license information.\r\n\r\nthis file work is a derivative of https://github.com/microsoft/MixedReality-GraphicsTools-Unreal/blob/main/Tools/scripts/Common.psm1\r\n#>\r\n\r\n<#\r\n.SYNOPSIS\r\n    Run clang-format on all source files.\r\n.PARAMETER Path\r\n    Path to the directory (or file) to be processed recursively. By default scans the entire repo.\r\n.PARAMETER ClangFormat\r\n    Path to clang-format executable, e.g. \"C:\\Tools\\clang-format.exe\"\r\n.PARAMETER ModifiedOnly\r\n    Scan only files modified in current git checkout.\r\n.PARAMETER Staged\r\n    Check only files staged for commit\r\n.PARAMETER ChangesFile\r\n    Scan only files listed in provided txt file (one path per line). Paths need to be relative to repo root.\r\n.PARAMETER Verify\r\n    Whether to fail if files are not formatted (instead of applying changes).\r\n.PARAMETER NoFail\r\n    Do not set RC=1 when errors found, i.e. only report errors in output.\r\n#>\r\n[CmdletBinding()]\r\nparam (\r\n    [string]$Path = $null,\r\n    [boolean]$DefaultPackagesConfig = $false,\r\n    [string]$ClangFormat = $null,\r\n    [boolean]$ModifiedOnly = $True,\r\n    [boolean]$Staged = $false,\r\n    [string]$ChangesFile = $null,\r\n    [boolean]$Verify = $false,\r\n    [boolean]$NoFail = $false\r\n)\r\n\r\n# Only check source files\r\n$FilePatterns = \"\\.(h|cpp|hpp|c|hxx)$\"\r\n\r\n$IgnoreFolders = \"(out|.git|.vs|.vscode|bin|CMakeFiles|generated|debug|x64|packages|_deps)$\"\r\n\r\n# Handle both execution methods: direct PowerShell and powershell.exe script invocation\r\nif ([string]::IsNullOrEmpty($PSScriptRoot)) {\r\n    $RepoRoot = (Get-Location).Path\r\n} else {\r\n    $RepoRoot = (Resolve-Path \"$PSScriptRoot\")\r\n}\r\n\r\n<#\r\n.SYNOPSIS\r\n    Given the path to the list of raw git changes, returns an array of\r\n    those changes rooted in the git root directory.\r\n.DESCRIPTION\r\n    For example, the raw git changes will contain lines like:\r\n\r\n    Assets/File.cs\r\n\r\n    This function will return a list of paths that look like (assuming\r\n    that RepoRoot is C:\\repo):\r\n\r\n    C:\\repo\\Assets\\File.cs\r\n#>\r\nfunction GetChangedFiles {\r\n    [CmdletBinding()]\r\n    param(\r\n        [string]$Filename,\r\n        [string]$RepoRoot\r\n    )\r\n    process {\r\n        $rawContent = Get-Content -Path $Filename\r\n        $processedContent = @()\r\n        foreach ($line in $rawContent) {\r\n            $joinedPath = Join-Path -Path $RepoRoot -ChildPath $line\r\n            $processedContent += $joinedPath\r\n        }\r\n        $processedContent\r\n    }\r\n}\r\n\r\nif ([string]::IsNullOrEmpty($Path)) {\r\n    $Path = $RepoRoot\r\n}\r\n\r\n$ModifiedFiles = $null\r\n\r\nif ($ChangesFile) {\r\n    if (-not (Test-Path -Path $ChangesFile -PathType Leaf)) {\r\n        Write-Host -ForegroundColor Red \"ChangesFile not found: $ChangesFile\"\r\n        Write-Host \"Checking all source files\"\r\n    }\r\n    else {\r\n        $ModifiedFiles = GetChangedFiles -Filename $ChangesFile -RepoRoot $RepoRoot\r\n        if (($null -eq $ModifiedFiles) -or ($ModifiedFiles.Count -eq 0)) {\r\n            Write-Host -ForegroundColor Green \"No modified files to format.\"\r\n            exit 0\r\n        }\r\n    }\r\n}\r\nelseif ($ModifiedOnly -or $Staged) {\r\n    $ModifiedFiles = @()\r\n    Push-Location -Path $RepoRoot\r\n    $Success = $False\r\n    try {\r\n        if ($Staged) {\r\n            $Status = (& git diff-index --cached --name-only HEAD)\r\n            $Success = ($LASTEXITCODE -eq 0)\r\n            $Status | ForEach-Object {\r\n                $FullPath = Resolve-Path $_ -ErrorAction SilentlyContinue\r\n                $FileName = Split-Path -Leaf -Path $FullPath\r\n                if ($FileName -match $FilePatterns) {\r\n                    $ModifiedFiles += $FullPath\r\n                }\r\n            }\r\n        }\r\n        else {\r\n            $Status = (& git status --porcelain)\r\n            $Success = ($LASTEXITCODE -eq 0)\r\n            $Status | ForEach-Object {\r\n                $FullPath = (Resolve-Path ($_.Trim() -split \" \", 2)[-1] -ErrorAction SilentlyContinue)\r\n                $FileName = Split-Path -Leaf -Path $FullPath\r\n                if ($FileName -match $FilePatterns) {\r\n                    $ModifiedFiles += $FullPath\r\n                }\r\n            }\r\n        }\r\n    }\r\n    catch {\r\n        # empty\r\n    }\r\n    if (-not $Success) {\r\n        Write-Host -ForegroundColor Red \"Could not get the list of modified files. Check if git is configured correctly.\"\r\n        exit 1\r\n    }\r\n    Pop-Location\r\n    if (($null -eq $ModifiedFiles) -or ($ModifiedFiles.Count -eq 0)) {\r\n        Write-Host -ForegroundColor Green \"No modified files to format.\"\r\n        exit 0\r\n    }\r\n}\r\n\r\n$ClangFormat = \"${LLVM_INSTALL_DIR}/clang-format.exe\"\r\nif (-not (Test-Path -Type Leaf -Path $ClangFormat)) {\r\n    # The artifact directory is only known by cmake. Let cmake drop a link/copy where we can find it.\r\n    $ClangFormat = \"$PSScriptRoot\\clang-format.exe\"\r\n}\r\n\r\nif (-not (Test-Path -Type Leaf -Path $ClangFormat)) {\r\n    Write-Host -ForegroundColor Red \"clang-format.exe not found, use cmake to grab llvm package or specify directly with -ClangFormat\"\r\n    exit 1\r\n}\r\n\r\nfunction Format-Directory {\r\n    [CmdletBinding()]\r\n    param (\r\n        [Parameter(Mandatory = $True)]\r\n        [string]$Path,\r\n        [Parameter(Mandatory = $True)]\r\n        [string]$ClangFormat,\r\n        [string]$RepoRoot = $null,\r\n        [AllowEmptyString()][string]$FilePatterns = $null,\r\n        [string[]]$ModifiedFiles = $null,\r\n        [boolean]$Verify = $false\r\n    )\r\n    process {\r\n        if (-not (Test-Path -Path $Path)) {\r\n            Write-Host -ForegroundColor Red \"Item not found: $Path\"\r\n            return $False\r\n        }\r\n        if ($null -eq $FilePatterns) {\r\n            $FilePatterns = \"\"\r\n        }\r\n        $Path = Resolve-Path $Path\r\n        $Success = $True\r\n        $FilesToFormat = @()\r\n        if ((Get-Item -Path $Path) -is [System.IO.DirectoryInfo]) {\r\n            Get-ChildItem -Path $Path -File `\r\n            | Where-Object { $_.Name -match $FilePatterns } `\r\n            | ForEach-Object {\r\n                $FilePath = \"$Path\\$($_.Name)\"\r\n                if (($null -eq $ModifiedFiles) -or ($ModifiedFiles -contains $FilePath)) {\r\n                    if (!($FilePath -match \"Intermediate\")) {\r\n                        $FilesToFormat += $FilePath\r\n                    }\r\n                }\r\n            }\r\n            Get-ChildItem -Path $Path -Directory `\r\n            | Where-Object { $_.Name -notmatch $IgnoreFolders } `\r\n            | ForEach-Object {\r\n                $SubResult = (Format-Directory -Path \"$Path\\$($_.Name)\" `\r\n                        -ClangFormat $ClangFormat `\r\n                        -RepoRoot $RepoRoot `\r\n                        -FilePatterns $FilePatterns `\r\n                        -ModifiedFiles $ModifiedFiles `\r\n                        -Verify $Verify)\r\n                $Success = $SubResult -and $Success\r\n            }\r\n        }\r\n        else {\r\n            $FilesToFormat += $Path\r\n        }\r\n        $FilesToFormat | ForEach-Object {\r\n            if ($Verify) {\r\n                Write-Host \"[clang-format] Checking formatting: $_\"\r\n                & $ClangFormat --style=file -Werror --dry-run $_\r\n            }\r\n            else {\r\n                Write-Host \"[clang-format] Formatting $_\"\r\n                & $ClangFormat --style=file -Werror -i $_\r\n            }\r\n            $Success = (0 -eq $LASTEXITCODE) -and $Success\r\n        }\r\n        return $Success\r\n    }\r\n}\r\n\r\n$Success = (Format-Directory -Path $Path `\r\n        -ClangFormat $ClangFormat `\r\n        -FilePatterns $FilePatterns `\r\n        -ModifiedFiles $ModifiedFiles `\r\n        -RepoRoot $RepoRoot `\r\n        -Verify $Verify)\r\n\r\nif ($Success) {\r\n    Write-Host \"Done.\"\r\n    exit 0\r\n}\r\nelse {\r\n    Write-Host -ForegroundColor Red \"Errors found (see output). Please make sure to resolve all issues before opening a Pull Request.\"\r\n    Write-Host -ForegroundColor Red \"Formatting can be applied by running:\"\r\n    Write-Host -ForegroundColor Red \"   powershell $PSCommandPath -ModifiedOnly ```$false [-Path <path to file or directory>]\"\r\n    if ($NoFail) {\r\n        exit 0  # do not prevent commit when used in pre-commit hook\r\n    }\r\n    exit 1\r\n}"
  },
  {
    "path": "tools/SetupClangFormat.bat",
    "content": "@ECHO OFF\r\nrem Copyright (c) Microsoft Corporation.\r\nrem Licensed under the MIT License.\r\n\r\nrem This script sets up git hooks for clang-format\r\n\r\npushd %~dp0%\r\ngit config --local core.hooksPath tools/hooks\r\npopd"
  },
  {
    "path": "tools/build-bundle.bat",
    "content": "@echo off\n\nset \"build_type=%1\"\nif NOT DEFINED build_type set \"build_type=Debug\"\n\ndel CMakeCache.txt &^\nrd /q /s _deps &^\ncmake -A arm64 . -DCMAKE_BUILD_TYPE=\"%build_type%\" &&^\ncmake --build . --config \"%build_type%\" -- -m &&^\ndel CMakeCache.txt &&^\nrd /q /s _deps &&^\ncmake . -A x64 -DBUILD_BUNDLE=TRUE -DCMAKE_BUILD_TYPE=\"%build_type%\" &&^\ncmake --build . --config \"%build_type%\" -- -m\n"
  },
  {
    "path": "tools/create-dev-cert.ps1",
    "content": "[CmdletBinding()]\nparam (\n    [string]$OutputPath\n)\n\n$ErrorActionPreference = \"Stop\"\nSet-StrictMode -Version Latest\n# Set PSModulePath environment variable explicitly to work with PowerShell 7\n$env:PSModulePath = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine')\n\n$cert = New-SelfSignedCertificate -Type Custom -Subject \"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US\" -KeyUsage DigitalSignature -FriendlyName \"WSL Dev cert\" -CertStoreLocation \"Cert:\\CurrentUser\\My\" -TextExtension @(\"2.5.29.37={text}1.3.6.1.5.5.7.3.3\", \"2.5.29.19={text}\") -HashAlgorithm \"SHA256\" -NotAfter (Get-Date).AddYears(10)\n\nExport-PfxCertificate -cert \"Cert:\\CurrentUser\\My\\$($cert.Thumbprint)\" -FilePath $OutputPath -Password (New-Object System.Security.SecureString) | Out-Null\nRemove-Item \"Cert:\\CurrentUser\\My\\$($cert.Thumbprint)\"  -DeleteKey | Out-Null\n\nWrite-Host \"Created new dev certificate in $OutputPath\""
  },
  {
    "path": "tools/create-initrd.ps1",
    "content": "# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n\n# Creates a CPIO newc format initramfs archive containing a single file named \"init\"\n# with mode 0100755 (rwxr-xr-x), uid 0, gid 0.\n\n[CmdletBinding()]\nparam (\n    [Parameter(Mandatory)][string]$InputFile,\n    [Parameter(Mandatory)][string]$OutputFile\n)\n\n$ErrorActionPreference = \"Stop\"\nSet-StrictMode -Version Latest\n\nfunction Write-Pad([System.IO.Stream]$Stream)\n{\n    $remainder = $Stream.Position % 4\n    if ($remainder -ne 0)\n    {\n        $pad = [byte[]]::new(4 - $remainder)\n        $Stream.Write($pad, 0, $pad.Length)\n    }\n}\n\nfunction Write-CpioEntry([System.IO.Stream]$Stream, [byte[]]$NameBytes, [byte[]]$FileData, [int]$Mode, [uint32]$Mtime)\n{\n    $header = \"070701\" +                        # header magic\n              \"00000001\" +                      # inode\n              (\"{0:X8}\" -f $Mode) +             # mode\n              \"00000000\" +                      # uid\n              \"00000000\" +                      # gid\n              \"00000001\" +                      # nlink\n              (\"{0:X8}\" -f $Mtime) +            # mtime\n              (\"{0:X8}\" -f $FileData.Length) +  # filesize\n              \"00000000\" +                      # devmajor\n              \"00000000\" +                      # devminor\n              \"00000000\" +                      # rdevmajor\n              \"00000000\" +                      # rdevminor\n              (\"{0:X8}\" -f $NameBytes.Length) + # namesize\n              \"00000000\"                        # check\n    $headerBytes = [System.Text.Encoding]::ASCII.GetBytes($header)\n    $Stream.Write($headerBytes, 0, $headerBytes.Length)\n    $Stream.Write($NameBytes, 0, $NameBytes.Length)\n    Write-Pad $Stream\n    if ($FileData.Length -gt 0)\n    {\n        $Stream.Write($FileData, 0, $FileData.Length)\n        Write-Pad $Stream\n    }\n}\n\n$data = [System.IO.File]::ReadAllBytes($InputFile)\n$name = [System.Text.Encoding]::ASCII.GetBytes(\"init`0\")\n$mtime = [uint32][System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()\n\n$out = [System.IO.File]::Create($OutputFile)\ntry\n{\n    Write-CpioEntry $out $name $data 0x81ED $mtime  # S_IFREG | 0755\n    $trailer = [System.Text.Encoding]::ASCII.GetBytes(\"TRAILER!!!`0\")\n    Write-CpioEntry $out $trailer ([byte[]]::new(0)) 0 0\n}\nfinally\n{\n    $out.Close()\n}\n"
  },
  {
    "path": "tools/deploy/deploy-to-host.ps1",
    "content": "#Requires -RunAsAdministrator\n\n[cmdletbinding(PositionalBinding = $false)]\nparam (\n    [ValidateSet(\"Debug\", \"Release\")][string]$BuildType = \"Debug\",\n    [string]$BuildOutputPath = [string](Get-Location),\n    [string]$PackageCertPath = $null,\n    [parameter(ValueFromRemainingArguments = $true)]\n    [string[]]$MsiArgs\n)\n\n$ErrorActionPreference = \"Stop\"\n\n$processorArchitecture = (Get-ItemProperty 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment').PROCESSOR_ARCHITECTURE\n$Platform = switch -Wildcard ($processorArchitecture) {\n    '*ARM64*' { 'arm64' }\n    '*AMD64*' { 'X64' }\n    default   { throw \"Failed to determine system architecture: $processorArchitecture\" }\n}\n\n$PackagePath = \"$BuildOutputPath\\bin\\$Platform\\$BuildType\\wsl.msi\"\n\n# msiexec.exe doesn't like symlinks, so use the canonical path\n$Target = (Get-ChildItem $PackagePath).Target\nif ($Target)\n{\n    $PackagePath = $Target\n}\n\nWrite-Host -ForegroundColor Green \"Installing: $PackagePath \"\n\n$MSIArguments = @(\n    \"/i\"\n    $PackagePath\n    \"/qn\"\n    \"/norestart\"\n)\n\nif ($MsiArgs)\n{\n    $MSIArguments += $MsiArgs\n}\n\n$exitCode = (Start-Process -Wait \"msiexec.exe\" -ArgumentList $MSIArguments -NoNewWindow -PassThru).ExitCode\nif ($exitCode -Ne 0)\n{\n    Write-Host \"Failed to install package: $exitCode\"\n    exit 1\n}\n\nWrite-Host -ForegroundColor Green \"Package $PackagePath installed successfully\"\n"
  },
  {
    "path": "tools/deploy/deploy-to-vm.ps1",
    "content": "[cmdletbinding(PositionalBinding = $false)]\nparam (\n    [string]$VmName,\n    [string]$Username,\n    [string]$Password,\n    [ValidateSet(\"X64\", \"arm64\")][string]$Platform = \"X64\",\n    [ValidateSet(\"Debug\", \"Release\")][string]$BuildType = \"Debug\",\n    [string]$RemoteTempFolder = \"C:\\\\\",\n    [string]$BuildOutputPath = [string](Get-Location),\n    [parameter(ValueFromRemainingArguments = $true)]\n    [string[]]$ExtraMsiArgs\n)\n\n$ErrorActionPreference = \"Stop\"\n\nif ([string]::IsNullOrEmpty($Password)) {\n    $SecurePassword = New-Object System.Security.SecureString\n}\nelse {\n    $SecurePassword = ConvertTo-SecureString \"$Password\" -AsPlainText -Force\n}\n\n$Credential = New-Object System.Management.Automation.PSCredential(\"$Username\", $SecurePassword)\n$Session = New-PSSession -VMName $VmName -Credential $Credential\n\n$Package = $BuildOutputPath + \"/bin/$Platform/$BuildType/wsl.msi\"\nCopy-Item -ToSession $Session -Path $Package -Destination \"$RemoteTempFolder\" -Force\n\n\nInvoke-Command -Session $Session -ScriptBlock {\n\n    $MSIArguments = @(\n    \"/i\"\n    \"$using:RemoteTempFolder\\wsl.msi\"\n    \"/qn\"\n    \"/norestart\"\n    )\n    \n    if ($using:ExtraMsiArgs)\n    {\n        $MSIArguments += $using:ExtraMsiArgs\n    }\n\n    $exitCode = (Start-Process -Wait \"msiexec.exe\" -ArgumentList $MSIArguments -NoNewWindow -PassThru).ExitCode\n    if ($exitCode -Ne 0)\n    {\n        Write-Host \"Failed to install package: $exitCode\"\n        exit 1\n    }\n\n    Write-Host \"Package $using:Package successfully deployed on $using:VmName\"\n}\n\nRemove-PSSession -Session $Session"
  },
  {
    "path": "tools/devops/create-change.py",
    "content": "import click\nimport requests\nimport json\nfrom git import Repo\n\nCOMMITTER_EMAIL = 'noreply@microsoft.com'\nREPO = 'microsoft/wsl'\n\n@click.command()\n@click.argument('repo_path', required=True)\n@click.argument('token', required=True)\n@click.argument('committer', required=True)\n@click.argument('message', required=True)\n@click.argument('branch', required=True)\n@click.option('--debug', default=False, is_flag=True)\ndef main(repo_path: str, token: str, committer: str, message: str, branch: str, debug: bool):\n    try:\n        repo = Repo(repo_path)\n\n        changed_files = [e.a_path for e in repo.index.diff(None)]\n\n        if not changed_files:\n            print('No files changed, skipping')\n            return\n\n        print(f'Changed files: {\",\".join(changed_files)}')\n\n\n        repo.create_head(branch).checkout()\n\n        with repo.config_writer() as config:\n            config.set_value(\"user\", \"email\", COMMITTER_EMAIL)\n            config.set_value(\"user\", \"name\", committer)\n\n        repo.git.commit('-a', m=message)\n        repo.git.push('origin', branch)\n\n        headers = {'Accept': 'application/vnd.github+json', 'Authorization': 'Bearer ' + token}\n\n        body = {\n            'title': message,\n            'description': 'Automated change',\n            'head': branch,\n            'base': 'master'\n        }\n\n        response = requests.post(f'https://api.github.com/repos/{REPO}/pulls', headers=headers, data=json.dumps(body), timeout=30)\n        response.raise_for_status()\n\n        print(f'Created pull request: {response.json()[\"html_url\"]}')\n\n    except:\n        if debug:\n            import pdb\n            import traceback\n            traceback.print_exc()\n            pdb.post_mortem()\n\n        raise\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "tools/devops/create-release.py",
    "content": "# pip install click requests gitpython\n\nimport click\nimport requests\nimport sys\nimport re\nimport os\nimport backoff\nimport functools\nfrom git import Repo\nfrom urllib.parse import urlparse\n\n@click.command()\n@click.argument('version', required=True)\n@click.argument('assets', default=None, nargs=-1)\n@click.option('--previous', default=None)\n@click.option('--max-message-lines', default=1)\n@click.option('--publish', is_flag=True, default=False)\n@click.option('--no-fetch', is_flag=True, default=False)\n@click.option('--github-token', default=None)\n@click.option('--use-current-ref', is_flag=True, default=False)\n@click.option('--auto-release-notes', is_flag=True, default=False)\ndef main(version: str, previous: str, max_message_lines: int, publish: bool, assets: list, no_fetch: bool, github_token: str, use_current_ref: bool, auto_release_notes: bool):\n    if publish:\n        # Click provides an empty tuple when no assets are passed. Guard against both\n        # an explicit None (older Click versions / direct invocation) and an empty\n        # collection so we do not accidentally create a release without payload.\n        if not assets:\n            raise RuntimeError('--publish requires at least one asset')\n\n        if github_token is None:\n            raise RuntimeError('--publish requires --github_token')\n\n    for e in assets:\n        if not os.path.exists(e):\n            raise RuntimeError(f'Asset not found: {e}')\n\n    if previous is None:\n        previous = get_previous_release(parse_tag(version))\n\n    current_ref = '<current-commit>' if use_current_ref else version\n    print(f'Creating release notes for: {previous} -> {current_ref}', file=sys.stderr)\n\n    changes = ''\n\n    if not auto_release_notes:\n        for e in get_change_list(None if use_current_ref else version, previous, not no_fetch):\n\n            # Detect attached github issues\n            issues = find_github_issues(e.message)\n            pr_description, pr_number = get_github_pr_message(github_token, e.message)\n            if pr_description is not None:\n                issues = issues.union(find_github_issues(pr_description))\n\n            if github_token is not None:\n                issues = filter_github_issues(issues, github_token)\n\n            if len(issues) > 1:\n                print(f'WARNING: found more than 1 github issues in message: {e.message}. Issues: {issues}', file=sys.stderr)\n\n            message = e.message[:-1] if e.message.endswith('\\n') else e.message\n\n            # Shrink the message if it's too long\n            lines = message.split('\\n')\n            message = '\\n'.join([e for e in lines if e][:max_message_lines])\n\n            # Get rid of the github PR #\n            if pr_number is not None:\n                message = message.replace(f'(#{pr_number})', '')\n\n            # Append to the changes (chr(92) == '\\n')\n            message = f'{message.replace(chr(92), \"\")} (solves {\",\".join(issues)})' if issues else message\n            changes += f'* {message}\\n'\n\n    if publish:\n        publish_release(version, changes, assets, auto_release_notes, github_token)\n    else:\n        print(f'\\n{changes}')\n\n@backoff.on_exception(backoff.expo, (requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.RequestException), max_time=600)\ndef get_github_pr_message(token: str, message: str) -> str:\n    match = re.search(r'\\(#([0-9]+)\\)', message)\n    if match is None:\n        print(f'Warning: failed to extract GitHub PR number from message: {message}')\n        return None, None\n\n    pr_number = match.group(1)\n    headers = {'Accept': 'application/vnd.github+json',\n               'Authorization': 'Bearer ' + token,\n               'X-GitHub-Api-Version': '2022-11-28'}\n\n    response = requests.get(f'https://api.github.com/repos/microsoft/wsl/pulls/{pr_number}', timeout=30, headers=headers)\n    response.raise_for_status()\n\n    return response.json()['body'], pr_number\n\n\ndef parse_tag(tag: str) -> list:\n    version = tag.split('.')\n    if len(version) != 3:\n        raise RuntimeError(f'Unexpected tag: {version}')\n\n    return tuple(int(e) for e in version)\n\ndef get_previous_release(version: tuple) -> str:\n    response = requests.get('https://api.github.com/repos/Microsoft/WSL/releases');\n    response.raise_for_status()\n\n    # Find the most recent release with a lower version number than this one\n    versions = [parse_tag(e['tag_name']) for e in response.json()]\n    previous_versions = [e for e in versions if e < version]\n\n    if not previous_versions:\n        raise RuntimeError(f'No previous found on GitHub. Response: {response.json()}')\n\n    return '.'.join(str(e) for e in max(previous_versions))\n\ndef find_github_issues(message: str):\n    # Look for urls first\n    urls = [urlparse(e) for e in re.findall(r\"https?://[^\\s^\\)]+\", message)]\n\n    issue_urls = [e for e in urls if e.hostname == 'github.com' and e.path.lower().startswith('/microsoft/wsl/issues/')]\n\n    issues = set(['#' + e.path.split('/')[-1] for e in issue_urls])\n\n    # Then add issue numbers\n    for e in re.findall(r\"#\\d+\", message):\n        issues.add(e)\n\n    return issues\n\ndef filter_github_issues(issues: list, token: str) -> list:\n\n    @functools.cache\n    def is_pr(number: str):\n        headers = {\n                   'Accept': 'application/vnd.github+json',\n                   'Authorization': 'Bearer ' + token,\n                   'X-GitHub-Api-Version': '2022-11-28'\n                  }\n\n        response = requests.get(f'https://api.github.com/repos/microsoft/wsl/issues/{number}', timeout=30, headers=headers)\n        response.raise_for_status()\n\n        return response.json().get('pull_request') is not None\n\n    return [e for e in issues if not is_pr(e.replace('#', ''))]\n\n\ndef get_change_list(version: str, previous: str, fetch: bool) -> list:\n    repo = Repo('.')\n\n    # Fetch origin first\n    if fetch and version is not None:\n        repo.remote('origin').fetch(previous)\n\n    # Find both current and previous version tags\n    previous_tag = repo.tag(previous)\n\n    # Set current ref\n    current_ref = repo.tag(version) if version is not None else repo.head\n\n    # Find common root between tags\n    merge_bases = repo.merge_base(previous_tag.commit, current_ref)\n    if len(merge_bases) == 0:\n        raise RuntimeError(f'No merge base found between {version} and {previous}')\n    elif len(merge_bases) > 1:\n        raise RuntimeError(f'Multiple merge bases found between {version} and {previous}')\n\n    # List commits between tags\n    for e in repo.iter_commits(rev=current_ref):\n        if e == merge_bases[0]:\n            return\n\n        yield e\n\n    raise RuntimeError(f'Tag {previous} is not an ancestor of {version}')\n\n\n@backoff.on_exception(backoff.expo, (requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.RequestException), max_time=600)\ndef publish_release(version: str, changes: str, assets: list, auto_release_notes: bool, token: str):\n    print(f'Creating private GitHub release for: {version}', file=sys.stderr)\n\n    # First create the release\n    headers = {'Accept': 'application/vnd.github+json',\n               'Authorization': 'Bearer ' + token,\n               'X-GitHub-Api-Version': '2022-11-28'}\n\n    content = {'tag_name': version,\n               'target_commitish': 'master',\n               'name': version,\n               \"draft\":True ,\n               'prerelease':True ,\n               'generate_release_notes': auto_release_notes}\n\n    if changes:\n        content['body'] = changes\n\n    response = requests.post('https://api.github.com/repos/microsoft/wsl/releases', json=content, headers=headers)\n    response.raise_for_status()\n\n    release = response.json()\n    print(f'Created release: {release[\"url\"]}', file=sys.stderr)\n\n    for asset in assets:\n        with open(asset, 'rb') as asset_content:\n            asset_size = os.path.getsize(asset)\n\n            # Append asset to the release assets\n            headers['Content-Type'] = 'application/octet-stream'\n\n            response = requests.post(f'https://uploads.github.com/repos/microsoft/wsl/releases/{release[\"id\"]}/assets?name={os.path.basename(asset)}', headers=headers, data=asset_content)\n            response.raise_for_status()\n\n            print(f'Attached asset: {asset} to release: {response.json()[\"url\"]}', file=sys.stderr)\n\n    print(f'The release has been created. Navigate to {release[\"html_url\"]} to edit the release notes and publish it', file=sys.stderr)\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "tools/devops/find-release.py",
    "content": "# pip install click gitpython\n\n# Usage: \n\n# python tools\\devops\\find-release.py src/windows/wsl/main.cpp 10\n# python tools\\devops\\find-release.py  --commit 4ec2def9f3f588c06308cb31cb758e5369761aa5\n\nimport click\nimport re\n\nfrom git import Repo, Commit, Tag\n\n@click.command()\n@click.argument('ref', required=True, type=str)\n@click.argument('line', default=None, required=False, type=int)\n@click.option('--commit', is_flag=True)\ndef main(ref: str, line: int, commit: bool):\n    repo = Repo('.')\n\n    repo.remote('origin').fetch()\n    tags = list_tags(repo)\n    \n    if commit:\n        change = repo.commit(ref)\n        click.secho(f'{ref}: {find_tag_for_commit(repo, tags, change)}', fg='green', bold=True)\n    else:\n    \n        for entry in repo.blame_incremental('HEAD', ref):\n            if line >= entry.linenos.start and line <= entry.linenos.stop:\n                click.secho(f'Changed in {find_tag_for_commit(repo, tags, entry.commit)} by {entry.commit.hexsha}', fg='green', bold=True)\n\n                print(repo.git.diff(entry.commit, entry.commit.parents[0], ref) + '\\n')\n    \n\ndef list_tags(repo: Repo) -> list:\n    return [e for e in sorted(repo.tags, key=lambda e: e.path) if re.match('refs/tags/[0-9]+\\\\.[0-9]+\\\\.[0-9]+', e.path)]\n\ndef find_tag_for_commit(repo: Repo, tags: list, commit: Commit) -> str:\n    for e in tags:\n        merge_bases = repo.merge_base(e, commit)\n        \n        if any(e == commit for e in merge_bases):\n            return e.path.replace('refs/tags/', '')\n\n    return \"[No tag found]\"\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "tools/devops/requirements.txt",
    "content": "azure-devops==7.1.0b4\nclick==8.1.3\ngitpython==3.1.41\nbackoff==2.2.1"
  },
  {
    "path": "tools/devops/validate-copyright-headers.py",
    "content": "import glob\nimport sys\nimport re\nimport os.path\n\nEXPECTED_HEADER = '.*Copyright \\\\(c\\\\) Microsoft.*All rights reserved.*'.casefold()\nEXTENSIONS = ['.c', '.cpp', '.cxx', '.h', '.hpp', '.hxx']\n\ndef has_header(path: str) -> bool:\n    with open(path, 'rb') as fd:\n        lines = fd.read().decode('utf-8', 'ignore').replace('\\r', '').split('\\n')\n\n    in_multiline_comment = False\n\n    for e in lines[:50]:\n        if e.startswith('/*'): # Simplified comment parsing\n            in_multiline_comment = True\n\n        if '*/' in e:\n            in_multiline_comment = False\n\n        if e.strip().startswith('//') or in_multiline_comment:\n            if re.match(EXPECTED_HEADER, e.casefold()):\n                return True\n\n\n    return False\n\ndef is_source_file(path: str) -> bool:\n    return any(e for e in EXTENSIONS if path.casefold().endswith(e))\n\ndef generate_header(path: str):\n    with open(path, 'rb') as fd:\n        content = fd.read().decode('utf-8', 'ignore')\n\n    header = f'''/*++\n\nCopyright (c) Microsoft. All rights reserved.\n\nModule Name:\n\n    {os.path.basename(path)}\n\nAbstract:\n\n    TODO\n\n--*/\n'''.replace('\\n', '\\r\\n')\n\n    with open(path, 'wb') as fd:\n        fd.write((header + content).encode('utf-8'))\n\n\ndef main(path: str, fix: bool):\n    files = glob.glob(f'{path}/**', recursive=True)\n\n    source_files = [e for e in files if is_source_file(e)]\n    print(f'Validate copyright headers for {len(source_files)} files')\n\n    missing_headers = [e for e in source_files if not has_header(e)]\n\n    if missing_headers:\n        if fix:\n            for e in missing_headers:\n                generate_header(e)\n\n        files = \"\\n\".join(missing_headers)\n        print(f'{len(missing_headers)} files are missing a copyright header:\\n{files}')\n        sys.exit(1)\n\n\nif __name__ == '__main__':\n    path = '.'\n    fix = False\n\n    for e in sys.argv[1:]:\n        if e == '--fix':\n            fix = True\n        else:\n            path = e\n\n    main(path, fix)"
  },
  {
    "path": "tools/devops/validate-localization.py",
    "content": "# pip install click\n\nimport sys\nimport os\nimport re\nimport xml.etree.ElementTree\nfrom xml.sax.saxutils import escape\n\ndef validate_line_endings(path: str, content: bytes):\n    line = 0\n    for i in range(0, len(content)):\n        if content[i] == ord('\\n'):\n            if i == 0 or content[i - 1] != ord('\\r'):\n                raise RuntimeError(f'Incorrect line ending (expected CRLF) in {path}:{line}')\n\n            line += 1\n\ndef get_strings_from_file(path: str, check_line_endings: bool) -> list:\n    with open(path, 'rb') as fd:\n        content = fd.read()\n\n    if check_line_endings:\n        validate_line_endings(path, content)\n\n    content = xml.etree.ElementTree.fromstring(content.decode())\n\n    result = {}\n\n    for e in content.findall('./data'):\n        nodes = list(e.iter())\n\n        text = next(n.text for n in nodes if n.tag == 'value')\n        comment = next((n.text for n in nodes if n.tag == 'comment'), '')\n        name = e.get('name')\n\n        if name in result:\n            raise RuntimeError(f'error: String \"{name}\" is duplicated in file \"{path}\"')\n\n        result[name] = text, comment\n\n    return result\n\ndef cut_insert(insert: str) -> str:\n    index = 1\n    while index < len(insert) and insert[index] != '$':\n        index += 1\n\n    if index + 1 >= len(insert) or insert[index] != '$':\n        raise RuntimeError(f'Invalid insert: {insert}')\n\n    index += 1\n\n    if insert[index] in ['h', 'l']:\n        index += 1\n\n    return insert[0:index]\n\ndef get_inserts_in_string(string: str) -> int:\n    return string.replace('{{', '').count('{')\n\ndef get_file_string_inserts(strings: list) -> dict:\n    return {name: get_inserts_in_string(value[0]) for name, value in strings.items()}\n\ndef validate_resource(baseline: dict, path: str):\n    strings = get_strings_from_file(path, False)\n    resource = get_file_string_inserts(strings)\n\n    result = True\n    for string, inserts in baseline.items():\n        if string not in resource:\n            print(f'warning: string {string} found in baseline but not in {path}')\n            continue\n\n        if inserts != resource[string]:\n            print(f'error: Different inserts found for string {string}. Baseline: {inserts}, {path}: {resource[string]}')\n            result = False\n\n        comment = strings[string][1]\n        locked_strings = re.findall('{Locked=\"([^}]*)\"}', comment, re.DOTALL)\n        for e in locked_strings:\n            if e not in strings[string][0]:\n                print(f'error: locked string \"{e}\" not found in string {string}: {strings[string][0]}')\n                result = False\n\n    return result\n\ndef find_argument_end(argument: str) -> int:\n    for i in range(len(argument)):\n        if not argument[i].isalnum() and argument[i] != '-' and argument[i] != '%':\n\n            # Include one extra character after the argument so that nothing gets added after the argument\n            # See: https://github.com/microsoft/WSL/issues/10756\n            return min(len(argument), i + 1)\n\n    return len(argument)\n\ndef get_locked_strings(name: str, string: str) -> tuple[list, bool]:\n    strings = []\n\n    def add_arguments(prefix):\n        for i, e in enumerate(string.split(prefix)):\n            if i == 0 and not e.startswith(prefix):\n                continue\n\n            stop_index = find_argument_end(e)\n            if stop_index > 1:\n                strings.append(prefix + e[:stop_index])\n\n    add_arguments('--')\n\n    if 'wslconfig'.lower() in name.lower():\n        add_arguments('/') # Edge case for wslconfig\n\n    if '.wslconfig'.lower() in string.lower():\n        strings.append('.wslconfig')\n\n    return strings, '{}' in string\n\ndef generate_string_comment(arguments: list, uses_insert: bool) -> str:\n    insert_rule = '{FixedPlaceholder=\"{}\"}' if uses_insert else ''\n    return insert_rule + ''.join(f'{{Locked=\"{e}\"}}' for e in arguments) + 'Command line arguments, file names and string inserts should not be translated'\n\ndef validate_comments(strings: dict):\n    result = True\n\n    comments_changes = {}\n    for name, (string, comment) in strings.items():\n        arguments, uses_insert = get_locked_strings(name, string)\n\n        if len(arguments) == 0 and not uses_insert:\n            continue # No command line arguments or inserts in this string\n\n        # For the sake of simplicity this logic makes the assumption that comments\n        # are always in the same order as of the original string\n        expected_comment = generate_string_comment(arguments, uses_insert)\n        if not expected_comment in comment:\n            comments_changes[name] = (comment, expected_comment)\n            print(f'Incorrect comment for string {name}. Expected comment: <comment>{expected_comment}</comment>')\n            result = False\n\n    return result, comments_changes\n\ndef fix_comments(comments: dict, path: str, strings: dict):\n    with open(path, 'rb') as fd:\n        content = fd.read()\n\n    missed = 0\n    for name, (comment, fixed_comment) in comments.items():\n        comment = comment.replace('\\n', '\\r\\n')\n        matches = content.count(comment.encode())\n        if comment and matches == 1:\n            content = content.replace(comment.encode(), fixed_comment.encode())\n            continue\n        elif not comment or matches == 0:\n            # Try to add the comment if it doesn't exist at all\n            reconstructed_xml = f'''  <data name=\"{name}\" xml:space=\"preserve\">\n    <value>{escape(strings[name][0])}</value>\n'''\n            suffix = '  </data>'\n            pattern = (reconstructed_xml + suffix).replace('\\n', '\\r\\n').encode()\n            if content.count(pattern) == 1:\n                content = content.replace(\n                pattern,\n                f'{reconstructed_xml}    <comment>{fixed_comment}</comment>\\n{suffix}'.replace('\\n', '\\r\\n').encode())\n\n                continue\n\n        click.secho(f\"Couldn't find unique match for comment (name={name}): {comment}. It needs to be manually replaced with: {fixed_comment}\")\n        missed += 1\n\n    with open(path, 'wb') as fd:\n        fd.write(content)\n\n    click.secho(f'Updated file: {path}. {missed} comments need manual changes', fg='green' if missed == 0 else 'yellow', bold=True)\n\n\ndef run(resource_folder: str, baseline_language: str, fix: bool):\n    baseline_file = f'{resource_folder}/{baseline_language}/Resources.resw'\n\n    strings = get_strings_from_file(baseline_file, True)\n    baseline = get_file_string_inserts(strings)\n\n    result, comments = validate_comments(strings)\n    for language in os.listdir(resource_folder):\n        path = f'{resource_folder}/{language}/Resources.resw'\n        print(f'Validating inserts in {path}')\n        result &= validate_resource(baseline, path)\n\n    if fix and comments:\n        fix_comments(comments, baseline_file, strings)\n\n    sys.exit(0 if result else 1)\n\n\nif __name__ == '__main__':\n    if len(sys.argv) == 3: # Hack to work around pip install errors in the build pipeline\n        run(sys.argv[1], sys.argv[2], False)\n    else:\n        import click\n\n        @click.command()\n        @click.argument('resource-folder', default='localization/strings')\n        @click.argument('baseline-language', default='en-us')\n        @click.option('--fix', is_flag=True)\n        def main(resource_folder: str, baseline_language: str, fix: bool):\n            run(resource_folder, baseline_language, fix)\n        \n        main()"
  },
  {
    "path": "tools/devops/version_functions.ps1",
    "content": "Set-StrictMode -Version Latest\n\nfunction Get-Current-Commit-Hash()\n{\n    return ([string](git log -1 --pretty=%h)).Trim()\n}\n\nfunction Get-VersionInfo\n{\n    [CmdletBinding()]\n    param([Parameter(Mandatory = $true)]$Nightly)\n\n    $ErrorActionPreference = \"Stop\"\n\n    if ($Nightly)\n    {\n        $suffix = 'nightly'\n    }\n    else\n    {\n        $suffix = 'build'\n    }\n\n    $output = git.exe describe --tags --match *.*.* --abbrev=1\n    if ($LastExitCode -ne 0)\n    {\n        throw \"git describe exited with error status: $LastExitCode. Make sure the main branch and tags are available.\"\n    }\n\n    $versionInfo = $output.split('-')\n    if ($versionInfo.Length -lt 1)\n    {\n        throw \"Unexpected output from git describe: $output\"\n    }\n\n    $version = $versionInfo[0]\n    if ($versionInfo.Length -lt 2)\n    {\n        $revision = 0 # This path is taken when the commit is directly on a tag\n    }\n    else\n    {\n        $revision = $versionInfo[1]\n    }\n\n    $result = @{\n                'MsixVersion' = \"$version.$revision\"\n                'NugetVersion' = \"$version-$suffix{0:d3}\" -f $revision\n              }\n\n    return $result\n}\n"
  },
  {
    "path": "tools/generateLocalizationHeader.ps1",
    "content": "# Script to generate Localization.h from resources.resw\r\n\r\n\r\nparam( [Parameter(Mandatory=$true)] $OutFile)\r\n\r\n$ErrorActionPreference = \"Stop\"\r\n\r\n$resourceFolder = \"$($PSScriptRoot)\\..\\localization\\strings\"\r\n$defaultLanguage = \"en-US\"\r\n$languages = @($defaultLanguage) + (Get-ChildItem -Path $resourceFolder | % { $_.name} | Where { $_ -Ne $defaultLanguage })\r\n\r\n$content = @\"\r\n// Copyright (C) Microsoft Corporation. All rights reserved.\r\n//\r\n// This file is generated by generateLocalizationHeader.ps1, do not edit it manually.\r\n\r\n#pragma once\r\n\r\n#include <vector>\r\n#include <stringshared.h>\r\n\r\nnamespace wsl::shared {\r\n\r\nclass Localization\r\n{\r\npublic:\r\n\r\n#ifdef WIN32\r\n\r\n#define ARGS std::make_wformat_args\r\nusing TChar = wchar_t;\r\n\r\n#else\r\n\r\n#define ARGS std::make_format_args\r\nusing TChar = char;\r\n\r\n#define TEXT(X) X\r\n\r\n#endif\r\n\r\nusing TString = std::basic_string<TChar>;\r\n\r\nenum class Options\r\n{\r\n    Default = 0,\r\n    DontImpersonate = 1\r\n};\r\n\"@\r\n\r\nfunction getArgumentsFromInserts\r\n{\r\n    param(\r\n        [string]$Name,\r\n        [string]$Content)\r\n\r\n    [System.Collections.ArrayList]$Arguments = @()\r\n\r\n    $Content = $Content -Replace '{{', ''\r\n    \r\n    return ([regex]::Matches($Content, \"{\" )).count\r\n}\r\n\r\nfunction generateEntry\r\n{\r\n    param(\r\n        [string]$Name,\r\n        [hashtable]$AllStrings)\r\n\r\n    $content = $AllStrings[$defaultLanguage][$Name]\r\n    $map = \"static const std::vector<std::pair<TString, const TChar*>> strings {`r`n              {TEXT(`\"$defaultLanguage`\"), TEXT(R`\"($content)`\")}\"\r\n\r\n    foreach ($language in $AllStrings.Keys)\r\n    {\r\n        if ($language -eq $defaultLanguage)\r\n        {\r\n            continue\r\n        }\r\n\r\n        $strings = $AllStrings[$language]\r\n\r\n        if (!$strings.ContainsKey($Name))\r\n        {\r\n            Write-Host \"Warning: string $Name not found in language: $language\"\r\n            continue\r\n        }\r\n        \r\n        $map += \",`r`n              {TEXT(`\"$language`\"), TEXT(R`\"($($strings[$Name]))`\")}\"\r\n    }\r\n\r\n    $map += \"}\"\r\n\r\n\r\n    $ArgumentsCount = getArgumentsFromInserts -Name $Name -Content $Content\r\n    if ($ArgumentsCount -eq 0)\r\n    {\r\n        return '\r\n        /* Message: {1}*/\r\n        static inline TString {0}(Options options = Options::Default)\r\n        {{\r\n            {2};\r\n\r\n            return LookupString(strings, options);\r\n        }}\r\n        ' -f $Name, $Content.split(\"`n\")[0], $map\r\n    }\r\n    else\r\n    {\r\n        $ArgumentTypes = [string]::Join(', ', ((1..$ArgumentsCount) |% {\"typename T$_\"}))\r\n        $ArgumentHeaders = [string]::Join(', ', ((1..$ArgumentsCount) |% {\"const T$_& arg$_\"}))\r\n        $Arguments = [string]::Join(', ', ((1..$ArgumentsCount) |% {\"arg$_\"}))\r\n        return '\r\n        /* Message: {4}*/\r\n        \r\n        template <{1}>\r\n        static TString {0}({2}, Options options = Options::Default)\r\n        {{\r\n            {5};\r\n\r\n            auto message = LookupString(strings, options);\r\n            return std::vformat(message, ARGS({3}));\r\n        }}\r\n        ' -f $Name, $ArgumentTypes, $ArgumentHeaders, $Arguments, $Content.split(\"`n\")[0], $map\r\n\r\n    }\r\n}\r\n\r\nfunction loadStrings\r\n{\r\n    param([string]$language)\r\n    $xml = [xml](Get-Content \"$resourceFolder/$language/Resources.resw\" -raw)\r\n\r\n    $strings = @{}\r\n\r\n    foreach($entry in $xml.root.data)\r\n    {\r\n        if ($strings.ContainsKey($entry.name))\r\n        {\r\n            throw \"Entry $($entry.name) duplicated in resource for: $language\"\r\n        }\r\n\r\n        # Skip generating strings intended for WSL Settings. These strings are\r\n        # unused in native C++ code. Further, their naming is incompatible with\r\n        # the function names generated for the localization header (contain '.').\r\n        if ($entry.name.StartsWith(\"Settings_\"))\r\n        {\r\n            continue\r\n        }\r\n\r\n        $strings[$entry.name] = $entry.value\r\n    }\r\n\r\n    return $strings\r\n}\r\n\r\n$allStrings = @{}\r\n\r\nforeach ($language in $languages)\r\n{\r\n    $allStrings[$language] = loadStrings -language $language\r\n}\r\n\r\nforeach($entry in $allStrings[$defaultLanguage].Keys)\r\n{\r\n    $content += generateEntry -Name $entry -allStrings $allStrings\r\n}\r\n\r\n$content += @\"\r\nprivate:\r\n    static const TChar* LookupString(const std::vector<std::pair<TString, const TChar*>>& strings, Options options);\r\n};\r\n\r\n} // namespace wsl::windows::common\r\n\r\n#undef ARGS\r\n\"@\r\n\r\n\r\nSet-Content -Path $OutFile -Encoding UTF8 $content"
  },
  {
    "path": "tools/hooks/pre-commit",
    "content": "#!/bin/sh\r\n\r\n# run only on Windows\r\nif [[ \"$OSTYPE\" == \"msys\" ]]; then\r\n\texec powershell.exe -NoProfile -ExecutionPolicy Bypass -Command \".\\FormatSource.ps1\" -Staged '$true' -Verify '$true' -NoFail '$true'\r\nfi\r\n\r\nexit 0"
  },
  {
    "path": "tools/test/CloudTest-Setup.bat",
    "content": "@echo off\n\nrem Required to make cloudtest use our version of taef\nsetx /M CloudTestWorkerCustomTaefExe \"%1\\taef\\te.exe\"\n\nmkdir %2\\WexLogFileOutput\nmklink /D %1\\WexLogFileOutput %2\\WexLogFileOutput\n\nrem These need to run before the package is installed\npowershell -NoProfile -Command \"Add-MpPreference -ExclusionPath 'C:\\Program Files\\WSL'\"\npowershell -NoProfile -Command \"Add-MpPreference -ExclusionProcess @('wsl.exe', 'wslg.exe', 'wslconfig.exe', 'wslrelay.exe', 'wslhost.exe', 'msrdc.exe', 'wslservice.exe', 'msal.wsl.proxy.exe', 'te.processhost.exe')\""
  },
  {
    "path": "tools/test/build-test-distro.ps1",
    "content": "<#\n.SYNOPSIS\n    Takes in an exported distribution, installs dependencies for testing, cleans unnecessary components, and exports it for later use.\n.PARAMETER Base\n    Base distribution to use.\n.PARAMETER OutputTarPath\n    Path to write the test distro .tar to.\n#>\n\n[CmdletBinding()]\nParam ($Base)\n\n$ErrorActionPreference = \"Stop\"\nSet-StrictMode -Version Latest\n\nfunction Run {\n    [CmdletBinding()]\n    param([scriptblock]$cmd)\n\n    Invoke-Command -ScriptBlock $cmd\n    if ($lastexitcode -ne 0) {\n        throw (\"$cmd failed with exit code: \" + $lastexitcode)\n    }\n}\n\nfunction RunInDistro {\n    [CmdletBinding()]\n    param([string]$cmd)\n    & cmd /c \"wsl.exe -d test_distro -- $cmd\"\n    if ($lastexitcode -ne 0) {\n        throw (\"wsl.exe -d test_distro -- $cmd failed with exit code: \" + $lastexitcode)\n    }\n}\n\n$git_version = (git describe --tags).split('-')\n$version = \"$($git_version[0])-$($git_version[1])\"\n\necho \"Building test_distro version: $version\"\n\nRun { wsl.exe --install $Base --name test_distro --version 2 --no-launch }\n\nRunInDistro(\"apt update\")\nRunInDistro(\"apt install daemonize libmount-dev genisoimage dosfstools make gcc socat systemd libpam-systemd bind9-dnsutils xz-utils bzip2 -f -y --no-install-recommends\")\nRunInDistro(\"apt purge cpio isc-dhcp-client isc-dhcp-common nftables rsyslog vim vim-tiny vim-common whiptail xxd init genisoimage tasksel kmod mawk udev cron -y -f --allow-remove-essential\")\nRunInDistro(\"apt-get autopurge -y\")\nRunInDistro(\"apt clean\")\nRunInDistro(\"umount /usr/lib/wsl/drivers\")\nRunInDistro(\"umount /usr/lib/wsl/lib\")\nRunInDistro(\"rm -rf /etc/wsl-distribution.conf /etc/wsl.conf /usr/share/doc/* /var/lib/apt/lists/* /var/log/* /var/cache/debconf/* /var/cache/ldconfig/* /usr/lib/wsl /usr/share/{gdb,vim,zsh,man}\")\nRunInDistro('rm -rf -- $(ls /usr/share/locale ^| grep -vE \"en|locale.alias\")')\nRunInDistro(\"rm /usr/lib/systemd/user/{systemd-tmpfiles-setup.service,systemd-tmpfiles-clean.timer,systemd-tmpfiles-clean.service}\")\nRunInDistro(\"rm /usr/lib/systemd/system/{systemd-tmpfiles-setup-dev.service,systemd-tmpfiles-setup.service,systemd-tmpfiles-clean.timer,systemd-tmpfiles-clean.service} /lib/systemd/system/{kmod-static-nodes.service,kmod.service,sysinit.target.wants/kmod-static-nodes.service}\")\nRun { wsl.exe --export test_distro \"test_distro.tar\" }\nRun { wsl.exe xz -e9 \"test_distro.tar\" }\n\n& \"$PSScriptRoot/../../_deps/nuget.exe\" pack Microsoft.WSL.TestDistro.nuspec -Properties  version=$version"
  },
  {
    "path": "tools/test/copy_and_build_tests.ps1",
    "content": "<#\n.SYNOPSIS\n    Copies linux unit tests to a given WSL distribution and builds them.\n.PARAMETER WslTestDirPath\n    The absolute path to the location of the \\wsl-build\\test\\linux directory.\n.PARAMETER DistroName\n    The name of the WSL distribution to copy and build the tests inside of.\n    Defaults to \"test_distro\"\n#>\n\nparam (\n    [Parameter( ValueFromPipeline = $true,\n        ValueFromPipelineByPropertyName = $true,\n        Mandatory = $true,\n        ParameterSetName = \"WslTestDirPath\",\n        HelpMessage = \"Path to \\wsl-build\\test\\linux\\ location\")]\n    [Parameter( ParameterSetName = \"CopyBuildTestAll \")]\n    [Alias('testpath')]\n    [string[]]$WslTestDirPath,\n    [Parameter( ValueFromPipeline = $true,\n        ValueFromPipelineByPropertyName = $true,\n        Mandatory = $false,\n        ParameterSetName = \"DistroName\",\n        HelpMessage = \"Name of WSL distro to run tests in; defaults to test_distro\")]\n    [Parameter( ParameterSetName = \"CopyBuildTestAll \")]\n    [Alias('distro')]\n    [string[]]$DistroName = \"test_distro\"\n)\n\n$ErrorActionPreference = \"Stop\"\nSet-StrictMode -Version Latest\n\nfunction Run {\n    [CmdletBinding()]\n    param([ScriptBlock]$cmd)\n\n    Invoke-Command -ScriptBlock $cmd\n    if ($LastExitCode -ne 0) {\n        throw (\"$cmd failed with exit code: \" + $lastexitcode)\n    }\n}\n\n$DistroPath = \"$env:LocalAppData\\lxss\"\n\n$copyScriptCommand = $PSScriptRoot + \"\\copy_tests.ps1 -WslTestDirPath $WslTestDirPath -DistroName $DistroName\"\n\n$cleanTestCommand = \"rm -rf /data/test\"\n$buildTestCommand = \"cd /data/test; ./build_tests.sh; less /data/test/log/build_output\"\n\n# clean test directory on linux side\nWrite-Output \"Cleaning unit tests at $DistroPath\\rootfs\\data\\test\"\nRun { wsl.exe --distribution $DistroName --user root --exec bash -c \"$cleanTestCommand\" }\n\n# call the logic in copy_tests.ps1\nInvoke-Expression $copyScriptCommand\n\n# build the tests on the linux side\nWrite-Output \"Building unit tests at $DistroPath\\rootfs\\data\\test\\\"\nRun { wsl.exe --distribution $DistroName --user root --exec bash -c \"$buildTestCommand\" }"
  },
  {
    "path": "tools/test/copy_tests.ps1",
    "content": "<#\n.SYNOPSIS\n    Copies linux unit tests to a given WSL distribution.\n.PARAMETER WslTestDirPath\n    The absolute path to the location of the \\wsl-build\\test\\linux directory.\n.PARAMETER DistroName\n    The name of the WSL distribution to copy the unit tests inside of.\n    Defaults to \"test_distro\"\n#>\n\nparam (\n    [Parameter( ValueFromPipeline = $true,\n        ValueFromPipelineByPropertyName = $true,\n        ParameterSetName = \"WslTestDirPath\",\n        HelpMessage = \"Path to \\wsl-build\\test\\linux\\ location\")]\n    [Parameter( ParameterSetName = \"CopyTestAll \")]\n    [Alias('testpath')]\n    [string[]]$WslTestDirPath,\n    [Parameter( ValueFromPipeline = $true,\n        ValueFromPipelineByPropertyName = $true,\n        Mandatory = $false,\n        ParameterSetName = \"DistroName\",\n        HelpMessage = \"Name of WSL distro to run tests in; defaults to test_distro\")]\n    [Parameter( ParameterSetName = \"CopyTestAll \")]\n    [Alias('distro')]\n    [string[]]$DistroName = 'test_distro'\n)\n\nWrite-Output \"WslTestDirPath: $WslTestDirPath\"\nWrite-Output \"DistroName: $DistroName\"\n\n$ErrorActionPreference = \"Stop\"\nSet-StrictMode -Version Latest\n\nfunction Run {\n    [CmdletBinding()]\n    param([ScriptBlock]$cmd)\n\n    Invoke-Command -ScriptBlock $cmd\n    if ($LastExitCode -ne 0) {\n        throw (\"$cmd failed with exit code: \" + $lastexitcode)\n    }\n}\n\n$DistroPath = \"$env:LocalAppData\\lxss\"\n\nWrite-Output \"Copying unit tests to $DistroPath\\rootfs\\data\\test\"\n\n# get wslpath to unit tests\n$WslUnitTestPath = Run { wsl.exe --exec wslpath \"$WslTestDirPath\\unit_tests\" }\n\n# set-up the folder structure for the tests and copy them into the linux distro\nRun { wsl.exe --distribution $DistroName --user root --exec bash -c \"umask 0; mkdir -p /data;\" }\nRun { wsl.exe --distribution $DistroName --user root --exec bash -c \"umask 0; cp -r $WslUnitTestPath /data/test\" }\nRun { wsl.exe --distribution $DistroName --user root --exec bash -c \"umask 0; mkdir -p /data/test/log; ls -la /data/test\" }\n\n# ensure that /etc/fstab exists for the unit tests that expect it\nRun { wsl.exe --distribution $DistroName --user root --exec bash -c \"( [ -e `\"/etc/fstab`\" ] || touch `\"/etc/fstab`\" )\" }"
  },
  {
    "path": "tools/test/gh-release-server.py",
    "content": "# pip install click\n# python gh-release-server.py  2.0.0 --msi ..\\..\\bin\\x64\\Debug\\wsl.msi --msix ..\\..\\bin\\x64\\Debug\\installer.msix\n\nimport click\nfrom http.server import SimpleHTTPRequestHandler, HTTPServer\nfrom http import HTTPStatus\nimport socketserver\nimport json\nfrom winreg import *\n\nRELEASES_PATH = '/releases'\nMSI_PATH = '/msi'\nMSIX_PATH = '/msix'\n\n@click.command()\n@click.argument('version', required=True)\n@click.option('--msi', default=None)\n@click.option('--port', default=8000)\n@click.option('--msix', default=None)\n@click.option('--pre-release', default=False, is_flag=True)\ndef main(port: int, version: str, msi: str, msix: str, pre_release: bool):\n    lxss_key = OpenKeyEx(HKEY_LOCAL_MACHINE, '''SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Lxss''', 0, KEY_SET_VALUE)\n\n    assets = []\n    if msix:\n        assets.append({'url': f'http://127.0.0.1:{port}{MSIX_PATH}', 'id': 0, 'name': 'wsl.msixbundle'})\n\n    if msi:\n        assets.append({'url': f'http://127.0.0.1:{port}{MSI_PATH}', 'id': 0, 'name': 'wsl.x64.msi'})\n\n    release_json = {'name': version, 'created_at': '2023-06-14T16:56:30Z', 'assets': assets}\n    release_response = json.dumps([release_json] if pre_release else release_json).encode()\n\n    class ReleaseRequestHandler(SimpleHTTPRequestHandler):\n        def translate_path(self, path):\n            return path\n\n        def do_GET(self):\n            print(self.path)\n\n            if self.path == RELEASES_PATH:\n                self.send_response(HTTPStatus.OK)\n                self.send_header(\"Content-type\", 'application/octet-stream')\n                self.send_header(\"Content-Length\", str(len(release_response)))\n                self.end_headers()\n\n                self.wfile.write(release_response)\n            elif self.path == MSI_PATH:\n                self.path = msi\n                file = self.send_head()\n                if file is None:\n                    raise RuntimeError(f'Failed to open {msi}')\n\n                self.copyfile(file, self.wfile)\n            elif self.path == MSIX_PATH:\n                self.path = msix\n                file = self.send_head()\n                if file is None:\n                    raise RuntimeError(f'Failed to open {msix}')\n\n                self.copyfile(file, self.wfile)\n            else:\n                print(f'Received unexpected request: {self.path}')\n\n    SetValueEx(lxss_key, 'GitHubUrlOverride', 0, REG_SZ, f'http://127.0.0.1:{port}{RELEASES_PATH}')\n\n    try:\n\n        with socketserver.TCPServer((\"127.0.0.1\", port), ReleaseRequestHandler) as server:\n            print(f'Serving on port {port}')\n            server.serve_forever()\n    finally:\n        DeleteValue(lxss_key, 'GitHubUrlOverride')\n\nif __name__ == '__main__':\n    main()"
  },
  {
    "path": "tools/test/run-tests.ps1",
    "content": "#Requires -RunAsAdministrator\n\n<#\n.SYNOPSIS\n    Runs all WSL tests; optionally sets-up a WSL distribution and environment prior to running the tests.\n.PARAMETER Version\n    The version of WSL to run the tests in. Defaults to \"2\".\n.PARAMETER SetupScript\n    Path to a setup script to be run prior to running the tests. Defaults to \".\\test-setup.ps1\".\n.PARAMETER DistroPath\n    Path to a .tar/.tar.gz file of the distro to be imported to run the tests with. Defaults to \".\\test_distro.tar.gz\".\n.PARAMETER Package\n    Path to the wsl.msix package to install. Defaults to \".\\wsl.msix\".\n.PARAMETER UnitTestsPath\n    Path to the linux/unit_tests directory to copy and install the unit tests.\n.PARAMETER PullRequest\n    Switch for whether or not this test pass is being run as a part of a pull request; skips certain tests if present. Defaults to $false.\n.PARAMETER TestDllPath\n    Path to the TAEF test DLL. Defaults to \".\\wsltests.dll\".\n.PARAMETER Fast\n    Handy flag to skip package and distro installation to make tests run faster during development. \n.PARAMETER TeArgs\n    Additional arguments for TE.exe.\n#>\n\n[cmdletbinding(PositionalBinding = $false)]\nparam (\n    [string]$Version = 2,\n    [string]$SetupScript = \".\\test-setup.ps1\",\n    [string]$DistroPath = \".\\test_distro.tar.gz\",\n    [string]$Package = \".\\installer.msix\",\n    [string]$UnitTestsPath = \".\\unit_tests\",\n    [switch]$PullRequest = $false,\n    [string]$TestDllPath = \".\\wsltests.dll\",\n    [switch]$Fast = $false,\n    [parameter(ValueFromRemainingArguments = $true)]\n    [string[]]$TeArgs\n)\n\nSet-StrictMode -Version Latest\n$ErrorActionPreference = \"Stop\"\n\nif ($Fast)\n{\n    $SetupScript = $null\n}\n\nte.exe $TestDllPath /p:SetupScript=$SetupScript  /p:Version=$Version /p:DistroPath=$DistroPath /p:Package=$Package /p:UnitTestsPath=$UnitTestsPath /p:PullRequest=$PullRequest /p:AllowUnsigned=1 @TeArgs\n\nif (!$?)\n{\n    exit 1\n}"
  },
  {
    "path": "tools/test/setup-vm-for-tests.ps1",
    "content": "<#\n.SYNOPSIS\n    Sets up a given Machine for running WSL tests.\n.PARAMETER VMName\n    Name of the VM or Computer to set-up for testing.\n.PARAMETER ComputerName \n    Name of the Computer to set-up for testing.\n.PARAMETER IPv6Addr \n    IPv6 address of the computer to set-up for testing.\n.PARAMETER Credential\n    A credential object to be used for authenticating with New-PSSession.\n.PARAMETER Username\n    Username on the given VM to set-up the tests under.\n.PARAMETER Password\n    Password associated with the Username, if needed.\n.PARAMETER ArtifactFolder\n    Specify when using cmake -S ArtifactFolder.\n.PARAMETER BuildType\n    The type of build to create when compiling the WSL source code. Defaults to \"Debug\".\n.PARAMETER MakeTrusted\n    An optional switch to add the destination to the TrustedHosts list.\n.PARAMETER SkipEnableFeatures\n    Skip enabling optional Windows features necessary for some tests.\n.PARAMETER RemoteFolder\n    Absolute Path to a folder on the VM to copy requisite files to. Defaults to \"C:\\Package\".\n.PARAMETER TaefFolder\n    Absolute Path to a folder on the VM to copy taef binaries. Defaults to \"C:\\Taef\".\n.PARAMETER SkipDistro\n    Skip copying over the distro.\n.PARAMETER TestDistroPath\n    Path to the distro image to import and use for testing, if needed. Auto filled if left empty.\n#>\n\n[CmdletBinding(PositionalBinding=$False, DefaultParameterSetName='vm')]\nparam (\n    [Parameter(Mandatory = $true, ParameterSetName='vm')][string]$VMName,\n    [Parameter(Mandatory = $true, ParameterSetName='hostname')][string]$ComputerName,\n    [Parameter(Mandatory = $true, ParameterSetName='ipv6')][string]$IPv6Addr,\n    [System.Management.Automation.PSCredential]$Credential,\n    [string]$Username,\n    [string]$Password,\n    [string]$ArtifactFolder,\n    [ValidateSet(\"Debug\", \"Release\")][string]$BuildType = \"Debug\",\n    [switch]$MakeTrusted,\n    [switch]$SkipEnableFeatures,\n    [string]$RemoteFolder = \"C:\\Package\",\n    [string]$TaefFolder = \"C:\\Taef\",\n    [switch]$SkipDistro,\n    [string]$TestDistroPath\n)\n\n$ErrorActionPreference = \"Stop\"\nSet-StrictMode -Version Latest\n\nif ($Credential -eq $null) {\n    if ([string]::IsNullOrEmpty($Username)) {\n        $Credential = Get-Credential\n    }\n    else {\n        if ([string]::IsNullOrEmpty($Password)) {\n            $SecurePassword = New-Object System.Security.SecureString\n        }\n        else {\n            $SecurePassword = ConvertTo-SecureString \"$Password\" -AsPlainText -Force\n        }\n        $Credential = New-Object System.Management.Automation.PSCredential(\"$Username\", $SecurePassword)\n    }\n}\n\nif ($PSCmdlet.ParameterSetName -eq 'ipv6') {\n    $ComputerName = $Ipv6Addr.Replace(\":\", \"-\")+\".ipv6-literal.net\"\n}\n\nif (![string]::IsNullOrEmpty($ComputerName) -and $MakeTrusted) {\n    Set-Item -Path WSMan:\\localhost\\Client\\TrustedHosts -Concatenate -Value $ComputerName\n}\n\n$Session = switch ($PSCmdlet.ParameterSetName) {\n    \"vm\" {\n        New-PSSession -VMName $VMName -Credential $Credential\n    }\n    default {\n        New-PSSession `\n            -Authentication Negotiate `\n            -ComputerName $ComputerName `\n            -Credential $Credential\n    }\n}\n$Arch = Invoke-Command -Session $Session {$env:PROCESSOR_ARCHITECTURE}\n$Platform = switch ($Arch) {\n    \"AMD64\" {\"X64\"}\n    \"ARM64\" {\"arm64\"}\n    default { throw \"Architecture $Arch unknown\" }\n}\n\nif ([string]::IsNullOrEmpty($TestDistroPath)) {\n    $TestDistroVersion = (Select-Xml -Path \"$PSScriptRoot\\..\\..\\packages.config\" -XPath '/packages/package[@id=''Microsoft.WSL.TestDistro'']/@version').Node.Value\n    $TestDistroPath =  \"$PSScriptRoot\\..\\..\\packages\\Microsoft.WSL.TestDistro.$TestDistroVersion\\test_distro.tar.xz\"\n}\n\nif ([string]::IsNullOrEmpty($ArtifactFolder)) {\n    $ArtifactFolder = \"$PSScriptRoot/../..\"\n}\n\n$Bin = \"$((Get-ItemProperty -Path $ArtifactFolder).FullName)/bin/$Platform/$BuildType\"\n\nif (!$SkipEnableFeatures) {\n    $Reboot = Invoke-Command -Session $Session -ScriptBlock {\n        $RestartNeeded = $false\n\n        $OptionalFeatureNames = @(\n            \"VirtualMachinePlatform\", # Needed to run V2 tests\n            \"Microsoft-Hyper-V-Management-PowerShell\", # Needed for mount tests\n            \"Microsoft-Windows-Subsystem-Linux\") # Needed to run V1 tests\n\n        foreach ($Name in $OptionalFeatureNames) {\n            if ((Get-WindowsOptionalFeature -Online -FeatureName $Name).State -ne \"Enabled\") {\n                $RestartNeeded = (Enable-WindowsOptionalFeature -Online -All -NoRestart -FeatureName $Name -WarningAction SilentlyContinue).RestartNeeded -or $RestartNeeded\n            }\n        }\n\n        return $RestartNeeded\n    }\n}\nelse {\n    $Reboot = $false\n}\n\nInvoke-Command -Session $Session -ArgumentList $RemoteFolder -ScriptBlock {\n    New-Item -ItemType Directory -Path \"$Using:RemoteFolder\" -Force\n}\nCopy-Item -ToSession $Session -Path \"$Bin/installer.msix\"  -Destination $RemoteFolder -Force\nCopy-Item -ToSession $Session -Path \"$Bin/wsltests.dll\"  -Destination $RemoteFolder -Force\nCopy-Item -ToSession $Session -Path \"$Bin/testplugin.dll\"  -Destination $RemoteFolder -Force\nCopy-Item -ToSession $Session -Path \"$PSScriptRoot/test-setup.ps1\"  -Destination $RemoteFolder -Force\nCopy-Item -ToSession $Session -Path \"$PSScriptRoot/run-tests.ps1\"  -Destination $RemoteFolder -Force\nCopy-Item -ToSession $Session -Path \"$PSScriptRoot/../../test/linux/unit_tests\" -Destination $RemoteFolder -Recurse -Force\n\nif (!$SkipDistro) {\n    Copy-Item -ToSession $Session -Path $TestDistroPath -Destination \"$RemoteFolder/test_distro.tar.gz\" -Force\n}\n\n$taefVersion = (Select-Xml -Path \"$PSScriptRoot\\..\\..\\packages.config\" -XPath '/packages/package[@id=''Microsoft.Taef'']/@version').Node.Value\n$taefPackage = \"$ArtifactFolder/packages/Microsoft.Taef.$taefVersion/build/Binaries/$Platform\"\nCopy-Item -ToSession $Session -Path \"$taefPackage\" -Destination $TaefFolder -Recurse -Force\nInvoke-Command -Session $Session -ArgumentList $RemoteFolder -ScriptBlock {\n    $path = [System.Environment]::GetEnvironmentVariable(\"PATH\", [EnvironmentVariableTarget]::User) -split \";\"\n    if ($path -notcontains $using:TaefFolder) {\n        $path += $using:TaefFolder\n        [System.Environment]::SetEnvironmentVariable(\"PATH\", $path -join \";\", [EnvironmentVariableTarget]::User)\n    }\n}\n\n\nif ($Reboot) {\n    Invoke-Command -Session $Session -ScriptBlock { Start-Process shutdown -ArgumentList \"-r\",\"-t 0\" }\n}"
  },
  {
    "path": "tools/test/test-setup.ps1",
    "content": "<#\n.SYNOPSIS\n    Runs all WSL tests; optionally sets-up a WSL distribution and environment prior to running the tests.\n.PARAMETER Version\n    The version of WSL to run the tests in.\n.PARAMETER DistroPath\n    Path to a .tar/.tar.gz file of the distro to be imported to run the tests with, if specified.\n.PARAMETER DistroName\n    The name to be given to the imported distro.\n.PARAMETER Package\n    Path to the wsl.msix package to install. Optional.\n.PARAMETER UnitTestsPath\n    Path to the linux/unit_tests directory to copy and install the unit tests. Optional.\n.PARAMETER PostInstallCommand\n    Command to run post-installation and set-up of the WSL distro used for testing, if specified.\n.PARAMETER AllowUnsigned\n    Imports .\\private-wsl.cert to the LocalMachine\\Root store.\n#>\n\n[CmdletBinding()]\nParam (\n    [Parameter(Mandatory = $true)]$Version,\n    $DistroPath,\n    $DistroName,\n    $Package = $null,\n    $UnitTestsPath = $null,\n    $PostInstallCommand = $null,\n    [switch]$AllowUnsigned)\n\n$ErrorActionPreference = \"Stop\"\nSet-StrictMode -Version Latest\n\nfunction Run {\n    [CmdletBinding()]\n    param([ScriptBlock]$cmd)\n\n    Invoke-Command -ScriptBlock $cmd\n    if ($lastexitcode -ne 0) {\n        throw (\"$cmd failed with exit code: \" + $lastexitcode)\n    }\n}\n\nif ($Package) {\n    $installedPackage = Get-AppxPackage MicrosoftCorporationII.WindowsSubsystemforLinux -AllUsers\n    if ($installedPackage) {\n        Write-Host \"Removing previous package installation\"\n        Remove-AppxPackage $installedPackage -AllUsers\n    }\n\n    $installedMsi = (Get-ItemProperty -Path \"HKLM:Software\\Microsoft\\Windows\\CurrentVersion\\Lxss\\MSI\" -Name ProductCode -ErrorAction Ignore)\n    if ($installedMsi)\n    {\n        Write-Host \"Removing MSI package: $($installedMsi.ProductCode)\"\n        $MSIArguments = @(\n            \"/norestart\"\n            \"/qn\"\n            \"/x\"\n            \"$($installedMsi.ProductCode)\"\n            )\n\n        $exitCode = (Start-Process -Wait \"msiexec.exe\" -ArgumentList $MSIArguments -NoNewWindow -PassThru).ExitCode\n        if ($exitCode -Ne 0)\n        {\n            Write-Host \"Failed to remove package: $exitCode\"\n            exit 1\n        }\n\n    }\n\n    Write-Host \"Installing package: $Package\"\n    try {\n        if ($AllowUnsigned)\n        {\n            # Try to add with -AllowUnsigned first (supported in newer PowerShell)\n            try {\n                Add-AppxPackage $Package -AllowUnsigned -ErrorAction Stop\n            }\n            catch {\n                # Fallback: manually import the certificate and trust it\n                Write-Host \"Attempting to import package certificate...\"\n                $signature = Get-AuthenticodeSignature -LiteralPath $Package\n                if (-not $signature.SignerCertificate) {\n                    Write-Error \"Package is not signed or has no certificate. Cannot import certificate.\"\n                    exit 1\n                }\n\n                $cert = $signature.SignerCertificate\n                $certPath = Join-Path $env:TEMP \"wsl-package-cert.cer\"\n                try {\n                    $cert | Export-Certificate -FilePath $certPath | Out-Null\n                    Import-Certificate -FilePath $certPath -CertStoreLocation Cert:\\LocalMachine\\Root | Out-Null\n                    Write-Host \"Certificate imported successfully. Retrying package installation...\"\n                }\n                finally {\n                    Remove-Item -Path $certPath -ErrorAction SilentlyContinue\n                }\n\n                # Retry installation after importing certificate\n                Add-AppxPackage $Package -ErrorAction Stop\n            }\n        }\n        else {\n            Add-AppxPackage $Package -ErrorAction Stop\n        }\n    }\n    catch {\n        Write-Host \"Error installing package: $_\"\n        Get-AppPackageLog | Select-Object -First 64 | Format-List\n        exit 1\n    }\n}\n\n# Disable OOBE during testing\n$UserLxssRegistryPath = \"HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss\"\nIf (-NOT (Test-Path $UserLxssRegistryPath))\n{\n    New-Item -Path $UserLxssRegistryPath -Force | Out-Null\n}\n\nNew-ItemProperty -Path $UserLxssRegistryPath -Name \"OOBEComplete\" -Value \"1\" -PropertyType DWORD -Force | Out-Null\n\nif ($DistroPath)\n{\n    & wsl.exe --unregister \"$DistroName\" # Ignore non-zero return for this call\n    Run { wsl.exe --import \"$DistroName\" \"$env:LocalAppData\\lxss\" \"$DistroPath\" --version \"$Version\" }\n    Run { wsl.exe --set-default \"$DistroName\" }\n}\n\nif ($UnitTestsPath) {\n    # get wslpath to unit tests\n    $WslUnitTestPath = Run { wsl.exe --exec wslpath \"$UnitTestsPath\" }\n\n    # set-up the folder structure for the tests and copy them into the linux distro\n    Run { wsl.exe --exec bash -c \"umask 0; mkdir -p /data;\" }\n    Run { wsl.exe --exec bash -c \"umask 0; cp -r $WslUnitTestPath /data/test\" }\n    Run { wsl.exe --exec bash -c \"umask 0; mkdir -p /data/test/log\" }\n\n    # ensure that /etc/fstab exists for the unit tests that expect it\n    Run { wsl.exe --exec bash -c \"( [ -e `\"/etc/fstab`\" ] || touch `\"/etc/fstab`\" )\" }\n}\n\n\nif ($PostInstallCommand) {\n    Run { wsl.exe \"$PostInstallCommand\" }\n}\n"
  },
  {
    "path": "tools/test/test.bat.in",
    "content": "@echo off\n\nrem This script is generated by CMake. See: tools/test/test.bat.in\n\nset \"Bin=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}\\${CMAKE_BUILD_TYPE}\"\nset \"Tools=${CMAKE_CURRENT_LIST_DIR}\\tools\"\npath %PATH%;${TAEF_SOURCE_DIR}\\build\\Binaries\\${TARGET_PLATFORM}\n\npowershell.exe -ExecutionPolicy Bypass \"%tools%\\test\\run-tests.ps1\" -SetupScript \"%tools%\\test\\test-setup.ps1\" -DistroPath \"${TEST_DISTRO_SOURCE_DIR}test_distro.tar.xz\" -TestDllPath \"%bin%\\wsltests.dll\" -UnitTestsPath \"${CMAKE_CURRENT_LIST_DIR}\\test\\linux\\unit_tests\" -Package \"%bin%\\installer.msix\" %* || goto fail\n\nexit /b 0\n\n:fail\n\necho \"Test failed\"\nexit /b 1"
  },
  {
    "path": "triage/config.yml",
    "content": "wpa_profile: no-filter.wpaProfile\r\n\r\nlogs_rules:\r\n    missing_logs_message: |\r\n        # Logs are required for review from WSL team\r\n        \r\n        If this a feature request, please reply with '/feature'. If this is a question, reply with '/question'.\r\n        **Otherwise, please attach logs by following the instructions below**, your issue will not be reviewed unless they are added. These logs will help us understand what is going on in your machine.\r\n        \r\n        <details>\r\n        \r\n        <summary>How to collect WSL logs</summary>\r\n        \r\n        Download and execute [collect-wsl-logs.ps1](https://github.com/Microsoft/WSL/blob/master/diagnostics/collect-wsl-logs.ps1) in an **administrative powershell prompt**:\r\n        \r\n        ```\r\n        Invoke-WebRequest -UseBasicParsing \"https://raw.githubusercontent.com/microsoft/WSL/master/diagnostics/collect-wsl-logs.ps1\" -OutFile collect-wsl-logs.ps1\r\n        Set-ExecutionPolicy Bypass -Scope Process -Force\r\n        .\\collect-wsl-logs.ps1\r\n        ```\r\n        \r\n        The script will output the path of the log file once done.\r\n\r\n        If this is a networking issue, please use `.\\collect-wsl-logs.ps1 -LogProfile networking` instead, following the instructions in [Collect WSL logs for networking issues](https://github.com/microsoft/WSL/blob/master/CONTRIBUTING.md#collect-wsl-logs-for-networking-issues)\r\n        \r\n        Once completed please upload the output files to this GitHub issue.\r\n         \r\n        See [Collect WSL logs (recommended method)](https://github.com/microsoft/WSL/blob/master/CONTRIBUTING.md#8-collect-wsl-logs-recommended-method).\r\n        \r\n        If you choose to email these logs instead of attaching them to the bug, please send them to wsl-gh-logs@microsoft.com with the GitHub issue number in the subject, and include a link to your GitHub issue comment in the message body, and reply with '/emailed-logs'.\r\n        </details>\r\n        \r\n    \r\n    missing_logs_add_tags: ['needs-author-feedback']\r\n    missing_logs_etl_message: 'No logs.etl found in the archive. Make sure that you ran `collect-wsl-logs.ps1` as administrator and that the `logs.etl` file is in the archive.'\r\n    skip_tags: ['emailed-logs']\r\n\r\noptional_component_rules:\r\n    - name: VirtualMachinePlatform\r\n      set: vmp-oc-enabled\r\n\r\n    - name: Microsoft-Windows-Subsystem-Linux\r\n      set: wsl-oc-enabled\r\n\r\ntags_rules:\r\n    ignore: ['/featurename']\r\n    tags:\r\n        - contains: '/question'\r\n          tag: 'question'\r\n        \r\n        - contains: '/feature'\r\n          tag: 'feature'\r\n          \r\n        - contains: '/emailed-logs'\r\n          tag: 'emailed-logs'\r\n\r\nrules:\r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: GuestLog\r\n        field1:\r\n            contains: 'brd: module loaded'\r\n      set: booting\r\n        \r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: GuestLog\r\n        field1:\r\n            contains: 'oom-kill'\r\n      set: \r\n        name: oom\r\n        capture:\r\n            field1: oom-error\r\n        \r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: GuestLog\r\n        field1:\r\n            contains: 'Kernel panic'\r\n      set: \r\n        name: kernel-panic\r\n        capture:\r\n            field1: kmsg\r\n    \r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: GuestLog\r\n        field1:\r\n            regex: 'WSL (.*) ERROR'\r\n      set: init-error\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: GuestLog\r\n        field1:\r\n            regex: 'init(.*) segfault'\r\n      set:\r\n        name: init-crash\r\n        capture:\r\n            field1: error\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Subsystem.Lxss\r\n        task: UserVisibleError\r\n      set: \r\n        name: user-visible-error\r\n        capture:\r\n            field3: error\r\n            \r\n    - logline:\r\n        provider: Microsoft.Windows.Subsystem.Lxss\r\n        task: UserVisibleError\r\n        field3: Wsl/CallMsi/REGDB_E_CLASSNOTREG\r\n      set: msix-bad-install-state\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Subsystem.Lxss\r\n        task: LxssException\r\n        field1: 'onecore\\vm\\wsl\\lxss\\wslutil\\liftedsupport.cpp'\r\n        field5: '0x80070002'\r\n      set: msix-bad-app-exec-alias-state\r\n\r\n    - logline:\r\n        provider: Microsoft-Windows-Hyper-V-Chipset\r\n        field1:\r\n            regex: '.*biosdevice.*80070057.*' # 80070057 = E_INVALIDARG\r\n      set: corrupted-initramfs\r\n        \r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: FailedToStartVm\r\n      set: \r\n        name: vm-failed-to-start\r\n        capture:\r\n          field4: error\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: Error\r\n        field8: '0x8004032D' # 0x8004032D = WSL_E_VIRTUAL_MACHINE_PLATFORM_REQUIRED\r\n      set: \r\n        name: vmp-required\r\n        capture:\r\n          field8: error\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: Error\r\n        field9: {contains: HcnCreateNetwork}\r\n      set:\r\n        name: hns-create-network-error\r\n        capture:\r\n          field9: error\r\n          field8: hresult\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n      set: service-running\r\n      oneshot: true\r\n      \r\n    - logline:\r\n        provider: Microsoft.Windows.Subsystem.Lxss\r\n        task: LxssException\r\n        field7: \r\n            regex: '.*0x80070422.*'\r\n      set: service-disabled-error\r\n      \r\n    - logline:\r\n        provider: Microsoft.Windows.Subsystem.Lxss\r\n        task: UserVisible Error\r\n        field3: {regex: '.*/ConfigureNetworking/HNS/.*'}\r\n      set: \r\n        name: hns-fatal-error\r\n        capture:\r\n          field3: error\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Hyper.V.NetMgmt\r\n        task: NetMgmt::CreateVirtualSwitch\r\n        field4: '0x80041002'\r\n      set: \r\n        name: vmswitch-known-issue\r\n        capture:\r\n            field4: error\r\n      \r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: GuestLog\r\n        field1:\r\n            contains: 'EXT4-fs error'\r\n      set: \r\n        name: ext4-error\r\n        capture:\r\n            field1: error\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: GuestLog\r\n        field1: {regex: \"EXT4-fs.*Can't find ext4 filesystem\"}\r\n      set: \r\n        name: ext4-mount-error\r\n        capture:\r\n            field1: error\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: GuestLog\r\n        field1: {regex: \".*No space left on device\"}\r\n      set: \r\n        name: disk-out-of-space\r\n        capture:\r\n            field1: error\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n        task: LinuxCrash\r\n      set:\r\n        name: linux-crash\r\n        capture:\r\n          field3: linux-crash-path\r\n          field6: linux-crash-process\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Lxss.Manager\r\n      set: wsl-service-logs\r\n      oneshot: true\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Subsystem.Lxss\r\n      set: wsl-client-logs\r\n      oneshot: true\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.Subsystem.Lxss\r\n        field8: {regex: \".*InstallMsix\\\\(.*\"}\r\n      set:\r\n        name: msix-install-error\r\n        capture:\r\n            field7: error\r\n      oneshot: true\r\n\r\n    - logline:\r\n        provider: Microsoft-Windows-VHDMP\r\n        task: VHD_VIRTUAL_DISK_HANDLE_CREATE_TASK\r\n        event: 'Microsoft-Windows-VHDMP/VHD_VIRTUAL_DISK_HANDLE_CREATE_TASK/win:Stop'\r\n        field1: {not: '0x00000000'}\r\n      set:\r\n          name: disk-attach-error\r\n          capture:\r\n            field1: error\r\n            field2: vhdpath\r\n\r\n    - logline:\r\n        provider: Microsoft.Windows.HyperV.Worker\r\n        task: FallbackError\r\n        field2: {regex: \".*vmchipset\\\\.dll.*80090006.*\"} #0x80090006 = NTE_BAD_SIGNATURE \r\n      set:\r\n        name: hyper-v-firmware-expired\r\n        capture:\r\n          field2: signature-error\r\n\r\nactions:\r\n    - foreach:\r\n        var: user-visible-error\r\n        debug_message: 'Detected user visible error: $error'\r\n        skip_similar_issues: false\r\n\r\n    - foreach:\r\n        var: linux-crash\r\n        debug_message: 'Found evidence of linux crash: $linux-crash-process (dump: $linux-crash-path)'\r\n        skip_similar_issues: false\r\n\r\n    - foreach:\r\n        var: disk-attach-error\r\n        debug_message: 'Found evidence of disk failing to attach. Error: $error, Path: $vhdpath'\r\n\r\n    - when:\r\n        condition: 'init-crash'\r\n        debug_message: 'Found evidence of init crash: $error'\r\n        tag: init-crash\r\n\r\n    - when:\r\n        condition: 'kernel-panic'\r\n        debug_message: 'Found evidence of kernel panic: $kmsg'\r\n        skip_similar_issues: false\r\n\r\n    - when: \r\n        condition: \r\n         and: ['oom', 'booting']\r\n        user_message: 'The logs show that WSL2 ran out of memory. Try increasing wsl2.memory in .wslconfig and see if that solves the issue.'\r\n        debug_message: 'Found evidence of OOM kill: $oom-error'\r\n        tag: needs-author-feedback\r\n        skip_similar_issues: true\r\n        \r\n    - when: \r\n        condition: \r\n         and: ['vm-failed-to-start', 'hyperv-firmware-expired']\r\n        user_message: 'The logs show that your Hyper-V firmware is expired. Please update your Windows build and see if that solves the issue'\r\n        tag: needs-author-feedback\r\n        skip_similar_issues: true\r\n        \r\n    - when: \r\n        condition:  'hns-create-network-error'\r\n        debug_message: 'Found evidence of HcnCreateNetwork failure: $error. HResult: $hresult'\r\n\r\n    - when:\r\n        condition:\r\n         and: ['vm-failed-to-start', 'hns-create-network-error']\r\n        user_message: \"The logs show that the VM failed to start because the HNS network couldn't be created. Adding network tag\"\r\n        tag: network\r\n        skip_similar_issues: true\r\n\r\n    - when:\r\n        condition:\r\n          and: ['vm-failed-to-start', 'corrupted-initramfs']\r\n        user_message: 'Your WSL installation seems corrupted. Please try to download and install the [latest WSL release](https://github.com/microsoft/WSL/releases/latest)'\r\n        tag: needs-author-feedback\r\n        skip_similar_issues: true\r\n       \r\n    - when:\r\n        condition:\r\n          and: [{not: 'service-running'}, 'service-disabled-error']\r\n        user_message: 'The logs show that wslservice is disabled. Try to run (elevated command prompt): `sc.exe config wslservice start= demand` and see if that solves the issue'\r\n        tag: needs-author-feedback\r\n        skip_similar_issues: true\r\n\r\n    - when:\r\n        condition:\r\n          and: ['vm-failed-to-start', 'hns-fatal-error']\r\n        user_message: 'An HNS error seems to be causing WSL2 to fail to start. Adding network tag'\r\n        debug_message: 'Found HNS error: $error'\r\n        tag: 'network'\r\n\r\n    - when:\r\n        condition:\r\n          and: ['vm-failed-to-start', 'vmswitch-known-issue']\r\n        user_message: 'Known vmswitch issue found (error: $error). Adding network tag'\r\n        debug_message: 'Found evidence of vmswitch error: $error'\r\n        tag: 'network'\r\n        skip_similar_issues: true\r\n\r\n        \r\n    - when: \r\n        condition: {or:  ['ext4-error', 'ext4-mount-error']}\r\n        user_message: 'The logs shows that a disk mount error occurred. Try to [follow these repair instructions](https://learn.microsoft.com/en-us/windows/wsl/disk-space#how-to-repair-a-vhd-mounting-error) and see if that solves the issue.'\r\n        debug_message: 'Found evidence of ext4 error: $error'\r\n        tag: 'needs-author-feedback'\r\n        skip_similar_issues: true\r\n\r\n\r\n    - when:\r\n        condition:\r\n          not:\r\n            or: ['wsl-client-logs', 'wsl-service-logs']\r\n        user_message: \"The log file doesn't contain any WSL traces. Please make sure **that you reproduced the issue while the log collection was running.**\"\r\n        debug_message: 'Found no WSL traces in the logs'\r\n        tag: 'needs-author-feedback'\r\n        skip_similar_issues: false\r\n\r\n    - when:\r\n        condition:\r\n          and:\r\n            - vmp-required\r\n            - not: vmp-oc-enabled\r\n        debug_message: 'Found evidence of virtual machine platform component missing: $error'\r\n        user_message: 'It appears that the VirtualMachinePlatform optional component is not enabled. Please try running: `dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart`, then reboot and see if that solves the issue.'\r\n        skip_similar_issues: false\r\n        tag: 'needs-author-feedback'\r\n\r\n    - when:\r\n        condition: msix-install-error\r\n        debug_message: 'Found evidence of MSIX install error: $error, adding msix tag'\r\n        tag: 'msix'\r\n\r\n    - when: \r\n        condition: msix-bad-app-exec-alias-state\r\n        debug_message: 'Found evidence of failed app execution alias execution in LiftedSupport'\r\n\r\n    - when:\r\n        condition: {or: [msix-bad-install-state, msix-bad-app-exec-alias-state]}\r\n        user_message: | \r\n            Your WSL installation appears to be in a bad state. Can you try running the following command to reinstall WSL (elevated powershell) and see if that solves the issue?\r\n            \r\n            ```\r\n            Invoke-WebRequest -UseBasicParsing \"https://raw.githubusercontent.com/microsoft/WSL/master/triage/install-latest-wsl.ps1\" -OutFile install-latest-wsl.ps1\r\n            Set-ExecutionPolicy Bypass -Scope Process -Force\r\n            .\\install-latest-wsl.ps1\r\n             ```\r\n            \r\n            \r\n        tag: 'msix'\r\n\r\n    - when:\r\n       condition: disk-out-of-space\r\n       debug_message: 'Found evidence of disk being full: $error'\r\n       user_message: |\r\n        It looks like WSL is out of space. If you're importing a new distribution, try to add: \r\n        \r\n        ```\r\n        [wsl2]\r\n        defaultVhdSize = <size> # (example: 1500GB)\r\n        ```\r\n        \r\n        Otherwise, if you need to increase the size of an existing distribution, you can follow [these instructions](https://learn.microsoft.com/en-us/windows/wsl/disk-space#how-to-expand-the-size-of-your-wsl-2-virtual-hard-disk).\r\n\r\n    - when:\r\n        condition:\r\n          and: ['hyper-v-firmware-expired', 'vm-failed-to-start']\r\n        tag: 'needs-author-feedback'\r\n        debug_message: 'Found evidence of hyper-v firmware expiration: $signature-error'\r\n        user_message: |\r\n          It looks like the Hyper-V firmware on your Windows build has expired. Please try to update Windows to a more recent build and see if that solves the issue."
  },
  {
    "path": "triage/install-latest-wsl.ps1",
    "content": "#Requires -RunAsAdministrator\r\n\r\n# This script downloads and installs the latest version of the WSL MSI package\r\n\r\n# Get current language code\r\n$chcp_num = (chcp) -replace '[^\\d]+(\\d+).*','$1'\r\n\r\n# Set language to english\r\nchcp 437\r\n\r\n$ErrorActionPreference = \"Stop\"\r\nSet-StrictMode -Version Latest\r\n\r\n$release = Invoke-WebRequest 'https://api.github.com/repos/microsoft/WSL/releases/latest' | ConvertFrom-Json\r\n$systeminfo = & systeminfo | findstr /C:\"System Type\"\r\nif ($systeminfo -like '*x64*')\r\n{\r\n    $target = '.x64.msi'\r\n} elseif ($systeminfo -like '*arm64*')\r\n{\r\n    $target = '.arm64.msi'\r\n} else\r\n{\r\n    throw 'Failed to determine system type ($systeminfo)'\r\n}\r\n\r\n[array]$assets = $release.assets | Where-Object { $_.name.ToLower().endswith('.x64.msi')}\r\nif ($assets.count -ne 1)\r\n{\r\n    throw 'Failed to find asset ($assets)'\r\n}\r\n\r\n$target = \"$env:tmp\\$($assets.name)\"\r\nWrite-Host \"Downloading $($assets.name) to $target\"\r\n\r\n$headers = New-Object \"System.Collections.Generic.Dictionary[[String],[String]]\"\r\n$headers.Add('Accept','application/octet-stream')\r\n\r\nInvoke-WebRequest $assets.url -Out $target -Headers $headers\r\n\r\n$MSIArguments = @(\r\n    \"/i\"\r\n    $target\r\n    \"/qn\"\r\n    \"/norestart\"\r\n)\r\n\r\n$exitCode = (Start-Process -Wait \"msiexec.exe\" -ArgumentList $MSIArguments -NoNewWindow -PassThru).ExitCode\r\nif ($exitCode -Ne 0)\r\n{\r\n    throw \"Failed to install package: $exitCode\"\r\n}\r\n\r\nWrite-Host 'Installation complete'\r\n\r\nRemove-Item $target -Force\r\n\r\n# Restore original language\r\nchcp $chcp_num\r\n"
  },
  {
    "path": "triage/no-filter.wpaProfile",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<WpaProfileContainer xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" Version=\"2\" xmlns=\"http://tempuri.org/SerializableElement.xsd\">\r\n  <Content xsi:type=\"WpaProfile2\">\r\n    <Sessions>\r\n      <Session Index=\"0\">\r\n        <FileReferences />\r\n      </Session>\r\n    </Sessions>\r\n    <Views>\r\n      <View Guid=\"fa2db56d-11da-45d9-b766-1168edd04483\" IsVisible=\"true\" Title=\"Analysis\">\r\n        <Graphs>\r\n          <Graph Guid=\"04f69f98-176e-4d1c-b44e-97f734996ab8\" LayoutStyle=\"All\" GraphHeight=\"125\" IsMinimized=\"false\" IsShown=\"true\" IsExpanded=\"false\" HelpText=\"{}{\\rtf1\\ansi\\ansicpg1252\\uc1\\htmautsp\\deff2{\\fonttbl{\\f0\\fcharset0 Times New Roman;}{\\f2\\fcharset0 Segoe UI;}}{\\colortbl\\red0\\green0\\blue0;\\red255\\green255\\blue255;}\\loch\\hich\\dbch\\pard\\plain\\ltrpar\\itap0{\\lang1033\\fs18\\f2\\cf0 \\cf0\\ql{\\f2 {\\ltrch Shows every event in the trace, including the associated payload fields.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 \\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch New capability - graph payload fields!}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 \\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch 1. Filter down to the event with the payload field you want to graph.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch 2. Drag the column corresponding to the payload field you want to graph to the right of the blue bar.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch 3. If the automatic graphing isn't representing your data correctly, go to View Editor and:}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch a. Adjust the aggregation mode for your column, or}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch b. Go to Advanced &gt; Graph Configuration and change your graphing aggregation mode.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par}\">\r\n            <Preset Name=\"Activity by Provider, Task, Opcode\" BarGraphIntervalCount=\"50\" IsThreadActivityTable=\"false\" GraphColumnCount=\"35\" KeyColumnCount=\"8\" LeftFrozenColumnCount=\"9\" RightFrozenColumnCount=\"33\" InitialFilterShouldKeep=\"true\" GraphFilterTopValue=\"0\" GraphFilterThresholdValue=\"0\" HelpText=\"{}{\\rtf1\\ansi\\ansicpg1252\\uc1\\htmautsp\\deff2{\\fonttbl{\\f0\\fcharset0 Times New Roman;}{\\f2\\fcharset0 Segoe UI;}}{\\colortbl\\red0\\green0\\blue0;\\red255\\green255\\blue255;}\\loch\\hich\\dbch\\pard\\plain\\ltrpar\\itap0{\\lang1033\\fs18\\f2\\cf0 \\cf0\\ql{\\f2 {\\ltrch Groups all the events by provider, task, and opcode.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par}&#xD;&#xA;}&#xD;&#xA;}\">\r\n              <MetadataEntries>\r\n                <MetadataEntry Guid=\"bbfc990a-b6c9-4dcd-858b-f040ab4a1efe\" Name=\"Time\" ColumnMetadata=\"StartTime\" />\r\n                <MetadataEntry Guid=\"bbfc990a-b6c9-4dcd-858b-f040ab4a1efe\" Name=\"Time\" ColumnMetadata=\"EndTime\" />\r\n                <MetadataEntry Guid=\"edf01e5d-3644-4dbc-ab9d-f8954e6db6ea\" Name=\"ThreadId\" ColumnMetadata=\"EndThreadId\" />\r\n              </MetadataEntries>\r\n              <HighlightEntries />\r\n              <Columns>\r\n                <Column Guid=\"8b4c40f8-0d99-437d-86ab-56ec200137dc\" Name=\"Provider Name\" SortPriority=\"1\" Width=\"200\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"2a23f9b1-65d6-46d2-87b2-72f3606b7f75\" Name=\"Provider Id\" SortPriority=\"5\" Width=\"100\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"511777f7-30ef-4e86-bd0b-0facaf23a0d3\" Name=\"Task Name\" SortPriority=\"2\" Width=\"80\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"5b51716b-b88f-443a-a396-c6316296d5f8\" Name=\"Opcode Name\" SortPriority=\"3\" Width=\"80\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"c72e1c5a-f6db-4c84-8c0b-85989e514075\" Name=\"Id\" SortPriority=\"3\" Width=\"80\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"d446a182-5203-4a29-aca1-4ab9bea0d1b5\" Name=\"Version\" SortPriority=\"0\" Width=\"80\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"7ee6a5ff-1faf-428a-a7c2-7d2cb2b5cf26\" Name=\"Process\" SortPriority=\"9\" Width=\"150\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"b51bcda1-7ea3-4409-b4af-51a704893bd9\" Name=\"Process Id\" SortPriority=\"0\" Width=\"50\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"cb796d44-2927-5ac1-d231-4b71904c18f5\" Name=\"Thread Name\" SortPriority=\"0\" Width=\"80\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"82ddfdff-ee93-5f35-08ac-4705069618dc\" Name=\"Thread Activity Tag\" SortPriority=\"0\" Width=\"80\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"2818954f-2d30-5569-4510-dade0a5a605c\" Name=\"Annotation\" SortPriority=\"0\" Width=\"80\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"90fe0b49-e3bb-440f-b829-5813c42108a1\" Name=\"Event Name\" SortPriority=\"4\" Width=\"100\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"72892fbe-0f55-426a-9aa1-26b6baf09ffb\" Name=\"Event Type\" SortPriority=\"6\" Width=\"100\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"0734f1a4-fbd9-4ff6-aec0-cf43875c8253\" Name=\"Message\" SortPriority=\"6\" Width=\"100\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"3be8610c-babb-4154-9970-1b2210928024\" Name=\"Cpu\" SortPriority=\"7\" Width=\"30\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"edf01e5d-3644-4dbc-ab9d-f8954e6db6ea\" Name=\"ThreadId\" SortPriority=\"8\" Width=\"50\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"0c5cf8cd-9b9e-5798-1a5d-09d429f7fa3c\" Name=\"Field 1\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 1\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"71badd11-26e5-56bc-44ec-12f4cc6a8f3e\" Name=\"Field 2\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 2\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"411dba2d-5d6e-5272-8287-636d0841768c\" Name=\"Field 3\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 3\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"048f5050-1f17-59b3-fa22-4b0781ee630b\" Name=\"Field 4\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 4\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"94e48f22-d499-5227-bb04-be011b4159b0\" Name=\"Field 5\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 5\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"c1054028-424a-59ba-e760-6d30abbd69c5\" Name=\"Field 6\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 6\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"5cbc4b58-2de1-5449-55b2-4651d4edf90a\" Name=\"Field 7\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 7\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"fc01e6c9-a43b-51a1-fd2e-112ba48aff65\" Name=\"Field 8\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 8\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"e3dcf300-46e2-5c43-ef4b-2c3db489ec25\" Name=\"Field 9\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 9\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"87c21ddb-4b29-58e3-5ddc-f114c5ca209a\" Name=\"Field 10\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 10\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"65f8fe42-ad02-5016-3521-f93329c76227\" Name=\"Field 11\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 11\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"03d48fd3-0fe4-57f5-a477-49beb0d70a1f\" Name=\"Field 12\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 12\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"998f8e2d-0f5a-5a54-0133-226e2de62c0b\" Name=\"Field 13\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 13\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"bb772c34-9600-5bf7-3f54-e6080f8a0611\" Name=\"Field 14\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 14\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"1479add7-3dcb-580b-f1e9-87d5186ced61\" Name=\"Field 15\" SortPriority=\"-1\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 15\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"342f7677-17b2-4c7e-b9ec-e89612c49792\" Name=\"Count\" AggregationMode=\"Sum\" SortOrder=\"Descending\" SortPriority=\"0\" Width=\"80\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"bbfc990a-b6c9-4dcd-858b-f040ab4a1efe\" Name=\"Time\" SortPriority=\"11\" Width=\"80\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n              </Columns>\r\n            </Preset>\r\n          </Graph>\r\n        </Graphs>\r\n        <SessionIndices>\r\n          <SessionIndex>0</SessionIndex>\r\n        </SessionIndices>\r\n      </View>\r\n      <View Guid=\"67563633-8128-4b0b-bf07-e76dc861e5c6\" IsVisible=\"true\" Title=\"Analysis\">\r\n        <Graphs>\r\n          <Graph Guid=\"04f69f98-176e-4d1c-b44e-97f734996ab8\" LayoutStyle=\"All\" GraphHeight=\"125\" IsMinimized=\"false\" IsShown=\"true\" IsExpanded=\"false\" HelpText=\"{}{\\rtf1\\ansi\\ansicpg1252\\uc1\\htmautsp\\deff2{\\fonttbl{\\f0\\fcharset0 Times New Roman;}{\\f2\\fcharset0 Segoe UI;}}{\\colortbl\\red0\\green0\\blue0;\\red255\\green255\\blue255;}\\loch\\hich\\dbch\\pard\\plain\\ltrpar\\itap0{\\lang1033\\fs18\\f2\\cf0 \\cf0\\ql{\\f2 {\\ltrch Shows every event in the trace, including the associated payload fields.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 \\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch New capability - graph payload fields!}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 \\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch 1. Filter down to the event with the payload field you want to graph.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch 2. Drag the column corresponding to the payload field you want to graph to the right of the blue bar.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch 3. If the automatic graphing isn't representing your data correctly, go to View Editor and:}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch a. Adjust the aggregation mode for your column, or}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch b. Go to Advanced &gt; Graph Configuration and change your graphing aggregation mode.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par}\">\r\n            <Preset Name=\"Activity by Provider, Task, Opcode\" BarGraphIntervalCount=\"50\" IsThreadActivityTable=\"false\" GraphColumnCount=\"36\" KeyColumnCount=\"7\" LeftFrozenColumnCount=\"8\" RightFrozenColumnCount=\"34\" InitialFilterShouldKeep=\"true\" GraphFilterTopValue=\"0\" GraphFilterThresholdValue=\"0\" HelpText=\"{}{\\rtf1\\ansi\\ansicpg1252\\uc1\\htmautsp\\deff2{\\fonttbl{\\f0\\fcharset0 Times New Roman;}{\\f2\\fcharset0 Segoe UI;}}{\\colortbl\\red0\\green0\\blue0;\\red255\\green255\\blue255;}\\loch\\hich\\dbch\\pard\\plain\\ltrpar\\itap0{\\lang1033\\fs18\\f2\\cf0 \\cf0\\ql{\\f2 {\\ltrch Groups all the events by provider, task, and opcode.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par}&#xD;&#xA;}&#xD;&#xA;}\">\r\n              <MetadataEntries>\r\n                <MetadataEntry Guid=\"edf01e5d-3644-4dbc-ab9d-f8954e6db6ea\" Name=\"ThreadId\" ColumnMetadata=\"EndThreadId\" />\r\n                <MetadataEntry Guid=\"bbfc990a-b6c9-4dcd-858b-f040ab4a1efe\" Name=\"Time\" ColumnMetadata=\"StartTime\" />\r\n                <MetadataEntry Guid=\"bbfc990a-b6c9-4dcd-858b-f040ab4a1efe\" Name=\"Time\" ColumnMetadata=\"EndTime\" />\r\n              </MetadataEntries>\r\n              <HighlightEntries />\r\n              <Columns>\r\n                <Column Guid=\"bbfc990a-b6c9-4dcd-858b-f040ab4a1efe\" Name=\"Time\" SortPriority=\"38\" Width=\"80\" IsVisible=\"true\">\r\n                  <DateTimeTimestampOptionsParameter DateTimeEnabled=\"true\" />\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"2a23f9b1-65d6-46d2-87b2-72f3606b7f75\" Name=\"Provider Id\" SortPriority=\"32\" Width=\"100\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"5b51716b-b88f-443a-a396-c6316296d5f8\" Name=\"Opcode Name\" SortPriority=\"29\" Width=\"80\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"c72e1c5a-f6db-4c84-8c0b-85989e514075\" Name=\"Id\" SortPriority=\"30\" Width=\"80\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"d446a182-5203-4a29-aca1-4ab9bea0d1b5\" Name=\"Version\" SortPriority=\"1\" Width=\"80\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"7ee6a5ff-1faf-428a-a7c2-7d2cb2b5cf26\" Name=\"Process\" SortPriority=\"37\" Width=\"150\" IsVisible=\"false\">\r\n                  <ProcessOptionsParameter />\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"b51bcda1-7ea3-4409-b4af-51a704893bd9\" Name=\"Process Id\" SortPriority=\"2\" Width=\"50\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"cb796d44-2927-5ac1-d231-4b71904c18f5\" Name=\"Thread Name\" SortPriority=\"3\" Width=\"80\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"82ddfdff-ee93-5f35-08ac-4705069618dc\" Name=\"Thread Activity Tag\" SortPriority=\"4\" Width=\"80\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"2818954f-2d30-5569-4510-dade0a5a605c\" Name=\"Annotation\" SortPriority=\"5\" Width=\"80\" IsVisible=\"false\">\r\n                  <AnnotationsOptionsParameter>\r\n                    <AnnotationQueryEntries />\r\n                  </AnnotationsOptionsParameter>\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"8b4c40f8-0d99-437d-86ab-56ec200137dc\" Name=\"Provider Name\" SortPriority=\"27\" Width=\"305\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"511777f7-30ef-4e86-bd0b-0facaf23a0d3\" Name=\"Task Name\" SortPriority=\"28\" Width=\"223\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"90fe0b49-e3bb-440f-b829-5813c42108a1\" Name=\"Event Name\" SortPriority=\"31\" Width=\"222\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"72892fbe-0f55-426a-9aa1-26b6baf09ffb\" Name=\"Event Type\" SortPriority=\"33\" Width=\"100\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"0734f1a4-fbd9-4ff6-aec0-cf43875c8253\" Name=\"Message\" SortPriority=\"34\" Width=\"100\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"3be8610c-babb-4154-9970-1b2210928024\" Name=\"Cpu\" SortPriority=\"35\" Width=\"30\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"edf01e5d-3644-4dbc-ab9d-f8954e6db6ea\" Name=\"ThreadId\" SortPriority=\"36\" Width=\"50\" IsVisible=\"false\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"0c5cf8cd-9b9e-5798-1a5d-09d429f7fa3c\" Name=\"Field 1\" SortPriority=\"6\" Width=\"352\" IsVisible=\"true\" HelpText=\"Field 1\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"71badd11-26e5-56bc-44ec-12f4cc6a8f3e\" Name=\"Field 2\" SortPriority=\"7\" Width=\"282\" IsVisible=\"true\" HelpText=\"Field 2\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"411dba2d-5d6e-5272-8287-636d0841768c\" Name=\"Field 3\" SortPriority=\"8\" Width=\"125\" IsVisible=\"true\" HelpText=\"Field 3\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"048f5050-1f17-59b3-fa22-4b0781ee630b\" Name=\"Field 4\" SortPriority=\"9\" Width=\"1061\" IsVisible=\"true\" HelpText=\"Field 4\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"94e48f22-d499-5227-bb04-be011b4159b0\" Name=\"Field 5\" SortPriority=\"10\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 5\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"c1054028-424a-59ba-e760-6d30abbd69c5\" Name=\"Field 6\" SortPriority=\"11\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 6\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"5cbc4b58-2de1-5449-55b2-4651d4edf90a\" Name=\"Field 7\" SortPriority=\"12\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 7\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"fc01e6c9-a43b-51a1-fd2e-112ba48aff65\" Name=\"Field 8\" SortPriority=\"13\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 8\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"e3dcf300-46e2-5c43-ef4b-2c3db489ec25\" Name=\"Field 9\" SortPriority=\"14\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 9\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"87c21ddb-4b29-58e3-5ddc-f114c5ca209a\" Name=\"Field 10\" SortPriority=\"15\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 10\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"65f8fe42-ad02-5016-3521-f93329c76227\" Name=\"Field 11\" SortPriority=\"16\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 11\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"03d48fd3-0fe4-57f5-a477-49beb0d70a1f\" Name=\"Field 12\" SortPriority=\"17\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 12\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"998f8e2d-0f5a-5a54-0133-226e2de62c0b\" Name=\"Field 13\" SortPriority=\"18\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 13\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"bb772c34-9600-5bf7-3f54-e6080f8a0611\" Name=\"Field 14\" SortPriority=\"19\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 14\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"1479add7-3dcb-580b-f1e9-87d5186ced61\" Name=\"Field 15\" SortPriority=\"20\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 15\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n                <Column Guid=\"342f7677-17b2-4c7e-b9ec-e89612c49792\" Name=\"Count\" AggregationMode=\"Sum\" SortOrder=\"Descending\" SortPriority=\"0\" Width=\"80\" IsVisible=\"true\">\r\n                  <ColorQueryEntries />\r\n                </Column>\r\n              </Columns>\r\n            </Preset>\r\n          </Graph>\r\n        </Graphs>\r\n        <SessionIndices>\r\n          <SessionIndex>0</SessionIndex>\r\n        </SessionIndices>\r\n      </View>\r\n    </Views>\r\n    <ModifiedGraphs>\r\n      <GraphSchema Guid=\"04f69f98-176e-4d1c-b44e-97f734996ab8\" HelpText=\"{}{\\rtf1\\ansi\\ansicpg1252\\uc1\\htmautsp\\deff2{\\fonttbl{\\f0\\fcharset0 Times New Roman;}{\\f2\\fcharset0 Segoe UI;}}{\\colortbl\\red0\\green0\\blue0;\\red255\\green255\\blue255;}\\loch\\hich\\dbch\\pard\\plain\\ltrpar\\itap0{\\lang1033\\fs18\\f2\\cf0 \\cf0\\ql{\\f2 {\\ltrch Shows every event in the trace, including the associated payload fields.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 \\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch New capability - graph payload fields!}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 \\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch 1. Filter down to the event with the payload field you want to graph.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch 2. Drag the column corresponding to the payload field you want to graph to the right of the blue bar.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch 3. If the automatic graphing isn't representing your data correctly, go to View Editor and:}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch a. Adjust the aggregation mode for your column, or}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par{\\f2 {\\ltrch b. Go to Advanced &gt; Graph Configuration and change your graphing aggregation mode.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par}\">\r\n        <ModifiedPresets />\r\n        <PersistedPresets>\r\n          <Preset Name=\"Activity by Provider, Task, Opcode\" BarGraphIntervalCount=\"50\" IsThreadActivityTable=\"false\" GraphColumnCount=\"36\" KeyColumnCount=\"7\" LeftFrozenColumnCount=\"8\" RightFrozenColumnCount=\"34\" InitialFilterShouldKeep=\"true\" GraphFilterTopValue=\"0\" GraphFilterThresholdValue=\"0\" HelpText=\"{}{\\rtf1\\ansi\\ansicpg1252\\uc1\\htmautsp\\deff2{\\fonttbl{\\f0\\fcharset0 Times New Roman;}{\\f2\\fcharset0 Segoe UI;}}{\\colortbl\\red0\\green0\\blue0;\\red255\\green255\\blue255;}\\loch\\hich\\dbch\\pard\\plain\\ltrpar\\itap0{\\lang1033\\fs18\\f2\\cf0 \\cf0\\ql{\\f2 {\\ltrch Groups all the events by provider, task, and opcode.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par}&#xD;&#xA;}&#xD;&#xA;}\">\r\n            <MetadataEntries>\r\n              <MetadataEntry Guid=\"edf01e5d-3644-4dbc-ab9d-f8954e6db6ea\" Name=\"ThreadId\" ColumnMetadata=\"EndThreadId\" />\r\n              <MetadataEntry Guid=\"bbfc990a-b6c9-4dcd-858b-f040ab4a1efe\" Name=\"Time\" ColumnMetadata=\"StartTime\" />\r\n              <MetadataEntry Guid=\"bbfc990a-b6c9-4dcd-858b-f040ab4a1efe\" Name=\"Time\" ColumnMetadata=\"EndTime\" />\r\n            </MetadataEntries>\r\n            <HighlightEntries />\r\n            <Columns>\r\n              <Column Guid=\"bbfc990a-b6c9-4dcd-858b-f040ab4a1efe\" Name=\"Time\" SortPriority=\"38\" Width=\"80\" IsVisible=\"true\">\r\n                <DateTimeTimestampOptionsParameter DateTimeEnabled=\"true\" />\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"2a23f9b1-65d6-46d2-87b2-72f3606b7f75\" Name=\"Provider Id\" SortPriority=\"32\" Width=\"100\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"5b51716b-b88f-443a-a396-c6316296d5f8\" Name=\"Opcode Name\" SortPriority=\"29\" Width=\"80\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"c72e1c5a-f6db-4c84-8c0b-85989e514075\" Name=\"Id\" SortPriority=\"30\" Width=\"80\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"d446a182-5203-4a29-aca1-4ab9bea0d1b5\" Name=\"Version\" SortPriority=\"1\" Width=\"80\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"7ee6a5ff-1faf-428a-a7c2-7d2cb2b5cf26\" Name=\"Process\" SortPriority=\"37\" Width=\"150\" IsVisible=\"false\">\r\n                <ProcessOptionsParameter />\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"b51bcda1-7ea3-4409-b4af-51a704893bd9\" Name=\"Process Id\" SortPriority=\"2\" Width=\"50\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"cb796d44-2927-5ac1-d231-4b71904c18f5\" Name=\"Thread Name\" SortPriority=\"3\" Width=\"80\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"82ddfdff-ee93-5f35-08ac-4705069618dc\" Name=\"Thread Activity Tag\" SortPriority=\"4\" Width=\"80\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"2818954f-2d30-5569-4510-dade0a5a605c\" Name=\"Annotation\" SortPriority=\"5\" Width=\"80\" IsVisible=\"false\">\r\n                <AnnotationsOptionsParameter>\r\n                  <AnnotationQueryEntries />\r\n                </AnnotationsOptionsParameter>\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"8b4c40f8-0d99-437d-86ab-56ec200137dc\" Name=\"Provider Name\" SortPriority=\"27\" Width=\"305\" IsVisible=\"true\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"511777f7-30ef-4e86-bd0b-0facaf23a0d3\" Name=\"Task Name\" SortPriority=\"28\" Width=\"223\" IsVisible=\"true\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"90fe0b49-e3bb-440f-b829-5813c42108a1\" Name=\"Event Name\" SortPriority=\"31\" Width=\"222\" IsVisible=\"true\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"72892fbe-0f55-426a-9aa1-26b6baf09ffb\" Name=\"Event Type\" SortPriority=\"33\" Width=\"100\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"0734f1a4-fbd9-4ff6-aec0-cf43875c8253\" Name=\"Message\" SortPriority=\"34\" Width=\"100\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"3be8610c-babb-4154-9970-1b2210928024\" Name=\"Cpu\" SortPriority=\"35\" Width=\"30\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"edf01e5d-3644-4dbc-ab9d-f8954e6db6ea\" Name=\"ThreadId\" SortPriority=\"36\" Width=\"50\" IsVisible=\"false\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"0c5cf8cd-9b9e-5798-1a5d-09d429f7fa3c\" Name=\"Field 1\" SortPriority=\"6\" Width=\"352\" IsVisible=\"true\" HelpText=\"Field 1\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"71badd11-26e5-56bc-44ec-12f4cc6a8f3e\" Name=\"Field 2\" SortPriority=\"7\" Width=\"282\" IsVisible=\"true\" HelpText=\"Field 2\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"411dba2d-5d6e-5272-8287-636d0841768c\" Name=\"Field 3\" SortPriority=\"8\" Width=\"125\" IsVisible=\"true\" HelpText=\"Field 3\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"048f5050-1f17-59b3-fa22-4b0781ee630b\" Name=\"Field 4\" SortPriority=\"9\" Width=\"1061\" IsVisible=\"true\" HelpText=\"Field 4\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"94e48f22-d499-5227-bb04-be011b4159b0\" Name=\"Field 5\" SortPriority=\"10\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 5\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"c1054028-424a-59ba-e760-6d30abbd69c5\" Name=\"Field 6\" SortPriority=\"11\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 6\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"5cbc4b58-2de1-5449-55b2-4651d4edf90a\" Name=\"Field 7\" SortPriority=\"12\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 7\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"fc01e6c9-a43b-51a1-fd2e-112ba48aff65\" Name=\"Field 8\" SortPriority=\"13\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 8\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"e3dcf300-46e2-5c43-ef4b-2c3db489ec25\" Name=\"Field 9\" SortPriority=\"14\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 9\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"87c21ddb-4b29-58e3-5ddc-f114c5ca209a\" Name=\"Field 10\" SortPriority=\"15\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 10\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"65f8fe42-ad02-5016-3521-f93329c76227\" Name=\"Field 11\" SortPriority=\"16\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 11\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"03d48fd3-0fe4-57f5-a477-49beb0d70a1f\" Name=\"Field 12\" SortPriority=\"17\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 12\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"998f8e2d-0f5a-5a54-0133-226e2de62c0b\" Name=\"Field 13\" SortPriority=\"18\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 13\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"bb772c34-9600-5bf7-3f54-e6080f8a0611\" Name=\"Field 14\" SortPriority=\"19\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 14\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"1479add7-3dcb-580b-f1e9-87d5186ced61\" Name=\"Field 15\" SortPriority=\"20\" Width=\"80\" IsVisible=\"true\" HelpText=\"Field 15\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n              <Column Guid=\"342f7677-17b2-4c7e-b9ec-e89612c49792\" Name=\"Count\" AggregationMode=\"Sum\" SortOrder=\"Descending\" SortPriority=\"0\" Width=\"80\" IsVisible=\"true\">\r\n                <ColorQueryEntries />\r\n              </Column>\r\n            </Columns>\r\n          </Preset>\r\n        </PersistedPresets>\r\n      </GraphSchema>\r\n    </ModifiedGraphs>\r\n  </Content>\r\n</WpaProfileContainer>"
  }
]